[
  {
    "path": ".gitignore",
    "content": "# Compiled class file\n*.class\n\n# Log file\n*.log\n\n# BlueJ files\n*.ctxt\n\n# Mobile Tools for Java (J2ME)\n.mtj.tmp/\n\n# Package Files #\n*.jar\n*.war\n*.nar\n*.ear\n*.zip\n*.tar.gz\n*.rar\n*.DS_Store\n*.exe\n\n# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml\nhs_err_pid*\n\n# editor files\n.vscode\n.*.swp\n\n# WebStorm\n.idea/\n\n# Build results\n[Dd]ebug/\n[Dd]ebugPublic/\n[Rr]elease/\n[Rr]eleases/\nx64/\nx86/\nbld/\n[Bb]in/\n[Oo]bj/\n[Ll]og/\n\n# Visual Studio 2015/2017 cache/options directory\n.vs/\n\n**/*.idea\n**/*.iml\n**/*out\n"
  },
  {
    "path": "DynamicStackBaseArray.java",
    "content": "package Stack;\n\nimport java.util.Iterator;\n\n/**\n * 顺序栈的动态扩容\n * Author: PeiJiaNi\n * @param <T>  顺序栈元素类型\n */\n\npublic class DynamicStackBaseArray<T> implements Iterable<T> {\n    private T[] items;   // 数组\n    private int count;   // 栈中的元素个数\n    private int length;  // 栈空间大小\n\n    /**\n     * 初始化栈\n     *\n     * @param length 栈空间大小\n     */\n    public DynamicStackBaseArray(int length) {\n        this.items = (T[]) new Object[length];\n        this.count = 0;\n        this.length = length;\n    }\n\n    /**\n     * 入栈操作 平均时间复杂度O(1)\n     *\n     * @param item 入栈元素\n     */\n    public void push(T item) {\n        // 栈空间已满，则扩容\n        if (count == length) {\n            resize(2 * items.length);\n        }\n\n        items[count++] = item;\n    }\n\n    /**\n     * 出栈操作 平均时间复杂度O(1)\n     *\n     * @return 如果栈内不为空，则返回栈顶元素，否则返回-1\n     */\n    public T pop() {\n        if (count == 0) {\n            System.out.println(\"当前栈已空，无法进行出栈操作\");\n            return null;\n        }\n\n        T item = items[--count];\n        items[count] = null;\n\n        if (count > 0 && (count == items.length / 4)) {\n            resize(items.length / 2);\n        }\n\n        // 返回下标为 count-1 的数组元素，并且栈中元素个数count-1\n        return item;\n    }\n\n    /**\n     * 栈空间动态增加或减小\n     *\n     * @param size\n     */\n    private void resize(int size) {\n        T[] newItems = (T[]) new Object[size];\n        for (int i = 0; i < count; i++) {\n            newItems[i] = this.items[i];\n        }\n        this.items = newItems;\n    }\n\n    //返回栈中最近添加的元素而不删除它\n    public T peek() {\n        return items[count - 1];\n    }\n\n    /**\n     * 判断当前栈是否为空\n     *\n     * @return 栈为空，则返回true,否则返回-1\n     */\n    public boolean isEmpty() {\n        return count == 0;\n    }\n\n    /**\n     * 返回栈中元素个数\n     *\n     * @return\n     */\n    public int size() {\n        return count;\n    }\n\n    @Override\n    public Iterator<T> iterator() {\n        return new ArrayIterator();\n    }\n\n    // 内部类\n    class ArrayIterator implements Iterator {\n        int numOfItems = count;\n\n        @Override\n        public boolean hasNext() {\n            return numOfItems > 0;\n        }\n\n        @Override\n        public T next() {\n            return items[--numOfItems];\n        }\n    }\n\n    public static void main(String[] args) {\n        DynamicStackBaseArray<Integer> stack = new DynamicStackBaseArray<Integer>(6);\n        stack.push(1);\n        stack.push(2);\n        stack.push(3);\n        stack.push(4);\n        stack.push(5);\n        // System.out.println(stack.peek());\n        Iterator iterator = stack.iterator();\n        // System.out.println(iterator.hasNext());\n        while (iterator.hasNext()) {\n            System.out.println(iterator.next());\n        }\n\n    }\n\n}\n\n"
  },
  {
    "path": "LICENSE",
    "content": "                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright [yyyy] [name of copyright owner]\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "README.md",
    "content": "# 数据结构和算法必知必会的50个代码实现\n### 微信搜索我的公众号“小争哥”，或者微信扫描下面二维码关注\n### 关注微信公众号，回复”PDF“获取独家算法资料。\n### 前Google工程师，10万人跟着学的《数据结构和算法之美》《设计模式之美》专栏作者\n![t2](https://github.com/wangzheng0822/markdownphotos/blob/master/pics/qrcode_for_gh_9b0e7afdff20_258.jpg)\n\n## 数组\n* 实现一个支持动态扩容的数组\n* 实现一个大小固定的有序数组，支持动态增删改操作\n* 实现两个有序数组合并为一个有序数组\n\n## 链表\n* 实现单链表、循环链表、双向链表，支持增删操作\n* 实现单链表反转\n* 实现两个有序的链表合并为一个有序链表\n* 实现求链表的中间结点\n\n## 栈\n* 用数组实现一个顺序栈\n* 用链表实现一个链式栈\n* 编程模拟实现一个浏览器的前进、后退功能\n\n## 队列\n* 用数组实现一个顺序队列\n* 用链表实现一个链式队列\n* 实现一个循环队列\n\n## 递归\n* 编程实现斐波那契数列求值f(n)=f(n-1)+f(n-2)\n* 编程实现求阶乘n!\n* 编程实现一组数据集合的全排列\n\n## 排序\n* 实现归并排序、快速排序、插入排序、冒泡排序、选择排序\n* 编程实现O(n)时间复杂度内找到一组数据的第K大元素\n\n## 二分查找\n* 实现一个有序数组的二分查找算法\n* 实现模糊二分查找算法（比如大于等于给定值的第一个元素）\n\n## 散列表\n* 实现一个基于链表法解决冲突问题的散列表\n* 实现一个LRU缓存淘汰算法\n\n## 字符串\n* 实现一个字符集，只包含a～z这26个英文字母的Trie树\n* 实现朴素的字符串匹配算法\n\n## 二叉树\n* 实现一个二叉查找树，并且支持插入、删除、查找操作\n* 实现查找二叉查找树中某个节点的后继、前驱节点\n* 实现二叉树前、中、后序以及按层遍历\n\n## 堆\n* 实现一个小顶堆、大顶堆、优先级队列\n* 实现堆排序\n* 利用优先级队列合并K个有序数组\n* 求一组动态数据集合的最大Top K\n\n## 图\n* 实现有向图、无向图、有权图、无权图的邻接矩阵和邻接表表示方法\n* 实现图的深度优先搜索、广度优先搜索\n* 实现Dijkstra算法、A*算法\n* 实现拓扑排序的Kahn算法、DFS算法\n\n## 回溯\n* 利用回溯算法求解八皇后问题\n* 利用回溯算法求解0-1背包问题\n\n## 分治\n* 利用分治算法求一组数据的逆序对个数\n\n## 动态规划\n* 0-1背包问题\n* 最小路径和\n* 编程实现莱文斯坦最短编辑距离\n* 编程实现查找两个字符串的最长公共子序列\n* 编程实现一个数据序列的最长递增子序列\n"
  },
  {
    "path": "StackBaseArray.java",
    "content": "package Stack;\n\n/**\n * 顺序栈(基于数组实现)\n * Author: PeiJiaNi\n */\npublic class StackBaseArray {\n    private int[] items;    // 数组\n    private int count;      // 栈中元素个数\n    private int length;   // 栈空间大小\n\n    public StackBaseArray(int capactiy) {\n        this.items = new int[capactiy];\n        this.count = 0;\n        this.length = capactiy;\n    }\n\n    /**\n     * 入栈操作 时间复杂度O(1)\n     * @param item 要入栈的元素\n     * @return     入栈成功则返回true,否则返回false\n     */\n    public boolean  push(int item) {\n        if(count == length) {\n            System.out.println(\"当前栈已满，无法进行入栈操作\");\n            return false;\n        }\n        items[count] = item;\n        ++count;\n        return true;\n    }\n\n    /**\n     * 出栈操作 时间复杂度O(1)\n     * @return 如果栈内不为空，则返回栈顶元素，否则返回-1\n     */\n    public int  pop(){\n        if(count == 0) {\n            System.out.println(\"当前栈已空，无法进行出栈操作\");\n            return -1;\n        }\n\n        // 返回下标为 count-1 的数组元素，并且栈中元素个数count-1\n        return items[--count];\n    }\n\n    public static void main(String[] args){\n        StackBaseArray stack = new StackBaseArray(6);\n        stack.push(1);\n        stack.push(2);\n        stack.push(3);\n        stack.push(4);\n        stack.push(5);\n        System.out.println(stack.pop());\n        System.out.println(stack.pop());\n        System.out.println(stack.pop());\n        System.out.println(stack.pop());\n        System.out.println(stack.pop());\n\n    }\n\n\n}\n\n"
  },
  {
    "path": "c-cpp/.gitignore",
    "content": "# main files\nmain.*\n\n# executives\na.out\n\n# objective files\n*.o\n*.obj\n"
  },
  {
    "path": "c-cpp/.gitkeep",
    "content": ""
  },
  {
    "path": "c-cpp/05_array/.gitkeep",
    "content": ""
  },
  {
    "path": "c-cpp/05_array/Array_gp.c",
    "content": "#include \"Array.h\"\n\n#include <string.h>\n#include <stdbool.h>\n\nArray* arrayCreate()\n{\n    struct Array *array = NULL;\n    array = malloc(sizeof(*array));\n    if (NULL == array)\n    {\n        return NULL;\n    }\n\n    array->p = NULL;\n\n    array->size = 0;\n    array->typeSize = 0;\n    array->len = 0;\n\n    array->dup = NULL;\n    array->free = NULL;\n    array->match = NULL;\n\n    return array;\n}\n\nvoid arrayInit(Array *array, int size, int typeSize)\n{\n    if (NULL == array\n        || typeSize <= 0\n        || size < 0)\n    {\n        return;\n    }\n\n    void *p = calloc(1, size* typeSize);\n    if (NULL == p)\n    {\n        return;\n    }\n\n    array->p = p;\n    array->len = 0;\n    array->size = size;\n    array->typeSize = typeSize;\n}\n\nint arrayInsert(Array *array, size_t pos, void *const value)\n{\n    if (NULL == array)\n    {\n        return -1;\n    }\n\n    if (array->len >= array->size)\n    {\n        return -2;\n    }\n\n    if (pos > array->size || pos <= 0)\n    {\n        return -3;\n    }\n\n    char *pBegin = array->p;\n    for (size_t i = array->len; i > pos - 1; --i)\n    {\n        void *pNew = pBegin + i * array->typeSize;\n        void *pOld = pBegin + (i - 1) *array->typeSize;\n        if (NULL != array->dup)\n        {\n            array->dup(pNew, pOld);\n        }\n        else\n        {\n            memcpy(pNew, pOld, array->typeSize);\n        }\n    }\n\n    void *pCopy = (void*)(pBegin + ((pos - 1) * array->typeSize));\n    if (NULL != array->dup)\n    {\n        array->dup(pCopy, value);\n    }\n    else\n    {\n        memcpy(pCopy, value, array->typeSize);\n    }\n    ++array->len;\n    return 0;\n}\n\nsize_t arraySearchValue(Array *array, void* const value)\n{\n    if (NULL == array)\n    {\n        return -1;\n    }\n\n    char *pBegin = array->p;\n    size_t i = 0;\n    for (; i < array->len; ++i)\n    {\n        int nCmp = 0;\n        if (NULL != array->match)\n        {\n            nCmp = array->match(pBegin + i * array->typeSize, value);\n        }\n        else\n        {\n            nCmp = memcmp(pBegin + i * array->typeSize, value, array->typeSize);\n        }\n\n        if (nCmp == 0)\n        {\n            break;\n        }\n    }\n\n    return i;\n}\n\nvoid* arrayIndex(Array *array, size_t index)\n{\n    if (NULL == array)\n    {\n        return NULL;\n    }\n\n    if (index > array->len\n        || index <= 0)\n    {\n        return NULL;\n    }\n\n    char *pBegin = array->p;\n    return pBegin + array->typeSize * (index - 1);\n}\n\nint arrayModify(Array *array, size_t pos, void *const value)\n{\n    if (NULL == array)\n    {\n        return -1;\n    }\n    if (pos > array->len\n        || pos <= 0)\n    {\n        return -2;\n    }\n\n    char *pBegin = array->p;\n    void *pOld = pBegin + (pos - 1) * array->typeSize;\n    if (NULL != array->dup)\n    {\n        array->dup(pOld, value);\n    }\n    else\n    {\n        memcpy(pOld, value, array->typeSize);\n    }\n\n    return 0;\n}\n\nsize_t arrayLen(Array *array)\n{\n    if (NULL == array)\n    {\n        return 0;\n    }\n\n    return array->len;\n}\n\nsize_t arraySize(Array *array)\n{\n    if (NULL == array)\n    {\n        return 0;\n    }\n\n    return array->size;\n}\n\nvoid arrayEmpty(Array *array)\n{\n    if (NULL == array)\n    {\n        return;\n    }\n\n    free(array->p);\n    array->p = NULL;\n    free(array);\n    array = NULL;\n}\n\nvoid arrayDelValue(Array *array, void *value)\n{\n    if (NULL == array)\n    {\n        return;\n    }\n\n    char* pBegin = array->p;\n    bool bCopy = false;\n    for (size_t i = 0; i < array->len; ++i)\n    {\n        if (!bCopy)\n        {\n            int nCmp = 0;\n            if (NULL != array->match)\n            {\n                nCmp = array->match(pBegin + i * array->typeSize, value);\n            }\n            else\n            {\n                nCmp = memcmp(pBegin + i * array->typeSize, value, array->typeSize);\n            }\n\n            if (0 == nCmp)\n            {\n                bCopy = true;\n                continue;\n            }\n        }\n        else\n        {\n            void *pOld = pBegin + (i + 1) * array->typeSize;\n            void *pNew = pBegin + i * array->typeSize;\n            if (NULL != array->dup)\n            {\n                array->dup(pNew, pOld);\n            }\n            else\n            {\n                memcpy(pNew, pOld, array->typeSize);\n            }\n        }\n    }\n\n    if (bCopy)\n    {\n        --array->len;\n    }\n}\n\nvoid arrayDelIndex(Array *array, size_t pos)\n{\n    if (NULL == array)\n    {\n        return;\n    }\n\n    if (pos > array->len || pos <= 0)\n    {\n        return;\n    }\n\n    char* pBegin = array->p;\n    for (size_t i = pos - 1; i < array->len - 1; ++i)\n    {\n        void *pOld = pBegin + (i + 1) * array->typeSize;\n        void *pNew = pBegin + i * array->typeSize;\n        if (NULL != array->dup)\n        {\n            array->dup(pNew, pOld);\n        }\n        else\n        {\n            memcpy(pNew, pOld, array->typeSize);\n        }\n    }\n\n    --array->len;\n}"
  },
  {
    "path": "c-cpp/05_array/Array_gp.h",
    "content": "#ifndef __ARRAY_H__\n#define __ARRAY_H__\n\n#include <stdio.h>\n#include <stdlib.h>\n\ntypedef struct Array\n{\n    // pָĿռС\n    size_t size;\n    // pָѾʹõĿռС\n    size_t len;\n    // ͵ĴС\n    size_t typeSize;\n    // ֵƺ\n    void(*dup)(void *ptr, void *key);\n    // ֵͷź\n    void(*free)(void *ptr);\n    // ֵȽϺ\n    int(*match)(void *ptr, void *key);\n    // ݵָ\n    void   *p;\n}Array;\n\n#define arraySetDupMethod(a, m) ((a)->dup = (m))\n#define arraySetFreeMethod(a, m) ((a)->free = (m))\n#define arraySetMatchMethod(a, m) ((a)->match = (m))\n\n#define arrayGetDupMethod(a) ((a)->dup)\n#define arrayGetFree(a) ((a)->free)\n#define arrayGetMatchMethod(a) ((a)->match)\n\nArray* arrayCreate();\nvoid arrayInit(Array *array, int size, int typeSize);\n\nint arrayInsert(Array *array, size_t pos, void *const value);\nsize_t arraySearchValue(Array *array, void* const value);\nvoid* arrayIndex(Array *array, size_t index);\nint arrayModify(Array *array, size_t pos, void *const value);\n\nsize_t arrayLen(Array *array);\nsize_t arraySize(Array *array);\n\nvoid arrayEmpty(Array *array);\nvoid arrayDelValue(Array *array, void *value);\nvoid arrayDelIndex(Array *array, size_t pos);\n\n#endif // !__ARRAY_H__"
  },
  {
    "path": "c-cpp/05_array/array.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\nstruct array {\n\tint size;\n\tint used;\n\tint *arr;\n};\n\nvoid dump(struct array *array)\n{\n\tint idx;\n\n\tfor (idx = 0; idx < array->used; idx++)\n\t\tprintf(\"[%02d]: %08d\\n\", idx, array->arr[idx]);\n}\n\nvoid alloc(struct array *array)\n{\n\tarray->arr = (int *)malloc(array->size * sizeof(int));\n}\n\nint insert(struct array *array, int elem)\n{\n\tint idx;\n\tif (array->used >= array->size)\n\t\treturn -1;\n\n\tfor (idx = 0; idx < array->used; idx++) {\n\t\tif (array->arr[idx] > elem)\n\t\t\tbreak;\n\t}\n\n\tif (idx < array->used)\n\t\tmemmove(&array->arr[idx+1], &array->arr[idx],\n\t\t\t(array->used - idx) * sizeof(int));\n\n\tarray->arr[idx] = elem;\n\tarray->used++;\n\treturn idx;\n}\n\nint delete(struct array *array, int idx)\n{\n\tif (idx < 0 || idx >= array->used)\n\t\treturn -1;\n\n\tmemmove(&array->arr[idx], &array->arr[idx+1],\n\t\t(array->used - idx - 1) * sizeof(int));\n\tarray->used--;\n\treturn 0;\n}\n\nint search(struct array *array, int elem)\n{\n\tint idx;\n\n\tfor (idx = 0; idx < array->used; idx++) {\n\t\tif (array->arr[idx] == elem)\n\t\t\treturn idx;\n\t\tif (array->arr[idx] > elem)\n\t\t\treturn -1;\n\t}\n\n\treturn -1;\n}\n\nint main()\n{\n\tint idx;\n\tstruct array ten_int = {10, 0, NULL};\n\n\talloc(&ten_int);\n\tif (!ten_int.arr)\n\t\treturn -1;\n\tinsert(&ten_int, 1);\n\tinsert(&ten_int, 3);\n\tinsert(&ten_int, 2);\n\tprintf(\"=== insert 1, 3, 2\\n\");\n\tdump(&ten_int);\n\n\tidx = search(&ten_int, 2);\n\tprintf(\"2 is at position %d\\n\", idx);\n\tidx = search(&ten_int, 9);\n\tprintf(\"9 is at position %d\\n\", idx);\n\n\tprintf(\"=== delete [6] element \\n\");\n\tdelete(&ten_int, 6);\n\tdump(&ten_int);\n\tprintf(\"=== delete [0] element \\n\");\n\tdelete(&ten_int, 0);\n\tdump(&ten_int);\n\treturn 0;\n}\n"
  },
  {
    "path": "c-cpp/06_linkedlist/.gitkeep",
    "content": ""
  },
  {
    "path": "c-cpp/06_linkedlist/Dlist/Dlist.c",
    "content": "/*************************************************************************\n > File Name: Dlist.c\n > Author:  jinshaohui\n > Mail:    jinshaohui789@163.com\n > Time:    18-10-07\n > Desc:    \n ************************************************************************/\n#include<stdio.h>\n#include <stdbool.h>\n#include \"./Dlist.h\"\n\n\n\nvoid dlist_init(stDlistHead *dlist)\t\t//链表初始化\n{\n\tdlist->size = 0;\n\tdlist->head = NULL;\n\tdlist->tail = NULL;\n\treturn;\n}\n\nvoid dlist_destory(stDlistHead *dlist)\t\t//删除链表\n{\n\tstDlistNode *pNode = NULL;\n\t\n\twhile(dlist->size > 0)\n\t{\n\t\tpNode = dlist->head;\n\t\tdlist->head = dlist->head->next;\n\t\tfree(pNode);\n\t\tdlist->size--;\n\t}\n\n\tmemset(dlist,0,sizeof(stDlistHead));\n\n\treturn;\n}\n\nint dlist_insert_head(stDlistHead *dlist,stDlistNode *pNode,int data)\t\t//插入头结点，操作的链表，操作的节点，数据\n{\n   if(pNode == NULL)\t\t//当只传递一个数据时\n   {\n\t    pNode = (stDlistNode *)malloc(sizeof(stDlistNode));\t\t//新建节点，为节点分配空间（malloc（）可能需要#include<malloc.h>）\n\t    if (pNode == NULL)\n\t    {\n\t\t    return -1;\n\t    }\n    }\n\n    pNode->data = data;\t\t\t\n\tpNode->prev = NULL;\n\tpNode->next = NULL;\n\n\tif (dlist->size == 0)\t\t//如果链表长度为0，即链表当前无节点，\n\t{\n\t\tdlist->head = pNode;\n\t\tdlist->tail = pNode;\n\t}\n\telse                           //如果链表已有节点，则令新插入节点为头节点\n\t{\n\t\tpNode->next = dlist->head;\n\t\tdlist->head->prev = pNode;\n\t\tdlist->head = pNode;\t\t\t\n\t}\n\n\tdlist->size++;\t\t//每成功调用一次，链表长度+1\n\treturn 0;\n}\n\nstDlistNode * dlist_remove_tail(stDlistHead *dlist)\t\t//删除尾部节点,并返回删除节点\n{\n\tstDlistNode *pNode = NULL;\n\n\tif(dlist->size == 0)\n\t{\n\t\treturn NULL;\n\t}\n\n    pNode = dlist->tail;\n\tif(dlist->size > 1)\n\t{\n\t\tdlist->tail = dlist->tail->prev;\n\t\tdlist->tail->next = NULL;\n\t}\n\telse\n\t{\n\t\tdlist->head = NULL;\n\t\tdlist->tail = NULL;\n\t}\n\tdlist->size--;\n\treturn pNode;\n}\n\nvoid dlist_remove_node(stDlistHead * dlist,stDlistNode *pNode) \t\t//删除指定节点\n{\n\tif ((dlist == NULL)||(pNode == NULL))\n\t{\n\t\treturn;\n\t}\n\n\tif (dlist->head == pNode)\n\t{\n\t\tdlist->head = dlist->head->next;\n\t}\n\telse if (dlist->tail == pNode)\n\t{\n\t\tdlist->tail = pNode->prev;\n\n\t\tdlist->tail->next = NULL;\n\t}\n\telse\n\t{\n\t\tpNode->prev->next = pNode->next;\n\t\tpNode->next->prev = pNode->prev;\n\t}\n\tdlist->size--;\n\tpNode->prev = NULL;\n\tpNode->next = NULL;\n\t\n\tif (dlist->size == 0)\n\t{\n\t\tmemset(dlist,0,sizeof(stDlistHead)); \t\t//将dlist占用内存块的所有值置为0，也就是清空head,tail指针内容\n\t}\n\n\treturn;\n}\nstDlistNode * dlist_search(stDlistHead * dlist,int data) \t\t//根据值搜索节点，并返回\n{\n\tstDlistNode *pNode = dlist->head;\n\twhile(pNode != NULL)\n\t{\n\t\tif (pNode->data == data)\n\t\t{\n\t\t\treturn pNode;\n\t\t}\n\t\tpNode = pNode->next;\n\n    }\n\treturn NULL;\n}\n\nvoid dlist_dump(stDlistHead *dlist)\t\t//显示链表中的数据\n{\n\tint no = 0;\n\tstDlistNode *pNode = dlist->head;\n\twhile(pNode != NULL)\t\t\n\t{\n\t\tprintf(\"\\r\\n [%d] = %d\",no++,pNode->data);\n\t\tpNode = pNode->next;\t\t//将pNode的下一个节点赋值给pNode，推进循环\n\t}\n\n\treturn;\n}\n\n\nvoid Lru_dlist(stDlistHead *dlist,int data) \t\t//LRU（最近最少使用）缓存淘汰算法\n{\n\tstDlistNode *pNode = NULL;\n\n\tpNode = dlist_search(dlist,data);\t\t\n\tif (pNode != NULL) \t\t\t\t//如果在链表中找到这个值，则删除储存这个值的节点，之后吧这个节点放在头部\n\t{\n\t\tdlist_remove_node(dlist,pNode);\n\t}\n\telse if(dlist->size >= 4)\t\t\t//没在链表中找到，且链表长度大于4，则从链表中删除尾部节点，将新数据放在头部\n\t{\n\t\tpNode = dlist_remove_tail(dlist);\n\n\t}\n\t\n\tdlist_insert_head(dlist ,pNode,data);\n\n\treturn;\n}\n\nint main()\n{\n\tstDlistHead dlist = {0};\n    stDlistNode * pNode = NULL;\n\n\tdlist_init(&dlist);\n\n\tprintf(\"\\r\\n inset 1,2,3\");\n\tdlist_insert_head(&dlist,NULL,1);\n\tdlist_insert_head(&dlist,NULL,2);\n\tdlist_insert_head(&dlist,NULL,3);\n\n\tdlist_dump(&dlist);\n\n\tpNode = dlist_remove_tail(&dlist);\n\tif(pNode != NULL)\n\t{\n\t\tprintf(\"\\r\\n remove %d\",pNode->data);\n\t}\n\tdlist_insert_head(&dlist,pNode,4);\n\tdlist_dump(&dlist);\n\n    Lru_dlist(&dlist,5);\n\tdlist_dump(&dlist);\n    Lru_dlist(&dlist,6);\n\tdlist_dump(&dlist);\n    Lru_dlist(&dlist,7);\n\tdlist_dump(&dlist);\n    Lru_dlist(&dlist,5);\n\tdlist_dump(&dlist);\n\n\n\n\twhile(dlist.size > 0)\n\t{\n\t    pNode = dlist_remove_tail(&dlist);\n\t    if(pNode != NULL)\n\t    { \n\t\t    printf(\"\\r\\n remove %d\",pNode->data);\n\t    \tfree (pNode);\n    \t}\n\t}\n\n\treturn 0;\n}\n"
  },
  {
    "path": "c-cpp/06_linkedlist/Dlist/Dlist.h",
    "content": "/*************************************************************************\n > File Name: Dlist.c\n > Author:  jinshaohui\n > Mail:    jinshaohui789@163.com\n > Time:    18-10-07\n > Desc:    \n ************************************************************************/\n#include<stdio.h>\n\ntypedef struct DlistNode          //双向链表中每一个节点\n{\t\t\t\t  \t\n\tstruct DlistNode *prev;   //节点前项指针\n\tstruct DlistNode *next;   //节点后项指针\n\tint    data;              //数据\n}stDlistNode;\n\ntypedef struct Dlisthead          //定义链表总体\n{\n\tint size;                 //链表长度\n\tstDlistNode *head;        //头指针\n\tstDlistNode *tail;        //尾部指针\n}stDlistHead;\n\n"
  },
  {
    "path": "c-cpp/06_linkedlist/LRUBasedLinkedList.cpp",
    "content": "typedef int DataType;\n\n//定义\nclass SNode\n{\npublic:\n    DataType data;\n    SNode * next;\n};\n\nclass SList \n{\npublic:\n    SList();\n    SList(int MaxSize);\n    ~SList();\n    void intsertElemAtBegin(DataType x);      //头部插入节点\n    bool findElem(DataType x);              //查找x，存在则返回1，不存在则返回0\n    void deleteElemAtEnd();             //删除尾节点\n    bool deleteElem(DataType x);    //删除指定节点，如果存在则删除，返回1，如果不存在，则删除失败返回0\n    bool isEmpty();                  // 查看链表是否为空，1表示不为空，0表示为空\n    bool isFull();                   // 查看链表是否满，1表示不满，0表示满\n    void printAll();             \n\n    void * findElemOptim(DataType x);     //针对此应用的优化，查找,返回指定元素的前一个节点的指针\n    void deleteElemOptim(void * snode);     //针对此应用的优化，删除\nprivate:\n    int MaxSize;       // 链表可以存放最大的数据\n    int length;             // 链表的长度\n    SNode * head;          // 指向头节点\n\n};\n\n\n/**\n * 1）单链表的插入，删除，查找操作；\n * 2）链表中存储的是 int 类型\n * \n * Author：caozx\n */\n#include<iostream>\nusing namespace std;\n\n// 初始化单链表\nSList::SList(){\n    head = new SNode;     //申请头节点\n    head -> next = NULL;\n    this -> MaxSize = 10;\n    this -> length = 0;\n}\nSList::SList(int MaxSize){\n    head = new SNode;     //申请头节点\n    head -> next = NULL;\n    this -> MaxSize = MaxSize;\n    this -> length = 0;\n}\n\n// 销毁单链表，要把开辟的空间都释放，然后再销毁。\nSList::~SList(){\n    SNode * ptr, * temp;\n    ptr = head;\n    while(ptr -> next != NULL){\n        temp = ptr -> next;\n        ptr -> next = ptr -> next -> next;\n        delete temp;   \n    }\n    delete head ; //删除头节点\n    this -> head = NULL;\n    this -> length = 0;\n}\n\n//链表头部插入节点\nvoid SList::intsertElemAtBegin(DataType x){\n    SNode * ptr = new SNode;\n    ptr -> data = x;\n\n    ptr -> next = head ->next;\n    head -> next = ptr;\n\n    this -> length ++;\n}\n\n//查找x，存在则返回1，不存在则返回0\n\nbool SList::findElem(DataType x)\n{\n    SNode * ptr;\n    ptr = head;\n    while(ptr -> next != NULL){\n        if(ptr -> next ->data == x){\n            return 1;\n        }\n        ptr = ptr -> next;\n    }\n    return 0;\n}\n\n// 删除尾结点\nvoid SList::deleteElemAtEnd(){\n    SNode * ptr , * temp;\n    ptr = head;\n    while(ptr -> next != NULL && ptr -> next -> next != NULL){   //倒数第二个节点\n        ptr = ptr -> next;\n    }\n    temp = ptr -> next;\n    ptr -> next = temp -> next;\n    this -> length --;\n    delete temp;\n}\n\n//删除指定节点，\n//如果存在则删除，返回1，表示存在且删除成功；\n//如果不存在则不删除，返回0，表示不存在该元素，不需要删除，也即删除失败\nbool SList::deleteElem(DataType x)\n{\n    SNode * ptr, * temp;\n    ptr = head;\n    while(ptr -> next != NULL){\n        if(ptr -> next ->data == x){\n            temp = ptr -> next;\n            ptr -> next = temp -> next;\n            delete temp;\n            this -> length --;\n            return 1;\n        }\n        ptr = ptr -> next;\n    }\n    return 0;\n}\n\n// 查看链表是否为空，1表示不为空，0表示为空\nbool SList::isEmpty()\n{\n    if(this -> length == 0){  //空\n        return 0;\n    }\n    else{\n        return 1;\n    }\n}\n\n// 查看链表是否满，1表示不满，0表示满\nbool SList::isFull()\n{   \n    if(this -> length == this -> MaxSize){  //满\n        return 0;\n    }\n    else{\n        return 1;\n    }\n}\n\n// 打印\nvoid SList::printAll()\n{\n    SNode * ptr;\n    ptr = head;\n    while(ptr -> next != NULL){\n        ptr = ptr -> next;\n        cout << ptr-> data <<\"   \";\n    }\n    cout << endl;\n}     \n\n//针对此应用的优化，查找,\n//若存在则返回指定元素的前一个节点的指针\n//若不存在，则返回NULL\n\nvoid * SList::findElemOptim(DataType x)\n{\n    SNode * ptr;\n    ptr = head;\n    while(ptr -> next != NULL){\n        if(ptr -> next ->data == x){\n            return (void *)ptr;\n        }\n        ptr = ptr -> next;\n    }\n    return NULL;\n}\n\n//针对此应用的优化，删除\n\nvoid SList::deleteElemOptim(void * snode)\n{\n     SNode * ptr, * temp;\n     ptr = (SNode *)snode;\n     temp =  ptr -> next;\n     ptr -> next = temp -> next;\n     this -> length --;\n     delete temp;\n}\nint main(int argc, char const *argv[])\n{\n    cout << \"test \"<< endl;   \n    SList slist(10);     //缓存最大10个。\n    int num = 0;\n    while(1)\n    {\n        cout << \"please enter a number,99999== exit\" << endl;\n        cin >> num;\n        if(num == 99999)\n            break;\n        /* 未优化\n        if(slist.findElem(num)){    //存在\n            slist.deleteElem(num);     //把原来的位置删除\n            slist.intsertElemAtBegin(num); //在链表头插入\n        }\n        */\n       //优化\n       SNode * prePtr = (SNode *)slist.findElemOptim(num);\n       if(prePtr != NULL){    //存在\n            slist.deleteElemOptim(prePtr);     //把原来的位置删除\n            slist.intsertElemAtBegin(num); //在链表头插入\n        }\n        else{      //不存在\n            if(slist.isFull()){    //不满\n                slist.intsertElemAtBegin(num);\n            }\n            else{                   //满\n                slist.deleteElemAtEnd();\n                slist.intsertElemAtBegin(num);\n            }\n        }\n        slist.printAll();\n    }\n    return 0;\n    system(\"pause\");\n}\n"
  },
  {
    "path": "c-cpp/06_linkedlist/list_isPalindrome/LinkList.cpp",
    "content": "#include \"LinkList.h\"\n\nvoid CreateListHead(LinkList *&L,ElemType a[],int n)\n{\n\tint i;\n\tLinkList *s;\n\tL = (LinkList *)malloc(sizeof(LinkList));\n\tL->next = NULL;\n\tfor(i = 0;i < n;i++)\n\t{\n\t\ts=(LinkList*)malloc(sizeof(LinkList));\n\t\ts->data = a[i];\n\t\ts->next = L->next;\n\t\tL->next = s;\n\t}\n}\nvoid CreateListTail(LinkList *&L,ElemType a[],int n)\n{\n\tint i;\n\tLinkList * s,* r;\n\tL = (LinkList *)malloc(sizeof(LinkList));\n\tr = L;\n\tfor(i = 0;i < n;i++)\n\t{\n\t\ts = (LinkList *)malloc(sizeof(LinkList));\n\t\ts->data = a[i];\n\t\tr->next = s;\n\t\tr = s;\n\t}\n\tr->next = NULL; \n}\nvoid InitList(LinkList *&L)\n{\n\tL=(LinkList *)malloc(sizeof(LinkList));\n\tL->next = NULL;\n}\nvoid DestroyList(LinkList *&L)\n{\n\tLinkList * pre = L,*p = L->next;\n\twhile(p!=NULL)\n\t{\n\t\tfree(pre);\n\t\tpre = p;\n\t\tp = L->next;\n\t}\n\tfree(pre);\n}\nbool ListEmpty(LinkList *L)\n{\n\treturn(L->next==NULL); \n}\nint ListLength(LinkList *L)\n{\n\tint n = 0;\n\tLinkList * p = L;\n\twhile(p->next!=NULL)\n\t{\n\t\tn++;\n\t\tp=p->next;\n\t}\n\treturn(n);\n}\nvoid ShowList(LinkList *L)\n{\n\tLinkList * p = L->next;//ָʼڵ\n\twhile(p!=NULL)\n\t{\n\t\tprintf(\" %c \",p->data);\n\t\tp = p->next; \n\t} \n\tprintf(\"\\n\");\n}\nbool GetListElem(LinkList *L,int i,ElemType &e)\n{\n\tint j = 0;\n\tLinkList *p = L;\n\twhile(j<i&&p!=NULL)\n\t{\n\t\tj++;\n\t\tp=p->next;\n\t}\n\tif(p==NULL)\n\t\treturn false;\n\telse\n\t{\n\t\te=p->data;\n\t\treturn true;\n\t}\n}\nint LocateElem(LinkList*L,ElemType e)\n{\n\tint i=1;\n\tLinkList *p = L->next;\n\twhile(p!=NULL&&p->data!=e){\n\t\tp=p->next;\n\t\ti++;\n\t}\n\tif(p==NULL)\n\t{\n\t\treturn(0);\n\t}\n\telse\n\t\treturn(i);\n}\nbool ListInsert(LinkList *&L,int i,ElemType e)\n{\n\tint j=0;\n\tLinkList *p =L,*s;\n\twhile(j<i-1&&p!=NULL)\n\t{\n\t\tj++;\n\t\tp=p->next;\n\t}\n\tif(p==NULL)\n\t{\n\t\treturn false;\n\t}\n\telse\n\t{\n\t\ts= (LinkList*)malloc(sizeof(LinkList));\n\t\ts->data = e;\n\t\ts->next = p->next;\n\t\tp->next = s;\n\t\treturn true;\n\t}\n}\nbool ListDelete(LinkList *&L,int i,ElemType &e)\n{\n\tint j=0;\n\tLinkList * p =L,*q;\n\twhile(j<i-1&&p!=NULL)\n\t{\n\t\tj++;\n\t\tp=p->next;\n\t}\n\tif(p==NULL)\n\t\treturn false;\n\telse\n\t{\n\t\tq=p->next;\n\t\tif(q==NULL)\n\t\t\treturn false;\n\t\te=q->data;\n\t\tp->next=q->next;\n\t\tfree(q);\n\t\treturn true;\n\t}\n}\n"
  },
  {
    "path": "c-cpp/06_linkedlist/list_isPalindrome/LinkList.h",
    "content": "#ifndef LINKLIST_H\n#define LINKLIST_H\n\n#include <stdlib.h>\n#include <stdio.h>\ntypedef char ElemType;\ntypedef struct LNode \n{\n\tElemType data;\n\tstruct LNode*next;\n}LinkList;\n\nvoid CreateListHead(LinkList *&L,ElemType a[],int n);\nvoid CreateListTail(LinkList *&L,ElemType a[],int n);\nvoid InitList(LinkList *&L);\nvoid DestroyList(LinkList *&L);\nbool ListEmpty(LinkList *L);\nint ListLength(LinkList *L);\nvoid ShowList(LinkList *L);\nbool GetListElem(LinkList *L,int i,ElemType &e);\nint LocateElem(LinkList*L,ElemType e);\nbool ListInsert(LinkList *&L,int i,ElemType e);\nbool ListDelete(LinkList *&L,int i,ElemType &e);\n#endif\n"
  },
  {
    "path": "c-cpp/06_linkedlist/palindromeList/LinkedList.hpp",
    "content": "/**\n * Author:  TripleZ<me@triplez.cn>\n * Date:    2018-10-10\n * Brief:   Linked list class.\n */\n\n#ifndef _LINKEDLIST_HPP_\n#define _LINKEDLIST_HPP_\n\n#include <cstdio>\n#include \"ListNode.hpp\"\n\nclass LinkedList {\npublic:\n    int size;\n    int length;\n    ListNode *head;\n    LinkedList();\n    LinkedList(int size);\n    ~LinkedList();\n    ListNode* FindElem(int elemVal);\n    bool DeleteElem(ListNode *elem);\n    bool DeleteLastElem();\n    bool InsertElemAtFront(int elemVal);\n    bool InsertElemAtBack(int elemVal);\n    void PrintList();\n};\n\nLinkedList::LinkedList() {\n    this -> head = new ListNode();\n    this -> head->next = nullptr;\n    this -> head->val = -1;\n    this -> size = 10; // default\n    this -> length = 0;\n}\n\nLinkedList::LinkedList(int size) {\n    this -> head = new ListNode();\n    this -> head->next = nullptr;\n    this -> head->val = -1;\n    \n    this -> size = size;\n    this -> length = 0;\n}\n\nLinkedList::~LinkedList() {\n    ListNode *p, *q;\n    p = this -> head;\n    while(p -> next != nullptr) {\n        q = p -> next;\n        p -> next = p -> next -> next;\n        delete q;\n    }\n    delete head;\n    this -> head = nullptr;\n    this -> length = 0;\n}\n\nListNode* LinkedList::FindElem(int elemVal) {\n    ListNode *p;\n    for (p = this -> head -> next; p != nullptr; p = p -> next) {\n        if (p -> val == elemVal) {\n            return p;\n        }\n    }\n    return nullptr;\n}\n\nbool LinkedList::DeleteElem(ListNode *elem) {\n    ListNode *prev, *next;\n    for (prev = this -> head; prev -> next != elem; prev = prev -> next);\n    next = elem -> next;\n    prev -> next = next;\n    delete elem;\n    this -> length --;\n    return true;\n}\n\nbool LinkedList::DeleteLastElem() {\n    ListNode *prev, *elem;\n    for (prev = this -> head; prev -> next -> next != nullptr; prev = prev -> next) ;\n    elem = prev -> next;\n    prev -> next = nullptr;\n    delete elem;\n    this -> length --;\n    return true;\n}\n\nbool LinkedList::InsertElemAtFront(int elemVal) {\n    ListNode *newNode = new ListNode();\n    newNode -> val = elemVal;\n    newNode -> next = this -> head -> next;\n    this -> head -> next = newNode;\n    this -> length ++;\n    return true;\n}\n\nbool LinkedList::InsertElemAtBack(int elemVal) {\n    ListNode *newNode = new ListNode();\n    newNode -> val = elemVal;\n    ListNode *end;\n    for (end = this -> head; end -> next != nullptr; end = end -> next);\n    end -> next = newNode;\n    newNode -> next = nullptr;\n    this -> length ++;\n    return true;\n}\n\nvoid LinkedList::PrintList() {\n    ListNode *elem;\n    printf(\"List: \");\n    for (elem = this -> head -> next; elem -> next != nullptr; elem = elem -> next) {\n        printf(\"%d - \", elem -> val);\n    }\n    printf(\"%d\\n\", elem -> val);\n}\n\n#endif\n"
  },
  {
    "path": "c-cpp/06_linkedlist/palindromeList/ListNode.hpp",
    "content": "/**\n * Author:  TripleZ<me@triplez.cn>\n * Date:    2018-10-10\n * Brief:   ListNode class.\n */\n\n#ifndef _LISTNODE_HPP_\n#define _LISTNODE_HPP_\n\nclass ListNode {\npublic:\n    int val;\n    ListNode *next;\n};\n\n#endif\n"
  },
  {
    "path": "c-cpp/06_linkedlist/palindromeList/palindromeList.cpp",
    "content": "/**\n * Author:  TripleZ<me@triplez.cn>\n * Date:    2018-10-10\n * Brief:   Check a list whether is palindrome.\n */\n\n#include <cstdio>\n#include \"LinkedList.hpp\"\n\nbool CheckPalindromeList(LinkedList *list) {\n    // 使用快慢指针找链表中点\n    ListNode *slow, *fast, *mid2;\n    slow = list -> head;\n    fast = list -> head;\n    while (fast -> next != nullptr) {\n        slow = slow -> next;\n        fast = fast -> next;\n        if (fast -> next != nullptr) {\n            fast = fast -> next;\n            mid2 = slow -> next;\n        } else {\n            mid2 = nullptr;\n        }\n    }\n\n    // 从中点向后逆转链表（区分奇偶情况）\n    ListNode *mid = slow;\n    ListNode *elem, *prev, *save;\n    if (mid2 == nullptr) {  // odd\n        elem = mid;\n        prev = mid -> next;\n    } else {  // even\n        elem = mid2;\n        prev = mid2 -> next;\n        mid2 -> next = nullptr;\n    }\n    save = prev -> next;\n    mid -> next = nullptr;\n    while (save != nullptr) {\n        prev -> next = elem;\n        elem = prev;\n        prev = save;\n        save = save -> next;\n    }\n    prev -> next = elem;\n\n    ListNode *end = prev;\n    ListNode *front = list -> head -> next;\n\n    // 从头尾同时遍历比较，检测链表是否为回文\n    bool palindrome = true;\n    while (front != end) {\n        // printf(\"%d, %d\\n\", front -> val, end -> val);\n        if (front -> val != end -> val) {\n            palindrome = false;\n            break;\n        }\n        front = front -> next;\n        end = end -> next;\n    }\n\n    palindrome ? printf(\"The list is palindrome~\\n\") : printf(\"The list is not palindrome!\\n\");\n\n    return palindrome;\n}\n\nint main(int argc, char const *argv[]) {\n    \n    int init[] = {1, 2, 3, 2, 1};\n    LinkedList *list = new LinkedList(5);\n    for (int i = 0; i < 5; i++) {\n        list -> InsertElemAtBack(init[i]);\n    }\n    list -> PrintList();\n\n    CheckPalindromeList(list);  // true\n    \n    list -> InsertElemAtFront(5);\n    CheckPalindromeList(list);  // false\n\n\n    int init2[] = {1, 2, 3, 3, 2, 1};\n    LinkedList *list2 = new LinkedList(10);\n    for (int i = 0; i < 6; i++) list2 -> InsertElemAtBack(init2[i]);\n    list2 -> PrintList();\n    CheckPalindromeList(list2);\n\n    list2 -> InsertElemAtBack(4);\n    CheckPalindromeList(list2);\n\n    return 0;\n}\n"
  },
  {
    "path": "c-cpp/06_linkedlist/single_list.c",
    "content": "#include <stdio.h>\n#include <stdbool.h>\n\nstruct single_list {\n\tstruct single_list *next;\n\tint val;\n};\n\nstruct single_list_head {\n\tstruct single_list *head;\n};\n\nbool is_empty(struct single_list_head *head)\n{\n\treturn head->head == NULL;\n}\n\nvoid dump(struct single_list_head *head)\n{\n\tstruct single_list *tmp = head->head;\n\tint idx = 0;\n\n\twhile (tmp) {\n\t\tprintf(\"[%02d]: %08d\\n\", idx++, tmp->val);\n\t\ttmp = tmp->next;\n\t}\n}\n\nvoid insert(struct single_list **prev, struct single_list *elem)\n{\n\tif (!prev)\n\t\treturn;\n\n\telem->next = *prev;\n\t*prev = elem;\n}\n\nvoid insert_head(struct single_list_head *head, struct single_list *elem)\n{\n\tinsert(&head->head, elem);\n}\n\nstruct single_list* del(struct single_list **prev)\n{\n\tstruct single_list *tmp;\n\n\tif (!prev)\n\t\treturn NULL;\n\tif (*prev == NULL)\n\t\treturn NULL;\n\ttmp = *prev;\n\t*prev = (*prev)->next;\n\ttmp->next = NULL;\n\n\treturn tmp;\n};\n\nstruct single_list* delete_head(struct single_list_head* head)\n{\n\treturn del(&head->head);\n};\n\nstruct single_list** search(struct single_list_head* head, int target)\n{\n\tstruct single_list **prev, *tmp;\n\n\tfor (prev = &head->head, tmp = *prev;\n\t     tmp && (tmp->val < target);\n\t     prev = &tmp->next, tmp = *prev)\n\t\t;\n\n\treturn prev;\n};\n\nvoid reverse(struct single_list_head* head)\n{\n\tstruct single_list_head tmp = {NULL};\n\tstruct single_list *elem;\n\n\twhile (!is_empty(head)) {\n\t\telem = delete_head(head);\n\t\tinsert_head(&tmp, elem);\n\t}\n\n\thead->head = tmp.head;\n}\n\nbool is_cyclic(struct single_list_head* head)\n{\n\tstruct single_list *s1, *s2;\n\n\ts1 = s2 = head->head;\n\n\twhile(s1 && s2) {\n\t\ts1 = s1->next;\n\t\ts2 = s2->next ? s2->next->next:s2->next;\n\n\t\tif (s1 == s2)\n\t\t\treturn true;\n\t}\n\treturn false;\n}\n\nstruct single_list* middle(struct single_list_head* head)\n{\n\tstruct single_list *s1, *s2;\n\tstruct single_list pseudo_head;\n\n\tpseudo_head.next = head->head;\n\ts1 = s2 = &pseudo_head;\n\n\twhile (true) {\n\t\tif (!s2 || !s2->next)\n\t\t\treturn s1;\n\t\ts1 = s1->next;\n\t\ts2 = s2->next->next;\n\t}\n\n\treturn NULL;\n};\n\nint main()\n{\n\tstruct single_list_head head = {NULL};\n\tstruct single_list lists[10];\n\tstruct single_list **prev;\n\tint idx;\n\n\tfor (idx = 0; idx < 10; idx++) {\n\t\tlists[idx].val = idx;\n\t\tlists[idx].next = NULL;\n\t}\n\n\tinsert_head(&head, &lists[6]);\n\tinsert_head(&head, &lists[5]);\n\tinsert_head(&head, &lists[4]);\n\tinsert_head(&head, &lists[1]);\n\tinsert_head(&head, &lists[0]);\n\n\tprintf(\"=== insert 0, 1, 4, 5, 6\\n\");\n\tdump(&head);\n\n\tprev = search(&head, 2);\n\tinsert(prev, &lists[2]);\n\tprintf(\"=== insert 2\\n\");\n\tdump(&head);\n\n\tprintf(\"middle elem is %d\\n\", middle(&head)->val);\n\n\tprev = search(&head, 2);\n\tif ((*prev) && ((*prev)->val == 2))\n\t\tprintf(\"The list contains 2\\n\");\n\telse\n\t\tprintf(\"The list not contains 2\\n\");\n\n\tdel(prev);\n\tprev = search(&head, 2);\n\tprintf(\"After remove 2\\n\");\n\tif ((*prev) && ((*prev)->val == 2))\n\t\tprintf(\"The list contains 2\\n\");\n\telse\n\t\tprintf(\"The list not contains 2\\n\");\n\tdump(&head);\n\n\tprintf(\"After reverse \\n\");\n\treverse(&head);\n\tdump(&head);\n\n\tprintf(\"middle elem is %d\\n\", middle(&head)->val);\n\n\tlists[0].next = &lists[6];\n\tprintf(\"list is%s cyclic\\n\", is_cyclic(&head)?\"\":\" not\");\n\n\treturn 0;\n}\n"
  },
  {
    "path": "c-cpp/06_linkedlist/singlelist_gc/singleList.c",
    "content": "#include \"singleList.h\"\n\n#include <string.h>\n\nlinkedList * listCreate()\n{\n    linkedList *list = NULL;\n    list = malloc(sizeof(*list));\n    if (NULL == list)\n    {\n        return NULL;\n    }\n\n    list->dup = NULL;\n    list->free = NULL;\n    list->match = NULL;\n\n    list->head = NULL;\n    list->len = 0;\n\n    return list;\n}\n\n// ͷ\nvoid listRelease(linkedList *list)\n{\n    if (NULL == list)\n    {\n        return;\n    }\n\n    listEmpty(list);\n\n    free(list);\n    list = NULL;\n}\n\nvoid listEmpty(linkedList *list)\n{\n    if (NULL == list)\n    {\n        return;\n    }\n\n    while (NULL != list->head)\n    {\n        listNode *pNode = list->head;\n        list->head = pNode->next;\n        if (NULL != list->free)\n        {\n            list->free(pNode->value);\n        }\n        else\n        {\n            free(pNode->value);\n        }\n\n        pNode->next = NULL;\n        free(pNode);\n        pNode = NULL;\n    }\n}\n\nlinkedList * listAddNodeHead(linkedList *list, void * value)\n{\n    if (NULL == list || NULL == value)\n    {\n        return list;\n    }\n\n    listNode *node = NULL;\n    node = malloc(sizeof(*node));\n    if (NULL == node)\n    {\n        return list;\n    }\n\n    node->value = value;\n    node->next = list->head;\n    list->head = node;\n\n    ++list->len;\n    return list;\n}\n\nlinkedList * listAddNodeTail(linkedList *list, void *value)\n{\n    if (NULL == list || NULL == value)\n    {\n        return list;\n    }\n\n    listNode *node = NULL;\n    node = malloc(sizeof(*node));\n    if (NULL == node)\n    {\n        return list;\n    }\n\n    node->value = value;\n    node->next = NULL;\n\n    if (NULL == list->head\n        && list->len == 0)\n    {\n        list->head = node;\n    }\n    else\n    {\n        listNode *tail = list->head;\n        listNode *pre = list->head;\n        while (NULL != tail)\n        {\n            pre = tail;\n            tail = tail->next;\n        }\n\n        pre->next = node;\n    }\n\n    ++list->len;\n    return list;\n}\n\nlinkedList * listInsertNode(linkedList *list, listNode *old_node, void *value, bool after)\n{\n    if (NULL == list || NULL == old_node)\n    {\n        return list;\n    }\n\n    listNode *pNode = NULL;\n    pNode = malloc(sizeof(*pNode));\n    if (NULL == pNode)\n    {\n        return list;\n    }\n\n    pNode->value = value;\n    if (after)\n    {\n        pNode->next = old_node->next;\n        old_node->next = pNode;\n    }\n    else\n    {\n        listNode *pre = list->head;\n        while (pre->next != old_node)\n        {\n            pre = pre->next;\n        }\n\n        if (NULL != pre)\n        {\n            pre->next = pNode;\n            pNode->next = old_node;\n        }\n    }\n\n    ++list->len;\n    return list;\n}\n\n// ûͷźʱͷŴ\nvoid listDelNode(linkedList *list, listNode *node)\n{\n    if (NULL == list || NULL == node)\n    {\n        return;\n    }\n\n    listNode *pre = list->head;\n    listNode *cur = list->head;\n    while (NULL != cur && cur != node)\n    {\n        pre = cur;\n        cur = cur->next;\n    }\n\n    // ڸ\n    if (NULL == pre)\n    {\n        return;\n    }\n\n    pre->next = node->next;\n    node->next = NULL;\n    --list->len;\n\n    if (NULL != list->free)\n    {\n        list->free(node->value);\n        free(node);\n        node = NULL;\n    }\n}\n\nlistNode * listSearchKey(linkedList *list, void *key)\n{\n    if (NULL == list)\n    {\n        return NULL;\n    }\n\n    listNode *node = list->head;\n    while (NULL != node)\n    {\n        if (NULL != list->match)\n        {\n            if (list->match(key, node->value) == 0)\n            {\n                return node;\n            }\n        }\n        else\n        {\n            if (key == node->value)\n            {\n                return node;\n            }\n        }\n\n        node = node->next;\n    }\n\n    return NULL;\n}\n\nlistNode * listIndex(linkedList *list, long index)\n{\n    if (NULL == list)\n    {\n        return NULL;\n    }\n\n    if (index <= 0\n        || index > list->len)\n    {\n        return NULL;\n    }\n\n    listNode *pNode = list->head;\n    for (long i = 0; i < index; ++i)\n    {\n        pNode = pNode->next;\n    }\n\n    return pNode;\n}\n\nlinkedList* listRewind(linkedList *list)\n{\n    if (NULL == list)\n    {\n        return NULL;\n    }\n\n    listNode *head = list->head;\n    listNode *pre = NULL;\n    listNode *next = NULL;\n    while (NULL != head)\n    {\n        next = head->next;\n        head->next = pre;\n        pre = head;\n        head = next;\n    }\n\n    list->head = pre;\n    return list;\n}\n\nsize_t listLength(linkedList *list)\n{\n    if (NULL == list)\n    {\n        return 0;\n    }\n\n    return list->len;\n}"
  },
  {
    "path": "c-cpp/06_linkedlist/singlelist_gc/singleList.h",
    "content": "#ifndef __SINGLELIST_H__\n#define __SINGLELIST_H__\n\n#include <stdlib.h>\n#include <stdbool.h>\n\ntypedef struct listNode\n{\n    struct listNode *next;\n    void *value;\n}listNode;\n\ntypedef struct linkedList\n{\n    listNode *head;\n    size_t len;\n    size_t typesize;\n\n    void(*dup)(void*, void*);\n    int(*match)(void*, void*);\n    void(*free)(void*);\n}linkedList;\n\n#define listSetDupMethod(l,m) ((l)->dup = (m))\n#define listSetFreeMethod(l,m) ((l)->free = (m))\n#define listSetMatchMethod(l,m) ((l)->match = (m))\n\n#define listGetDupMethod(l) ((l)->dup)\n#define listGetFree(l) ((l)->free)\n#define listGetMatchMethod(l) ((l)->match)\n\nlinkedList *listCreate();\nvoid listRelease(linkedList *list);\nvoid listEmpty(linkedList *list);\nlinkedList *listAddNodeHead(linkedList *list, void *value);\nlinkedList *listAddNodeTail(linkedList *list, void *value);\nlinkedList *listInsertNode(linkedList *list, listNode *old_node, void *value, bool after);\nvoid listDelNode(linkedList *list, listNode *node);\n\nlistNode *listSearchKey(linkedList *list, void *key);\nlistNode *listIndex(linkedList *list, long index);\nlinkedList* listRewind(linkedList *list);\n\nsize_t listLength(linkedList *list);\n\n#endif // !__SINGLELIST_H__\n"
  },
  {
    "path": "c-cpp/07_linkedlist/.gitkeep",
    "content": ""
  },
  {
    "path": "c-cpp/07_linkedlist/LinkedListAlgo.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n\n/**\n * 1) 单链表反转\n * 2) 链表中环的检测\n * 3) 两个有序的链表合并\n * 4) 删除链表倒数第 n 个结点\n * 5) 求链表的中间结点\n *\n * Author: Smallfly\n */\n\ntypedef struct SinglyLinkedNode {\n    int data;\n    struct SinglyLinkedNode* next;\n} SinglyLinkedNode;\n\nvoid insertNode(SinglyLinkedNode** head_ref, int data);\nvoid printLinkedList(SinglyLinkedNode* head);\n\n/** 反转单链表 */\n\nvoid reverse(SinglyLinkedNode** head_ref) {\n    if (*head_ref == NULL) return;\n    \n    SinglyLinkedNode *prev = NULL;\n    SinglyLinkedNode *current = *head_ref;\n    while (current) {\n        SinglyLinkedNode *next = current->next;\n        if (!next) {\n            // 到达尾结点时，将地址存入 head_ref\n            *head_ref = current;\n        }\n        current->next = prev;\n        prev = current;\n        current = next;\n    }\n}\n\nvoid test_reverse() {\n    SinglyLinkedNode* head = NULL;\n    insertNode(&head, 3);\n    insertNode(&head, 2);\n    insertNode(&head, 1);\n    \n    reverse(&head);\n    printLinkedList(head);\n}\n\n/** 检测单链表是否有环 */\n\n// 这里使用一级指针也可以\nint checkCircle(SinglyLinkedNode** head_ref) {\n    if (*head_ref == NULL) return 0;\n    SinglyLinkedNode *slow = *head_ref, *fast = *head_ref;\n    while (fast != NULL && fast->next != NULL) {\n        fast = fast->next->next;\n        slow = slow->next;\n        if (slow == fast) return 1;\n    }\n    return 0;\n}\n\nvoid test_checkCircle() {\n    SinglyLinkedNode* head = NULL;\n    insertNode(&head, 3);\n    insertNode(&head, 2);\n    insertNode(&head, 1);\n    \n    int result1 = checkCircle(&head);\n    printf(\"has circle: %d\\n\", result1);\n    \n    // make circle linklist\n    SinglyLinkedNode* current = malloc(sizeof(SinglyLinkedNode));\n    current->data = 0;\n    SinglyLinkedNode* h = current;\n    for (int i = 1; i < 4; ++i) {\n        SinglyLinkedNode* node = malloc(sizeof(SinglyLinkedNode));\n        node->data = i;\n        current->next = node;\n        //reset current node\n        current = node;\n    }\n    current->next = h;\n    \n    int result2 = checkCircle(&h);\n    printf(\"has circle: %d\\n\", result2);\n}\n\n/** 有序链表合并 */\n\nvoid moveNode(SinglyLinkedNode** dest_ref, SinglyLinkedNode** src_ref);\n\nSinglyLinkedNode* mergeSortedLinkedList(SinglyLinkedNode* la, SinglyLinkedNode* lb) {\n    // 辅助结点，next 指针持有合并后的有序链表\n    SinglyLinkedNode dummy;\n    \n    // 有序链表的尾结点\n    SinglyLinkedNode* tail = &dummy;\n    \n    while (1) {\n        // 如果有一个链表为空，直接与另一个链表接起来\n        if (!la) {\n            tail->next = lb;\n            break;\n        } else if (!lb) {\n            tail->next = la;\n            break;\n        }\n        \n        // 将头结点较小的优先添加到 tail\n        if (la->data <= lb->data) {\n            moveNode(&(tail->next), &la);\n        } else {\n            moveNode(&(tail->next), &lb);\n        }\n        tail = tail->next;\n    }\n    \n    return dummy.next;\n}\n\n// 将 src_ref 的头结点，添加到 dest_ref 的头部。\nvoid moveNode(SinglyLinkedNode** dest_ref, SinglyLinkedNode** src_ref) {\n    if (*src_ref == NULL) return;\n    SinglyLinkedNode* new_node = *src_ref;\n    \n    *src_ref = new_node->next;\n    \n    new_node->next = *dest_ref;\n    *dest_ref = new_node;\n}\n\nvoid test_mergeSortedLinkedList() {\n    SinglyLinkedNode* a = NULL;\n    insertNode(&a, 10);\n    insertNode(&a, 5);\n    insertNode(&a, 0);\n    \n    SinglyLinkedNode* b = NULL;\n    insertNode(&b, 8);\n    insertNode(&b, 6);\n    insertNode(&b, 3);\n    \n    SinglyLinkedNode* result = mergeSortedLinkedList(a, b);\n    printLinkedList(result);\n    \n    SinglyLinkedNode* result2 = mergeSortedLinkedList(a, NULL);\n    printLinkedList(result2);\n}\n\n/** 删除倒数第 K 个结点 */\n\nvoid deleteLastKth(SinglyLinkedNode** head_ref, int k) {\n    if (*head_ref == NULL || k == 0) return;\n    \n    // 快指针向前移动 k-1\n    SinglyLinkedNode* fast = *head_ref;\n    int i = 1;\n    while (i < k && fast != NULL) {\n        fast = fast->next;\n        ++i;\n    }\n    \n    // 如果快指针为空，说明结点个数小于 k\n    if (fast == NULL) return;\n    \n    SinglyLinkedNode* slow = *head_ref;\n    SinglyLinkedNode* prev = NULL;\n    while (fast->next != NULL) {\n        fast = fast->next;\n        prev = slow;\n        slow = slow->next;\n    }\n    \n    // 如果 prev 为空，头结点刚好是第 k 个结点\n    if (!prev) {\n        (*head_ref) = (*head_ref)->next;\n    } else {\n        prev->next = slow->next;\n    }\n    free(slow);\n}\n\nvoid test_deleteLastKth() {\n    SinglyLinkedNode* head = NULL;\n    insertNode(&head, 1);\n    insertNode(&head, 2);\n    insertNode(&head, 3);\n    insertNode(&head, 4);\n    insertNode(&head, 5);\n    \n    // 1. 删除头结点\n    deleteLastKth(&head, 5);\n    printLinkedList(head);\n    \n    // 2. 删除中间结点\n    deleteLastKth(&head, 2);\n    printLinkedList(head);\n    \n}\n\n/** 求中间结点  */\n\nSinglyLinkedNode* findMiddleNode(SinglyLinkedNode* head) {\n    if (!head) return NULL;\n    SinglyLinkedNode* slow = head;\n    SinglyLinkedNode* fast = head;\n    \n    // 1. 慢指针走一步，快指针两步\n    while (fast->next != NULL && fast->next->next != NULL) {\n        slow = slow->next;\n        fast = fast->next->next;\n    }\n    \n    return slow;\n}\n\nvoid test_findMiddleNode() {\n    SinglyLinkedNode* head = NULL;\n    insertNode(&head, 1);\n    insertNode(&head, 2);\n    insertNode(&head, 3);\n    insertNode(&head, 4);\n    insertNode(&head, 5);\n    \n    SinglyLinkedNode* middleNode = findMiddleNode(head);\n    printf(\"%d\\n\", middleNode->data);\n    printLinkedList(head);\n}\n\n/** 工具方法 */\n\n// 插入新结点到链表头部\nvoid insertNode(SinglyLinkedNode** head_ref, int data) {\n    SinglyLinkedNode* new_node = malloc(sizeof(SinglyLinkedNode));\n    new_node->data = data;\n    new_node->next = *head_ref;\n    *head_ref = new_node;\n}\n\n// 打印链表\nvoid printLinkedList(SinglyLinkedNode* node) {\n    printf(\"--- start ---\\n\");\n    while (node) {\n        printf(\"data: %d\\n\", node->data);\n        node = node->next;\n    }\n    printf(\"--- end ---\\n\");\n}\n\n// 跑测试\nvoid test() {\n    \n    test_reverse();\n    \n//    test_checkCircle();\n    \n//    test_mergeSortedLinkedList();\n    \n//    test_deleteLastKth();\n    \n//    test_findMiddleNode();\n}\n"
  },
  {
    "path": "c-cpp/07_linkedlist/SingleList.cpp",
    "content": "#include <iostream>\r\n#include <string>\r\n#include <functional>\r\nusing namespace std;\r\n\r\n\r\nclass CElement;\r\n/***\r\n * @brief \r\n*/\r\nclass CSingleList\r\n{\r\npublic:\r\n    CSingleList();\r\n    ~CSingleList();\r\n\r\n    /**\r\n     * @brief ..ĩβ\r\n     * @return ɹطǿָ,ʧ\r\n    */\r\n    CElement* Insert(void* lpData, int iDataSize);\r\n    /**\r\n     * @brief ..ָλò\r\n     * @return ɹطǿָ,ʧ\r\n    */\r\n    CElement* Insert(CElement* lpElement, void* lpData, int iDataSize);\r\n    /**\r\n     * @brief ɾ\r\n    */\r\n    void Delete(CElement*);\r\n\r\n    /**\r\n     * @brief \r\n    */\r\n    CElement* Begin();\r\n    /**\r\n     * @brief һԪ\r\n    */\r\n    CElement* Next();\r\n    /***\r\n     * @brief β\r\n    */\r\n    CElement* End();\r\n\r\n    /**\r\n     * @brief Ƿǿ\r\n     * @return շTRUE򷵻FALSE\r\n    */\r\n    bool Empty();\r\n\r\n    /**\r\n     * @brief ת\r\n    */\r\n    void Reverse();\r\n\r\n    /**\r\n     * @brief ⻷\r\n     * @return TRUEʱʾڻ,򲻴ڻ.\r\n    */\r\n    bool CheckCircle();\r\n\r\n    /**\r\n     * @brief ϲ2\r\n    */\r\n    void Merge(CSingleList& lst, std::function<int(void* t1, void* t2)>);\r\n\r\n    /**\r\n     * @brief ɾK\r\n    */\r\n    void DeleteLastKth(int k);\r\n    /**\r\n     * @brief мڵ\r\n    */\r\n    CElement* Center();\r\nprivate:\r\n    void Insert(CElement* lpNewElement, CElement* lpCurElement, bool bBack = true);\r\n    void Insert(CElement* lpNewElement);\r\n    CElement* Tail();\r\n\r\n\tCSingleList(CSingleList const & rhs);\r\n\tCSingleList& operator= (CSingleList const& rhs);\r\nprivate:\r\n    /**ͷ*/\r\n    CElement* m_lpHead;\r\n    /**ڱ*/\r\n    CElement* m_lpSentinel;\r\n    /**ս㣬End() */\r\n    CElement* m_lpNull;\r\n    /**ǰ. öʱʹ. */\r\n    CElement* m_lpCur;\r\n};\r\n\r\n/***\r\n * @brief Ԫ.\r\n*/\r\nclass CElement\r\n{\r\n    friend class CSingleList;\r\nprotected:\r\n    CElement();\r\n    ~CElement();\r\npublic:\r\n    /***\r\n     * @brief ȡָ\r\n    */\r\n    void* GetDataPtr();\r\nprotected:\r\n    /**һ*/\r\n    CElement* m_lpNext;\r\n    void* m_lpData;\r\n};\r\n\r\n\r\nvoid CreateList(CSingleList& lst)\r\n{\r\n    //ѭԪصβ\r\n    for(int i=1; i<10;i++)\r\n    {\r\n        int* p = new int();\r\n        *p = i;\r\n        lst.Insert(p, 4);\r\n    }\r\n}\r\nvoid PrintList(CSingleList& lst)\r\n{\r\n    CElement* lpElement = lst.Begin();\r\n    while(lpElement != lst.End())\r\n    {\r\n        std::cout<<*((int*)lpElement->GetDataPtr())<<std::endl;\r\n        lpElement = lst.Next();\r\n    }\r\n}\r\n\r\nint main()\r\n{\r\n    {\r\n        /// Ļ/ö/ɾ\r\n        CSingleList lst;\r\n        CElement* lpElement = NULL;\r\n        CreateList(lst);\r\n\r\n        std::cout<<\"öǰԪ\"<<std::endl;\r\n        PrintList(lst);\r\n\r\n        std::cout<<\"ָԪ,ָԪغԪ\"<<std::endl;\r\n        lpElement = lst.Begin();\r\n        while(lpElement != lst.End())\r\n        {\r\n            if(*((int*)lpElement->GetDataPtr()) == 5)\r\n            {\r\n                int* p = new int();\r\n                *p = 55;\r\n                lst.Insert(lpElement,p, 4);\r\n                break;\r\n            }else{\r\n                lpElement = lst.Next();\r\n            }\r\n        }\r\n\r\n        std::cout<<\"öǰԪ\"<<std::endl;\r\n        PrintList(lst);\r\n\r\n        std::cout<<\"ָԪ(7Ԫ),ɾָԪ\"<<std::endl;\r\n        lpElement = lst.Begin();\r\n        while(lpElement != lst.End())\r\n        {\r\n            if(*((int*)lpElement->GetDataPtr()) == 7)\r\n            {\r\n                lst.Delete(lpElement);\r\n                break;\r\n            }else{\r\n                lpElement = lst.Next();\r\n            }\r\n        }\r\n        std::cout<<\"öǰԪ\"<<std::endl;\r\n        PrintList(lst);\r\n    }\r\n\r\n    std::cout<<\"--------------------------\"<<std::endl;\r\n    {\r\n        /// ķת\r\n        CSingleList lst;\r\n        CElement* lpElement = NULL;\r\n        CreateList(lst);\r\n        std::cout<<\"ת\"<<std::endl;\r\n        lst.Reverse();\r\n        PrintList(lst);\r\n    }\r\n\r\n    std::cout<<\"--------------------------\"<<std::endl;\r\n    {\r\n        /// ⻷\r\n        CSingleList lst;\r\n        CElement* lpElement = NULL;\r\n        CreateList(lst);\r\n        std::cout<<\"⻷\"<<std::endl;\r\n        bool bRet = lst.CheckCircle();\r\n        if(bRet){\r\n            std::cout<<\"ڻ.\"<<std::endl;\r\n        }else{\r\n            std::cout<<\"ڻ.\"<<std::endl;\r\n        }\r\n    }\r\n\r\n    std::cout<<\"--------------------------\"<<std::endl;\r\n    {\r\n        /// ϲ\r\n        CSingleList lst,lst2;\r\n        CElement* lpElement = NULL;\r\n        for(int i=1; i<30;i++)\r\n        {\r\n            int* p = new int();\r\n            *p = i;\r\n            if(i%4){\r\n                lst2.Insert(p, 4);\r\n            }else{\r\n                lst.Insert(p, 4);\r\n            }\r\n        }\r\n        std::cout<<\"öǰԪ\"<<std::endl;\r\n        PrintList(lst);\r\n        std::cout<<\"......\"<<std::endl;\r\n        PrintList(lst2);\r\n        lst.Merge(lst2,[](void* lpT1, void* lpT2) -> int{\r\n            if(*((int*)lpT1) < *((int*)lpT2)){\r\n                return -1;\r\n            }else if(*((int*)lpT1) == *((int*)lpT2)){\r\n                return 0;\r\n            }else if(*((int*)lpT1) > *((int*)lpT2)){\r\n                return 1;\r\n            }\r\n            return 0;\r\n        });\r\n        std::cout<<\"ϲ֮󣬴ӡǰ.\"<<std::endl;\r\n        PrintList(lst);\r\n    }\r\n    std::cout<<\"--------------------------\"<<std::endl;\r\n    {\r\n        /// ɾK,鿴мڵ\r\n        CSingleList lst;\r\n        CreateList(lst);\r\n        std::cout<<\"ɾ0\"<<std::endl;\r\n        lst.DeleteLastKth(0);\r\n        PrintList(lst);\r\n        CElement* lpCenter = lst.Center();\r\n        std::cout<<\"мڵ:\"<<*((int*)lpCenter->GetDataPtr())<<std::endl;\r\n\r\n        std::cout<<\"ɾ1\"<<std::endl;\r\n        lst.DeleteLastKth(1);\r\n        PrintList(lst);\r\n        lpCenter = lst.Center();\r\n        std::cout<<\"мڵ:\"<<*((int*)lpCenter->GetDataPtr())<<std::endl;\r\n\r\n        std::cout<<\"ɾ3\"<<std::endl;\r\n        lst.DeleteLastKth(3);\r\n        PrintList(lst);\r\n        lpCenter = lst.Center();\r\n        std::cout<<\"мڵ:\"<<*((int*)lpCenter->GetDataPtr())<<std::endl;\r\n    }\r\n    std::cin.ignore();\r\n\r\n    return 0;\r\n}\r\n\r\nCSingleList::CSingleList()\r\n{\r\n    m_lpHead = new CElement();\r\n    m_lpSentinel = new CElement();\r\n    m_lpNull = new CElement();\r\n    m_lpCur = NULL;\r\n    m_lpHead->m_lpNext = m_lpSentinel;\r\n}\r\nCSingleList::~CSingleList()\r\n{\r\n    if(NULL != m_lpSentinel)\r\n    {\r\n        delete m_lpSentinel;\r\n        m_lpSentinel = NULL;\r\n    }\r\n    if(NULL != m_lpNull)\r\n    {\r\n        delete m_lpNull;\r\n        m_lpNull = NULL;\r\n    }\r\n    if(NULL != m_lpHead)\r\n    {\r\n        delete m_lpHead;\r\n        m_lpHead = NULL;\r\n    }\r\n}\r\nCElement* CSingleList::Insert(void* lpData, int iDataSize)\r\n{\r\n    CElement* lpNewElement = new CElement();\r\n    if(NULL == lpNewElement)\r\n    {\r\n        return NULL;\r\n    }\r\n    lpNewElement->m_lpData = lpData;\r\n    Insert(lpNewElement, Tail());\r\n    return lpNewElement;\r\n}\r\nCElement* CSingleList::Insert(CElement* lpElement, void* lpData, int iDataSize)\r\n{\r\n    if((NULL == lpElement) || (End() == lpElement))\r\n    {\r\n        return NULL;\r\n    }\r\n    CElement* lpNewElement = new CElement();\r\n    if(NULL == lpNewElement)\r\n    {\r\n        return NULL;\r\n    }\r\n    lpNewElement->m_lpData = lpData;\r\n    Insert(lpNewElement, lpElement);\r\n    return lpNewElement;\r\n}\r\nvoid CSingleList::Insert(CElement* lpNewElement, CElement* lpCurElement, bool bBack /*= true*/)\r\n{\r\n    if(bBack){//뵽ָԪصĺ\r\n        lpNewElement->m_lpNext = lpCurElement->m_lpNext;\r\n        lpCurElement->m_lpNext = lpNewElement;\r\n    }else{//뵽ָԪصǰ\r\n        CElement* lpIter = m_lpSentinel;\r\n        while(NULL != lpIter)\r\n        {\r\n            if(lpIter->m_lpNext == lpCurElement)\r\n            {\r\n                lpNewElement->m_lpNext = lpIter->m_lpNext;\r\n                lpIter->m_lpNext = lpNewElement;\r\n                break;\r\n            }else{\r\n                lpIter = lpIter->m_lpNext;\r\n            }\r\n        }\r\n    }\r\n}\r\n\r\nvoid CSingleList::Delete(CElement* lpElement)\r\n{\r\n    if((NULL == lpElement) || (End() == lpElement))\r\n    {\r\n        return;\r\n    }\r\n    CElement* lpCurElement = m_lpHead->m_lpNext;\r\n    while(NULL != lpCurElement->m_lpNext)\r\n    {\r\n        if(lpCurElement->m_lpNext == lpElement)\r\n        {\r\n            lpCurElement->m_lpNext = lpCurElement->m_lpNext->m_lpNext;\r\n            break;\r\n        }else{\r\n            lpCurElement = lpCurElement->m_lpNext;\r\n        }\r\n    }\r\n}\r\n\r\nCElement* CSingleList::Tail()\r\n{\r\n    CElement* lpCurElement = m_lpHead->m_lpNext;\r\n    while(NULL != lpCurElement->m_lpNext)\r\n    {\r\n        lpCurElement = lpCurElement->m_lpNext;\r\n    }\r\n    return lpCurElement;\r\n}\r\n\r\nCElement* CSingleList::Begin()\r\n{\r\n    m_lpCur = NULL;\r\n    if(NULL == m_lpHead->m_lpNext->m_lpNext)\r\n    {\r\n        m_lpCur = End();\r\n    }else{\r\n        m_lpCur = m_lpHead->m_lpNext->m_lpNext;\r\n    }\r\n    return m_lpCur;\r\n}\r\n\r\nCElement* CSingleList::Next()\r\n{\r\n    if((NULL == m_lpCur) || (End() == m_lpCur))\r\n    {\r\n        return m_lpCur;\r\n    }\r\n    m_lpCur = m_lpCur->m_lpNext;\r\n    if(NULL == m_lpCur)\r\n    {\r\n        m_lpCur = End();\r\n    }\r\n    return m_lpCur;\r\n}\r\n\r\nCElement* CSingleList::End()\r\n{\r\n    return m_lpNull;\r\n}\r\n\r\nbool CSingleList::Empty()\r\n{\r\n    return Begin() == End();\r\n}\r\n\r\nvoid CSingleList::Reverse()\r\n{\r\n    if(Empty())\r\n    {\r\n        return;\r\n    }\r\n    CElement* lpPre = NULL;\r\n    CElement* lpTmp = NULL;\r\n    CElement* lpCurElement = m_lpSentinel->m_lpNext;\r\n    while(1)\r\n    {\r\n        lpTmp = lpCurElement->m_lpNext;\r\n        lpCurElement->m_lpNext = lpPre;\r\n        if(NULL == lpTmp)\r\n        {\r\n            break;\r\n        }\r\n        lpPre = lpCurElement;\r\n        lpCurElement = lpTmp;\r\n    }\r\n    m_lpSentinel->m_lpNext = lpCurElement;\r\n}\r\n\r\nbool CSingleList::CheckCircle()\r\n{\r\n    if(Empty())\r\n    {\r\n        return false;\r\n    }\r\n    CElement* lpFast = m_lpSentinel->m_lpNext;\r\n    CElement* lpSlow = m_lpSentinel->m_lpNext;\r\n    while ((NULL != lpFast) && (NULL != lpFast->m_lpNext)) \r\n    {\r\n        lpFast = lpFast->m_lpNext->m_lpNext;\r\n        lpSlow = lpSlow->m_lpNext;\r\n        if (lpFast == lpSlow)\r\n        {\r\n            return true;\r\n        }\r\n    }\r\n    return false;\r\n}\r\n\r\n/**\r\n * ϲ2\r\n*/\r\nvoid CSingleList::Merge(CSingleList& lst, std::function<int(void* t1, void* t2)> fnCompare)\r\n{\r\n    CElement* lpL1 = Begin();\r\n    CElement* lpL2 = lst.Begin();\r\n\r\n    if(!fnCompare)\r\n    {\r\n        return;\r\n    }\r\n    int iRet = 0;\r\n    while((lpL2 != lst.End()))\r\n    {\r\n        if(lpL1 != End())\r\n        {\r\n            /**\r\n             * Ҫȷλ\r\n             * \r\n             * 1,2; 1 <- 2, 2ϲ1\r\n             * \r\n             * 1ԪС2еԪأѭ1д2еĵǰԪصԪ\r\n             * 1ҵĵԪλ[A]ʱ2еĵǰԪز뵽Ԫλ[A]ǰ;\r\n             * 1вλ1ĩλԪ\r\n            */\r\n            iRet = fnCompare(lpL1->GetDataPtr(), lpL2->GetDataPtr());\r\n            if(iRet < 0){\r\n                lpL1 = Next();\r\n                while(lpL1 != End()){\r\n                    iRet = fnCompare(lpL1->GetDataPtr(), lpL2->GetDataPtr());\r\n                    if(iRet > 0){\r\n                        break;\r\n                    }\r\n                    lpL1 = Next();\r\n                }\r\n            }\r\n        }else{\r\n            iRet = -1;\r\n        }\r\n        CElement* lpNewElement = new CElement();\r\n        if(NULL != lpNewElement)\r\n        {\r\n            lpNewElement->m_lpData = lpL2->GetDataPtr();\r\n            if(lpL1 != End())\r\n            {\r\n                Insert(lpNewElement,lpL1, iRet < 0);\r\n            }else{\r\n                CElement* lpTail = Tail();\r\n                Insert(lpNewElement,lpTail);\r\n            }\r\n        }\r\n        lpL2 = lst.Next();\r\n    }\r\n}\r\n\r\nvoid CSingleList::DeleteLastKth(int k)\r\n{\r\n    int i = 1;\r\n    if(k <= 0)\r\n    {\r\n        return;\r\n    }\r\n    CElement* lpFast = Begin();\r\n    while((lpFast != End()) && (i < k))\r\n    {\r\n        lpFast = Next();\r\n        ++i;\r\n    }\r\n    if (lpFast == End())\r\n    {\r\n        return;\r\n    }\r\n    CElement* lpSlow = Begin();\r\n    CElement* lpPrev = NULL;\r\n    while (NULL != lpFast->m_lpNext)\r\n    {\r\n        lpFast = lpFast->m_lpNext;\r\n        lpPrev = lpSlow;\r\n        lpSlow = Next();\r\n    }\r\n    if(NULL != lpPrev)\r\n    {\r\n        lpPrev->m_lpNext = lpPrev->m_lpNext->m_lpNext;\r\n    }\r\n}\r\n\r\nCElement* CSingleList::Center()\r\n{\r\n    CElement* lpFast = Begin();\r\n    CElement* lpSlow = lpFast;\r\n    while((NULL != lpFast->m_lpNext) && (NULL != lpFast->m_lpNext->m_lpNext))\r\n    {\r\n        lpFast = lpFast->m_lpNext->m_lpNext;\r\n        lpSlow = lpSlow->m_lpNext;\r\n    }\r\n    return lpSlow;\r\n}\r\n\r\nCElement::CElement()\r\n{\r\n    m_lpNext = NULL;\r\n    m_lpData = NULL;\r\n}\r\nCElement::~CElement()\r\n{\r\n\r\n}\r\n\r\nvoid* CElement::GetDataPtr()\r\n{\r\n    return m_lpData;\r\n}"
  },
  {
    "path": "c-cpp/07_linkedlist/linked_list.h",
    "content": "/**\n * 单链表\n *\n * Author: Liam Huang (Liam0205)\n */\n\n#ifndef LINKEDLIST_LINKED_LIST_H_\n#define LINKEDLIST_LINKED_LIST_H_\n\n#include <memory>\n\ntemplate <typename T>\nstruct Node {\n    using ptr_t = std::shared_ptr<Node<T>>;\n    T     data;\n    ptr_t next;\n\n    Node(T data_) : data(data_), next(nullptr) {}\n    Node() : next(nullptr) {}\n};\n\n#endif  // LINKEDLIST_LINKED_LIST_H_\n"
  },
  {
    "path": "c-cpp/07_linkedlist/linked_list_algo.hpp",
    "content": "/**\n * 0) 遍历单链表\n * 1) 单链表反转\n * 2) 链表中环的检测\n * 3) 两个有序的链表合并\n * 4) 删除链表倒数第n个结点\n * 5) 求链表的中间结点\n *\n * Author: Liam Huang (Liam0205)\n */\n\n#ifndef LINKEDLIST_LINKED_LIST_ALGO_HPP_\n#define LINKEDLIST_LINKED_LIST_ALGO_HPP_\n\n#include <memory>\n\n#include \"linked_list.h\"\n\ntemplate <typename T, typename UnaryFunc>\nvoid traverse(typename Node<T>::ptr_t head, UnaryFunc do_traverse) {\n    auto sentry  = std::make_shared<Node<T>>();\n    sentry->next = head;\n    decltype(sentry) work = sentry;\n    while (work = work->next) {\n        do_traverse(work);\n    }\n}\n\ntemplate <typename T>\ntypename Node<T>::ptr_t reverse(typename Node<T>::ptr_t head) {\n    if (nullptr == head or nullptr == head->next) {\n        return head;\n    }\n    decltype(head) prev = nullptr, curr = head, next = head->next;\n    while (nullptr != next) {\n        curr->next = prev;\n        prev = curr;\n        curr = next;\n        next = curr->next;\n    }\n    curr->next = prev;\n    return curr;\n}\n\ntemplate <typename T>\nbool check_circle(typename Node<T>::ptr_t head) {\n    if (nullptr == head or nullptr == head->next) {\n        return false;\n    }\n    decltype(head) slow = head, fast = head;\n    while (nullptr != fast and nullptr != fast->next) {\n        slow = slow->next;\n        fast = fast->next->next;\n        if (slow == fast) {\n            return true;\n        }\n    }\n    return false;\n}\n\ntemplate <typename T>\ntypename Node<T>::ptr_t merge_two_sorted_lists(typename Node<T>::ptr_t lhs,\n                                               typename Node<T>::ptr_t rhs) {\n    if (nullptr == lhs) { return rhs; }\n    if (nullptr == rhs) { return lhs; }\n\n    decltype(lhs) l = lhs, r = rhs, head = nullptr, work = nullptr;\n\n    if (l->data < r->data) {\n        head = l;\n        l = l->next;\n    } else {\n        head = r;\n        r = r->next;\n    }\n    work = head;\n\n    while (nullptr != l and nullptr != r) {\n        if (l->data < r->data) {\n            work->next = l;\n            l = l->next;\n        } else {\n            work->next = r;\n            r = r->next;\n        }\n        work = work->next;\n    }\n\n    if (nullptr != l) {\n        work->next = l;\n    } else {\n        work->next = r;\n    }\n\n    return head;\n}\n\ntemplate <typename T>\ntypename Node<T>::ptr_t deleth_last_Kth(typename Node<T>::ptr_t head,\n                                                         size_t n) {\n    decltype(head) sentry = std::make_shared<Node<T>>();\n    sentry->next = head;\n    decltype(head) prev = sentry, curr = sentry->next, fast = sentry->next;\n    for (size_t i = 0; i != n; ++i) {\n        if (nullptr != fast) {\n            fast = fast->next;\n        } else {\n            return sentry->next;\n        }\n    }\n    while (nullptr != fast) {\n        prev = curr;\n        curr = curr->next;\n        fast = fast->next;\n    }\n    prev->next = curr->next;\n    return sentry->next;\n}\n\ntemplate <typename T>\ntypename Node<T>::ptr_t find_middle_node(typename Node<T>::ptr_t head) {\n    if (nullptr == head or nullptr == head->next) {\n        return head;\n    }\n    decltype(head) slow = head, fast = head;\n    while (nullptr != fast and nullptr != fast->next) {\n        slow = slow->next;\n        fast = fast->next->next;\n        if (slow == fast) {\n            return nullptr;\n        }\n    }\n    return slow;\n}\n\n#endif  // LINKEDLIST_LINKED_LIST_ALGO_HPP_\n"
  },
  {
    "path": "c-cpp/07_linkedlist/linklist_jinshaohui.c",
    "content": "/*************************************************************************\n > File Name: lisklist.c\n > Author:  jinshaohui\n > Mail:    jinshaohui789@163.com\n > Time:    18-10-07\n > Desc:    \n ************************************************************************/\n#include<stdio.h>\n\n\nstruct stlistNode\n{\n\tint val;\n\tstruct listNode *next;\n}listNode;\n\n/*反转链表*/\nlistNode reverseList(listNode *head)\n{\n\tlistNode *prev = NULL;\n\tlistNode *next = NULL;\n\n\twhile(head != NULL)\n\t{\n\t\tnext = head->next;\n\t\thead->next = prev;\n\t\tprev = head;\n\t\thead = next;\n\t}\n\n\treturn prev;\n}\n\n/*判断链表是否有环*/\nint hasCycle(listNode *head)\n{\n\tlistNode * fast = head;\n\tlistNode * low = head;\n\n\twhile(fast != NULL && fast->next != NULL)\n\t{\n\t\tlow = low->next;\n\t\tfast = fast->next->next;\n\t\tif (low == fast)\n\t\t{\n\t\t\treturn 1;\n\t\t}\n\t}\n\n\treturn 0;\n}\n/*合并有序链表*/\nlistNode *mergeTwoLists(listNode *l1,listNode *l2)\n{\n\tlistNode head = {0};\n\tlistNode *pRes = &head;\n\n\twhile(1)\n\t{\n\t\tif(l1 == NULL)\n\t\t{\n\t\t\tpRes->next = l2;\n\t\t}\n\n\t\tif (l2 == NULL)\n\t\t{\n\t\t\tpRes->next = l1;\n\t\t}\n\n\t\tif(l1->val < l2->val)\n\t\t{\n\t\t\tpRes->next = l1;\n\t\t\tl1 = l1->next;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tpRes->next = l2;\n\t\t\tl2 = l2->next;\n\t\t}\n\t\tpRes = pRes->next;\n\t}\n\n\treturn head;\n}\n/*\n *删除链表倒数第n个节点，并返回链表头节点 */\n\nlistNode * removeNthFromEnd(listNode*headi,int n)\n{\n\tlistNode *fast = head;\n\tlistNode *prev = NULL;\n\tlistNpde *next = head;\n\tint k = n;\n\t\n\t/*快指针往后移动k-1*/\n\twhile((k > 1) && (fast != NULL))\n\t{\n\t\tfast = fast->next;\n\t\tk--;\n\t}\n\t\n\t/*说明链表数目不足n个*/\n\tif (fast == NULL)\n\t{\n\t\treturn head;\n\t}\n\n\twhile (fast->next != NULL)\n\t{\n\t\tfast = fast->next;\n\t\tprev = next;\n\t\tnext = next->next;\n\t}\n\n\tif(prev == NULL)\n\t{\n\t\thead = head->next;\n\t}\n\telse\n\t{\n\t\tprev->next = prev->next->next;\n\t}\n\n\treturn head;\n}\n/*求链表的中间节点*/\nlistNode *middleNode(listNode *head)\n{\n\tlistNode * fast = head;\n\tlistNode * low = head;\n\n\twhile(fast != NULL && fast->next != NULL)\n\t{\n\t\tlow = low->next;\n\t\tfast = fast->next->next;\n\t}\n\n\treturn low;\n}\n"
  },
  {
    "path": "c-cpp/08_stack/StackBasedOnArray/StackBasedOnArray.cpp",
    "content": "/**\n * 1）顺序栈的操作：入栈和出栈；\n * 2）采用模板的方法实现存储任意类型的数据\n * 3）采用数组的栈，支持动态扩容，每次扩容1.5 倍，初始栈的大小是 10 。\n * \n * Author：caozx\n * time ；2018年10月11日\n */\n\n#include <iostream>\n#include \"StackBasedOnArray.h\"\nusing namespace std;\n\n//构造函数，创建栈\n//类模板成员函数的写法  template<class T> 返回值类型 类名<T>::成员函数名(参数列表){}\ntemplate<class T> ArrayStack<T>::ArrayStack()   \n{   \n    this -> count = 10;\n    this -> flag = 0;\n    this -> array = new T[this -> count];\n    if (! this -> array){\n        cout << \"array malloc memory failure\" << endl;\n    }\n}\n\n\n//有参构造函数，创建栈\ntemplate<class T> ArrayStack<T>::ArrayStack(int count)   \n{   \n    this -> count = count;\n    this -> flag = 0;\n    this -> array = new T[this -> count];\n    if (! this -> array){\n        cout << \"array malloc memory failure\" << endl;\n    }\n}\n\n//析构函数，销毁栈\ntemplate <class T> ArrayStack<T>::~ArrayStack(){\n    this -> count = 0;\n    this -> flag = 0;\n    if(this -> array){\n        delete [] this -> array;\n        this -> array = NULL;\n    }\n    \n} \n\n// 入栈\ntemplate<class T> void ArrayStack<T>::push(T data){\n    if(this -> flag == this -> count){\n        cout << \"The stack is full , so need to enlarge 1.5x! \"<< endl;\n        this -> count = int (1.5 * this -> count);\n        T * temp = new T [this -> count];\n        for(int i = 0; i < this -> flag ; i++){\n            temp[i] = this -> array[i];\n            //cout << temp[i] <<endl;\n        }\n        delete [] this -> array;       //释放原来的空间\n        temp[this -> flag] = data;\n        this -> flag ++;\n        this -> array = temp;\n    }\n    else{\n        this -> array [this -> flag] = data;\n        this -> flag ++ ;\n    }\n}\n\n//出栈，并删除栈顶元素\ntemplate<class T> T ArrayStack<T>::pop(){\n    this -> flag --;\n    T temp = this -> array[this -> flag];\n    return temp;\n}\n\n//出栈，不删除栈顶元素\ntemplate<class T> T ArrayStack<T>::peek(){\n    T temp = this -> array[this -> flag - 1];\n    return temp;\n}\n\ntemplate<class T> int ArrayStack<T>::stackSize(){\n    return this -> flag;\n}\n\ntemplate<class T> int ArrayStack<T>::stackMaxSize(){\n    return this -> count;\n}\n\nint main(int argc, char const *argv[])\n{\n    cout << \" === test begin ===\" << endl;\n    ArrayStack <int> arrstack(12);\n    arrstack.push(10);\n    arrstack.push(20);\n    arrstack.push(30);\n    arrstack.push(40);\n    arrstack.push(50);\n    arrstack.push(60);\n    arrstack.push(70);\n    arrstack.push(80);\n    arrstack.push(90);\n    arrstack.push(100);\n    arrstack.push(110);\n    arrstack.push(120);\n    arrstack.push(130);\n    arrstack.push(140);\n    arrstack.push(150);\n\n    cout << \"peek , not delete \" << arrstack.peek() << endl;\n    cout << \"pop , delete \" << arrstack.pop()<<endl;\n    \n    arrstack.push(210);\n    arrstack.push(220);\n\n    cout << \"peek , not delete \" << arrstack.peek() << endl;\n    cout << \"pop , delete \" << arrstack.pop()<<endl;    \n   \n    system(\"pause\");\n    return 0;\n}\n"
  },
  {
    "path": "c-cpp/08_stack/StackBasedOnArray/StackBasedOnArray.h",
    "content": "\n// 类模板的声明(line 3)，类模板实例化后就是模板类\n// 类模板声明的写法   template <class T> class 类名{}\ntemplate <class T> class ArrayStack\n{\npublic:\n    ArrayStack();\n    ArrayStack(int count);\n    ~ArrayStack();\n    void push(T data);     //入栈\n    T pop();      //出栈，并删除栈顶元素\n    T peek();     //返回栈顶元素，不删除栈顶元素，栈顶指针不变\n    int stackSize();\n    int stackMaxSize();\n\nprivate:\n    int flag;       //栈顶标签，指向栈顶\n    int count ;     //栈的容量\n    T *array;       //指针\n};\n"
  },
  {
    "path": "c-cpp/08_stack/StackBasedOnLinkedList/StackBasedOnLinkedList.cpp",
    "content": "/**\n * 1）链式栈的操作：入栈，出栈以及返回栈的大小；\n * 2）采用模板的方法实现存储任意类型的数据\n * 3）采用单链表实现栈\n * 4）pop和peek 出栈的返回值稍微有点问题，当栈为空的时候，返回null，cpp默认返回的是0。\n *    * 改进方法就是不用函数的返回值返回栈顶元素，而是采用参数列表的形式返回，这样稍微有点麻烦\n *    * 或者就是在使用的时候先调用size函数判断以下\n * Author：caozx\n * time ；2018年10月11日\n */\n\n#include <iostream>\n#include \"StackBasedOnLinkedList.h\"\nusing namespace std;\n\ntemplate<class T> LinkedListStack<T>::LinkedListStack()\n{\n    this -> count = 0;\n    this -> head = new LinkedNode;\n    this -> head -> next = NULL;\n}\n\ntemplate<class T> LinkedListStack<T>::~LinkedListStack()\n{\n    LinkedNode * ptr, * temp;\n    ptr = head;\n    while(ptr -> next != NULL){\n        temp = ptr -> next;\n        ptr -> next = temp -> next;\n        delete temp;   \n    }\n    delete head ; //删除头节点\n    this -> head = NULL;\n    this -> count = 0;\n}\n\n// 入栈\ntemplate<class T> void LinkedListStack<T>::push(const T & data)\n{\n    LinkedNode * insertPtr = new LinkedNode;\n    insertPtr -> data = data;\n    insertPtr -> next = this -> head -> next;\n    head -> next = insertPtr;\n    this -> count ++;\n    cout << \"push data : \" << this -> head -> next -> data << endl;  \n}\n\n//返回栈顶元素，即出栈，不删除栈顶元素\ntemplate<class T> T LinkedListStack<T>::peek()\n{\n    if(this -> count == 0 || this -> head -> next == NULL){\n        cout << \" stack is empty, peek fail\"<< endl;\n        return NULL;\n    }\n    else{\n        return this -> head -> next -> data;\n    }\n}\n\n//出栈，删除栈顶元素\ntemplate<class T> T LinkedListStack<T>::pop()\n{\n    if(this -> count == 0 || this -> head -> next == NULL){\n        cout << \" stack is empty, pop fail\"<< endl;\n        return NULL;\n    }\n    else{\n        LinkedNode * temp = this -> head -> next;\n        this -> head -> next = temp -> next;\n        T data = temp -> data;\n        delete temp;\n        this -> count --;\n        return  data;\n    }\n\n}\n\n//返回栈的大小\ntemplate<class T> int LinkedListStack<T>::size() const\n{\n    return this -> count;\n}             \n\nint main(int argc, char const *argv[])\n{\n    cout << \" === StackBasedOnLinkedList test begin ===\" << endl;\n    LinkedListStack <float> stack;\n    cout << \"size===\"<<stack.size()<<endl;\n    stack.push(10.1);\n    stack.push(20.2);\n    stack.push(30.);\n    stack.push(40.4);\n    stack.push(50.5);\n    stack.push(60.6);\n    cout << \"size===\"<<stack.size()<<endl;\n    cout << \"stack peek  \" << stack.peek() << endl;\n    cout << \"stack pop  \" << stack.pop() << endl;\n    cout << \"size===\"<<stack.size()<<endl;\n    cout << \"stack pop  \" << stack.pop() << endl;\n    cout << \"stack pop  \" << stack.pop() << endl;\n    cout << \"stack pop  \" << stack.pop() << endl;\n    cout << \"stack pop  \" << stack.pop() << endl;\n    cout << \"stack pop  \" << stack.pop() << endl;\n    cout << \"size===\"<<stack.size()<<endl;\n    cout << \"stack pop  \" << stack.pop() << endl;\n    cout << \"stack peek  \" << stack.peek() << endl;\n    stack.push(110.1);\n    stack.push(120.2);\n    stack.push(130.3);\n    stack.push(140.4);\n    stack.push(150.5);\n    stack.push(160.6);\n    cout << \"size===\"<<stack.size()<<endl;\n    cout << \"stack peek  \" << stack.peek() << endl;\n    cout << \"stack pop  \" << stack.pop() << endl;\n    cout << \"stack pop  \" << stack.pop() << endl;\n    cout << \"stack pop  \" << stack.pop() << endl;\n    cout << \"stack peek  \" << stack.peek() << endl;   //peek\n    cout << \"stack pop  \" << stack.pop() << endl;\n    cout << \"stack pop  \" << stack.pop() << endl;\n    cout << \"stack pop  \" << stack.pop() << endl;\n    cout << \"size===\"<<stack.size()<<endl;\n    cout << \"stack peek  \" << stack.peek() << endl;   //peek\n    cout << \"stack pop  \" << stack.pop() << endl;    \n    system(\"pause\");\n    return 0;\n}\n"
  },
  {
    "path": "c-cpp/08_stack/StackBasedOnLinkedList/StackBasedOnLinkedList.h",
    "content": "// 类模板的声明，关键字 class 也可以更换成 typename\ntemplate<class T> class LinkedListStack\n{\npublic:\n    LinkedListStack();\n    ~LinkedListStack();\n\n    void push(const T & data);     //入栈\n    T peek();                    //返回栈顶元素，即出栈，不删除栈顶元素\n    T pop();                      //出栈，删除栈顶元素\n    int size() const;             //返回栈的大小\nprivate:\n    int count;                   //存放栈的大小，因为是单链表所以这里不规定栈的最大可承载量\n    struct LinkedNode\n    {\n        T data;\n        LinkedNode * next;\n    };\n    LinkedNode * head;    // 单链表的头指针，不带头节点\n};\n"
  },
  {
    "path": "c-cpp/08_stack/arrayStack/arrayStack.c",
    "content": "/*************************************************************************\n > File Name: arrayStack.c\n > Author:  jinshaohui\n > Mail:    jinshaohui789@163.com\n > Time:    18-10-12\n > Desc:   数组实现顺序栈 \n ************************************************************************/\n#include<stdio.h>\n#include<stdlib.h>\n#include<string.h>\n#include\"./arrayStack.h\"\n\n/*创建并初始化顺序栈*/\nstArrayStack * arrayStack_create(int size)\n{\n\tstArrayStack *parrStack = NULL;\n\n\tparrStack = (stArrayStack *)malloc(sizeof(stArrayStack));\n\tif (parrStack == NULL)\n\t{\n\t\treturn NULL;\n\t}\n\t\n\tparrStack->size = size;\n\tparrStack->pos = -1;\n\tparrStack->array = (int *)malloc(sizeof(int)*size);\n\tif(parrStack->array == NULL)\n\t{\n\t\tfree(parrStack);\n\t\treturn NULL;\n\t}\n\n\treturn parrStack;\n}\n/*销毁顺序栈*/\nvoid arrayStack_destory(stArrayStack * parrStack)\n{\n\tif(parrStack == NULL)\n\t{\n\t\treturn;\n\t}\n\n\tif (parrStack->array != NULL)\n\t{\n\t\tfree(parrStack->array);\n\t}\n\n\tfree(parrStack);\n\treturn;\n}\n/*出栈*/\nint arrayStack_pop(stArrayStack *parrStack)\n{\n\tint data = 0;\n\n\tif(arrayStack_is_empty(parrStack))\n\t{\n\t\treturn -1;\n\t}\n    data = parrStack->array[parrStack->pos];\n\tparrStack->pos--;\n\n\treturn data;\n}\n/*入栈*/\nint arrayStack_push(stArrayStack *parrStack,int data)\n{\n\tif(arrayStack_is_full(parrStack))\n\t{\n\t\treturn -1;\n\t}\n\n    parrStack->pos++;\n\tparrStack->array[parrStack->pos] = data;\n\n\treturn 0;\n}\n\nint arrayStack_push_new(stArrayStack*parrStack,int data)\n{\n\tint *ptmp = NULL;\n\n\t/*如果栈不满，直接插入*/\n\tif(!arrayStack_is_full(parrStack))\n\t{\n\t\treturn arrayStack_push(parrStack,data);\n\t}\n\n\t/*如果栈已经满，申请内存*/\n    ptmp = (int *)malloc(2*parrStack->size*sizeof(int));\n\tif (ptmp == NULL)\n\t{\n\t\treturn -1;\n\t}\n\n\tmemcpy(ptmp,parrStack->array,parrStack->size*sizeof(int));\n\n\tfree(parrStack->array);\n\n    parrStack->array = ptmp;\n\tparrStack->size = 2*parrStack->size;\n\tparrStack->pos++;\n    parrStack->array[parrStack->pos] = data;\n\n\treturn ;\n}\n\nvoid arrayStack_dump(stArrayStack *parrStack)\n{\n\tint i = 0;\n\n\tif (arrayStack_is_empty(parrStack))\n\t{\n\t\tprintf(\"\\r\\n arrayStack is empty.\");\n\t\treturn;\n\t}\n\tprintf(\"\\r\\narrayStack size = %d,pos= %d,\",\n\t\t\tparrStack->size,parrStack->pos);\n\tfor(i = 0; i <= parrStack->pos; i++)\n\t{\n\t\tprintf(\"\\r\\narry[%d] = %d\",i,parrStack->array[i]);\n\t}\n}\n\nint main()\n{\n\tint i = 0;\n\tint ret = 0;\n\tstArrayStack * parrStack = NULL;\n\n\tprintf(\"\\r\\n create size = 4 arrayStack.\");\n\n\tparrStack = arrayStack_create(4);\n\tif (parrStack == NULL)\n\t{\n\t    printf(\"\\r\\n create size = 4 arrayStack faided.\");\n\t\treturn 0;\n\t}\n\n\tfor (i = 0; i < 5; i++)\n\t{\n\t\tret = arrayStack_push(parrStack,i);\n\t\tif(ret != 0)\n\t\t{\n\t        printf(\"\\r\\n push size = %d arrayStack faided.\",i);\n\n\t\t}\n\t}\n\tarrayStack_dump(parrStack);\n\t\t\n\tret = arrayStack_push_new(parrStack,4);\n\tif(ret != 0)\n\t{\n\t        printf(\"\\r\\n push size = %d arrayStack faided.\",4);\n    }\n\tarrayStack_dump(parrStack);\n\n\tarrayStack_destory(parrStack);\n    \n\treturn;\n}\n"
  },
  {
    "path": "c-cpp/08_stack/arrayStack/arrayStack.h",
    "content": "/*************************************************************************\n > File Name: arrayStack.h\n > Author:  jinshaohui\n > Mail:    jinshaohui789@163.com\n > Time:    18-10-12\n > Desc:    \n ************************************************************************/\n\n#ifndef ARRAY_STACJ_H\n#define ARRAY_STACJ_H\n\ntypedef struct _array_stack\n{\n\tint size;/*栈的大小*/\n\tint pos;/*当前存储元素的个数，即栈顶元素下表*/\n\tint *array;/*数据存储区*/\n}stArrayStack;\n\n#define arrayStack_size(arrayStack) (arrayStack->size)\n#define arrayStack_is_empty(arrayStack) (arrayStack->pos == -1)\n#define arrayStack_is_full(arrayStack)  (arrayStack->pos == (arrayStack->size-1))\n\n#endif\n"
  },
  {
    "path": "c-cpp/08_stack/linkList/linklist_stack.c",
    "content": "/*************************************************************************\n > File Name: linklist_stack.c\n > Author:  jinshaohui\n > Mail:    jinshaohui789@163.com\n > Time:    18-10-12\n > Desc:    \n ************************************************************************/\n#include<stdio.h>\n#include<stdlib.h>\n#include<string.h>\n#include \"./linklist_stack.h\"\n\nlinklist_stack * stack_create()\n{\n\tlinklist_stack * stack = NULL;\n\n\tstack = (linklist_stack *)malloc(sizeof(linklist_stack));\n\tif (stack == NULL)\n\t{\n\t\treturn NULL;\n\t}\n\n\tstack->next = NULL;\n\n\treturn stack;\n}\n\nvoid stack_destory(linklist_stack* stack)\n{\n\tlinklist_stack * ptmp = NULL;\n\n\twhile(!stack_is_empty(stack))\n\t{\n\t\tptmp = stack->next;\n\t\tstack->next = stack->next->next;\n\n\t\tfree(ptmp);\n\t}\n\n\tfree(stack);\n\n\treturn;\n}\n\nint stack_push(linklist_stack *stack,int data)\n{\n\tlinklist_stack * ptmp = NULL;\n\n\tptmp = (linklist_stack *)malloc(sizeof(linklist_stack));\n\tif (ptmp == NULL)\n\t{\n\t\treturn -1;\n\t}\n\n\tptmp->data = data;\n\tptmp->next = stack->next;\n\tstack->next = ptmp;\n\n\treturn 0;\n}\n\nint stack_pop(linklist_stack*stack,int *data)\n{\n\tlinklist_stack *ptmp = NULL;\n\tif (data == NULL)\n\t{\n\t\treturn -1;\n\t}\n\tif(stack_is_empty(stack))\n\t{\n\t\treturn -1;\t\t\n\t}\n\t*data = stack->next->data;\n    ptmp = stack->next;\n\tstack->next = ptmp->next;\n\tfree(ptmp);\n\n    return 0;\n}\n\n\nvoid stack_dump(linklist_stack *stack)\n{\n\tlinklist_stack * ptmp = stack->next;\n\t\n\twhile(ptmp != NULL)\n\t{\n\t\tprintf(\"\\r\\n data = %d\",ptmp->data);\n\t\tptmp = ptmp->next;\n\t}\n\treturn;\n}\n\nint main()\n{\n\tint i = 0;\n\tint ret = 0;\n\tint data = 0;\n\tlinklist_stack * stack = NULL;\n\n    stack = stack_create();\n\tif (stack == NULL)\n\t{\n\t\tprintf(\"\\r\\n stack create falied.\");\n        return 0;\t\t\t\n\t}\n    \n\tfor (i = 0; i < 4; i++)\n\t{\n\t\tret = stack_push(stack,i);\n\t\tif(ret != 0)\n\t\t{\n\t\t    printf(\"\\r\\n stack push %d falied.\",i);\n\t\t}\n\t}\n\n    stack_dump(stack);\n\n\tfor (i = 0; i < 5; i++)\n\t{\n\t\tret = stack_pop(stack,&data);\n\t\tif(ret != 0)\n\t\t{\n\t\t    printf(\"\\r\\n stack pop%d falied.\", i);\n\t\t}\n\t\telse\n\t\t{\n\t\t    printf(\"\\r\\n data = %d,\",data);\n\t\t}\n\t}\n\n\tstack_destory(stack);\n\n\treturn 0;\n\n}\n"
  },
  {
    "path": "c-cpp/08_stack/linkList/linklist_stack.h",
    "content": "/*************************************************************************\n > File Name: linklist_stack.h\n > Author:  jinshaohui\n > Mail:    jinshaohui789@163.com\n > Time:    18-10-12\n > Desc:    \n ************************************************************************/\n\n#ifndef STACK_LINK_LIST_H\n#define STACK_LINK_LIST_H\n\ntypedef struct _linkliststack\n{\n\tint data;\n\tstruct _linkliststack *next;\n}linklist_stack;\n\n\n#define stack_is_empty(liststack) (liststack->next == NULL)\n\n#endif\n\n"
  },
  {
    "path": "c-cpp/08_stack/linked_list.h",
    "content": "/**\n * C++ 版本单链表结点\n *\n * Author: Liam Huang (Liam0205)\n */\n\n#ifndef STACK_LINKED_LIST_H_\n#define STACK_LINKED_LIST_H_\n\n#include <memory>\n\ntemplate <typename T>\nstruct Node {\n    using ptr_t = std::shared_ptr<Node<T>>;\n    T     data;\n    ptr_t next;\n\n    Node(T data_) : data(data_), next(nullptr) {}\n    Node() : next(nullptr) {}\n};\n\n#endif\n"
  },
  {
    "path": "c-cpp/08_stack/stack_based_on_linked_list.hpp",
    "content": "/**\n * 基于链表实现的栈。\n *\n * Author: Liam Huang (Liam0205)\n */\n\n#ifndef STACK_STACK_BASED_ON_LINKED_LIST_HPP_\n#define STACK_STACK_BASED_ON_LINKED_LIST_HPP_\n\n#include <memory>\n#include \"linked_list.h\"\n\ntemplate <typename T>\nclass Stack {\n  public:\n    using value_type = T;\n    using node_type  = typename Node<value_type>::ptr_t;\n\n  private:\n    node_type top_   = nullptr;\n\n  public:\n    bool empty(void) const {\n        return nullptr == top_;\n    }\n    void push(const value_type& value) {\n        auto node = std::make_shared<node_type>(value);\n        if (this->empty()) {\n            top_ = node;\n        } else {\n            node->next = top_;\n            top_ = node;\n        }\n    }\n    value_type top(void) const {\n        if (not this->empty()) {\n            return top_->data;\n        } else {\n            throw \"Fetch data from empty stack!\";\n        }\n    }\n    void pop(void) {\n        if (not this->empty()) {\n            top_ = top_->next;\n            return;\n        } else {\n            throw \"Pop from empty stack!\";\n        }\n    }\n};\n\n#endif\n"
  },
  {
    "path": "c-cpp/09_queue/.gitkeep",
    "content": ""
  },
  {
    "path": "c-cpp/09_queue/array_queue/array_queue.c",
    "content": "/*************************************************************************\n > File Name: array_queue.c\n > Author:  jinshaohui\n > Mail:    jinshaohui789@163.com\n > Time:    18-10-12\n > Desc:    \n ************************************************************************/\n#include<stdio.h>\n#include<string.h>\n#include<stdlib.h>\n#include\"./array_queue.h\"\n\n\narray_queue * array_queue_create(int size)\n{\n\tarray_queue * queue = NULL;\n\n\tqueue = (array_queue*)malloc(sizeof(array_queue));\n\tif (queue == NULL)\n\t{\n\t\treturn NULL;\n\t}\n\tqueue->array = (int *)malloc(sizeof(int)*size);\n\tif (queue->array == NULL)\n\t{\n        free(queue);\n\t\treturn NULL;\n\t}\n\tqueue->size  = size;\n\tqueue->num   = 0;\n\tqueue->head  = 0;\n\tqueue->tail  = 0;\n\n\treturn queue;\n}\n\nvoid array_queue_destory(array_queue *queue)\n{\n    if (queue == NULL)\n\t{\n\t\treturn;\n\t}\n\n\tif (queue->array != NULL)\n\t{\n\t\tfree(queue->array);\n\t}\n\n\tfree(queue);\n\treturn;\n}\n\n/*入队列 */\nint array_queue_enqueue(array_queue *queue,int data)\n{\n\t/*队列为空，或者队列满时，返回-1*/\n\tif ((queue == NULL) || (array_queue_is_full(queue)))\n\t{\n\t\treturn -1;\n\t}\n\n\tqueue->num++;\n\tqueue->array[queue->tail] = data;\n\tqueue->tail = (queue->tail + 1) % queue->size;\n\n\treturn 0;\n}\n\n/*出队列*/\nint array_queue_dequeue(array_queue * queue,int *data)\n{\n\t/*队列为空，数据存储为空，队列为空时返回-1*/\n\tif ((queue == NULL) || (data == NULL) || (array_queue_is_empty(queue)))\n\t{\n\t\treturn -1;\n\t}\n    *data = queue->array[queue->head];\n\tqueue->num--;\n\tqueue->head = (queue->head + 1) % queue->size;\n\n    return 0;\n}\n\nvoid array_queue_dump(array_queue *queue)\n{\n\tint i = 0;\n\tint pos = 0;\n\n\tif ((queue == NULL) || (array_queue_is_empty(queue)))\n\t{\n\t\tprintf(\"\\r\\n queue is empty\");\n\t\treturn;\n\t}\n\n\tprintf(\"\\r\\n size:%d,num:%d,head:%d,tali:%d\",\n\t\t\tqueue->size,queue->num,queue->head,queue->tail);\n\tfor (i = 0; i < queue->num; i ++)\n\t{\n\t\tpos = (queue->head + i) %queue->size;\n\t\tprintf(\"\\r\\n array[%d] = %d\",pos,queue->array[pos]);\n\t}\n    return;\n}\n\nint main()\n{\n\tint i = 0;\n\tint ret = 0;\n\tint data = 0;\n\tarray_queue * queue = NULL;\n\n\tqueue = array_queue_create(4);\n\tif (queue == NULL)\n\t{\n\t\tprintf(\"\\r\\n queue is create failed.\");\n\t\treturn 0;\n\t}\n\t/*队列时空时，出队返回错误*/\n\tret = array_queue_dequeue(queue, &data);\n\tif (ret != 0)\n\t{\n\t\t    printf(\"\\r\\n queue %d dequeue failed.\",ret);\n\t}\n\n\t/*队列大小是4，入队5个，最后一个报错*/\n\tfor (i = 0; i < 5; i++)\n\t{\n\t\tret = array_queue_enqueue(queue,i);\n\t\tif (ret != 0)\n\t\t{\n\t\t    printf(\"\\r\\n queue %d enqueue failed.\",i);\n\n\t\t}\n\t}\n\n\tarray_queue_dump(queue);\n    \n\tret = array_queue_dequeue(queue, &data);\n\tif (ret != 0)\n\t{\n\t\t    printf(\"\\r\\n queue %d dequeue failed.\",i);\n\t}\n\tprintf(\"\\r\\n queue %d dequue.\",data);\n\tarray_queue_dump(queue);\n\tdata = 5;\n\tprintf(\"\\r\\n queue %d enqueue.\",data);\n\tret = array_queue_enqueue(queue,data);\n\tif (ret != 0)\n\t{\n\t\tprintf(\"\\r\\n queue %d enqueue failed.\",data);\n    }\n\tarray_queue_dump(queue);\n    \n\tarray_queue_destory(queue);\n\treturn 0;\n\n}\n\n"
  },
  {
    "path": "c-cpp/09_queue/array_queue/array_queue.h",
    "content": "/*************************************************************************\n > File Name: array_queue.h\n > Author:  jinshaohui\n > Mail:    jinshaohui789@163.com\n > Time:    18-10-12\n > Desc:    \n ************************************************************************/\n#ifndef ARRAY_QUEUE_H\n#define ARRAY_QUEUE_H\n\ntypedef struct _array_queue\n{\n\tint size;/*队列的大小*/\n\tint num; /*当前存储数据的大小*/\n\tint head;/*队列的头*/\n\tint tail;/*队列的尾*/\n\tint *array;/*数据存储区*/\n}array_queue;\n\n#define array_queue_is_empty(array_queue) (array_queue->num == 0)\n#define array_queue_is_full(array_queue)  ((array_queue->num) == (array_queue->size))\n\n#endif\n\n"
  },
  {
    "path": "c-cpp/09_queue/array_queue.hpp",
    "content": "/**\n * Created by Liam Huang (Liam0205) on 2018/10/10.\n */\n\n#ifndef QUEUE_ARRAY_QUEUE_HPP_\n#define QUEUE_ARRAY_QUEUE_HPP_\n\ntemplate <typename T>\nclass ArrayQueue {\n  private:\n    T*     items_    = nullptr;\n    size_t capacity_ = 0;\n    size_t head_     = 0;\n    size_t tail_     = 0;\n\n  public:\n    ArrayQueue() = delete;\n    ArrayQueue(const size_t capacity) : capacity_(capacity) {\n        items_ = new T[capacity_];\n    }\n    ~ArrayQueue() {\n        if (nullptr != items_) {\n            delete[] items_;\n            items_ = nullptr;\n        }\n    }\n    ArrayQueue(const ArrayQueue& other) : capacity_(other.capacity_) {\n        items_ = new T[capacity_];\n        for (size_t i = other.head_; i != other.tail_; ++i) {\n            enqueue(other.items_[i]);\n        }\n    }\n    ArrayQueue& operator=(const ArrayQueue& rhs) {\n        delete[] items_;\n        head_     = 0;\n        tail_     = 0;\n        capacity_ = rhs.capacity_;\n        items_    = new T[capacity_];\n        for (size_t i = rhs.head_; i != rhs.tail_; ++i) {\n            enqueue(rhs.items_[i]);\n        }\n        return *this;\n    }\n    ArrayQueue(ArrayQueue&& other) : items_(other.items_),\n                                     capacity_(other.capacity_),\n                                     head_(other.head_),\n                                     tail_(other.tail_) {\n        other.items_    = nullptr;\n        other.capacity_ = 0;\n        other.head_     = 0;\n        other.tail_     = 0;\n    }\n    ArrayQueue& operator=(ArrayQueue&& rhs) {\n        delete[] items_;\n        items_        = rhs.items_;\n        capacity_     = rhs.capacity_;\n        head_         = rhs.head_;\n        tail_         = rhs.tail_;\n        rhs.items_    = nullptr;\n        rhs.capacity_ = 0;\n        rhs.head_     = 0;\n        rhs.tail_     = 0;\n        return *this;\n    }\n\n  public:\n    void enqueue(T item) {\n        if (capacity_ == tail_) {\n            throw \"Push data into a full queue!\";\n        }\n        items_[tail_++] = item;\n    }\n    T head() const {\n        if (head_ != tail_) {\n            return items_[head_];\n        } else {\n            throw \"Fetch data from an empty queue!\";\n        }\n    }\n    void dequeue() {\n        if (head_ != tail_) {\n            ++head_;\n        } else {\n            throw \"Pop data from an empty queue!\";\n        }\n    }\n\n  public:\n    template <typename UnaryFunc>\n    void traverse(UnaryFunc do_traverse) {\n        for (size_t i = head_; i != tail_; ++i) {\n            do_traverse(items_[i]);\n        }\n    }\n};\n\n#endif  // QUEUE_ARRAY_QUEUE_HPP_\n"
  },
  {
    "path": "c-cpp/09_queue/array_queue_test.cc",
    "content": "#include <iostream>\n#include \"array_queue.hpp\"\n\nint main() {\n    auto do_traverse = [&](auto item){ std::cout << item << ' '; };\n\n    ArrayQueue<int> array_queue_1(3);\n    array_queue_1.enqueue(1);\n    array_queue_1.enqueue(2);\n    array_queue_1.enqueue(3);\n    // array_queue_1.enqueue(4);  // throw\n    array_queue_1.traverse(do_traverse);\n    std::cout << std::endl;\n\n    ArrayQueue<int> array_queue_2(array_queue_1);  // copy constructor\n    array_queue_2.traverse(do_traverse);\n    std::cout << std::endl;\n\n    ArrayQueue<int> array_queue_3(std::move(array_queue_2));  // move constructor\n    array_queue_3.traverse(do_traverse);\n    std::cout << std::endl;\n    array_queue_2.traverse(do_traverse);\n    std::cout << std::endl;\n\n    std::cout << array_queue_3.head() << std::endl;\n    array_queue_3.dequeue();\n    std::cout << array_queue_3.head() << std::endl;\n    array_queue_3.dequeue();\n    std::cout << array_queue_3.head() << std::endl;\n    array_queue_3.dequeue();\n    // std::cout << array_queue_3.head() << std::endl;  // throw\n    // array_queue_3.dequeue();  // throw\n\n    ArrayQueue<int> array_queue_4(1);\n    array_queue_4 = array_queue_1;  // copy assignment\n    array_queue_4.traverse(do_traverse);\n    std::cout << std::endl;\n\n    ArrayQueue<int> array_queue_5(100);\n    array_queue_5 = std::move(array_queue_4);  // move assignment\n    array_queue_5.traverse(do_traverse);\n    std::cout << std::endl;\n    array_queue_4.traverse(do_traverse);\n    std::cout << std::endl;\n\n    std::cout << array_queue_5.head() << std::endl;\n    array_queue_5.dequeue();\n    std::cout << array_queue_5.head() << std::endl;\n    array_queue_5.dequeue();\n    std::cout << array_queue_5.head() << std::endl;\n    array_queue_5.dequeue();\n    // std::cout << array_queue_5.head() << std::endl;  // throw\n    // array_queue_5.dequeue();  // throw\n\n    return 0;\n}\n"
  },
  {
    "path": "c-cpp/09_queue/block_queue.hpp",
    "content": "/**\n * Created by Liam Huang (Liam0205) on 2018/10/11.\n */\n\n#ifndef QUEUE_BLOCK_QUEUE_HPP_\n#define QUEUE_BLOCK_QUEUE_HPP_\n\n#include <queue>\n#include <mutex>\n#include <condition_variable>\n\ntemplate <typename T>\nclass BlockQueue {\n  public:\n    using value_type      = T;\n    using container_type  = std::queue<value_type>;\n    using size_type       = typename container_type::size_type;\n\n  private:\n    size_type capacity_ = 0;\n    container_type container_;\n    mutable std::mutex mutex_;\n    mutable std::condition_variable not_empty_;\n    mutable std::condition_variable not_full_;\n\n  public:\n    BlockQueue() = delete;\n    BlockQueue(const size_type capacity) : capacity_(capacity) {}\n    BlockQueue(const BlockQueue&) = default;\n    BlockQueue(BlockQueue&&) = default;\n    BlockQueue& operator=(const BlockQueue&) = default;\n    BlockQueue& operator=(BlockQueue&&) = default;\n\n  private:\n    bool empty() const { return container_.empty(); }\n    bool full() const { return not(container_.size() < capacity_); }\n\n  public:\n    void put(const value_type& item) {\n        std::unqiue_lock<std::mutex> lock(mutex_);\n        while (full()) {\n            not_full_.wait(lock);\n        }\n        container_.push(item);\n        not_empty_.notify_one();\n    }\n    void take(value_type& out) {\n        std::unique_lock<std::mutex> lock(mutex_);\n        while (empty()) {\n            not_empty_.wait(lock);\n        }\n        out = container_.front();\n        container_.pop();\n        not_full_.notify_one();\n    }\n    template <typename Duration>\n    bool put_for(const value_type& item, const Duration& d) {\n        std::unqiue_lock<std::mutex> lock(mutex_);\n        if (not_full_.wait_for(lock, d, [&](){ return not full(); })) {\n            container_.push(item);\n            not_empty_.notify_one();\n            return true;\n        } else {\n            return false;\n        }\n    }\n    template <typename Duration>\n    bool take_for(const Duration& d, value_type& out) {\n        std::unique_lock<std::mutex> lock(mutex_);\n        if (not_empty_.wait_for(lock, d, [&](){ return not empty(); })) {\n            out = container_.front();\n            container_.pop();\n            not_full_.notify_one();\n            return true;\n        } else {\n            return false;\n        }\n    }\n};\n\n#endif  // QUEUE_BLOCK_QUEUE_HPP_\n\n"
  },
  {
    "path": "c-cpp/09_queue/circular_queue.hpp",
    "content": "/**\n * Created by Liam Huang (Liam0205) on 2018/10/10.\n */\n\n#ifndef QUEUE_CIRCULAR_QUEUE_HPP_\n#define QUEUE_CIRCULAR_QUEUE_HPP_\n\ntemplate <typename T>\nclass CircularQueue {\n  private:\n    T*     items_    = nullptr;\n    size_t capacity_ = 0;\n    size_t head_     = 0;\n    size_t tail_     = 0;\n\n  public:\n    CircularQueue() = delete;\n    CircularQueue(const size_t capacity) : capacity_(capacity) {\n        items_ = new T[capacity_];\n    }\n    ~CircularQueue() {\n        if (nullptr != items_) {\n            delete[] items_;\n            items_ = nullptr;\n        }\n    }\n    CircularQueue(const CircularQueue& other) : capacity_(other.capacity_) {\n        items_ = new T[capacity_];\n        for (size_t i = other.head_; i != other.tail_; ++i) {\n            enqueue(other.items_[i]);\n        }\n    }\n    CircularQueue& operator=(const CircularQueue& rhs) {\n        delete[] items_;\n        head_     = 0;\n        tail_     = 0;\n        capacity_ = rhs.capacity_;\n        items_    = new T[capacity_];\n        for (size_t i = rhs.head_; i != rhs.tail_; ++i) {\n            enqueue(rhs.items_[i]);\n        }\n        return *this;\n    }\n    CircularQueue(CircularQueue&& other) : items_(other.items_),\n                                     capacity_(other.capacity_),\n                                     head_(other.head_),\n                                     tail_(other.tail_) {\n        other.items_    = nullptr;\n        other.capacity_ = 0;\n        other.head_     = 0;\n        other.tail_     = 0;\n    }\n    CircularQueue& operator=(CircularQueue&& rhs) {\n        delete[] items_;\n        items_        = rhs.items_;\n        capacity_     = rhs.capacity_;\n        head_         = rhs.head_;\n        tail_         = rhs.tail_;\n        rhs.items_    = nullptr;\n        rhs.capacity_ = 0;\n        rhs.head_     = 0;\n        rhs.tail_     = 0;\n        return *this;\n    }\n\n  public:\n    void enqueue(T item) {\n        if ((tail_ + 1) % capacity_ == head_) {\n            throw \"Push data into a full queue!\";\n        }\n        items_[tail_] = item;\n        tail_ = (tail_ + 1) % capacity_;\n    }\n    T head() const {\n        if (head_ != tail_) {\n            return items_[head_];\n        } else {\n            throw \"Fetch data from an empty queue!\";\n        }\n    }\n    void dequeue() {\n        if (head_ != tail_) {\n            head_ = (head_ + 1) % capacity_;\n        } else {\n            throw \"Pop data from an empty queue!\";\n        }\n    }\n\n  public:\n    template <typename UnaryFunc>\n    void traverse(UnaryFunc do_traverse) {\n        if (0 == capacity_) return;\n        for (size_t i = head_; i % capacity_ != tail_; ++i) {\n            do_traverse(items_[i % capacity_]);\n        }\n    }\n};\n\n#endif  // QUEUE_CIRCULAR_QUEUE_HPP_\n"
  },
  {
    "path": "c-cpp/09_queue/circular_queue_test.cc",
    "content": "#include <iostream>\n#include \"circular_queue.hpp\"\n\nint main() {\n    auto do_traverse = [&](auto item){ std::cout << item << ' '; };\n\n    CircularQueue<int> circular_queue_1(4);\n    circular_queue_1.enqueue(1);\n    circular_queue_1.enqueue(2);\n    circular_queue_1.enqueue(3);\n    // circular_queue_1.enqueue(4);  // throw\n    circular_queue_1.traverse(do_traverse);\n    std::cout << std::endl;\n\n    CircularQueue<int> circular_queue_2(circular_queue_1);  // copy constructor\n    circular_queue_2.traverse(do_traverse);\n    std::cout << std::endl;\n\n    CircularQueue<int> circular_queue_3(std::move(circular_queue_2));  // move constructor\n    circular_queue_3.traverse(do_traverse);\n    std::cout << std::endl;\n    circular_queue_2.traverse(do_traverse);\n    std::cout << std::endl;\n\n    std::cout << circular_queue_3.head() << std::endl;\n    circular_queue_3.dequeue();\n    std::cout << circular_queue_3.head() << std::endl;\n    circular_queue_3.dequeue();\n    std::cout << circular_queue_3.head() << std::endl;\n    circular_queue_3.dequeue();\n    // std::cout << circular_queue_3.head() << std::endl;  // throw\n    // circular_queue_3.dequeue();  // throw\n\n    CircularQueue<int> circular_queue_4(1);\n    circular_queue_4 = circular_queue_1;  // copy assignment\n    circular_queue_4.traverse(do_traverse);\n    std::cout << std::endl;\n\n    CircularQueue<int> circular_queue_5(100);\n    circular_queue_5 = std::move(circular_queue_4);  // move assignment\n    circular_queue_5.traverse(do_traverse);\n    std::cout << std::endl;\n    circular_queue_4.traverse(do_traverse);\n    std::cout << std::endl;\n\n    std::cout << circular_queue_5.head() << std::endl;\n    circular_queue_5.dequeue();\n    std::cout << circular_queue_5.head() << std::endl;\n    circular_queue_5.dequeue();\n    std::cout << circular_queue_5.head() << std::endl;\n    circular_queue_5.dequeue();\n    // std::cout << circular_queue_5.head() << std::endl;  // throw\n    // circular_queue_5.dequeue();  // throw\n\n    for (size_t i = 0; i != 4; ++i) {\n        circular_queue_1.dequeue();\n        circular_queue_1.enqueue(i + 4);\n        circular_queue_1.traverse(do_traverse);\n        std::cout << std::endl;\n    }\n    return 0;\n}\n"
  },
  {
    "path": "c-cpp/09_queue/concurrency_queue.hpp",
    "content": "/**\n * Created by Liam Huang (Liam0205) on 2018/10/11.\n */\n\n#ifndef QUEUE_CONCURRENCY_QUEUE_HPP_\n#define QUEUE_CONCURRENCY_QUEUE_HPP_\n\n#include <queue>\n#include <mutex>\n#include <condition_variable>\n#include <memory>\n\ntemplate <typename T>\nclass ConcurrencyQueue {\n  public:\n    using value_type      = T;\n    using container_type  = std::queue<value_type>;\n    using size_type       = typename container_type::size_type;\n\n  private:\n    container_type container_;\n    mutable std::mutex mutex_;\n    std::condition_variable container_cond_;\n\n  public:\n    ConcurrencyQueue() = default;\n    ConcurrencyQueue(const ConcurrencyQueue&) = default;\n    ConcurrencyQueue(ConcurrencyQueue&&) = default;\n    ConcurrencyQueue& operator=(const ConcurrencyQueue&) = default;\n    ConcurrencyQueue& operator=(ConcurrencyQueue&&) = default;\n\n  private:\n    bool empty_() const { return container_.empty(); }\n\n  public:\n    bool empty() const {\n        std::lock_guard<std::mutex> lg(mutex_);\n        return container_.empty();\n    }\n    void push(value_type item) {\n        std::lock_guard<std::mutex> lg(mutex_);\n        container_.push(std::move(item));\n        container_cond_.notify_one();\n    }\n    void wait_and_pop(value_type& out) {\n        std::unique_lock<std::mutex> lk(mutex_);\n        while (empty_()) {\n            container_cond_.wait(lk)\n        }\n        out = std::move(container_.front());\n        container_.pop();\n    }\n    std::shared_ptr<value_type> wait_and_pop() {\n        std::unique_lock<std::mutex> lk(mutex_);\n        while (empty_()) {\n            container_cond_.wait(lk)\n        }\n        auto res = std::make_shared<value_type>(std::move(container_.front()));\n        container_.pop();\n        return res;\n    }\n    bool try_pop(value_type& out) {\n        std::lock_guard<std::mutex> lg(mutex_);\n        if (empty_()) {\n            return false;\n        } else {\n            out = std::move(container_.front());\n            container_.pop();\n            return true;\n        }\n    }\n    std::shared_ptr<value_type> try_pop() {\n        std::lock_guard<std::mutex> lg(mutex_);\n        if (empty_()) {\n            return nullptr;\n        } else {\n            auto res = std::make_shared<value_type>(std::move(container_.front()));\n            container_.pop();\n            return res;\n        }\n    }\n};\n\n#endif  // QUEUE_CONCURRENCY_QUEUE_HPP_\n\n"
  },
  {
    "path": "c-cpp/09_queue/dynamic_array_queue.hpp",
    "content": "/**\n * Created by Liam Huang (Liam0205) on 2018/10/10.\n */\n\n#ifndef QUEUE_DYNAMIC_ARRAY_QUEUE_HPP_\n#define QUEUE_DYNAMIC_ARRAY_QUEUE_HPP_\n\ntemplate <typename T>\nclass DynamicArrayQueue {\n  private:\n    T*     items_    = nullptr;\n    size_t capacity_ = 0;\n    size_t head_     = 0;\n    size_t tail_     = 0;\n\n  public:\n    DynamicArrayQueue() = delete;\n    DynamicArrayQueue(const size_t capacity) : capacity_(capacity) {\n        items_ = new T[capacity_];\n    }\n    ~DynamicArrayQueue() {\n        if (nullptr != items_) {\n            delete[] items_;\n            items_ = nullptr;\n        }\n    }\n    DynamicArrayQueue(const DynamicArrayQueue& other) : capacity_(other.capacity_) {\n        items_ = new T[capacity_];\n        for (size_t i = other.head_; i != other.tail_; ++i) {\n            enqueue(other.items_[i]);\n        }\n    }\n    DynamicArrayQueue& operator=(const DynamicArrayQueue& rhs) {\n        delete[] items_;\n        head_     = 0;\n        tail_     = 0;\n        capacity_ = rhs.capacity_;\n        items_    = new T[capacity_];\n        for (size_t i = rhs.head_; i != rhs.tail_; ++i) {\n            enqueue(rhs.items_[i]);\n        }\n        return *this;\n    }\n    DynamicArrayQueue(DynamicArrayQueue&& other) : items_(other.items_),\n                                     capacity_(other.capacity_),\n                                     head_(other.head_),\n                                     tail_(other.tail_) {\n        other.items_    = nullptr;\n        other.capacity_ = 0;\n        other.head_     = 0;\n        other.tail_     = 0;\n    }\n    DynamicArrayQueue& operator=(DynamicArrayQueue&& rhs) {\n        delete[] items_;\n        items_        = rhs.items_;\n        capacity_     = rhs.capacity_;\n        head_         = rhs.head_;\n        tail_         = rhs.tail_;\n        rhs.items_    = nullptr;\n        rhs.capacity_ = 0;\n        rhs.head_     = 0;\n        rhs.tail_     = 0;\n        return *this;\n    }\n\n  public:\n    void enqueue(T item) {\n        if (capacity_ == tail_ - head_) {\n            throw \"Push data into a full queue!\";\n        }\n        if (capacity_ == tail_) {\n            // item transport\n            for (size_t i = head_; i != tail_; ++i) {\n                items_[i - head_] = items_[i];\n            }\n            tail_ = tail_ - head_;\n            head_ = 0;\n        }\n        items_[tail_++] = item;\n    }\n    T head() const {\n        if (head_ != tail_) {\n            return items_[head_];\n        } else {\n            throw \"Fetch data from an empty queue!\";\n        }\n    }\n    void dequeue() {\n        if (head_ != tail_) {\n            ++head_;\n        } else {\n            throw \"Pop data from an empty queue!\";\n        }\n    }\n\n  public:\n    template <typename UnaryFunc>\n    void traverse(UnaryFunc do_traverse) {\n        for (size_t i = head_; i != tail_; ++i) {\n            do_traverse(items_[i]);\n        }\n    }\n};\n\n#endif  // QUEUE_DYNAMIC_ARRAY_QUEUE_HPP_\n"
  },
  {
    "path": "c-cpp/09_queue/dynamic_array_queue_test.cc",
    "content": "#include <iostream>\n#include \"dynamic_array_queue.hpp\"\n\nint main() {\n    auto do_traverse = [&](auto item){ std::cout << item << ' '; };\n\n    DynamicArrayQueue<int> dynamic_array_queue_1(3);\n    dynamic_array_queue_1.enqueue(1);\n    dynamic_array_queue_1.enqueue(2);\n    dynamic_array_queue_1.enqueue(3);\n    // dynamic_array_queue_1.enqueue(4);  // throw\n    dynamic_array_queue_1.traverse(do_traverse);\n    std::cout << std::endl;\n\n    DynamicArrayQueue<int> dynamic_array_queue_2(dynamic_array_queue_1);  // copy constructor\n    dynamic_array_queue_2.traverse(do_traverse);\n    std::cout << std::endl;\n\n    DynamicArrayQueue<int> dynamic_array_queue_3(std::move(dynamic_array_queue_2));  // move constructor\n    dynamic_array_queue_3.traverse(do_traverse);\n    std::cout << std::endl;\n    dynamic_array_queue_2.traverse(do_traverse);\n    std::cout << std::endl;\n\n    std::cout << dynamic_array_queue_3.head() << std::endl;\n    dynamic_array_queue_3.dequeue();\n    std::cout << dynamic_array_queue_3.head() << std::endl;\n    dynamic_array_queue_3.dequeue();\n    std::cout << dynamic_array_queue_3.head() << std::endl;\n    dynamic_array_queue_3.dequeue();\n    // std::cout << dynamic_array_queue_3.head() << std::endl;  // throw\n    // dynamic_array_queue_3.dequeue();  // throw\n\n    DynamicArrayQueue<int> dynamic_array_queue_4(1);\n    dynamic_array_queue_4 = dynamic_array_queue_1;  // copy assignment\n    dynamic_array_queue_4.traverse(do_traverse);\n    std::cout << std::endl;\n\n    DynamicArrayQueue<int> dynamic_array_queue_5(100);\n    dynamic_array_queue_5 = std::move(dynamic_array_queue_4);  // move assignment\n    dynamic_array_queue_5.traverse(do_traverse);\n    std::cout << std::endl;\n    dynamic_array_queue_4.traverse(do_traverse);\n    std::cout << std::endl;\n\n    std::cout << dynamic_array_queue_5.head() << std::endl;\n    dynamic_array_queue_5.dequeue();\n    std::cout << dynamic_array_queue_5.head() << std::endl;\n    dynamic_array_queue_5.dequeue();\n    std::cout << dynamic_array_queue_5.head() << std::endl;\n    dynamic_array_queue_5.dequeue();\n    // std::cout << dynamic_array_queue_5.head() << std::endl;  // throw\n    // dynamic_array_queue_5.dequeue();  // throw\n\n    for (size_t i = 0; i != 3; ++i) {\n        dynamic_array_queue_1.dequeue();\n        dynamic_array_queue_1.enqueue(i + 4);\n        dynamic_array_queue_1.traverse(do_traverse);\n        std::cout << std::endl;\n    }\n    return 0;\n}\n"
  },
  {
    "path": "c-cpp/09_queue/linked_queue.hpp",
    "content": "/**\n * Created by Liam Huang (Liam0205) on 2018/10/10.\n */\n\n#ifndef QUEUE_LINKED_QUEUE_HPP_\n#define QUEUE_LINKED_QUEUE_HPP_\n\n#include <memory>\n\ntemplate <typename T>\nstruct Node {\n    using ptr_t = std::shared_ptr<Node<T>>;\n    T     data;\n    ptr_t next;\n\n    Node(T data_) : data(data_), next(nullptr) {}\n    Node() : next(nullptr) {}\n};\n\ntemplate <typename T>\nclass LinkedQueue {\n  public:\n    using node_type  = Node<T>;\n    using node_ptr_t = typename node_type::ptr_t;\n\n  private:\n    node_ptr_t head_        = nullptr;\n    node_ptr_t before_tail_ = nullptr;\n\n  public:\n    LinkedQueue()  = default;\n    ~LinkedQueue() = default;\n    LinkedQueue(const LinkedQueue& other) = default;\n    LinkedQueue& operator=(const LinkedQueue& rhs) = default;\n    LinkedQueue(LinkedQueue&& other) = default;\n    LinkedQueue& operator=(LinkedQueue&& rhs) = default;\n\n  public:\n    void enqueue(T item) {\n        if (nullptr == head_) {\n            head_ = std::make_shared<node_type>(item);\n            before_tail_ = head_;\n        } else {\n            before_tail_->next = std::make_shared<node_type>(item);\n            before_tail_ = before_tail_->next;\n        }\n    }\n    T head() const {\n        if (nullptr != head_) {\n            return head_->data;\n        } else {\n            throw \"Fetch data from an empty queue!\";\n        }\n    }\n    void dequeue() {\n        if (nullptr != head_) {\n            head_ = head_->next;\n            if (nullptr == head_) {\n                before_tail_ = nullptr;\n            }\n        } else {\n            throw \"Pop data from an empty queue!\";\n        }\n    }\n\n  public:\n    template <typename UnaryFunc>\n    void traverse(UnaryFunc do_traverse) {\n        for (node_ptr_t work = head_; nullptr != work; work = work->next) {\n            do_traverse(work->data);\n        }\n    }\n};\n\n#endif  // QUEUE_LINKED_QUEUE_HPP_\n"
  },
  {
    "path": "c-cpp/09_queue/linked_queue_test.cc",
    "content": "#include <iostream>\n#include \"linked_queue.hpp\"\n\nint main() {\n    auto do_traverse = [&](auto item){ std::cout << item << ' '; };\n\n    LinkedQueue<int> linked_queue_1;\n    linked_queue_1.enqueue(1);\n    linked_queue_1.enqueue(2);\n    linked_queue_1.enqueue(3);\n    linked_queue_1.traverse(do_traverse);\n    std::cout << std::endl;\n\n    LinkedQueue<int> linked_queue_2(linked_queue_1);  // copy constructor\n    linked_queue_2.traverse(do_traverse);\n    std::cout << std::endl;\n\n    LinkedQueue<int> linked_queue_3(std::move(linked_queue_2));  // move constructor\n    linked_queue_3.traverse(do_traverse);\n    std::cout << std::endl;\n    linked_queue_2.traverse(do_traverse);\n    std::cout << std::endl;\n\n    std::cout << linked_queue_3.head() << std::endl;\n    linked_queue_3.dequeue();\n    std::cout << linked_queue_3.head() << std::endl;\n    linked_queue_3.dequeue();\n    std::cout << linked_queue_3.head() << std::endl;\n    linked_queue_3.dequeue();\n    // std::cout << linked_queue_3.head() << std::endl;  // throw\n    // linked_queue_3.dequeue();  // throw\n\n    LinkedQueue<int> linked_queue_4;\n    linked_queue_4 = linked_queue_1;  // copy assignment\n    linked_queue_4.traverse(do_traverse);\n    std::cout << std::endl;\n\n    LinkedQueue<int> linked_queue_5;\n    linked_queue_5 = std::move(linked_queue_4);  // move assignment\n    linked_queue_5.traverse(do_traverse);\n    std::cout << std::endl;\n    linked_queue_4.traverse(do_traverse);\n    std::cout << std::endl;\n\n    std::cout << linked_queue_5.head() << std::endl;\n    linked_queue_5.dequeue();\n    std::cout << linked_queue_5.head() << std::endl;\n    linked_queue_5.dequeue();\n    std::cout << linked_queue_5.head() << std::endl;\n    linked_queue_5.dequeue();\n    // std::cout << linked_queue_5.head() << std::endl;  // throw\n    // linked_queue_5.dequeue();  // throw\n\n    return 0;\n}\n"
  },
  {
    "path": "c-cpp/09_queue/list_queue/list_queue.c",
    "content": "/*************************************************************************\n > File Name: list_queue.c\n > Author:  jinshaohui\n > Mail:    jinshaohui789@163.com\n > Time:    18-10-13\n > Desc:    \n ************************************************************************/\n#include<stdio.h>\n#include<stdlib.h>\n#include<string.h>\n#include\"./list_queue.h\"\n\n/*创建队列头*/\nlist_queue *list_queue_create()\n{\n\tlist_queue * queue = NULL;\n\n\tqueue = (list_queue *)malloc(sizeof(list_queue));\n\tif(queue == NULL)\n\t{\n\t\treturn NULL;\n\t}\n\n\tqueue->num  = 0;\n\tqueue->head = NULL;\n\tqueue->tail = NULL;\n\n\treturn queue;\n}\nvoid list_queue_destroy(list_queue*queue)\n{\n\tint i = 0;\n\tint data = 0;\n\n\tif ((queue == NULL) || (list_queue_is_empty(queue)))\n\t{\n\t\treturn ;\n\t}\n\n\twhile(!list_queue_is_empty(queue))\n\t{\n\t\t(void)list_queue_dequeue(queue,&data);\n\t}\n\n    free(queue);\n\treturn;\n}\nint list_queue_enqueue(list_queue *queue,int data)\n{\n\tqueue_node *ptmp = NULL;\n\n\tif(queue == NULL)\n\t{\n\t\treturn -1;\n\t}\n\n\tptmp = (queue_node *)malloc(sizeof(queue_node));\n\tif (ptmp == NULL)\n\t{\n\t\treturn -1;\n\t}\n\n\tptmp->data = data;\n\tptmp->next = NULL;\n\tif (queue->head == NULL)\n\t{\n\t\tqueue->head = ptmp;\n\t}\n\telse\n\t{\n\t    queue->tail->next = ptmp;\n\n\t}\n\tqueue->tail = ptmp;\n\tqueue->num++;\n\n\treturn 0;\n}\n\n/*出队*/\nint list_queue_dequeue(list_queue *queue,int *data)\n{\n\tqueue_node * ptmp = NULL;\n\n\tif ((queue == NULL) || (data == NULL) || list_queue_is_empty(queue))\n\t{\n\t\treturn -1;\n\t}\n\n\t*data = queue->head->data;\n    ptmp = queue->head;\n\tqueue->head = queue->head->next;\n\tqueue->num--;\n\n\tif (queue->head == NULL)\n\t{\n\t\tqueue->tail = NULL;\n\t}\n\n\t\n\tfree(ptmp);\n\treturn 0;\n}\nvoid list_queue_dump(list_queue*queue)\n{\n\tint i = 0;\n\tqueue_node *ptmp = NULL;\n\n\tif ((queue == NULL) || (list_queue_is_empty(queue)))\n\t{\n\t\treturn;\n\t}\n\n\tptmp = queue->head;\n\n    printf(\"\\r\\n----dump queue num = %d--------\",queue->num);\n\twhile(ptmp != NULL)\n\t{\n\t\tprintf(\"\\r\\nnode[%d] = %d\",i,ptmp->data);\n\t\ti++;\n\t\tptmp = ptmp->next;\n\t}\n\tprintf(\"\\r\\n---------------------------------\\r\\n\");\n    \n\treturn;\n}\n\nint main()\n{\n\tint i = 0;\n\tint data = 0;\n\tint ret = 0;\n\tlist_queue * queue;\n\n\tqueue = list_queue_create();\n\tif (queue == NULL)\n\t{\n\t\tprintf(\"\\r\\nlist queue create falied..\");\n\t\treturn 0;\n\t}\n\n\tfor (i = 0; i < 5; i++)\n\t{\n\t\t(void)list_queue_enqueue(queue,i);\n\t}\n\tlist_queue_dump(queue);\n\n    ret = list_queue_dequeue(queue,&data);\n\tif(ret != 0)\n\t{\n\t\tprintf(\"\\r\\nlist queue dequeue %d falied.\",data);\n\t}\n\tprintf(\"\\r\\nlist queue dequeue %d\",data);\n\tlist_queue_dump(queue);\n\n\n    ret = list_queue_dequeue(queue,&data);\n\tif(ret != 0)\n\t{\n\t\tprintf(\"\\r\\nlist queue dequeue %d failed.\",data);\n\t}\n    printf(\"\\r\\nlist queue dequeue %d\",data);\n\tlist_queue_dump(queue);\n\n\tprintf(\"\\r\\nlist queue enqueue %d\",data);\n\t(void)list_queue_enqueue(queue,data);\n\tlist_queue_dump(queue);\n\n    list_queue_destroy(queue);\n\treturn 0;\n}\n"
  },
  {
    "path": "c-cpp/09_queue/list_queue/list_queue.h",
    "content": "/*************************************************************************\n > File Name: list_queue.h\n > Author:  jinshaohui\n > Mail:    jinshaohui789@163.com\n > Time:    18-10-13\n > Desc:    \n ************************************************************************/\n\n#ifndef LINK_LIST_QUEUE_H\n#define LINK_LIST_QUEUE_H\n\ntypedef struct _list_queue_node\n{\n\tint data;\n\tstruct _list_queue_node *next;\n}queue_node;\n\ntypedef struct _list_queue\n{\n\tint num;\n\tqueue_node *head;\n\tqueue_node *tail;\n}list_queue;\n\n#define list_queue_is_empty(queue) ((queue->num) == 0)\n\n#endif\n"
  },
  {
    "path": "c-cpp/09_queue/lock_free_queue.hpp",
    "content": "/**\n * Created by Liam Huang (Liam0205) on 2018/10/11.\n */\n\n#ifndef QUEUE_LOCK_FREE_QUEUE_HPP_\n#define QUEUE_LOCK_FREE_QUEUE_HPP_\n\n#include <memory>\n#include <atomic>\n\ntemplate <typename T>\nclass LockFreeQueue {\n  public:\n    using value_type      = T;\n\n  private:\n    struct node {\n        std::shared<value_type> data = nullptr;\n        node* next                   = nullptr;\n    };\n    std::atomic<node*> head = nullptr;\n    std::atomic<node*> tail = nullptr;\n\n  public:\n    LockFreeQueue() head(new node), tail(head.load()) {}\n    LockFreeQueue(const LockFreeQueue&) = delete;\n    LockFreeQueue(LockFreeQueue&& other) : head(other.head.load()), tail(other.tail.load()) {\n        other.head.store(nullptr);\n        other.tail.store(nullptr);\n    }\n    LockFreeQueue& operator=(const LockFreeQueue&) = delete;\n    LockFreeQueue& operator=(LockFreeQueue&& rhs) {\n        while (node* const old_head = head.load()) {\n            head.store(old_head->next);\n            delete old_head;\n        }\n        head.store(rhs.head.load());\n        tail.store(rhs.tail.load());\n        rhs.head.store(nullptr);\n        rhs.tail.store(nullptr);\n    }\n    ~LockFreeQueue() {\n        while (node* const old_head = head.load()) {\n            head.store(old_head->next);\n            delete old_head;\n        }\n    }\n\n  private:\n    node* pop_head() {\n        node* const res = head.load();\n        if (res == tail.load()) {\n            return nullptr;\n        }\n        head.store(res->next);\n        return res;\n    }\n\n  public:\n    bool empty() const {\n        return head.load() == tail.load();\n    }\n    std::shared_ptr<value_type> pop() {\n        node* old_head = pop_head();\n        if (nullptr == old_head) {\n            return nullptr;\n        } else {\n            auto res = old_head->data;\n            delete old_head;\n            return res;\n        }\n    }\n    void push(value_type new_value) {\n        auto new_data = std::make_shared<value_type>(new_value);\n        node* p = new node;\n        node* old_tail = tail.load();\n        old_tail->data.swap(new_data);\n        old_tail->next = p;\n        tail_.store(p);\n    }\n};\n\n#endif  // QUEUE_LOCK_FREE_QUEUE_HPP_\n\n"
  },
  {
    "path": "c-cpp/09_queue/ring_queue.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <stdbool.h>\n\nstruct ring_queue {\n\tint cap;\n\tint head, tail;\n\tint *_q;\n};\n\nint alloc_queue(struct ring_queue* queue, int cap)\n{\n\tif (!queue || cap < 0)\n\t\treturn -1;\n\tif (queue->_q)\n\t\treturn -1;\n\n\tqueue->_q = (int *)malloc(cap * sizeof(int));\n\tif (!queue->_q)\n\t\treturn -1;\n\n\tqueue->head = queue->tail = 0;\n\tqueue->cap = cap;\n\treturn 0;\n}\n\nvoid free_queue(struct ring_queue *queue)\n{\n\tqueue->cap = 0;\n\tqueue->head = queue->tail = 0;\n\tfree(queue->_q);\n}\n\nint _valid_index(int curr, int step, int cap)\n{\n\treturn (curr + step) % cap;\n}\n\nint _next(int curr, int cap)\n{\n\treturn _valid_index(curr, 1, cap);\n}\n\nbool is_empty(struct ring_queue *queue)\n{\n\treturn (queue->head == queue->tail);\n}\n\nbool is_full(struct ring_queue *queue)\n{\n\tint next_tail = _next(queue->tail, queue->cap);\n\treturn (next_tail == queue->head);\n}\n\nint enqueue(struct ring_queue* queue, int elem)\n{\n\tif (is_full(queue))\n\t\treturn -1;\n\n\tqueue->_q[queue->tail] = elem;\n\tqueue->tail = _next(queue->tail, queue->cap);\n\treturn 0;\n}\n\nint dequeue(struct ring_queue* queue, int *elem)\n{\n\tif (is_empty(queue))\n\t\treturn -1;\n\n\tif (elem)\n\t\t*elem = queue->_q[queue->head];\n\tqueue->head = _next(queue->head, queue->cap);\n\treturn 0;\n}\n\nint size(struct ring_queue* queue)\n{\n\tint size = queue->tail - queue->head;\n\n\tif (size < 0)\n\t\tsize += queue->cap;\n\treturn size;\n}\n\nvoid dump(struct ring_queue* queue)\n{\n\tint i, idx;\n\n\tprintf(\"Queue has %d elements with %d capacity\\n\",\n\t\tsize(queue), queue->cap);\n\tfor (i = 0; i < size(queue); i++) {\n\t\tidx = _valid_index(queue->head, i, queue->cap);\n\t\tprintf(\"[%02d]: %08d\\n\", idx, queue->_q[idx]);\n\t}\n}\n\nint main()\n{\n\tstruct ring_queue queue = {0, 0, 0, NULL};\n\tint i;\n\n\tif (alloc_queue(&queue, 8)) {\n\t\tprintf(\"Failed to allocate a queue\\n\");\n\t\treturn -1;\n\t}\n\n\tprintf(\"A new queue is %s\\n\", is_empty(&queue)?\"empty\":\"not empty\");\n\n\tenqueue(&queue, 1);\n\tprintf(\"After enqueue 1 element, queue is %s\\n\", is_empty(&queue)?\"empty\":\"not empty\");\n\tdequeue(&queue, NULL);\n\tprintf(\"After dequeue 1 element, queue is %s\\n\", is_empty(&queue)?\"empty\":\"not empty\");\n\n\tfor (i = 0; i < 7; i++)\n\t\tenqueue(&queue, i);\n\tprintf(\"After enqueue 7 element, queue is %s\\n\", is_full(&queue)?\"full\":\"not full\");\n\n\tfor (i = 0; i < 4; i++) {\n\t\tdequeue(&queue, NULL);\n\t\tenqueue(&queue, i);\n\t}\n\tprintf(\"After enqueue/dequeue 4 element, queue is %s\\n\",\n\t\t\tis_full(&queue)?\"full\":\"not full\");\n\tprintf(\"Head is %d, Tail is %d\\n\", queue.head, queue.tail);\n\n\tdump(&queue);\n\tfree_queue(&queue);\n\treturn 0;\n}\n"
  },
  {
    "path": "c-cpp/10_recursive/.gitkeep",
    "content": ""
  },
  {
    "path": "c-cpp/10_recursive/one_two_step.c",
    "content": "/*************************************************************************\n > File Name: one_two_step.c\n > Author:  jinshaohui\n > Mail:    jinshaohui789@163.com\n > Time:    18-10-19\n > Desc:    \n ************************************************************************/\n#include<stdio.h>\n#include<stdlib.h>\n#include<string.h>\n\n/*爬楼梯的问题，解决重复计算，采用数据保存方法*/\n\nint helper(int n ,int *vlaue)\n{\n\n    if(vlaue[n] != 0)\n\t{\n\t\treturn vlaue[n];\n\t}\n\n    vlaue[n] = helper(n - 1,vlaue) + helper(n - 2,vlaue);\n\n\treturn vlaue[n];\n}\n\nint climbStaris(int n)\n{\n\tint *vlaue = NULL;\n\tint res = 0;\n\n\tvlaue = (int *)malloc(sizeof(int)*(n+1));\n\tif(vlaue == NULL)\n\t{\n\t\treturn -1;\n\t}\n\n\tmemset(vlaue,0,sizeof(int)*(n + 1));\n    vlaue[0] = 0;\n\tvlaue[1] = 1;\n\tvlaue[2] = 2;\n    res = helper(n,vlaue);\n\tfree(vlaue);\n\n\treturn res;\n}\n\nint main()\n{\n\n\tprintf(\"\\r\\nnum%d ,%d\",5,climbStaris(5));\n\tprintf(\"\\r\\nnum%d ,%d\",6,climbStaris(6));\n\tprintf(\"\\r\\nnum%d ,%d\",7,climbStaris(7));\n\treturn 0;\n}\n\n\n"
  },
  {
    "path": "c-cpp/10_recursive/one_two_step.cc",
    "content": "#include <iostream>\n#include <unordered_map>\n\nclass SolutionOFOneTwoStep {\n  private:\n    static std::unordered_map<size_t, size_t> result_;\n\n  public:\n    enum class POLICY {\n        RECURSIVE,\n        NONRECURSIVE\n    };\n\n  private:\n    size_t recursive(size_t steps) {\n        auto iter = result_.find(steps);\n        if (result_.end() != iter) {  // found.\n            return iter->second;\n        } else {\n            size_t res = operator()(steps - 1) + operator()(steps - 2);\n            result_.insert({steps, res});\n            return res;\n        }\n    }\n    size_t nonrecursive(size_t steps) {\n        auto iter = result_.find(steps);\n        if (result_.end() != iter) {  // found.\n            return iter->second;\n        } else {\n            size_t start;\n            for (start = steps; start != 2 and result_.end() == result_.find(start); --start) {}\n            for (size_t i = start; i != steps; ++i) {\n                result_.insert({i + 1, result_[i - 1] + result_[i]});\n            }\n            return result_[steps];\n        }\n    }\n\n  public:\n    size_t operator()(size_t steps, const POLICY policy = POLICY::RECURSIVE) {\n        if (policy == POLICY::RECURSIVE) {\n            return recursive(steps);\n        } else if (policy == POLICY::NONRECURSIVE) {\n            return nonrecursive(steps);\n        }\n    }\n    static void debug() {\n        for (auto kv : result_) {\n            std::cout << kv.first << ' ' << kv.second << std::endl;\n        }\n        std::cout << std::endl;\n    }\n};\n\nstd::unordered_map<size_t, size_t> SolutionOFOneTwoStep::result_ = {{1, 1}, {2, 2}};\n\nint main() {\n    SolutionOFOneTwoStep::debug();\n\n    std::cout << SolutionOFOneTwoStep()(5, SolutionOFOneTwoStep::POLICY::RECURSIVE) << std::endl;\n    SolutionOFOneTwoStep::debug();\n\n    std::cout << SolutionOFOneTwoStep()(10, SolutionOFOneTwoStep::POLICY::NONRECURSIVE) << std::endl;\n    SolutionOFOneTwoStep::debug();\n\n    std::cout << SolutionOFOneTwoStep()(20, SolutionOFOneTwoStep::POLICY::RECURSIVE) << std::endl;\n    SolutionOFOneTwoStep::debug();\n\n    return 0;\n}\n\n"
  },
  {
    "path": "c-cpp/11_sorts/.gitkeep",
    "content": ""
  },
  {
    "path": "c-cpp/11_sorts/sorts.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <stdbool.h>\n\nstruct array {\n\tint size;\n\tint used;\n\tint *arr;\n};\n\nvoid dump(struct array *array)\n{\n\tint idx;\n\t\n\tfor (idx = 0; idx < array->used; idx++)\n\t\tprintf(\"[%02d]: %08d\\n\", idx, array->arr[idx]);\n}\n\nvoid alloc(struct array *array)\n{\n\tarray->arr = (int *)malloc(array->size * sizeof(int));\n}\n\nvoid bubble_sort(struct array *array)\n{\n\tint i, j;\n\n\tif (array->used <= 1)\n\t\treturn;\n\n\tfor (i = 0; i < array->used; i++) {\n\t\tbool has_swap = false;\n\t\tfor (j = 0; j < array->used - i - 1; j++) {\n\t\t\tif (array->arr[j] > array->arr[j+1]) {\n\t\t\t\tint tmp;\n\t\t\t\ttmp = array->arr[j];\n\t\t\t\tarray->arr[j] = array->arr[j+1];\n\t\t\t\tarray->arr[j+1] = tmp;\n\t\t\t\thas_swap = true;\n\t\t\t}\n\n\t\t}\n\t\tif (!has_swap)\n\t\t\tbreak;\n\t}\n}\n\nvoid bubble_sort_test()\n{\n\tint idx;\n\tstruct array ten_int = {10, 0, NULL};\n\n\talloc(&ten_int);\n\tfor (idx = 0; idx < 10; idx++)\n\t\tten_int.arr[idx] = 30 - idx;\n\tten_int.used = 10;\n\tdump(&ten_int);\n\tbubble_sort(&ten_int);\n\tdump(&ten_int);\n}\n\nvoid insertion_sort(struct array *array)\n{\n\tint i, j;\n\n\tif (array->used <= 1)\n\t\treturn;\n\n\tfor (i = 1; i < array->used; i++) {\n\t\tint val = array->arr[i];\n\n\t\tfor (j = i - 1; j >= 0; j--) {\n\t\t\tif (val < array->arr[j])\n\t\t\t\tarray->arr[j+1] = array->arr[j]; \n\t\t\telse\n\t\t\t\tbreak;\n\t\t}\n\t\tarray->arr[j+1] = val;\n\t}\n}\n\nvoid insertion_sort_test()\n{\n\tint idx;\n\tstruct array ten_int = {10, 0, NULL};\n\n\talloc(&ten_int);\n\tfor (idx = 0; idx < 10; idx++)\n\t\tten_int.arr[idx] = 30 - idx;\n\tten_int.used = 10;\n\tdump(&ten_int);\n\tinsertion_sort(&ten_int);\n\tdump(&ten_int);\n}\n\nvoid selection_sort(struct array *array)\n{\n\tint i, j;\n\n\tif (array->used <= 1)\n\t\treturn;\n\n\tfor (i = 0; i < array->used - 1; i++) {\n\t\tint tmp, idx = i;\n\n\t\tfor (j = i + 1; j < array->used; j++)\n\t\t\tif (array->arr[j] < array->arr[idx])\n\t\t\t\tidx = j;\n\n\t\tif (idx == i)\n\t\t\tcontinue;\n\n\t\ttmp = array->arr[i];\n\t\tarray->arr[i] = array->arr[idx];\n\t\tarray->arr[idx] = tmp;\n\t}\n}\n\nvoid selection_sort_test()\n{\n\tint idx;\n\tstruct array ten_int = {10, 0, NULL};\n\n\talloc(&ten_int);\n\tfor (idx = 0; idx < 10; idx++)\n\t\tten_int.arr[idx] = 30 - idx;\n\tten_int.used = 10;\n\tdump(&ten_int);\n\tselection_sort(&ten_int);\n\tdump(&ten_int);\n}\n\nint main()\n{\n\t//bubble_sort_test();\n\t//selection_sort_test();\n\tinsertion_sort_test();\n\treturn 0;\n}\n"
  },
  {
    "path": "c-cpp/11_sorts/sorts.cpp",
    "content": "// C program for implementation of selection sort \n#include <stdio.h> \n\nvoid swap(int *xp, int *yp) \n{ \n\tint temp = *xp; \n\t*xp = *yp; \n\t*yp = temp; \n} \n\nvoid selectionSort(int arr[], int n) \n{ \n\tint i, j, min_idx; \n\n\t// One by one move boundary of unsorted subarray \n\tfor (i = 0; i < n-1; i++) \n\t{ \n\t\t// Find the minimum element in unsorted array \n\t\tmin_idx = i; \n\t\tfor (j = i+1; j < n; j++) \n\t\tif (arr[j] < arr[min_idx]) \n\t\t\tmin_idx = j; \n\n\t\t// Swap the found minimum element with the first element \n\t\tswap(&arr[min_idx], &arr[i]); \n\t} \n} \n\n/* Function to print an array */\nvoid printArray(int arr[], int size) \n{ \n\tint i; \n\tfor (i=0; i < size; i++) \n\t\tprintf(\"%d \", arr[i]); \n\tprintf(\"\\n\"); \n} \n\n// Driver program to test above functions \nint main() \n{ \n\tint arr[] = {64, 25, 12, 22, 11}; \n\tint n = sizeof(arr)/sizeof(arr[0]); \n\tselectionSort(arr, n); \n\tprintf(\"Sorted array: \\n\"); \n\tprintArray(arr, n); \n\treturn 0; \n} \n"
  },
  {
    "path": "c-cpp/11_sorts/sorts.hpp",
    "content": "/**\n * Created by Liam Huang (Liam0205) on 2018/10/16.\n */\n\n#ifndef SORTS_SORTS_HPP_\n#define SORTS_SORTS_HPP_\n\n#include <iterator>\n#include <functional>\n\ntemplate <typename BidirIt,\n          typename BinaryPred = std::less<typename std::iterator_traits<BidirIt>::value_type>>\nvoid bubble_sort(BidirIt first, BidirIt last, BinaryPred comp = BinaryPred()) {\n    if (std::distance(first, last) <= 1) { return; }\n    bool flag = true;\n    for (auto it = first; flag and it != last; ++it) {\n        flag = false;\n        for (auto itt = first; itt != last - std::distance(first, it) - 1; ++itt) {\n            if (comp(*(itt + 1), *itt)) {\n                std::swap(*itt, *(itt + 1));\n                flag = true;\n            }\n        }\n    }\n}\n\ntemplate <typename BidirIt,\n          typename BinaryPred = std::less<typename std::iterator_traits<BidirIt>::value_type>>\nvoid insertion_sort(BidirIt first, BidirIt last, BinaryPred comp = BinaryPred()) {\n    if (std::distance(first, last) <= 1) { return; }\n    for (auto it = first + 1; it != last; ++it) {\n        const auto target = *it;\n        auto       itt    = it;\n        for (; std::distance(first, itt) > 0 and comp(target, *(itt - 1)); --itt) {\n            *itt = *(itt - 1);\n        }\n        *itt = target;\n    }\n}\n\ntemplate <typename BidirIt,\n          typename BinaryPred = std::less<typename std::iterator_traits<BidirIt>::value_type>>\nvoid selection_sort(BidirIt first, BidirIt last, BinaryPred comp = BinaryPred()) {\n    if (std::distance(first, last) <= 1) { return; }\n    for (auto it = first; it != last - 1; ++it) {\n        auto tag = it;\n        for (auto itt = it + 1; itt != last; ++itt) {\n            if (comp(*itt, *tag)) {\n                tag = itt;\n            }\n        }\n        if (tag != it) {\n            std::swap(*it, *tag);\n        }\n    }\n}\n\ntemplate <typename FrwdIt,\n          typename BinaryPred = std::less<typename std::iterator_traits<FrwdIt>::value_type>>\nvoid bubble_down_sort(FrwdIt first, FrwdIt last, BinaryPred comp = BinaryPred()) {\n    if (std::distance(first, last) <= 1) { return; }\n    for (auto it = first; it != last; ++it) {\n        for (auto itt = it + 1; itt != last; ++itt) {\n            if (comp(*itt, *it)) {\n                std::swap(*it, *itt);\n            }\n        }\n    }\n}\n\ntemplate <typename BidirIt,\n          typename BinaryPred = std::less<typename std::iterator_traits<BidirIt>::value_type>>\nvoid shell_sort(BidirIt first, BidirIt last, BinaryPred comp = BinaryPred()) {\n    const size_t len = std::distance(first, last);\n    if (len <= 1) { return; }\n    for (size_t step = len / 2; step >= 1; step /= 2) {\n        for (auto it = first + step; it != last; ++it) {\n            auto target = *it;\n            auto itt    = it - step;\n            for (; std::distance(first, itt) >= 0 and comp(target, *itt); itt -= step) {\n                *(itt + step) = *itt;\n            }\n            *(itt + step) = target;\n        }\n    }\n}\n\n#endif  // SORTS_SORTS_HPP_\n\n"
  },
  {
    "path": "c-cpp/11_sorts/sorts_jinshaohui.c",
    "content": "/*************************************************************************\n > File Name: sorts_jinshaohui.c\n > Author:  jinshaohui\n > Mail:    jinshaohui789@163.com\n > Time:    18-10-19\n > Desc:    \n ************************************************************************/\n#include<stdio.h>\n#include<stdlib.h>\n#include<string.h>\n\n#define SWAP(a,b) \\\ndo{\\\n\t(a) ^= (b);\\\n\t(b) ^= (a);\\\n\t(a) ^= (b);\\\n}while(0)\n\n/*冒泡排序*/\nvoid bubble_sort(int a[],int size)\n{\n\tint i = 0;\n\tint j = 0;\n\tint swap_flg = 0;\n\n\tif (size < 1)\n\t{\n\t\treturn;\n\t}\n\n\tfor (i = size - 1; i > 0; i--)/*排序的趟数*/\n\t{\n\t\tswap_flg = 0;/*每次设置交换标识为0*/\n\t\tfor (j = 0; j < i; j++)/*本趟排序的遍历元素个数*/\n\t\t{\n\t\t\tif (a[j] > a[j + 1])\n\t\t\t{\n\t\t\t\tSWAP(a[j],a[j+1]);\n\t\t\t\tswap_flg = 1;\n\t\t\t}\n\t\t}\n        /*本趟数，无数据交换的话，说明已经有序，直接退出*/\n\t\tif (swap_flg == 0)\n\t\t{\n\t\t\tbreak;\n\t\t}\n\t}\n\treturn;\n}\n\n/*插入排序*/\nvoid insert_sort(int a[],int size)\n{\n\tint i = 0;\n\tint j = 0;\n\tint key = 0;\n\n\tfor (i = 1; i < size; i ++)/*需要插入的元素个数*/\n\t{\n\t\tkey = a[i];/*保存插入的元素数据*/\n\t\tj = i - 1;\n        /* i 之前的元素都是有序的，找到比key小的插入到他后面，\n\t\t * 比key大的，需要往后挪一个位置*/\n\t\twhile((j >= 0) && (a[j] > key))\n\t\t{\n\t\t    a[j + 1] = a[j];\n\t\t\tj--;\n\t\t}\n\t\ta[j + 1] = key;\n\t}\n\n    return;\n}\n/*选择排序*/\nvoid select_sort(int a[],int size)\n{\n\tint i = 0;\n\tint j = 0;\n\tint min = 0;\n\n\tfor (i = 0; i < size - 1; i++) \n\t{\n\t\tmin = i;\n        for (j = i + 1; j < size; j++)\n\t\t{\n\t\t\tif (a[j] < a[min])\n\t\t\t{\n\t\t\t\tmin = j;\n\t\t\t}\n\t\t}\n\n\t\tif (min != i)\n\t\t{\n\t\t\tSWAP(a[i],a[min]);\n\t\t}\n\t}\n\treturn;\n}\n\nvoid dump(int a[],int size)\n{\n\tint i = 0;\n\n\tprintf(\"\\r\\n\");\n\tfor (i = 0; i < size; i++ )\n\t{\n\t\tprintf(\"%d \",a[i]);\n\t}\n\tprintf(\"\\r\\n\");\n    return;\n}\n\nint main()\n{\n\tint a[10] = {9,11,4,15,16,3,20,44,5,10};\n\n    //bubble_sort(a,sizeof(a)/sizeof(int));\n    //insert_sort(a,sizeof(a)/sizeof(int));\n    select_sort(a,sizeof(a)/sizeof(int));\n    \n    dump(a,sizeof(a)/sizeof(int));\n\n\treturn 0;\n}\n"
  },
  {
    "path": "c-cpp/11_sorts/sorts_test.cc",
    "content": "#include <iostream>\n#include <vector>\n\n#include \"sorts.hpp\"\n\nint main() {\n    const std::vector<int> test_data{1, 2, 3, 0};\n\n    std::vector<int> a(test_data.begin(), test_data.end());\n    bubble_sort(a.begin(), a.end());\n    for (auto i : a) {\n        std::cout << i << ' ';\n    }\n    std::cout << '\\n';\n\n    std::vector<int> b(test_data.begin(), test_data.end());\n    insertion_sort(b.begin(), b.end());\n    for (auto i : b) {\n        std::cout << i << ' ';\n    }\n    std::cout << '\\n';\n\n    std::vector<int> c(test_data.begin(), test_data.end());\n    selection_sort(c.begin(), c.end());\n    for (auto i : c) {\n        std::cout << i << ' ';\n    }\n    std::cout << '\\n';\n\n    std::vector<int> d(test_data.begin(), test_data.end());\n    bubble_down_sort(d.begin(), d.end());\n    for (auto i : d) {\n        std::cout << i << ' ';\n    }\n    std::cout << '\\n';\n\n    std::vector<int> e(test_data.begin(), test_data.end());\n    shell_sort(e.begin(), e.end());\n    for (auto i : e) {\n        std::cout << i << ' ';\n    }\n    std::cout << '\\n';\n\n    return 0;\n}\n"
  },
  {
    "path": "c-cpp/12_sorts/.gitkeep",
    "content": ""
  },
  {
    "path": "c-cpp/12_sorts/merge_sort.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <stdbool.h>\n#include <string.h>\n\nvoid dump(int *arr, int size)\n{\n\tint idx;\n\n\tfor (idx = 0; idx < size; idx++)\n\t\tprintf(\"%08d\\n\", arr[idx]);\n}\n\nvoid __merge(int *arr, int p, int q, int r)\n{\n\tint *tmp;\n\tint i, j, k;\n\n\ttmp = (int*)malloc((r - p + 1) * sizeof(int));\n\n\tif (!tmp)\n\t\tabort();\n\n\tfor (i = p, j = q + 1, k = 0; i <= q && j <= r;) {\n\t\tif (arr[i] <= arr[j])\n\t\t\ttmp[k++] = arr[i++];\n\t\telse\n\t\t\ttmp[k++] = arr[j++];\n\t}\n\n\tif (i == q + 1) {\n\t\tfor (; j <= r;)\n\t\t\ttmp[k++] = arr[j++];\n\t} else {\n\t\tfor (; i <= q;)\n\t\t\ttmp[k++] = arr[i++];\n\t}\n\n\tmemcpy(arr + p, tmp, (r - p + 1) * sizeof(int));\n\tfree(tmp);\n}\n\nvoid __merge_sort(int *arr, int p, int r)\n{\n\tint q;\n\n\tif (p >= r)\n\t\treturn;\n\n\tq = (p + r) / 2;\n\t__merge_sort(arr, p, q);\n\t__merge_sort(arr, q + 1, r);\n\t__merge(arr, p, q, r);\n}\n\nvoid merge_sort(int *arr, int size)\n{\n\t__merge_sort(arr, 0, size - 1);\n}\n\nvoid merge_verify()\n{\n\tint test[10] = {5, 8, 9, 23, 67, 1, 3, 7, 31, 56};\n\n\t__merge(test, 0, 4, 9);\n\n\tdump(test, 10);\n}\n\nvoid merge_sort_test()\n{\n\tint test[10] = {5, 8, 9, 23, 67, 1, 3, 7, 31, 56};\n\n\tmerge_sort(test, 10);\n\n\tdump(test, 10);\n}\n\nint main()\n{\n\t//merge_verify();\n\tmerge_sort_test();\n\treturn 0;\n}\n"
  },
  {
    "path": "c-cpp/12_sorts/merge_sort.hpp",
    "content": "/**\n * Created by Liam Huang (Liam0205) on 2018/10/17.\n */\n\n#ifndef SORTS_MERGE_SORT_HPP_\n#define SORTS_MERGE_SORT_HPP_\n\n#include <functional>\n#include <algorithm>\n#include <iterator>\n#include <vector>\n\nnamespace detail {\ntemplate <typename InputIt1, typename InputIt2, typename OutputIt,\n          typename BinaryPred = std::less<typename std::iterator_traits<InputIt1>::value_type>>\nOutputIt merge(InputIt1 first1, InputIt1 last1,\n               InputIt2 first2, InputIt2 last2,\n               OutputIt d_first,\n               BinaryPred comp = BinaryPred()) {\n    for (; first1 != last1; ++d_first) {\n        if (first2 == last2) {\n            return std::copy(first1, last1, d_first);\n        }\n        if (comp(*first2, *first1)) {\n            *d_first = *first2;\n            ++first2;\n        } else {\n            *d_first = *first1;\n            ++first1;\n        }\n    }\n    return std::copy(first2, last2, d_first);\n}\n}  // namespace detail\n\ntemplate <typename FrwdIt,\n          typename BinaryPred = std::less<typename std::iterator_traits<FrwdIt>::value_type>>\nvoid merge_sort(FrwdIt first, FrwdIt last, BinaryPred comp = BinaryPred()) {\n    const auto len = std::distance(first, last);\n    if (len <= 1) { return; }\n    auto cut = first + len / 2;\n    merge_sort(first, cut, comp);\n    merge_sort(cut, last, comp);\n    std::vector<typename std::iterator_traits<FrwdIt>::value_type> tmp;\n    tmp.reserve(len);\n    detail::merge(first, cut, cut, last, std::back_inserter(tmp), comp);\n    std::copy(tmp.begin(), tmp.end(), first);\n}\n\ntemplate <typename BidirIt,\n          typename BinaryPred = std::less<typename std::iterator_traits<BidirIt>::value_type>>\nvoid inplace_merge_sort(BidirIt first, BidirIt last, BinaryPred comp = BinaryPred()) {\n    const auto len = std::distance(first, last);\n    if (len <= 1) { return; }\n    auto cut = first + len / 2;\n    inplace_merge_sort(first, cut, comp);\n    inplace_merge_sort(cut, last, comp);\n    std::inplace_merge(first, cut, last, comp);\n}\n\n#endif  // SORTS_MERGE_SORT_HPP_\n\n"
  },
  {
    "path": "c-cpp/12_sorts/merge_sort_test.cc",
    "content": "/**\n * Created by Liam Huang (Liam0205) on 2018/10/17.\n */\n\n#include <iostream>\n#include <vector>\n#include \"merge_sort.hpp\"\n\nint main() {\n    const std::vector<int> test_data{0, -1, 3, 190, -500};\n\n    std::vector<int> a{test_data};\n    merge_sort(a.begin(), a.end());\n    for (auto i : a) {\n        std::cout << i << ' ';\n    }\n    std::cout << std::endl;\n\n    std::vector<int> b{test_data};\n    inplace_merge_sort(b.begin(), b.end());\n    for (auto i : b) {\n        std::cout << i << ' ';\n    }\n    std::cout << std::endl;\n\n    return 0;\n}\n\n"
  },
  {
    "path": "c-cpp/12_sorts/my12_sorts/merge_sort.c",
    "content": "/*************************************************************************\n > File Name: merge_sort.c\n > Author:  jinshaohui\n > Mail:    jinshaohui789@163.com\n > Time:    18-10-19\n > Desc:    \n ************************************************************************/\n#include<stdio.h>\n#include<assert.h>\n#include<string.h>\n#include<stdlib.h>\n\n#define SORT_MAX  (1000000)\n\nvoid dump(int a[],int size);\nvoid merge_sentry(int a[],int middle,int left,int right)\n{\n\tint *pleft = NULL;\n\tint *pright = NULL;\n\tint i = 0;\n\tint j = 0;\n\tint k = 0;\n\tint left_size = middle - left + 1;\n\tint right_size = right - middle;\n\n\t\n\tpleft = (int *)malloc(sizeof(int)*(left_size + 1));\n\tassert(pleft != NULL);\n\tpright = (int *)malloc(sizeof(int)*(right_size + 1));\n\tassert(pright != NULL);\n\n\tfor(i = 0; i < left_size; i ++)\n\t{\n\t\tpleft[i] = a[left + i];\n\t}\n\tpleft[left_size] = SORT_MAX;\n\tfor(i = 0; i < right_size; i ++)\n\t{\n\t\tpright[i] = a[middle + 1 + i];\n\t}\n\tpright[right_size] = SORT_MAX;\n\n    for (k = left,i = 0,j = 0; k <= right; k++)\n\t{\n\t\tif (pleft[i] <= pright[j])\n\t\t{\n\t\t\ta[k] = pleft[i++];\n\t\t}\n\t\telse\n\t\t{\n\t\t\ta[k] = pright[j++];\n\t\t}\n\t}\n\n\tfree(pleft);\n\tfree(pright);\n\n    return;\n}\n\n/*两个有序数组合并*/\nvoid merge(int a[],int middle,int left,int right)\n{\n    int *tmp = NULL;\n\tint i = 0;\n\tint j = 0;\n\tint k = 0;\n\n\ttmp = (int*)malloc((right - left + 1)*sizeof(int));\n    assert(tmp != NULL);\n\n\ti = left;\n\tj = middle + 1;\n\n\twhile(1)\n\t{\n\t    if((i > middle) || (j > right))\n\t    {\n\t\t\tbreak;\n\t    }\n\n\t\tif (a[i] > a[j])\n\t\t{\n\t\t\ttmp[k++] = a[j++];\n\t\t}\n\t\telse\n\t\t{\n\t\t\ttmp[k++] = a[i++];\n\t\t}\n\t}\n\n\tif (i > middle)\n\t{\n\t\twhile(j <= right)\n\t\t{\n\t\t\ttmp[k++] = a[j++];\n\t\t}\n\t}\n\telse\n\t{\n\t\twhile(i <= middle)\n\t\t{\n\t\t\ttmp[k++] = a[i++];\n\t\t}\n\t}\n\n\tmemcpy((a + left),tmp,(right - left + 1)*sizeof(int));\n\n\tfree(tmp);\n\n\treturn  ;\n}\n\nvoid merge_sort(int a[],int left,int right)\n{\n    int middle = 0;\n\n\tif(left >= right)\n\t{\n\t\treturn;\n\t}\n\n\tmiddle = (left + right)/2;\n\n\tmerge_sort(a,left,middle);\n\tmerge_sort(a,middle + 1,right);\n\n    merge_sentry(a,middle,left,right);\n\n\treturn;\n}\n\nvoid dump(int a[],int size)\n{\n\tint i = 0;\n\n\tif(size == 0)\n\t{\n\t\treturn;\n\t}\n\n\tprintf(\"\\r\\n\");\n\tfor (i = 0; i < size; i++ )\n\t{\n\t\tprintf(\"%d \",a[i]);\n\t}\n\tprintf(\"\\r\\n\");\n    return;\n}\nint main()\n{\n    int a[10] = {30,20,10,15,4,8,40,80,20,9};\n    \n\tmerge_sort(a,0,9);\n\n\tdump(a,10);\n\n\treturn 0;\n}\n\n"
  },
  {
    "path": "c-cpp/12_sorts/my12_sorts/quick_sort.c",
    "content": "/*************************************************************************\n > File Name: quick_sort.c\n > Author:  jinshaohui\n > Mail:    jinshaohui789@163.com\n > Time:    18-10-19\n > Desc:    \n ************************************************************************/\n#include<stdio.h>\n#include<assert.h>\n#include<string.h>\n#include<stdlib.h>\n\n/* SWAP 使用必须主要，不能是同一个数据进行交换*/\n#define SWAP(a,b) \\\ndo{\\\n\t(a) ^= (b);\\\n\t(b) ^= (a);\\\n\t(a) ^= (b);\\\n}while(0)\n\nint partition2(int a[],int left,int right)\n{\n   int i = left;\n   int j = left;\n\n   for(; j <  right;j++)\n   {\n\t   if (a[j] < a[right])\n\t   {\n\t\t   if(i != j)\n\t\t   {\n\t\t\t   SWAP(a[i],a[j]);\n\t\t   }\n\t\t   i++;\n\t   }\n   }\n\n   if(i != right)\n   {\n       SWAP(a[i],a[right]);\n   }\n\n   return i;\n}\n\nint partition(int a[],int left,int right)\n{\n   int i = left;\n   int j = right;\n   int key = a[left];\n\n   while(i < j)\n   {\n\t   while((i < j)&& (a[j] >= key))\n\t   {\n\t\t   j--;\n\t   }\n\t   if (i < j)\n\t   {\n\t\t   a[i] = a[j];\n\t   }\n\t   while((i < j) && a[i] <= key)\n\t   {\n\t\t   i++;\n\t   }\n\n\t   if (i<j)\n\t   {\n\t\t   a[j] = a[i];\n\t   }\n   }\n   a[i] = key;\n\n   return i;\n}\n\nvoid quick_sort(int a[],int left,int right)\n{\n\tint q = 0;\n\t/*递归终止条件*/\n\tif (left >= right)\n\t{\n\t\treturn;\n\t}\n\n\tq = partition2(a,left,right);\n\tquick_sort(a,left,(q - 1));\n\tquick_sort(a,(q + 1),right);\n\treturn;\n}\n\nvoid dump(int a[],int size)\n{\n\tint i = 0;\n\n\tprintf(\"\\r\\n\");\n\tfor (i = 0; i < size; i++ )\n\t{\n\t\tprintf(\"%d \",a[i]);\n\t}\n\tprintf(\"\\r\\n\");\n    return;\n}\n\nint helper(int a[],int left,int right,int k)\n{\n\tint q = 0;\n\n\tq = partition(a,left,right);\n\n\tif (q > (k - 1))\n\t{\n\t\treturn helper(a,left,q-1,k);\n\t}\n\telse if (q < (k - 1))\n\t{\n\t\treturn helper(a,q+1,right,k);\n\t}\n\n\treturn a[q];\n}\n\n/*求无序数组中从小到大第K个元素的数值*/\nint findKthlagest(int a[],int size,int k)\n{\n\treturn helper(a,0,size-1,k);\n}\n\n\nint main()\n{\n    int a[10] = {30,20,10,15,4,8,40,80,20,9};\n    int k;\n\n\tscanf(\"%d\",&k);\n\n\tprintf(\"\\r\\n从小到大排序，第%d个元素数值是%d\",k,findKthlagest(a,10,k));\n\n\tquick_sort(a,0,9);\n\n\tdump(a,10);\n\n\treturn 0;\n}\n\n"
  },
  {
    "path": "c-cpp/12_sorts/quick_sort.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <stdbool.h>\n#include <string.h>\n\nvoid dump(int *arr, int size)\n{\n\tint idx;\n\n\tfor (idx = 0; idx < size; idx++)\n\t\tprintf(\"%08d\\n\", arr[idx]);\n}\n\nvoid swap(int *a, int *b)\n{\n\tint tmp = *a;\n\t*a = *b;\n\t*b = tmp;\n}\n\nint partition(int *arr, int p, int r)\n{\n\t//int pivot = arr[r];\n\tint i, j;\n\n\ti = j = p;\n\n\tfor (; j < r; j++) {\n\t\tif (arr[j] < arr[r]) {\n\t\t\tif(i != j)\n\t\t\t{\n\t\t\tswap(arr + i, arr + j);\n\n\t\t\t}\n\t\t\ti++;\n\t\t}\n\t}\n\t\n\tswap(arr + i, arr + r);\n\treturn i;\n}\n\nvoid __quick_sort(int *arr, int p, int r)\n{\n\tint q;\n\n\tif (p >= r)\n\t\treturn;\n\n\tq = partition(arr, p, r);\n\t__quick_sort(arr, p, q-1);\n\t__quick_sort(arr, q+1, r);\n}\n\nvoid quick_sort(int *arr, int size)\n{\n\t__quick_sort(arr, 0, size - 1);\n}\n\nvoid quick_sort_test()\n{\n\tint test[10] = {5, 8, 9, 23, 67, 1, 3, 7, 31, 56};\n\n\tquick_sort(test, 10);\n\n\tdump(test, 10);\n}\n\nint main()\n{\n\tquick_sort_test();\n\treturn 0;\n}\n"
  },
  {
    "path": "c-cpp/12_sorts/quick_sort.hpp",
    "content": "/**\n * Created by Liam Huang (Liam0205) on 2018/10/17.\n */\n\n#ifndef SORTS_QUICK_SORT_HPP_\n#define SORTS_QUICK_SORT_HPP_\n\n#include <functional>\n#include <iterator>\n#include <algorithm>\n#include <utility>\n\nnamespace detail {\ntemplate <typename T, typename Compare = std::less<T>>\nconst T& median(const T& a, const T& b, const T& c, Compare comp = Compare()) {\n    if (comp(a, b) and comp(b, c) or comp(c, b) and comp(b, a)) {\n        return b;\n    } else if (comp(b, c) and comp(c, a) or comp(a, c) and comp(c, b)) {\n        return c;\n    } else {\n        return a;\n    }\n}\n\ntemplate <typename Iter,\n          typename T = typename std::iterator_traits<Iter>::value_type,\n          typename Compare = std::less<T>>\nconst T& iter_median(Iter a, Iter b, Iter c, Compare comp = Compare()) {\n    return median(*a, *b, *c, comp);\n}\n\ntemplate <typename BidirIt,\n          typename T = typename std::iterator_traits<BidirIt>::value_type,\n          typename Compare = std::less<T>>\nstd::pair<BidirIt, BidirIt> inplace_partition(BidirIt first,\n                                              BidirIt last,\n                                             const T& pivot,\n                                           Compare comp = Compare()) {\n    BidirIt last_less, last_greater, first_equal, last_equal;\n    for (last_less = first, last_greater = first, first_equal = last;\n                                         last_greater != first_equal; ) {\n        if (comp(*last_greater, pivot)) {\n            std::iter_swap(last_greater++, last_less++);\n        } else if (comp(pivot, *last_greater)) {\n            ++last_greater;\n        } else {  // pivot == *last_greater\n            std::iter_swap(last_greater, --first_equal);\n        }\n    }\n    const auto cnt = std::distance(first_equal, last);\n    std::swap_ranges(first_equal, last, last_less);\n    first_equal    = last_less;\n    last_equal     = first_equal + cnt;\n    return {first_equal, last_equal};\n}\n}  // namespace detail\n\ntemplate <typename BidirIt,\n          typename T = typename std::iterator_traits<BidirIt>::value_type,\n          typename Compare = std::less<T>>\nvoid quick_sort(BidirIt first, BidirIt last, Compare comp = Compare()) {\n    for (auto size = std::distance(first, last); size > 1; size = std::distance(first, last)) {\n        const T pivot = detail::iter_median(first, last - 1, first + size / 2, comp);\n        const auto eq = detail::inplace_partition(first, last, pivot, comp);\n        quick_sort(first, eq.first, comp);\n        first = eq.second;  // Liam Huang: economize half of recursive calling.\n    }\n}\n\n#endif  // SORTS_QUICK_SORT_HPP_\n\n"
  },
  {
    "path": "c-cpp/12_sorts/quick_sort_test.cc",
    "content": "/**\n * Created by Liam Huang (Liam0205) on 2018/10/17.\n */\n\n#include <iostream>\n#include <vector>\n#include <algorithm>\n\n#include \"quick_sort.hpp\"\n\nvoid test_quick_sort(std::vector<int> test_data) {\n    quick_sort(test_data.begin(), test_data.end());\n    std::transform(test_data.begin(), test_data.end(),\n            std::ostream_iterator<int>(std::cout, \" \"), [](int i){ return i; });\n    std::cout << '\\n';\n}\n\nint main() {\n    test_quick_sort({-3, -1, 1, -2, -3, 0, -3, 100, 1, 1, -100});\n    test_quick_sort({1, 1, 1});\n    test_quick_sort({1, 0, -1});\n    test_quick_sort({1});\n    return 0;\n}\n\n"
  },
  {
    "path": "c-cpp/13_sorts/.gitkeep",
    "content": ""
  },
  {
    "path": "c-cpp/13_sorts/bucket_sort.hpp",
    "content": "/**\n * Created by Liam Huang (Liam0205) on 2018/10/26.\n */\n\n#ifndef SORTS_BUCKET_SORT_HPP_\n#define SORTS_BUCKET_SORT_HPP_\n\n#include <iterator>\n#include <functional>\n#include <algorithm>\n#include <vector>\n\ntemplate <size_t BucketSize,\n          typename IterT,\n          typename T = typename std::iterator_traits<IterT>::value_type,\n          typename Compare = std::less<T>>\nvoid bucket_sort(IterT first, IterT last, Compare comp = Compare()) {\n    const T min = *std::min_element(first, last), max = *std::max_element(first, last);\n    const T range = max + 1 - min;\n    const size_t bucket_num = (range - 1) / BucketSize + 1;\n\n    std::vector<std::vector<T>> buckets(bucket_num);\n    for (auto b : buckets) {\n        b.reserve(2 * BucketSize);\n    }\n\n    for (IterT i = first; i != last; ++i) {\n        size_t idx = (*i - min) / BucketSize;\n        buckets[idx].emplace_back(*i);\n    }\n\n    IterT dest = first;\n    for (auto b : buckets) {\n        std::sort(b.begin(), b.end(), comp);\n        std::copy(b.begin(), b.end(), dest);\n        dest += b.size();\n    }\n\n    return;\n}\n\n#endif  // SORTS_BUCKET_SORT_HPP_\n\n"
  },
  {
    "path": "c-cpp/13_sorts/bucket_sort_test.cc",
    "content": "/**\n * Created by Liam Huang (Liam0205) on 2018/10/26.\n */\n\n#include <iostream>\n#include <vector>\n#include <functional>\n\n#include \"bucket_sort.hpp\"\n\ntemplate <size_t BucketSize,\n          typename Container,\n          typename T = typename Container::value_type,\n          typename Compare = std::less<T>>\nvoid test_bucket_sort(Container cont, Compare comp = Compare()) {\n    bucket_sort<BucketSize>(cont.begin(), cont.end(), comp);\n    std::transform(cont.begin(), cont.end(), std::ostream_iterator<T>(std::cout, \" \"),\n            [](T i){ return i; });\n    std::cout << std::endl;\n}\n\nint main() {\n    std::vector<int> test{3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5, 8, 9, 7, 9};\n\n    test_bucket_sort<2>(test);  // 1 1 2 3 3 4 5 5 5 6 7 8 9 9 9\n    test_bucket_sort<3>(test);  // 1 1 2 3 3 4 5 5 5 6 7 8 9 9 9\n    test_bucket_sort<4>(test);  // 1 1 2 3 3 4 5 5 5 6 7 8 9 9 9\n    test_bucket_sort<5>(test);  // 1 1 2 3 3 4 5 5 5 6 7 8 9 9 9\n    test_bucket_sort<6>(test);  // 1 1 2 3 3 4 5 5 5 6 7 8 9 9 9\n\n    return 0;\n}\n\n"
  },
  {
    "path": "c-cpp/13_sorts/counting_sort.hpp",
    "content": "/**\n * Created by Liam Huang (Liam0205) on 2018/10/26.\n */\n\n#ifndef SORTS_COUNTING_SORT_HPP_\n#define SORTS_COUNTING_SORT_HPP_\n\n#include <iterator>\n#include <functional>\n#include <algorithm>\n#include <vector>\n\ntemplate <typename IterT,\n          typename T = typename std::iterator_traits<IterT>::value_type>\nvoid counting_sort(IterT first, IterT last) {\n    const auto len = std::distance(first, last);\n    if (len < 2) { return; }\n\n    const T max = *std::max_element(first, last);\n    if (max == 0) { return; }\n\n    std::vector<size_t> counter(max + 1);\n    for (IterT i = first; i != last; ++i) {\n        ++counter[*i];\n    }\n    for (size_t i = 1; i != max + 1; ++i) {\n        const size_t j = max - i;\n        counter[j] += counter[j + 1];  // Liam Huang: count of numbers that is not less than j.\n    }\n\n    std::vector<T> temp(len);\n    for (IterT i = first; i != last; ++i) {\n        temp[len - counter[*i]] = *i;\n        --counter[*i];                 // Liam Huang: stable for relative position.\n    }\n    std::copy(temp.begin(), temp.end(), first);\n}\n\n#endif  // SORTS_COUNTING_SORT_HPP_\n\n"
  },
  {
    "path": "c-cpp/13_sorts/counting_sort_test.cc",
    "content": "/**\n * Created by Liam Huang (Liam0205) on 2018/10/26.\n */\n\n#include <iostream>\n#include <vector>\n#include <functional>\n\n#include \"counting_sort.hpp\"\n\ntemplate <typename Container,\n          typename T = typename Container::value_type>\nvoid test_counting_sort(Container cont) {\n    counting_sort(cont.begin(), cont.end());\n    std::transform(cont.begin(), cont.end(), std::ostream_iterator<T>(std::cout, \" \"),\n            [](T i){ return i; });\n    std::cout << std::endl;\n}\n\nint main() {\n    // Liam Huang: pi for test\n    const std::vector<int> test1{3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5, 8, 9, 7, 9, 3};\n    const std::vector<int> test2{2, 3, 8, 4, 6, 2, 6, 4, 3, 3, 8, 3, 2, 7, 9};\n    const std::vector<int> test3{5, 0, 2, 8, 8, 4, 1, 9, 7, 1, 6, 9, 3, 9, 9};\n    const std::vector<int> test4{3, 7, 5, 1, 0, 5, 8, 2, 0, 9, 7, 4, 9, 4, 4};\n    const std::vector<int> test5{5, 9, 2, 3, 0, 7, 8, 1, 6, 4, 0, 6, 2, 8, 6};\n\n    test_counting_sort(test1);  // 1 1 2 3 3 3 4 5 5 5 6 7 8 9 9 9\n    test_counting_sort(test2);  // 2 2 2 3 3 3 3 4 4 6 6 7 8 8 9\n    test_counting_sort(test3);  // 0 1 1 2 3 4 5 6 7 8 8 9 9 9 9\n    test_counting_sort(test4);  // 0 0 1 2 3 4 4 4 5 5 7 7 8 9 9\n    test_counting_sort(test5);  // 0 0 1 2 2 3 4 5 6 6 6 7 8 8 9\n\n    return 0;\n}\n\n"
  },
  {
    "path": "c-cpp/13_sorts/sort.c",
    "content": "/*************************************************************************\n > File Name: sort.c\n > Author:  jinshaohui\n > Mail:    jinshaohui789@163.com\n > Time:    18-10-20\n > Desc:    \n ************************************************************************/\n#include<stdio.h>\n#include<stdlib.h>\n#include<string.h>\n#include<math.h>\n#include<assert.h>\n\nvoid dump(int a[],int size)\n{\n\tint i = 0;\n\n\tprintf(\"\\r\\n\");\n\n\tfor(i = 0; i <size; i++)\n\t{\n\t\tprintf(\"%d \",a[i]);\n\t}\n\tprintf(\"\\r\\n\");\n}\n\n/*计数排序，时间复杂度0（n)，非原地排序\n *计数排序也是利用桶排序的解决方式\n * 如果数组最大值max比数组大小size大很多不适合；\n * 计数排序要求时非负整数\n * */\nvoid count_sort(int a[],int size)\n{\n\tint i = 0;\n\tint max = 0;\n\tint *count = 0;\n\tint *res = 0;\n\n\t/*找到最大数*/\n\tfor (i = 0 ; i< size; i++)\n\t{\n\t\tif (a[i] > max)\n\t\t{\n\t\t\tmax = a[i];\n\t\t}\n\t}\n\n\tcount = (int *)malloc(sizeof(int)*(max + 1));\n\tassert(count != NULL);\n\n\tmemset(count,0,sizeof(int)*(max + 1));\n\n\t/*计数*/\n\tfor (i = 0; i < size;i++)\n\t{\n\t\tcount[a[i]]++;\n\t}\n\t\n\t/*依次累加*/\n\tfor(i = 1 ;i <= max; i ++)\n\t{\n\t\tcount[i] += count[i-1];\n\t}\n\n\tres = (int *)malloc(sizeof(int)*(size));\n\tassert(res != NULL);\n    /*核心代码，count[a[i] - 1]就是排序好的下标*/\n\tfor (i = size-1;i >= 0; i--)\n\t{\n\t\tres[count[a[i]] -1] = a[i];\n\t\tcount[a[i]]--;\n\t}\n    \n\tmemcpy(a,res,size*(sizeof(int)));\n\n\tfree(res);\n\tfree(count);\n\treturn;\n}\n\n\nint count_sort_test()\n{\n\tint a [10]={1,5,6,8,10,9,3,1,2,1};\n    printf(\"\\r\\n conunt sort test ....\");\n\tcount_sort(a,10);\n\tdump(a,10);\n\n\treturn 0;\n}\n\n#define NUM_OF_POS(a,pval) ((a)/pval)%10\nvoid radix_sort(int a[],int size,int num_count)\n{\n\tint count[10] = {0}; /*计数*/\n\tint *pres = NULL;\n\tint i = 0;\n\tint j = 0;\n\tint pval = 10;\n\tint index = 0;\n\tint break_flg = 0;\n\n\tpres = (int *)malloc(sizeof(int)*size);\n\tassert(pres != NULL);\n\n\tfor (i = 0; i < num_count; i ++)\n\t{\n\t\tmemset(count,0,sizeof(int)*10);\n\n\t\t/*求当前的基数*/\n        pval = pow(10,i);\n\n\t    /*计数*/\n\t\tfor (j = 0; j < size; j++)\n\t\t{\n\t\t\tindex = NUM_OF_POS(a[j],pval);\n\t\t\tcount[index]++;\n\t\t}\n\n\t\t/*小的优化，可能位数最大的就1，其他的位数差很多*/\n\t\tif(count[0] == 9)\n\t\t{\n\t\t\tbreak_flg++;\n\t\t}\n\n\t\tif(break_flg >=2)\n\t\t{\n\t\t\tprintf(\"\\r\\n %i\",i);\n\t\t\tbreak;\n\t\t}\n\n\t\t/*累加*/\n\t\tfor(j = 1; j < 10; j ++)\n\t\t{\n\t\t\tcount[j] += count[j-1];\n\t\t}\n\n\t\t/*排序必须从后往前，否则不是稳定排序*/\n\t\tfor(j = size -1; j >= 0; j--)\n\t\t{\n\t\t\tindex = NUM_OF_POS(a[j],pval);\n            pres[count[index] - 1] = a[j];\n\t\t\tcount[index]--;\n\t\t}\n        /*本轮排序好的，拷贝到a中*/\n\t\tmemcpy(a,pres,sizeof(int)*size);\n\t}\n\n\treturn;\n}\n\nvoid radix_sort_test()\n{\n\tint a[10] = {123,12341,1232134,124,236,128,1112313129,98,9,8989};\n    printf(\"\\r\\n radix sort test.....\");\n\tradix_sort(a,10,10);\n\tdump(a,10);\n\treturn;\n}\n\nstruct barrel {   \n    int node[10];   \n    int count;/* the num of node */  \n};   \nint partition(int a[],int left,int right)\n{\n   int i = left;\n   int j = right;\n   int key = a[left];\n\n   while(i < j)\n   {\n\t   while((i < j)&& (a[j] >= key))\n\t   {\n\t\t   j--;\n\t   }\n\t   if (i < j)\n\t   {\n\t\t   a[i] = a[j];\n\t   }\n\t   while((i < j) && a[i] <= key)\n\t   {\n\t\t   i++;\n\t   }\n\n\t   if (i<j)\n\t   {\n\t\t   a[j] = a[i];\n\t   }\n   }\n   a[i] = key;\n\n   return i;\n}\n\nvoid quick_sort(int a[],int left,int right)\n{\n\tint q = 0;\n\t/*递归终止条件*/\n\tif (left >= right)\n\t{\n\t\treturn;\n\t}\n\n\tq = partition(a,left,right);\n\tquick_sort(a,left,(q - 1));\n\tquick_sort(a,(q + 1),right);\n\treturn;\n}\n  \nvoid bucket_sort(int data[], int size)   \n{   \n    int max, min, num, pos;   \n    int i, j, k;   \n    struct barrel *pBarrel;   \n  \n    max = min = data[0];   \n    for (i = 1; i < size; i++) {   \n        if (data[i] > max) {   \n            max = data[i];   \n        } else if (data[i] < min) {   \n            min = data[i];   \n        }   \n    }   \n    num = (max - min + 1) / 10 + 1;   \n    pBarrel = (struct barrel*)malloc(sizeof(struct barrel) * num);   \n    memset(pBarrel, 0, sizeof(struct barrel) * num);   \n  \n    /* put data[i] into barrel which it belong to */  \n    for (i = 0; i < size; i++) {   \n        k = (data[i] - min + 1) / 10;/* calculate the index of data[i] in barrel */  \n        (pBarrel + k)->node[(pBarrel + k)->count] = data[i];   \n        (pBarrel + k)->count++;   \n    }   \n       \n    pos = 0;   \n    for (i = 0; i < num; i++) {\n\t\tif ((pBarrel + i)->count != 0)\n\t\t{\n            quick_sort((pBarrel+i)->node, 0, ((pBarrel+i)->count)-1);/* sort node in every barrel */  \n  \n            for (j = 0; j < (pBarrel+i)->count; j++) {   \n                data[pos++] = (pBarrel+i)->node[j];   \n            }\n\t\t}\n    }   \n    free(pBarrel);   \n}\n\nvoid bucket_sort_test()\n{\n\tint a[] = {78, 17, 39, 26, 72, 94, 21, 12, 23, 91};   \n    int size = sizeof(a) / sizeof(int);   \n\tprintf(\"\\r\\n bucket sort test ...\");\n    bucket_sort(a, size); \n\tdump(a,size);\n\t\n}\n\nint main()\n{\n    count_sort_test();\n\n\tradix_sort_test();\n\n\tbucket_sort_test();\n\treturn 0;\n}\n"
  },
  {
    "path": "c-cpp/14_sorts/.gitkeep",
    "content": ""
  },
  {
    "path": "c-cpp/14_sorts/analytics_of_std_sort.md",
    "content": "# C++ STL 中的 std::sort 分析\n\n参见 [Liam Huang 的博客](https://liam.page/)中的 3 篇文章：\n\n* [谈谈基于比较的排序算法的复杂度下界](https://liam.page/2018/08/28/lower-bound-of-comparation-based-sort-algorithm/)\n* [谈谈内省式排序算法](https://liam.page/2018/08/29/introspective-sort/)\n* [谈谈 STL 中的 std::sort](https://liam.page/2018/09/18/std-sort-in-STL/)\n\n"
  },
  {
    "path": "c-cpp/14_sorts/counting_sort.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <stdbool.h>\n#include <string.h>\n\nvoid dump(int *arr, int size)\n{\n\tint i;\n\n\tfor (i = 0; i < size; i++)\n\t\tprintf(\"%08d\\n\", arr[i]);\n}\n\n// content in arr must be positive integer\nvoid counting_sort(int *arr, int size)\n{\n\tint max, i;\n\tint *count, *tmp;\n\n\tif (size <= 1)\n\t\treturn;\n\n\tmax = 0;\n\t// find the biggest integer\n\tfor (i = 0; i < size; i++) {\n\t\tif (max < arr[i])\n\t\t\tmax = arr[i];\n\t}\n\n\t// init count to 0\n\tcount = (int*)malloc((max+1) * sizeof(int));\n\ttmp = (int*)malloc(size * sizeof(int));\n\tif (!count || !tmp)\n\t\treturn;\n\tmemset(count, 0, (max + 1) * sizeof(int));\n\n\t// counting\n\tfor (i = 0; i < size; i++)\n\t\tcount[arr[i]]++;\n\tfor (i = 1; i < max + 1; i++)\n\t\tcount[i] = count[i-1] + count[i];\n\n\t// iterate arr and put it to the correct index in tmp\n\tfor (i = 0; i < size; i++){\n\t\tint index = count[arr[i]] - 1;\n\t\ttmp[index] = arr[i];\n\t\tcount[arr[i]]--;\n\t}\n\n\t// move back to arr\n\tmemcpy(arr, tmp, size * sizeof(int));\n}\n\nvoid counting_sort_test()\n{\n\tint test_data[10] = {3, 23, 98, 1, 27, 36, 52, 89, 76, 44};\n\n\tcounting_sort(test_data, 10);\n\tdump(test_data, 10);\n}\n\nint main()\n{\n\tcounting_sort_test();\n\treturn 0;\n}\n"
  },
  {
    "path": "c-cpp/15_bsearch/.gitkeep",
    "content": ""
  },
  {
    "path": "c-cpp/15_bsearch/binary_search.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <stdbool.h>\n#include <string.h>\n\ntypedef int(*bs)(int *arr, int size, int val); \n\nint binary_search_r(int *arr, int size, int val)\n{\n\tint mid = size / 2;\n\tint idx;\n\n\tif (arr[mid] == val)\n\t\treturn mid;\n\n\t// mid == 0 means size == 1\n\t// so the only element in array doesn't equal to val\n\tif (!mid)\n\t\treturn -1;\n\n\tif (arr[mid] < val) {\n\t\tidx = binary_search_r(arr + mid + 1, size - mid - 1, val);\n\t\tif (idx != -1)\n\t\t\tidx += mid + 1;\n\t} else {\n\t\tidx = binary_search_r(arr, mid, val);\n\t}\n\n\treturn idx;\n}\n\nint binary_search_i(int *arr, int size, int val)\n{\n\tint low = 0, high = size - 1, mid;\n\n\twhile (low <= high) {\n\t\tmid = (low + high) / 2;\n\t\tif (arr[mid] == val)\n\t\t\treturn mid;\n\n\t\tif (arr[mid] < val)\n\t\t\tlow = mid + 1;\n\t\telse\n\t\t\thigh = mid - 1;\n\t}\n\n\treturn -1;\n}\n\nvoid iteratioin_test(bs binary_search)\n{\n\tint arr[10] = {1, 4, 5, 9, 12, 19, 21, 28, 31, 36};\n\tint idx;\n\n\tidx = binary_search(arr, 10, 12);\n\tif (idx != -1)\n\t\tprintf(\"find 12 at %d\\n\", idx);\n\telse\n\t\tprintf(\"12 not in arr \\n\");\n\n\tidx = binary_search(arr, 10, 13);\n\tif (idx != -1)\n\t\tprintf(\"find 13 at %d\\n\", idx);\n\telse\n\t\tprintf(\"13 not in arr \\n\");\n\n\tidx = binary_search(arr, 10, 1);\n\tif (idx != -1)\n\t\tprintf(\"find 1 at %d\\n\", idx);\n\telse\n\t\tprintf(\"1 not in arr \\n\");\n\n\tidx = binary_search(arr, 10, 36);\n\tif (idx != -1)\n\t\tprintf(\"find 36 at %d\\n\", idx);\n\telse\n\t\tprintf(\"36 not in arr \\n\");\n\n\tidx = binary_search(arr, 10, 31);\n\tif (idx != -1)\n\t\tprintf(\"find 31 at %d\\n\", idx);\n\telse\n\t\tprintf(\"31 not in arr \\n\");\n\n}\n\nint main()\n{\n\tprintf(\"=== Test iteration version:\\n\");\n\titeratioin_test(binary_search_i);\n\n\tprintf(\"=== Test recursive version:\\n\");\n\titeratioin_test(binary_search_r);\n\n\treturn 0;\n}\n"
  },
  {
    "path": "c-cpp/15_bsearch/bsearch.hpp",
    "content": "/**\n * Created by Liam Huang (Liam0205) on 2018/10/24.\n */\n\n#ifndef BSEARCH_BSEARCH_HPP_\n#define BSEARCH_BSEARCH_HPP_\n\n#include <iterator>\n#include <functional>\n\n// Liam Huang: The algorithm works right with iterators that meet the ForwardIterator requirement,\n//             but with a bad time complexity. For better performance, iterators should meet\n//             the RandomAccessIterator requirement.\ntemplate <typename IterT,\n          typename ValueT = typename std::iterator_traits<IterT>::value_type,\n          typename Compare = std::less<ValueT>>\nIterT bsearch(IterT first,\n              IterT last,\n             ValueT target,\n            Compare comp = Compare()) {\n    IterT result = last;\n    while (std::distance(first, last) > 0) {\n        IterT mid = first + std::distance(first, last) / 2;\n        if (comp(*mid, target)) {\n            first = mid + 1;\n        } else if (comp(target, *mid)) {\n            last = mid;\n        } else {  // equal\n            result = mid;\n            break;\n        }\n    }\n    return result;\n}\n\n#endif  // BSEARCH_BSEARCH_HPP_\n\n"
  },
  {
    "path": "c-cpp/15_bsearch/bsearch_c/bsearch.c",
    "content": "/*************************************************************************\n > File Name: bsearch.c\n > Author:  jinshaohui\n > Mail:    jinshaohui789@163.com\n > Time:    18-10-21\n > Desc:    \n ************************************************************************/\n#include<stdio.h>\n#include<stdlib.h>\n#include<assert.h>\n\n\nint mybsearch(int a[],int size,int value)\n{\n\tint mid = 0;\n\tint left = 0;\n\tint right = size - 1;\n\n\twhile(left <= right)\n\t{\n\t\t/*防止size数量太大是，（left + right）数据翻转，导致问题*/\n\t\tmid = left + ((right - left)>>1);\n\n\t\tif (a[mid] == value)\n\t\t{\n\t\t\treturn mid;\n\t\t}\n\t\telse if (a[mid] < value)\n\t\t{\n\t\t\tleft = mid + 1;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tright = mid - 1;\n\t\t}\n\t}\n\n\treturn -1;\n}\n\nint helper(int a[], int left,int right,int value)\n{\n\tint mid = 0;\n\n\tif (left > right)\n\t{\n\t\treturn -1;\n\t}\n\t/*防止size数量太大是，（left + right）数据翻转，导致问题*/\n\tmid = left + ((right - left)>>1);\n\tif (a[mid] == value)\n\t{\n\t\treturn mid;\n\t}\n\telse if (a[mid] < value)\n\t{\n\t\treturn helper(a,mid + 1,right,value);\n\t}\n\telse\n\t{\n\t\treturn helper(a,left,mid - 1,value);\n\t}\n    return -1;\n}\n/*递归实现*/\nint mybsearch_2(int a[],int size,int value)\n{\n\n\treturn helper(a,0,size-1,value);\n}\n\nint main()\n{\n\tint a[10] = {5,6,8,9,10,11,23,42,53,123};\n    int data = 0;\n\tint res = 0;\n\n\tprintf(\"\\r\\n输入一个整数\");\n\tscanf(\"%d\",&data);\n    res = mybsearch(a,10,data);\n\tprintf(\"data[%d] %s 在数据中，下标是%d\",data,(res != -1)?\"\":\"不\",res);\n\t\n\tprintf(\"\\r\\n输入一个整数\");\n\tscanf(\"%d\",&data);\n    res = mybsearch_2(a,10,data);\n\tprintf(\"data[%d] %s 在数据中，下标是%d\",data,(res != -1)?\"\":\"不\",res);\n\treturn;\n}\n"
  },
  {
    "path": "c-cpp/15_bsearch/bsearch_c/sqrt.c",
    "content": "/*************************************************************************\n > File Name: sqrt.c\n > Author:  jinshaohui\n > Mail:    jinshaohui789@163.com\n > Time:    18-10-31\n > Desc:    \n ************************************************************************/\n#include<stdio.h>\n#include<string.h>\n#include<stdlib.h>\n#include<assert.h>\n\n\n/*求解精度设置*/\n#define E 0.000001 \ndouble mybsearch(double num)\n{\n\tdouble start = 1.0;\n\tdouble end = num;\n    double mid = 0.0;\n\twhile(1)\n\t{\n\t    mid = (start + end)/2;\n        if(((mid*mid - num) <= E) && ((mid*mid - num) >= -E))\n\t\t{\n\t\t\treturn mid;\n\t\t}\n\n\t\tif ((mid*mid - num) > E)\n\t\t{\n\t\t\tend = mid;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tstart = mid;\n\t\t}\n \t}\n\n\treturn 0;\n}\n\t\n\nint main()\n{\n\tdouble num = 0.0;\n\n\t/*这里需要注意：double的输入方式*/\n\tscanf(\"%lf\",&num);\n\tprintf(\"\\r\\n num %lf的平方根是%lf\",num,mybsearch(num));\n\n\treturn 0;\n}\n\n"
  },
  {
    "path": "c-cpp/15_bsearch/bsearch_test.cc",
    "content": "/**\n * Created by Liam Huang (Liam0205) on 2018/10/24.\n */\n\n#include <iostream>\n#include <vector>\n\n#include \"bsearch.hpp\"\n\ntemplate <typename VecT, typename T = typename VecT::value_type>\nvoid test_bsearch(const VecT& test, T target) {\n    auto it = bsearch(test.begin(), test.end(), target);\n    std::cout << std::distance(test.begin(), it) << std::endl;\n}\n\nint main() {\n    std::vector<int> test{0, 0, 1, 2, 3, 4, 4, 5, 5, 5, 5, 5, 6, 7};  // std::less<int>()\n\n    test_bsearch(test, 8);                        // 14\n    test_bsearch(test, -1);                       // 14\n    test_bsearch(test, 0);                        // 0, 1\n    test_bsearch(test, 4);                        // 5, 6\n    test_bsearch(test, 5);                        // 7, 8, 9, 10, 11\n    test_bsearch(test, 7);                        // 13\n\n    return 0;\n}\n\n"
  },
  {
    "path": "c-cpp/16_bsearch/.gitkeep",
    "content": ""
  },
  {
    "path": "c-cpp/16_bsearch/bsearch.c",
    "content": "/*************************************************************************\n > File Name: bsearch.c\n > Author:  jinshaohui\n > Mail:    jinshaohui789@163.com\n > Time:    18-10-21\n > Desc:    \n ************************************************************************/\n#include<stdio.h>\n#include<stdlib.h>\n#include<assert.h>\n\n/*二分查找算法的变形问题\n *1、查找第一个等于给定数值的元素\n *2、查找最后一个等于给定数值的元素\n *3、查找第一个大于等于给定数值的元素\n *4、查找第一个小于等于给定数值的元素\n * */\n\n\n /*1、查找第一个等于给定数值的元素*/\nint mybsearch_1(int a[],int size,int value)\n{\n\tint mid = 0;\n\tint left = 0;\n\tint right = size - 1;\n\n\twhile(left <= right)\n\t{\n\t\t/*防止size数量太大是，（left + right）数据翻转，导致问题*/\n\t\tmid = left + ((right - left)>>1);\n\n\t\tif (a[mid] < value)\n\t\t{\n\t\t\tleft = mid + 1;\n\t\t}\n\t\telse if (a[mid] > value)\n\t\t{\n\t\t\tright = mid - 1;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif ((mid == 0) || (a[mid - 1] != value))\n\t\t\t{\n                return mid;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tright = mid - 1;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn -1;\n}\n\n /*2、查找最后一个等于给定数值的元素*/\nint mybsearch_2(int a[],int size,int value)\n{\n\tint mid = 0;\n\tint left = 0;\n\tint right = size - 1;\n\n\twhile(left <= right)\n\t{\n\t\t/*防止size数量太大是，（left + right）数据翻转，导致问题*/\n\t\tmid = left + ((right - left)>>1);\n\n\t\tif (a[mid] < value)\n\t\t{\n\t\t\tleft = mid + 1;\n\t\t}\n\t\telse if (a[mid] > value)\n\t\t{\n\t\t\tright = mid - 1;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif ((mid == (size - 1)) || (a[mid + 1] != value))\n\t\t\t{\n                return mid;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tleft = mid + 1;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn -1;\n}\n /*3、查找第一个大于等于给定数值的元素*/\nint mybsearch_3(int a[],int size,int value)\n{\n\tint mid = 0;\n\tint left = 0;\n\tint right = size - 1;\n\n\twhile(left <= right)\n\t{\n\t\t/*防止size数量太大是，（left + right）数据翻转，导致问题*/\n\t\tmid = left + ((right - left)>>1);\n\n\t\tif (a[mid] < value)\n\t\t{\n\t\t\tleft = mid + 1;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t/*a[mid] >= value 当mid==0 或者a[mid-1] > value 说明是第一个大于等于value*/\n\t\t\tif ((mid == 0) || (a[mid - 1] < value))\n\t\t\t{\n                return mid;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tright = mid - 1;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn -1;\n}\n\n /*4、查找第一个小于等于给定数值的元素*/\nint mybsearch_4(int a[],int size,int value)\n{\n\tint mid = 0;\n\tint left = 0;\n\tint right = size - 1;\n\n\twhile(left <= right)\n\t{\n\t\t/*防止size数量太大是，（left + right）数据翻转，导致问题*/\n\t\tmid = left + ((right - left)>>1);\n\n\t\tif (a[mid] > value)\n\t\t{\n\t\t\tright = mid - 1;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t/*a[mid] <= value 时，当前mid == size -1 数组中最大的数值；\n\t\t\t *                    或者a[mid + 1] 大于vlaue，就是mid就第一个小于等于value*/\n\t\t\tif ((mid == (size - 1)) || (a[mid + 1] > value))\n\t\t\t{\n                return mid;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tleft = mid + 1;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn -1;\n}\nint main()\n{\n\tint a[10] = {5,6,6,9,10,11,11,22,33,33};\n    int data = 0;\n\tint i = 0;\n\tint res =0;\n\n\tprintf(\"\\r\\n\");\n    for(i = 0; i < 10 ; i++)\n\t{\n\t\tprintf(\"%d \",a[i]);\n\t}\n\tprintf(\"\\r\\n\");\n\tprintf(\"\\r\\n输入一个整数\");\n\tscanf(\"%d\",&data);\n    res = mybsearch_1(a,10,data);\n\tprintf(\"第一个等于data[%d]，下标是%d\",data,res);\n\t\n\tprintf(\"\\r\\n输入一个整数\");\n\tscanf(\"%d\",&data);\n    res = mybsearch_2(a,10,data);\n\tprintf(\"最后一个等于data[%d]，下标是%d\",data,res);\n\n\tprintf(\"\\r\\n输入一个整数\");\n\tscanf(\"%d\",&data);\n    res = mybsearch_2(a,10,data);\n\tprintf(\"第一个大于等于data[%d]，下标是%d\",data,res);\n\n\tprintf(\"\\r\\n输入一个整数\");\n\tscanf(\"%d\",&data);\n    res = mybsearch_2(a,10,data);\n\tprintf(\"第一个小等于data[%d]，下标是%d\",data,res);\n\treturn;\n}\n"
  },
  {
    "path": "c-cpp/16_bsearch/bsearch_variant.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <stdbool.h>\n#include <string.h>\n\n\nint binary_search(int *arr, int size, int val)\n{\n\tint low = 0, high = size - 1, mid;\n\n\twhile (low <= high) {\n\t\tmid = (low + high) / 2;\n\t\tif (arr[mid] == val)\n\t\t\treturn mid;\n\n\t\tif (arr[mid] < val)\n\t\t\tlow = mid + 1;\n\t\telse\n\t\t\thigh = mid - 1;\n\t}\n\n\treturn -1;\n}\n\n/* \n * find the first index with *val*\n *\n * This is a little tricky because the calculation of mid is integer based, it\n * will be cast to the lower bound of an integer.\n * \n * In case the [low, high] range is of size 1 or 2 and arr[mid] >= val, we will\n * have:\n *\n * mid = (low + high) / 2 = low\n * high = mid - 1 = low - 1 < low, which break the loop\n *\n */\nint binary_search_first(int *arr, int size, int val)\n{\n\tint low = 0, high = size - 1, mid;\n\n\twhile (low <= high) {\n\t\tmid = (low + high) / 2;\n\t\t//printf(\"[%d-%d] %d\\n\", low, high, mid);\n\n\t\tif (arr[mid] >= val)\n\t\t\thigh = mid - 1;\n\t\telse\n\t\t\tlow = mid + 1;\n\t}\n\n\t//printf(\"[%d-%d] %d\\n\", low, high, mid);\n\tif (arr[low] == val)\n\t\treturn low;\n\telse\n\t\treturn -1;\n}\n\nint binary_search_last(int *arr, int size, int val)\n{\n\tint low = 0, high = size - 1, mid;\n\n\twhile (low <= high) {\n\t\tmid = (low + high) / 2;\n\n\t\tif (arr[mid] <= val)\n\t\t\tlow = mid + 1;\n\t\telse\n\t\t\thigh = mid - 1;\n\t}\n\n\tif (arr[high] == val)\n\t\treturn high;\n\telse\n\t\treturn -1;\n}\n\nint binary_search_first_r(int *arr, int size, int val)\n{\n\tint mid = size / 2;\n\tint idx;\n\n\tif (size <= 0)\n\t\treturn -1;\n\n\t// we find *val* at mid, try first half\n\tif (arr[mid] == val) {\n\t\tidx = binary_search_first_r(arr, mid, val);\n\t\treturn idx != -1 ? idx : mid;\n\t}\n\n\t// mid == 0 means size == 1\n\t// so the only element in array doesn't equal to val\n\tif (!mid)\n\t\treturn -1;\n\n\tif (arr[mid] < val) {\n\t\tidx = binary_search_first_r(arr + mid + 1, size - mid - 1, val);\n\t\tif (idx != -1)\n\t\t\tidx += mid + 1;\n\t} else {\n\t\tidx = binary_search_first_r(arr, mid, val);\n\t}\n\n\treturn idx;\n}\n\nint binary_search_last_r(int *arr, int size, int val)\n{\n\tint mid = size / 2;\n\tint idx;\n\n\tif (size <= 0)\n\t\treturn -1;\n\n\t// we find *val* at mid, try last half\n\tif (arr[mid] == val) {\n\t\tidx = binary_search_last_r(arr+mid+1, size-mid-1, val);\n\t\tif (idx != -1)\n\t\t\tmid += idx + 1;\n\t\treturn mid;\n\t}\n\n\t// mid == 0 means size == 1\n\t// so the only element in array doesn't equal to val\n\tif (!mid)\n\t\treturn -1;\n\n\tif (arr[mid] < val) {\n\t\tidx = binary_search_last_r(arr + mid + 1, size - mid - 1, val);\n\t\tif (idx != -1)\n\t\t\tidx += mid + 1;\n\t} else {\n\t\tidx = binary_search_last_r(arr, mid, val);\n\t}\n\n\treturn idx;\n}\n\nint binary_search_first_bigger(int *arr, int size, int val)\n{\n\tint low = 0, high = size - 1, mid;\n\n\twhile (low <= high) {\n\t\tmid = (low + high) / 2;\n\n\t\tif (arr[mid] >= val) {\n\t\t\tif (mid == 0 || arr[mid-1] < val)\n\t\t\t\treturn mid;\n\t\t\thigh = mid - 1;\n\t\t} else {\n\t\t\tlow = mid + 1;\n\t\t}\n\t}\n\n\treturn -1;\n}\n\nint binary_search_first_bigger_r(int *arr, int size, int val)\n{\n\tint mid = size / 2;\n\tint idx;\n\n\tif (size <= 0)\n\t\treturn -1;\n\n\tif (arr[mid] >= val) {\n\t\t// find one bigger than val, try first half\n\t\tidx = binary_search_first_bigger_r(arr, mid, val);\n\t\tif (idx == -1)\n\t\t\tidx = mid;\n\t} else {\n\t\t// the bigger one may sit in second half\n\t\tidx = binary_search_first_bigger_r(arr + mid + 1, size - mid - 1, val);\n\t\tif (idx != -1)\n\t\t\tidx += mid + 1;\n\t}\n\n\treturn idx;\n}\n\nint binary_search_last_smaller(int *arr, int size, int val)\n{\n\tint low = 0, high = size - 1, mid;\n\n\twhile (low <= high) {\n\t\tmid = (low + high) / 2;\n\n\t\tif (arr[mid] <= val) {\n\t\t\tif (mid == 0 || arr[mid+1] > val)\n\t\t\t\treturn mid;\n\t\t\tlow = mid + 1;\n\t\t} else {\n\t\t\thigh = mid - 1;\n\t\t}\n\t}\n\n\treturn -1;\n}\n\nint binary_search_last_smaller_r(int *arr, int size, int val)\n{\n\tint mid = size / 2;\n\tint idx;\n\n\tif (size <= 0)\n\t\treturn -1;\n\n\tif (arr[mid] <= val) {\n\t\t// find one smaller than val, try second half\n\t\tidx = binary_search_last_smaller_r(arr + mid + 1, size - mid - 1, val);\n\t\tif (idx != -1)\n\t\t\tidx += mid + 1;\n\t\telse\n\t\t\tidx = mid;\n\t} else {\n\t\t// the smaller one may sit in first half\n\t\tidx = binary_search_last_smaller_r(arr, mid, val);\n\t}\n\n\treturn idx;\n}\n\nint main()\n{\n\tint arr[10] = {1, 4, 5, 9, 12, 14, 19, 19, 31, 36};\n\tint idx;\n\n\tprintf(\"Test Array:\\n\");\n\tfor (idx = 0; idx < 10; idx++)\n\t\tprintf(\"%8d\", arr[idx]);\n\tprintf(\"\\n\");\n\n\tidx = binary_search_first(arr, 10, 19);\n\tif (idx != -1)\n\t\tprintf(\"first 19 at %d\\n\", idx);\n\telse\n\t\tprintf(\"19 not in arr \\n\");\n\n\tidx = binary_search_first_r(arr, 10, 19);\n\tif (idx != -1)\n\t\tprintf(\"first 19 at %d\\n\", idx);\n\telse\n\t\tprintf(\"19 not in arr \\n\");\n\n\tidx = binary_search_last(arr, 10, 19);\n\tif (idx != -1)\n\t\tprintf(\"last 19 at %d\\n\", idx);\n\telse\n\t\tprintf(\"19 not in arr \\n\");\n\n\tidx = binary_search_last_r(arr, 10, 19);\n\tif (idx != -1)\n\t\tprintf(\"last 19 at %d\\n\", idx);\n\telse\n\t\tprintf(\"19 not in arr \\n\");\n\n\tidx = binary_search_first_bigger(arr, 10, 12);\n\tif (idx != -1)\n\t\tprintf(\"first bigger 12 at %d\\n\", idx);\n\telse\n\t\tprintf(\"12 not in arr \\n\");\n\n\tidx = binary_search_first_bigger_r(arr, 10, 12);\n\tif (idx != -1)\n\t\tprintf(\"first bigger 12 at %d\\n\", idx);\n\telse\n\t\tprintf(\"12 not in arr \\n\");\n\n\tidx = binary_search_last_smaller(arr, 10, 12);\n\tif (idx != -1)\n\t\tprintf(\"last smaller 12 at %d\\n\", idx);\n\telse\n\t\tprintf(\"12 not in arr \\n\");\n\n\tidx = binary_search_last_smaller_r(arr, 10, 12);\n\tif (idx != -1)\n\t\tprintf(\"last smaller 12 at %d\\n\", idx);\n\telse\n\t\tprintf(\"12 not in arr \\n\");\n\n\treturn 0;\n}\n"
  },
  {
    "path": "c-cpp/16_bsearch/bsearch_varients.hpp",
    "content": "/**\n * Created by Liam Huang (Liam0205) on 2018/10/26.\n */\n\n#ifndef BSEARCH_BSEARCH_VARIENTS_HPP_\n#define BSEARCH_BSEARCH_VARIENTS_HPP_\n\n#include <iterator>\n#include <functional>\n\nenum class BsearchPolicy { UNSPECIFIED, FIRST, LAST, FIRST_NOT_LESS, LAST_NOT_GREATER };\n\n// Liam Huang: The algorithm works right with iterators that meet the ForwardIterator requirement,\n//             but with a bad time complexity. For better performance, iterators should meet\n//             the RandomAccessIterator requirement.\ntemplate <typename IterT,\n          typename ValueT = typename std::iterator_traits<IterT>::value_type,\n          typename Compare>\nIterT bsearch(IterT first,\n              IterT last,\n             ValueT target,\n            Compare comp,\n      BsearchPolicy policy = BsearchPolicy::UNSPECIFIED) {\n    IterT result = last;\n    while (std::distance(first, last) > 0) {\n        IterT mid = first + std::distance(first, last) / 2;\n        if (policy == BsearchPolicy::FIRST_NOT_LESS) {\n            if (!comp(*mid, target)) {\n                if (mid == first or comp(*(mid - 1), target)) {\n                    result = mid;\n                    break;\n                } else {\n                    last = mid;\n                }\n            } else {\n                first = mid + 1;\n            }\n        } else if (policy == BsearchPolicy::LAST_NOT_GREATER) {\n            if (comp(target, *mid)) {\n                last = mid;\n            } else {\n                if (std::distance(mid, last) == 1 or comp(target, *(mid + 1))) {\n                    result = mid;\n                    break;\n                } else {\n                    first = mid + 1;\n                }\n            }\n        } else {  // policy == UNSPECIFIED or FIRST or LAST\n            if (comp(*mid, target)) {\n                first = mid + 1;\n            } else if (comp(target, *mid)) {\n                last = mid;\n            } else {  // equal\n                if (policy == BsearchPolicy::FIRST) {\n                    if (mid == first or comp(*(mid - 1), *mid)) {\n                        result = mid;\n                        break;\n                    } else {\n                        last = mid;\n                    }\n                } else if (policy == BsearchPolicy::LAST) {\n                    if (std::distance(mid, last) == 1 or comp(*mid, *(mid + 1))) {\n                        result = mid;\n                        break;\n                    } else {\n                        first = mid + 1;\n                    }\n                } else {\n                    result = mid;\n                    break;\n                }\n            }\n        }\n    }\n    return result;\n}\n\ntemplate <typename IterT,\n          typename ValueT = typename std::iterator_traits<IterT>::value_type,\n          typename Compare = std::less<ValueT>>\nIterT bsearch(IterT first,\n              IterT last,\n             ValueT target,\n      BsearchPolicy policy = BsearchPolicy::UNSPECIFIED) {\n\treturn bsearch(first, last, target, Compare(), policy);\n}\n\n#endif  // BSEARCH_BSEARCH_VARIENTS_HPP_\n\n"
  },
  {
    "path": "c-cpp/16_bsearch/bsearch_varients_test.cc",
    "content": "/**\n * Created by Liam Huang (Liam0205) on 2018/10/26.\n */\n\n#include <iostream>\n#include <vector>\n\n#include \"bsearch_varients.hpp\"\n\ntemplate <typename VecT, typename T = typename VecT::value_type>\nvoid test_bsearch(const VecT& test,\n                            T target,\n                BsearchPolicy policy = BsearchPolicy::UNSPECIFIED) {\n    auto it = bsearch(test.begin(), test.end(), target, policy);\n    std::cout << std::distance(test.begin(), it) << std::endl;\n}\n\nint main() {\n    std::vector<int> test{0, 0, 1, 2, 3, 4, 4, 5, 5, 5, 5, 5, 6, 8};  // std::less<int>()\n\n    test_bsearch(test, 8);                                   // 14\n    test_bsearch(test, -1);                                  // 14\n    test_bsearch(test, 0);                                   // 0, 1\n    test_bsearch(test, 0, BsearchPolicy::FIRST);             // 0\n    test_bsearch(test, 0, BsearchPolicy::LAST);              // 1\n    test_bsearch(test, 4);                                   // 5, 6\n    test_bsearch(test, 4, BsearchPolicy::FIRST);             // 5\n    test_bsearch(test, 4, BsearchPolicy::LAST);              // 6\n    test_bsearch(test, 5);                                   // 7, 8, 9, 10, 11\n    test_bsearch(test, 5, BsearchPolicy::FIRST);             // 7\n    test_bsearch(test, 5, BsearchPolicy::LAST);              // 11\n    test_bsearch(test, 7, BsearchPolicy::FIRST_NOT_LESS);    // 13\n    test_bsearch(test, 7, BsearchPolicy::LAST_NOT_GREATER);  // 12\n    test_bsearch(test, 7, BsearchPolicy::FIRST);             // 14\n    test_bsearch(test, 8);                                   // 13\n    test_bsearch(test, 8, BsearchPolicy::FIRST);             // 13\n    test_bsearch(test, 8, BsearchPolicy::LAST);              // 13\n\n    return 0;\n}\n\n"
  },
  {
    "path": "c-cpp/17_skiplist/.gitkeep",
    "content": ""
  },
  {
    "path": "c-cpp/17_skiplist/SkipList.cpp",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <iostream>\n#include <string>\n#include <cstring>\n#include <random>\n#include <ctime>\nusing namespace std;\n\n/**\n * һʵַ\n * д洢Ҵ洢ǲظġ\n *\n * C++汾.\n * JAVA汾 ԭ AuthorZHENG\n * \n * Authorpuhuaqiang\n * \n *  ṹ:\n * \n *  K           1           9\n *  K-1         1     5     9\n *  K-2         1  3  5  7  9\n *  ...             ....\n *  0(ԭʼ)  1  2  3  4  5  6  7  8  9\n */\n\nconst int MAX_LEVEL = 16;\n\n/**\n * @brief ڵ\n*/\nclass CNode\n{\npublic:\n    CNode();\n    ~CNode();\n\n    std::string toString();\n    /**\n     * @brief ȡ\n    */\n    CNode** GetIdxList();\n\n    /**\n     * @brief \n    */\n    void SetData(int v);\n    /**\n     * @brief ȡ\n    */\n    int GetData();\n    /**\n    * @brief \n    */\n    void SetLevel(int l);\nprivate:\n    /**ǰڵֵ*/\n    int m_data;\n    /** \n     * ǰڵÿȼһڵ.\n     * 2 N1 N2\n     * 1 N1 N2\n     * N1Ǳڵ, m_lpForwards[x] N2\n     * \n     * [0] ԭʼ.\n     */\n    CNode* m_lpForwards[MAX_LEVEL];\n    /**ǰڵڵ*/\n    int m_iMaxLevel;\n};\n\n/**\n * @brief \n*/\nclass CSkipList\n{\npublic:\n    CSkipList();\n    ~CSkipList();\n    /**\n     * @brief ֵָĽڵ\n     * @param v \n    */\n    CNode* Find(int v);\n    /**\n     * @brief ֵָ\n     * @param v \n    */\n    void Insert(int v);\n    /**\n     * @brief ɾֵָĽڵ\n     * @param v \n    */\n    int Delete(int v);\n    void PrintAll();\n    /**\n     * @brief ӡṹ\n     * @param l -1ʱӡмĽṹ >=0ʱӡָĽṹ\n    */\n    void PrintAll(int l);\n    /**\n     * @brief ڵʱ,õK\n     * @return K\n    */\n    int RandomLevel();\n\nprivate:\n    int levelCount;\n    /**\n     * \n     * ͷ/(ڵ)\n    */\n    CNode* m_lpHead;\n};\n\nint main()\n{\n    CSkipList skipList;\n    /// ԭʼֵ\n    for(int i=1; i< 50; i++){\n        if((i%3) == 0){\n            skipList.Insert(i);\n        }\n    }\n    for(int i=1; i< 50; i++){\n        if((i%3) == 1){\n            skipList.Insert(i);\n        }\n    }\n    skipList.PrintAll();\n    std::cout<<std::endl;\n    /// ӡеȼṹ\n    skipList.PrintAll(-1);\n    /// \n    std::cout<<std::endl;\n    CNode* lpNode = skipList.Find(27);\n    if(NULL != lpNode){\n        std::cout<<\"ֵΪ27Ľڵ,ҵýڵ,ڵֵ:\"<<lpNode->GetData()<<std::endl;\n    }else{\n        std::cout<<\"ֵΪ27Ľڵ,δҵýڵ\"<<std::endl;\n    }\n    /// ɾ\n    std::cout<<std::endl;\n    int ret = skipList.Delete(46);\n    if(0 == ret){\n        std::cout<<\"ֵΪ46Ľڵ,ҵýڵ,ɾɹ\"<<std::endl;\n    }else{\n        std::cout<<\"ֵΪ46Ľڵ,ҵýڵ,ɾʧ\"<<std::endl;\n    }\n    std::cout<<std::endl;\n    //ӡеȼṹ\n    skipList.PrintAll(-1);\n    std::cin.ignore();\n    return 0;\n}\n\nCNode::CNode()\n{\n    m_data = -1;\n    m_iMaxLevel = 0;\n    for(int i=0; i<MAX_LEVEL; i++){\n        m_lpForwards[i] = NULL;\n    }\n}\nCNode::~CNode()\n{\n\n}\nCNode** CNode::GetIdxList()\n{\n    return m_lpForwards;\n}\n\nvoid CNode::SetData(int v)\n{\n    m_data = v;\n}\nint CNode::GetData()\n{\n    return m_data;\n}\nvoid CNode::SetLevel(int l)\n{\n    m_iMaxLevel = l;\n}\nstd::string CNode::toString()\n{\n    char tmp[32];\n    std::string ret;\n\n    ret.append(\"{ data: \");\n    sprintf(tmp, \"%d\", m_data);\n    ret.append(tmp);\n    ret.append(\"; levels: \");\n    sprintf(tmp, \"%d\", m_iMaxLevel);\n    ret.append(tmp);\n    ret.append(\" }\");\n    return ret;\n}\n\nCSkipList::CSkipList()\n{\n    levelCount = 1;\n    m_lpHead = new CNode();\n}\nCSkipList::~CSkipList()\n{\n\n}\nCNode* CSkipList::Find(int v)\n{\n    CNode* lpNode = m_lpHead;\n    /**\n     *  ʼ.\n     * K -> k-1 -> k-2 ...->0\n    */\n    for(int i=levelCount-1; i>=0; --i){\n        /**\n         * СvĽڵ(lpNode).\n        */\n        while((NULL != lpNode->GetIdxList()[i]) && (lpNode->GetIdxList()[i]->GetData() < v)){\n            lpNode = lpNode->GetIdxList()[i];\n        }\n    }\n    /**\n     * lpNode СvĽڵ, lpNodeһڵ͵ڻvĽڵ\n    */\n    if((NULL != lpNode->GetIdxList()[0]) && (lpNode->GetIdxList()[0]->GetData() == v)){\n        return lpNode->GetIdxList()[0];\n    }\n    return NULL;\n}\nvoid CSkipList::Insert(int v)\n{\n    /// ½ڵ\n    CNode* lpNewNode = new CNode();\n    if(NULL == lpNewNode){\n        return;\n    }\n\n    /**\n     * ½ڵֲڵ\n     *  3, µĽڵ123ϵ\n    */\n    int level = RandomLevel();\n    lpNewNode->SetData(v);\n    lpNewNode->SetLevel(level);\n\n    /**\n     * ʱ\n     * ҪǵõµĽڵÿϵλ\n    */\n    CNode *lpUpdateNode[level];\n    for(int i=0; i<level; i++){\n        /// ÿͷڵ\n        lpUpdateNode[i] =m_lpHead;\n    }\n    CNode* lpFind = m_lpHead;\n    for(int i= level-1; i >= 0; --i){\n        /**\n         * λ\n         *   eg.  1  1  7  10\n         *    6\n         *   lpFind->GetIdxList()[i]->GetData() : ʾڵlpFindڵ1һڵ\n         *    \"lpFind->GetIdxList()[i]->GetData() < v\"ʱ,\n         *   ½ڵҪ뵽 lpFindڵĺ, lpFind->GetIdxList()[i] ڵǰ\n         *    lpFind1  lpFind->GetIdxList()[i] 7\n        */\n        while((NULL != lpFind->GetIdxList()[i]) && (lpFind->GetIdxList()[i]->GetData() < v)){\n            lpFind = lpFind->GetIdxList()[i];\n        }\n        /// lpFind ½ڵ iĺһڵ\n        lpUpdateNode[i] = lpFind;\n    }\n\n    for(int i=0; i<level; ++i){\n        /**\n         * ָλ\n         *   eg  1 1  7  10\n         *      6.\n         *      lpUpdateNode[i] ڵ1; lpUpdateNode[i]->GetIdxList()[i]ڵ7\n         *  \n         *  2 6 17֮\n        */\n        lpNewNode->GetIdxList()[i] = lpUpdateNode[i]->GetIdxList()[i];\n        lpUpdateNode[i]->GetIdxList()[i] = lpNewNode;\n    }\n    if(levelCount < level){\n        levelCount = level;\n    }\n}\nint CSkipList::Delete(int v)\n{\n    int ret = -1;\n    CNode *lpUpdateNode[levelCount];\n    CNode *lpFind = m_lpHead;\n    for(int i=levelCount-1; i>= 0; --i){\n        /**\n         * СvĽڵ(lpFind).\n        */\n        while((NULL != lpFind->GetIdxList()[i]) && (lpFind->GetIdxList()[i]->GetData() < v)){\n            lpFind = lpFind->GetIdxList()[i];\n        }\n        lpUpdateNode[i] = lpFind;\n    }\n    /**\n     * lpFind СvĽڵ, lpFindһڵ͵ڻvĽڵ\n    */\n    if((NULL != lpFind->GetIdxList()[0]) && (lpFind->GetIdxList()[0]->GetData() == v)){\n        for(int i=levelCount-1; i>=0; --i){\n            if((NULL != lpUpdateNode[i]->GetIdxList()[i]) && (v == lpUpdateNode[i]->GetIdxList()[i]->GetData())){\n                lpUpdateNode[i]->GetIdxList()[i] = lpUpdateNode[i]->GetIdxList()[i]->GetIdxList()[i];\n                ret = 0;\n            }\n        }\n    }\n    return ret;\n}\nvoid CSkipList::PrintAll()\n{\n    CNode* lpNode = m_lpHead;\n    while(NULL != lpNode->GetIdxList()[0]){\n        std::cout<<lpNode->GetIdxList()[0]->toString().data()<<std::endl;\n        lpNode = lpNode->GetIdxList()[0];\n    }\n}\nvoid CSkipList::PrintAll(int l)\n{\n    for(int i=MAX_LEVEL-1; i>=0;--i){\n        CNode* lpNode = m_lpHead;\n        std::cout<<\"\"<<i<<\":\"<<std::endl;\n        if((l < 0) || ((l >= 0) && (l == i))){\n            while(NULL != lpNode->GetIdxList()[i]){\n                std::cout<<lpNode->GetIdxList()[i]->GetData()<<\" \";\n                lpNode = lpNode->GetIdxList()[i];\n            }\n            std::cout<<std::endl;\n            if(l >= 0){\n                break;\n            }\n        }\n    }\n}\nint GetRandom()\n{\n    static int _count = 1;\n\tstd::default_random_engine generator(time(0) + _count);\n\tstd::uniform_int_distribution<int> distribution(1,99999/*0x7FFFFFFF*/);\n\tint dice_roll = distribution(generator);\n    _count += 100;\n\treturn dice_roll;\n}\nint CSkipList::RandomLevel()\n{\n    int level = 1;\n    for(int i=1; i<MAX_LEVEL; ++i){\n        if(1 == (GetRandom()%3)){\n            level++;\n        }\n    }\n    return level;\n}"
  },
  {
    "path": "c-cpp/17_skiplist/skiplist.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <stdbool.h>\n#include <string.h>\n#include <time.h>\n\n// https://www.youtube.com/watch?v=2g9OSRKJuzM&t=17s\n\n#define MAX_LEVEL 15\n\nstruct node {\n\tint val;\n\tint max_level;\n\tstruct node *forward[MAX_LEVEL];\n};\n\nstruct skip_list {\n\tstruct node head;\n\tint max_level;\n\tint max_level_nodes;\n};\n\nvoid node_init(struct node* node)\n{\n\tmemset(node, 0, sizeof(struct node));\n}\n\nvoid skip_list_init(struct skip_list* sl)\n{\n\tnode_init(&sl->head);\n\tsl->max_level = 0;\n\tsl->max_level_nodes = 0;\n}\n\nvoid random_init()\n{\n\tstatic bool done = false;\n\n\tif (done)\n\t\treturn;\n\n\tsrandom(time(NULL));\n\tdone = true;\n}\n\nint random_level(void)\n{\n    int i, level = 1;\n\n    random_init();\n\n    for (i = 1; i < MAX_LEVEL; i++)\n\t    if (random() % 2 == 1)\n\t\t    level++;\n\n    return level;\n}\n\nvoid random_level_test()\n{\n\tprintf(\"random level %d\\n\", random_level());\n\tprintf(\"random level %d\\n\", random_level());\n\tprintf(\"random level %d\\n\", random_level());\n\tprintf(\"random level %d\\n\", random_level());\n\tprintf(\"random level %d\\n\", random_level());\n}\n\nvoid insert(struct skip_list *sl, int val)\n{\n\tint level = random_level();\n\tstruct node *update[MAX_LEVEL];\n\tstruct node *new, *p;\n\tint i;\n\n\tnew = (struct node*)malloc(sizeof(struct node));\n\tif (!new)\n\t\treturn;\n\n\tnew->max_level = level;\n\tnew->val = val;\n\n\tfor (int i = 0; i < MAX_LEVEL; i++)\n\t\tupdate[i] = &sl->head;\n\n\tp = &sl->head;\n\tfor (i = level - 1; i >= 0; i--) {\n\t\twhile(p->forward[i] && p->forward[i]->val < val)\n\t\t\tp = p->forward[i];\n\n\t\tupdate[i] = p;\n\t}\n\n\tfor (i = 0; i < level; i++) {\n\t\tnew->forward[i] = update[i]->forward[i];\n\t\tupdate[i]->forward[i] = new;\n\t}\n\n\tif (sl->max_level < level) {\n\t\tsl->max_level = level;\n\t\tsl->max_level_nodes = 1;\n\t} else if (sl->max_level == level)\n\t\tsl->max_level_nodes++;\n}\n\nstruct node *find(struct skip_list* sl, int val)\n{\n\tstruct node *node = &sl->head;\n\tint i;\n\n\tfor (i = sl->max_level - 1; i >= 0; i--) {\n\t\twhile (node->forward[i] && node->forward[i]->val < val)\n\t\t\tnode = node->forward[i];\n\t}\n\n\tif (node->forward[0] && node->forward[0]->val == val) {\n\t\treturn node->forward[0];\n\t}\n\telse\n\t\treturn NULL;\n}\n\nvoid delete(struct skip_list* sl, int val)\n{\n\tstruct node *update[MAX_LEVEL];\n\tstruct node *p;\n\tint i;\n\n\tp = &sl->head;\n\n\tfor (i = sl->max_level; i >= 0; i--) {\n\t\twhile (p->forward[i] && p->forward[i]->val < val)\n\t\t\tp = p->forward[i];\n\n\t\tupdate[i] = p;\n\t}\n\n\tif (p->forward[0] == NULL || p->forward[0]->val != val)\n\t\treturn;\n\n\tif (p->forward[0]->max_level == sl->max_level)\n\t\tsl->max_level_nodes--;\n\n\tfor (i = sl->max_level-1; i >= 0; i--) {\n\t\tif (update[i]->forward[i] && update[i]->forward[i]->val == val)\n\t\t\tupdate[i]->forward[i] = update[i]->forward[i]->forward[i]; \n\t}\n\n\t// fixup max_level and max_level_nodes\n\tif (sl->max_level_nodes == 0) {\n\t\t//sl->max_level--;\n\t\tp = &sl->head;\n\t\t// skip (max_level - 1), direct test (max_level - 2)\n\t\t// since no nodes on (max_level - 1)\n\t\tfor (i = sl->max_level - 2; i >= 0; i--) {\n\t\t\twhile (p->forward[i]) {\n\t\t\t\tsl->max_level_nodes++;\n\t\t\t\tp = p->forward[i];\n\t\t\t}\n\n\t\t\tif (sl->max_level_nodes) {\n\t\t\t\tsl->max_level = i + 1;\n\t\t\t\tbreak;\n\t\t\t} else\n\t\t\t\tsl->max_level = i;\n\t\t}\n\t}\n}\n\n\nvoid print_sl(struct skip_list* sl)\n{\n\tstruct node *node;\n\tint level;\n\n\tprintf(\"%d level skip list with %d nodes on top\\n\",\n\t\tsl->max_level, sl->max_level_nodes);\n\t\n\tfor (level = sl->max_level - 1; level >= 0; level--) {\n\t\tnode = &sl->head;\n\t\tprintf(\"Level[%02d]:\", level);\n\t\twhile (node->forward[level]) {\n\t\t\tprintf(\"%4d\", node->forward[level]->val);\n\t\t\tnode = node->forward[level];\n\t\t}\n\t\tprintf(\"\\n\");\n\t}\n}\n\nint main()\n{\n\tstruct skip_list sl;\n\tstruct node *node = NULL;\n\tint i;\n\n\tskip_list_init(&sl);\n\tprint_sl(&sl);\n\n\tfor (i = 0; i < 10; i++)\n\t\tinsert(&sl, i);\n\tprint_sl(&sl);\n\n\tnode = find(&sl, 8);\n\tif (node)\n\t\tprintf(\"find 8 in sl %d\\n\", node->val);\n\telse\n\t\tprintf(\"8 not in sl\\n\");\n\n\tfor (i = 0; i < 10; i++) {\n\t\tdelete(&sl, i);\n\t\tprint_sl(&sl);\n\t}\n\n\treturn 0;\n}\n"
  },
  {
    "path": "c-cpp/17_skiplist/skiplist.hpp",
    "content": "/**\n * Created by Liam Huang (Liam0205) on 2018/10/29.\n */\n\n#ifndef SKIPLIST_SKIPLIST_HPP_\n#define SKIPLIST_SKIPLIST_HPP_\n\n#include <functional>\n#include <type_traits>\n#include <vector>\n#include <chrono>\n#include <random>\n#include <initializer_list>\n#include <limits>\n#include <iostream>\n\ntemplate <typename Value>\nclass skiplist {\n  public:\n    using value_type = Value;\n    using hash_type  = std::hash<value_type>;\n    using key_type   = typename hash_type::result_type;\n    using size_type  = size_t;\n\n  private:\n    struct InternalNode {\n        value_type     value;\n        const key_type key;\n        std::vector<InternalNode*> forwards;  // pointers to successor nodes\n\n        InternalNode(const key_type& k, const size_type lv)\n            : value(), key(k), forwards(lv, nullptr) {}\n        InternalNode(const value_type& v, const size_type lv)\n            : value(v), key(hash_type()(value)), forwards(lv, nullptr) {}\n    };\n    using node_type = InternalNode;\n\n  private:\n    const size_type MAX_LEVEL                          = 16;\n    const double PROBABILITY                           = 0.5;\n    const unsigned int seed                            =\n        std::chrono::system_clock::now().time_since_epoch().count();\n    mutable\n    std::default_random_engine generator               = std::default_random_engine(seed);\n    mutable\n    std::binomial_distribution<size_type> distribution =\n        std::binomial_distribution<size_type>(MAX_LEVEL - 1, PROBABILITY);\n    node_type* head                                    = nullptr;\n    node_type* nil                                     = nullptr;\n    static const value_type default_value;\n\n  public:\n    skiplist() {\n        key_type head_key = std::numeric_limits<key_type>::min();\n        key_type nil_key  = std::numeric_limits<key_type>::max();\n        head = new node_type(head_key, MAX_LEVEL);\n        nil  = new node_type(nil_key,  MAX_LEVEL);\n        std::fill(head->forwards.begin(), head->forwards.end(), nil);\n    }\n    skiplist(std::initializer_list<value_type> init) : skiplist() {\n        for (const value_type& v : init) {\n            insert(v);\n        }\n    }\n    skiplist(const skiplist& other) = delete;\n    skiplist(skiplist&& other) :\n        MAX_LEVEL(std::move(other.MAX_LEVEL)),\n        PROBABILITY(std::move(other.PROBABILITY)),\n        seed(std::move(other.seed)),\n        generator(std::move(other.generator)),\n        distribution(std::move(other.distribution)),\n        head(other.head),\n        nil(other.nil) {\n        other.head = nullptr;\n        other.nil  = nullptr;\n    }\n    ~skiplist() {\n        node_type* node = head;\n        while (nullptr != node and nullptr != node->forwards[0]) {\n            node_type* tmp = node;\n            node = node->forwards[0];\n            delete tmp;\n        }\n        delete node;\n    }\n    skiplist& operator=(const skiplist& other) = delete;\n    skiplist& operator=(skiplist&& other)      = delete;\n\n  private:\n    inline size_type get_random_level() const {\n        return distribution(generator);\n    }\n    static size_type get_node_level(const node_type* node) {\n        return node->forwards.size();\n    }\n    static node_type* make_node(const value_type& v, const size_type lv) {\n        return new node_type(v, lv);\n    }\n    /**\n     * @brief   returns a pointer to the first node such that\n     *          node->key == hash_type()(v) and node->value == v.\n     */\n    node_type* get_first_equal(const value_type& v) const {\n        const key_type target = hash_type()(v);\n        node_type* x = head;\n        for (size_type i = get_node_level(head); i > 0; --i) {\n            while (x->forwards[i - 1]->key < target or\n                    x->forwards[i - 1]->key == target and x->forwards[i - 1]->value != v) {\n                x = x->forwards[i - 1];\n            }\n        }\n        return x->forwards[0];\n    }\n    /**\n     * @brief   returns a collection of nodes.\n     *          returns[i] is the pointer to the last node at level i + 1\n     *          such that returns[i]->key < hash_type()(v) or\n     *          returns[i]->key == hash_type()(v) but returns[i]->value != v.\n     */\n    std::vector<node_type*> get_predecessors(const value_type& v) const {\n        const key_type target = hash_type()(v);\n        std::vector<node_type*> results(get_node_level(head), nullptr);\n        node_type* x = head;\n        for (size_type i = get_node_level(head); i > 0; --i) {\n            while (x->forwards[i - 1]->key < target or\n                    x->forwards[i - 1]->key == target and x->forwards[i - 1]->value != v) {\n                x = x->forwards[i - 1];\n            }\n            results[i - 1] = x;\n        }\n        return results;\n    }\n\n  public:\n    const value_type& find(const value_type& target) {\n        node_type* x = get_first_equal(target);\n        if (nullptr != x and nil != x and x->value == target) {\n            return x->value;\n        } else {\n            return default_value;\n        }\n    }\n    void  insert(const value_type& value) {\n        std::vector<node_type*> preds       = get_predecessors(value);\n        const size_type         new_node_lv = get_random_level();\n        node_type*              new_node    = make_node(value, new_node_lv);\n        for (size_type i = 0; i != new_node_lv; ++i) {\n            new_node->forwards[i] = preds[i]->forwards[i];\n            preds[i]->forwards[i] = new_node;\n        }\n    }\n    void  erase(const value_type& value) {\n        std::vector<node_type*> preds = get_predecessors(value);\n\n        node_type* node               = preds[0]->forwards[0];\n        if (node == nil or node->value != value) { return; }\n\n        for (size_type i = 0; i != get_node_level(node); ++i) {\n            preds[i]->forwards[i] = node->forwards[i];\n        }\n        delete node;\n    }\n    void  print(std::ostream& os) const {\n        node_type* list = head->forwards[0];\n        os << \"{\";\n\n        while (list != nil) {\n            os << \"key: \" << list->key << \" value: \" << list->value\n               << \" level: \" << get_node_level(list);\n\n            list = list->forwards[0];\n\n            if (list != nil) os << \" : \";\n\n            os << \"\\n\";\n        }\n        os << \"}\\n\";\n    }\n};\n\ntemplate <typename Value>\nconst typename skiplist<Value>::value_type skiplist<Value>::default_value =\n    typename skiplist<Value>::value_type();\n\n#endif  // SKIPLIST_SKIPLIST_HPP_\n\n"
  },
  {
    "path": "c-cpp/17_skiplist/skiplist_c/skiplist.c",
    "content": "/*************************************************************************\n > File Name: skiplist.c\n > Author:  jinshaohui\n > Mail:    jinshaohui789@163.com\n > Time:    18-10-31\n > Desc:    \n ************************************************************************/\n#include<stdio.h>\n#include<stdlib.h>\n#include<string.h>\n#include<assert.h>\n#include\"./skiplist.h\"\n\n\n/*创建node节点*/\nnode* skip_list_create_node(int level,int key,int value)\n{\n\tnode * tmp = NULL;\n\n\ttmp =(node *)malloc(sizeof(node) + level*sizeof(node *));\n\tassert(tmp != NULL);\n\n\tmemset(tmp,0,sizeof(node) + level*sizeof(node*));\n\ttmp->key = key;\n\ttmp->value = value;\n\ttmp->max_level = level;\n\t\n    return tmp;\n}\n\n/*创建跳表的表头，max_level层数*/\nskiplist * skip_list_create(int max_level)\n{\n\tint i = 0;\n\tskiplist * list = NULL;\n\n\tlist = (skiplist *)malloc (sizeof(skiplist));\n\tassert(list != NULL);\n\n\tlist->level = 1;\n\tlist->count = 0;\n\n\tlist->head = skip_list_create_node(max_level,0,0);\n\tif(list->head == NULL)\n\t{\n\n\t\tfree(list);\n\t\treturn NULL;\n\t}\n\t\n\treturn list;\n}\n\n/*skiplist 销毁*/\nvoid skip_list_destory(skiplist * list)\n{\n\tint i = 0;\n\tnode * tmp = NULL;\n\n\tif((list == NULL) || (list->head == NULL))\n\t{\n\t\treturn;\n\t}\n\twhile(list->head->next[0] != NULL)\n\t{\n        tmp = list->head->next[0];\n        list->head->next[0] = tmp->next[0];\n\t\tfree(tmp);\n\t}\n\n    free(list->head);\n\tfree(list);\n\treturn;\n}\n\n/*插入元素获得层数，是随机产生的*/\nint skip_list_level(skiplist * list)\n{\n\tint i = 0;\n\tint level = 1;\n\tfor (i = 1; i < list->head->max_level; i++)\n\t{\n\t\tif ((rand()%2) == 1)\n\t\t{\n\t\t\tlevel++;\n\t\t}\n\t}\n\n\treturn level;\n}\nint skip_list_insert(skiplist *list,int key,int value)\n{\n\tint i = 0;\n\tint level = 0;\n\tnode **update = NULL;/*用来更新每层的指针*/\n\tnode *tmp = NULL;\n\tnode *prev = NULL;\n\n\tif (list == NULL)\n\t{\n\t\treturn 1;\n\t}\n\n    /*申请update空间用于保存每层的指针*/\n\tupdate = (node **)malloc(sizeof(node *)*list->head->max_level);\n\tif (update == NULL)\n\t{\n\t\treturn 2;\n\t}\n\n    /*逐层查询节点的*/\n\tprev = list->head;\n\tfor (i = (list->level -1); i >= 0; i--)\n\t{\n\t    /*初始化每level层的头指针*/\n\t\twhile(((tmp = prev->next[i]) != NULL) && (tmp->key < key))\n\t\t{\n\t\t\tprev  = tmp;\n\t\t}\n\t\tupdate[i] = prev;\n\t}\n\n\t/*当前key已经存在，返回错误*/\n\tif ((tmp!= NULL) && (tmp->key == key))\n\t{\n\t\treturn 3;\n\t}\n\t/*获取插入元素的随机层数，并更新跳表的最大层数*/\n\tlevel = skip_list_level(list);\n\t/*创建当前数据节点*/\n\ttmp = skip_list_create_node(level,key,value);\n\tif (tmp == NULL)\n\t{\n\t\treturn 4;\n\t}\n\n\t/*更新最大层数*/\n\tif (level > list->level)\n\t{\n\t\tfor (i = list->level;i < level; i ++)\n\t\t{\n\t\t\tupdate[i] = list->head;\n\t\t}\n\t\tlist->level = level;\n\t}\n\n\t/*逐层更新节点的指针*/\n\tfor(i = 0; i < level; i++)\n\t{\n        tmp->next[i] = update[i]->next[i];\n\t\tupdate[i]->next[i] = tmp; \n\t}\n\n    list->count++;\n\treturn 0;\n}\n\nint skip_list_delete(skiplist * list, int key ,int *value)\n{\n\tint i = 0;\n\tnode **update = NULL;/*用来更新每层的指针*/\n\tnode *tmp = NULL;\n\tnode *prev = NULL;\n\n\tif ((list == NULL) && (value == NULL)&& (list->count == 0))\n\t{\n\t\treturn 1;\n\t}\n    /*申请update空间用于保存每层的指针*/\n\tupdate = (node **)malloc(sizeof(node *)*list->level);\n\tif (update == NULL)\n\t{\n\t\treturn 2;\n\t}\n    /*逐层查询节点的*/\n\tprev = list->head;\n\tfor (i = (list->level -1); i >= 0; i--)\n\t{\n\t    /*初始化每level层的头指针*/\n\t\twhile(((tmp = prev->next[i]) != NULL) && (tmp->key < key))\n\t\t{\n\t\t\tprev = tmp;\n\t\t}\n\t\tupdate[i] = prev;\n\t}\n\n\tif ((tmp != NULL)\n\t\t&& (tmp->key == key))\n\t{\n\t\t*value = tmp->value;\n\t    /*逐层删除*/\n\t\tfor(i = 0; i < list->level; i++)\n\t\t{\n\t\t\tif(update[i]->next[i] == tmp)\n\t\t\t{\n\t\t\t     update[i]->next[i] = tmp->next[i];       \n\t\t\t}\t\n\t\t}\n\n\t\tfree(tmp);\n\t\ttmp = NULL;\n        \n\t\t/*更新level的层数*/\n\t\tfor (i = list->level - 1; i >= 0; i++)\n\t\t{\n\t\t\tif (list->head->next[i] == NULL )\n\t\t\t{\n\t\t\t\tlist->level--;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tlist->count--;\n\n\t}\n\telse\n\t{\n\t\treturn 3;/*未找到节点*/\n\t}\n\n    return 0 ;\n}\n\n/*查询当前key是否在跳表中，如果存在返回查询的value数值，不存在返回-1*/\nint skip_list_search(skiplist *list,int key,int *value)\n{\n\tint i = 0;\n    node *prev = NULL;\n\tnode *tmp = NULL;\n\n\tif((list == NULL) || (list->count == 0) || (value == NULL))\n\t{\n\t\treturn 1;\n\t}\n    prev = list->head;\n\tfor(i = list->level - 1; i >= 0; i--)\n\t{\n\t\twhile(((tmp = prev->next[i]) != NULL) && (tmp->key <= key))\n\t\t{\n\t\t\tif (tmp->key == key)\n\t\t\t{\n\t\t        *value = tmp->value;\n\t\t        return 0;\n\t\t\t}\n\t\t\tprev = tmp;\n\t\t}\n\t}\n\n    return -1;\n}\n\nvoid skip_list_dump(skiplist *list)\n{\n\tint i = 0;\n    node *ptmp = NULL;\n    printf(\"\\r\\n----------------------------------------------\");\n\tprintf(\"\\r\\n skip list level[%d],count[%d]\",list->level,list->count);\n\tfor(i = list->level - 1; i >= 0; i --)\n\t{\n\t\tptmp = list->head->next[i];\n\t\tprintf(\"\\r\\n level[%d]:\",i);\n\t\twhile(ptmp != NULL)\n\t\t{\n\t\t\tprintf(\"%d-%d \",ptmp->key,ptmp->value);\n\t\t\tptmp = ptmp->next[i];\n\t\t}\n\t}\n    printf(\"\\r\\n----------------------------------------------\");\n\treturn;\n}\n\nint main()\n{\n\tint res = 0;\n\tint key = 0;\n\tint value = 0;\n    skiplist *list = NULL;\n\n\n\tlist = skip_list_create(5);\n\tassert(list != NULL);\n\n\twhile(1)\n\t{\n\t\tprintf(\"\\r\\n 请输入key 和 value，当key = 1000时，退出输入：\");\n\t\tscanf(\"%d%d\",&key,&value);\n\t\tif (key == 1000)\n\t\t{\n\t\t\tbreak;\n\t\t}\n\t\tres = skip_list_insert(list,key,value);\n\t\tif (res != 0)\n\t\t{\n\t\t\tprintf(\"\\r\\n skip list insert %d,failed,res=%d.\",key,res);\n\t\t}\n\t}\n\tskip_list_dump(list);\n\n\twhile(1)\n\t{\n\t\tprintf(\"\\r\\n 通过key 查询value的数值，当key = 1000时，退出查询\");\n\t\tscanf(\"%d\",&key);\n\t\tif(key == 1000)\n\t\t{\n\t\t\tbreak;\n\t\t}\n\t\tres = skip_list_search(list,key,&value);\n\t\tif (res != 0)\n\t\t{\n\t\t\tprintf(\"\\r\\n skip list search %d,failed,res=%d.\",key,res);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tprintf(\"\\r\\n skip list search %d,sucessful,value=%d.\",key,value);\n\n\t\t}\n\t}\n\tskip_list_dump(list);\n\twhile(1)\n\t{\n\t\tprintf(\"\\r\\n 通过key 删除节点，当key = 1000时，退出删除\");\n\t\tscanf(\"%d\",&key);\n\t\tif(key == 1000)\n\t\t{\n\t\t\tbreak;\n\t\t}\n\t\tres = skip_list_delete(list,key,&value);\n\t\tif (res != 0)\n\t\t{\n\t\t\tprintf(\"\\r\\n skip list search %d,failed,res=%d.\",key,res);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tprintf(\"\\r\\n skip list search %d,sucessful,value=%d.\",key,value);\n\n\t\t}\n\t}\n\n\tskip_list_dump(list);\n\tskip_list_destory(list);\n\n\treturn 0;\n}\n"
  },
  {
    "path": "c-cpp/17_skiplist/skiplist_c/skiplist.h",
    "content": "/*************************************************************************\n > File Name: skiplist.h\n > Author:  jinshaohui\n > Mail:    jinshaohui789@163.com\n > Time:    18-10-31\n > Desc:    \n ************************************************************************/\n#ifndef __SKIP_LIST_H__\n#define __SKIP_LIST_H__\n\n\ntypedef struct _node\n{\n\tint key;    /*key是唯一的*/\n\tint value;  /*存储的内容*/\n\tint max_level; /*当前节点最大层数*/\n\tstruct _node *next[0];/*level层链表结构*/\n}node;\n\ntypedef struct _skiplist\n{\n\tint level;\n\tint count;\n\tnode *head;\n}skiplist;\n\n/*根据当前结构体元素的地址，获取到结构体首地址*/\n#define offsetof(TYPE,MEMBER) ((size_t) &((TYPE *)0)->MEMBER)\n#define container(ptr,type,member) ({\\\n  const typeof( ((type *)0)->member) *__mptr = (ptr);\\\n  (type *) ( (char *)__mptr - offsetof(type,member));})\n\n#endif\n"
  },
  {
    "path": "c-cpp/17_skiplist/skiplist_test.cc",
    "content": "/**\n * Created by Liam Huang (Liam0205) on 2018/10/30.\n */\n\n#include <iostream>\n#include <string>\n\n#include \"skiplist.hpp\"\n\nint main() {\n    // 1. Initialize a skip list for test\n    // * default constructor\n    // * constructor with initializer list\n    // * insert\n    skiplist<std::string> ss{\"1\", \"2\", \"3\", \"4\", \"5\"};\n\n    // 1a. show\n    // * print\n    ss.print(std::cout);\n    std::cout << std::endl;\n\n    // 2. move construction\n    // * move constructor\n    skiplist<std::string> s(std::move(ss));\n\n    // 2a. show\n    // * print\n    s.print(std::cout);\n    std::cout << std::endl;\n\n    // 3.a find something doesn't exist.\n    // * find\n    auto f = s.find(\"0\");\n    if (!f.empty()) {\n        std::cout << \"Node found!\\nvalue: \" << f << '\\n';\n    } else {\n        std::cout << \"Node NOT found!\\n\";\n    }\n\n    // 3.b find something does exist.\n    // * find\n    auto ff = s.find(\"1\");\n    if (!ff.empty()) {\n        std::cout << \"Node found!\\tvalue: \" << ff << '\\n';\n    } else {\n        std::cout << \"Node NOT found!\\n\";\n    }\n\n    // 4. insert() - reassign\n    s.insert(\"TEST\");\n\n    // 4a. print()\n    s.print(std::cout);\n    std::cout << std::endl;\n\n    // 5. erase()\n    s.erase(\"TEST\");\n\n    // 5a. print();\n    s.print(std::cout);\n    std::cout << std::endl;\n\n    std::cout << \"\\nDone!\\n\";\n\n    return 0;\n    // 6. destructor\n}\n"
  },
  {
    "path": "c-cpp/17_skiplist/skiplist_tr.hpp",
    "content": "/**\n * Created by Liam Huang (Liam0205) on 2018/10/30.\n */\n\n#ifndef SKIPLIST_SKIPLIST_TR_HPP_\n#define SKIPLIST_SKIPLIST_TR_HPP_\n\n#ifdef LIAM_UT_DEBUG_\n#include <assert.h>\n#include <iostream>\n#endif\n\n#include <set>\n#include <vector>\n#include <list>\n#include <functional>\n#include <type_traits>\n#include <random>\n#include <limits>\n#include <algorithm>\n#include <initializer_list>\n#include <iterator>\n\nnamespace skiplist_detail {\ntemplate <typename Key, typename Value>\nstruct InternalNode {\n    using iterator = typename std::list<InternalNode>::iterator;\n    const Key             key;\n    std::multiset<Value>  values;\n    std::vector<iterator> forwards;\n\n    InternalNode() = delete;\n    explicit InternalNode(const Key& k) : key(k) {}\n};\n\ntemplate <typename IntType>\nclass random_level {\n  private:\n    mutable std::random_device                  rd;\n    mutable std::mt19937                        gen = std::mt19937(rd());\n    mutable std::binomial_distribution<IntType> dist;\n\n  public:\n    random_level(IntType max_level, double prob) : dist(max_level - 1, prob) {}\n    inline IntType operator()() const { return dist(gen); }\n};\n}  // namespace skiplist_detail\n\nenum class erase_policy { ALL, SINGLE };\n\ntemplate <typename Value,\n          typename Hash = std::hash<Value>,\n          size_t Factor = 2>\nclass skiplist {\n  public:\n    using value_type     = Value;\n    using size_type      = size_t;\n    using hasher         = Hash;\n    using hash_type      = typename Hash::result_type;\n    using compare        = std::less<hash_type>;\n    using node_type      = skiplist_detail::InternalNode<hash_type, value_type>;\n    using container      = std::list<node_type>;\n    using iterator       = typename container::iterator;\n    using const_iterator = typename container::const_iterator;\n    static_assert(std::is_same<iterator, typename node_type::iterator>::value,\n            \"STATIC ASSERT FAILED! iterator type differs.\");\n\n  private:\n    size_type                                        max_lv_ = 2;\n    double                                           prob_   = 0.5;\n    mutable skiplist_detail::random_level<size_type> rl_;\n    container                                        cont_;\n\n  public:\n    skiplist() : rl_(max_lv_, prob_) {\n        init_internally();\n    }\n    explicit skiplist(const size_type max_lv, const double prob = 0.5)\n        : max_lv_(max_lv), prob_(prob), rl_(max_lv_, prob_) {\n        init_internally();\n    }\n    skiplist(skiplist&& other) = default;\n    skiplist& operator=(skiplist&& other) = default;\n    ~skiplist() = default;\n    template <typename InputIt>\n    skiplist(InputIt first, InputIt last) : skiplist() {\n        using value_type_in_iter = typename std::iterator_traits<InputIt>::value_type;\n        static_assert(std::is_same<value_type, value_type_in_iter>::value,\n                \"STATIC ASSERT FAILED! Value in InputIt should be the same to value_type.\");\n        for (InputIt i = first; i != last; ++i) {\n            insert(*i);\n        }\n    }\n    skiplist(std::initializer_list<value_type> init) : skiplist(init.begin(), init.end()) {}\n\n  private:  // noncopyable\n    skiplist(const skiplist&) = delete;\n    skiplist& operator=(const skiplist&) = delete;\n\n  private:\n    void init_internally() {\n        const hash_type tail_key = std::numeric_limits<hash_type>::max();\n        node_type tail(tail_key);\n        tail.forwards.resize(max_lv_, cont_.end());\n        iterator tail_iter = cont_.insert(cont_.begin(), std::move(tail));\n\n        const hash_type head_key = std::numeric_limits<hash_type>::min();\n        node_type head(head_key);\n        head.forwards.resize(max_lv_, tail_iter);\n        cont_.insert(cont_.begin(), std::move(head));\n\n#ifdef LIAM_UT_DEBUG_\n        assert(cont_.begin()->key == head_key);\n        for (auto it : cont_.begin()->forwards) {\n            assert(it->key == tail_key);\n        }\n        for (auto it : std::next(cont_.begin())->forwards) {\n            assert(it == cont_.end());\n        }\n        std::cerr << \"UT_DEBUG: all assert in init_internally() success!\\n\";\n#endif\n\n        return;\n    }\n    /**\n     * @brief   return a const_iterator points to the last element\n     *          such that its hash_key <= target_hash_key\n     */\n    const_iterator find_helper(const hash_type& key) const {\n#ifdef LIAM_UT_DEBUG_\n        std::cerr << \"Keys contained in the list: \";\n        for (auto node : cont_) {\n            std::cerr << node.key << ' ';\n        }\n        std::cerr << '\\n';\n        std::cerr << \"Target key: \" << key << '\\n';\n#endif\n        const_iterator iter = begin();\n        for (size_type i = 0; i != max_lv_; ++i) {\n            size_type focus = max_lv_ - 1 - i;\n            // invariant: iter->key <= key\n            while (not compare()(key, iter->forwards[focus]->key)) {\n#ifdef LIAM_UT_DEBUG_\n                std::cerr << \"i: \" << i << \" focus: \" << focus << \". \"\n                          << \"since iter->forwards[focus]->key[\" << iter->forwards[focus]->key\n                          << \"] <= key[\" << key << \"], \";\n#endif\n                iter = iter->forwards[focus];\n#ifdef LIAM_UT_DEBUG_\n                std::cerr << \"step forward iter to [\" << iter->key << \"]\\n\";\n#endif\n            }\n            // result: iter->key <= key < iter->forwards[focus]->key\n#ifdef LIAM_UT_DEBUG_\n            std::cerr << \"The following fact holds at level \" << focus\n                      << \": iter->key[\" << iter->key << \"] <= key[\"\n                      << key << \"] < iter->forwards[focus]->key[\" << iter->forwards[focus]->key\n                      <<\"].\\n\";\n#endif\n        }\n        return iter;\n    }\n    std::vector<iterator> find_predecessors(const hash_type& key, const size_type& lv) {\n#ifdef LIAM_UT_DEBUG_\n        std::cerr << \"Keys contained in the list: \";\n        for (auto node : cont_) {\n            std::cerr << node.key << ' ';\n        }\n        std::cerr << '\\n';\n        std::cerr << \"Target key: \" << key << '\\n';\n#endif\n        std::vector<iterator> res;\n        res.resize(lv + 1);\n        iterator iter = begin();\n        for (size_type i = 0; i != max_lv_; ++i) {\n            size_type focus = max_lv_ - 1 - i;\n#ifdef LIAM_UT_DEBUG_\n            std::cerr << \"i: \" << i << \" focus: \" << focus << \".\\n\";\n#endif\n            // invariant: iter->key < key\n            while (compare()(iter->forwards[focus]->key, key)) {\n#ifdef LIAM_UT_DEBUG_\n                std::cerr << \"since iter->forwards[focus]->key[\" << iter->forwards[focus]->key\n                          << \"] < key[\" << key << \"], \";\n#endif\n                iter = iter->forwards[focus];\n#ifdef LIAM_UT_DEBUG_\n                std::cerr << \"step forward iter to [\" << iter->key << \"]\\n\";\n#endif\n            }\n            // result: iter->key < key <= iter->forwards[focus]->key\n#ifdef LIAM_UT_DEBUG_\n            std::cerr << \"The following fact holds at level \" << focus\n                      << \": iter->key[\" << iter->key << \"] < key[\" << key\n                      << \"] <= iter->forwards[focus]->key[\" << iter->forwards[focus]->key\n                      <<\"].\\n\";\n#endif\n            if (focus < lv + 1) {\n                res[focus] = iter;\n#ifdef LIAM_UT_DEBUG_\n                std::cerr << \"predecessor at level [\" << focus\n                          << \"] has been recorded, while level upper limit is \" << lv <<\".\\n\";\n#endif\n            }\n        }\n        return res;\n    }\n\n  public:\n    size_type size() const {\n        return cont_.size() - 2;\n    }\n    bool empty() const {\n        return size() == 0;\n    }\n    iterator begin() {\n        return cont_.begin();\n    }\n    const_iterator begin() const {\n        return cont_.cbegin();\n    }\n    const_iterator cbegin() const {\n        return cont_.cbegin();\n    }\n    iterator end() {\n        return cont_.end();\n    }\n    const_iterator end() const {\n        return cont_.cend();\n    }\n    const_iterator cend() const {\n        return cont_.cend();\n    }\n    void grow(const size_type new_max_lv) {\n        if (max_lv_ < new_max_lv) {\n#ifdef LIAM_UT_DEBUG_\n            std::cerr << \"grow from [\" << max_lv_ << \"] to [\"\n                      << new_max_lv << \"]!\\n\";\n#endif\n            max_lv_ = new_max_lv;\n\n            iterator tail = std::prev(cont_.end());\n            auto beg_tail = tail->forwards.end();\n            tail->forwards.resize(max_lv_, cont_.end());\n\n            iterator head = cont_.begin();\n            auto beg_head = head->forwards.end();\n            head->forwards.resize(max_lv_, tail);\n\n            return;\n        } else {\n#ifdef LIAM_UT_DEBUG_\n            std::cerr << \"abandon growing!\\n\";\n#endif\n            return;\n        }\n    }\n    void grow() {\n        grow(Factor * max_lv_);\n    }\n    size_type capability() const {\n        return std::pow(Factor, max_lv_);\n    }\n\n  public:\n    const_iterator find(const value_type& target) const {\n#ifdef LIAM_UT_DEBUG_\n            std::cerr << \"finding [\" << target << \"]!\\n\";\n#endif\n        const hash_type key = hasher()(target);\n        const_iterator iter = find_helper(key);\n        return (iter->key == key) ? iter : cont_.end();\n    }\n    void insert(const value_type& target) {\n#ifdef LIAM_UT_DEBUG_\n            std::cerr << \"inserting [\" << target << \"]!\\n\";\n#endif\n        if (size() > static_cast<double>(Factor - 1) / Factor * capability()) {\n#ifdef LIAM_UT_DEBUG_\n            std::cerr << \"size[\" << size() << \"], Factor[\" << Factor << \"], capability[\" << capability() << \"]!\\n\";\n#endif\n            grow();\n        }\n        const hash_type key = hasher()(target);\n        const size_type lv  = rl_();\n        std::vector<iterator> predecessors = find_predecessors(key, lv);\n        if (predecessors[0]->forwards[0]->key == key) {  // key already in skiplist\n#ifdef LIAM_UT_DEBUG_\n            std::cerr << \"key [\" << key << \"] already in the skiplist, insert directly!\\n\";\n#endif\n            predecessors[0]->forwards[0]->values.insert(target);\n            return;\n        } else {\n#ifdef LIAM_UT_DEBUG_\n            std::cerr << \"key [\" << key << \"] not in the skiplist, insert a new node!\\n\";\n#endif\n            node_type node(key);\n            node.forwards.resize(lv + 1);\n            node.values.insert(target);\n            iterator inserted = cont_.insert(predecessors[0]->forwards[0], std::move(node));\n            for (size_type i = 0; i != lv + 1; ++i) {\n                inserted->forwards[i]        = predecessors[i]->forwards[i];\n                predecessors[i]->forwards[i] = inserted;\n            }\n#ifdef LIAM_UT_DEBUG_\n            assert(inserted->forwards[0] == std::next(inserted));\n#endif\n            return;\n        }\n    }\n    void erase(const value_type& target,\n              const erase_policy policy = erase_policy::ALL) {\n#ifdef LIAM_UT_DEBUG_\n            std::cerr << \"erasing [\" << target << \"]!\\n\";\n#endif\n        const hash_type key = hasher()(target);\n        std::vector<iterator> predecessors = find_predecessors(key, max_lv_);\n        if (predecessors[0]->forwards[0]->key == key) {  // hit\n#ifdef LIAM_UT_DEBUG_\n            std::cerr << \"key [\" << key << \"] is in the skiplist!\\n\";\n#endif\n            iterator found = predecessors[0]->forwards[0];\n            for (auto iter = found->values.begin(); iter != found->values.end(); ) {\n                if (policy == erase_policy::ALL) {\n                    if (*iter == target) {\n                        iter = found->values.erase(iter);\n                    } else {\n                        ++iter;\n                    }\n                } else if (policy == erase_policy::SINGLE) {\n                    if (*iter == target) {\n                        found->values.erase(iter);\n                        break;\n                    }\n                }\n            }\n#ifdef LIAM_UT_DEBUG_\n            std::cerr << \"target(s) removed!\\n\";\n#endif\n            if (found->values.empty()) {\n                const size_type lvp1 = found->forwards.size();  // lv plus 1\n                for (size_type i = 0; i != lvp1; ++i) {\n                    predecessors[i]->forwards[i] = found->forwards[i];\n                }\n                cont_.erase(found);\n#ifdef LIAM_UT_DEBUG_\n            std::cerr << \"empty node removed!\\n\";\n#endif\n                return;\n            } else {\n                return;\n            }\n        } else {\n#ifdef LIAM_UT_DEBUG_\n            std::cerr << \"key [\" << key << \"] is not in the skiplist, do nothing!\\n\";\n#endif\n            return;\n        }\n    }\n};\n\n#endif  // SKIPLIST_SKIPLIST_TR_HPP_\n"
  },
  {
    "path": "c-cpp/17_skiplist/skiplist_tr_test.cc",
    "content": "/**\n * Created by Liam Huang (Liam0205) on 2018/10/30.\n */\n#include <assert.h>\n\n#include <iostream>\n#include <map>\n#include <string>\n\n#include \"skiplist_tr.hpp\"\n\nint main() {\n    // 1. UT for skiplist_detail::random_level\n    skiplist_detail::random_level<size_t> rl(5, 0.5);\n    std::map<size_t, size_t>    hist;\n    for (size_t i = 0; i != 10000; ++i) {\n        ++hist[rl()];\n    }\n    for (auto p : hist) {\n        std::cout << p.first << ' ' << std::string(p.second / 100, '*') << '\\n';\n    }\n\n    // 2. UT for skiplist(), init_internally(), size(), empty()\n    skiplist<std::string> sl_default;\n    assert(sl_default.empty());\n    // 2.1. UT for grow with abandon\n    sl_default.grow(1);\n    assert(sl_default.capability() == 4);\n    // 2.2. UT for grow\n    sl_default.grow(10);\n    assert(sl_default.capability() == 1024);\n\n    // 3. UT for constructor of initializer_list and InputIt, init_internally(), insert(),\n    // find_predecessors()\n    skiplist<std::string> sl{\"hello\", \"world\", \"!\"};\n    assert(not sl.empty());\n    assert(3 == sl.size());\n\n    // 4. UT for find() find_helper()\n    auto search = sl_default.find(\"nonexist\");\n    assert(search == sl_default.cend());\n    assert(search == sl_default.end());\n    search = sl.find(\"hello\");\n    assert(search != sl.cend());\n    assert(search != sl.end());\n    search = sl.find(\"nonexist\");\n    assert(search == sl.cend());\n    assert(search == sl.end());\n\n    // 5. UT for insert(), find_predecessors()\n    // 5.1. UT for insert a already-exist item\n    sl.insert(\"hello\");\n    search = sl.find(\"hello\");\n    assert(search != sl.cend());\n    assert(search != sl.end());\n    // 5.2. UT for insert a new incoming item\n    search = sl.find(\"now exist\");\n    assert(search == sl.cend());\n    assert(search == sl.end());\n    sl.insert(\"now exist\");\n    search = sl.find(\"now exist\");\n    assert(search != sl.cend());\n    assert(search != sl.end());\n\n    // 6. UT for erase(), find_predecessors()\n    sl.insert(\"hello\");\n    sl.insert(\"hello\");\n    sl.insert(\"hello\");\n    sl.insert(\"hello\");\n    // 6.1. UT for erase single item\n    sl.erase(\"hello\", erase_policy::SINGLE);\n    search = sl.find(\"hello\");\n    assert(search != sl.cend());\n    assert(search != sl.end());\n    // 6.2. UT for erase all items\n    sl.erase(\"hello\", erase_policy::ALL);\n    search = sl.find(\"hello\");\n    assert(search == sl.cend());\n    assert(search == sl.end());\n    // 6.3 UT for erase non-exist item\n    sl.erase(\"nonexist\");\n\n    // 7. UT for insert() behind erase()\n    // 7.1. different word\n    sl.insert(\"world\");\n    search = sl.find(\"world\");\n    assert(search != sl.cend());\n    assert(search != sl.end());\n    assert(search->values.count(\"world\") == 2);\n    // 7.1. same word, also UT for grow()\n    search = sl.find(\"hello\");\n    assert(search == sl.cend());\n    assert(search == sl.end());\n    sl.insert(\"hello\");\n    sl.insert(\"hello\");\n    sl.insert(\"hello\");\n    sl.insert(\"hello\");\n    sl.insert(\"hello\");\n    search = sl.find(\"hello\");\n    assert(search != sl.cend());\n    assert(search != sl.end());\n    assert(search->values.count(\"hello\") == 5);\n\n    return 0;\n    // 8. UT for ~skiplist()\n}\n\n"
  },
  {
    "path": "c-cpp/18_hashtable/.gitkeep",
    "content": ""
  },
  {
    "path": "c-cpp/18_hashtable/hash_map.cc",
    "content": "/**\n * Created by Liam Huang (Liam0205) on 2018/08/14.\n * This is an old test file for hash_map, created by Liam.\n * Just for showing the inner logic for hash_map class template.\n * Original posted on:\n *   https://github.com/Liam0205/leetcode/tree/master/met/hash_map.cc\n */\n\n#include <iostream>\n#include <utility>\n#include <vector>\n#include <functional>\n#include <memory>\n\ntemplate <typename Key, typename T, typename Hash = std::hash<Key>>\nclass hash_map {\n  public:\n    using key_type = Key;\n    using mapped_type = T;\n    using value_type = std::pair<const key_type, mapped_type>;\n    using size_type = size_t;\n    using hasher = std::hash<Key>;\n\n  private:  // helper\n    using wrapper = std::shared_ptr<value_type>;\n\n  public:  // constructor\n    hash_map() {\n        container_.resize(primes_[size_level_]);\n    }\n\n  public:  // capacity\n    bool empty() const { return empty_; }\n    size_type size() const { return size_; }\n    size_type max_size() const { return primes_[size_level_]; }\n\n  public:  // find and modify\n    mapped_type& operator[](const key_type& key) {\n        auto hashed = find_hash(key);\n        if (not(container_[hashed]) and construct_new_on_position(hashed, key) and\n                load_factor() > max_load_factor()) {\n            expand();\n        }\n        return container_[hashed]->second;\n    }\n\n  public:  // hash policy\n    double load_factor() const { return static_cast<double>(size()) / max_size(); }\n    double max_load_factor() const { return max_load_factor_; }\n    void expand() const {\n        ++size_level_;\n        std::vector<wrapper> temp;\n        temp.resize(primes_[size_level_]);\n        for (auto w : container_) {\n            if (nullptr != w) {\n                auto hashed = find_hash(w->first);\n                temp[hashed] = w;\n            }\n        }\n        container_ = std::move(temp);\n    }\n\n  private:  // helper functions\n    size_type find_hash(const key_type& key) const {\n        const size_t csz = container_.size();\n        size_t count = 0;\n        size_t hashed = hasher_(key) % csz;\n        while (nullptr != container_[hashed] and container_[hashed]->first != key) {\n            hashed = (hashed + ++count) % csz;\n        }\n        return hashed;\n    }\n    bool construct_new_on_position(const size_type pos, const key_type& key) {\n        empty_ = false;\n        ++size_;\n        container_[pos] = std::make_shared<value_type>(std::make_pair(key, mapped_type()));\n        return true;\n    }\n\n  private:\n    const hasher hasher_ = hasher();\n    mutable size_t size_level_ = 0;\n    mutable std::vector<wrapper> container_;\n    static const size_t primes_[];\n    bool empty_ = true;\n    size_type size_ = 0;\n    double max_load_factor_ = 0.75;\n};\ntemplate <typename Key, typename T, typename Hash>\nconst size_t hash_map<Key, T, Hash>::primes_[] = {7, 17, 29, 53, 101, 211, 401, 809, 1601, 3203};  // ...\n\nint main() {\n    hash_map<int, int> test;\n    test[1];\n    test[2] = 2;\n    std::cout << test[1] << ' ' << test[2] << std::endl;\n    return 0;\n}\n"
  },
  {
    "path": "c-cpp/18_hashtable/hashtable.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <stdbool.h>\n#include <string.h>\n#include <time.h>\n\n/* One implementation of hash table with linear probing. */\n\n#define HASH_SHIFT 4\n#define HASH_SIZE (1 << HASH_SHIFT)\n#define HASH_MASK (HASH_SIZE - 1)\n\nstruct hash_table {\n\tunsigned int  used;\n\tunsigned long entry[HASH_SIZE];\n};\n\nvoid hash_table_reset(struct hash_table *table)\n{\n\tint i;\n\n\ttable->used = 0;\n\tfor (i = 0; i < HASH_SIZE; i++)\n\t\ttable->entry[i] = ~0;\n}\n\nunsigned int hash_function(unsigned long value)\n{\n\treturn value & HASH_MASK;\n}\n\nvoid dump_hash_table(struct hash_table *table)\n{\n\tint i;\n\n\tfor (i = 0; i < HASH_SIZE; i++) {\n\t\tif (table->entry[i] == ~0)\n\t\t\tprintf(\"%2u:       nil \\n\", i);\n\t\telse\n\t\t\tprintf(\"%2u:%10lu -> %2u\\n\",\n\t\t\t\ti, table->entry[i],\n\t\t\t\thash_function(table->entry[i]));\n\t}\n}\n\nvoid hash_function_test()\n{\n\tint i;\n\n\tsrandom(time(NULL));\n\n\tfor (i = 0; i < 10; i++) {\n\t\tunsigned long val = random();\n\t\tprintf(\"%10lu -> %2u\\n\", val, hash_function(val));;\n\t}\n}\n\nunsigned int next_probe(unsigned int prev_key)\n{\n\treturn (prev_key + 1) & HASH_MASK;\n}\n\nvoid next_probe_test()\n{\n\tint i;\n\tunsigned int key1, key2;\n\n\tkey1 = 0;\n\tfor (i = 0; i < HASH_SIZE; i++) {\n\t\tkey2 = next_probe(key1);\n\t\tprintf(\"%2u -> %2u\\n\", key1, key2);\n\t\tkey1 = key2;\n\t}\n}\n\nvoid hash_table_add(struct hash_table *table, unsigned long value)\n{\n\tunsigned int key = hash_function(value);\n\n\tif (table->used >= HASH_SIZE)\n\t\treturn;\n\n\twhile (table->entry[key] != ~0)\n\t\tkey = next_probe(key);\n\n\ttable->entry[key] = value;\n\ttable->used++;\n}\n\nunsigned int hash_table_slot(struct hash_table *table, unsigned long value)\n{\n\tint i;\n\tunsigned int key = hash_function(value);\n\n\tfor (i = 0; i < HASH_SIZE; i++) {\n\t\tif (table->entry[key] == value || table->entry[key] == ~0)\n\t\t\tbreak;\n\t\tkey = next_probe(key);\n\t}\n\n\treturn key;\n}\n\nbool hash_table_find(struct hash_table *table, unsigned long value)\n{\n\treturn table->entry[hash_table_slot(table, value)] == value;\n}\n\nvoid hash_table_del(struct hash_table *table, unsigned long value)\n{\n\tunsigned int i, j, k;\n\n\tif (!hash_table_find(table, value))\n\t\treturn;\n\n\ti = j = hash_table_slot(table, value);\n\n\twhile (true) {\n\t\ttable->entry[i] = ~0;\n\n\t\tdo {\n\t\t\tj = next_probe(j);\n\t\t\tif (table->entry[j] == ~0)\n\t\t\t\treturn;\n\t\t\tk = hash_function(table->entry[j]);\n\t\t} while ((i <= j) ? (i < k && k <= j) : (i < k || k <= j));\n\n\t\ttable->entry[i] = table->entry[j];\n\t\ti = j;\n\t}\n\ttable->used++;\n}\n\nvoid hash_table_add_test()\n{\n\tstruct hash_table table;\n\n\thash_table_reset(&table);\n\thash_table_add(&table, 87645);\n\n\tprintf(\"Table has%s 87645\\n\",\n\t\thash_table_find(&table, 87645) ? \"\":\"n't\");\n\tprintf(\"Table has%s 87647\\n\",\n\t\thash_table_find(&table, 87647) ? \"\":\"n't\");\n}\n\nvoid hash_table_del_test1()\n{\n\tstruct hash_table table;\n\n\thash_table_reset(&table);\n\thash_table_add(&table, 0x1ff0);\n\thash_table_add(&table, 0x2ff0);\n\thash_table_add(&table, 0x3ff0);\n\tdump_hash_table(&table);\n\n\tprintf(\"=== Remove 0x1ff0\\n\");\n\thash_table_del(&table, 0x1ff0);\n\tdump_hash_table(&table);\n}\n\nvoid hash_table_del_test2()\n{\n\tstruct hash_table table;\n\n\thash_table_reset(&table);\n\thash_table_add(&table, 0x1ff0);\n\thash_table_add(&table, 0x1ff1);\n\thash_table_add(&table, 0x1ff2);\n\thash_table_add(&table, 0x2ff0);\n\tdump_hash_table(&table);\n\n\tprintf(\"=== Remove 0x1ff0\\n\");\n\thash_table_del(&table, 0x1ff0);\n\tdump_hash_table(&table);\n}\n\nint main()\n{\n\t//hash_function_test();\n\t//next_probe_test();\n\t//hash_table_add_test();\n\thash_table_del_test2();\n\n\treturn 0;\n}\n"
  },
  {
    "path": "c-cpp/18_hashtable/listhash/listhash.c",
    "content": "/*************************************************************************\n > File Name: listhash.c\n > Author:  jinshaohui\n > Mail:    jinshaohui789@163.com\n > Time:    18-11-07\n > Desc:    \n ************************************************************************/\n#include<stdio.h>\n#include<stdlib.h>\n#include<string.h>\n#include<assert.h>\n#include\"listhash.h\"\n\n#ifdef MEMORY_TEST\n#include<mcheck.h>\n#endif\n\nhashtab * hashtab_create(int size,hash_key_func hash_value,\n\t\tkeycmp_func keycmp,hash_node_free_func hash_node_free)\n{\n\thashtab * h = NULL;\n\tint i = 0;\n\n\tif ((size < 0) || (hash_value == NULL) || (keycmp == NULL))\n\t{\n\t\treturn NULL;\n\t}\n\n\th = (hashtab *)malloc(sizeof(hashtab));\n\tif (h == NULL)\n\t{\n\t\treturn NULL;\n\t}\n\n\th->htables = (hashtab_node **)malloc(size * sizeof(hashtab_node*));\n    if (h->htables == NULL)\n\t{\n\t\treturn NULL;\n\t}\n\n\th->size = size;\n\th->nel = 0;\n\th->hash_value = hash_value;\n\th->keycmp = keycmp;\n\th->hash_node_free = hash_node_free;\n    \n\tfor (i = 0; i < size; i++)\n\t{\n\t\th->htables[i] = NULL;\n\t}\n\n\treturn h;\n}\n\nvoid hashtab_destory(hashtab *h)\n{\n\tint i = 0;\n\thashtab_node * cur = NULL;\n\thashtab_node * tmp = NULL;\n\n\tif (h == NULL)\n\t{\n\t\treturn;\n\t}\n\n\tfor (i = 0; i <h->size; i++)\n\t{\n\t\tcur = h->htables[i];\n\t\twhile (cur != NULL)\n\t\t{\n\t\t\ttmp = cur;\n\t\t\tcur = cur->next;\n\t\t\th->hash_node_free(tmp);\n\t\t}\n\t\th->htables[i] = NULL;\n\t}\n\t\n    free(h->htables);\n\tfree(h);\n\treturn;\n}\n\nint hashtab_insert(hashtab * h,void *key,void *data)\n{\n\tunsigned int hvalue = 0;\n\tint i = 0;\n\thashtab_node *cur = NULL; \n\thashtab_node *prev = NULL; \n\thashtab_node *newnode = NULL;\n\n\tif ((h == NULL) || (key == NULL) || (data == NULL))\n\t{\n\t\treturn 1;\n\t}\n\n\t/*获取hash 数值*/\n\thvalue = h->hash_value(h,key);\n\tcur = h->htables[hvalue];\n\n    /*hash桶中元素是从小到大排列的，找到要插入的位置*/\n\twhile((cur != NULL) && (h->keycmp(h,key,cur->key) > 0))\n\t{\n\t\tprev = cur;\n\t\tcur = cur->next;\n\t}\n\n\t/*如果key和当前key比对一致，直接返回，数据已经存在*/\n\tif ((cur != NULL) && (h->keycmp(h,key,cur->key) == 0))\n\t{\n\t\treturn 2;\n\t}\n\n\tnewnode = (hashtab_node *)malloc(sizeof(hashtab_node));\n\tif (newnode == NULL)\n\t{\n\t\treturn 3;\n\t}\n\n    newnode->key = key;\n\tnewnode->data = data;\n\tif (prev == NULL)\n\t{\n\t\tnewnode->next = h->htables[hvalue];\n\t\th->htables[hvalue] = newnode;\n\t}\n\telse\n\t{\n\t\tnewnode->next = prev->next;\n\t\tprev->next = newnode;\n\t}\n\n\th->nel++;\n    return 0;\n}\n\nhashtab_node *hashtab_delete(hashtab *h, void *key)\n{\n\tint hvalue = 0;\n\tint i = 0;\n\thashtab_node *cur = NULL; \n\thashtab_node *prev = NULL; \n\t\n\tif ((h == NULL) || (key == NULL))\n\t{\n\t\treturn NULL;\n\t}\n\n\t/*获取hash 数值*/\n\thvalue = h->hash_value(h,key);\n\tcur = h->htables[hvalue];\n    /*hash桶中元素是从小到大排列的，找到要插入的位置*/\n\twhile((cur != NULL) && (h->keycmp(h,key,cur->key) >= 0))\n\t{\n\t\tif (h->keycmp(h,key,cur->key) == 0)\n\t\t{\n\t\t\tif (prev == NULL)\n\t\t\t{\n                 h->htables[hvalue] = cur->next;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n                prev->next = cur->next;\n\t\t\t}\n\t\t\treturn cur;\n\t\t}\n\t\tprev = cur;\n\t\tcur = cur->next;\n\t}\n\n    return NULL;\n}\n\nvoid *hashtab_search(hashtab*h,void *key)\n{\n\tint hvalue = 0;\n\tint i = 0;\n\thashtab_node *cur = NULL; \n\t\n\tif ((h == NULL) || (key == NULL))\n\t{\n\t\treturn NULL;\n\t}\n\n\t/*获取hash 数值*/\n\thvalue = h->hash_value(h,key);\n\tcur = h->htables[hvalue];\n    /*hash桶中元素是从小到大排列的，找到要插入的位置*/\n\twhile((cur != NULL) && (h->keycmp(h,key,cur->key) >= 0))\n\t{\n\t\tif (h->keycmp(h,key,cur->key) == 0)\n\t\t{\n\t\t\treturn cur->data;\n\t\t}\n\t\tcur = cur->next;\n\t}\n\n    return NULL;\n}\n\nvoid hashtab_dump(hashtab *h)\n{\n\tint i = 0;\n\thashtab_node * cur = NULL;\n\n\tif (h == NULL)\n\t{\n\t\treturn ;\n\t}\n\n\tprintf(\"\\r\\n----开始--size[%d],nel[%d]------------\",h->size,h->nel);\n    for( i = 0; i < h->size; i ++)\n\t{\n\t\tprintf(\"\\r\\n htables[%d]:\",i);\n\t\tcur = h->htables[i];\n\t\twhile((cur != NULL))\n\t\t{\n\t\t    printf(\"key[%s],data[%s] \",cur->key,cur->data);\t\n\t\t\tcur = cur->next;\n\t\t}\n\t}\n\n\tprintf(\"\\r\\n----结束--size[%d],nel[%d]------------\",h->size,h->nel);\n}\n\nstruct test_node\n{\n\tchar key[80];\n\tchar data[80];\n};\n\nunsigned int siample_hash(const char *str)\n{\n\tregister unsigned int hash = 0;\n\tregister unsigned int seed = 131;\n\n\twhile(*str)\n\t{\n\t\thash = hash*seed + *str++;\n\t}\n\n\treturn hash & (0x7FFFFFFF);\n}\n\nint hashtab_hvalue(hashtab *h,const void *key)\n{\n\treturn (siample_hash(key) % h->size);\n}\n\nint hashtab_keycmp(hashtab *h,const void *key1,const void *key2)\n{\n\treturn strcmp(key1,key2);\n}\n\nvoid hashtab_node_free(hashtab_node*node)\n{\n    struct test_node * ptmp = NULL;\n\n\tptmp = container(node->key,struct test_node,key);\n\n\tfree(ptmp);\n\tfree(node);\n}\n\nint main ()\n{\n\t\n\tint i = 0;\n\tint res = 0;\n\tchar *pres = NULL;\n\thashtab_node * node = NULL;\n\tstruct test_node *p = NULL;\n    hashtab *h = NULL;\n    #ifdef MEMORY_TEST\n\tsetenv(\"MALLOC_TRACE\",\"1.txt\",1);\n    mtrace();\n    #endif\n\n\th = hashtab_create(5,hashtab_hvalue,hashtab_keycmp,hashtab_node_free);\n    assert(h!= NULL);\n\twhile(1)\n\t{\n\t\tp = (struct test_node*)malloc(sizeof(struct test_node));\n\t\tassert(p != NULL);\n\t\tprintf(\"\\r\\n 请输入key 和value，当可以等于\\\"quit\\\"时退出\");\n        scanf(\"%s\",p->key);\n\t\tscanf(\"%s\",p->data);\n\n\t\tif(strcmp(p->key,\"quit\") == 0)\n\t\t{\n\t\t\tfree(p);\n\t\t\tbreak;\n\t\t}\n\n        res = hashtab_insert(h,p->key,p->data);\n\t\tif (res != 0)\n\t\t{\n\t\t\tfree(p);\n\t\t\tprintf(\"\\r\\n key[%s],data[%s] insert failed %d\",p->key,p->data,res);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tprintf(\"\\r\\n key[%s],data[%s] insert success %d\",p->key,p->data,res);\n\t\t}\n\t}\n\n\thashtab_dump(h);\n\n\twhile(1)\n\t{\n\t\tp = (struct test_node*)malloc(sizeof(struct test_node));\n\t\tassert(p != NULL);\n\t\tprintf(\"\\r\\n 请输入key 查询value的数值，当可以等于\\\"quit\\\"时退出\");\n        scanf(\"%s\",p->key);\n\n\t\tif(strcmp(p->key,\"quit\") == 0)\n\t\t{\n\t\t\tfree(p);\n\t\t\tbreak;\n\t\t}\n        pres = hashtab_search(h,p->key);\n\t\tif (pres == NULL)\n\t\t{\n\t\t\tprintf(\"\\r\\n key[%s] search data failed\",p->key);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tprintf(\"\\r\\n key[%s],search data[%s] success\",p->key,pres);\n\t\t}\n\t\tfree(p);\n\t}\n\thashtab_dump(h);\n\twhile(1)\n\t{\n\t\tp = (struct test_node*)malloc(sizeof(struct test_node));\n\t\tassert(p != NULL);\n\t\tprintf(\"\\r\\n 请输入key 删除节点的数值，当可以等于\\\"quit\\\"时退出\");\n        scanf(\"%s\",p->key);\n\n\t\tif(strcmp(p->key,\"quit\") == 0)\n\t\t{\n\t\t\tfree(p);\n\t\t\tbreak;\n\t\t}\n        node = hashtab_delete(h,p->key);\n\t\tif (node == NULL)\n\t\t{\n\t\t\tprintf(\"\\r\\n key[%s] delete node failed \",p->key);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tprintf(\"\\r\\n key[%s],delete data[%s] success\",node->key,node->data);\n\t\t    h->hash_node_free(node);\n\t\t}\n\t\tfree(p);\n\t    hashtab_dump(h);\n\t}\n\n\thashtab_destory(h);\n    #ifdef MEMORY_TEST\n    muntrace();\n    #endif\n\treturn 0;\n\n}\n"
  },
  {
    "path": "c-cpp/18_hashtable/listhash/listhash.h",
    "content": "/*************************************************************************\n > File Name: listhash.h\n > Author:  jinshaohui\n > Mail:    jinshaohui789@163.com\n > Time:    18-11-06\n > Desc: 根据linux内核模块hashtab编写用户层hashtab接口\n         linux-4.19.1\\security\\selinux\\ss\\hashtab.c\n\t\t linux-4.19.1\\security\\selinux\\ss\\hashtab.h\n ************************************************************************/\n\n\n#ifndef __HASHTAB_H__\n#define __HASHTAB_H__\n\n\ntypedef struct _hashtab_node\n{\n\tvoid * key;\n\tvoid * data;\n\tstruct _hashtab_node *next;\n}hashtab_node;\n\ntypedef struct _hashtab\n{\n\thashtab_node **htables; /*哈希桶*/\n\tint size;              /*哈希桶的最大数量*/\n\tint nel;               /*哈希桶中元素的个数*/\n    int (*hash_value)(struct _hashtab *h,const void *key); /*哈希函数*/\n    int (*keycmp)(struct _hashtab *h,const void *key1,const void *key2);/*哈希key比较函数，当哈希数值一致时使用*/\n    void (*hash_node_free)(hashtab_node *node);\n}hashtab;\n\n\n#define HASHTAB_MAX_NODES  (0xffffffff)\n\ntypedef int (*hash_key_func)(struct _hashtab *h,const void *key); /*哈希函数*/\ntypedef int (*keycmp_func)(struct _hashtab *h,const void *key1,const void *key2);/*哈希key比较函数，当哈希数值一致时使用*/\ntypedef void (*hash_node_free_func)(hashtab_node *node);\n/*根据当前结构体元素的地址，获取到结构体首地址*/\n#define offsetof(TYPE,MEMBER) ((size_t) &((TYPE *)0)->MEMBER)\n#define container(ptr,type,member) ({\\\n  const typeof( ((type *)0)->member) *__mptr = (ptr);\\\n  (type *) ( (char *)__mptr - offsetof(type,member));})\n\n\nhashtab * hashtab_create(int size,hash_key_func hash_value,\n    \tkeycmp_func keycmp,hash_node_free_func hash_node_free);\nvoid hashtab_destory(hashtab *h);\nint hashtab_insert(hashtab * h,void *key,void *data);\nhashtab_node *hashtab_delete(hashtab *h, void *key);\nvoid *hashtab_search(hashtab*h,void *key);\n\n#endif\n"
  },
  {
    "path": "c-cpp/19_Dlisthash/Dlist.h",
    "content": "/*************************************************************************\n > File Name: Dlist.h\n > Author:  jinshaohui\n > Mail:    jinshaohui789@163.com\n > Time:    18-11-08\n > Desc:    linux内核源码双向链表实现include/linux/list.h\n ************************************************************************/\n#ifndef _LIST_HEAD_H\n#define _LIST_HEAD_H\n\n// 双向链表节点\nstruct list_head {\n    struct list_head *next, *prev;\n};\n\n// 初始化节点：设置name节点的前继节点和后继节点都是指向name本身。\n#define LIST_HEAD_INIT(name) { &(name), &(name) }\n\n// 定义表头(节点)：新建双向链表表头name，并设置name的前继节点和后继节点都是指向name本身。\n#define LIST_HEAD(name) \\\n    struct list_head name = LIST_HEAD_INIT(name)\n\n// 初始化节点：将list节点的前继节点和后继节点都是指向list本身。\nstatic inline void INIT_LIST_HEAD(struct list_head *list)\n{\n    list->next = list;\n    list->prev = list;\n}\n\n// 添加节点：将new插入到prev和next之间。\nstatic inline void __list_add(struct list_head *new,\n                  struct list_head *prev,\n                  struct list_head *next)\n{\n    next->prev = new;\n    new->next = next;\n    new->prev = prev;\n    prev->next = new;\n}\n\n// 添加new节点：将new添加到head之后，是new称为head的后继节点。\nstatic inline void list_add(struct list_head *new, struct list_head *head)\n{\n    __list_add(new, head, head->next);\n}\n\n// 添加new节点：将new添加到head之前，即将new添加到双链表的末尾。\nstatic inline void list_add_tail(struct list_head *new, struct list_head *head)\n{\n    __list_add(new, head->prev, head);\n}\n\n// 从双链表中删除entry节点。\nstatic inline void __list_del(struct list_head * prev, struct list_head * next)\n{\n    next->prev = prev;\n    prev->next = next;\n}\n\n// 从双链表中删除entry节点。\nstatic inline void list_del(struct list_head *entry)\n{\n    __list_del(entry->prev, entry->next);\n}\n\n// 从双链表中删除entry节点。\nstatic inline void __list_del_entry(struct list_head *entry)\n{\n    __list_del(entry->prev, entry->next);\n}\n\n// 从双链表中删除entry节点，并将entry节点的前继节点和后继节点都指向entry本身。\nstatic inline void list_del_init(struct list_head *entry)\n{\n    __list_del_entry(entry);\n    INIT_LIST_HEAD(entry);\n}\n\n// 用new节点取代old节点\nstatic inline void list_replace(struct list_head *old,\n                struct list_head *new)\n{\n    new->next = old->next;\n    new->next->prev = new;\n    new->prev = old->prev;\n    new->prev->next = new;\n}\n\n// 双链表是否为空\nstatic inline int list_empty(const struct list_head *head)\n{\n    return head->next == head;\n}\n\n// 获取\"MEMBER成员\"在\"结构体TYPE\"中的位置偏移\n#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)\n\n// 根据\"结构体(type)变量\"中的\"域成员变量(member)的指针(ptr)\"来获取指向整个结构体变量的指针\n#define container_of(ptr, type, member) ({          \\\n    const typeof( ((type *)0)->member ) *__mptr = (ptr);    \\\n    (type *)( (char *)__mptr - offsetof(type,member) );})\n\n// 遍历双向链表\n#define list_for_each(pos, head) \\\n    for (pos = (head)->next; pos != (head); pos = pos->next)\n\n#define list_for_each_safe(pos, n, head) \\\n    for (pos = (head)->next, n = pos->next; pos != (head); \\\n        pos = n, n = pos->next)\n\n#define list_entry(ptr, type, member) \\\n    container_of(ptr, type, member)\n\n#endif\n"
  },
  {
    "path": "c-cpp/19_Dlisthash/LinkedHashMap.c",
    "content": "/*************************************************************************\n > File Name: LinkedHashMap.c\n > Author:  jinshaohui\n > Mail:    jinshaohui789@163.com\n > Time:    18-11-08\n > Desc:    \n ************************************************************************/\n#include<assert.h>\n#include<string.h>\n#include<stdlib.h>\n#include<stdio.h>\n#include \"Dlist.h\"\n#include \"LinkedHashMap.h\"\n\n\nLinkedHashMap *LinkedHashMap_Create(int size,int nel_max,\n\t\thash_value_func hash_value,keycmp_func keycmp,\n\t\thash_node_free_func hash_node_free)\n{\n\tint i = 0;\n\tLinkedHashMap *h = NULL;\n\n\tif ((size <= 0) || (hash_value == NULL) || (keycmp == NULL))\n\t{\n\t\treturn NULL;\n\t}\n\n\th = (LinkedHashMap *)malloc(sizeof(LinkedHashMap));\n\tif (h == NULL)\n\t{\n\t\treturn NULL;\n\t}\n\n\th->hTabs = (LiskedHashMapNode**)malloc(sizeof(LiskedHashMapNode*) *size);\n\tif (h->hTabs == NULL)\n\t{\n\t\treturn NULL;\n\t}\n\th->size = size;\n\th->nel = 0;\n\th->nel_max = nel_max;\n\th->hash_value = hash_value;\n\th->keycmp = keycmp;\n\th->hash_node_free = hash_node_free;\n\n\tfor (i = 0; i < size; i++)\n\t{\n\t\th->hTabs[i] = NULL;\n\t}\n\n\tINIT_LIST_HEAD(&(h->header));\n\n\treturn h;\n}\n\nvoid LinkedHashMap_destory(LinkedHashMap *h)\n{\n\tstruct list_head * pos = NULL;\n\tstruct list_head * next = NULL;\n\tLiskedHashMapNode * ptmp = NULL;\n\n\tif (h == NULL)\n\t{\n\t\treturn;\n\t}\n\n\tlist_for_each_safe(pos,next,&h->header)\n\t{\n\t\tptmp = container_of(pos,LiskedHashMapNode,Dlist_node);\n\t\t/*从双向链表中删除*/\n\t\tlist_del_init(pos);\n\t\tif (h->hash_node_free != NULL)\n\t\t{\n\t\t\th->hash_node_free(ptmp,1);\n\t\t}\n\t}\n\n\tfree(h->hTabs);\n\tfree(h);\n\n    return;\n}\n\nint LinkedHashMap_insert(LinkedHashMap *h,void *key,void *data)\n{\n\tint i = 0;\n\tint hPos = 0;\n\tstruct list_head *pos = NULL;\n\tLiskedHashMapNode *cur = NULL;\n\tLiskedHashMapNode *prev = NULL;\n\n\thPos = h->hash_value(h,key);\n    cur = h->hTabs[hPos];\n\twhile((cur != NULL)&& (h->keycmp(h,key,cur->key) != 0))\n\t{\n\t\tprev = cur;\n\t\tcur = cur->next;\n\t}\n\n\tif(cur == NULL)\n\t{\n\t\t/*链表节点满时，取表头节点，从当前哈希表和双向链表中都删除*/\n        if(h->nel_max == h->nel)\n\t\t{\n\t\t\tcur = LinkedHashMap_delete(h,list_entry(h->header.next,LiskedHashMapNode,Dlist_node)->key);\n\n\t\t\tassert(cur != NULL);\n\t\t\t/*释放节点key 和data的内容*/\n\t\t\th->hash_node_free(cur,0);\n\t\t}\n\t\telse/*链表不满时，创建新的节点*/\n\t\t{\n\t\t    cur = (LiskedHashMapNode *)malloc(sizeof(LiskedHashMapNode));\n\t        if (cur == NULL)\n\t\t    {\n\t\t\t    return 1;\n\n\t\t    }\n\t\t}\n\t   /*插入到hash桶中*/\n\t    if(prev == NULL)\n\t    {\n            cur->next = h->hTabs[hPos];\n\t\t    h->hTabs[hPos] = cur;\n\t    }\n\t    else\n\t    {\n\t\t    cur->next = prev->next;\n\t\t    prev->next= cur;\n\t    }\n\t\th->nel++;\n\t}\n\telse\n\t{\n\t\t/*从双向链表中删除*/\n\t\tlist_del_init(&(cur->Dlist_node));\n\t\t/*只删除key 和data的内存*/\n\t\th->hash_node_free(cur,0);\n\t}\n\n\t/*赋值*/\n\tcur->key = key;\n\tcur->data = data;\n\n\n\t/*加的双向链表尾部*/\n\tlist_add_tail(&(cur->Dlist_node),&(h->header));\n\t\n\treturn 0;\n}\n\nLiskedHashMapNode * LinkedHashMap_delete(LinkedHashMap *h,void *key)\n{\n\tint hPos = 0;\n\tstruct list_head *pos = NULL;\n\tLiskedHashMapNode *cur = NULL;\n\tLiskedHashMapNode *prev = NULL;\n\n\t/*查找当前节点*/\n\thPos = h->hash_value(h,key);\n    cur = h->hTabs[hPos];\n\twhile((cur != NULL)&& (h->keycmp(h,key,cur->key) != 0))\n\t{\n\t\tprev = cur;\n\t\tcur = cur->next;\n\t}\n\n\tif (cur == NULL)\n    {\n\t\treturn NULL;\n\t}\n\n\t/*从哈希桶中删除*/\n\tif(prev == NULL)\n\t{\n\t    h->hTabs[hPos] = cur->next;\n\t}\n\telse\n\t{\n\t\tprev->next = cur->next;\n\t}\n\n\t/*从双向链表中删除*/\n\tlist_del_init(&(cur->Dlist_node));\n\th->nel--;\n\n    return cur;\n}\n\nvoid *LinkedHashMap_search(LinkedHashMap *h,void *key)\n{\n\tint hPos = 0;\n\tLiskedHashMapNode *cur = NULL;\n\n\t/*查找当前节点*/\n\thPos = h->hash_value(h,key);\n    cur = h->hTabs[hPos];\n\twhile((cur != NULL)&& (h->keycmp(h,key,cur->key) != 0))\n\t{\n\t\tcur = cur->next;\n\t}\n\n\tif (cur == NULL)\n\t{\n\t    return NULL;\n\t}\n\n\t/*从双向链表中删除节点，加入尾部*/\n\tif (h->header.prev != &(cur->Dlist_node))\n\t{\n\t    list_del_init(&(cur->Dlist_node));\n\t    list_add_tail(&(cur->Dlist_node),&(h->header));\n\t}\n\n\treturn cur->data;\n}\n\nvoid LinkedHashMap__dump(LinkedHashMap *h)\n{\n\tint i = 0;\n\tLiskedHashMapNode * cur = NULL;\n\tstruct list_head *pos = NULL;\n\n\tif (h == NULL)\n\t{\n\t\treturn ;\n\t}\n\n\tprintf(\"\\r\\n----开始--size[%d],nel[%d]------------\",h->size,h->nel);\n    for( i = 0; i < h->size; i ++)\n\t{\n\t\tprintf(\"\\r\\n htables[%d]:\",i);\n\t\tcur = h->hTabs[i];\n\t\twhile((cur != NULL))\n\t\t{\n\t\t    printf(\"key[%s],data[%s] \",cur->key,cur->data);\t\n\t\t\tcur = cur->next;\n\t\t}\n\t}\n\n\tprintf(\"\\r\\n--------------------------------------------------------\\r\\n\");\n\n\tlist_for_each(pos,&(h->header))\n\t{\n\t\tcur = list_entry(pos,LiskedHashMapNode,Dlist_node);\n\t\tprintf(\"key[%s] \",cur->key);\t\n\n\t}\n\n\tprintf(\"\\r\\n----结束--size[%d],nel[%d]------------\",h->size,h->nel);\n}\n\n\n\n\n\nstruct test_node\n{\n\tchar key[80];\n\tchar data[80];\n};\n\nunsigned int siample_hash(const char *str)\n{\n\tregister unsigned int hash = 0;\n\tregister unsigned int seed = 131;\n\n\twhile(*str)\n\t{\n\t\thash = hash*seed + *str++;\n\t}\n\n\treturn hash & (0x7FFFFFFF);\n}\n\nint hashtab_hvalue(LinkedHashMap *h,const void *key)\n{\n\treturn (siample_hash(key) % h->size);\n}\n\nint hashtab_keycmp(LinkedHashMap *h,const void *key1,const void *key2)\n{\n\treturn strcmp(key1,key2);\n}\n\nvoid hashtab_node_free(LiskedHashMapNode *node,int flg)\n{\n    struct test_node * ptmp = NULL;\n\n\tptmp = list_entry(node->key,struct test_node,key);\n\n\tfree(ptmp);\n\tif (flg)\n\t{\n\t    free(node);\n\t}\n}\n\n\nint main ()\n{\n\t\n\tint i = 0;\n\tint res = 0;\n\tchar *pres = NULL;\n\tLiskedHashMapNode * node = NULL;\n\tstruct test_node *p = NULL;\n    LinkedHashMap *h = NULL;\n\tsetenv(\"MALLOC_TRACE\",\"1.txt\",1);\n    mtrace();\n\n\th = LinkedHashMap_Create(3,6,hashtab_hvalue,hashtab_keycmp,hashtab_node_free);\n    assert(h!= NULL);\n\twhile(1)\n\t{\n\t\tp = (struct test_node*)malloc(sizeof(struct test_node));\n\t\tassert(p != NULL);\n\t\tprintf(\"\\r\\n 请输入key 和value，当可以等于\\\"quit\\\"时退出\");\n        scanf(\"%s\",p->key);\n\t\tscanf(\"%s\",p->data);\n\n\t\tif(strcmp(p->key,\"quit\") == 0)\n\t\t{\n\t\t\tfree(p);\n\t\t\tbreak;\n\t\t}\n\n        res = LinkedHashMap_insert(h,p->key,p->data);\n\t\tif (res != 0)\n\t\t{\n\t\t\tfree(p);\n\t\t\tprintf(\"\\r\\n key[%s],data[%s] insert failed %d\",p->key,p->data,res);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tprintf(\"\\r\\n key[%s],data[%s] insert success %d\",p->key,p->data,res);\n\t\t}\n\t    LinkedHashMap__dump(h);\n\t}\n\n\n\twhile(1)\n\t{\n\t\tp = (struct test_node*)malloc(sizeof(struct test_node));\n\t\tassert(p != NULL);\n\t\tprintf(\"\\r\\n 请输入key 查询value的数值，当可以等于\\\"quit\\\"时退出\");\n        scanf(\"%s\",p->key);\n\n\t\tif(strcmp(p->key,\"quit\") == 0)\n\t\t{\n\t\t\tfree(p);\n\t\t\tbreak;\n\t\t}\n        pres = LinkedHashMap_search(h,p->key);\n\t\tif (pres == NULL)\n\t\t{\n\t\t\tprintf(\"\\r\\n key[%s] search data failed\",p->key);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tprintf(\"\\r\\n key[%s],search data[%s] success\",p->key,pres);\n\t\t}\n\t\tfree(p);\n\tLinkedHashMap__dump(h);\n\t}\n\n\twhile(1)\n\t{\n\t\tp = (struct test_node*)malloc(sizeof(struct test_node));\n\t\tassert(p != NULL);\n\t\tprintf(\"\\r\\n 请输入key 删除节点的数值，当可以等于\\\"quit\\\"时退出\");\n        scanf(\"%s\",p->key);\n\n\t\tif(strcmp(p->key,\"quit\") == 0)\n\t\t{\n\t\t\tfree(p);\n\t\t\tbreak;\n\t\t}\n        node = LinkedHashMap_delete(h,p->key);\n\t\tif (node == NULL)\n\t\t{\n\t\t\tprintf(\"\\r\\n key[%s] delete node failed \",p->key);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tprintf(\"\\r\\n key[%s],delete data[%s] success\",node->key,node->data);\n\t\t    h->hash_node_free(node,1);\n\t\t}\n\t\tfree(p);\n\t    LinkedHashMap__dump(h);\n\t}\n\n    LinkedHashMap_destory(h);\n    muntrace();\n\treturn 0;\n\n}\n\n"
  },
  {
    "path": "c-cpp/19_Dlisthash/LinkedHashMap.h",
    "content": "/*************************************************************************\n > File Name: LinkedHashMap.h\n > Author:  jinshaohui\n > Mail:    jinshaohui789@163.com\n > Time:    18-11-08\n > Desc:    \n ************************************************************************/\n\n\n#ifndef __LINKED_HASH_MAP__\n#define __LINKED_HASH_MAP__\n\n/*数据存放节点*/\ntypedef struct _lisked_hash_map_node\n{\n\tvoid *key; /*键*/\n\tvoid *data; /*数据*/ZZ\n\tstruct _lisked_hash_map_node *next; /*哈希冲突时，用来挂接后续节点*/\n\tstruct list_head Dlist_node;/*用来挂接双向链表*/\n}LiskedHashMapNode;\n\ntypedef struct _lisked_hash_map\n{\n\tLiskedHashMapNode **hTabs;/*哈希桶*/\n\tstruct list_head header;/*双向循环链表头*/\n\tint size; /**/\n\tint nel_max; /*支持最大节点数*/\n\tint nel;   /*当前节点数*/\n    int (*hash_value)(struct _lisked_hash_map *h,const void *key); /*哈希函数*/\n    int (*keycmp)(struct _lisked_hash_map *h,const void *key1,const void *key2);/*哈希key比较函数，当哈希数值一致时使用*/\n    void (*hash_node_free)(LiskedHashMapNode *node,int flg);/*用来释放节点内存*/\n\n}LinkedHashMap;\n\ntypedef int (*hash_value_func)(struct _lisked_hash_map *h,const void *key); /*哈希函数*/\ntypedef int (*keycmp_func)(struct _lisked_hash_map *h,const void *key1,const void *key2);/*哈希key比较函数，当哈希数值一致时使用*/\ntypedef void (*hash_node_free_func)(LiskedHashMapNode *node,int flg);\n\nLiskedHashMapNode * LinkedHashMap_delete(LinkedHashMap *h,void *key);\n#endif\n"
  },
  {
    "path": "c-cpp/23_binarytree/binarytree.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <stdbool.h>\n#include <string.h>\n#include <time.h>\n\n/* Implement binary tree in array */\n\n#define MAX_TREE_NODES (1 << 8)\n\nstruct node {\n\tint data;\n};\n\nstruct binary_tree {\n\tunion {\n\t\tunsigned long nodes;\n\t\tstruct node *n[MAX_TREE_NODES];\n\t};\n};\n\nvoid init_binary_tree(struct binary_tree *tree)\n{\n\tint i;\n\n\tfor(i = 0; i < MAX_TREE_NODES; i++) {\n\t\ttree->n[i] = NULL;\n\t}\n}\n\nstruct node* create_node(int data)\n{\n\tstruct node* n;\n\n\tn = malloc(sizeof(struct node));\n\n\tif (n)\n\t\tn->data = data;\n\n\treturn n;\n}\n\nvoid fake_a_tree(struct binary_tree* tree)\n{\n\t/* data is in ordered */\n\tint i, data[10] = {7, 4, 9, 2, 6, 8, 10, 1, 3, 5};\n\n\tinit_binary_tree(tree);\n\n\t/* root start at 1 */\n\tfor (i = 0; i < 10; i++)\n\t\ttree->n[i+1] = create_node(data[i]);\n\n\ttree->nodes = 10;\n}\n\nvoid _in_order(struct binary_tree* tree, int index)\n{\n\tif (!tree->n[index])\n\t\treturn;\n\n\t/* left child at (index << 1) */\n\t_in_order(tree, index << 1);\n\n\tprintf(\"[%2d]: %4d\\n\", index, tree->n[index]->data);\n\n\t/* right child at (index << 1) + 1 */\n\t_in_order(tree, (index << 1) + 1);\n}\n\nvoid in_order(struct binary_tree* tree)\n{\n\t_in_order(tree, 1);\n}\n\nint main()\n{\n\tstruct binary_tree tree;\n\n\tfake_a_tree(&tree);\n\tin_order(&tree);\n\treturn 0;\n}\n"
  },
  {
    "path": "c-cpp/23_binarytree/tree/binarytree.c",
    "content": "/*************************************************************************\n > File Name: binarytree.c\n > Author:  jinshaohui\n > Mail:    jinshaohui789@163.com\n > Time:    18-11-12\n > Desc:    \n ************************************************************************/\n#include<assert.h>\n\n#include<string.h>\n#include<stdlib.h>\n#include<stdio.h>\n#include\"list_queue.h\"\n\ntypedef struct _treenode\n{\n\tint data;\n\tstruct _treenode *lchild;\n\tstruct _treenode *rchild;\n}Tnode,Tree;\n\nvoid binarytree_create(Tree **Root)\n{\n\tint a = 0;\n\tprintf(\"\\r\\n输入节点数值((当输入为100时，当前节点创建完成))):\");\n\tscanf(\"%d\",&a);\n\n\n\tif (a == 100)\n\t{\n\t\t*Root = NULL;\n\t}\n\telse\n\t{\n\t\t*Root = (Tnode *)malloc(sizeof(Tnode));\n\t\tif (*Root == NULL)\n\t\t{\n\t\t\treturn;\n\t\t}\n\n\t\t(*Root)->data = a;\n\t\tprintf(\"\\r\\n create %d 的左孩子:\",a);\n\t\tbinarytree_create(&((*Root)->lchild));\n\t\tprintf(\"\\r\\n create %d 的右孩子:\",a);\n\t\tbinarytree_create(&((*Root)->rchild));\n\t}\n\n\treturn ;\n}\n\nvoid binarytree_destory(Tree *root)\n{\n\tif (root == NULL)\n\t{\n\t\treturn;\n\t}\n\n\tbinarytree_destory(root->lchild);\n\tbinarytree_destory(root->rchild);\n\tfree(root);\n}\n\n/*先序遍历:根结点--》左子树---》右子树*/\nvoid binarytree_preorder(Tree *root)\n{\n\tif (root == NULL)\n\t{\n\t\treturn;\n\t}\n\tprintf(\" %d \",root->data);\n\tbinarytree_preorder(root->lchild);\n\tbinarytree_preorder(root->rchild);\n    return;\n}\n/*中序遍历:左子树--》跟节点---》右子树*/\nvoid binarytree_inorder(Tree *root)\n{\n\tif (root == NULL)\n\t{\n\t\treturn;\n\t}\n\tbinarytree_inorder(root->lchild);\n\tprintf(\" %d \",root->data);\n\tbinarytree_inorder(root->rchild);\n    return;\n}\n/*后序遍历:左子树---》右子树-》根节点*/\nvoid binarytree_postorder(Tree *root)\n{\n\tif (root == NULL)\n\t{\n\t\treturn;\n\t}\n\tbinarytree_postorder(root->lchild);\n\tbinarytree_postorder(root->rchild);\n\tprintf(\" %d \",root->data);\n    return;\n}\n\nvoid binarytree_levelorder(Tree * root)\n{\n\tlist_queue *queue = NULL;\n\tTnode * node = NULL;\n\n\tif(root == NULL)\n\t{\n\t\treturn;\n\t}\n\n\tqueue = list_queue_create();\n\n\t/*根节点先入队*/\n\tlist_queue_enqueue(queue,(void *)root);\n\n\twhile(!list_queue_is_empty(queue))\n\t{\n\t\tlist_queue_dequeue(queue,(void *)&node);\n\t\tprintf(\" %d \",node->data);\n\n\t\tif(node->lchild != NULL)\n\t\t{\n\t\t\tlist_queue_enqueue(queue,(void *)node->lchild);\n\t\t}\n\n\t\tif(node->rchild != NULL)\n\t\t{\n\t\t\tlist_queue_enqueue(queue,(void *)node->rchild);\n\t\t}\n\t}\n\n\tfree(queue);\n\n}\n/*打印叶子节点*/\nvoid binarytree_printfleaf(Tree *root)\n{\n\tif (root == NULL)\n\t{\n\t\treturn;\n\t}\n\n\tif ((root->lchild == NULL) && (root->rchild == NULL))\n\t{\n\t\tprintf(\" %d \",root->data);\n\t}\n\telse\n\t{\n\t\tbinarytree_printfleaf(root->lchild);\n\t\tbinarytree_printfleaf(root->rchild);\n\t}\n}\n/*打印叶子的个数*/\nint binarytree_getleafnum(Tree*root)\n{\n\tif (root == NULL)\n\t{\n\t\treturn 0;\n\t}\n\n\tif ((root->lchild == NULL) && (root->rchild == NULL))\n\t{\n\t\treturn 1;\n\t}\n\t\n\treturn binarytree_getleafnum(root->lchild) + binarytree_getleafnum(root->rchild);\n\n}\n/*打印数的高度*/\nint binarytree_gethigh(Tree *root)\n{\n\tint lhigh = 0;\n\tint rhigh = 0;\n\t\n\tif (root == NULL)\n\t{\n\t\treturn 0;\n\t}\n\n\tlhigh = binarytree_gethigh(root->lchild);\n\trhigh = binarytree_gethigh(root->rchild);\n\n\treturn ((lhigh > rhigh)?(lhigh + 1):(rhigh + 1));\n}\n\nint main()\n{\n\tTree *root = NULL;\n\n\tsetenv(\"MALLOC_TRACE\",\"1.txt\",1);\n    mtrace();\n\t\n\tprintf(\"\\r\\n创建二叉树:\");\n\tbinarytree_create(&root);\n\tprintf(\"\\r\\n先序遍历二叉树:\");\n\tbinarytree_preorder(root);\n\tprintf(\"\\r\\n中序遍历二叉树:\");\n\tbinarytree_inorder(root);\n\tprintf(\"\\r\\n后序遍历二叉树:\");\n\tbinarytree_postorder(root);\n\tprintf(\"\\r\\n层次遍历二叉树:\");\n\tbinarytree_levelorder(root);\n\n\tprintf(\"\\r\\n打印二叉树叶子节点:\");\n\tbinarytree_printfleaf(root);\n\tprintf(\"\\r\\n打印二叉树叶子节点个数:%d\",binarytree_getleafnum(root));\n\tprintf(\"\\r\\n打印二叉树高度:%d\",binarytree_gethigh(root));\n\n\tbinarytree_destory(root);\n\n    muntrace();\n\treturn 0;\n}\n\n"
  },
  {
    "path": "c-cpp/23_binarytree/tree/list_queue.c",
    "content": "/*************************************************************************\n > File Name: list_queue.c\n > Author:  jinshaohui\n > Mail:    jinshaohui789@163.com\n > Time:    18-10-13\n > Desc:    \n ************************************************************************/\n#include<stdio.h>\n#include<stdlib.h>\n#include<string.h>\n#include\"./list_queue.h\"\n\n/*创建队列头*/\nlist_queue *list_queue_create()\n{\n\tlist_queue * queue = NULL;\n\n\tqueue = (list_queue *)malloc(sizeof(list_queue));\n\tif(queue == NULL)\n\t{\n\t\treturn NULL;\n\t}\n\n\tqueue->num  = 0;\n\tqueue->head = NULL;\n\tqueue->tail = NULL;\n\n\treturn queue;\n}\nint list_queue_enqueue(list_queue *queue,void *data)\n{\n\tqueue_node *ptmp = NULL;\n\n\tif(queue == NULL)\n\t{\n\t\treturn -1;\n\t}\n\n\tptmp = (queue_node *)malloc(sizeof(queue_node));\n\tif (ptmp == NULL)\n\t{\n\t\treturn -1;\n\t}\n\n\tptmp->data = data;\n\tptmp->next = NULL;\n\tif (queue->head == NULL)\n\t{\n\t\tqueue->head = ptmp;\n\t}\n\telse\n\t{\n\t    queue->tail->next = ptmp;\n\n\t}\n\tqueue->tail = ptmp;\n\tqueue->num++;\n\n\treturn 0;\n}\n\n/*出队*/\nint list_queue_dequeue(list_queue *queue,void **data)\n{\n\tqueue_node * ptmp = NULL;\n\n\tif ((queue == NULL) || (data == NULL) || list_queue_is_empty(queue))\n\t{\n\t\treturn -1;\n\t}\n\n\t*data = queue->head->data;\n    ptmp = queue->head;\n\tqueue->head = queue->head->next;\n\tqueue->num--;\n\n\tif (queue->head == NULL)\n\t{\n\t\tqueue->tail = NULL;\n\t}\n\n\t\n\tfree(ptmp);\n\treturn 0;\n}\n"
  },
  {
    "path": "c-cpp/23_binarytree/tree/list_queue.h",
    "content": "/*************************************************************************\n > File Name: list_queue.h\n > Author:  jinshaohui\n > Mail:    jinshaohui789@163.com\n > Time:    18-10-13\n > Desc:    \n ************************************************************************/\n\n#ifndef LINK_LIST_QUEUE_H\n#define LINK_LIST_QUEUE_H\n\ntypedef struct _list_queue_node\n{\n\tvoid *data;\n\tstruct _list_queue_node *next;\n}queue_node;\n\ntypedef struct _list_queue\n{\n\tint num;\n\tqueue_node *head;\n\tqueue_node *tail;\n}list_queue;\n\n#define list_queue_is_empty(queue) ((queue->num) == 0)\nlist_queue *list_queue_create();\nint list_queue_enqueue(list_queue *queue,void *data);\nint list_queue_dequeue(list_queue *queue,void **data);\n\n#endif\n"
  },
  {
    "path": "c-cpp/24_binarysearchtree/binary_search_tree.cpp",
    "content": "/*\r\n * Filename: /home/zwk/code/data_structrue/c++/tree/binary_search_tree/main.cpp\r\n * Path: /home/zwk/code/data_structrue/c++/tree/binary_search_tree\r\n * Created Date: Wednesday, May 8th 2019, 11:04:48 pm\r\n * Author: zwk\r\n *\r\n * refer to https://time.geekbang.org/column/article/68334\r\n */\r\n\r\n#include <iostream>\r\n\r\nusing namespace std;\r\n\r\ntypedef int DataType;\r\n\r\nstruct treeNode\r\n{\r\n    DataType data;\r\n    treeNode *left = nullptr;\r\n    treeNode *right = nullptr;\r\n};\r\n\r\nclass binarySearchTree\r\n{\r\nprivate:\r\n    treeNode *root;\r\n    int num; // tree node numbers\r\npublic:\r\n    binarySearchTree() : num(0)\r\n    {\r\n        root = new treeNode;\r\n        root->left = nullptr;\r\n        root->right = nullptr;\r\n    }\r\n\r\n    bool find(DataType it, treeNode *root)\r\n    {\r\n        if (nullptr == root)\r\n            return false;\r\n        if (it == root->data) {\r\n            return true;\r\n        } else if (it > root->data) {\r\n            return find(it, root->right);\r\n        } else {\r\n            return find(it, root->left);\r\n        }\r\n    }\r\n\r\n    bool find_data(DataType it)\r\n    {\r\n        return find(it, root);\r\n        /*\r\n\t\ttreeNode* p = root;\r\n\t\twhile (p != nullptr)\r\n\t\t{\r\n\t\t\tif (it < p->data)p = p->left;\r\n\t\t\telse if (it>p->data)p = p->right;\r\n\t\t\telse return true;\r\n\t\t}\r\n\t\treturn false;\r\n\t\t*/\r\n    }\r\n\r\n    DataType get_max()\r\n    {\r\n        if (nullptr == root)\r\n            return NULL;\r\n        treeNode *tmp = root;\r\n        while (tmp->right != nullptr) {\r\n            tmp = tmp->right;\r\n        }\r\n        return tmp->data;\r\n    }\r\n\r\n    DataType get_min()\r\n    {\r\n        if (nullptr == root)\r\n            return NULL;\r\n        treeNode *tmp = root;\r\n        while (tmp->left != nullptr) {\r\n            tmp = tmp->left;\r\n        }\r\n        return tmp->data;\r\n    }\r\n\r\n    void insert_data(DataType it)\r\n    //  利用二分查找的思想，借助树的结构使用递归\r\n    {\r\n        if (0 == num) {\r\n            root->data = it;\r\n            num++;\r\n            return;\r\n        }\r\n        treeNode *p = root;\r\n        while (p != nullptr) {\r\n            if (it < p->data) {\r\n                if (nullptr == p->left) {\r\n                    p->left = new treeNode;\r\n                    p->left->data = it;\r\n                    num++;\r\n                    return;\r\n                }\r\n                p = p->left;\r\n            } else {\r\n                if (nullptr == p->right) {\r\n                    p->right = new treeNode;\r\n                    p->right->data = it;\r\n                    num++;\r\n                    return;\r\n                }\r\n                p = p->right;\r\n            }\r\n        }\r\n    }\r\n\r\n    DataType get_prenode(DataType it)\r\n    {\r\n        if (nullptr == root)\r\n            return NULL;\r\n        if (it == root->data)\r\n            return NULL;\r\n        treeNode *p = root;\r\n        treeNode *pp = nullptr;\r\n        while (p != nullptr) {\r\n            if (p->data < it) {\r\n                pp = p; // label parent root\r\n                p = p->right;\r\n\r\n            } else if (p->data > it) {\r\n                pp = p; // label parent root\r\n                p = p->left;\r\n            } else {\r\n\r\n                break;\r\n            }\r\n        }\r\n        return ((nullptr == p) ? NULL : pp->data);\r\n    }\r\n\r\n    DataType get_postnode(DataType it)\r\n    {\r\n        if (nullptr == root)\r\n            return -1;\r\n        treeNode *p = root;\r\n        while (p != nullptr) {\r\n            if (p->data < it) {\r\n                p = p->right;\r\n            } else if (p->data > it) {\r\n                p = p->left;\r\n            } else {\r\n                break;\r\n            }\r\n        }\r\n        if (nullptr == p) {\r\n            return -1;\r\n        } else if (p->left != nullptr) {\r\n            return p->left->data;\r\n        } else if (p->right != nullptr) {\r\n            return p->right->data;\r\n        } else {\r\n            return NULL;\r\n        }\r\n    }\r\n\r\n    void mid_order(treeNode *rt)\r\n    {\r\n        if (nullptr == rt)\r\n            return;\r\n        mid_order(rt->left);\r\n        cout << rt->data << '\\t';\r\n        mid_order(rt->right);\r\n    }\r\n\r\n    void order()\r\n    {\r\n        if (nullptr == root)\r\n            return;\r\n        return mid_order(root);\r\n    }\r\n\r\n    int get_high(treeNode *rt)\r\n    {\r\n        int lhigh = 0;\r\n        int rhigh = 0;\r\n        if (nullptr == rt)\r\n            return 0;\r\n        lhigh = get_high(rt->left);\r\n        rhigh = get_high(rt->right);\r\n        return ((lhigh > rhigh) ? (lhigh + 1) : (rhigh + 1));\r\n    }\r\n\r\n    int high()\r\n    {\r\n        if (nullptr == root)\r\n            return 1;\r\n        return get_high(root);\r\n    }\r\n\r\n    void delet(DataType it)\r\n    {\r\n        if (NULL == root)\r\n            return;\r\n        treeNode *p = root;\r\n        treeNode *pp = NULL; //pp记录的是p的父节点\r\n        while (p != NULL && p->data != it) {\r\n            pp = p;\r\n            if (it > p->data)\r\n                p = p->right;\r\n            else\r\n                p = p->left;\r\n        }\r\n        if (p == NULL)\r\n            return; //没有找到\r\n        //删除的节点有两个子节点\r\n        if (p->left != NULL && p->right != NULL) {\r\n            treeNode *minP = p->right;\r\n            treeNode *minPP = p;       //记录P的父节点\r\n            while (minP->left != NULL) //寻找右子树最小节点\r\n            {\r\n                minPP = minP;\r\n                minP = minP->left;\r\n            }\r\n            // 注意这里，非常巧妙的办法。只是换值。“换汤不换药”\r\n            // 用后继节点替换到要删除节点的位置。 然后就变成删除后继节点的问题了。为了逻辑统一 代码书写简洁。我们把后继节点赋给了p\r\n            p->data = minP->data; //将minP的值替换到p中\r\n            //将p换到叶节点上，使用叶节点方法进行删除， 而且最小节点肯定没有左节点，叶节点删除方法参见后面的代码。\r\n            p = minP;\r\n            pp = minPP;\r\n        }\r\n\r\n        //删除节点是叶节点或者是仅有一个节点\r\n        treeNode *child;\r\n        if (p->left != NULL)\r\n            child = p->left;\r\n        else if (p->right != NULL)\r\n            child = p->right;\r\n        else\r\n            child = NULL;\r\n\r\n        if (NULL == pp)\r\n            root = child; //删除的是根节点\r\n        else if (p == pp->left)\r\n            pp->left = child;\r\n        else\r\n            pp->right = child;\r\n    }\r\n};\r\n\r\nint main()\r\n{\r\n    binarySearchTree my_tree;\r\n\r\n    // must input in the order of layers\r\n    my_tree.insert_data(33);\r\n    my_tree.insert_data(16);\r\n    my_tree.insert_data(50);\r\n    my_tree.insert_data(13);\r\n    my_tree.insert_data(18);\r\n    my_tree.insert_data(34);\r\n    my_tree.insert_data(58);\r\n    my_tree.insert_data(15);\r\n    my_tree.insert_data(17);\r\n    my_tree.insert_data(25);\r\n    my_tree.insert_data(51);\r\n    my_tree.insert_data(66);\r\n    my_tree.insert_data(19);\r\n    my_tree.insert_data(27);\r\n    my_tree.insert_data(55);\r\n\r\n    if (my_tree.find_data(25)) {\r\n        cout << \"找到了数字25\" << endl;\r\n    } else {\r\n        cout << \"没有找到数字25\" << endl;\r\n    }\r\n    my_tree.delet(13);\r\n    my_tree.delet(18);\r\n    my_tree.delet(55);\r\n    cout << \"Max:  \" << my_tree.get_max() << endl;\r\n    cout << \"Min:  \" << my_tree.get_min() << endl;\r\n    cout << \"pre node of 17 is \" << my_tree.get_prenode(17) << endl;\r\n    cout << \"pre node of 51 is \" << my_tree.get_prenode(51) << endl;\r\n    cout << \"pre node of 33 is \" << my_tree.get_prenode(33) << endl;\r\n\r\n    cout << \"post node of 19 is \" << my_tree.get_postnode(19) << endl;\r\n    cout << \"post node of 25 is \" << my_tree.get_postnode(25) << endl;\r\n    cout << \"post node of 58 is \" << my_tree.get_postnode(58) << endl;\r\n    cout << \"post node of 58 is \" << my_tree.get_postnode(51) << endl;\r\n    my_tree.order();\r\n    cout << \"high of tree is \" << my_tree.high() << endl;\r\n    return 0;\r\n}\r\n"
  },
  {
    "path": "c-cpp/24_binarysearchtree/binarysearchtree.c",
    "content": "/*************************************************************************\n > File Name: binarysearchtree.c\n > Author:  jinshaohui\n > Mail:    jinshaohui789@163.com\n > Time:    18-11-12\n > Desc:    \n ************************************************************************/\n#include<assert.h>\n#include<string.h>\n#include<stdlib.h>\n#include<stdio.h>\n#include\"binarysearchtree.h\"\n\n\nbstree *bstree_create(compare_fuc compare,destory_fuc destory)\n{\n\tbstree *tree = NULL;\n\n\ttree = (bstree*)malloc(sizeof(bstree));\n\tif (tree == NULL)\n\t{\n\t\treturn NULL;\n\t}\n\n\ttree->size = 0;\n    tree->compare = compare;\n    tree->destory = destory;\n\ttree->root = NULL;\n    return tree;\n}\n\nbstree_node *bstree_search(bstree *tree,mytype data)\n{\n\tbstree_node *node = NULL;\n\tint res = 0;\n\n\tif ((tree == NULL) || (bstree_is_empty(tree)))\n\t{\n\t\treturn NULL;\n\t}\n    node = tree->root;\n\n\twhile(node != NULL)\n\t{\n\t\tres = tree->compare(data,node->data);\n\t\tif(res == 0)\n\t\t{\n\t\t\treturn node;\n\t\t}\n\t\telse if (res > 0)\n\t\t{\n\t\t\tnode = node->rchild;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tnode = node->lchild;\n\t\t}\n\t}\n   \n\treturn NULL;\n}\n\nint bstree_insert(bstree * tree, mytype data)\n{\n    bstree_node *node = NULL;\n    bstree_node *tmp = NULL;\n\tint res = 0;\n\n\tif (tree == NULL)\n\t{\n\t\treturn -1;\n\t}\n\n\tnode = (bstree_node *)malloc(sizeof(bstree_node));\n\tif (node == NULL)\n\t{\n\t\treturn -2;\n\t}\n\n\tnode->data = data;\n\tnode->lchild = NULL;\n\tnode->rchild = NULL;\n\n\t/*如果二叉树为空，直接挂到根节点*/\n\tif (bstree_is_empty(tree))\n\t{\n        tree->root = node;\n\t\ttree->size++;\n\t\treturn 0;\n\t}\n\n\ttmp = tree->root;\n\n\twhile(tmp != NULL)\n\t{\n\t\tres = tree->compare(data,tmp->data);\n\t\tif (res > 0) /*去右孩子查找*/\n\t\t{\n\t\t\tif (tmp->rchild == NULL)\n\t\t\t{\n\t\t\t\ttmp->rchild = node;\n\t\t\t\ttree->size++;\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t    tmp = tmp->rchild;\n\t\t}\n\t\telse /*去左孩子查找*/\n\t\t{\n\t\t\tif(tmp->lchild == NULL)\n\t\t\t{\n\t\t\t\ttmp->lchild = node;\n\t\t\t\ttree->size++;\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t\ttmp = tmp->lchild;\n\t\t}\n\t}\n    \n\treturn -3;\n}\n\nint bstree_delete(bstree *tree,mytype data)\n{\n\tbstree_node *node = NULL;/*要删除的节点*/\n\tbstree_node *pnode = NULL;/*要删除节点的父节点*/\n\tbstree_node *minnode = NULL;/*要删除节点的父节点*/\n\tbstree_node *pminnode = NULL;/*要删除节点的父节点*/\n    mytype tmp = 0;\n\tint res = 0;\n\n\tif ((tree == NULL) || (bstree_is_empty(tree)))\n\t{\n\t\treturn -1;\n\t}\n\n\tnode = tree->root;\n\twhile ((node != NULL) && ((res = tree->compare(data,node->data)) != 0))\n\t{\n\t\tpnode = node;\n\t\tif(res > 0)\n\t\t{\n            node = node->rchild;\n\t\t}\n\t\telse\n\t\t{\n            node = node->lchild;\n\t\t}\n\t}\n\t/*说明要删除的节点不存在*/\n\tif (node == NULL)\n\t{\n\t\treturn -2;\n\t}\n\n    /*1、如果要删除node有2个子节点，需要找到右子树的最小节点minnode，\n\t * 更新minnode和node节点数据，这样minnode节点就是要删除的节点\n\t * 再更新node和pnode节点指向要删除的节点*/\n\tif ((node->lchild != NULL) && (node->rchild != NULL))\n\t{\n\t\tminnode = node->rchild;\n\t\tpminnode = node;\n\n\t\twhile(minnode->lchild != NULL)\n\t\t{\n\t\t\tpminnode = minnode;\n\t\t\tminnode = minnode->lchild;\n\t\t}\n\n\t\t/*node 节点和minnode节点数据互换*/\n        tmp = node->data;\n\t\tnode->data = minnode->data;\n\t\tminnode->data = tmp;\n\t\t/*更新要删除的节点和其父节点*/\n\t\tnode = minnode;\n\t\tpnode = pminnode;\n\t}\n\n\t/*2、当前要删除的节点只有左孩子或者右孩子时，直接父节点的直向删除的节点*/\n\tif (node->lchild != NULL)\n\t{\n        minnode = node->lchild;\n\t}\n\telse if (node->rchild != NULL)\n\t{\n\t\tminnode = node->rchild;\n\t}\n\telse\n\t{\n\t\tminnode = NULL;\n\t}\n\n\tif (pnode == NULL)/*当要删除的时根节点时,*/\n\t{\n\t\ttree->root = minnode;\n\t}\n\telse if (pnode->lchild == node)\n\t{\n\t\tpnode->lchild = minnode;\n\t}\n\telse\n\t{\n\t\tpnode->rchild = minnode;\n\t}\n    tree->size--;\n\tfree (node);\n\n\treturn 0;\n}\n\n/*采用递归方式删除节点*/\nvoid bstree_destory_node(bstree *tree,bstree_node *root)\n{\n\tif (root == NULL)\n\t{\n\t\treturn;\n\t}\n\n\tbstree_destory_node(tree,root->lchild);\n\tbstree_destory_node(tree,root->rchild);\n\tfree(root);\n}\n\n/*二叉搜索树销毁*/\nvoid bstree_destory(bstree *tree)\n{\n\tbstree_destory_node(tree,tree->root);\n\tfree(tree);\n\treturn;\n}\n\n/*中序遍历打印树节点*/\nvoid bstree_inorder_node(bstree_node *root)\n{\n\tbstree_node *node = NULL;\n\tif (root == NULL)\n\t{\n\t\treturn;\n\t}\n\n\tbstree_inorder_node(root->lchild);\n\tprintf(\" %d \",root->data);\n\tbstree_inorder_node(root->rchild);\n    return;\n}\n\nvoid bstree_dump(bstree *tree)\n{\n\tbstree_node *node = NULL;\n\tif ((tree == NULL) || (bstree_is_empty(tree)))\n\t{\n\t\tprintf(\"\\r\\n 当前树是空树\");\n\t}\n\tprintf(\"\\r\\nSTART-----------------%d------------\\r\\n\",tree->size);\n\tbstree_inorder_node(tree->root);\n\tprintf(\"\\r\\nEND---------------------------------\",tree->size);\n\n}\n\nint bstree_compare(mytype key1,mytype key2)\n{\n\tif (key1 == key2)\n\t{\n\t\treturn 0;\n\t}\n\telse if (key1 > key2)\n\t{\n\t\treturn 1;\n\t}\n\telse\n\t{\n\t\treturn -1;\n\t}\n}\n\nint main()\n{\n\tbstree *tree = NULL;\n\tbstree_node *node = NULL;\n\tmytype data = 0;\n\tint res = 0;\n\n\tsetenv(\"MALLOC_TRACE\",\"1.txt\",1);\n    mtrace();\n\n\ttree = bstree_create(bstree_compare,NULL);\n\tassert(tree != NULL);\n\n    while(1)\n\t{\n\t\tprintf(\"\\r\\n插入一个数字，输入100时退出：\");\n\t\tscanf(\"%d\",&data);\n\t\tif(data == 100)break;\n\t\tres = bstree_insert(tree,data);\n\t\tprintf(\"\\r\\n %d 插入%s成功\",data,(res != 0)?(\"不\"):(\" \"));\n\t}\n\tbstree_dump(tree);\n\n    while(1)\n\t{\n\t\tprintf(\"\\r\\n查询一个数字，输入100时退出：\");\n\t\tscanf(\"%d\",&data);\n\t\tif(data == 100)break;\n\t\tnode = bstree_search(tree,data);\n\t\tprintf(\"\\r\\n %d %s存在树中\",data,(node == NULL)?(\"不\"):(\" \"));\n\t}\n\tbstree_dump(tree);\n    while(1)\n\t{\n\t\tprintf(\"\\r\\n删除一个数字，输入100时退出：\");\n\t\tscanf(\"%d\",&data);\n\t\tif(data == 100)break;\n\t\tres = bstree_delete(tree,data);\n\t\tprintf(\"\\r\\n %d 删除%s成功\",data,(res != 0)?(\"不\"):(\" \"));\n\t    bstree_dump(tree);\n\t}\n\n\tbstree_destory(tree);\n\n    muntrace();\n\n\treturn 0;\n}\n"
  },
  {
    "path": "c-cpp/24_binarysearchtree/binarysearchtree.h",
    "content": "/*************************************************************************\n > File Name: binarysearchtree.h\n > Author:  jinshaohui\n > Mail:    jinshaohui789@163.com\n > Time:    18-11-12\n > Desc:    \n ************************************************************************/\n#ifndef __BINARY_SEARCH_TREE__\n#define __BINARY_SEARCH_TREE__\ntypedef int mytype;\n\ntypedef struct _bstree_node\n{\n\tmytype data;\n\tstruct _bstree_node *lchild;\n\tstruct _bstree_node *rchild;\n}bstree_node;\n\ntypedef struct _bstree\n{\n    int size;\n    int (*compare)(mytype key1,mytype key2);\n\tint (*destory)(mytype data);\n\tbstree_node *root;\n}bstree;\n\ntypedef int (*compare_fuc)(mytype key1,mytype key2);\ntypedef int (*destory_fuc)(mytype data);\n\n#define bstree_is_empty(tree)  (tree->size == 0)\n\nbstree *bstree_create(compare_fuc compare,destory_fuc destory);\n\n#endif\n"
  },
  {
    "path": "c-cpp/24_binarysearchtree/bst.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <stdbool.h>\n#include <string.h>\n#include <time.h>\n\nenum child_dir {\n\tleft_child,\n\tright_child,\n\troot,\n};\n\nstruct node {\n\tunsigned long data; \n\tstruct node *left;\n\tstruct node *right;\n};\n\nstruct root {\n\tstruct node *r;\n};\n\nvoid dump(struct node *node, int level, enum child_dir dir)\n{\n\tif (!node)\n\t\treturn;\n\n\tdump(node->right, level + 1, right_child);\n\n\tif (dir == left_child)\n\t\tprintf(\"%*s\\n\", level*3, \"|\");\n\n\tprintf(\"%*s - %05lu\\n\", level*3, \" \", node->data);\n\n\tif (dir == right_child)\n\t\tprintf(\"%*s\\n\", level*3, \"|\");\n\n\tdump(node->left, level + 1, left_child);\n}\n\nstruct node* find(struct root *root, unsigned long data)\n{\n\tstruct node* n = root->r;\n\n\twhile (n) {\n\t\tif (n->data == data)\n\t\t\treturn n;\n\t\tif (data < n->data)\n\t\t\tn = n->left;\n\t\telse\n\t\t\tn = n->right;\n\t}\n\n\treturn NULL;\n}\n\nstruct node* new_node(unsigned long data)\n{\n\tstruct node *n;\n\n\tn = malloc(sizeof(struct node));\n\n\tn->data = data;\n\tn->left = n->right = NULL;\n\treturn n;\n}\n\nvoid insert(struct root *root, struct node *new)\n{\n\tstruct node *parent;\n\n\tif (!root->r) {\n\t\troot->r = new;\n\t\treturn;\n\t}\n\n\tparent = root->r;\n\n\twhile (true) {\n\t\t/* Don't support duplicate data */\n\t\tif (new->data == parent->data)\n\t\t\tbreak;\n\n\t\tif (new->data < parent->data) {\n\t\t\tif (!parent->left) {\n\t\t\t\tparent->left = new;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tparent = parent->left;\n\t\t} else {\n\t\t\tif (!parent->right) {\n\t\t\t\tparent->right = new;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tparent = parent->right;\n\t\t}\n\t}\n}\n\nstruct node* delete(struct root *root, unsigned long data)\n{\n\tstruct node *n = root->r, **p = &root->r;\n\tstruct node *child;\n\n\twhile (n && n->data != data) {\n\t\tif (data < n->data) {\n\t\t\tp = &n->left;\n\t\t\tn = n->left;\n\t\t} else {\n\t\t\tp = &n->right;\n\t\t\tn = n->right;\n\t\t}\n\t}\n\n\tif (!n)\n\t\treturn NULL;\n\t\n\tif (n->left && n->right) {\n\t\tstruct node *rn = n->right, **rp = &n->right;\n\n\t\twhile (rn->left) {\n\t\t\trp = &rn->left;\n\t\t\trn = rn->left;\n\t\t}\n\n\t\tn->data = rn->data;\n\t\tn = rn;\n\t\tp = rp;\n\t}\n\n\tchild = n->left ? n->left : n->right;\n\t*p = child;\n\n\treturn NULL;\n}\n\nvoid insert_test()\n{\n\tstruct root tree;\n\tstruct node* n;\n\n\ttree.r = NULL;\n\n\tinsert(&tree, new_node(9));\n\n\tinsert(&tree, new_node(5));\n\tinsert(&tree, new_node(2));\n\tinsert(&tree, new_node(8));\n\n\tinsert(&tree, new_node(18));\n\tinsert(&tree, new_node(13));\n\tinsert(&tree, new_node(21));\n\tinsert(&tree, new_node(20));\n\n\tdump(tree.r, 0, root);\n\n\tn = find(&tree, 18);\n\tif (n && n->data == 18)\n\t\tprintf(\"Get 18\\n\");\n\n}\n\nvoid delete_test()\n{\n\tstruct root tree;\n\tstruct node* n;\n\n\ttree.r = NULL;\n\n\tinsert(&tree, new_node(9));\n\n\tinsert(&tree, new_node(5));\n\tinsert(&tree, new_node(2));\n\tinsert(&tree, new_node(8));\n\n\tinsert(&tree, new_node(18));\n\tinsert(&tree, new_node(13));\n\tinsert(&tree, new_node(21));\n\tinsert(&tree, new_node(20));\n\n\tdump(tree.r, 0, root);\n\n\tdelete(&tree, 20);\n\tprintf(\"Delete 20\\n\");\n\tdump(tree.r, 0, root);\n\n\tdelete(&tree, 9);\n\tprintf(\"Delete 9\\n\");\n\tdump(tree.r, 0, root);\n}\n\nint main()\n{\n\t//insert_test();\n\tdelete_test();\n\treturn 0;\n}\n"
  },
  {
    "path": "c-cpp/24_tree/Trie.c",
    "content": "/*************************************************************************\r\n > Author:  Liu Zhang\r\n > Mail:    lz-850610@163.com\r\n > Time:    2019-05-10\r\n > Desc:    字典树实现\r\n ************************************************************************/\r\n#include <stdio.h>\r\n#include <stdlib.h>\r\n#include <string.h>\r\n\r\n#define OK 1\r\n#define ERROR 0\r\n#define TRUE 1\r\n#define FALSE 0\r\n\r\ntypedef int Status;\r\n\r\ntypedef struct Node {\r\n    char data;\r\n    struct Node *children[26];\r\n    Status end;\r\n} Trie, *TriePtr;\r\n\r\nvoid Init(TriePtr *T)\r\n{\r\n    (*T) = (TriePtr)malloc(sizeof(Trie));\r\n    (*T)->data = '/';\r\n    (*T)->end = FALSE;\r\n}\r\n\r\nvoid Insert(TriePtr T, char *str) {\r\n\r\n    int index;\r\n    char c;\r\n\r\n    while(c = *str++)\r\n    {\r\n        index = c - 'a';\r\n        if (T->children[index] == NULL)\r\n        {\r\n            TriePtr Node;\r\n            Node = (TriePtr)malloc(sizeof(Trie));\r\n            Node->data = c;\r\n            Node->end = FALSE;\r\n            T->children[index] = Node;\r\n        }\r\n\r\n        T = T->children[index];\r\n    }\r\n\r\n    T->end = TRUE;\r\n}\r\n\r\n\r\nStatus Search(TriePtr T, char *str) {\r\n\r\n    int index;\r\n    char c;\r\n\r\n    while(c = *str++)\r\n    {\r\n        index = c - 'a';\r\n        if (T->children[index] == NULL)\r\n        {\r\n            return FALSE;\r\n        }\r\n\r\n        T = T->children[index];\r\n    }\r\n\r\n    if (T->end) {\r\n        return TRUE;\r\n    } else {\r\n        return FALSE;\r\n    }\r\n}\r\n\r\n\r\nint main(int argc, char const *argv[])\r\n{\r\n    TriePtr T;\r\n    Init(&T);\r\n    char *str = \"hello\";\r\n    char *str2 = \"hi\";\r\n\r\n    Insert(T, str);\r\n\r\n    printf(\"str is search %d\\n\", Search(T, str));\r\n    printf(\"str2 is search %d\\n\", Search(T, str2));\r\n    return 0;\r\n}"
  },
  {
    "path": "c-cpp/24_tree/binarysearchtree.c",
    "content": "/*************************************************************************\n > Author:  Liu Zhang\n > Mail:    lz-850610@163.com\n > Time:    2019-05-10\n > Desc:    二叉搜索树实现\n ************************************************************************/\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n#define OK 1\n#define ERROR 0\n#define TRUE 1\n#define FALSE 0\ntypedef int Status;\ntypedef char ElemType;\n\ntypedef struct node {\n    ElemType data;\n    struct node *lchild, *rchild;\n} BTree, *BTreePtr;\n\n/**************** 插入 **********************/\nStatus Insert(BTreePtr *T, ElemType e) {\n\n    BTreePtr p;\n\n    if (*T == NULL) {\n        *T = (BTreePtr)malloc(sizeof(BTree));\n        (*T)->data = e;\n\n        return TRUE;\n    } else {\n        p = *T;\n        while ( p != NULL) {\n\n            if (e > p->data) {\n\n                if (p->rchild == NULL) {\n                    p->rchild = (BTreePtr) malloc (sizeof(BTree));\n                    p->rchild->data = e;\n                    return TRUE;\n                }\n                p = p->rchild;\n            } else {\n\n                if (p->lchild == NULL)\n                {\n                    p->lchild = (BTreePtr) malloc (sizeof(BTree));\n                    p->lchild->data = e;\n                    return TRUE;\n                }\n                p = p->lchild;\n            }\n        }\n    }\n\n    return FALSE;\n}\n\n/**************** 删除 **********************/\nStatus Delete(BTreePtr T, ElemType e) {\n    BTreePtr p, pp, minP, minPP, child;\n    child = NULL;\n    p = T;\n    pp = NULL;\n\n    while ( (p != NULL) && (p->data != e) ) {\n        pp = p;\n\n        if (e > p->data) {\n            p = p->rchild;\n        } else {\n            p = p->lchild;\n        }\n    }\n\n    if (p == NULL) return FALSE;\n\n    //双节点\n    if ((p->lchild != NULL) && (p->rchild != NULL))\n    {\n        minPP = p;\n        minP = p->rchild;\n\n        while (minP->lchild != NULL) {\n            minPP = minP;\n            minP = minP->lchild;\n        }\n        p->data = minP->data;\n        minPP->lchild = minP->rchild;\n        free(minP);\n\n        return TRUE;\n    }\n\n    //有一个节点\n    if ((p->lchild != NULL) || (p->rchild != NULL)) { //应该将原有的pp同child连接在一起\n\n        if (p->lchild) {\n            child = p->lchild;\n        } else {\n           child = p->rchild;\n        }\n        if(pp->data>p->data)\n        {\n            pp->lchild=child;\n        } else\n        {\n            pp->rchild=child;\n        }\n        free(p);\n        return TRUE;\n    }\n\n    //没有节点\n    if (pp->lchild == p) {//这里面临pp除p以外的节点为null的情况\n        pp->lchild = child;\n    } else {\n        pp->rchild = child;\n    }\n\n    return TRUE;\n}\n\n/**************** 查找 **********************/\n\nStatus Find(BTreePtr T, ElemType e) {\n\n    if (T == NULL) return FALSE;\n\n    while ((T != NULL) && (T->data != e)) {\n\n        if (e > T->data) {\n            T = T->rchild;\n        } else {\n            T = T->lchild;\n        }\n    }\n\n    if (T) {\n        return TRUE;\n    } else {\n        return FALSE;\n    }\n}\n\n\n/**************** 最大值 **********************/\nElemType FindMax(BTreePtr T) {\n    ElemType max;\n\n    while(T != NULL) {\n        max = T->data;\n        T = T->rchild;\n    }\n    return max;\n}\n\n\n/**************** 最小值 **********************/\nElemType FindMin(BTreePtr T) {\n    ElemType min;\n\n    while(T != NULL) {\n        min = T->data;\n        T = T->lchild;\n    }\n    return min;\n}\n\n\nvoid PreOrderTraverse(BTreePtr T)//前序遍历二叉树\n{\n    if (T == NULL) return;\n\n    if(T)\n    {\n        printf(\"%d \",T->data);\n        PreOrderTraverse(T->lchild);\n        PreOrderTraverse(T->rchild);\n    }\n}\n\n\nvoid DestroyTree(BTreePtr T) {\n    if (T)\n    {\n        if (T->lchild)\n        {\n            DestroyTree(T->lchild);\n        }\n\n        if(T->rchild)\n        {\n            DestroyTree(T->rchild);\n        }\n\n        free(T);\n        T = NULL;\n    }\n}\n\n/***************** 执行测试 *************************/\nint main(int argc, char const *argv[])\n{\n    BTreePtr T;\n    T = NULL;\n    int a[] = {33, 16, 50, 13, 18, 34, 58, 15, 17, 25, 51, 66, 19, 27, 55};\n    int i;\n    for (i = 0; i < 15; i++) {\n        Insert(&T, a[i]);\n    }\n    printf(\"Max is %d\\n\", FindMax(T));\n    printf(\"Min is %d\\n\", FindMin(T));\n    Delete(T, 18);\n    Delete(T, 13);\n    PreOrderTraverse(T);\n    DestroyTree(T);\n\n    return 0;\n}\n"
  },
  {
    "path": "c-cpp/28_heap/heap.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <stdbool.h>\n#include <string.h>\n#include <time.h>\n\n/* Implement heap */\n\n#define MAX_HEAP_SIZE (1 << 8)\n\nstruct element {\n\tint data;\n};\n\nstruct heap {\n\tunion {\n\t\tunsigned long elements;\n\t\tstruct element *elem[MAX_HEAP_SIZE];\n\t};\n};\n\nvoid init_heap(struct heap *heap)\n{\n\tint i;\n\n\tfor(i = 0; i < MAX_HEAP_SIZE; i++) {\n\t\theap->elem[i] = NULL;\n\t}\n}\n\nvoid dump_heap(struct heap *heap, int index)\n{\n\tstruct element *elem;\n\tint level;\n\n\tif (index > heap->elements)\n\t\treturn;\n\n\telem = heap->elem[index];\n\tlevel = fls(index);\n\n\tdump_heap(heap, index * 2 + 1);\n\n\tif (!(index % 2) && index != 1)\n\t\tprintf(\"%*s\\n\", level*3, \"|\");\n\n\tprintf(\"%*s - %05d\\n\", level*3, \" \", elem->data);\n\n\tif (index % 2 && index != 1)\n\t\tprintf(\"%*s\\n\", level*3, \"|\");\n\n\tdump_heap(heap, index * 2);\n}\n\nvoid dump(struct heap *heap, int elements)\n{\n\tint i;\n\n\tfor (i = 1; i <= elements; i++)\n\t\tprintf(\"[%02d]: %4d\\n\", i, heap->elem[i]->data);\n\n}\n\nstruct element* create_element(int data)\n{\n\tstruct element *elem;\n\n\telem = malloc(sizeof(struct element));\n\n\tif (elem)\n\t\telem->data = data;\n\n\treturn elem;\n}\n\nvoid fake_a_heap(struct heap *heap)\n{\n\t/* data is in ordered */\n\tint i, data[10] = {7, 4, 9, 2, 6, 8, 10, 1, 3, 5};\n\n\tinit_heap(heap);\n\n\t/* root start at 1 */\n\tfor (i = 0; i < 10; i++)\n\t\theap->elem[i+1] = create_element(data[i]);\n\n\theap->elements = 10;\n}\n\nvoid swap(struct heap *heap, int i, int j)\n{\n\tstruct element *tmp;\n\n\ttmp = heap->elem[j];\n\theap->elem[j] = heap->elem[i];\n\theap->elem[i] = tmp;\n}\n\nvoid heapify(struct heap *heap, int parent)\n{\n\tstruct element **elem = heap->elem;\n\tint elements = heap->elements;\n\tint left, right, max;\n\n\twhile (true) {\n\t\tleft = parent * 2;\n\t\tright = left + 1;\n\t\t\n\t\tmax = parent;\n\t\tif (left <= elements && elem[max]->data < elem[left]->data)\n\t\t\tmax = left;\n\t\tif (right <= elements && elem[max]->data < elem[right]->data)\n\t\t\tmax = right;\n\n\t\tif (max == parent)\n\t\t\tbreak;\n\n\t\tswap(heap, max, parent);\n\t\tparent = max;\n\t}\n}\n\nvoid build_heap(struct heap *heap)\n{\n\tint i;\n\n\tfor (i = heap->elements / 2; i >= 1; i--)\n\t\theapify(heap, i);\n}\n\nint heap_sort(struct heap *heap)\n{\n\tint elements = heap->elements;\n\n\twhile (heap->elements) {\n\t\tswap(heap, 1, heap->elements);\n\t\theap->elements--;\n\t\theapify(heap, 1);\n\t}\n\t\n\treturn elements;\n}\n\nint main()\n{\n\tstruct heap heap;\n\tint elements;\n\n\tfake_a_heap(&heap);\n\tdump_heap(&heap, 1);\n\n\tprintf(\"After Heapify:\\n\");\n\tbuild_heap(&heap);\n\tdump_heap(&heap, 1);\n\n\tprintf(\"After Heap sort:\\n\");\n\telements = heap_sort(&heap);\n\tdump(&heap, elements);\n\treturn 0;\n}\n"
  },
  {
    "path": "c-cpp/30_Graph/graph.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <stdbool.h>\n#include <string.h>\n#include <time.h>\n\nstruct vertex;\nstruct vertex_adjs {\n\tstruct vertex *v;\n\tstruct vertex_adjs *next;\n};\n\nstruct vertex {\n\tint data;\n\tstruct vertex_adjs *adj;\n};\n\n#define MAX_GRAPH_VERTEX (1 << 8)\nstruct graph {\n\tstruct vertex *vxs[MAX_GRAPH_VERTEX];\n};\n\nvoid init_graph(struct graph *graph)\n{\n\tint i;\n\n\tfor (i = 0; i < MAX_GRAPH_VERTEX; i++)\n\t\tgraph->vxs[i] = NULL;\n}\n\nstruct vertex *create_vertex(int data)\n{\n\tstruct vertex *v;\n\n\tv = malloc(sizeof(struct vertex));\n\n\tif (v) {\n\t\tv->data = data;\n\t\tv->adj = NULL;\n\t}\n\n\treturn v;\n}\n\nstruct vertex_adjs *create_vertex_adj(struct vertex *v)\n{\n\tstruct vertex_adjs *v_adj;\n\n\tv_adj = malloc(sizeof(struct vertex_adjs));\n\n\tif (!v_adj)\n\t\treturn NULL;\n\n\tv_adj->v = v;\n\tv_adj->next = NULL;\n\treturn v_adj;\n}\n\nvoid insert_adj(struct vertex *v, struct vertex *adj)\n{\n\tstruct vertex_adjs **v_adj;\n\n\tv_adj = &v->adj;\n\n\twhile (*v_adj)\n\t\tv_adj = &(*v_adj)->next;\n\n\t*v_adj = create_vertex_adj(adj);\n}\n\nvoid dump_raw(struct graph *graph)\n{\n\tint i;\n\n\tfor (i = 0; i < MAX_GRAPH_VERTEX; i++) {\n\t\tstruct vertex *v = graph->vxs[i];\n\t\tstruct vertex_adjs *adj;\n\t\tif (v == NULL)\n\t\t\tcontinue;\n\n\t\tprintf(\"Vertex[%02d]: %8d ->\", i, v->data);\n\n\t\tadj = v->adj;\n\t\twhile (adj) {\n\t\t\tprintf(\" %8d,\", adj->v->data);\n\t\t\tadj = adj->next;\n\t\t}\n\t\tprintf(\"\\n\");\n\t}\n}\n\n/* \n  1 ----- 2 ----- 3\n  |     / |     /\n  |    /  |    / \n  |   /   |   /  \n  |  /    |  /   \n  | /     | /    \n  4 ----- 5\n*/\nvoid fake_a_graph(struct graph *graph)\n{\n\tint i;\n\n\tinit_graph(graph);\n\n\tfor (i = 0; i < 5; i++)\n\t\tgraph->vxs[i] = create_vertex(i+1);\n\n\t/* connect 1 -> 2, 1 -> 4 */\n\tinsert_adj(graph->vxs[0], graph->vxs[1]);\n\tinsert_adj(graph->vxs[0], graph->vxs[3]);\n\t/* connect 2 -> 1, 2 -> 3, 2 -> 5, 2 -> 4 */\n\tinsert_adj(graph->vxs[1], graph->vxs[0]);\n\tinsert_adj(graph->vxs[1], graph->vxs[2]);\n\tinsert_adj(graph->vxs[1], graph->vxs[4]);\n\tinsert_adj(graph->vxs[1], graph->vxs[3]);\n\t/* connect 3 -> 2, 3 -> 5 */\n\tinsert_adj(graph->vxs[2], graph->vxs[1]);\n\tinsert_adj(graph->vxs[2], graph->vxs[4]);\n\t/* connect 4 -> 1, 4 -> 2, 4 -> 5 */\n\tinsert_adj(graph->vxs[3], graph->vxs[0]);\n\tinsert_adj(graph->vxs[3], graph->vxs[1]);\n\tinsert_adj(graph->vxs[3], graph->vxs[4]);\n\t/* connect 5 -> 4, 5 -> 2, 5 -> 3 */\n\tinsert_adj(graph->vxs[4], graph->vxs[3]);\n\tinsert_adj(graph->vxs[4], graph->vxs[1]);\n\tinsert_adj(graph->vxs[4], graph->vxs[3]);\n}\n\nint main()\n{\n\tstruct graph g;\n\n\tfake_a_graph(&g);\n\tdump_raw(&g);\n\treturn 0;\n}\n"
  },
  {
    "path": "c-cpp/bst.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <stdbool.h>\n#include <string.h>\n#include <time.h>\n\nenum child_dir {\n\tleft_child,\n\tright_child,\n\troot,\n};\n\nstruct node {\n\tunsigned long data; \n\tstruct node *left;\n\tstruct node *right;\n};\n\nstruct root {\n\tstruct node *r;\n};\n\nvoid dump(struct node *node, int level, enum child_dir dir)\n{\n\tif (!node)\n\t\treturn;\n\n\tdump(node->right, level + 1, right_child);\n\n\tif (dir == left_child)\n\t\tprintf(\"%*s\\n\", level*3, \"|\");\n\n\tprintf(\"%*s - %05lu\\n\", level*3, \" \", node->data);\n\n\tif (dir == right_child)\n\t\tprintf(\"%*s\\n\", level*3, \"|\");\n\n\tdump(node->left, level + 1, left_child);\n}\n\nstruct node* find(struct root *root, unsigned long data)\n{\n\tstruct node* n = root->r;\n\n\twhile (n) {\n\t\tif (n->data == data)\n\t\t\treturn n;\n\t\tif (data < n->data)\n\t\t\tn = n->left;\n\t\telse\n\t\t\tn = n->right;\n\t}\n\n\treturn NULL;\n}\n\nstruct node* new_node(unsigned long data)\n{\n\tstruct node *n;\n\n\tn = malloc(sizeof(struct node));\n\n\tn->data = data;\n\tn->left = n->right = NULL;\n\treturn n;\n}\n\nvoid insert(struct root *root, struct node *new)\n{\n\tstruct node *parent;\n\n\tif (!root->r) {\n\t\troot->r = new;\n\t\treturn;\n\t}\n\n\tparent = root->r;\n\n\twhile (true) {\n\t\t/* Don't support duplicate data */\n\t\tif (new->data == parent->data)\n\t\t\tbreak;\n\n\t\tif (new->data < parent->data) {\n\t\t\tif (!parent->left) {\n\t\t\t\tparent->left = new;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tparent = parent->left;\n\t\t} else {\n\t\t\tif (!parent->right) {\n\t\t\t\tparent->right = new;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tparent = parent->right;\n\t\t}\n\t}\n}\n\nstruct node* delete(struct root *root, unsigned long data)\n{\n\tstruct node *n = root->r, **p = &root->r;\n\tstruct node *child;\n\n\twhile (n && n->data != data) {\n\t\tif (data < n->data) {\n\t\t\tp = &n->left;\n\t\t\tn = n->left;\n\t\t} else {\n\t\t\tp = &n->right;\n\t\t\tn = n->right;\n\t\t}\n\t}\n\n\tif (!n)\n\t\treturn NULL;\n\t\n\tif (n->left && n->right) {\n\t\tstruct node *rn = n->right, **rp = &n->right;\n\n\t\twhile (rn->left) {\n\t\t\trp = &rn->left;\n\t\t\trn = rn->left;\n\t\t}\n\n\t\tn->data = rn->data;\n\t\tn = rn;\n\t\tp = rp;\n\t}\n\n\tchild = n->left ? n->left : n->right;\n\t*p = child;\n\n\treturn NULL;\n}\n\nvoid insert_test()\n{\n\tstruct root tree;\n\tstruct node* n;\n\n\ttree.r = NULL;\n\n\tinsert(&tree, new_node(9));\n\n\tinsert(&tree, new_node(5));\n\tinsert(&tree, new_node(2));\n\tinsert(&tree, new_node(8));\n\n\tinsert(&tree, new_node(18));\n\tinsert(&tree, new_node(13));\n\tinsert(&tree, new_node(21));\n\tinsert(&tree, new_node(20));\n\n\tdump(tree.r, 0, root);\n\n\tn = find(&tree, 18);\n\tif (n && n->data == 18)\n\t\tprintf(\"Get 18\\n\");\n\n}\n\nvoid delete_test()\n{\n\tstruct root tree;\n\tstruct node* n;\n\n\ttree.r = NULL;\n\n\tinsert(&tree, new_node(9));\n\n\tinsert(&tree, new_node(5));\n\tinsert(&tree, new_node(2));\n\tinsert(&tree, new_node(8));\n\n\tinsert(&tree, new_node(18));\n\tinsert(&tree, new_node(13));\n\tinsert(&tree, new_node(21));\n\tinsert(&tree, new_node(20));\n\n\tdump(tree.r, 0, root);\n\n\tdelete(&tree, 20);\n\tprintf(\"Delete 20\\n\");\n\tdump(tree.r, 0, root);\n\n\tdelete(&tree, 9);\n\tprintf(\"Delete 9\\n\");\n\tdump(tree.r, 0, root);\n}\n\nint main()\n{\n\t//insert_test();\n\tdelete_test();\n\treturn 0;\n}\n"
  },
  {
    "path": "csharp/05-array/Array.cs",
    "content": "using System;\n\nnamespace algo05_array\n{\n    public sealed class Array<T> where T : IComparable<T>\n    {\n        private T[] _data;\n        private readonly int _capacity;\n        private int _length;\n\n        public Array(int capacity)\n        {\n            _data = new T[capacity];\n            _capacity = capacity;\n            _length = 0;\n        }\n\n        // length of list\n        public int Length => _length;\n\n        // insert a new element at specified index (index start from 0)\n        public void Insert(int index, T newElem)\n        {\n            if (_length == _capacity)\n            {\n                throw new OutOfMemoryException(\"List has no more space\");\n            }\n\n            if (index < 0 || index > _length)\n            {\n                throw new IndexOutOfRangeException(\"Index was outside the bounds of the list\");\n            }\n\n            // to loop array from end until finding the target index\n            for (int k = _length; k > index; k--)\n            {\n                _data[k] = _data[k - 1];\n            }\n\n            _data[index] = newElem;\n\n            _length++;\n        }\n\n        // get an element base on index\n        public T Find(int index)\n        {\n            if (index < 0 || index > _length - 1)\n            {\n                throw new IndexOutOfRangeException(\"Index was outside the bounds of the list\");\n            }\n\n            return _data[index];\n        }\n\n        // search the node which matches specified value and return its index (index start from 0)\n        public int IndexOf(T val)\n        {\n            if (_length == 0) return -1;\n            if (_data[0].Equals(val)) return 0;\n            if (_data[_length - 1].CompareTo(val) == 0) return _length - 1;\n\n            int start = 1;\n            while (start < _length - 1)\n            {\n                if (_data[start].CompareTo(val) == 0) return start;\n                start++;\n            }\n\n            return -1;\n        }\n\n        // delete an node which is on the specified index\n        public bool Delete(int index)\n        {\n            if (index < 0 || index > _length - 1)\n            {\n                throw new IndexOutOfRangeException(\"Index must be in the bound of list\");\n            }\n\n            T deletedElem = _data[index];\n            if (index < _length - 1)\n            {\n                for (int k = index; k < _length; k++)\n                {\n                    _data[k] = _data[k + 1];\n                }\n            }\n\n            _length--;\n\n            return true;\n        }\n\n        // delete an node \n        public bool Delete(T val)\n        {\n            int index;\n            for (index = 0; index < Length; index++)\n            {\n                if (_data[index].CompareTo(val) == 0) break;\n            }\n\n            if (index >= Length) return false;\n\n            return Delete(index);\n        }\n\n        // clear list\n        public void Clear()\n        {\n            _data = new T[_capacity];\n            _length = 0;\n        }\n    }\n}"
  },
  {
    "path": "csharp/05-array/algo05_array.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <TargetFramework>netcoreapp2.2</TargetFramework>\n    <IsPackable>false</IsPackable>\n  </PropertyGroup>\n\n</Project>\n"
  },
  {
    "path": "csharp/06-linkedlist/LRUWithArray.cs",
    "content": "using algo05_array;\n\nnamespace algo06_linked_list\n{\n    /// <summary>\n    /// 使用数组实现LRU缓存淘汰算法\n    /// </summary>\n    public class LRUWithArray\n    {\n        private readonly int _capacity;\n\n        public LRUWithArray(int capacity)\n        {\n            _capacity = capacity;\n            CachedList = new Array<int>(capacity);\n        }\n\n        public Array<int> CachedList { get; }\n\n        public void Set(int val)\n        {\n            // 找出该值在缓存中的索引位置\n            int idx = CachedList.IndexOf(val);\n\n            // 存在该缓存值\n            if (idx != -1)\n            {\n                CachedList.Delete(idx);\n                CachedList.Insert(0, val);\n                return;\n            }\n\n            // 不存在该缓存值\n            if (CachedList.Length == _capacity)\n            {\n                // 缓存已满，删除最后一个元素\n                CachedList.Delete(CachedList.Length - 1);\n            }\n\n            // 将新缓存插入到表头\n            CachedList.Insert(0, val);\n        }\n    }\n}"
  },
  {
    "path": "csharp/06-linkedlist/LRUWithLinkedList.cs",
    "content": "namespace algo06_linked_list\n{\n    /// <summary>\n    /// 使用单链表实现LRU缓存淘汰算法\n    /// </summary>\n    public class LRUWithLinkedList\n    {\n        private readonly int _capacity;\n\n        /// <summary>\n        /// 构造函数\n        /// </summary>\n        /// <param name=\"capacity\">缓存容量</param>\n        public LRUWithLinkedList(int capacity = 10)\n        {\n            _capacity = capacity;\n        }\n\n        public SingleLinkedList<int> CachedList { get; } = new SingleLinkedList<int>();\n\n        /// <summary>\n        /// 存储缓存数据\n        /// </summary>\n        /// <param name=\"val\"></param>\n        public void Set(int val)\n        {\n            // 尝试删除匹配到和给定值相等的结点，并返回\n            var deletedNode = CachedList.Delete(value: val);\n\n            // 数据在缓存中存在，从原位置删除，然后插入到表头\n            if (deletedNode != null)\n            {\n                CachedList.Insert(1, val);\n                return;\n            }\n\n            // 数据不存在\n            if (CachedList.Length == _capacity)\n            {\n                // 链表已满，删除尾结点，将新数据插入到头部\n                CachedList.Delete(CachedList.Length);\n            }\n\n            // 将新缓存值插入到表头\n            CachedList.Insert(1, val);\n        }\n    }\n}"
  },
  {
    "path": "csharp/06-linkedlist/LRU缓存实现思路.txt",
    "content": "实现LRU缓存淘汰算法思路：\n\n维护一个有序单链表，越靠近链尾的数据是最早访问的。\n当有一个新的数据被访问时，\n1. 如果数据在缓存中，则将其从原位置删除，然后插入到表头；\n2. 如果数据不在缓存中，有两种情况：\n    1) 链表未满，则将数据插入到表头；\n    2) 链表已满，则删除尾结点，将新数据插入到表头。\n"
  },
  {
    "path": "csharp/06-linkedlist/SingleLinkedList.cs",
    "content": "using System;\n\nnamespace algo06_linked_list\n{\n    /// <summary>\n    /// 单链表的插入、删除、清空、查找\n    /// </summary>\n    /// <typeparam name=\"T\"></typeparam>\n    public class SingleLinkedList<T> where T : IComparable<T>\n    {\n        public SingleLinkedList()\n        {\n            Head = new ListNode<T>(default(T));\n        }\n\n        public SingleLinkedList(params T[] list)\n        {\n            Head = new ListNode<T>(default(T));\n            if (list == null) return;\n\n            var p = Head;\n            foreach (var item in list)\n            {\n                var q = new ListNode<T>(item);\n                p.Next = q;\n                p = q;\n            }\n\n            Length = list.Length;\n        }\n\n        // Head node\n        public ListNode<T> First => Head.Next;\n        public ListNode<T> Head { get; }\n\n        public int Length { get; private set; }\n\n        public ListNode<T> Insert(int position, T newElem)\n        {\n            if (position < 1 || position > Length + 1)\n            {\n                throw new IndexOutOfRangeException(\"Position must be in bound of list\");\n            }\n\n            var p = Head;\n\n            int j = 1;\n            while (p != null && j < position)\n            {\n                p = p.Next;\n                ++j;\n            }\n\n            var newNode = new ListNode<T>(newElem);\n            newNode.Next = p.Next;\n            p.Next = newNode;\n\n            Length++;\n\n            return newNode;\n        }\n\n        public ListNode<T> Find(int position)\n        {\n            ListNode<T> p = First;\n            int j = 1;\n\n            while (p != null && j < position)\n            {\n                p = p.Next;\n                j++;\n            }\n\n            if (p == null || j > position)\n            {\n                return null;\n            }\n\n            return p;\n        }\n\n        public ListNode<T> Find(T elem)\n        {\n            ListNode<T> p = Head.Next;\n\n            while (p != null)\n            {\n                if (p.Value.CompareTo(elem) == 0) return p;\n\n                p = p.Next;\n            }\n\n            return null;\n        }\n\n        public ListNode<T> Delete(T value)\n        {\n            ListNode<T> cur = Head;\n            while (cur.Next != null && cur.Next.Value.CompareTo(value) != 0)\n            {\n                cur = cur.Next;\n            }\n\n            if (cur.Next == null) return null;\n\n            var q = cur.Next;\n            cur.Next = q.Next;\n\n            Length--;\n\n            return q;\n        }\n\n        public ListNode<T> Delete(int position)\n        {\n            if (position < 1 || position > Length)\n            {\n                return null;\n            }\n\n            var p = First;\n            int j = 1;\n            while (p != null && j < position - 1)\n            {\n                p = p.Next;\n                ++j;\n            }\n\n            var q = p.Next;\n            p.Next = q.Next;\n\n            Length--;\n\n            return q;\n        }\n\n        public void Clear()\n        {\n            var cur = Head;\n            while (cur.Next != null)\n            {\n                var q = cur.Next;\n                cur.Next = null;\n\n                cur = q;\n            }\n\n            Length = 0;\n        }\n    }\n\n    public class ListNode<T>\n    {\n        public ListNode(T value)\n        {\n            Value = value;\n        }\n\n        public T Value { get; }\n\n        public ListNode<T> Next { get; set; }\n    }\n}"
  },
  {
    "path": "csharp/06-linkedlist/algo06_linked_list.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <TargetFramework>netcoreapp2.2</TargetFramework>\n\n    <IsPackable>false</IsPackable>\n  </PropertyGroup>\n\n  <ItemGroup>\n    <ProjectReference Include=\"..\\05-array\\algo05_array.csproj\" />\n  </ItemGroup>\n\n</Project>\n"
  },
  {
    "path": "csharp/07-linkedlist/_07_linkedlist/SingleLinkedListAlgo.cs",
    "content": "﻿using System;\nusing algo06_linked_list;\n\nnamespace algo07_linkedlist\n{\n    /// <summary>\n    /// 单链表常用算法操作\n    /// 1. 链表反转\n    /// 2. 环的检测\n    /// 3. 两个有序链表的合并\n    /// 4. 删除链表倒数第n个结点\n    /// 5. 求链表的中间结点\n    /// </summary>\n    /// <typeparam name=\"T\"></typeparam>\n    public class SingleLinkedListAlgo<T> : SingleLinkedList<T> where T : IComparable<T>\n    {\n        public SingleLinkedListAlgo(params T[] list) : base(list)\n        {\n        }\n\n        /// <summary>\n        /// 链表反转\n        /// </summary>\n        public void Reverse()\n        {\n            if (Length <= 1) return;\n\n            ListNode<T> p = First;\n            ListNode<T> q = First.Next;\n\n            ListNode<T> r = null;\n\n            p.Next = null;\n            while (q != null)\n            {\n                r = q.Next;\n\n                q.Next = p;\n                p = q;\n                q = r;\n            }\n\n            Head.Next = p;\n        }\n\n        /// <summary>\n        /// 环的检测\n        /// </summary>\n        /// <remarks>\n        /// 用快慢两个指针，快指针每次移动2个结点，慢指针每次移动1个结点，当两个指针相遇时，说明存在环。\n        /// LeetCode 编号： 141\n        /// </remarks>\n        public bool HasCycle()\n        {\n            if (Length == 0) return false;\n\n            var slow = Head.Next;\n            var fast = Head.Next.Next;\n\n            while (fast != null && slow != null && fast != slow)\n            {\n                fast = fast.Next?.Next;\n                slow = slow.Next;\n            }\n\n            bool ret = fast == slow;\n            return ret;\n        }\n\n        /// <summary>\n        /// 合并两个有序链表(从小到大)\n        /// </summary>\n        /// <remarks>LeetCode 编号： 21</remarks>\n        /// <param name=\"listAlgo\"></param>\n        /// <returns></returns>\n        public SingleLinkedListAlgo<T> Merge(SingleLinkedListAlgo<T> listAlgo)\n        {\n            if (listAlgo == null) return null;\n\n            var root = new SingleLinkedListAlgo<T>();\n\n            ListNode<T> pointer = root.Head; // 总是向新链表的尾结点\n\n            var head1 = listAlgo.First;\n            var head2 = this.First;\n\n            while (head1 != null && head2 != null)\n            {\n                if (head1.Value.CompareTo(head2.Value) < 0)\n                {\n                    pointer.Next = head1;\n                    head1 = head1.Next;\n                }\n                else\n                {\n                    pointer.Next = head2;\n                    head2 = head2.Next;\n                }\n\n                pointer = pointer.Next; // 指向尾结点\n            }\n\n            if (head1 != null)\n            {\n                pointer.Next = head1;\n            }\n\n            if (head2 != null)\n            {\n                pointer.Next = head2;\n            }\n\n            return root;\n        }\n\n        /// <summary>\n        /// 删除倒数第n个结点\n        /// </summary>\n        /// <remarks>\n        /// 用快慢两个指针，快指针比慢指针早n个结点，然后再同步移动两个指针，当快指针指向尾结点时，慢指针就是将要删除的结点\n        /// LeetCode 编号： 19\n        /// </remarks>\n        /// <param name=\"n\"></param>\n        public void RemoveNthNodeFromEnd(int n)\n        {\n            if (n < 1 || n > Length) return;\n\n            ListNode<T> preNode = Head;\n            ListNode<T> curNode = Head;\n\n            for (int i = 0; i < n; i++)\n            {\n                curNode = curNode.Next;\n            }\n\n            if (curNode == null) return;\n\n            while (curNode.Next != null)\n            {\n                preNode = preNode.Next;\n                curNode = curNode.Next;\n            }\n\n            preNode.Next = preNode.Next.Next;\n        }\n\n        /// <summary>\n        /// 链表的中间结点\n        /// </summary>\n        /// <remarks>\n        /// 思路： 利用快慢指针，快指针步长2，慢指针步长1，当快指针到达尾结点时，慢指针正好到达中间结点\n        /// LeetCode 编号： 876\n        /// </remarks>\n        /// <returns></returns>\n        public ListNode<T> FindMiddleNode()\n        {\n            if (First?.Next == null) return null;\n\n            ListNode<T> slowPointer = First;\n            ListNode<T> fastPointer = First.Next;\n\n            while (fastPointer.Next?.Next != null)\n            {\n                fastPointer = fastPointer.Next.Next;\n                slowPointer = slowPointer.Next;\n            }\n\n            slowPointer = slowPointer.Next;\n            return slowPointer;\n        }\n    }\n}"
  },
  {
    "path": "csharp/07-linkedlist/_07_linkedlist/algo07_linkedlist.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\n\n    <PropertyGroup>\n        <TargetFramework>netcoreapp2.2</TargetFramework>\n    </PropertyGroup>\n\n    <ItemGroup>\n      <ProjectReference Include=\"..\\..\\06-linkedlist\\algo06_linked_list.csproj\" />\n    </ItemGroup>\n\n</Project>\n"
  },
  {
    "path": "csharp/08-stack/algo08_stack/ArrayStack.cs",
    "content": "﻿using System;\n\nnamespace algo08_stack\n{\n    public class ArrayStack<T>\n    {\n        private readonly int _capacity;\n\n        private readonly T[] _data;\n\n        private int _top = -1; // 指向栈顶元素，当为-1时表示栈为空\n\n        public ArrayStack(int capacity)\n        {\n            _capacity = capacity;\n\n            _data = new T[capacity];\n        }\n\n        public int Count => _top + 1;\n\n        public void Push(T val)\n        {\n            if (Count == _capacity) throw new InvalidOperationException(\"Stack full.\");\n\n            _top++;\n\n            _data[_top] = val;\n        }\n\n        public T Pop()\n        {\n            if (_top == -1) throw new InvalidOperationException(\"Stack empty.\");\n\n            T val = _data[_top];\n            _top--;\n\n            return val;\n        }\n    }\n}"
  },
  {
    "path": "csharp/08-stack/algo08_stack/LinkedStack.cs",
    "content": "﻿using System;\n\nnamespace algo08_stack\n{\n    public class LinkedStack<T>\n    {\n        private StackListNode<T> _top;\n\n        public int Count { get; private set; }\n\n        public void Push(T val)\n        {\n            var newNode = new StackListNode<T>(val);\n            newNode.Next = _top;\n            _top = newNode;\n\n            Count++;\n        }\n\n        public T Pop()\n        {\n            if (_top == null) throw new InvalidOperationException(\"Stack empty\");\n\n            T val = _top.Value;\n            _top = _top.Next;\n\n            Count--;\n\n            return val;\n        }\n\n        public void Clear()\n        {\n            while (Count > 0)\n            {\n                Pop();\n            }\n        }\n    }\n\n    public class StackListNode<T>\n    {\n        public StackListNode(T nodeValue)\n        {\n            Value = nodeValue;\n        }\n\n        public T Value { get; set; }\n        public StackListNode<T> Next { get; set; }\n    }\n}"
  },
  {
    "path": "csharp/08-stack/algo08_stack/LinkedStackBrowser.cs",
    "content": "namespace algo08_stack\n{\n    /// <summary>\n    /// 利用链栈实现浏览器怎么进后退\n    /// </summary>\n    public class LinkedStackBrowser\n    {\n        private readonly LinkedStack<string> _backStack = new LinkedStack<string>();\n        private readonly LinkedStack<string> _forwardStack = new LinkedStack<string>();\n\n        public void Open(string url)\n        {\n            _backStack.Push(url);\n\n            _forwardStack.Clear();\n        }\n\n        public string Backward()\n        {\n            if (_backStack.Count == 0) return string.Empty;\n\n            string url = _backStack.Pop();\n\n            _forwardStack.Push(url);\n\n            return url;\n        }\n\n        public string Forward()\n        {\n            if (_forwardStack.Count == 0) return string.Empty;\n\n            string url = _forwardStack.Pop();\n            _backStack.Push(url);\n\n            return url;\n        }\n    }\n}"
  },
  {
    "path": "csharp/08-stack/algo08_stack/algo08_stack.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\n\n    <PropertyGroup>\n        <TargetFramework>netcoreapp2.2</TargetFramework>\n    </PropertyGroup>\n\n</Project>\n"
  },
  {
    "path": "csharp/Tests/_05_array_tests/Array.Tests.cs",
    "content": "using System;\nusing algo05_array;\nusing Xunit;\nusing Xunit.Abstractions;\n\nnamespace _05_array_tests\n{\n    public class ArrayTests\n    {\n        private readonly Array<int> _sqList;\n        private readonly ITestOutputHelper _output;\n\n        public ArrayTests(ITestOutputHelper output)\n        {\n            _sqList = new Array<int>(10);\n            _output = output;\n        }\n\n        private void PrintList()\n        {\n            for (int idx = 0; idx < _sqList.Length; idx++)\n            {\n                var elem = _sqList.Find(idx);\n                _output.WriteLine(elem.ToString());\n            }\n        }\n\n        [Fact]\n        public void Length_Equal_1_After_InsertOneElement()\n        {\n            _sqList.Insert(0, 1);\n            Assert.True(_sqList.Length == 1);\n        }\n\n        [Fact]\n        public void Insert_ThrowIndexOutOfRangeException_When_Index_GreaterThan_Length()\n        {\n            _sqList.Insert(0, 1);\n            Exception ex = Assert.Throws<IndexOutOfRangeException>(() => _sqList.Insert(3, 2));\n            Assert.IsType<IndexOutOfRangeException>(ex);\n        }\n\n        [Fact]\n        public void Insert_ThrowIndexOutOfRangeException_When_Index_LessThan_Zero()\n        {\n            Exception ex = Assert.Throws<IndexOutOfRangeException>(() => _sqList.Insert(-1, 1));\n            Assert.IsType<IndexOutOfRangeException>(ex);\n        }\n\n        [Fact]\n        public void Insert_ThrowIndexOutOfRangeException_When_List_Is_Full()\n        {\n            _sqList.Insert(0, 11);\n            _sqList.Insert(1, 10);\n            _sqList.Insert(2, 9);\n            _sqList.Insert(3, 8);\n            _sqList.Insert(4, 7);\n            _sqList.Insert(5, 6);\n            _sqList.Insert(6, 5);\n            _sqList.Insert(7, 4);\n            _sqList.Insert(8, 3);\n            _sqList.Insert(9, 2);\n\n            PrintList();\n\n            Exception ex = Assert.Throws<OutOfMemoryException>(() => _sqList.Insert(10, 101));\n            Assert.IsType<OutOfMemoryException>(ex);\n        }\n\n        [Fact]\n        public void Delete_ThrowIndexOutOfRangeException_When_Index_LessThan_Zero()\n        {\n            Exception ex = Assert.Throws<IndexOutOfRangeException>(() => _sqList.Delete(-1));\n            Assert.IsType<IndexOutOfRangeException>(ex);\n        }\n\n        [Fact]\n        public void Delete_ThrowIndexOutOfRangeException_When_Index_GreaterThan_Length()\n        {\n            _sqList.Insert(0, 11);\n            _sqList.Insert(1, 22);\n            Exception ex = Assert.Throws<IndexOutOfRangeException>(() => _sqList.Delete(3));\n            Assert.IsType<IndexOutOfRangeException>(ex);\n        }\n\n        [Fact]\n        public void Delete_First_Element_Success()\n        {\n            _sqList.Insert(0, 100);\n            _sqList.Insert(1, 11);\n            _sqList.Insert(2, 22);\n\n            bool ret = _sqList.Delete(1);\n            Assert.True(ret);\n        }\n\n        [Fact]\n        public void Delete_Last_Element_Success()\n        {\n            _sqList.Insert(0, 100);\n            _sqList.Insert(1, 11);\n            _sqList.Insert(2, 22);\n            _sqList.Insert(3, 33);\n\n            bool ret = _sqList.Delete(3);\n            Assert.True(ret);\n        }\n\n        [Fact]\n        public void Delete_Middle_Element()\n        {\n            _sqList.Insert(0, 100);\n            _sqList.Insert(1, 11);\n            _sqList.Insert(2, 22);\n            _sqList.Insert(3, 33);\n\n            bool ret = _sqList.Delete(2);\n            Assert.True(ret);\n        }\n\n        [Fact]\n        public void Find_ThrowsIndexOutOfRangeException_When_Index_LessThan_Zero()\n        {\n            _sqList.Insert(0, 100);\n            _sqList.Insert(1, 11);\n            _sqList.Insert(2, 22);\n            _sqList.Insert(3, 33);\n\n            Exception ex = Assert.Throws<IndexOutOfRangeException>(() => _sqList.Find(-1));\n            Assert.IsType<IndexOutOfRangeException>(ex);\n        }\n\n        [Fact]\n        public void Find_ThrowsIndexOutOfRangeException_When_Index_GreaterThan_Length()\n        {\n            _sqList.Insert(0, 100);\n            _sqList.Insert(1, 11);\n            _sqList.Insert(2, 22);\n            _sqList.Insert(3, 33);\n\n            Exception ex = Assert.Throws<IndexOutOfRangeException>(() => _sqList.Find(4));\n            Assert.IsType<IndexOutOfRangeException>(ex);\n        }\n\n        [Fact]\n        public void Find_Last_Position_Return_33()\n        {\n            _sqList.Insert(0, 100);\n            _sqList.Insert(1, 11);\n            _sqList.Insert(2, 22);\n            _sqList.Insert(3, 33);\n\n            var elem = _sqList.Find(3);\n\n            Assert.Equal(33, elem);\n        }\n\n        [Fact]\n        public void Find_First_Element_Return_11()\n        {\n            _sqList.Insert(0, 100);\n            _sqList.Insert(1, 11);\n            _sqList.Insert(2, 22);\n            _sqList.Insert(3, 33);\n\n            var elem = _sqList.Find(1);\n\n            Assert.Equal(11, elem);\n        }\n\n        [Fact]\n        public void IndexOf_Return_Negative_1_When_Element_Not_Exist()\n        {\n            _sqList.Insert(0, 100);\n            _sqList.Insert(1, 11);\n            _sqList.Insert(2, 22);\n            _sqList.Insert(3, 33);\n\n            var elem = _sqList.IndexOf(55);\n\n            Assert.Equal(-1, elem);\n        }\n\n        [Fact]\n        public void IndexOf_Return_First_Index()\n        {\n            _sqList.Insert(0, 100);\n            _sqList.Insert(1, 11);\n            _sqList.Insert(2, 22);\n            _sqList.Insert(3, 33);\n\n            var elem = _sqList.IndexOf(100);\n\n            Assert.Equal(0, elem);\n        }\n\n        [Fact]\n        public void IndexOf_Return_Last_Index()\n        {\n            _sqList.Insert(0, 100);\n            _sqList.Insert(1, 11);\n            _sqList.Insert(2, 22);\n            _sqList.Insert(3, 33);\n\n            var elem = _sqList.IndexOf(33);\n\n            Assert.Equal(3, elem);\n        }\n\n        [Fact]\n        public void Clear_Then_Length_Equal_Zero()\n        {\n            _sqList.Insert(0, 100);\n            _sqList.Insert(1, 10);\n            _sqList.Insert(2, 9);\n            _sqList.Insert(3, 8);\n            _sqList.Insert(4, 7);\n            _sqList.Insert(5, 6);\n            _sqList.Insert(6, 5);\n            _sqList.Insert(7, 4);\n            _sqList.Insert(8, 3);\n            _sqList.Insert(9, 2);\n\n            Assert.Equal(10, _sqList.Length);\n\n            _sqList.Clear();\n\n            Assert.Equal(0, _sqList.Length);\n        }\n    }\n}"
  },
  {
    "path": "csharp/Tests/_05_array_tests/algo05_array_tests.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <TargetFramework>netcoreapp2.2</TargetFramework>\n\n    <IsPackable>false</IsPackable>\n\n    <AssemblyName>05_array_tests</AssemblyName>\n  </PropertyGroup>\n\n  <ItemGroup>\n    <PackageReference Include=\"Microsoft.NET.Test.Sdk\" Version=\"15.9.0\" />\n    <PackageReference Include=\"xunit\" Version=\"2.4.0\" />\n    <PackageReference Include=\"xunit.runner.visualstudio\" Version=\"2.4.0\" />\n  </ItemGroup>\n\n  <ItemGroup>\n    <ProjectReference Include=\"..\\..\\05-array\\algo05_array.csproj\" />\n  </ItemGroup>\n\n</Project>\n"
  },
  {
    "path": "csharp/Tests/_06_linkedlist_tests/BaseLinkedListTests.cs",
    "content": "using System;\nusing algo06_linked_list;\n\nnamespace algo06_linkedlist_tests\n{\n    public class BaseLinkedListTests\n    {\n        protected void PrintLinkedList<T> (SingleLinkedList<T> list) where T : IComparable<T>\n        {\n            if (list == null) return;\n\n            var p = list.First;\n            while (p != null)\n            {\n                System.Console.WriteLine (p.Value);\n                p = p.Next;\n            }\n        }\n    }\n}"
  },
  {
    "path": "csharp/Tests/_06_linkedlist_tests/LRUWithArray.Tests.cs",
    "content": "using algo05_array;\nusing algo06_linked_list;\nusing Xunit;\nusing Xunit.Abstractions;\n\nnamespace algo06_linkedlist_tests\n{\n    public class LRUWithArrayTests\n    {\n        private ITestOutputHelper _output;\n\n        public LRUWithArrayTests(ITestOutputHelper output)\n        {\n            _output = output;\n        }\n\n        private void PrintList(Array<int> list)\n        {\n            if (list == null) return;\n            for (int idx = 0; idx < list.Length; idx++)\n            {\n                _output.WriteLine(list.Find(idx).ToString());\n            }\n        }\n\n        [Fact]\n        public void LRU_Set_Value_When_Not_Existed()\n        {\n            var lru = new LRUWithArray(5);\n\n            lru.Set(1);\n            lru.Set(3);\n            lru.Set(5);\n            lru.Set(7);\n            lru.Set(9);\n\n            var list = lru.CachedList;\n\n            PrintList(list);\n\n            Assert.Equal(9, list.Find(0));\n        }\n\n\n        [Fact]\n        public void LRU_Set_Value_When_Existed()\n        {\n            var lru = new LRUWithArray(5);\n\n            lru.Set(1);\n            lru.Set(3);\n            lru.Set(5);\n            lru.Set(7);\n            lru.Set(3);\n\n            var list = lru.CachedList;\n\n            PrintList(list);\n\n            Assert.Equal(3, list.Find(0));\n        }\n\n        [Fact]\n        public void LRU_Set_Value_When_Full()\n        {\n            var lru = new LRUWithArray(5);\n\n            lru.Set(1);\n            lru.Set(3);\n            lru.Set(5);\n            lru.Set(7);\n            lru.Set(9);\n            lru.Set(8);\n\n            var list = lru.CachedList;\n\n            PrintList(list);\n\n            Assert.Equal(8, list.Find(0));\n        }\n    }\n}"
  },
  {
    "path": "csharp/Tests/_06_linkedlist_tests/LRUWithLinkedList.Tests.cs",
    "content": "using Xunit;\nusing Xunit.Abstractions;\nusing algo06_linked_list;\n\nnamespace algo06_linkedlist_tests\n{\n    public class LRUWithLinkedListTests : BaseLinkedListTests\n    {\n        [Fact]\n        public void LRU_Set_Value_When_Not_Existed()\n        {\n            var lru = new LRUWithLinkedList();\n\n            lru.Set(1);\n            lru.Set(3);\n            lru.Set(5);\n            lru.Set(7);\n            lru.Set(9);\n\n            var list = lru.CachedList;\n\n            PrintLinkedList(list);\n\n            Assert.Equal(9, list.First.Value);\n        }\n\n        [Fact]\n        public void LRU_Set_Value_When_Existed()\n        {\n            var lru = new LRUWithLinkedList();\n\n            lru.Set(1);\n            lru.Set(3);\n            lru.Set(5);\n            lru.Set(7);\n            lru.Set(3);\n\n            var list = lru.CachedList;\n\n            PrintLinkedList(list);\n\n            Assert.Equal(3, list.First.Value);\n        }\n\n        [Fact]\n        public void LRU_Set_Value_When_Full()\n        {\n            var lru = new LRUWithLinkedList(5);\n\n            lru.Set(1);\n            lru.Set(3);\n            lru.Set(5);\n            lru.Set(7);\n            lru.Set(9);\n            lru.Set(8);\n\n            var list = lru.CachedList;\n\n            PrintLinkedList(list);\n\n            Assert.Equal(8, list.First.Value);\n        }\n    }\n}"
  },
  {
    "path": "csharp/Tests/_06_linkedlist_tests/SingleLinkedList.Tests.cs",
    "content": "using System;\nusing Xunit;\nusing Xunit.Abstractions;\nusing algo06_linked_list;\n\nnamespace algo06_linkedlist_tests\n{\n    public class SingleLinkedListTests : BaseLinkedListTests\n    {\n        [Fact]\n        public void Insert_3_Elements_Return_Length_3()\n        {\n            var list = new SingleLinkedList<string>(\"The\", \"Quick\", \"Brown\");\n\n            PrintLinkedList(list);\n\n            Assert.Equal(3, list.Length);\n        }\n\n        [Fact]\n        public void Insert_Some_Elements_Then_Verify_First()\n        {\n            var list = new SingleLinkedList<string>(\"The\", \"Quick\", \"Brown\");\n\n            Assert.Equal(\"The\", list.First.Value);\n        }\n\n        [Fact]\n        public void Insert_Some_Elements_Then_Verify_Last()\n        {\n            var list = new SingleLinkedList<string>(\"The\", \"Quick\", \"Brown\");\n\n            Assert.Equal(\"Brown\", list.First.Next.Next.Value);\n        }\n\n        [Fact]\n        public void Find_Return_Null_When_Position_LessThan_1()\n        {\n            var list = new SingleLinkedList<string>(\"The\", \"Quick\", \"Brown\");\n\n            var node = list.Find(0);\n            Assert.Null(node);\n        }\n\n        [Fact]\n        public void Find_Return_Null_When_Position_GreaterThan_Length()\n        {\n            var list = new SingleLinkedList<string>(\"The\", \"Quick\", \"Brown\");\n\n            var node = list.Find(4);\n            Assert.Null(node);\n        }\n\n        [Fact]\n        public void Find_Return_Correct_When_Position_Valid()\n        {\n            var list = new SingleLinkedList<string>(\"The\", \"Quick\", \"Brown\");\n\n            var node = list.Find(2);\n            Assert.Equal(\"Quick\", node.Value);\n        }\n\n        [Fact]\n        public void Delete_Return_Null_When_Position_LessThan_1()\n        {\n            var list = new SingleLinkedList<string>(\"The\", \"Quick\", \"Brown\");\n\n            var node = list.Delete(0);\n            Assert.Null(node);\n        }\n\n        [Fact]\n        public void Delete_Return_Null_When_Position_GreaterThan_Length()\n        {\n            var list = new SingleLinkedList<string>(\"The\", \"Quick\", \"Brown\");\n\n            var node = list.Delete(4);\n            Assert.Null(node);\n        }\n\n        [Fact]\n        public void Delete_By_Value_Success_When_Element_Exist()\n        {\n            var list = new SingleLinkedList<string>(\"The\", \"Quick\", \"Brown\", \"Fox\", \"jumps\", \"over\", \"the\", \"lazy\",\n                \"dog\");\n\n            var deletedNode = list.Delete(\"over\");\n\n            PrintLinkedList(list);\n\n            Assert.Equal(\"over\", deletedNode.Value);\n            Assert.Equal(8, list.Length);\n        }\n\n        [Fact]\n        public void Delete_By_Value_Success_When_Element_Not_Exist()\n        {\n            var list = new SingleLinkedList<string>(\"The\", \"Quick\", \"Brown\", \"Fox\", \"jumps\", \"over\", \"the\", \"lazy\",\n                \"dog\");\n\n            var deletedNode = list.Delete(\"hello\");\n\n            PrintLinkedList(list);\n\n            Assert.Null(deletedNode);\n            Assert.Equal(9, list.Length);\n        }\n\n        [Fact]\n        public void Delete_By_Value_Success_When_Delete_First()\n        {\n            var list = new SingleLinkedList<string>(\"The\", \"Quick\", \"Brown\", \"Fox\", \"jumps\", \"over\", \"the\", \"lazy\",\n                \"dog\");\n\n            var deletedNode = list.Delete(\"The\");\n\n            PrintLinkedList(list);\n\n            Assert.Equal(\"The\", deletedNode.Value);\n            Assert.Equal(8, list.Length);\n        }\n\n        [Fact]\n        public void Delete_By_Value_Success_When_Delete_Last()\n        {\n            var list = new SingleLinkedList<string>(\"The\", \"Quick\", \"Brown\", \"Fox\", \"jumps\", \"over\", \"the\", \"lazy\",\n                \"dog\");\n\n            var deletedNode = list.Delete(\"dog\");\n\n            PrintLinkedList(list);\n\n            Assert.Equal(\"dog\", deletedNode.Value);\n            Assert.Equal(8, list.Length);\n        }\n\n        [Fact]\n        public void Delete_Success_When_Position_Valid()\n        {\n            var list = new SingleLinkedList<string>(\"The\", \"Quick\", \"Brown\", \"Fox\", \"jumps\", \"over\", \"the\", \"lazy\",\n                \"dog\");\n\n            var node = list.Delete(3);\n\n            PrintLinkedList(list);\n\n            Assert.Equal(\"Brown\", node.Value);\n            Assert.Equal(8, list.Length);\n        }\n\n        [Fact]\n        public void Clear_Length_Equal_0()\n        {\n            var list = new SingleLinkedList<string>(\"The\", \"Quick\", \"Brown\");\n\n            list.Clear();\n\n            Assert.Equal(0, list.Length);\n        }\n\n        [Fact]\n        public void Clear_First_Is_Null()\n        {\n            var list = new SingleLinkedList<string>(\"The\", \"Quick\", \"Brown\");\n\n            list.Clear();\n\n            Assert.Null(list.First);\n        }\n\n    }\n}"
  },
  {
    "path": "csharp/Tests/_06_linkedlist_tests/algo06_linkedlist_tests.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <TargetFramework>netcoreapp2.2</TargetFramework>\n\n    <IsPackable>false</IsPackable>\n  </PropertyGroup>\n\n  <ItemGroup>\n    <PackageReference Include=\"Microsoft.NET.Test.Sdk\" Version=\"15.9.0\" />\n    <PackageReference Include=\"xunit\" Version=\"2.4.0\" />\n    <PackageReference Include=\"xunit.runner.visualstudio\" Version=\"2.4.0\" />\n  </ItemGroup>\n\n  <ItemGroup>\n    <ProjectReference Include=\"..\\..\\05-array\\algo05_array.csproj\" />\n    <ProjectReference Include=\"..\\..\\06-linkedlist\\algo06_linked_list.csproj\" />\n  </ItemGroup>\n\n</Project>\n"
  },
  {
    "path": "csharp/Tests/_07_linkedlist_tests/SingleLinkedListAlgo.Tests.cs",
    "content": "using System;\nusing Xunit;\nusing algo06_linkedlist_tests;\nusing algo06_linked_list;\nusing algo07_linkedlist;\n\nnamespace algo07_linkedlist_tests\n{\n    public class SingleLinkedListAlgoTests : BaseLinkedListTests\n    {\n        [Fact]\n        public void Reverse_When_List_Is_Empty()\n        {\n            var list = new SingleLinkedListAlgo<string>();\n\n            list.Reverse();\n\n            PrintLinkedList(list);\n\n            Assert.Null(list.First);\n        }\n\n        [Fact]\n        public void Reverse_When_List_Has_Many_Elements()\n        {\n            var list = new SingleLinkedListAlgo<string>(\"The\", \"Quick\", \"Brown\", \"Fox\", \"jumps\", \"over\", \"the\", \"lazy\",\n                \"dog\");\n\n            list.Reverse();\n\n            PrintLinkedList(list);\n\n            Assert.True(list.First.Value == \"dog\");\n        }\n\n        [Fact]\n        public void HasCycle_List_Empty()\n        {\n            var list = new SingleLinkedListAlgo<string>(\"The\", \"Quick\", \"Brown\", \"Fox\", \"jumps\", \"over\", \"the\", \"lazy\",\n                \"dog\");\n\n            bool hasCycle = list.HasCycle();\n\n            Assert.False(hasCycle);\n        }\n\n        [Fact]\n        public void HasCycle_False_When_List_Length_1()\n        {\n            var list = new SingleLinkedListAlgo<string>(\"The\");\n\n            bool hasCycle = list.HasCycle();\n\n            Assert.False(hasCycle);\n        }\n\n        [Fact]\n        public void HasCycle_False_When_List_Length_2()\n        {\n            var list = new SingleLinkedListAlgo<string>(\"The\", \"Quick\");\n\n            bool hasCycle = list.HasCycle();\n\n            Assert.False(hasCycle);\n        }\n\n        [Fact]\n        public void HasCycle_True_When_List_Length_2()\n        {\n            var list = new SingleLinkedListAlgo<string>();\n\n            var firstNode = list.Insert(1, \"The\");\n            var secondNode = list.Insert(2, \"Quick\");\n\n            secondNode.Next = firstNode;\n\n            bool hasCycle = list.HasCycle();\n\n            Assert.True(hasCycle);\n        }\n\n        [Fact]\n        public void HasCycle_False()\n        {\n            var linkList =\n                new SingleLinkedListAlgo<string>(\"The\", \"Quick\", \"Brown\", \"fox\", \"jumps\", \"over\", \"the\", \"lazy\", \"dog\");\n\n            bool hasCycle = linkList.HasCycle();\n\n            Assert.False(hasCycle);\n        }\n\n        [Fact]\n        public void HasCycle_True()\n        {\n            var list = new SingleLinkedListAlgo<string>();\n\n            // 初始化一个具有环的链表\n            list.Insert(1, \"The\");\n            list.Insert(2, \"Quick\");\n            list.Insert(3, \"Brown\");\n            var fourthNode = list.Insert(4, \"fox\");\n            list.Insert(5, \"jumps\");\n            list.Insert(6, \"over\");\n            list.Insert(7, \"the\");\n            list.Insert(8, \"lazy\");\n            var last = list.Insert(9, \"dog\");\n\n            last.Next = fourthNode;\n\n            bool hasCycle = list.HasCycle();\n\n            Assert.True(hasCycle);\n        }\n\n        [Fact]\n        public void Merge()\n        {\n            var list1 = new SingleLinkedListAlgo<int>(1, 2, 4);\n            var list2 = new SingleLinkedListAlgo<int>(1, 3, 4);\n\n            var list3 = list1.Merge(list2);\n\n            PrintLinkedList(list3);\n\n            Assert.True(list1.First.Next.Next.Value == 2);\n        }\n\n        [Fact]\n        public void Remove_2th_Node_From_End()\n        {\n            var list = new SingleLinkedListAlgo<int>(1, 2, 3, 4, 5);\n            list.RemoveNthNodeFromEnd(2);\n\n            PrintLinkedList(list);\n\n            Assert.True(list.First.Next.Next.Next.Value == 5);\n        }\n\n        [Fact]\n        public void FindMiddleNode()\n        {\n            var list = new SingleLinkedListAlgo<int>(1, 2, 3, 4, 5);\n\n            ListNode<int> middleNode = list.FindMiddleNode();\n\n            Assert.True(middleNode.Value == 3);\n        }\n    }\n}"
  },
  {
    "path": "csharp/Tests/_07_linkedlist_tests/algo07_linkedlist_tests.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\n\n    <PropertyGroup>\n        <TargetFramework>netcoreapp2.2</TargetFramework>\n\n        <IsPackable>false</IsPackable>\n    </PropertyGroup>\n\n    <ItemGroup>\n        <PackageReference Include=\"Microsoft.NET.Test.Sdk\" Version=\"15.9.0\" />\n        <PackageReference Include=\"xunit\" Version=\"2.4.0\" />\n        <PackageReference Include=\"xunit.runner.visualstudio\" Version=\"2.4.0\" />\n    </ItemGroup>\n\n    <ItemGroup>\n      <ProjectReference Include=\"..\\..\\06-linkedlist\\algo06_linked_list.csproj\" />\n      <ProjectReference Include=\"..\\..\\07-linkedlist\\_07_linkedlist\\algo07_linkedlist.csproj\" />\n      <ProjectReference Include=\"..\\_06_linkedlist_tests\\algo06_linkedlist_tests.csproj\" />\n    </ItemGroup>\n\n</Project>\n"
  },
  {
    "path": "csharp/Tests/algo08_stack_tests/ArrayStack.Tests.cs",
    "content": "using System;\nusing algo08_stack;\nusing Xunit;\nusing Xunit.Abstractions;\n\nnamespace algo08_stack_tests\n{\n    public class ArrayStackTests\n    {\n        private readonly ITestOutputHelper _output;\n\n        public ArrayStackTests(ITestOutputHelper output)\n        {\n            _output = output;\n        }\n\n        private void PrintStackArray<T>(ArrayStack<T> list)\n        {\n            if (list.Count == 0) return;\n\n            while (list.Count > 0)\n            {\n                T item = list.Pop();\n                _output.WriteLine(item.ToString());\n            }\n        }\n\n        [Fact]\n        public void Push_3_Elements_Then_Length_Equal_3()\n        {\n            var stack = new ArrayStack<int>(5);\n            stack.Push(2);\n            stack.Push(4);\n            stack.Push(6);\n\n            Assert.Equal(3, stack.Count);\n\n            PrintStackArray(stack);\n        }\n\n        [Fact]\n        public void Push_Throw_InvalidOperationException_When_Stack_Full()\n        {\n            var stack = new ArrayStack<int>(5);\n            stack.Push(2);\n            stack.Push(4);\n            stack.Push(6);\n            stack.Push(8);\n            stack.Push(10);\n\n            Exception ex = Assert.Throws<InvalidOperationException>(() => stack.Push(11));\n            Assert.IsType<InvalidOperationException>(ex);\n\n            PrintStackArray(stack);\n        }\n\n        [Fact]\n        public void Pop_Throw_InvalidOperationException_When_Stack_Empty()\n        {\n            var stack = new ArrayStack<int>(5);\n\n            Exception ex = Assert.Throws<InvalidOperationException>(() => stack.Pop());\n            Assert.IsType<InvalidOperationException>(ex);\n\n            PrintStackArray(stack);\n        }\n\n        [Fact]\n        public void Pop_Valid_When_Stack_Not_Empty()\n        {\n            var stack = new ArrayStack<int>(5);\n            stack.Push(2);\n            stack.Push(4);\n\n            int val = stack.Pop();\n            Assert.Equal(4, val);\n\n            PrintStackArray(stack);\n        }\n    }\n}"
  },
  {
    "path": "csharp/Tests/algo08_stack_tests/LinkedStack.Tests.cs",
    "content": "using System;\nusing algo08_stack;\nusing Xunit;\nusing Xunit.Abstractions;\n\nnamespace algo08_stack_tests\n{\n    public class LinkedStackTests\n    {\n        private readonly ITestOutputHelper _output;\n\n        public LinkedStackTests(ITestOutputHelper output)\n        {\n            _output = output;\n        }\n\n        private void PrintStackLinkedList<T>(LinkedStack<T> list)\n        {\n            if (list.Count == 0) return;\n\n            while (list.Count > 0)\n            {\n                var val = list.Pop();\n                _output.WriteLine(val.ToString());\n            }\n        }\n\n        [Fact]\n        public void Push_3_Elements_Then_Length_Equal_3()\n        {\n            var stack = new LinkedStack<int>();\n            stack.Push(2);\n            stack.Push(4);\n            stack.Push(6);\n\n            Assert.Equal(3, stack.Count);\n\n            PrintStackLinkedList(stack);\n        }\n\n        [Fact]\n        public void Pop_Throw_InvalidOperationException_When_Stack_Empty()\n        {\n            var stack = new LinkedStack<int>();\n\n            Exception ex = Assert.Throws<InvalidOperationException>(() => stack.Pop());\n            Assert.IsType<InvalidOperationException>(ex);\n\n            PrintStackLinkedList(stack);\n        }\n\n        [Fact]\n        public void Pop_Valid_When_Stack_Not_Empty()\n        {\n            var stack = new LinkedStack<int>();\n            stack.Push(2);\n            stack.Push(4);\n\n            var nodeVal = stack.Pop();\n            Assert.Equal(4, nodeVal);\n\n            PrintStackLinkedList(stack);\n        }\n    }\n}"
  },
  {
    "path": "csharp/Tests/algo08_stack_tests/LinkedStackBrowser.Tests.cs",
    "content": "using algo08_stack;\nusing Xunit;\nusing Xunit.Abstractions;\n\nnamespace algo08_stack_tests\n{\n    public class LinkedStackBrowserTests\n    {\n        private readonly ITestOutputHelper _output;\n\n        public LinkedStackBrowserTests(ITestOutputHelper output)\n        {\n            _output = output;\n        }\n\n        [Fact]\n        public void Browser_Open_4_Links_Back_2_Return_Right_Link()\n        {\n            var browser = new LinkedStackBrowser();\n            browser.Open(\"www.google.com\");\n            browser.Open(\"www.baidu.com\");\n            browser.Open(\"www.qq.com\");\n            browser.Open(\"www.dadu.com\");\n\n            string url = browser.Backward();\n            url = browser.Backward();\n\n            Assert.Equal(\"www.qq.com\", url);\n        }\n\n        [Fact]\n        public void Browser_Open_4_Links_Back_4_Return_Empty()\n        {\n            var browser = new LinkedStackBrowser();\n            browser.Open(\"www.google.com\");\n            browser.Open(\"www.baidu.com\");\n            browser.Open(\"www.qq.com\");\n            browser.Open(\"www.dadu.com\");\n\n            browser.Backward();\n            browser.Backward();\n            browser.Backward();\n            browser.Backward();\n            string url = browser.Backward();\n\n            Assert.Equal(string.Empty, url);\n        }\n\n        [Fact]\n        public void Browser_Forward_Before_End()\n        {\n            var browser = new LinkedStackBrowser();\n            browser.Open(\"www.google.com\");\n            browser.Open(\"www.baidu.com\");\n            browser.Open(\"www.qq.com\");\n            browser.Open(\"www.dadu.com\");\n\n            browser.Backward();\n            browser.Backward();\n            browser.Backward();\n\n            browser.Forward();\n            string url = browser.Forward();\n\n            Assert.Equal(\"www.qq.com\", url);\n        }\n\n        [Fact]\n        public void Browser_Forward_Until_End()\n        {\n            var browser = new LinkedStackBrowser();\n            browser.Open(\"www.google.com\");\n            browser.Open(\"www.baidu.com\");\n            browser.Open(\"www.qq.com\");\n            browser.Open(\"www.dadu.com\");\n\n            browser.Backward();\n            browser.Backward();\n            browser.Backward();\n\n            browser.Forward();\n            browser.Forward();\n            browser.Forward();\n            string url = browser.Forward();\n\n            Assert.Equal(string.Empty, url);\n        }\n\n        [Fact]\n        public void Browser_Backward_And_Open_New_Then_Cannot_Forward()\n        {\n            var browser = new LinkedStackBrowser();\n            browser.Open(\"www.google.com\");\n            browser.Open(\"www.baidu.com\");\n            browser.Open(\"www.qq.com\");\n\n            browser.Backward();\n            browser.Backward();\n            \n            browser.Open(\"www.dadu.com\");\n\n            string url = browser.Forward();\n\n            Assert.Equal(string.Empty, url);\n        }\n    }\n}"
  },
  {
    "path": "csharp/Tests/algo08_stack_tests/algo08_stack_tests.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\n\n    <PropertyGroup>\n        <TargetFramework>netcoreapp2.2</TargetFramework>\n\n        <IsPackable>false</IsPackable>\n    </PropertyGroup>\n\n    <ItemGroup>\n        <PackageReference Include=\"Microsoft.NET.Test.Sdk\" Version=\"15.9.0\" />\n        <PackageReference Include=\"xunit\" Version=\"2.4.0\" />\n        <PackageReference Include=\"xunit.runner.visualstudio\" Version=\"2.4.0\" />\n    </ItemGroup>\n\n    <ItemGroup>\n      <ProjectReference Include=\"..\\..\\08-stack\\algo08_stack\\algo08_stack.csproj\" />\n    </ItemGroup>\n\n</Project>\n"
  },
  {
    "path": "csharp/csharp.sln",
    "content": "﻿\nMicrosoft Visual Studio Solution File, Format Version 12.00\n# Visual Studio 15\nVisualStudioVersion = 15.0.26124.0\nMinimumVisualStudioVersion = 15.0.26124.0\nProject(\"{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}\") = \"algo05_array\", \"05-array\\algo05_array.csproj\", \"{B88033F6-FF08-434A-AED7-91F5CDB73402}\"\nEndProject\nProject(\"{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}\") = \"algo06_linked_list\", \"06-linkedlist\\algo06_linked_list.csproj\", \"{29CABAFB-2581-42D1-ABD9-F0D9E0BB8DC2}\"\nEndProject\nProject(\"{2150E333-8FDC-42A3-9474-1A3956D46DE8}\") = \"Tests\", \"Tests\", \"{B0248987-EEDF-4D93-8E12-C00B1EB5B6CB}\"\nEndProject\nProject(\"{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}\") = \"algo05_array_tests\", \"Tests\\_05_array_tests\\algo05_array_tests.csproj\", \"{14982212-49E4-4409-8BFD-2D8A2945BD83}\"\nEndProject\nProject(\"{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}\") = \"algo06_linkedlist_tests\", \"Tests\\_06_linkedlist_tests\\algo06_linkedlist_tests.csproj\", \"{1B93D9C6-D6C1-4619-BFB9-D84C29099223}\"\nEndProject\nProject(\"{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}\") = \"algo07_linkedlist\", \"07-linkedlist\\_07_linkedlist\\algo07_linkedlist.csproj\", \"{5A4CCBB9-F683-4436-AAEE-4B3ABA76936B}\"\nEndProject\nProject(\"{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}\") = \"algo07_linkedlist_tests\", \"Tests\\_07_linkedlist_tests\\algo07_linkedlist_tests.csproj\", \"{66C3BC00-C135-4279-A666-A330A86F20D5}\"\nEndProject\nProject(\"{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}\") = \"algo08_stack\", \"08-stack\\algo08_stack\\algo08_stack.csproj\", \"{E080D481-C98E-43F3-B1D1-51DCF4CF7146}\"\nEndProject\nProject(\"{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}\") = \"algo08_stack_tests\", \"Tests\\algo08_stack_tests\\algo08_stack_tests.csproj\", \"{6A249475-54EA-4039-9B0C-DC0E0A884C07}\"\nEndProject\nGlobal\n\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\n\t\tDebug|Any CPU = Debug|Any CPU\n\t\tDebug|x64 = Debug|x64\n\t\tDebug|x86 = Debug|x86\n\t\tRelease|Any CPU = Release|Any CPU\n\t\tRelease|x64 = Release|x64\n\t\tRelease|x86 = Release|x86\n\tEndGlobalSection\n\tGlobalSection(SolutionProperties) = preSolution\n\t\tHideSolutionNode = FALSE\n\tEndGlobalSection\n\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\n\t\t{B88033F6-FF08-434A-AED7-91F5CDB73402}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{B88033F6-FF08-434A-AED7-91F5CDB73402}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{B88033F6-FF08-434A-AED7-91F5CDB73402}.Debug|x64.ActiveCfg = Debug|Any CPU\n\t\t{B88033F6-FF08-434A-AED7-91F5CDB73402}.Debug|x64.Build.0 = Debug|Any CPU\n\t\t{B88033F6-FF08-434A-AED7-91F5CDB73402}.Debug|x86.ActiveCfg = Debug|Any CPU\n\t\t{B88033F6-FF08-434A-AED7-91F5CDB73402}.Debug|x86.Build.0 = Debug|Any CPU\n\t\t{B88033F6-FF08-434A-AED7-91F5CDB73402}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{B88033F6-FF08-434A-AED7-91F5CDB73402}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{B88033F6-FF08-434A-AED7-91F5CDB73402}.Release|x64.ActiveCfg = Release|Any CPU\n\t\t{B88033F6-FF08-434A-AED7-91F5CDB73402}.Release|x64.Build.0 = Release|Any CPU\n\t\t{B88033F6-FF08-434A-AED7-91F5CDB73402}.Release|x86.ActiveCfg = Release|Any CPU\n\t\t{B88033F6-FF08-434A-AED7-91F5CDB73402}.Release|x86.Build.0 = Release|Any CPU\n\t\t{29CABAFB-2581-42D1-ABD9-F0D9E0BB8DC2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{29CABAFB-2581-42D1-ABD9-F0D9E0BB8DC2}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{29CABAFB-2581-42D1-ABD9-F0D9E0BB8DC2}.Debug|x64.ActiveCfg = Debug|Any CPU\n\t\t{29CABAFB-2581-42D1-ABD9-F0D9E0BB8DC2}.Debug|x64.Build.0 = Debug|Any CPU\n\t\t{29CABAFB-2581-42D1-ABD9-F0D9E0BB8DC2}.Debug|x86.ActiveCfg = Debug|Any CPU\n\t\t{29CABAFB-2581-42D1-ABD9-F0D9E0BB8DC2}.Debug|x86.Build.0 = Debug|Any CPU\n\t\t{29CABAFB-2581-42D1-ABD9-F0D9E0BB8DC2}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{29CABAFB-2581-42D1-ABD9-F0D9E0BB8DC2}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{29CABAFB-2581-42D1-ABD9-F0D9E0BB8DC2}.Release|x64.ActiveCfg = Release|Any CPU\n\t\t{29CABAFB-2581-42D1-ABD9-F0D9E0BB8DC2}.Release|x64.Build.0 = Release|Any CPU\n\t\t{29CABAFB-2581-42D1-ABD9-F0D9E0BB8DC2}.Release|x86.ActiveCfg = Release|Any CPU\n\t\t{29CABAFB-2581-42D1-ABD9-F0D9E0BB8DC2}.Release|x86.Build.0 = Release|Any CPU\n\t\t{14982212-49E4-4409-8BFD-2D8A2945BD83}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{14982212-49E4-4409-8BFD-2D8A2945BD83}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{14982212-49E4-4409-8BFD-2D8A2945BD83}.Debug|x64.ActiveCfg = Debug|Any CPU\n\t\t{14982212-49E4-4409-8BFD-2D8A2945BD83}.Debug|x64.Build.0 = Debug|Any CPU\n\t\t{14982212-49E4-4409-8BFD-2D8A2945BD83}.Debug|x86.ActiveCfg = Debug|Any CPU\n\t\t{14982212-49E4-4409-8BFD-2D8A2945BD83}.Debug|x86.Build.0 = Debug|Any CPU\n\t\t{14982212-49E4-4409-8BFD-2D8A2945BD83}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{14982212-49E4-4409-8BFD-2D8A2945BD83}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{14982212-49E4-4409-8BFD-2D8A2945BD83}.Release|x64.ActiveCfg = Release|Any CPU\n\t\t{14982212-49E4-4409-8BFD-2D8A2945BD83}.Release|x64.Build.0 = Release|Any CPU\n\t\t{14982212-49E4-4409-8BFD-2D8A2945BD83}.Release|x86.ActiveCfg = Release|Any CPU\n\t\t{14982212-49E4-4409-8BFD-2D8A2945BD83}.Release|x86.Build.0 = Release|Any CPU\n\t\t{1B93D9C6-D6C1-4619-BFB9-D84C29099223}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{1B93D9C6-D6C1-4619-BFB9-D84C29099223}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{1B93D9C6-D6C1-4619-BFB9-D84C29099223}.Debug|x64.ActiveCfg = Debug|Any CPU\n\t\t{1B93D9C6-D6C1-4619-BFB9-D84C29099223}.Debug|x64.Build.0 = Debug|Any CPU\n\t\t{1B93D9C6-D6C1-4619-BFB9-D84C29099223}.Debug|x86.ActiveCfg = Debug|Any CPU\n\t\t{1B93D9C6-D6C1-4619-BFB9-D84C29099223}.Debug|x86.Build.0 = Debug|Any CPU\n\t\t{1B93D9C6-D6C1-4619-BFB9-D84C29099223}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{1B93D9C6-D6C1-4619-BFB9-D84C29099223}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{1B93D9C6-D6C1-4619-BFB9-D84C29099223}.Release|x64.ActiveCfg = Release|Any CPU\n\t\t{1B93D9C6-D6C1-4619-BFB9-D84C29099223}.Release|x64.Build.0 = Release|Any CPU\n\t\t{1B93D9C6-D6C1-4619-BFB9-D84C29099223}.Release|x86.ActiveCfg = Release|Any CPU\n\t\t{1B93D9C6-D6C1-4619-BFB9-D84C29099223}.Release|x86.Build.0 = Release|Any CPU\n\t\t{5A4CCBB9-F683-4436-AAEE-4B3ABA76936B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{5A4CCBB9-F683-4436-AAEE-4B3ABA76936B}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{5A4CCBB9-F683-4436-AAEE-4B3ABA76936B}.Debug|x64.ActiveCfg = Debug|Any CPU\n\t\t{5A4CCBB9-F683-4436-AAEE-4B3ABA76936B}.Debug|x64.Build.0 = Debug|Any CPU\n\t\t{5A4CCBB9-F683-4436-AAEE-4B3ABA76936B}.Debug|x86.ActiveCfg = Debug|Any CPU\n\t\t{5A4CCBB9-F683-4436-AAEE-4B3ABA76936B}.Debug|x86.Build.0 = Debug|Any CPU\n\t\t{5A4CCBB9-F683-4436-AAEE-4B3ABA76936B}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{5A4CCBB9-F683-4436-AAEE-4B3ABA76936B}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{5A4CCBB9-F683-4436-AAEE-4B3ABA76936B}.Release|x64.ActiveCfg = Release|Any CPU\n\t\t{5A4CCBB9-F683-4436-AAEE-4B3ABA76936B}.Release|x64.Build.0 = Release|Any CPU\n\t\t{5A4CCBB9-F683-4436-AAEE-4B3ABA76936B}.Release|x86.ActiveCfg = Release|Any CPU\n\t\t{5A4CCBB9-F683-4436-AAEE-4B3ABA76936B}.Release|x86.Build.0 = Release|Any CPU\n\t\t{66C3BC00-C135-4279-A666-A330A86F20D5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{66C3BC00-C135-4279-A666-A330A86F20D5}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{66C3BC00-C135-4279-A666-A330A86F20D5}.Debug|x64.ActiveCfg = Debug|Any CPU\n\t\t{66C3BC00-C135-4279-A666-A330A86F20D5}.Debug|x64.Build.0 = Debug|Any CPU\n\t\t{66C3BC00-C135-4279-A666-A330A86F20D5}.Debug|x86.ActiveCfg = Debug|Any CPU\n\t\t{66C3BC00-C135-4279-A666-A330A86F20D5}.Debug|x86.Build.0 = Debug|Any CPU\n\t\t{66C3BC00-C135-4279-A666-A330A86F20D5}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{66C3BC00-C135-4279-A666-A330A86F20D5}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{66C3BC00-C135-4279-A666-A330A86F20D5}.Release|x64.ActiveCfg = Release|Any CPU\n\t\t{66C3BC00-C135-4279-A666-A330A86F20D5}.Release|x64.Build.0 = Release|Any CPU\n\t\t{66C3BC00-C135-4279-A666-A330A86F20D5}.Release|x86.ActiveCfg = Release|Any CPU\n\t\t{66C3BC00-C135-4279-A666-A330A86F20D5}.Release|x86.Build.0 = Release|Any CPU\n\t\t{E080D481-C98E-43F3-B1D1-51DCF4CF7146}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{E080D481-C98E-43F3-B1D1-51DCF4CF7146}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{E080D481-C98E-43F3-B1D1-51DCF4CF7146}.Debug|x64.ActiveCfg = Debug|Any CPU\n\t\t{E080D481-C98E-43F3-B1D1-51DCF4CF7146}.Debug|x64.Build.0 = Debug|Any CPU\n\t\t{E080D481-C98E-43F3-B1D1-51DCF4CF7146}.Debug|x86.ActiveCfg = Debug|Any CPU\n\t\t{E080D481-C98E-43F3-B1D1-51DCF4CF7146}.Debug|x86.Build.0 = Debug|Any CPU\n\t\t{E080D481-C98E-43F3-B1D1-51DCF4CF7146}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{E080D481-C98E-43F3-B1D1-51DCF4CF7146}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{E080D481-C98E-43F3-B1D1-51DCF4CF7146}.Release|x64.ActiveCfg = Release|Any CPU\n\t\t{E080D481-C98E-43F3-B1D1-51DCF4CF7146}.Release|x64.Build.0 = Release|Any CPU\n\t\t{E080D481-C98E-43F3-B1D1-51DCF4CF7146}.Release|x86.ActiveCfg = Release|Any CPU\n\t\t{E080D481-C98E-43F3-B1D1-51DCF4CF7146}.Release|x86.Build.0 = Release|Any CPU\n\t\t{6A249475-54EA-4039-9B0C-DC0E0A884C07}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{6A249475-54EA-4039-9B0C-DC0E0A884C07}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{6A249475-54EA-4039-9B0C-DC0E0A884C07}.Debug|x64.ActiveCfg = Debug|Any CPU\n\t\t{6A249475-54EA-4039-9B0C-DC0E0A884C07}.Debug|x64.Build.0 = Debug|Any CPU\n\t\t{6A249475-54EA-4039-9B0C-DC0E0A884C07}.Debug|x86.ActiveCfg = Debug|Any CPU\n\t\t{6A249475-54EA-4039-9B0C-DC0E0A884C07}.Debug|x86.Build.0 = Debug|Any CPU\n\t\t{6A249475-54EA-4039-9B0C-DC0E0A884C07}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{6A249475-54EA-4039-9B0C-DC0E0A884C07}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{6A249475-54EA-4039-9B0C-DC0E0A884C07}.Release|x64.ActiveCfg = Release|Any CPU\n\t\t{6A249475-54EA-4039-9B0C-DC0E0A884C07}.Release|x64.Build.0 = Release|Any CPU\n\t\t{6A249475-54EA-4039-9B0C-DC0E0A884C07}.Release|x86.ActiveCfg = Release|Any CPU\n\t\t{6A249475-54EA-4039-9B0C-DC0E0A884C07}.Release|x86.Build.0 = Release|Any CPU\n\tEndGlobalSection\n\tGlobalSection(NestedProjects) = preSolution\n\t\t{14982212-49E4-4409-8BFD-2D8A2945BD83} = {B0248987-EEDF-4D93-8E12-C00B1EB5B6CB}\n\t\t{1B93D9C6-D6C1-4619-BFB9-D84C29099223} = {B0248987-EEDF-4D93-8E12-C00B1EB5B6CB}\n\t\t{66C3BC00-C135-4279-A666-A330A86F20D5} = {B0248987-EEDF-4D93-8E12-C00B1EB5B6CB}\n\t\t{6A249475-54EA-4039-9B0C-DC0E0A884C07} = {B0248987-EEDF-4D93-8E12-C00B1EB5B6CB}\n\tEndGlobalSection\nEndGlobal\n"
  },
  {
    "path": "csharp/csharp.sln.DotSettings.user",
    "content": "﻿<wpf:ResourceDictionary xml:space=\"preserve\" xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" xmlns:s=\"clr-namespace:System;assembly=mscorlib\" xmlns:ss=\"urn:shemas-jetbrains-com:settings-storage-xaml\" xmlns:wpf=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\">\n\t<s:Boolean x:Key=\"/Default/CodeStyle/Naming/CSharpAutoNaming/IsNotificationDisabled/@EntryValue\">True</s:Boolean>\n\t<s:String x:Key=\"/Default/CodeStyle/Naming/CSharpNaming/AutoDetectedNamingRules/=Method/@EntryIndexedValue\">&lt;Policy Inspect=\"True\" Prefix=\"\" Suffix=\"\" Style=\"AaBb\"&gt;&lt;ExtraRule Prefix=\"\" Suffix=\"\" Style=\"AaBb_AaBb\" /&gt;&lt;/Policy&gt;</s:String>\n\t<s:String x:Key=\"/Default/CodeStyle/Naming/CSharpNaming/AutoDetectedNamingRules/=Parameters/@EntryIndexedValue\">&lt;Policy Inspect=\"True\" Prefix=\"\" Suffix=\"\" Style=\"aaBb\" /&gt;</s:String>\n\t<s:String x:Key=\"/Default/CodeStyle/Naming/CSharpNaming/AutoDetectedNamingRules/=TypesAndNamespaces/@EntryIndexedValue\">&lt;Policy Inspect=\"False\" Prefix=\"\" Suffix=\"\" Style=\"AaBb\" /&gt;</s:String>\n\t<s:String x:Key=\"/Default/CodeStyle/Naming/CSharpNaming/AutoDetectedNamingRules/=Locals/@EntryIndexedValue\">&lt;Policy Inspect=\"True\" Prefix=\"\" Suffix=\"\" Style=\"aaBb\" /&gt;</s:String>\n\t<s:String x:Key=\"/Default/CodeStyle/Naming/CSharpNaming/AutoDetectedNamingRules/=PrivateInstanceFields/@EntryIndexedValue\">&lt;Policy Inspect=\"True\" Prefix=\"_\" Suffix=\"\" Style=\"aaBb\" /&gt;</s:String>\n\t<s:String x:Key=\"/Default/CodeStyle/Naming/CSharpNaming/AutoDetectedNamingRules/=Property/@EntryIndexedValue\">&lt;Policy Inspect=\"True\" Prefix=\"\" Suffix=\"\" Style=\"AaBb\" /&gt;</s:String>\n\t<s:Boolean x:Key=\"/Default/CodeStyle/Naming/CSharpNaming/ApplyAutoDetectedRules/@EntryValue\">True</s:Boolean>\n\t<s:Boolean x:Key=\"/Default/CodeStyle/Naming/CSharpAutoNaming/IsNamingAutoDetectionCompleted/@EntryValue\">True</s:Boolean></wpf:ResourceDictionary>"
  },
  {
    "path": "f21",
    "content": "// A Stack based C++ program to find next \n// greater element for all array elements \n// in same order as input. \n#include <bits/stdc++.h> \n\nusing namespace std; \n\n/* prints element and NGE pair for all \nelements of arr[] of size n */\nvoid printNGE(int arr[], int n) \n{ \n\tstack<int> s; \n\tunordered_map<int, int> mp; \n\n\t/* push the first element to stack */\n\ts.push(arr[0]); \n\t\n\n\t// iterate for rest of the elements \n\tfor (int i = 1; i < n; i++) { \n\n\t\tif (s.empty()) { \n\t\t\ts.push(arr[i]); \n\t\t\tcontinue; \n\t\t} \n\n\t\t/* if stack is not empty, then \n\tpop an element from stack. \n\tIf the popped element is smaller \n\tthan next, then \n\ta) print the pair \n\tb) keep popping while elements are \n\tsmaller and stack is not empty */\n\t\twhile (s.empty() == false && s.top() < arr[i]) { \n\t\t\tmp[s.top()] = arr[i]; \n\t\t\ts.pop(); \n\t\t} \n\n\t\t/* push next to stack so that we can find \n\tnext smaller for it */\n\t\ts.push(arr[i]); \n\t} \n\n\t/* After iterating over the loop, the remaining \nelements in stack do not have the next smaller \nelement, so print -1 for them */\n\twhile (s.empty() == false) { \n\t\tmp[s.top()] = -1; \n\t\ts.pop(); \n\t} \n\n\tfor (int i=0; i<n; i++) \n\tcout << arr[i] << \" ---> \" << mp[arr[i]] << endl; \n} \n\n/* Driver program to test above functions */\nint main() \n{ \n\tint arr[] = { 11, 13, 21, 3 }; \n\tint n = sizeof(arr) / sizeof(arr[0]); \n\tprintNGE(arr, n); \n\treturn 0; \n} \n"
  },
  {
    "path": "go/.gitkeep",
    "content": ""
  },
  {
    "path": "go/05_array/.gitkeep",
    "content": ""
  },
  {
    "path": "go/05_array/array.go",
    "content": "package _5_array\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n)\n\n/**\n * 1) 数组的插入、删除、按照下标随机访问操作；\n * 2）数组中的数据是int类型的；\n *\n * Author: leo\n */\n\ntype Array struct {\n\tdata   []int\n\tlength uint\n}\n\n//为数组初始化内存\nfunc NewArray(capacity uint) *Array {\n\tif capacity == 0 {\n\t\treturn nil\n\t}\n\treturn &Array{\n\t\tdata:   make([]int, capacity, capacity),\n\t\tlength: 0,\n\t}\n}\n\nfunc (this *Array) Len() uint {\n\treturn this.length\n}\n\n//判断索引是否越界\nfunc (this *Array) isIndexOutOfRange(index uint) bool {\n\tif index >= uint(cap(this.data)) {\n\t\treturn true\n\t}\n\treturn false\n}\n\n//通过索引查找数组，索引范围[0,n-1]\nfunc (this *Array) Find(index uint) (int, error) {\n\tif this.isIndexOutOfRange(index) {\n\t\treturn 0, errors.New(\"out of index range\")\n\t}\n\treturn this.data[index], nil\n}\n\n//插入数值到索引index上\nfunc (this *Array) Insert(index uint, v int) error {\n\tif this.Len() == uint(cap(this.data)) {\n\t\treturn errors.New(\"full array\")\n\t}\n\tif index != this.length && this.isIndexOutOfRange(index) {\n\t\treturn errors.New(\"out of index range\")\n\t}\n\n\tfor i := this.length; i > index; i-- {\n\t\tthis.data[i] = this.data[i-1]\n\t}\n\tthis.data[index] = v\n\tthis.length++\n\treturn nil\n}\n\nfunc (this *Array) InsertToTail(v int) error {\n\treturn this.Insert(this.Len(), v)\n}\n\n//删除索引index上的值\nfunc (this *Array) Delete(index uint) (int, error) {\n\tif this.isIndexOutOfRange(index) {\n\t\treturn 0, errors.New(\"out of index range\")\n\t}\n\tv := this.data[index]\n\tfor i := index; i < this.Len()-1; i++ {\n\t\tthis.data[i] = this.data[i+1]\n\t}\n\tthis.length--\n\treturn v, nil\n}\n\n//打印数列\nfunc (this *Array) Print() {\n\tvar format string\n\tfor i := uint(0); i < this.Len(); i++ {\n\t\tformat += fmt.Sprintf(\"|%+v\", this.data[i])\n\t}\n\tfmt.Println(format)\n}\n"
  },
  {
    "path": "go/05_array/array_test.go",
    "content": "package _5_array\n\nimport (\n\t\"testing\"\n)\n\nfunc TestInsert(t *testing.T) {\n\tcapacity := 10\n\tarr := NewArray(uint(capacity))\n\tfor i := 0; i < capacity-2; i++ {\n\t\terr := arr.Insert(uint(i), i+1)\n\t\tif nil != err {\n\t\t\tt.Fatal(err.Error())\n\t\t}\n\t}\n\tarr.Print()\n\n\tarr.Insert(uint(6), 999)\n\tarr.Print()\n\n\tarr.InsertToTail(666)\n\tarr.Print()\n}\n\nfunc TestDelete(t *testing.T) {\n\tcapacity := 10\n\tarr := NewArray(uint(capacity))\n\tfor i := 0; i < capacity; i++ {\n\t\terr := arr.Insert(uint(i), i+1)\n\t\tif nil != err {\n\t\t\tt.Fatal(err.Error())\n\t\t}\n\t}\n\tarr.Print()\n\n\tfor i := 9; i >= 0; i-- {\n\t\t_, err := arr.Delete(uint(i))\n\t\tif nil != err {\n\t\t\tt.Fatal(err)\n\t\t}\n\t\tarr.Print()\n\t}\n}\n\nfunc TestFind(t *testing.T) {\n\tcapacity := 10\n\tarr := NewArray(uint(capacity))\n\tfor i := 0; i < capacity; i++ {\n\t\terr := arr.Insert(uint(i), i+1)\n\t\tif nil != err {\n\t\t\tt.Fatal(err.Error())\n\t\t}\n\t}\n\tarr.Print()\n\n\tt.Log(arr.Find(0))\n\tt.Log(arr.Find(9))\n\tt.Log(arr.Find(11))\n}\n"
  },
  {
    "path": "go/06_linkedlist/.gitkeep",
    "content": ""
  },
  {
    "path": "go/06_linkedlist/palindrome-linked-list.go",
    "content": "/**\n * Definition for singly-linked list.\n * type ListNode struct {\n *     Val int\n *     Next *ListNode\n * }\n */\nfunc isPalindrome(head *ListNode) bool  {\n\t\tvar slow *ListNode = head\n\t\tvar fast *ListNode = head\n\t\tvar prev *ListNode = nil\n\t\tvar temp *ListNode = nil\n\t\n\t\tif (head == nil || head.Next == nil) {\n\t\t\treturn true\n\t\t}\n\t\n\t\tfor (fast != nil && fast.Next !=nil){\n\t\t\tfast = fast.Next.Next\n\t\t\ttemp = slow.Next\n\t\t\tslow.Next = prev\n\t\t\tprev = slow\n\t\t\tslow = temp\n\t\t}\t\t\t\t\t\t\t// 快的先跑完,同时反转了一半链表,剪短\n\t\n\t\tif fast != nil {\n\t\t\tslow = slow.Next\t\t// 处理余数,跨过中位数\n                                    // prev 增加中 2->1->nil\n\t\t}\n\t\n\t\tvar l1 *ListNode = prev\n\t\tvar l2 *ListNode = slow\n\t\n\t\tfor (l1 != nil && l2 !=nil && l1.Val == l2.Val){\n\t\t\tl1 = l1.Next\n\t\t\tl2 = l2.Next\n\t\t}\n\t\n\t\treturn (l1 == nil && l2 == nil)\n\t\n\t}\n"
  },
  {
    "path": "go/06_linkedlist/palindrome.go",
    "content": "package _6_linkedlist\n\n/*\n思路1：开一个栈存放链表前半段\n时间复杂度：O(N)\n空间复杂度：O(N)\n*/\nfunc isPalindrome1(l *LinkedList) bool {\n\tlLen := l.length\n\tif lLen == 0 {\n\t\treturn false\n\t}\n\tif lLen == 1 {\n\t\treturn true\n\t}\n\n\ts := make([]string, 0, lLen/2)\n\tcur := l.head\n\tfor i := uint(1); i <= lLen; i++ {\n\t\tcur = cur.next\n\t\tif lLen%2 != 0 && i == (lLen/2+1) { //如果链表有奇数个节点，中间的直接忽略\n\t\t\tcontinue\n\t\t}\n\t\tif i <= lLen/2 { //前一半入栈\n\t\t\ts = append(s, cur.GetValue().(string))\n\t\t} else { //后一半与前一半进行对比\n\t\t\tif s[lLen-i] != cur.GetValue().(string) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t}\n\t}\n\n\treturn true\n}\n\n/*\n思路2\n找到链表中间节点，将前半部分转置，再从中间向左右遍历对比\n时间复杂度：O(N)\n*/\nfunc isPalindrome2(l *LinkedList) bool {\n\tlLen := l.length\n\tif lLen == 0 {\n\t\treturn false\n\t}\n\tif lLen == 1 {\n\t\treturn true\n\t}\n\tvar isPalindrome = true\n\tstep := lLen / 2\n\tvar pre *ListNode = nil\n\tcur := l.head.next\n\tnext := l.head.next.next\n\tfor i := uint(1); i <= step; i++ {\n\t\ttmp := cur.GetNext()\n\t\tcur.next = pre\n\t\tpre = cur\n\t\tcur = tmp\n\t\tnext = cur.GetNext()\n\t}\n\tmid := cur\n\n\tvar left, right *ListNode = pre, nil\n\tif lLen%2 != 0 { //\n\t\tright = mid.GetNext()\n\t} else {\n\t\tright = mid\n\t}\n\n\tfor nil != left && nil != right {\n\t\tif left.GetValue().(string) != right.GetValue().(string) {\n\t\t\tisPalindrome = false\n\t\t\tbreak\n\t\t}\n\t\tleft = left.GetNext()\n\t\tright = right.GetNext()\n\t}\n\n\t//复原链表\n\tcur = pre\n\tpre = mid\n\tfor nil != cur {\n\t\tnext = cur.GetNext()\n\t\tcur.next = pre\n\t\tpre = cur\n\t\tcur = next\n\t}\n\tl.head.next = pre\n\n\treturn isPalindrome\n}\n"
  },
  {
    "path": "go/06_linkedlist/palindrome_test.go",
    "content": "package _6_linkedlist\n\nimport \"testing\"\n\nfunc TestPalindrome1(t *testing.T) {\n\tstrs := []string{\"heooeh\", \"hello\", \"heoeh\", \"a\", \"\"}\n\tfor _, str1 := range strs {\n\t\tl := NewLinkedList()\n\t\tfor _, c := range str1 {\n\t\t\tl.InsertToTail(string(c))\n\t\t}\n\t\tl.Print()\n\t\tt.Log(isPalindrome1(l))\n\t}\n}\n\nfunc TestPalindrome2(t *testing.T) {\n\tstrs := []string{\"heooeh\", \"hello\", \"heoeh\", \"a\", \"\"}\n\tfor _, str1 := range strs {\n\t\tl := NewLinkedList()\n\t\tfor _, c := range str1 {\n\t\t\tl.InsertToTail(string(c))\n\t\t}\n\t\tl.Print()\n\t\tt.Log(isPalindrome2(l))\n\t\tl.Print()\n\t}\n}\n"
  },
  {
    "path": "go/06_linkedlist/singlelinkedlist.go",
    "content": "package _6_linkedlist\n\nimport \"fmt\"\n\n/*\n单链表基本操作\nauthor:leo\n*/\n\ntype ListNode struct {\n\tnext  *ListNode\n\tvalue interface{}\n}\n\ntype LinkedList struct {\n\thead   *ListNode\n\tlength uint\n}\n\nfunc NewListNode(v interface{}) *ListNode {\n\treturn &ListNode{nil, v}\n}\n\nfunc (this *ListNode) GetNext() *ListNode {\n\treturn this.next\n}\n\nfunc (this *ListNode) GetValue() interface{} {\n\treturn this.value\n}\n\nfunc NewLinkedList() *LinkedList {\n\treturn &LinkedList{NewListNode(0), 0}\n}\n\n//在某个节点后面插入节点\nfunc (this *LinkedList) InsertAfter(p *ListNode, v interface{}) bool {\n\tif nil == p {\n\t\treturn false\n\t}\n\tnewNode := NewListNode(v)\n\toldNext := p.next\n\tp.next = newNode\n\tnewNode.next = oldNext\n\tthis.length++\n\treturn true\n}\n\n//在某个节点前面插入节点\nfunc (this *LinkedList) InsertBefore(p *ListNode, v interface{}) bool {\n\tif nil == p || p == this.head {\n\t\treturn false\n\t}\n\tcur := this.head.next\n\tpre := this.head\n\tfor nil != cur {\n\t\tif cur == p {\n\t\t\tbreak\n\t\t}\n\t\tpre = cur\n\t\tcur = cur.next\n\t}\n\tif nil == cur {\n\t\treturn false\n\t}\n\tnewNode := NewListNode(v)\n\tpre.next = newNode\n\tnewNode.next = cur\n\tthis.length++\n\treturn true\n}\n\n//在链表头部插入节点\nfunc (this *LinkedList) InsertToHead(v interface{}) bool {\n\treturn this.InsertAfter(this.head, v)\n}\n\n//在链表尾部插入节点\nfunc (this *LinkedList) InsertToTail(v interface{}) bool {\n\tcur := this.head\n\tfor nil != cur.next {\n\t\tcur = cur.next\n\t}\n\treturn this.InsertAfter(cur, v)\n}\n\n//通过索引查找节点\nfunc (this *LinkedList) FindByIndex(index uint) *ListNode {\n\tif index >= this.length {\n\t\treturn nil\n\t}\n\tcur := this.head.next\n\tvar i uint = 0\n\tfor ; i < index; i++ {\n\t\tcur = cur.next\n\t}\n\treturn cur\n}\n\n//删除传入的节点\nfunc (this *LinkedList) DeleteNode(p *ListNode) bool {\n\tif nil == p {\n\t\treturn false\n\t}\n\tcur := this.head.next\n\tpre := this.head\n\tfor nil != cur {\n\t\tif cur == p {\n\t\t\tbreak\n\t\t}\n\t\tpre = cur\n\t\tcur = cur.next\n\t}\n\tif nil == cur {\n\t\treturn false\n\t}\n\tpre.next = p.next\n\tp = nil\n\tthis.length--\n\treturn true\n}\n\n//打印链表\nfunc (this *LinkedList) Print() {\n\tcur := this.head.next\n\tformat := \"\"\n\tfor nil != cur {\n\t\tformat += fmt.Sprintf(\"%+v\", cur.GetValue())\n\t\tcur = cur.next\n\t\tif nil != cur {\n\t\t\tformat += \"->\"\n\t\t}\n\t}\n\tfmt.Println(format)\n}\n"
  },
  {
    "path": "go/06_linkedlist/singlelinkedlist_test.go",
    "content": "package _6_linkedlist\n\nimport \"testing\"\n\nfunc TestInsertToHead(t *testing.T) {\n\tl := NewLinkedList()\n\tfor i := 0; i < 10; i++ {\n\t\tl.InsertToHead(i + 1)\n\t}\n\tl.Print()\n}\n\nfunc TestInsertToTail(t *testing.T) {\n\tl := NewLinkedList()\n\tfor i := 0; i < 10; i++ {\n\t\tl.InsertToTail(i + 1)\n\t}\n\tl.Print()\n}\n\nfunc TestFindByIndex(t *testing.T) {\n\tl := NewLinkedList()\n\tfor i := 0; i < 10; i++ {\n\t\tl.InsertToTail(i + 1)\n\t}\n\tt.Log(l.FindByIndex(0))\n\tt.Log(l.FindByIndex(9))\n\tt.Log(l.FindByIndex(5))\n\tt.Log(l.FindByIndex(11))\n}\n\nfunc TestDeleteNode(t *testing.T) {\n\tl := NewLinkedList()\n\tfor i := 0; i < 3; i++ {\n\t\tl.InsertToTail(i + 1)\n\t}\n\tl.Print()\n\n\tt.Log(l.DeleteNode(l.head.next))\n\tl.Print()\n\n\tt.Log(l.DeleteNode(l.head.next.next))\n\tl.Print()\n}\n"
  },
  {
    "path": "go/07_linkedlist/.gitkeep",
    "content": ""
  },
  {
    "path": "go/07_linkedlist/main.go",
    "content": "package _7_linkedlist\n\nimport \"fmt\"\n\n//单链表节点\ntype ListNode struct {\n\tnext  *ListNode\n\tvalue interface{}\n}\n\n//单链表\ntype LinkedList struct {\n\thead *ListNode\n}\n\n//打印链表\nfunc (this *LinkedList) Print() {\n\tcur := this.head.next\n\tformat := \"\"\n\tfor nil != cur {\n\t\tformat += fmt.Sprintf(\"%+v\", cur.value)\n\t\tcur = cur.next\n\t\tif nil != cur {\n\t\t\tformat += \"->\"\n\t\t}\n\t}\n\tfmt.Println(format)\n}\n\n/*\n单链表反转\n时间复杂度：O(N)\n*/\nfunc (this *LinkedList) Reverse() {\n\tif nil == this.head || nil == this.head.next || nil == this.head.next.next {\n\t\treturn\n\t}\n\n\tvar pre *ListNode = nil\n\tcur := this.head.next\n\tfor nil != cur {\n\t\ttmp := cur.next\n\t\tcur.next = pre\n\t\tpre = cur\n\t\tcur = tmp\n\t}\n\n\tthis.head.next = pre\n}\n\n/*\n判断单链表是否有环\n*/\nfunc (this *LinkedList) HasCycle() bool {\n\tif nil != this.head {\n\t\tslow := this.head\n\t\tfast := this.head\n\t\tfor nil != fast && nil != fast.next {\n\t\t\tslow = slow.next\n\t\t\tfast = fast.next.next\n\t\t\tif slow == fast {\n\t\t\t\treturn true\n\t\t\t}\n\t\t}\n\t}\n\treturn false\n}\n\n/*\n两个有序单链表合并\n*/\nfunc MergeSortedList(l1, l2 *LinkedList) *LinkedList {\n\tif nil == l1 || nil == l1.head || nil == l1.head.next {\n\t\treturn l2\n\t}\n\tif nil == l2 || nil == l2.head || nil == l2.head.next {\n\t\treturn l1\n\t}\n\n\tl := &LinkedList{head: &ListNode{}}\n\tcur := l.head\n\tcurl1 := l1.head.next\n\tcurl2 := l2.head.next\n\tfor nil != curl1 && nil != curl2 {\n\t\tif curl1.value.(int) > curl2.value.(int) {\n\t\t\tcur.next = curl2\n\t\t\tcurl2 = curl2.next\n\t\t} else {\n\t\t\tcur.next = curl1\n\t\t\tcurl1 = curl1.next\n\t\t}\n\t\tcur = cur.next\n\t}\n\n\tif nil != curl1 {\n\t\tcur.next = curl1\n\t} else if nil != curl2 {\n\t\tcur.next = curl2\n\t}\n\n\treturn l\n}\n\n/*\n删除倒数第N个节点\n*/\nfunc (this *LinkedList) DeleteBottomN(n int) {\n\tif n <= 0 || nil == this.head || nil == this.head.next {\n\t\treturn\n\t}\n\n\tfast := this.head\n\tfor i := 1; i <= n && fast != nil; i++ {\n\t\tfast = fast.next\n\t}\n\n\tif nil == fast {\n\t\treturn\n\t}\n\n\tslow := this.head\n\tfor nil != fast.next {\n\t\tslow = slow.next\n\t\tfast = fast.next\n\t}\n\tslow.next = slow.next.next\n}\n\n/*\n获取中间节点\n*/\nfunc (this *LinkedList) FindMiddleNode() *ListNode {\n\tif nil == this.head || nil == this.head.next {\n\t\treturn nil\n\t}\n\tif nil == this.head.next.next {\n\t\treturn this.head.next\n\t}\n\n\tslow, fast := this.head, this.head\n\tfor nil != fast && nil != fast.next {\n\t\tslow = slow.next\n\t\tfast = fast.next.next\n\t}\n\treturn slow\n}\n"
  },
  {
    "path": "go/07_linkedlist/main_test.go",
    "content": "package _7_linkedlist\n\nimport \"testing\"\n\nvar l *LinkedList\n\nfunc init() {\n\tn5 := &ListNode{value: 5}\n\tn4 := &ListNode{value: 4, next: n5}\n\tn3 := &ListNode{value: 3, next: n4}\n\tn2 := &ListNode{value: 2, next: n3}\n\tn1 := &ListNode{value: 1, next: n2}\n\tl = &LinkedList{head: &ListNode{next: n1}}\n}\n\nfunc TestReverse(t *testing.T) {\n\tl.Print()\n\tl.Reverse()\n\tl.Print()\n}\n\nfunc TestHasCycle(t *testing.T) {\n\tt.Log(l.HasCycle())\n\tl.head.next.next.next.next.next.next = l.head.next.next.next\n\tt.Log(l.HasCycle())\n}\n\nfunc TestMergeSortedList(t *testing.T) {\n\tn5 := &ListNode{value: 9}\n\tn4 := &ListNode{value: 7, next: n5}\n\tn3 := &ListNode{value: 5, next: n4}\n\tn2 := &ListNode{value: 3, next: n3}\n\tn1 := &ListNode{value: 1, next: n2}\n\tl1 := &LinkedList{head: &ListNode{next: n1}}\n\n\tn10 := &ListNode{value: 10}\n\tn9 := &ListNode{value: 8, next: n10}\n\tn8 := &ListNode{value: 6, next: n9}\n\tn7 := &ListNode{value: 4, next: n8}\n\tn6 := &ListNode{value: 2, next: n7}\n\tl2 := &LinkedList{head: &ListNode{next: n6}}\n\n\tMergeSortedList(l1, l2).Print()\n}\n\nfunc TestDeleteBottomN(t *testing.T) {\n\tl.Print()\n\tl.DeleteBottomN(3)\n\tl.Print()\n}\n\nfunc TestFindMiddleNode(t *testing.T) {\n\tl.DeleteBottomN(1)\n\tl.DeleteBottomN(1)\n\tl.DeleteBottomN(1)\n\tl.DeleteBottomN(1)\n\tl.Print()\n\tt.Log(l.FindMiddleNode())\n}\n"
  },
  {
    "path": "go/08_stack/SimpleBrowser.go",
    "content": "package _8_stack\n\nimport \"fmt\"\n\ntype Browser struct {\n\tforwardStack Stack\n\tbackStack    Stack\n}\n\nfunc NewBrowser() *Browser {\n\treturn &Browser{\n\t\tforwardStack: NewArrayStack(),\n\t\tbackStack:    NewLinkedListStack(),\n\t}\n}\n\nfunc (this *Browser) CanForward() bool {\n\tif this.forwardStack.IsEmpty() {\n\t\treturn false\n\t}\n\treturn true\n}\n\nfunc (this *Browser) CanBack() bool {\n\tif this.backStack.IsEmpty() {\n\t\treturn false\n\t}\n\treturn true\n}\n\nfunc (this *Browser) Open(addr string) {\n\tfmt.Printf(\"Open new addr %+v\\n\", addr)\n\tthis.forwardStack.Flush()\n}\n\nfunc (this *Browser) PushBack(addr string) {\n\tthis.backStack.Push(addr)\n}\n\nfunc (this *Browser) Forward() {\n\tif this.forwardStack.IsEmpty() {\n\t\treturn\n\t}\n\ttop := this.forwardStack.Pop()\n\tthis.backStack.Push(top)\n\tfmt.Printf(\"forward to %+v\\n\", top)\n}\n\nfunc (this *Browser) Back() {\n\tif this.backStack.IsEmpty() {\n\t\treturn\n\t}\n\ttop := this.backStack.Pop()\n\tthis.forwardStack.Push(top)\n\tfmt.Printf(\"back to %+v\\n\", top)\n}\n"
  },
  {
    "path": "go/08_stack/SimpleBrowser_test.go",
    "content": "package _8_stack\n\nimport \"testing\"\n\nfunc TestBrowser(t *testing.T) {\n\tb := NewBrowser()\n\tb.PushBack(\"www.qq.com\")\n\tb.PushBack(\"www.baidu.com\")\n\tb.PushBack(\"www.sina.com\")\n\tif b.CanBack() {\n\t\tb.Back()\n\t}\n\tif b.CanForward() {\n\t\tb.Forward()\n\t}\n\tif b.CanBack() {\n\t\tb.Back()\n\t}\n\tif b.CanBack() {\n\t\tb.Back()\n\t}\n\tif b.CanBack() {\n\t\tb.Back()\n\t}\n\tb.Open(\"www.taobao.com\")\n\tif b.CanForward() {\n\t\tb.Forward()\n\t}\n}\n"
  },
  {
    "path": "go/08_stack/StackBasedOnArray.go",
    "content": "package _8_stack\n\nimport \"fmt\"\n\n/*\n基于数组实现的栈\n*/\n\ntype ArrayStack struct {\n\t//数据\n\tdata []interface{}\n\t//栈顶指针\n\ttop int\n}\n\nfunc NewArrayStack() *ArrayStack {\n\treturn &ArrayStack{\n\t\tdata: make([]interface{}, 0, 32),\n\t\ttop:  -1,\n\t}\n}\n\nfunc (this *ArrayStack) IsEmpty() bool {\n\tif this.top < 0 {\n\t\treturn true\n\t}\n\treturn false\n}\n\nfunc (this *ArrayStack) Push(v interface{}) {\n\tif this.top < 0 {\n\t\tthis.top = 0\n\t} else {\n\t\tthis.top += 1\n\t}\n\n\tif this.top > len(this.data)-1 {\n\t\tthis.data = append(this.data, v)\n\t} else {\n\t\tthis.data[this.top] = v\n\t}\n}\n\nfunc (this *ArrayStack) Pop() interface{} {\n\tif this.IsEmpty() {\n\t\treturn nil\n\t}\n\tv := this.data[this.top]\n\tthis.top -= 1\n\treturn v\n}\n\nfunc (this *ArrayStack) Top() interface{} {\n\tif this.IsEmpty() {\n\t\treturn nil\n\t}\n\treturn this.data[this.top]\n}\n\nfunc (this *ArrayStack) Flush() {\n\tthis.top = -1\n}\n\nfunc (this *ArrayStack) Print() {\n\tif this.IsEmpty() {\n\t\tfmt.Println(\"empty statck\")\n\t} else {\n\t\tfor i := this.top; i >= 0; i-- {\n\t\t\tfmt.Println(this.data[i])\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "go/08_stack/StackBasedOnArray_test.go",
    "content": "package _8_stack\n\nimport \"testing\"\n\nfunc TestArrayStack_Push(t *testing.T) {\n\ts := NewArrayStack()\n\ts.Push(1)\n\ts.Push(2)\n\tt.Log(s.Pop())\n\ts.Push(3)\n\tt.Log(s.Pop())\n\tt.Log(s.Pop())\n\ts.Push(4)\n\tt.Log(s.Pop())\n\ts.Print()\n}\n\nfunc TestArrayStack_Pop(t *testing.T) {\n\ts := NewArrayStack()\n\ts.Push(1)\n\ts.Push(2)\n\ts.Push(3)\n\ts.Print()\n\n\tt.Log(s.Pop())\n\tt.Log(s.Pop())\n\tt.Log(s.Pop())\n\tt.Log(s.Pop())\n\ts.Print()\n}\n\nfunc TestArrayStack_Top(t *testing.T) {\n\ts := NewArrayStack()\n\ts.Push(1)\n\ts.Push(2)\n\ts.Push(3)\n\n\tt.Log(s.Top())\n\ts.Pop()\n\tt.Log(s.Top())\n\ts.Pop()\n\tt.Log(s.Top())\n\ts.Pop()\n\tt.Log(s.Top())\n\ts.Pop()\n}\n"
  },
  {
    "path": "go/08_stack/StackBasedOnLinkedList.go",
    "content": "package _8_stack\n\nimport \"fmt\"\n\n/*\n基于链表实现的栈\n*/\ntype node struct {\n\tnext *node\n\tval  interface{}\n}\n\ntype LinkedListStack struct {\n\t//栈顶节点\n\ttopNode *node\n}\n\nfunc NewLinkedListStack() *LinkedListStack {\n\treturn &LinkedListStack{nil}\n}\n\nfunc (this *LinkedListStack) IsEmpty() bool {\n\treturn this.topNode == nil\n}\n\nfunc (this *LinkedListStack) Push(v interface{}) {\n\tthis.topNode = &node{next: this.topNode, val: v}\n}\n\nfunc (this *LinkedListStack) Pop() interface{} {\n\tif this.IsEmpty() {\n\t\treturn nil\n\t}\n\tv := this.topNode.val\n\tthis.topNode = this.topNode.next\n\treturn v\n}\n\nfunc (this *LinkedListStack) Top() interface{} {\n\tif this.IsEmpty() {\n\t\treturn nil\n\t}\n\treturn this.topNode.val\n}\n\nfunc (this *LinkedListStack) Flush() {\n\tthis.topNode = nil\n}\n\nfunc (this *LinkedListStack) Print() {\n\tif this.IsEmpty() {\n\t\tfmt.Println(\"empty stack\")\n\t} else {\n\t\tcur := this.topNode\n\t\tfor nil != cur {\n\t\t\tfmt.Println(cur.val)\n\t\t\tcur = cur.next\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "go/08_stack/StackBasedOnLinkedList_test.go",
    "content": "package _8_stack\n\nimport \"testing\"\n\nfunc TestLinkedListStack_Push(t *testing.T) {\n\ts := NewLinkedListStack()\n\ts.Push(1)\n\ts.Push(2)\n\ts.Push(3)\n\ts.Print()\n}\n\nfunc TestLinkedListStack_Pop(t *testing.T) {\n\ts := NewLinkedListStack()\n\ts.Push(1)\n\ts.Push(2)\n\ts.Push(3)\n\ts.Print()\n\n\tt.Log(s.Pop())\n\tt.Log(s.Pop())\n\tt.Log(s.Pop())\n\tt.Log(s.Pop())\n\ts.Print()\n}\n\nfunc TestLinkedListStack_Top(t *testing.T) {\n\ts := NewLinkedListStack()\n\ts.Push(1)\n\ts.Push(2)\n\ts.Push(3)\n\n\tt.Log(s.Top())\n\ts.Pop()\n\tt.Log(s.Top())\n\ts.Pop()\n\tt.Log(s.Top())\n\ts.Pop()\n\tt.Log(s.Top())\n\ts.Pop()\n}\n"
  },
  {
    "path": "go/08_stack/StatckInterface.go",
    "content": "package _8_stack\n\ntype Stack interface {\n\tPush(v interface{})\n\tPop() interface{}\n\tIsEmpty() bool\n\tTop() interface{}\n\tFlush()\n}\n"
  },
  {
    "path": "go/09_queue/CircularQueue.go",
    "content": "package _9_queue\n\nimport \"fmt\"\n\ntype CircularQueue struct {\n\tq        []interface{}\n\tcapacity int\n\thead     int\n\ttail     int\n}\n\nfunc NewCircularQueue(n int) *CircularQueue {\n\tif n == 0 {\n\t\treturn nil\n\t}\n\treturn &CircularQueue{make([]interface{}, n), n, 0, 0}\n}\n\n/*\n栈空条件：head==tail为true\n*/\nfunc (this *CircularQueue) IsEmpty() bool {\n\tif this.head == this.tail {\n\t\treturn true\n\t}\n\treturn false\n}\n\n/*\n栈满条件：(tail+1)%capacity==head为true\n*/\nfunc (this *CircularQueue) IsFull() bool {\n\tif this.head == (this.tail+1)%this.capacity {\n\t\treturn true\n\t}\n\treturn false\n}\n\nfunc (this *CircularQueue) EnQueue(v interface{}) bool {\n\tif this.IsFull() {\n\t\treturn false\n\t}\n\tthis.q[this.tail] = v\n\tthis.tail = (this.tail + 1) % this.capacity\n\treturn true\n}\n\nfunc (this *CircularQueue) DeQueue() interface{} {\n\tif this.IsEmpty() {\n\t\treturn nil\n\t}\n\tv := this.q[this.head]\n\tthis.head = (this.head + 1) % this.capacity\n\treturn v\n}\n\nfunc (this *CircularQueue) String() string {\n\tif this.IsEmpty() {\n\t\treturn \"empty queue\"\n\t}\n\tresult := \"head\"\n\tvar i = this.head\n\tfor true {\n\t\tresult += fmt.Sprintf(\"<-%+v\", this.q[i])\n\t\ti = (i + 1) % this.capacity\n\t\tif i == this.tail {\n\t\t\tbreak\n\t\t}\n\t}\n\tresult += \"<-tail\"\n\treturn result\n}\n"
  },
  {
    "path": "go/09_queue/CircularQueue_test.go",
    "content": "package _9_queue\n\nimport \"testing\"\n\nfunc TestCircularQueue_EnQueue(t *testing.T) {\n\tq := NewCircularQueue(5)\n\tq.EnQueue(1)\n\tq.EnQueue(2)\n\tq.EnQueue(3)\n\tq.EnQueue(4)\n\tq.EnQueue(5)\n\tq.EnQueue(6)\n\tt.Log(q)\n}\n\nfunc TestCircularQueue_DeQueue(t *testing.T) {\n\tq := NewCircularQueue(5)\n\tq.EnQueue(1)\n\tq.EnQueue(2)\n\tq.EnQueue(3)\n\tq.EnQueue(4)\n\tq.EnQueue(5)\n\tq.EnQueue(6)\n\tt.Log(q)\n\tt.Log(q.DeQueue())\n\tt.Log(q)\n\tq.EnQueue(5)\n\tt.Log(q)\n\tq.DeQueue()\n\tt.Log(q)\n\tq.DeQueue()\n\tt.Log(q)\n\tq.DeQueue()\n\tt.Log(q)\n\tq.DeQueue()\n\tt.Log(q)\n}\n"
  },
  {
    "path": "go/09_queue/QueueBasedOnArray.go",
    "content": "package _9_queue\n\nimport \"fmt\"\n\ntype ArrayQueue struct {\n\tq        []interface{}\n\tcapacity int\n\thead     int\n\ttail     int\n}\n\nfunc NewArrayQueue(n int) *ArrayQueue {\n\treturn &ArrayQueue{make([]interface{}, n), n, 0, 0}\n}\n\nfunc (this *ArrayQueue) EnQueue(v interface{}) bool {\n\tif this.tail == this.capacity {\n\t\treturn false\n\t}\n\tthis.q[this.tail] = v\n\tthis.tail++\n\treturn true\n}\n\nfunc (this *ArrayQueue) DeQueue() interface{} {\n\tif this.head == this.tail {\n\t\treturn nil\n\t}\n\tv := this.q[this.head]\n\tthis.head++\n\treturn v\n}\n\nfunc (this *ArrayQueue) String() string {\n\tif this.head == this.tail {\n\t\treturn \"empty queue\"\n\t}\n\tresult := \"head\"\n\tfor i := this.head; i <= this.tail-1; i++ {\n\t\tresult += fmt.Sprintf(\"<-%+v\", this.q[i])\n\t}\n\tresult += \"<-tail\"\n\treturn result\n}\n"
  },
  {
    "path": "go/09_queue/QueueBasedOnArray_test.go",
    "content": "package _9_queue\n\nimport \"testing\"\n\nfunc TestArrayQueue_EnQueue(t *testing.T) {\n\tq := NewArrayQueue(5)\n\tq.EnQueue(1)\n\tq.EnQueue(2)\n\tq.EnQueue(3)\n\tq.EnQueue(4)\n\tq.EnQueue(5)\n\tq.EnQueue(6)\n\tt.Log(q)\n}\n\nfunc TestArrayQueue_DeQueue(t *testing.T) {\n\tq := NewArrayQueue(5)\n\tq.EnQueue(1)\n\tq.EnQueue(2)\n\tq.EnQueue(3)\n\tq.EnQueue(4)\n\tq.EnQueue(5)\n\tq.EnQueue(6)\n\tt.Log(q)\n\tq.DeQueue()\n\tt.Log(q)\n\tq.DeQueue()\n\tt.Log(q)\n\tq.DeQueue()\n\tt.Log(q)\n\tq.DeQueue()\n\tt.Log(q)\n\tq.DeQueue()\n\tt.Log(q)\n}\n"
  },
  {
    "path": "go/09_queue/QueueBasedOnLinkedList.go",
    "content": "package _9_queue\n\nimport \"fmt\"\n\ntype ListNode struct {\n\tval  interface{}\n\tnext *ListNode\n}\n\ntype LinkedListQueue struct {\n\thead   *ListNode\n\ttail   *ListNode\n\tlength int\n}\n\nfunc NewLinkedListQueue() *LinkedListQueue {\n\treturn &LinkedListQueue{nil, nil, 0}\n}\n\nfunc (this *LinkedListQueue) EnQueue(v interface{}) {\n\tnode := &ListNode{v, nil}\n\tif nil == this.tail {\n\t\tthis.tail = node\n\t\tthis.head = node\n\t} else {\n\t\tthis.tail.next = node\n\t\tthis.tail = node\n\t}\n\tthis.length++\n}\n\nfunc (this *LinkedListQueue) DeQueue() interface{} {\n\tif this.head == nil {\n\t\treturn nil\n\t}\n\tv := this.head.val\n\tthis.head = this.head.next\n\tthis.length--\n\treturn v\n}\n\nfunc (this *LinkedListQueue) String() string {\n\tif this.head == nil {\n\t\treturn \"empty queue\"\n\t}\n\tresult := \"head<-\"\n\tfor cur := this.head; cur != nil; cur = cur.next {\n\t\tresult += fmt.Sprintf(\"<-%+v\", cur.val)\n\t}\n\tresult += \"<-tail\"\n\treturn result\n}\n"
  },
  {
    "path": "go/09_queue/QueueBasedOnLinkedList_test.go",
    "content": "package _9_queue\n\nimport \"testing\"\n\nfunc TestListQueue_EnQueue(t *testing.T) {\n\tq := NewLinkedListQueue()\n\tq.EnQueue(1)\n\tq.EnQueue(2)\n\tq.EnQueue(3)\n\tq.EnQueue(4)\n\tq.EnQueue(5)\n\tq.EnQueue(6)\n\tt.Log(q)\n}\n\nfunc TestListQueue_DeQueue(t *testing.T) {\n\tq := NewLinkedListQueue()\n\tq.EnQueue(1)\n\tq.EnQueue(2)\n\tq.EnQueue(3)\n\tq.EnQueue(4)\n\tq.EnQueue(5)\n\tq.EnQueue(6)\n\tt.Log(q)\n\tq.DeQueue()\n\tt.Log(q)\n\tq.DeQueue()\n\tt.Log(q)\n\tq.DeQueue()\n\tt.Log(q)\n\tq.DeQueue()\n\tt.Log(q)\n\tq.DeQueue()\n\tt.Log(q)\n}\n"
  },
  {
    "path": "go/10_recursion/Factorial.go",
    "content": "package Recursion\n\n// 迭代实现阶乘\ntype Fac struct {\n\tval map[int]int\n}\n\nfunc  NewFactorial(n int) *Fac {\n\treturn &Fac{\n\t\tmake(map[int]int, n),\n\t}\n}\n\nfunc (fac *Fac) Factorial(n int) int {\n\tif fac.val[n] != 0{\n\t\treturn fac.val[n]\n\t}\n\n\tif n <= 1{\n\t\tfac.val[n] = 1\n\t\treturn 1\n\t}else {\n\t\tres := n * fac.Factorial(n-1)\n\t\tfac.val[n] =res\n\t\treturn res\n\t}\n}\n\nfunc (fac *Fac) Print(n int )  {\n\tprintln(fac.val[n])\n}\n"
  },
  {
    "path": "go/10_recursion/Factorial_test.go",
    "content": "package Recursion\n\nimport \"testing\"\n\nfunc TestFac_Factorial(t *testing.T) {\n\tfac := NewFactorial(10)\n\tfor i:=1; i<15; i++{\n\t\tfac.Factorial(i)\n\t\tfac.Print(i)\n\t}\n}\n"
  },
  {
    "path": "go/10_recursion/Fibonacci.go",
    "content": "package Recursion\n\nimport \"fmt\"\n\n// 递归实现斐波那契数列\ntype Fibs struct {\n\tval map[int]int  // 使用字典存储结果\n}\n\nfunc NewFibs(n int) *Fibs  {\n\treturn &Fibs{\n\t\tmake(map[int]int, n),\n\t}\n}\n\nfunc (fib *Fibs)Fibonacci(n int) int {\n\tif fib.val[n] != 0{\n\t\treturn fib.val[n]\n\t}\n\tif n <= 1 {\n\t\tfib.val[1] = 1\n\t\treturn 1\n\t}else if n ==2{\n\t\tfib.val[2] = 1\n\t\treturn 1\n\t} else {\n\t\tres := fib.Fibonacci(n-1) + fib.Fibonacci(n-2)\n\t\tfib.val[n] = res\n\t\treturn res\n\t}\n}\n\nfunc (fib *Fibs)Print(n int) {\n\tfmt.Println(fib.val[n])\n}\n"
  },
  {
    "path": "go/10_recursion/Fibonacci_test.go",
    "content": "package Recursion\n\nimport \"testing\"\n\nfunc TestFibs_Fibonacci(t *testing.T) {\n\tfib := NewFibs(10)\n\tfor i:=1; i<15; i++{\n\t\tfib.Fibonacci(i)\n\t\tfib.Print(i)\n\t}\n}"
  },
  {
    "path": "go/10_recursion/RangAll.go",
    "content": "package Recursion\n\nimport (\n\t\"fmt\"\n)\n// 实现一组数据集合的全排列\ntype RangeType struct {\n\tvalue []interface{}\n}\n\nfunc NewRangeArray(n int) *RangeType  {\n\treturn &RangeType{\n\t\tmake([]interface{},n),\n\t}\n}\n\nfunc (slice *RangeType)RangeALL( start int)  {\n\tlen := len(slice.value)\n\tif start == len-1{\n\t\t// 如果已经是最后位置，直接将数组数据合并输出\n\t\tfmt.Println(slice.value)\n\t}\n\n\tfor i:=start; i<len; i++{\n\t\t// i = start 时输出自己\n\t\t// 如果i和start的值相同就没有必要交换\n\t\tif i==start || slice.value[i] != slice.value[start]{\n\t\t\t//交换当前这个与后面的位置\n\t\t\tslice.value[i], slice.value[start] = slice.value[start], slice.value[i]\n\t\t\t//递归处理索引+1\n\t\t\tslice.RangeALL(start+1)\n\t\t\t//换回来，因为是递归，如果不换回来会影响后面的操作，并且出现重复\n\t\t\tslice.value[i], slice.value[start] = slice.value[start], slice.value[i]\n\t\t}\n\t}\n}\n\n\n\n\n"
  },
  {
    "path": "go/10_recursion/RangAll_test.go",
    "content": "package Recursion\n\nimport \"testing\"\n\nfunc TestRangeALL(t *testing.T) {\n\tslice1 := NewRangeArray(4)\n\tfor i:=0; i<4; i++{\n\t\tslice1.value[i] = i+1\n\t}\n\tslice1.RangeALL(0)\n\n\tslice2 := NewRangeArray(3)\n\tslice2.value[0] = \"a\"\n\tslice2.value[1] = \"b\"\n\tslice2.value[2] = \"c\"\n\tslice2.RangeALL(0)\n\n}\n"
  },
  {
    "path": "go/11_sorts/Sort.go",
    "content": "package _1_sorts\n\n/*\n冒泡排序、插入排序、选择排序\n */\n\n//冒泡排序，a是数组，n表示数组大小\nfunc BubbleSort(a []int, n int) {\n\tif n <= 1 {\n\t\treturn\n\t}\n\tfor i := 0; i < n; i++ {\n\t\t// 提前退出标志\n\t\tflag := false\n\t\tfor j := 0; j < n-i-1; j++ {\n\t\t\tif a[j] > a[j+1] {\n\t\t\t\ta[j], a[j+1] = a[j+1], a[j]\n\t\t\t\t//此次冒泡有数据交换\n\t\t\t\tflag = true\n\t\t\t}\n\t\t}\n\t\t// 如果没有交换数据，提前退出\n\t\tif !flag {\n\t\t\tbreak\n\t\t}\n\t}\n}\n\n// 插入排序，a表示数组，n表示数组大小\nfunc InsertionSort(a []int, n int) {\n\tif n <= 1 {\n\t\treturn\n\t}\n\tfor i := 1; i < n; i++ {\n\t\tvalue := a[i]\n\t\tj := i - 1\n\t\t//查找要插入的位置并移动数据\n\t\tfor ; j >= 0; j-- {\n\t\t\tif a[j] > value {\n\t\t\t\ta[j+1] = a[j]\n\t\t\t} else {\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\ta[j+1] = value\n\t}\n}\n\n// 选择排序，a表示数组，n表示数组大小\nfunc SelectionSort(a []int, n int) {\n\tif n <= 1 {\n\t\treturn\n\t}\n\tfor i := 0; i < n; i++ {\n\t\t// 查找最小值\n\t\tminIndex := i\n\t\tfor j := i + 1; j < n; j++ {\n\t\t\tif a[j] < a[minIndex] {\n\t\t\t\tminIndex = j\n\t\t\t}\n\t\t}\n\t\t// 交换\n\t\ta[i], a[minIndex] = a[minIndex],a[i]\n\n\t}\n}\n"
  },
  {
    "path": "go/11_sorts/Sort_test.go",
    "content": "package _1_sorts\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n)\n\nfunc TestBubbleSort(t *testing.T) {\n\tarr := []int{1,5,9,6,3,7,5,10}\n\tfmt.Println(\"排序前：\",arr)\n\tBubbleSort(arr,len(arr))\n\tfmt.Println(\"排序后：\",arr)\n}\n\nfunc TestInsertionSort(t *testing.T) {\n\tarr := []int{1,5,9,6,3,7,5,10}\n\tfmt.Println(\"排序前：\",arr)\n\tInsertionSort(arr,len(arr))\n\tfmt.Println(\"排序后：\",arr)\n}\n\nfunc TestSelectionSort(t *testing.T) {\n\tarr := []int{1,5,9,6,3,7,5,10}\n\tfmt.Println(\"排序前：\",arr)\n\tSelectionSort(arr,len(arr))\n\tfmt.Println(\"排序后：\",arr)\n}\n"
  },
  {
    "path": "go/12_sorts/MergeSort.go",
    "content": "package _2_sorts\n\nfunc MergeSort(arr []int) {\n\tarrLen := len(arr)\n\tif arrLen <= 1 {\n\t\treturn\n\t}\n\n\tmergeSort(arr, 0, arrLen-1)\n}\n\nfunc mergeSort(arr []int, start, end int) {\n\tif start >= end {\n\t\treturn\n\t}\n\n\tmid := (start + end) / 2\n\tmergeSort(arr, start, mid)\n\tmergeSort(arr, mid+1, end)\n\tmerge(arr, start, mid, end)\n}\n\nfunc merge(arr []int, start, mid, end int) {\n\ttmpArr := make([]int, end-start+1)\n\n\ti := start\n\tj := mid + 1\n\tk := 0\n\tfor ; i <= mid && j <= end; k++ {\n\t\tif arr[i] <= arr[j] {\n\t\t\ttmpArr[k] = arr[i]\n\t\t\ti++\n\t\t} else {\n\t\t\ttmpArr[k] = arr[j]\n\t\t\tj++\n\t\t}\n\t}\n\n\tfor ; i <= mid; i++ {\n\t\ttmpArr[k] = arr[i]\n\t\tk++\n\t}\n\tfor ; j <= end; j++ {\n\t\ttmpArr[k] = arr[j]\n\t\tk++\n\t}\n\tcopy(arr[start:end+1], tmpArr)\n}\n"
  },
  {
    "path": "go/12_sorts/MergeSort_test.go",
    "content": "package _2_sorts\n\nimport \"testing\"\n\nfunc TestMergeSort(t *testing.T) {\n\tarr := []int{5, 4}\n\tMergeSort(arr)\n\tt.Log(arr)\n\n\tarr = []int{5, 4, 3, 2, 1}\n\tMergeSort(arr)\n\tt.Log(arr)\n}\n"
  },
  {
    "path": "go/12_sorts/QuickSort.go",
    "content": "package _2_sorts\n\n// QuickSort is quicksort methods for golang\nfunc QuickSort(arr []int) {\n\tseparateSort(arr, 0, len(arr)-1)\n}\n\nfunc separateSort(arr []int, start, end int) {\n\tif start >= end {\n\t\treturn\n\t}\n\ti := partition(arr, start, end)\n\tseparateSort(arr, start, i-1)\n\tseparateSort(arr, i+1, end)\n}\n\nfunc partition(arr []int, start, end int) int {\n\t// 选取最后一位当对比数字\n\tpivot := arr[end]\n\n\tvar i = start\n\tfor j := start; j < end; j++ {\n\t\tif arr[j] < pivot {\n\t\t\tif !(i == j) {\n\t\t\t\t// 交换位置\n\t\t\t\tarr[i], arr[j] = arr[j], arr[i]\n\t\t\t}\n\t\t\ti++\n\t\t}\n\t}\n\n\tarr[i], arr[end] = arr[end], arr[i]\n\n\treturn i\n}\n"
  },
  {
    "path": "go/12_sorts/QuickSort_test.go",
    "content": "package _2_sorts\n\nimport (\n\t\"math/rand\"\n\t\"testing\"\n)\n\nfunc createRandomArr(length int) []int {\n\tarr := make([]int, length, length)\n\tfor i := 0; i < length; i++ {\n\t\tarr[i] = rand.Intn(100)\n\t}\n\treturn arr\n}\n\nfunc TestQuickSort(t *testing.T) {\n\tarr := []int{5, 4}\n\tQuickSort(arr)\n\tt.Log(arr)\n\n\tarr = createRandomArr(100)\n\tQuickSort(arr)\n\tt.Log(arr)\n}\n"
  },
  {
    "path": "go/13_sorts/BucketSort.go",
    "content": "package LinearSort\n\nimport (\n\t\"algorithm/Sort\"\n\t\"fmt\"\n)\n\n// 桶排序\n\n// 获取待排序数组中的最大值\nfunc getMax(a []int)int{\n\tmax := a[0]\n\tfor i:=1; i<len(a); i++{\n\t\tif a[i] > max{\n\t\t\tmax = a[i]\n\t\t}\n\t}\n\treturn max\n}\n\n\nfunc BucketSort(a []int)  {\n\tnum := len(a)\n\tif num <= 1{\n\t\treturn\n\t}\n\tmax := getMax(a)\n\tbuckets := make([][]int, num)  // 二维切片\n\n\tindex :=0\n\tfor i:=0; i< num; i++{\n\t\tindex = a[i]*(num -1) / max  // 桶序号\n\t\tbuckets[index] = append(buckets[index],a[i]) // 加入对应的桶中\n\t}\n\n\ttmpPos := 0  // 标记数组位置\n\tfor i := 0; i < num; i++ {\n\t\tbucketLen := len(buckets[i])\n\t\tif bucketLen > 0{\n\t\t\tSort.QuickSort(buckets[i])  // 桶内做快速排序\n\t\t\tcopy(a[tmpPos:], buckets[i])\n\t\t\ttmpPos += bucketLen\n\t\t}\n\t}\n\n}\n\n\n// 桶排序简单实现\nfunc BucketSortSimple(source []int)  {\n\tif len(source)<=1{\n\t\treturn\n\t}\n\tarray := make([]int, getMax(source)+1)\n\tfor i:=0; i<len(source); i++{\n\t\tarray[source[i]] ++\n\t}\n    fmt.Println(array)\n\tc := make([]int,0)\n\tfor i:=0; i<len(array); i++{\n\t\tfor array[i] != 0 {\n            c = append(c, i)\n\t\t\tarray[i] --\n\t\t}\n\t}\n\tcopy(source,c)\n\n}\n\n"
  },
  {
    "path": "go/13_sorts/BucketSort_test.go",
    "content": "package LinearSort\n\nimport \"testing\"\n\nfunc TestBucketSort(t *testing.T) {\n\ta := []int{1,6,3,5,8,6,4}\n\tBucketSort(a)\n\tt.Log(a)\n}\n\nfunc TestBucketSortSimple(t *testing.T) {\n\ta := []int{1,6,3,5,8,6,4}\n\tBucketSortSimple(a)\n\tt.Log(a)\n}\n"
  },
  {
    "path": "go/14_sorts/CountingSort.go",
    "content": "package _4_sorts\n\nimport \"math\"\n\nfunc CountingSort(a []int, n int) {\n\tif n <= 1 {\n\t\treturn\n\t}\n\n\tvar max int = math.MinInt32\n\tfor i := range a {\n\t\tif a[i] > max {\n\t\t\tmax = a[i]\n\t\t}\n\t}\n\n\tc := make([]int, max+1)\n\tfor i := range a {\n\t\tc[a[i]]++\n\t}\n\tfor i := 1; i <= max; i++ {\n\t\tc[i] += c[i-1]\n\t}\n\n\tr := make([]int, n)\n\tfor i := n - 1; i >= 0; i-- {\n\t\tindex := c[a[i]] - 1\n\t\tr[index] = a[i]\n\t\tc[a[i]]--\n\t}\n\n\tcopy(a, r)\n}\n"
  },
  {
    "path": "go/14_sorts/CountingSort_test.go",
    "content": "package _4_sorts\n\nimport \"testing\"\n\nfunc TestCountingSort(t *testing.T) {\n\tarr := []int{5, 4}\n\tCountingSort(arr, len(arr))\n\tt.Log(arr)\n\n\tarr = []int{5, 4, 3, 2, 1}\n\tCountingSort(arr, len(arr))\n\tt.Log(arr)\n}\n"
  },
  {
    "path": "go/15_binarysearch/binarysearch.go",
    "content": "package _5_binarysearch\n\nfunc BinarySearch(a []int, v int) int {\n\tn := len(a)\n\tif n == 0 {\n\t\treturn -1\n\t}\n\n\tlow := 0\n\thigh := n - 1\n\tfor low <= high {\n\t\tmid := (low + high) / 2\n\t\tif a[mid] == v {\n\t\t\treturn mid\n\t\t} else if a[mid] > v {\n\t\t\thigh = mid - 1\n\t\t} else {\n\t\t\tlow = mid + 1\n\t\t}\n\t}\n\n\treturn -1\n}\n\nfunc BinarySearchRecursive(a []int, v int) int {\n\tn := len(a)\n\tif n == 0 {\n\t\treturn -1\n\t}\n\n\treturn bs(a, v, 0, n-1)\n}\n\nfunc bs(a []int, v int, low, high int) int {\n\tif low > high {\n\t\treturn -1\n\t}\n\n\tmid := (low + high) / 2\n\tif a[mid] == v {\n\t\treturn mid\n\t} else if a[mid] > v {\n\t\treturn bs(a, v, low, mid-1)\n\t} else {\n\t\treturn bs(a, v, mid+1, high)\n\t}\n}\n\n//查找第一个等于给定值的元素\nfunc BinarySearchFirst(a []int, v int) int {\n\tn := len(a)\n\tif n == 0 {\n\t\treturn -1\n\t}\n\n\tlow := 0\n\thigh := n - 1\n\tfor low <= high {\n\t\tmid := (low + high) >> 1\n\t\tif a[mid] > v {\n\t\t\thigh = mid - 1\n\t\t} else if a[mid] < v {\n\t\t\tlow = mid + 1\n\t\t} else {\n\t\t\tif mid == 0 || a[mid-1] != v {\n\t\t\t\treturn mid\n\t\t\t} else {\n\t\t\t\thigh = mid - 1\n\t\t\t}\n\t\t}\n\t}\n\n\treturn -1\n}\n\n//查找最后一个值等于给定值的元素\nfunc BinarySearchLast(a []int, v int) int {\n\tn := len(a)\n\tif n == 0 {\n\t\treturn -1\n\t}\n\n\tlow := 0\n\thigh := n - 1\n\tfor low <= high {\n\t\tmid := (low + high) >> 1\n\t\tif a[mid] > v {\n\t\t\thigh = mid - 1\n\t\t} else if a[mid] < v {\n\t\t\tlow = mid + 1\n\t\t} else {\n\t\t\tif mid == n-1 || a[mid+1] != v {\n\t\t\t\treturn mid\n\t\t\t} else {\n\t\t\t\tlow = mid + 1\n\t\t\t}\n\t\t}\n\t}\n\n\treturn -1\n}\n\n//查找第一个大于等于给定值的元素\nfunc BinarySearchFirstGT(a []int, v int) int {\n\tn := len(a)\n\tif n == 0 {\n\t\treturn -1\n\t}\n\n\tlow := 0\n\thigh := n - 1\n\tfor low <= high {\n\t\tmid := (high + low) >> 1\n\t\tif a[mid] > v {\n\t\t\thigh = mid - 1\n\t\t} else if a[mid] < v {\n\t\t\tlow = mid + 1\n\t\t} else {\n\t\t\tif mid != n-1 && a[mid+1] > v {\n\t\t\t\treturn mid + 1\n\t\t\t} else {\n\t\t\t\tlow = mid + 1\n\t\t\t}\n\t\t}\n\t}\n\n\treturn -1\n}\n\n//查找最后一个小于等于给定值的元素\nfunc BinarySearchLastLT(a []int, v int) int {\n\tn := len(a)\n\tif n == 0 {\n\t\treturn -1\n\t}\n\n\tlow := 0\n\thigh := n - 1\n\tfor low <= high {\n\t\tmid := (low + high) >> 1\n\t\tif a[mid] > v {\n\t\t\thigh = mid - 1\n\t\t} else if a[mid] < v {\n\t\t\tlow = mid + 1\n\t\t} else {\n\t\t\tif mid == 0 || a[mid-1] < v {\n\t\t\t\treturn mid - 1\n\t\t\t} else {\n\t\t\t\thigh = mid - 1\n\t\t\t}\n\t\t}\n\t}\n\n\treturn -1\n}\n"
  },
  {
    "path": "go/15_binarysearch/binarysearch_test.go",
    "content": "package _5_binarysearch\n\nimport \"testing\"\n\nfunc TestBinarySearch(t *testing.T) {\n\tvar a []int\n\n\ta = []int{1, 3, 5, 6, 8}\n\tif BinarySearch(a, 8) != 4 {\n\t\tt.Fatal(BinarySearch(a, 3))\n\t}\n\tif BinarySearch(a, 4) != -1 {\n\t\tt.Fatal(BinarySearch(a, 4))\n\t}\n}\n\nfunc TestBinarySearchRecursive(t *testing.T) {\n\tvar a []int\n\n\ta = []int{1, 3, 5, 6, 8}\n\tif BinarySearchRecursive(a, 8) != 4 {\n\t\tt.Fatal(BinarySearch(a, 3))\n\t}\n\tif BinarySearchRecursive(a, 4) != -1 {\n\t\tt.Fatal(BinarySearch(a, 4))\n\t}\n}\n\nfunc TestBinarySearchFirst(t *testing.T) {\n\tvar a []int\n\n\ta = []int{1, 2, 2, 2, 3, 4}\n\tif BinarySearchFirst(a, 2) != 1 {\n\t\tt.Fatal(BinarySearchFirst(a, 2))\n\t}\n\n\ta = []int{1, 2, 2, 2, 3, 4}\n\tif BinarySearchFirst(a, 3) != 4 {\n\t\tt.Fatal(BinarySearchFirst(a, 3))\n\t}\n}\n\nfunc TestBinarySearchLast(t *testing.T) {\n\tvar a []int\n\n\ta = []int{1, 2, 2, 2, 3, 4}\n\tif BinarySearchLast(a, 2) != 3 {\n\t\tt.Fatal(BinarySearchLast(a, 2))\n\t}\n\n\ta = []int{1, 2, 2, 2, 3, 4}\n\tif BinarySearchLast(a, 3) != 4 {\n\t\tt.Fatal(BinarySearchLast(a, 3))\n\t}\n}\n\nfunc TestBinarySearchFirstGT(t *testing.T) {\n\tvar a []int\n\n\ta = []int{1, 2, 2, 2, 3, 4}\n\tif BinarySearchFirstGT(a, 2) != 4 {\n\t\tt.Fatal(BinarySearchFirstGT(a, 2))\n\t}\n\n\ta = []int{1, 2, 2, 2, 3, 4}\n\tif BinarySearchFirstGT(a, 1) != 1 {\n\t\tt.Fatal(BinarySearchFirstGT(a, 1))\n\t}\n\n\ta = []int{1, 2, 2, 2, 3, 4}\n\tif BinarySearchFirstGT(a, 3) != 5 {\n\t\tt.Fatal(BinarySearchFirstGT(a, 3))\n\t}\n\n\ta = []int{1, 2, 2, 2, 3, 4}\n\tif BinarySearchFirstGT(a, 4) != -1 {\n\t\tt.Fatal(BinarySearchFirstGT(a, 4))\n\t}\n}\n\nfunc TestBinarySearchLastLT(t *testing.T) {\n\tvar a []int\n\n\ta = []int{1, 2, 2, 2, 3, 4}\n\tif BinarySearchLastLT(a, 2) != 0 {\n\t\tt.Fatal(BinarySearchLastLT(a, 2))\n\t}\n\n\ta = []int{1, 2, 2, 2, 3, 4}\n\tif BinarySearchLastLT(a, 1) != -1 {\n\t\tt.Fatal(BinarySearchLastLT(a, 1))\n\t}\n\n\ta = []int{1, 2, 2, 2, 3, 4}\n\tif BinarySearchLastLT(a, 3) != 3 {\n\t\tt.Fatal(BinarySearchLastLT(a, 3))\n\t}\n\n\ta = []int{1, 2, 2, 2, 3, 4}\n\tif BinarySearchLastLT(a, 4) != 4 {\n\t\tt.Fatal(BinarySearchLastLT(a, 4))\n\t}\n}\n"
  },
  {
    "path": "go/17_skiplist/skiplist.go",
    "content": "package _7_skiplist\n\nimport (\n\t\"fmt\"\n\t\"math\"\n\t\"math/rand\"\n)\n\nconst (\n\t//最高层数\n\tMAX_LEVEL = 16\n)\n\n//跳表节点结构体\ntype skipListNode struct {\n\t//跳表保存的值\n\tv interface{}\n\t//用于排序的分值\n\tscore int\n\t//层高\n\tlevel int\n\t//每层前进指针\n\tforwards []*skipListNode\n}\n\n//新建跳表节点\nfunc newSkipListNode(v interface{}, score, level int) *skipListNode {\n\treturn &skipListNode{v: v, score: score, forwards: make([]*skipListNode, level, level), level: level}\n}\n\n//跳表结构体\ntype SkipList struct {\n\t//跳表头结点\n\thead *skipListNode\n\t//跳表当前层数\n\tlevel int\n\t//跳表长度\n\tlength int\n}\n\n//实例化跳表对象\nfunc NewSkipList() *SkipList {\n\t//头结点，便于操作\n\thead := newSkipListNode(0, math.MinInt32, MAX_LEVEL)\n\treturn &SkipList{head, 1, 0}\n}\n\n//获取跳表长度\nfunc (sl *SkipList) Length() int {\n\treturn sl.length\n}\n\n//获取跳表层级\nfunc (sl *SkipList) Level() int {\n\treturn sl.level\n}\n\n//插入节点到跳表中\nfunc (sl *SkipList) Insert(v interface{}, score int) int {\n\tif nil == v {\n\t\treturn 1\n\t}\n\n\t//查找插入位置\n\tcur := sl.head\n\t//记录每层的路径\n\tupdate := [MAX_LEVEL]*skipListNode{}\n\ti := MAX_LEVEL - 1\n\tfor ; i >= 0; i-- {\n\t\tfor nil != cur.forwards[i] {\n\t\t\tif cur.forwards[i].v == v {\n\t\t\t\treturn 2\n\t\t\t}\n\t\t\tif cur.forwards[i].score > score {\n\t\t\t\tupdate[i] = cur\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tcur = cur.forwards[i]\n\t\t}\n\t\tif nil == cur.forwards[i] {\n\t\t\tupdate[i] = cur\n\t\t}\n\t}\n\n\t//通过随机算法获取该节点层数\n\tlevel := 1\n\tfor i := 1; i < MAX_LEVEL; i++ {\n\t\tif rand.Int31()%7 == 1 {\n\t\t\tlevel++\n\t\t}\n\t}\n\n\t//创建一个新的跳表节点\n\tnewNode := newSkipListNode(v, score, level)\n\n\t//原有节点连接\n\tfor i := 0; i <= level-1; i++ {\n\t\tnext := update[i].forwards[i]\n\t\tupdate[i].forwards[i] = newNode\n\t\tnewNode.forwards[i] = next\n\t}\n\n\t//如果当前节点的层数大于之前跳表的层数\n\t//更新当前跳表层数\n\tif level > sl.level {\n\t\tsl.level = level\n\t}\n\n\t//更新跳表长度\n\tsl.length++\n\n\treturn 0\n}\n\n//查找\nfunc (sl *SkipList) Find(v interface{}, score int) *skipListNode {\n\tif nil == v || sl.length == 0 {\n\t\treturn nil\n\t}\n\n\tcur := sl.head\n\tfor i := sl.level - 1; i >= 0; i-- {\n\t\tfor nil != cur.forwards[i] {\n\t\t\tif cur.forwards[i].score == score && cur.forwards[i].v == v {\n\t\t\t\treturn cur.forwards[i]\n\t\t\t} else if cur.forwards[i].score > score {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tcur = cur.forwards[i]\n\t\t}\n\t}\n\n\treturn nil\n}\n\n//删除节点\nfunc (sl *SkipList) Delete(v interface{}, score int) int {\n\tif nil == v {\n\t\treturn 1\n\t}\n\n\t//查找前驱节点\n\tcur := sl.head\n\t//记录前驱路径\n\tupdate := [MAX_LEVEL]*skipListNode{}\n\tfor i := sl.level - 1; i >= 0; i-- {\n\t\tupdate[i] = sl.head\n\t\tfor nil != cur.forwards[i] {\n\t\t\tif cur.forwards[i].score == score && cur.forwards[i].v == v {\n\t\t\t\tupdate[i] = cur\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tcur = cur.forwards[i]\n\t\t}\n\t}\n\n\tcur = update[0].forwards[0]\n\tfor i := cur.level - 1; i >= 0; i-- {\n\t\tif update[i] == sl.head && cur.forwards[i] == nil {\n\t\t\tsl.level = i\n\t\t}\n\n\t\tif nil == update[i].forwards[i] {\n\t\t\tupdate[i].forwards[i] = nil\n\t\t} else {\n\t\t\tupdate[i].forwards[i] = update[i].forwards[i].forwards[i]\n\t\t}\n\t}\n\n\tsl.length--\n\n\treturn 0\n}\n\nfunc (sl *SkipList) String() string {\n\treturn fmt.Sprintf(\"level:%+v, length:%+v\", sl.level, sl.length)\n}\n"
  },
  {
    "path": "go/17_skiplist/skiplist_test.go",
    "content": "package _7_skiplist\n\nimport \"testing\"\n\nfunc TestSkipList(t *testing.T) {\n\tsl := NewSkipList()\n\n\tsl.Insert(\"leo\", 95)\n\tt.Log(sl.head.forwards[0])\n\tt.Log(sl.head.forwards[0].forwards[0])\n\tt.Log(sl)\n\tt.Log(\"-----------------------------\")\n\n\tsl.Insert(\"jack\", 88)\n\tt.Log(sl.head.forwards[0])\n\tt.Log(sl.head.forwards[0].forwards[0])\n\tt.Log(sl.head.forwards[0].forwards[0].forwards[0])\n\tt.Log(sl)\n\tt.Log(\"-----------------------------\")\n\n\tsl.Insert(\"lily\", 100)\n\tt.Log(sl.head.forwards[0])\n\tt.Log(sl.head.forwards[0].forwards[0])\n\tt.Log(sl.head.forwards[0].forwards[0].forwards[0])\n\tt.Log(sl.head.forwards[0].forwards[0].forwards[0].forwards[0])\n\tt.Log(sl)\n\tt.Log(\"-----------------------------\")\n\n\tt.Log(sl.Find(\"jack\", 88))\n\tt.Log(\"-----------------------------\")\n\n\tsl.Delete(\"leo\", 95)\n\tt.Log(sl.head.forwards[0])\n\tt.Log(sl.head.forwards[0].forwards[0])\n\tt.Log(sl.head.forwards[0].forwards[0].forwards[0])\n\tt.Log(sl)\n\tt.Log(\"-----------------------------\")\n}\n"
  },
  {
    "path": "go/20_lru/lru_cache.go",
    "content": "package lru_cache\n\nconst (\n\thostbit = uint64(^uint(0)) == ^uint64(0)\n\tLENGTH  = 100\n)\n\ntype lruNode struct {\n\tprev *lruNode\n\tnext *lruNode\n\n\tkey   int // lru key\n\tvalue int // lru value\n\n\thnext *lruNode // 拉链\n}\n\ntype LRUCache struct {\n\tnode []lruNode // hash list\n\n\thead *lruNode // lru head node\n\ttail *lruNode // lru tail node\n\n\tcapacity int //\n\tused     int //\n}\n\nfunc Constructor(capacity int) LRUCache {\n\treturn LRUCache{\n\t\tnode:     make([]lruNode, LENGTH),\n\t\thead:     nil,\n\t\ttail:     nil,\n\t\tcapacity: capacity,\n\t\tused:     0,\n\t}\n}\n\nfunc (this *LRUCache) Get(key int) int {\n\tif this.tail == nil {\n\t\treturn -1\n\t}\n\n\tif tmp := this.searchNode(key); tmp != nil {\n\t\tthis.moveToTail(tmp)\n\t\treturn tmp.value\n\t}\n\treturn -1\n}\n\nfunc (this *LRUCache) Put(key int, value int) {\n\t// 1. 首次插入数据\n\t// 2. 插入数据不在 LRU 中\n\t// 3. 插入数据在 LRU 中\n\t// 4. 插入数据不在 LRU 中, 并且 LRU 已满\n\n\tif tmp := this.searchNode(key); tmp != nil {\n\t\ttmp.value = value\n\t\tthis.moveToTail(tmp)\n\t\treturn\n\t}\n\tthis.addNode(key, value)\n\n\tif this.used > this.capacity {\n\t\tthis.delNode()\n\t}\n}\n\nfunc (this *LRUCache) addNode(key int, value int) {\n\tnewNode := &lruNode{\n\t\tkey:   key,\n\t\tvalue: value,\n\t}\n\n\ttmp := &this.node[hash(key)]\n\tnewNode.hnext = tmp.hnext\n\ttmp.hnext = newNode\n\tthis.used++\n\n\tif this.tail == nil {\n\t\tthis.tail, this.head = newNode, newNode\n\t\treturn\n\t}\n\tthis.tail.next = newNode\n\tnewNode.prev = this.tail\n\tthis.tail = newNode\n}\n\nfunc (this *LRUCache) delNode() {\n\tif this.head == nil {\n\t\treturn\n\t}\n\tprev := &this.node[hash(this.head.key)]\n\ttmp := prev.hnext\n\n\tfor tmp != nil && tmp.key != this.head.key {\n\t\tprev = tmp\n\t\ttmp = tmp.hnext\n\t}\n\tif tmp == nil {\n\t\treturn\n\t}\n\tprev.hnext = tmp.hnext\n\tthis.head = this.head.next\n\tthis.head.prev = nil\n\tthis.used--\n}\n\nfunc (this *LRUCache) searchNode(key int) *lruNode {\n\tif this.tail == nil {\n\t\treturn nil\n\t}\n\n\t// 查找\n\ttmp := this.node[hash(key)].hnext\n\tfor tmp != nil {\n\t\tif tmp.key == key {\n\t\t\treturn tmp\n\t\t}\n\t\ttmp = tmp.hnext\n\t}\n\treturn nil\n}\n\nfunc (this *LRUCache) moveToTail(node *lruNode) {\n\tif this.tail == node {\n\t\treturn\n\t}\n\tif this.head == node {\n\t\tthis.head = node.next\n\t\tthis.head.prev = nil\n\t} else {\n\t\tnode.next.prev = node.prev\n\t\tnode.prev.next = node.next\n\t}\n\n\tnode.next = nil\n\tthis.tail.next = node\n\tnode.prev = this.tail\n\n\tthis.tail = node\n}\n\nfunc hash(key int) int {\n\tif hostbit {\n\t\treturn (key ^ (key >> 32)) & (LENGTH - 1)\n\t}\n\treturn (key ^ (key >> 16)) & (LENGTH - 1)\n}\n"
  },
  {
    "path": "go/20_lru/lru_cache_test.go",
    "content": "package lru_cache\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc Test_Hostbit(t *testing.T) {\n\tfmt.Println(hostbit)\n}\n\nfunc Test_LRU(t *testing.T) {\n\tlru := Constructor(2)\n\n\tlru.Put(1, 1)\n\tlru.Put(2, 2)\n\tassert.Equal(t, lru.Get(1), 1)  // returns 1\n\tlru.Put(3, 3)                   // evicts key 2\n\tassert.Equal(t, lru.Get(2), -1) // returns -1 (not found)\n\tlru.Put(4, 4)                   // evicts key 1\n\tassert.Equal(t, lru.Get(1), -1) // returns -1 (not found)\n\tassert.Equal(t, lru.Get(3), 3)  // returns 3\n\tassert.Equal(t, lru.Get(4), 4)  // returns 4\n}\n\nfunc Test_LRU_PutGet(t *testing.T) {\n\tlru := Constructor(1)\n\n\tlru.Put(1, 2)\n\tassert.Equal(t, lru.Get(1), 2) // returns 2\n}\n\nfunc Test_LRU_PutGetPutGetGet(t *testing.T) {\n\tlru := Constructor(1)\n\n\tlru.Put(2, 1)\n\tassert.Equal(t, lru.Get(2), 1) // returns 1\n\tlru.Put(3, 2)\n\tassert.Equal(t, lru.Get(2), -1) // returns -1\n\tassert.Equal(t, lru.Get(3), 2)  // returns 2\n}\n\nfunc Test_LRU_PPGPPG(t *testing.T) {\n\tlru := Constructor(2)\n\n\tlru.Put(2, 1)\n\tlru.Put(2, 2)\n\tassert.Equal(t, lru.Get(2), 2) // returns 2\n\tlru.Put(1, 4)\n\tlru.Put(4, 1)\n\tassert.Equal(t, lru.Get(2), -1) // returns -1\n\tassert.Equal(t, lru.Get(3), -1) // returns -1\n}\n\nfunc Test_LRU_PPGPPG_2(t *testing.T) {\n\tlru := Constructor(2)\n\n\tlru.Put(2, 1)\n\tlru.Put(2, 2)\n\tassert.Equal(t, lru.Get(2), 2) // returns 2\n\tlru.Put(1, 1)\n\tlru.Put(4, 1)\n\tassert.Equal(t, lru.Get(2), -1) // returns -1\n\tassert.Equal(t, lru.Get(3), -1) // returns -1\n}\n"
  },
  {
    "path": "go/23_binarytree/binarytree.go",
    "content": "package binarytree\n\ntype TreeNode struct {\n\tVal   int\n\tLeft  *TreeNode\n\tRight *TreeNode\n}\n\nfunc preOrderTraversal(root *TreeNode) []int {\n\tif root == nil {\n\t\treturn nil\n\t}\n\tif root.Left == nil && root.Right == nil {\n\t\treturn []int{root.Val}\n\t}\n\tvar stack []*TreeNode\n\tvar res []int\n\tstack = append(stack, root)\n\tfor len(stack) != 0 {\n\t\te := stack[len(stack)-1]\n\t\tstack = stack[:len(stack)-1]\n\t\tres = append(res, e.Val)\n\t\tif e.Right != nil {\n\t\t\tstack = append(stack, e.Right)\n\t\t}\n\t\tif e.Left != nil {\n\t\t\tstack = append(stack, e.Left)\n\t\t}\n\t}\n\treturn res\n}\n\nfunc inOrderTraversal(root *TreeNode) []int {\n\tif root == nil {\n\t\treturn nil\n\t}\n\tif root.Left == nil && root.Right == nil {\n\t\treturn []int{root.Val}\n\t}\n\tres := inOrderTraversal(root.Left)\n\tres = append(res, root.Val)\n\tres = append(res, inOrderTraversal(root.Right)...)\n\n\treturn res\n}\n\nfunc postOrderTraversal(root *TreeNode) []int {\n\tif root == nil {\n\t\treturn nil\n\t}\n\tvar res []int\n\tif root.Left != nil {\n\t\tlres := postOrderTraversal(root.Left)\n\t\tif len(lres) > 0 {\n\t\t\tres = append(res, lres...)\n\t\t}\n\t}\n\tif root.Right != nil {\n\t\trres := postOrderTraversal(root.Right)\n\t\tif len(rres) > 0 {\n\t\t\tres = append(res, rres...)\n\t\t}\n\t}\n\tres = append(res, root.Val)\n\treturn res\n}\n"
  },
  {
    "path": "go/23_binarytree/binarytree_test.go",
    "content": "package binarytree\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nvar tcs = []struct {\n\tpre, in, post []int\n}{\n\n\t{\n\t\t[]int{1, 2, 3},\n\t\t[]int{1, 3, 2},\n\t\t[]int{3, 2, 1},\n\t},\n\n\t{\n\t\t[]int{1, 2, 4, 5, 3, 6, 7},\n\t\t[]int{4, 2, 5, 1, 6, 3, 7},\n\t\t[]int{4, 5, 2, 6, 7, 3, 1},\n\t},\n\t// 可以有多个 testCase\n}\n\nfunc PreIn2Tree(pre, in []int) *TreeNode {\n\tif len(pre) != len(in) {\n\t\tpanic(\"preIn2Tree 中两个切片的长度不相等\")\n\t}\n\n\tif len(in) == 0 {\n\t\treturn nil\n\t}\n\n\tres := &TreeNode{\n\t\tVal: pre[0],\n\t}\n\n\tif len(in) == 1 {\n\t\treturn res\n\t}\n\n\tidx := indexOf(res.Val, in)\n\n\tres.Left = PreIn2Tree(pre[1:idx+1], in[:idx])\n\tres.Right = PreIn2Tree(pre[idx+1:], in[idx+1:])\n\n\treturn res\n}\n\nfunc indexOf(val int, nums []int) int {\n\tfor i, v := range nums {\n\t\tif v == val {\n\t\t\treturn i\n\t\t}\n\t}\n\n\treturn 0\n}\n\nfunc Test_preOrderTraversal(t *testing.T) {\n\tast := assert.New(t)\n\n\tfor _, tc := range tcs {\n\t\tfmt.Printf(\"~~%v~~\\n\", tc)\n\n\t\troot := PreIn2Tree(tc.pre, tc.in)\n\t\tast.Equal(tc.pre, preOrderTraversal(root), \"输入:%v\", tc)\n\t}\n}\n\nfunc Test_inOrderTraversal(t *testing.T) {\n\tast := assert.New(t)\n\n\tfor _, tc := range tcs {\n\t\tfmt.Printf(\"~~%v~~\\n\", tc)\n\n\t\troot := PreIn2Tree(tc.pre, tc.in)\n\t\tast.Equal(tc.in, inOrderTraversal(root), \"输入:%v\", tc)\n\t}\n}\n\nfunc Test_postOrderTraversal(t *testing.T) {\n\tast := assert.New(t)\n\n\tfor _, tc := range tcs {\n\t\tfmt.Printf(\"~~%v~~\\n\", tc)\n\n\t\troot := PreIn2Tree(tc.pre, tc.in)\n\t\tast.Equal(tc.post, postOrderTraversal(root), \"输入:%v\", tc)\n\t}\n}\n"
  },
  {
    "path": "go/24_tree/BinarySearchTree.go",
    "content": "package _4_tree\n\ntype BST struct {\n\t*BinaryTree\n\t//比对函数，0:v==nodeV,正数:v>nodeV,负数:v<nodeV\n\tcompareFunc func(v, nodeV interface{}) int\n}\n\nfunc NewBST(rootV interface{}, compareFunc func(v, nodeV interface{}) int) *BST {\n\tif nil == compareFunc {\n\t\treturn nil\n\t}\n\treturn &BST{BinaryTree: NewBinaryTree(rootV), compareFunc: compareFunc}\n}\n\nfunc (this *BST) Find(v interface{}) *Node {\n\tp := this.root\n\tfor nil != p {\n\t\tcompareResult := this.compareFunc(v, p.data)\n\t\tif compareResult == 0 {\n\t\t\treturn p\n\t\t} else if compareResult > 0 { //v > nodeV\n\t\t\tp = p.right\n\t\t} else { //v < nodeV\n\t\t\tp = p.left\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (this *BST) Insert(v interface{}) bool {\n\tp := this.root\n\tfor nil != p {\n\t\tcompareResult := this.compareFunc(v, p.data)\n\t\tif compareResult == 0 {\n\t\t\treturn false\n\t\t} else if compareResult > 0 {\n\t\t\tif nil == p.right {\n\t\t\t\tp.right = NewNode(v)\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tp = p.right\n\t\t} else {\n\t\t\tif nil == p.left {\n\t\t\t\tp.left = NewNode(v)\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tp = p.left\n\t\t}\n\t}\n\treturn true\n}\n\nfunc (this *BST) Delete(v interface{}) bool {\n\tvar pp *Node = nil\n\tp := this.root\n\tdeleteLeft := false\n\tfor nil != p {\n\t\tcompareResult := this.compareFunc(v, p.data)\n\t\tif compareResult > 0 {\n\t\t\tpp = p\n\t\t\tp = p.right\n\t\t\tdeleteLeft = false\n\t\t} else if compareResult < 0 {\n\t\t\tpp = p\n\t\t\tp = p.left\n\t\t\tdeleteLeft = true\n\t\t} else {\n\t\t\tbreak\n\t\t}\n\t}\n\n\tif nil == p { //需要删除的节点不存在\n\t\treturn false\n\t} else if nil == p.left && nil == p.right { //删除的是一个叶子节点\n\t\tif nil != pp {\n\t\t\tif deleteLeft {\n\t\t\t\tpp.left = nil\n\t\t\t} else {\n\t\t\t\tpp.right = nil\n\t\t\t}\n\t\t} else { //根节点\n\t\t\tthis.root = nil\n\t\t}\n\t} else if nil != p.right { //删除的是一个有右孩子，不一定有左孩子的节点\n\t\t//找到p节点右孩子的最小节点\n\t\tpq := p\n\t\tq := p.right //向右走一步\n\t\tfromRight := true\n\t\tfor nil != q.left { //向左走到底\n\t\t\tpq = q\n\t\t\tq = q.left\n\t\t\tfromRight = false\n\t\t}\n\t\tif fromRight {\n\t\t\tpq.right = nil\n\t\t} else {\n\t\t\tpq.left = nil\n\t\t}\n\t\tq.left = p.left\n\t\tq.right = p.right\n\t\tif nil == pp { //根节点被删除\n\t\t\tthis.root = q\n\t\t} else {\n\t\t\tif deleteLeft {\n\t\t\t\tpq.left = q\n\t\t\t} else {\n\t\t\t\tpq.right = q\n\t\t\t}\n\t\t}\n\t} else { //删除的是一个只有左孩子的节点\n\t\tif nil != pp {\n\t\t\tif deleteLeft {\n\t\t\t\tpp.left = p.left\n\t\t\t} else {\n\t\t\t\tpp.right = p.left\n\t\t\t}\n\t\t} else {\n\t\t\tif deleteLeft {\n\t\t\t\tthis.root = p.left\n\t\t\t} else {\n\t\t\t\tthis.root = p.left\n\t\t\t}\n\t\t}\n\t}\n\n\treturn true\n\n}\n\nfunc (this *BST) Min() *Node {\n\tp := this.root\n\tfor nil != p.left {\n\t\tp = p.left\n\t}\n\treturn p\n}\n\nfunc (this *BST) Max() *Node {\n\tp := this.root\n\tfor nil != p.right {\n\t\tp = p.right\n\t}\n\treturn p\n}\n"
  },
  {
    "path": "go/24_tree/BinarySearchTree_test.go",
    "content": "package _4_tree\n\nimport \"testing\"\n\nvar compareFunc = func(v, nodeV interface{}) int {\n\tvInt := v.(int)\n\tnodeVInt := nodeV.(int)\n\n\tif vInt > nodeVInt {\n\t\treturn 1\n\t} else if vInt < nodeVInt {\n\t\treturn -1\n\t}\n\treturn 0\n}\n\nfunc TestBST_Find(t *testing.T) {\n\tbst := NewBST(1, compareFunc)\n\n\tbst.Insert(3)\n\tbst.Insert(1)\n\tbst.Insert(2)\n\tbst.Insert(7)\n\tbst.Insert(5)\n\n\tt.Log(bst.Find(2))\n}\n\nfunc TestBST_Insert(t *testing.T) {\n\tbst := NewBST(1, compareFunc)\n\n\tbst.Insert(3)\n\tbst.Insert(1)\n\tbst.Insert(2)\n\tbst.Insert(7)\n\tbst.Insert(5)\n\n\tbst.InOrderTraverse()\n}\n\nfunc TestBST_Min(t *testing.T) {\n\tbst := NewBST(1, compareFunc)\n\n\tbst.Insert(3)\n\tbst.Insert(1)\n\tbst.Insert(2)\n\tbst.Insert(7)\n\tbst.Insert(5)\n\n\tt.Log(bst.Min())\n}\n\nfunc TestBST_Max(t *testing.T) {\n\tbst := NewBST(1, compareFunc)\n\n\tbst.Insert(3)\n\tbst.Insert(1)\n\tbst.Insert(2)\n\tbst.Insert(7)\n\tbst.Insert(5)\n\n\tt.Log(bst.Max())\n}\n\nfunc TestBST_DeleteA(t *testing.T) {\n\tbst := NewBST(1, compareFunc)\n\n\tbst.Insert(3)\n\tbst.Insert(2)\n\tbst.Insert(7)\n\tbst.Insert(5)\n\n\tt.Log(bst.Delete(7))\n\n\tbst.InOrderTraverse()\n}\n\nfunc TestBST_DeleteB(t *testing.T) {\n\tbst := NewBST(1, compareFunc)\n\n\tt.Log(bst.Delete(1))\n\tt.Log(bst.root)\n\n\tbst.InOrderTraverse()\n}\n\nfunc TestBST_DeleteC(t *testing.T) {\n\tbst := NewBST(1, compareFunc)\n\n\tbst.Insert(3)\n\tbst.Insert(2)\n\tbst.Insert(7)\n\tbst.Insert(5)\n\n\tt.Log(bst.Delete(1))\n\n\tbst.InOrderTraverse()\n}\n\nfunc TestBST_DeleteD(t *testing.T) {\n\tbst := NewBST(1, compareFunc)\n\n\tbst.Insert(3)\n\tbst.Insert(2)\n\tbst.Insert(5)\n\n\tt.Log(bst.Delete(1))\n\n\tbst.InOrderTraverse()\n}\nfunc TestBST_DeleteE(t *testing.T) {\n\tbst := NewBST(5, compareFunc)\n\n\tbst.Insert(3)\n\tbst.Insert(2)\n\tbst.Insert(4)\n\tbst.Insert(1)\n\n\tt.Log(bst.Delete(5))\n\tbst.InOrderTraverse()\n}\n\nfunc TestBST_DeleteF(t *testing.T) {\n\tbst := NewBST(5, compareFunc)\n\n\tbst.Insert(3)\n\tbst.Insert(2)\n\tbst.Insert(4)\n\tbst.Insert(1)\n\n\tt.Log(bst.Delete(2))\n\tbst.InOrderTraverse()\n}\n\nfunc TestBST_DeleteG(t *testing.T) {\n\tbst := NewBST(5, compareFunc)\n\n\tbst.Insert(3)\n\tbst.Insert(2)\n\tbst.Insert(4)\n\tbst.Insert(1)\n\n\tt.Log(bst.Delete(1))\n\tbst.InOrderTraverse()\n}\n"
  },
  {
    "path": "go/24_tree/BinaryTree.go",
    "content": "package _4_tree\n\nimport \"fmt\"\n\ntype BinaryTree struct {\n\troot *Node\n}\n\nfunc NewBinaryTree(rootV interface{}) *BinaryTree {\n\treturn &BinaryTree{NewNode(rootV)}\n}\n\nfunc (this *BinaryTree) InOrderTraverse() {\n\tp := this.root\n\ts := NewArrayStack()\n\n\tfor !s.IsEmpty() || nil != p {\n\t\tif nil != p {\n\t\t\ts.Push(p)\n\t\t\tp = p.left\n\t\t} else {\n\t\t\ttmp := s.Pop().(*Node)\n\t\t\tfmt.Printf(\"%+v \", tmp.data)\n\t\t\tp = tmp.right\n\t\t}\n\t}\n\tfmt.Println()\n}\n\nfunc (this *BinaryTree) PreOrderTraverse() {\n\tp := this.root\n\ts := NewArrayStack()\n\n\tfor !s.IsEmpty() || nil != p {\n\t\tif nil != p {\n\t\t\tfmt.Printf(\"%+v \", p.data)\n\t\t\ts.Push(p)\n\t\t\tp = p.left\n\t\t} else {\n\t\t\tp = s.Pop().(*Node).right\n\t\t}\n\t}\n\n\tfmt.Println()\n}\n\nfunc (this *BinaryTree) PostOrderTraverse() {\n\ts1 := NewArrayStack()\n\ts2 := NewArrayStack()\n\ts1.Push(this.root)\n\tfor !s1.IsEmpty() {\n\t\tp := s1.Pop().(*Node)\n\t\ts2.Push(p)\n\t\tif nil != p.left {\n\t\t\ts1.Push(p.left)\n\t\t}\n\t\tif nil != p.right {\n\t\t\ts1.Push(p.right)\n\t\t}\n\t}\n\n\tfor !s2.IsEmpty() {\n\t\tfmt.Printf(\"%+v \", s2.Pop().(*Node).data)\n\t}\n}\n\n//use one stack, pre cursor to traverse from post order\nfunc (this *BinaryTree) PostOrderTraverse2() {\n\tr := this.root\n\ts := NewArrayStack()\n\n\t//point to last visit node\n\tvar pre *Node\n\n\ts.Push(r)\n\n\tfor !s.IsEmpty() {\n\t\tr = s.Top().(*Node)\n\t\tif (r.left == nil && r.right == nil) ||\n\t\t\t(pre != nil && (pre == r.left || pre == r.right)) {\n\n\t\t\tfmt.Printf(\"%+v \", r.data)\n\t\t\ts.Pop()\n\t\t\tpre = r\n\t\t} else {\n\t\t\tif r.right != nil {\n\t\t\t\ts.Push(r.right)\n\t\t\t}\n\n\t\t\tif r.left != nil {\n\t\t\t\ts.Push(r.left)\n\t\t\t}\n\n\t\t}\n\n\t}\n}\n"
  },
  {
    "path": "go/24_tree/BinaryTree_test.go",
    "content": "package _4_tree\n\nimport \"testing\"\n\nfunc TestBinaryTree_InOrderTraverse(t *testing.T) {\n\tbinaryTree := NewBinaryTree(1)\n\tbinaryTree.root.left = NewNode(3)\n\tbinaryTree.root.right = NewNode(4)\n\tbinaryTree.root.right.left = NewNode(5)\n\n\tbinaryTree.InOrderTraverse()\n}\n\nfunc TestBinaryTree_PreOrderTraverse(t *testing.T) {\n\tbinaryTree := NewBinaryTree(1)\n\tbinaryTree.root.left = NewNode(3)\n\tbinaryTree.root.right = NewNode(4)\n\tbinaryTree.root.right.left = NewNode(5)\n\n\tbinaryTree.PreOrderTraverse()\n}\n\nfunc TestBinaryTree_PostOrderTraverse(t *testing.T) {\n\tbinaryTree := NewBinaryTree(1)\n\tbinaryTree.root.left = NewNode(3)\n\tbinaryTree.root.right = NewNode(4)\n\tbinaryTree.root.right.left = NewNode(5)\n\n\tbinaryTree.PostOrderTraverse()\n}\n\nfunc TestBinaryTree_PostOrderTraverse2(t *testing.T) {\n\tbinaryTree := NewBinaryTree(1)\n\tbinaryTree.root.left = NewNode(3)\n\tbinaryTree.root.right = NewNode(4)\n\tbinaryTree.root.right.left = NewNode(5)\n\n\tbinaryTree.PostOrderTraverse2()\n}\n"
  },
  {
    "path": "go/24_tree/StackBasedOnArray.go",
    "content": "package _4_tree\n\nimport \"fmt\"\n\n/*\n基于数组实现的栈\n*/\n\ntype ArrayStack struct {\n\t//数据\n\tdata []interface{}\n\t//栈顶指针\n\ttop int\n}\n\nfunc NewArrayStack() *ArrayStack {\n\treturn &ArrayStack{\n\t\tdata: make([]interface{}, 0, 32),\n\t\ttop:  -1,\n\t}\n}\n\nfunc (this *ArrayStack) IsEmpty() bool {\n\tif this.top < 0 {\n\t\treturn true\n\t}\n\treturn false\n}\n\nfunc (this *ArrayStack) Push(v interface{}) {\n\tif this.top < 0 {\n\t\tthis.top = 0\n\t} else {\n\t\tthis.top += 1\n\t}\n\n\tif this.top > len(this.data)-1 {\n\t\tthis.data = append(this.data, v)\n\t} else {\n\t\tthis.data[this.top] = v\n\t}\n}\n\nfunc (this *ArrayStack) Pop() interface{} {\n\tif this.IsEmpty() {\n\t\treturn nil\n\t}\n\tv := this.data[this.top]\n\tthis.top -= 1\n\treturn v\n}\n\nfunc (this *ArrayStack) Top() interface{} {\n\tif this.IsEmpty() {\n\t\treturn nil\n\t}\n\treturn this.data[this.top]\n}\n\nfunc (this *ArrayStack) Flush() {\n\tthis.top = -1\n}\n\nfunc (this *ArrayStack) Print() {\n\tif this.IsEmpty() {\n\t\tfmt.Println(\"empty statck\")\n\t} else {\n\t\tfor i := this.top; i >= 0; i-- {\n\t\t\tfmt.Println(this.data[i])\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "go/24_tree/TreeNode.go",
    "content": "package _4_tree\n\nimport \"fmt\"\n\ntype Node struct {\n\tdata  interface{}\n\tleft  *Node\n\tright *Node\n}\n\nfunc NewNode(data interface{}) *Node {\n\treturn &Node{data: data}\n}\n\nfunc (this *Node) String() string {\n\treturn fmt.Sprintf(\"v:%+v, left:%+v, right:%+v\", this.data, this.left, this.right)\n}\n"
  },
  {
    "path": "go/28_heap/heap.go",
    "content": "package heap\n\ntype Heap struct {\n\ta     []int\n\tn     int\n\tcount int\n}\n\n//init heap\nfunc NewHeap(capacity int) *Heap {\n\theap := &Heap{}\n\theap.n = capacity\n\theap.a = make([]int, capacity+1)\n\theap.count = 0\n\treturn heap\n}\n\n//top-max heap -> heapify from down to up\nfunc (heap *Heap) insert(data int) {\n\t//defensive\n\tif heap.count == heap.n {\n\t\treturn\n\t}\n\n\theap.count++\n\theap.a[heap.count] = data\n\n\t//compare with parent node\n\ti := heap.count\n\tparent := i / 2\n\tfor parent > 0 && heap.a[parent] < heap.a[i] {\n\t\tswap(heap.a, parent, i)\n\t\ti = parent\n\t\tparent = i / 2\n\t}\n}\n\n//heapfify from up to down\nfunc (heap *Heap) removeMax() {\n\n\t//defensive\n\tif heap.count == 0 {\n\t\treturn\n\t}\n\n\t//swap max and last\n\tswap(heap.a, 1, heap.count)\n\theap.count--\n\n\t//heapify from up to down\n\theapifyUpToDown(heap.a, heap.count)\n}\n\n//heapify\nfunc heapifyUpToDown(a []int, count int) {\n\n\tfor i := 1; i <= count/2; {\n\n\t\tmaxIndex := i\n\t\tif a[i] < a[i*2] {\n\t\t\tmaxIndex = i * 2\n\t\t}\n\n\t\tif i*2+1 <= count && a[maxIndex] < a[i*2+1] {\n\t\t\tmaxIndex = i*2 + 1\n\t\t}\n\n\t\tif maxIndex == i {\n\t\t\tbreak\n\t\t}\n\n\t\tswap(a, i, maxIndex)\n\t\ti = maxIndex\n\t}\n\n}\n\n//swap two elements\nfunc swap(a []int, i int, j int) {\n\ttmp := a[i]\n\ta[i] = a[j]\n\ta[j] = tmp\n}\n"
  },
  {
    "path": "go/28_heap/heap_sort.go",
    "content": "package heap\n\n//build a heap\nfunc buidHeap(a []int, n int) {\n\n\t//heapify from the last parent node\n\tfor i := n / 2; i >= 1; i-- {\n\t\theapifyUpToDown(a, i, n)\n\t}\n\n}\n\n//sort by ascend, a index begin from 1, has n elements\nfunc sort(a []int, n int) {\n\tbuidHeap(a, n)\n\n\tk := n\n\tfor k >= 1 {\n\t\tswap(a, 1, k)\n\t\theapifyUpToDown(a, 1, k-1)\n\t\tk--\n\t}\n}\n\n//heapify from up to down , node index = top\nfunc heapifyUpToDown(a []int, top int, count int) {\n\n\tfor i := top; i <= count/2; {\n\n\t\tmaxIndex := i\n\t\tif a[i] < a[i*2] {\n\t\t\tmaxIndex = i * 2\n\t\t}\n\n\t\tif i*2+1 <= count && a[maxIndex] < a[i*2+1] {\n\t\t\tmaxIndex = i*2 + 1\n\t\t}\n\n\t\tif maxIndex == i {\n\t\t\tbreak\n\t\t}\n\n\t\tswap(a, i, maxIndex)\n\t\ti = maxIndex\n\t}\n\n}\n\n//swap two elements\nfunc swap(a []int, i int, j int) {\n\ttmp := a[i]\n\ta[i] = a[j]\n\ta[j] = tmp\n}\n"
  },
  {
    "path": "go/29_priority_queue/heap.go",
    "content": "package pqueue\n\nfunc adjustHeap(src []Node, start, end int) {\n\tif start >= end {\n\t\treturn\n\t}\n\n\t// 只需要保证优先级最高的节点在 src[1] 的位置即可\n\tfor i := end / 2; i >= start; i-- {\n\t\thigh := i\n\t\tif src[high].priority < src[2*i].priority {\n\t\t\thigh = 2 * i\n\t\t}\n\t\tif 2*i+1 <= end && src[high].priority < src[2*i+1].priority {\n\t\t\thigh = 2*i + 1\n\t\t}\n\t\tif high == i {\n\t\t\tcontinue\n\t\t}\n\t\tsrc[high], src[i] = src[i], src[high]\n\t}\n}\n"
  },
  {
    "path": "go/29_priority_queue/heap_test.go",
    "content": "package pqueue\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc Test_AdjustHeap(t *testing.T) {\n\tlist := []Node{Node{0, 0}, Node{1, 1}, Node{2, 2}, Node{3, 3}, Node{4, 1}, Node{6, 6}}\n\n\tadjustHeap(list, 1, len(list)-1)\n\tassert.Equal(t, 6, list[1].value)\n}\n"
  },
  {
    "path": "go/29_priority_queue/priority_queue.go",
    "content": "package pqueue\n\n// Node 队列节点\ntype Node struct {\n\tvalue    int\n\tpriority int\n}\n\n// PQueue priority queue\ntype PQueue struct {\n\theap []Node\n\n\tcapacity int\n\tused     int\n}\n\n// NewPriorityQueue new\nfunc NewPriorityQueue(capacity int) PQueue {\n\treturn PQueue{\n\t\theap:     make([]Node, capacity+1, capacity+1),\n\t\tcapacity: capacity,\n\t\tused:     0,\n\t}\n}\n\n// Push 入队\nfunc (q *PQueue) Push(node Node) {\n\n\tif q.used > q.capacity {\n\t\t// 队列已满\n\t\treturn\n\t}\n\tq.used++\n\tq.heap[q.used] = node\n\t// 堆化可以放在 Pop 中\n\t// adjustHeap(q.heap, 1, q.used)\n}\n\n// Pop 出队列\nfunc (q *PQueue) Pop() Node {\n\tif q.used == 0 {\n\t\treturn Node{-1, -1}\n\t}\n\t// 先堆化, 再取堆顶元素\n\tadjustHeap(q.heap, 1, q.used)\n\tnode := q.heap[1]\n\n\tq.heap[1] = q.heap[q.used]\n\tq.used--\n\n\treturn node\n}\n\n// Top 获取队列顶部元素\nfunc (q *PQueue) Top() Node {\n\tif q.used == 0 {\n\t\treturn Node{-1, -1}\n\t}\n\n\tadjustHeap(q.heap, 1, q.used)\n\treturn q.heap[1]\n}\n"
  },
  {
    "path": "go/29_priority_queue/priority_queue_test.go",
    "content": "package pqueue\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc Test_Push(t *testing.T) {\n\tqueue := NewPriorityQueue(100)\n\n\tqueue.Push(Node{0, 1})\n\tassert.Equal(t, 0, queue.Top().value)\n\n\tqueue.Push(Node{3, 1})\n\tassert.Equal(t, 0, queue.Top().value)\n\n\tqueue.Push(Node{3, 2})\n\tassert.Equal(t, 3, queue.Top().value)\n\n\tqueue.Push(Node{6, 6})\n\tassert.Equal(t, 6, queue.Top().value)\n\n\tqueue.Push(Node{12, 5})\n\tassert.Equal(t, 6, queue.Top().value)\n\n\tqueue.Push(Node{13, 8})\n\tassert.Equal(t, 13, queue.Top().value)\n}\n\nfunc Test_PushPop(t *testing.T) {\n\tqueue := NewPriorityQueue(100)\n\n\tqueue.Push(Node{0, 1})\n\tqueue.Push(Node{3, 1})\n\tqueue.Push(Node{3, 2})\n\tqueue.Push(Node{6, 6})\n\tqueue.Push(Node{12, 5})\n\tqueue.Push(Node{13, 8})\n\tassert.Equal(t, 13, queue.Top().value)\n\n\tassert.Equal(t, 13, queue.Pop().value)\n\tassert.Equal(t, 6, queue.Pop().value)\n\tassert.Equal(t, 12, queue.Top().value)\n\tassert.Equal(t, 12, queue.Pop().value)\n\n\tqueue.Push(Node{24, 8})\n\tassert.Equal(t, 24, queue.Top().value)\n}\n\n// 无法保证入队顺序和出队顺序的一致性\n// func Test_PushPop_Equal(t *testing.T) {\n// \tqueue := NewPriorityQueue(9)\n\n// \tqueue.Push(Node{0, 1})  // 8\n// \tqueue.Push(Node{3, 1})  // 9\n// \tqueue.Push(Node{3, 2})  // 3\n// \tqueue.Push(Node{6, 2})  // 4\n// \tqueue.Push(Node{11, 3}) // 2\n// \tqueue.Push(Node{12, 2}) // 5\n// \tqueue.Push(Node{13, 2}) // 6\n// \tqueue.Push(Node{19, 5}) // 1\n// \tqueue.Push(Node{17, 2}) // 7\n\n// \tassert.Equal(t, 19, queue.Pop().value)\n// \tassert.Equal(t, 11, queue.Pop().value)\n// \tassert.Equal(t, 3, queue.Pop().value)\n// \tassert.Equal(t, 6, queue.Pop().value)\n// \tassert.Equal(t, 12, queue.Pop().value)\n// \tassert.Equal(t, 13, queue.Pop().value)\n// \tassert.Equal(t, 17, queue.Pop().value)\n// \tassert.Equal(t, 0, queue.Pop().value)\n// \tassert.Equal(t, 3, queue.Pop().value)\n// }\n"
  },
  {
    "path": "go/29_priority_queue/readme.md",
    "content": "\n## TODO\n-  该实现方式不能保证 相同优先级的元素在出队列时  和入队列的顺序是一致的"
  },
  {
    "path": "go/31_graph/graph_search.go",
    "content": "package graph\n\nimport (\n\t\"container/list\"\n\t\"fmt\"\n)\n\n//adjacency table, 无向图\ntype Graph struct {\n\tadj []*list.List\n\tv   int\n}\n\n//init graphh according to capacity\nfunc newGraph(v int) *Graph {\n\tgraphh := &Graph{}\n\tgraphh.v = v\n\tgraphh.adj = make([]*list.List, v)\n\tfor i := range graphh.adj {\n\t\tgraphh.adj[i] = list.New()\n\t}\n\treturn graphh\n}\n\n//insert as add edge，一条边存2次\nfunc (self *Graph) addEdge(s int, t int) {\n\tself.adj[s].PushBack(t)\n\tself.adj[t].PushBack(s)\n}\n\n//search path by BFS\nfunc (self *Graph) BFS(s int, t int) {\n\n\t//todo\n\tif s == t {\n\t\treturn\n\t}\n\n\t//init prev\n\tprev := make([]int, self.v)\n\tfor index := range prev {\n\t\tprev[index] = -1\n\t}\n\n\t//search by queue\n\tvar queue []int\n\tvisited := make([]bool, self.v)\n\tqueue = append(queue, s)\n\tvisited[s] = true\n\tisFound := false\n\tfor len(queue) > 0 && !isFound {\n\t\ttop := queue[0]\n\t\tqueue = queue[1:]\n\t\tlinkedlist := self.adj[top]\n\t\tfor e := linkedlist.Front(); e != nil; e = e.Next() {\n\t\t\tk := e.Value.(int)\n\t\t\tif !visited[k] {\n\t\t\t\tprev[k] = top\n\t\t\t\tif k == t {\n\t\t\t\t\tisFound = true\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tqueue = append(queue, k)\n\t\t\t\tvisited[k] = true\n\t\t\t}\n\t\t}\n\t}\n\n\tif isFound {\n\t\tprintPrev(prev, s, t)\n\t} else {\n\t\tfmt.Printf(\"no path found from %d to %d\\n\", s, t)\n\t}\n\n}\n\n//search by DFS\nfunc (self *Graph) DFS(s int, t int) {\n\n\tprev := make([]int, self.v)\n\tfor i := range prev {\n\t\tprev[i] = -1\n\t}\n\n\tvisited := make([]bool, self.v)\n\tvisited[s] = true\n\n\tisFound := false\n\tself.recurse(s, t, prev, visited, isFound)\n\n\tprintPrev(prev, s, t)\n}\n\n//recursivly find path\nfunc (self *Graph) recurse(s int, t int, prev []int, visited []bool, isFound bool) {\n\n\tif isFound {\n\t\treturn\n\t}\n\n\tvisited[s] = true\n\n\tif s == t {\n\t\tisFound = true\n\t\treturn\n\t}\n\n\tlinkedlist := self.adj[s]\n\tfor e := linkedlist.Front(); e != nil; e = e.Next() {\n\t\tk := e.Value.(int)\n\t\tif !visited[k] {\n\t\t\tprev[k] = s\n\t\t\tself.recurse(k, t, prev, visited, false)\n\t\t}\n\t}\n\n}\n\n//print path recursively\nfunc printPrev(prev []int, s int, t int) {\n\n\tif t == s || prev[t] == -1 {\n\t\tfmt.Printf(\"%d \", t)\n\t} else {\n\t\tprintPrev(prev, s, prev[t])\n\t\tfmt.Printf(\"%d \", t)\n\t}\n\n}\n\n//func main() {\n//\tgraph := newGraph(8)\n//\tgraph.addEdge(0, 1)\n//\tgraph.addEdge(0, 3)\n//\tgraph.addEdge(1, 2)\n//\tgraph.addEdge(1, 4)\n//\tgraph.addEdge(2, 5)\n//\tgraph.addEdge(3, 4)\n//\tgraph.addEdge(4, 5)\n//\tgraph.addEdge(4, 6)\n//\tgraph.addEdge(5, 7)\n//\tgraph.addEdge(6, 7)\n//\n//\tgraph.BFS(0, 7)\n//\tfmt.Println()\n//\tgraph.BFS(1, 3)\n//\tfmt.Println()\n//\tgraph.DFS(0, 7)\n//\tfmt.Println()\n//\tgraph.DFS(1, 3)\n//\tfmt.Println()\n//}\n"
  },
  {
    "path": "go/32_string/string_bf.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n)\n\n//BF search pattern index, return the first match subs start index\nfunc bfSearch(main string, pattern string) int {\n\n\t//defensive\n\tif len(main) == 0 || len(pattern) == 0 || len(main) < len(pattern) {\n\t\treturn -1\n\t}\n\n\tfor i := 0; i <= len(main)-len(pattern); i++ {\n\t\tsubStr := main[i : i+len(pattern)]\n\t\tif subStr == pattern {\n\t\t\treturn i\n\t\t}\n\t}\n\n\treturn -1\n}\n\nfunc main() {\n\n\tmain := \"abcd227fac\"\n\tpattern := \"ac\"\n\tfmt.Println(bfSearch(main, pattern))\n}\n"
  },
  {
    "path": "go/32_string/string_bm.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"math\"\n)\n\n//bc: pattern char index hash mapping\nfunc generateBC(pattern string) []int {\n\n\tbc := make([]int, 256)\n\n\tfor index := range bc {\n\t\tbc[index] = -1\n\t}\n\n\tfor index, char := range pattern {\n\t\tbc[int(char)] = index\n\t}\n\n\treturn bc\n}\n\n//generate suffix and prefix array for pattern\nfunc generateGS(pattern string) ([]int, []bool) {\n\tm := len(pattern)\n\tsuffix := make([]int, m)\n\tprefix := make([]bool, m)\n\n\t//init\n\tfor i := 0; i < m; i++ {\n\t\tsuffix[i] = -1\n\t\tprefix[i] = false\n\t}\n\n\tfor i := 0; i < m-1; i++ {\n\t\tj := i\n\t\tk := 0\n\t\tfor j >= 0 && pattern[j] == pattern[m-1-k] {\n\t\t\tj--\n\t\t\tk++\n\t\t\tsuffix[k] = j + 1\n\t\t}\n\n\t\tif j == -1 {\n\t\t\tprefix[k] = true\n\t\t}\n\t}\n\n\treturn suffix, prefix\n}\n\n//todo\nfunc moveByGS(patternLength int, badCharStartIndex int, suffix []int, prefix []bool) int {\n\n\t//length of good suffix\n\tk := patternLength - badCharStartIndex - 1\n\n\t//complete match\n\tif suffix[k] != -1 {\n\t\treturn badCharStartIndex + 1 - suffix[k]\n\t}\n\n\t//partial match\n\tfor t := patternLength - 1; t > badCharStartIndex+1; t-- {\n\t\tif prefix[t] {\n\t\t\treturn t\n\t\t}\n\t}\n\n\t//no match\n\treturn patternLength\n\n}\n\nfunc bmSearch(main string, pattern string) int {\n\t//defensive\n\tif len(main) == 0 || len(pattern) == 0 || len(pattern) > len(main) {\n\t\treturn -1\n\t}\n\n\tbc := generateBC(pattern)\n\tsuffix, prefix := generateGS(pattern)\n\n\tn := len(main)\n\tm := len(pattern)\n\n\t// i : start index of main string\n\tstep := 1\n\tfor i := 0; i <= n-m; i = i + step {\n\t\tsubStr := main[i : i+m]\n\t\tk, j := findBadChar(subStr, pattern, bc)\n\n\t\tstepForBC := j - k\n\t\t//j is bad char occur index\n\t\tif j == -1 {\n\t\t\treturn i\n\t\t}\n\n\t\tstepForGS := -1\n\t\tif j < m-1 {\n\t\t\tstepForGS = moveByGS(m, j, suffix, prefix)\n\t\t}\n\n\t\t//k is bad char index in pattern\n\t\tstep = int(math.Max(float64(stepForBC), float64(stepForGS)))\n\t}\n\n\treturn -1\n}\n\nfunc findBadChar(subStr string, pattern string, bc []int) (int, int) {\n\n\tj := -1\n\tk := -1\n\tbadChar := rune(0)\n\n\tfor index := len(subStr) - 1; index >= 0; index-- {\n\t\tif subStr[index] != pattern[index] {\n\t\t\tj = index\n\t\t\tbadChar = rune(subStr[index])\n\t\t\tbreak\n\t\t}\n\t}\n\n\t//if bad character exist, then find it's index at pattern\n\tif j > 0 {\n\t\tk = bc[int(badChar)]\n\t}\n\n\treturn k, j\n}\n\nfunc main() {\n\n\tmain := \"abcacabcbcabcabc\"\n\tpattern := \"cabcab\"\n\n\tfmt.Println(bmSearch(main, pattern))\n}\n"
  },
  {
    "path": "go/34_kmp/kmp.go",
    "content": "package main\n\nimport (\n\t\"fmt\" \n)\n\nfunc getNexts(pattern string) []int {\n\tm := len(pattern)\n\tnexts := make([]int, m)\n\tfor index := range nexts {\n\t\tnexts[index] = -1\n\t}\n\n\tfor i := 1; i < m - 1; i++ {\n\t\tj := nexts[i - 1]\n\n\t\tfor pattern[j + 1] != pattern[i] && j >= 0 {\n\t\t\tj = nexts[j]\n\t\t}\n\n\t\tif pattern[j + 1] == pattern[i] {\n\t\t\tj += 1\n\t\t}\n\n\t\tnexts[i] = j\n\t}\n\n\treturn nexts\n}\n\nfunc findByKMP(s string, pattern string) int {\n\tn := len(s)\n\tm := len(pattern)\n\tif n < m {\n\t\treturn -1\n\t}\n\n\tnexts := getNexts(pattern)\n\n\tj := 0\n\tfor i := 0; i < n; i++ {\n\t\tfor j > 0 && s[i] != pattern[j] {\n\t\t\tj = nexts[j - 1] + 1\n\t\t}\n\n\t\tif s[i] == pattern[j] {\n\t\t\tif j == m - 1 {\n\t\t\t\treturn i - m + 1\n\t\t\t}\n\t\t\tj += 1\n\t\t}\n\t}\n\n\treturn -1\n}\n\nfunc main(){\n\ts := \"abc abcdab abcdabcdabde\"\n\tpattern := \"bcdabd\"\n\tfmt.Println(findByKMP(s, pattern)) //16\n\n\ts = \"aabbbbaaabbababbabbbabaaabb\"\n\tpattern = \"abab\"\n\tfmt.Println(findByKMP(s, pattern)) //11\n\n\ts = \"aabbbbaaabbababbabbbabaaabb\"\n\tpattern = \"ababacd\"\n\tfmt.Println(findByKMP(s, pattern)) //-1\n\n\ts = \"hello\"\n\tpattern = \"ll\"\n\tfmt.Println(findByKMP(s, pattern)) //2\n}"
  },
  {
    "path": "go/41_dynamic_programming/backtracking/leastcoins.go",
    "content": "package backtracking\n\nimport (\n\t\"fmt\"\n)\n\nconst intMax = int(^uint(0) >> 1)\n\nvar Cnt int\n\n// LeastCoins find least number of coins of which the total values are equals a given one\nfunc LeastCoins(targetTotal int, coinOptions []int) int {\n\tminNum := intMax\n\n\tmemo := make([][]int, targetTotal+1)\n\tfor i := range memo {\n\t\tmemo[i] = make([]int, len(coinOptions))\n\t}\n\tfmt.Println(\"start\")\n\tleastCoins(&minNum, 0, targetTotal, len(coinOptions)-1, coinOptions, memo)\n\tfmt.Println(\"end\")\n\n\treturn minNum\n\n}\n\nfunc leastCoins(minNum *int, cNum, totalValue, opIndex int, coinOptions []int, memo [][]int) {\n\tCnt++\n\tif 0 == totalValue {\n\t\tif cNum < *minNum {\n\t\t\t*minNum = cNum\n\t\t}\n\n\t\treturn\n\t}\n\n\tif opIndex < 0 {\n\t\treturn\n\t}\n\n\tnum4Option := 0\n\tremaining := totalValue - coinOptions[opIndex]*num4Option\n\tfor remaining >= 0 {\n\n\t\tif opIndex != 0 {\n\t\t\tif shouldSkip(memo, remaining, opIndex-1, cNum+num4Option) {\n\t\t\t\tgoto Next\n\t\t\t}\n\t\t}\n\t\tleastCoins(minNum, cNum+num4Option, remaining, opIndex-1, coinOptions, memo)\n\n\tNext:\n\t\tnum4Option++\n\t\tremaining = totalValue - coinOptions[opIndex]*num4Option\n\n\t}\n\n}\n\nfunc shouldSkip(memo [][]int, totalValue, nextOpIdex, cNum int) bool {\n\tif memo[totalValue][nextOpIdex] > 0 && memo[totalValue][nextOpIdex] <= cNum {\n\t\tfmt.Printf(\"skip，%d, %d  as %d <= %d \\n\", totalValue, nextOpIdex, memo[totalValue][nextOpIdex], cNum)\n\t\treturn true\n\t}\n\tif memo[totalValue][nextOpIdex] == 0 || memo[totalValue][nextOpIdex] > cNum {\n\t\tmemo[totalValue][nextOpIdex] = cNum\n\t}\n\treturn false\n}\n\nfunc LeastCoins2(targetTotal int, coinOptions []int) int {\n\n\tminNum := intMax\n\tmemo := make([][]bool, targetTotal)\n\tfor i := range memo {\n\t\tmemo[i] = make([]bool, targetTotal/coinOptions[0])\n\t}\n\n\tfmt.Println(\"start\")\n\tleastCoins2(&minNum, targetTotal, coinOptions, 0, 0, memo)\n\tfmt.Println(\"end\")\n\n\treturn minNum\n\n}\n\nfunc leastCoins2(minNum *int, targetTotal int, coinOptions []int, cNum, cValue int, memo [][]bool) {\n\tCnt++\n\tif cValue == targetTotal {\n\t\tif *minNum > cNum {\n\t\t\t*minNum = cNum\n\t\t}\n\t\treturn\n\t}\n\n\tfor _, coin := range coinOptions {\n\t\tif coin+cValue <= targetTotal && !memo[cValue+coin-1][cNum] {\n\t\t\tmemo[cValue+coin-1][cNum] = true\n\t\t\tleastCoins2(minNum, targetTotal, coinOptions, cNum+1, cValue+coin, memo)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "go/41_dynamic_programming/backtracking/leastcoins_test.go",
    "content": "package backtracking\n\nimport \"testing\"\n\nfunc TestFindLeastCoins(t *testing.T) {\n\n\tcoinOptions := []int{1, 3, 5, 10, 50}\n\n\tCnt = 0\n\tresult := LeastCoins(9, coinOptions)\n\n\tt.Log(\"test 1 =====================\")\n\tif result != 3 {\n\t\tt.Logf(\"least coins %d\", result)\n\t\tt.Error(\"failed\")\n\t}\n\tt.Logf(\"cnt===%d\", Cnt)\n\n\tCnt = 0\n\tt.Log(\"test 2 =====================\")\n\tresult = LeastCoins(36, coinOptions)\n\n\tif result != 5 {\n\t\tt.Logf(\"least coins %d\", result)\n\t\tt.Error(\"failed\")\n\t}\n\tt.Logf(\"cnt===%d\", Cnt)\n\n}\n\nfunc TestFindLeastCoins2(t *testing.T) {\n\n\tcoinOptions := []int{1, 3, 5, 10, 50}\n\n\tCnt = 0\n\tresult := LeastCoins2(9, coinOptions)\n\n\tt.Log(\"test 1 =====================\")\n\tif result != 3 {\n\t\tt.Logf(\"least coins %d\", result)\n\t\tt.Error(\"failed\")\n\t}\n\n\tt.Logf(\"cnt===%d\", Cnt)\n\n\tCnt = 0\n\tt.Log(\"test 2 =====================\")\n\tresult = LeastCoins2(36, coinOptions)\n\n\tif result != 5 {\n\t\tt.Logf(\"least coins %d\", result)\n\t\tt.Error(\"failed\")\n\t}\n\tt.Logf(\"cnt===%d\", Cnt)\n\n}\n"
  },
  {
    "path": "go/41_dynamic_programming/dp/leastcoins.go",
    "content": "package dp\n\nvar Cnt int\n\n// minNum(v) = 1 + min(minNum(v-i))\nfunc LeastCoins(targetTotal int, coinOptions []int) int {\n\tmemo := make([]int, targetTotal)\n\n\tcMinNum := leastCoins(targetTotal, coinOptions, memo)\n\n\treturn cMinNum\n\n}\n\n// State Transition Table\nfunc LeastCoins2(targetTotal int, coinOptions []int) int {\n\tmemo := make([]int, targetTotal)\n\n\tfor i := 1; i <= targetTotal; i++ {\n\n\t\tminNum := -1\n\t\tfor _, coin := range coinOptions {\n\t\t\tCnt++\n\t\t\tif i < coin {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tif i == coin {\n\t\t\t\tminNum = 1\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tif minNum == -1 || minNum > memo[i-coin-1]+1 {\n\t\t\t\tminNum = memo[i-coin-1] + 1\n\t\t\t}\n\t\t}\n\t\tmemo[i-1] = minNum\n\t}\n\n\treturn memo[targetTotal-1]\n\n}\n\nfunc leastCoins(targetTotal int, coinOptions, memo []int) int {\n\tCnt++\n\tif targetTotal == 0 {\n\t\treturn 0\n\t}\n\tif memo[targetTotal-1] != 0 {\n\t\treturn memo[targetTotal-1]\n\t}\n\tcMinNum := -1\n\tfor _, coin := range coinOptions {\n\t\tif targetTotal-coin < 0 {\n\t\t\tcontinue\n\t\t}\n\t\tcNum := 1 + leastCoins(targetTotal-coin, coinOptions, memo)\n\t\tif cMinNum == -1 || cNum < cMinNum {\n\t\t\tcMinNum = cNum\n\t\t}\n\n\t}\n\tmemo[targetTotal-1] = cMinNum\n\treturn cMinNum\n}\n"
  },
  {
    "path": "go/41_dynamic_programming/dp/leastcoins_test.go",
    "content": "package dp\n\nimport \"testing\"\n\nfunc TestFindLeastCoins(t *testing.T) {\n\n\tcoinOptions := []int{1, 3, 5, 10, 50}\n\n\tCnt = 0\n\tresult := LeastCoins(9, coinOptions)\n\n\tt.Log(\"test 1 =====================\")\n\tif result != 3 {\n\t\tt.Logf(\"least coins %d\", result)\n\t\tt.Error(\"failed\")\n\t}\n\tt.Logf(\"cnt===%d\", Cnt)\n\n\tCnt = 0\n\tt.Log(\"test 2 =====================\")\n\tresult = LeastCoins(36, coinOptions)\n\n\tif result != 5 {\n\t\tt.Logf(\"least coins %d\", result)\n\t\tt.Error(\"failed\")\n\t}\n\tt.Logf(\"cnt===%d\", Cnt)\n\n}\n\nfunc TestFindLeastCoins2(t *testing.T) {\n\n\tcoinOptions := []int{1, 3, 5, 10, 50}\n\n\tCnt = 0\n\tresult := LeastCoins2(9, coinOptions)\n\n\tt.Log(\"test 1 =====================\")\n\tif result != 3 {\n\t\tt.Logf(\"least coins %d\", result)\n\t\tt.Error(\"failed\")\n\t}\n\tt.Logf(\"cnt===%d\", Cnt)\n\n\tCnt = 0\n\tt.Log(\"test 2 =====================\")\n\tresult = LeastCoins2(36, coinOptions)\n\n\tif result != 5 {\n\t\tt.Logf(\"least coins %d\", result)\n\t\tt.Error(\"failed\")\n\t}\n\tt.Logf(\"cnt===%d\", Cnt)\n\n}\n"
  },
  {
    "path": "go/42_dynamic_programming/longest_common_substring.go",
    "content": "package main\n\nimport \"fmt\"\n\nfunc lsc(s1 string, s2 string) int {\n\tm := len(s1)\n\tn := len(s2)\n\n\tmemo := make([][]int, m + 1)\n\tfor i := 0; i < m + 1; i++ {\n\t\tmemo[i] = make([]int, n + 1)\n\t}\n\n\n\tfor i := 1; i < m + 1; i++ {\n\t\tfor j := 1; j < n + 1; j++ {\n\t\t\tif s1[i - 1] == s2[j - 1] {\n\t\t\t\tmemo[i][j] = memo[i - 1][j - 1] + 1\n\t\t\t}\n\t\t}\n\t}\n\n\tfmt.Println(memo)\n\tlongest := 0\n\tfor i, _ := range memo {\n\t\tfor j, e2 := range memo[i] {\n\t\t\tif longest < memo[i][j] {\n\t\t\t\tlongest = e2\n\t\t\t}\n\t\t}\n\t}\n\n\treturn longest\n}\n\nfunc main() {\n\tfmt.Println(lsc(\"blue\", \"clues\"))\t//3\n\tfmt.Println(lsc(\"fosh\", \"fish\"))\t//2\n\tfmt.Println(lsc(\"fosh\", \"fort\"))\t//2\n\tfmt.Println(lsc(\"hish\", \"fish\"))\t//3\n\tfmt.Println(lsc(\"hish\", \"vista\"))\t//2\n}"
  },
  {
    "path": "go/45_bitmap/bitmap.go",
    "content": "package bitmap\n\n// BitMap implement bitmap\ntype BitMap []byte\n\n// New create BitMap\nfunc New(length uint) BitMap {\n\treturn make([]byte, length/8+1)\n}\n\n// Set set value in bitmap\nfunc (b BitMap) Set(value uint) {\n\tbyteIndex := value / 8\n\tif byteIndex >= uint(len(b)) {\n\t\treturn\n\t}\n\tbitIndex := value % 8\n\t[]byte(b)[byteIndex] |= 1 << bitIndex\n}\n\n// Get check whether value exist or not\nfunc (b BitMap) Get(value uint) bool {\n\tbyteIndex := value / 8\n\tif byteIndex >= uint(len(b)) {\n\t\treturn false\n\t}\n\tbitIndex := value % 8\n\treturn []byte(b)[byteIndex]&(1<<bitIndex) != 0\n}\n"
  },
  {
    "path": "go/45_bitmap/bitmap_test.go",
    "content": "package bitmap\n\nimport (\n\t\"testing\"\n)\n\nfunc TestBitMap(t *testing.T) {\n\tbitMap := New(80)\n\tfor i := 0; i <= 100; i += 10 {\n\t\tbitMap.Set(uint(i))\n\t}\n\tfor i := 0; i <= 100; i += 10 {\n\t\tt.Log(bitMap.Get(uint(i)))\n\t}\n}\n"
  },
  {
    "path": "go/binarysearch2.go",
    "content": "package binarySearch\n\n//查找第一个值等于给定值得元素\nfunc BinarySearch2(nums []int, value int) int {\n\tlength := len(nums)\n\tstart := 0\n\tend := length - 1\n\tfor start <= end {\n\t\tmid := start + ((end - start) >> 1)\n\t\tif nums[mid] > value {\n\t\t\tend = mid - 1\n\t\t} else if nums[mid] < value {\n\t\t\tstart = mid + 1\n\t\t} else {\n\t\t\tfor mid >= 0 {\n\t\t\t\tif nums[mid-1] == value {\n\t\t\t\t\tmid--\n\t\t\t\t} else {\n\t\t\t\t\treturn mid\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn -1\n}\n\n// 查找最后一个值等于给定值的元素\nfunc BinarySearch3(nums []int, value int) int {\n\tstart := 0\n\tend := len(nums) - 1\n\tfor start <= end {\n\t\tmid := start + ((end - start) >> 1)\n\t\tif nums[mid] > value {\n\t\t\tend = mid - 1\n\t\t} else if nums[mid] < value {\n\t\t\tstart = mid + 1\n\t\t} else {\n\t\t\tfor mid < len(nums) {\n\t\t\t\tif nums[mid+1] == value {\n\t\t\t\t\tmid++\n\t\t\t\t} else {\n\t\t\t\t\treturn mid\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn -1\n}\n\n// 查找第一个大于等于给定值的元素\nfunc BinarySearch4(nums []int, value int) int {\n\tstart := 0\n\tend := len(nums) - 1\n\tfor start <= end {\n\t\tmid := (start + end) >> 1\n\t\tif nums[mid] < value {\n\t\t\tstart = mid + 1\n\t\t} else {\n\t\t\tfor mid >= 0 {\n\t\t\t\tif nums[mid-1] >= value {\n\t\t\t\t\tmid--\n\t\t\t\t} else {\n\t\t\t\t\treturn mid\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn -1\n}\n\n// 查找最后一个小于等于给定值的元素\nfunc BinarySearch5(nums []int, value int) int {\n\tstart := 0\n\tend := len(nums) - 1\n\tfor start <= end {\n\t\tmid := (start + end) >> 1\n\t\tif nums[mid] > value {\n\t\t\tend = mid - 1\n\t\t} else {\n\t\t\tfor mid < len(nums) {\n\t\t\t\tif nums[mid+1] <= value {\n\t\t\t\t\tmid++\n\t\t\t\t} else {\n\t\t\t\t\treturn mid\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn -1\n}\n"
  },
  {
    "path": "java/05_array/.gitkeep",
    "content": ""
  },
  {
    "path": "java/05_array/Array.java",
    "content": "package array;\n\n/**\n * 1) 数组的插入、删除、按照下标随机访问操作；\n * 2）数组中的数据是int类型的；\n *\n * Author: Zheng\n * modify: xing, Gsealy\n */\npublic class Array {\n    //定义整型数据data保存数据\n    public int data[];\n    //定义数组长度\n    private int n;\n    //定义中实际个数\n    private int count;\n\n    //构造方法，定义数组大小\n    public Array(int capacity){\n        this.data = new int[capacity];\n        this.n = capacity;\n        this.count=0;//一开始一个数都没有存所以为0\n    }\n\n    //根据索引，找到数据中的元素并返回\n    public int find(int index){\n        if (index<0 || index>=count) return -1;\n        return data[index];\n    }\n\n    //插入元素:头部插入，尾部插入\n    public boolean insert(int index, int value){\n        //数组中无元素 \n\n        //if (index == count && count == 0) {\n        //    data[index] = value;\n        //    ++count;\n        //    return true;\n        //}\n\n        // 数组空间已满\n        if (count == n) {\n            System.out.println(\"没有可插入的位置\");\n            return false;\n        }\n        // 如果count还没满，那么就可以插入数据到数组中\n        // 位置不合法\n        if (index < 0||index > count ) {\n            System.out.println(\"位置不合法\");\n            return false;\n        }\n        // 位置合法\n        for( int i = count; i > index; --i){\n            data[i] = data[i - 1];\n        }\n        data[index] = value;\n        ++count;\n        return true;\n    }\n    //根据索引，删除数组中元素\n    public boolean delete(int index){\n        if (index<0 || index >=count) return false;\n        //从删除位置开始，将后面的元素向前移动一位\n        for (int i=index+1; i<count; ++i){\n            data[i-1] = data[i];\n        }\n        //删除数组末尾元素  这段代码不需要也可以\n        /*int[] arr = new int[count-1];\n        for (int i=0; i<count-1;i++){\n            arr[i] = data[i];\n        }\n        this.data = arr;*/\n\n        --count;\n        return true;\n    }\n    public void printAll() {\n        for (int i = 0; i < count; ++i) {\n            System.out.print(data[i] + \" \");\n        }\n        System.out.println();\n    }\n\n    public static void main(String[] args) {\n        Array array = new Array(5);\n        array.printAll();\n        array.insert(0, 3);\n        array.insert(0, 4);\n        array.insert(1, 5);\n        array.insert(3, 9);\n        array.insert(3, 10);\n        //array.insert(3, 11);\n        array.printAll();\n    }\n}\n"
  },
  {
    "path": "java/05_array/GenericArray.java",
    "content": "public class GenericArray<T> {\n    private T[] data;\n    private int size;\n\n    // 根据传入容量，构造Array\n    public GenericArray(int capacity) {\n        data = (T[]) new Object[capacity];\n        size = 0;\n    }\n\n    // 无参构造方法，默认数组容量为10\n    public GenericArray() {\n        this(10);\n    }\n\n    // 获取数组容量\n    public int getCapacity() {\n        return data.length;\n    }\n\n    // 获取当前元素个数\n    public int count() {\n        return size;\n    }\n\n    // 判断数组是否为空\n    public boolean isEmpty() {\n        return size == 0;\n    }\n\n    // 修改 index 位置的元素\n    public void set(int index, T e) {\n        checkIndex(index);\n        data[index] = e;\n    }\n\n    // 获取对应 index 位置的元素\n    public T get(int index) {\n        checkIndex(index);\n        return data[index];\n    }\n\n    // 查看数组是否包含元素e\n    public boolean contains(T e) {\n        for (int i = 0; i < size; i++) {\n            if (data[i].equals(e)) {\n                return true;\n            }\n        }\n        return false;\n    }\n\n    // 获取对应元素的下标, 未找到，返回 -1\n    public int find(T e) {\n        for ( int i = 0; i < size; i++) {\n            if (data[i].equals(e)) {\n                return i;\n            }\n        }\n        return -1;\n    }\n\n\n    // 在 index 位置，插入元素e, 时间复杂度 O(m+n)\n    public void add(int index, T e) {\n        checkIndexForAdd(index);\n        // 如果当前元素个数等于数组容量，则将数组扩容为原来的2倍\n        if (size == data.length) {\n            resize(2 * data.length);\n        }\n\n        for (int i = size - 1; i >= index; i--) {\n            data[i + 1] = data[i];\n        }\n        data[index] = e;\n        size++;\n    }\n\n    // 向数组头插入元素\n    public void addFirst(T e) {\n        add(0, e);\n    }\n\n    // 向数组尾插入元素\n    public void addLast(T e) {\n        add(size, e);\n    }\n\n    // 删除 index 位置的元素，并返回\n    public T remove(int index) {\n        checkIndex(index);\n\n        T ret = data[index];\n        for (int i = index + 1; i < size; i++) {\n            data[i - 1] = data[i];\n        }\n        size --;\n        data[size] = null;\n\n        // 缩容\n        if (size == data.length / 4 && data.length / 2 != 0) {\n            resize(data.length / 2);\n        }\n\n        return ret;\n    }\n\n    // 删除第一个元素\n    public T removeFirst() {\n        return remove(0);\n    }\n\n    // 删除末尾元素\n    public T removeLast() {\n        return remove(size - 1);\n    }\n\n    // 从数组中删除指定元素\n    public void removeElement(T e) {\n        int index = find(e);\n        if (index != -1) {\n            remove(index);\n        }\n    }\n\n    @Override\n    public String toString() {\n        StringBuilder builder = new StringBuilder();\n        builder.append(String.format(\"Array size = %d, capacity = %d \\n\", size, data.length));\n        builder.append('[');\n        for (int i = 0; i < size; i++) {\n            builder.append(data[i]);\n            if (i != size - 1) {\n                builder.append(\", \");\n            }\n        }\n        builder.append(']');\n        return builder.toString();\n    }\n\n\n    // 扩容方法，时间复杂度 O(n)\n    private void resize(int capacity) {\n        T[] newData = (T[]) new Object[capacity];\n\n        for (int i = 0; i < size; i++) {\n            newData[i] = data[i];\n        }\n        data = newData;\n    }\n\n    private void checkIndex(int index) {\n        if (index < 0 || index >= size) {\n            throw new IllegalArgumentException(\"Add failed! Require index >=0 and index < size.\");\n        }\n    }\n\n    private void checkIndexForAdd(int index) {\n        if(index < 0 || index > size) {\n            throw new IllegalArgumentException(\"remove failed! Require index >=0 and index <= size.\");\n        }\n    }\n}\n"
  },
  {
    "path": "java/06_linkedlist/.gitkeep",
    "content": ""
  },
  {
    "path": "java/06_linkedlist/LRUBaseLinkedList.java",
    "content": "package linked.singlelist;\n\n\nimport java.util.Scanner;\n\n/**\n * 基于单链表LRU算法（java）\n *\n * @author hoda\n * @create 2018-12-17\n */\npublic class LRUBaseLinkedList<T> {\n\n    /**\n     * 默认链表容量\n     */\n    private final static Integer DEFAULT_CAPACITY = 10;\n\n    /**\n     * 头结点\n     */\n    private SNode<T> headNode;\n\n    /**\n     * 链表长度\n     */\n    private Integer length;\n\n    /**\n     * 链表容量\n     */\n    private Integer capacity;\n\n    public LRUBaseLinkedList() {\n        this.headNode = new SNode<>();\n        this.capacity = DEFAULT_CAPACITY;\n        this.length = 0;\n    }\n\n    public LRUBaseLinkedList(Integer capacity) {\n        this.headNode = new SNode<>();\n        this.capacity = capacity;\n        this.length = 0;\n    }\n\n    public void add(T data) {\n        SNode preNode = findPreNode(data);\n\n        // 链表中存在，删除原数据，再插入到链表的头部\n        if (preNode != null) {\n            deleteElemOptim(preNode);\n            intsertElemAtBegin(data);\n        } else {\n            if (length >= this.capacity) {\n                //删除尾结点\n                deleteElemAtEnd();\n            }\n            intsertElemAtBegin(data);\n        }\n    }\n\n    /**\n     * 删除preNode结点下一个元素\n     *\n     * @param preNode\n     */\n    private void deleteElemOptim(SNode preNode) {\n        SNode temp = preNode.getNext();\n        preNode.setNext(temp.getNext());\n        temp = null;\n        length--;\n    }\n\n    /**\n     * 链表头部插入节点\n     *\n     * @param data\n     */\n    private void intsertElemAtBegin(T data) {\n        SNode next = headNode.getNext();\n        headNode.setNext(new SNode(data, next));\n        length++;\n    }\n\n    /**\n     * 获取查找到元素的前一个结点\n     *\n     * @param data\n     * @return\n     */\n    private SNode findPreNode(T data) {\n        SNode node = headNode;\n        while (node.getNext() != null) {\n            if (data.equals(node.getNext().getElement())) {\n                return node;\n            }\n            node = node.getNext();\n        }\n        return null;\n    }\n\n    /**\n     * 删除尾结点\n     */\n    private void deleteElemAtEnd() {\n        SNode ptr = headNode;\n        // 空链表直接返回\n        if (ptr.getNext() == null) {\n            return;\n        }\n\n        // 倒数第二个结点\n        while (ptr.getNext().getNext() != null) {\n            ptr = ptr.getNext();\n        }\n\n        SNode tmp = ptr.getNext();\n        ptr.setNext(null);\n        tmp = null;\n        length--;\n    }\n\n    private void printAll() {\n        SNode node = headNode.getNext();\n        while (node != null) {\n            System.out.print(node.getElement() + \",\");\n            node = node.getNext();\n        }\n        System.out.println();\n    }\n\n    public class SNode<T> {\n\n        private T element;\n\n        private SNode next;\n\n        public SNode(T element) {\n            this.element = element;\n        }\n\n        public SNode(T element, SNode next) {\n            this.element = element;\n            this.next = next;\n        }\n\n        public SNode() {\n            this.next = null;\n        }\n\n        public T getElement() {\n            return element;\n        }\n\n        public void setElement(T element) {\n            this.element = element;\n        }\n\n        public SNode getNext() {\n            return next;\n        }\n\n        public void setNext(SNode next) {\n            this.next = next;\n        }\n    }\n\n    public static void main(String[] args) {\n        LRUBaseLinkedList list = new LRUBaseLinkedList();\n        Scanner sc = new Scanner(System.in);\n        while (true) {\n            list.add(sc.nextInt());\n            list.printAll();\n        }\n    }\n}\n"
  },
  {
    "path": "java/06_linkedlist/LRUBasedArray.java",
    "content": "package linkedlist;\n\nimport java.util.HashMap;\nimport java.util.Map;\n/**\n * Created by SpecialYang in 2018/12/7 2:00 PM.\n *\n * 基于数组实现的LRU缓存\n * 1. 空间复杂度为O(n)\n * 2. 时间复杂度为O(n)\n * 3. 不支持null的缓存\n */\npublic class LRUBasedArray<T> {\n\n    private static final int DEFAULT_CAPACITY = (1 << 3);\n\n    private int capacity;\n\n    private int count;\n\n    private T[] value;\n\n    private Map<T, Integer> holder;\n\n    public LRUBasedArray() {\n        this(DEFAULT_CAPACITY);\n    }\n\n    public LRUBasedArray(int capacity) {\n        this.capacity = capacity;\n        value = (T[]) new Object[capacity];\n        count = 0;\n        holder = new HashMap<T, Integer>(capacity);\n    }\n\n    /**\n     * 模拟访问某个值\n     * @param object\n     */\n    public void offer(T object) {\n        if (object == null) {\n            throw new IllegalArgumentException(\"该缓存容器不支持null!\");\n        }\n        Integer index = holder.get(object);\n        if (index == null) {\n            if (isFull()) {\n                removeAndCache(object);\n            } else {\n                cache(object, count);\n            }\n        } else {\n            update(index);\n        }\n    }\n\n    /**\n     * 若缓存中有指定的值，则更新位置\n     * @param end\n     */\n    public void update(int end) {\n        T target = value[end];\n        rightShift(end);\n        value[0] = target;\n        holder.put(target, 0);\n    }\n\n    /**\n     * 缓存数据到头部，但要先右移\n     * @param object\n     * @param end 数组右移的边界\n     */\n    public void cache(T object, int end) {\n        rightShift(end);\n        value[0] = object;\n        holder.put(object, 0);\n        count++;\n    }\n\n    /**\n     * 缓存满的情况，踢出后，再缓存到数组头部\n     * @param object\n     */\n    public void removeAndCache(T object) {\n        T key = value[--count];\n        holder.remove(key);\n        cache(object, count);\n    }\n\n    /**\n     * end左边的数据统一右移一位\n     * @param end\n     */\n    private void rightShift(int end) {\n        for (int i = end - 1; i >= 0; i--) {\n            value[i + 1] = value[i];\n            holder.put(value[i], i + 1);\n        }\n    }\n\n    public boolean isContain(T object) {\n        return holder.containsKey(object);\n    }\n\n    public boolean isEmpty() {\n        return count == 0;\n    }\n\n    public boolean isFull() {\n        return count == capacity;\n    }\n\n    @Override\n    public String toString() {\n        StringBuilder sb = new StringBuilder();\n        for (int i = 0; i < count; i++) {\n            sb.append(value[i]);\n            sb.append(\" \");\n        }\n        return sb.toString();\n    }\n\n    static class TestLRUBasedArray {\n\n        public static void main(String[] args) {\n            testDefaultConstructor();\n            testSpecifiedConstructor(4);\n//            testWithException();\n        }\n\n        private static void testWithException() {\n            LRUBasedArray<Integer> lru = new LRUBasedArray<Integer>();\n            lru.offer(null);\n        }\n\n        public static void testDefaultConstructor() {\n            System.out.println(\"======无参测试========\");\n            LRUBasedArray<Integer> lru = new LRUBasedArray<Integer>();\n            lru.offer(1);\n            lru.offer(2);\n            lru.offer(3);\n            lru.offer(4);\n            lru.offer(5);\n            System.out.println(lru);\n            lru.offer(6);\n            lru.offer(7);\n            lru.offer(8);\n            lru.offer(9);\n            System.out.println(lru);\n        }\n\n        public static void testSpecifiedConstructor(int capacity) {\n            System.out.println(\"======有参测试========\");\n            LRUBasedArray<Integer> lru = new LRUBasedArray<Integer>(capacity);\n            lru.offer(1);\n            System.out.println(lru);\n            lru.offer(2);\n            System.out.println(lru);\n            lru.offer(3);\n            System.out.println(lru);\n            lru.offer(4);\n            System.out.println(lru);\n            lru.offer(2);\n            System.out.println(lru);\n            lru.offer(4);\n            System.out.println(lru);\n            lru.offer(7);\n            System.out.println(lru);\n            lru.offer(1);\n            System.out.println(lru);\n            lru.offer(2);\n            System.out.println(lru);\n        }\n    }\n}\n"
  },
  {
    "path": "java/06_linkedlist/SinglyLinkedList.java",
    "content": "package linkedlist;\n\n/**\n * 1）单链表的插入、删除、查找操作；\n * 2）链表中存储的是int类型的数据；\n *\n * Author：Zheng\n */\npublic class SinglyLinkedList {\n\n    private Node head = null;\n\n    public Node findByValue(int value) {\n        Node p = head;\n        while (p != null && p.data != value) {\n            p = p.next;\n        }\n\n        return p;\n    }\n\n    public Node findByIndex(int index) {\n        Node p = head;\n        int pos = 0;\n        while (p != null && pos != index) {\n            p = p.next;\n            ++pos;\n        }\n\n        return p;\n    }\n\n    //无头结点\n    //表头部插入\n    //这种操作将于输入的顺序相反，逆序\n    public void insertToHead(int value) {\n        Node newNode = new Node(value, null);\n        insertToHead(newNode);\n    }\n\n    public void insertToHead(Node newNode) {\n        if (head == null) {\n            head = newNode;\n        } else {\n            newNode.next = head;\n            head = newNode;\n        }\n    }\n\n    //顺序插入\n    //链表尾部插入\n    public void insertTail(int value){\n\n        Node newNode = new Node(value, null);\n        //空链表，可以插入新节点作为head，也可以不操作\n        if (head == null){\n            head = newNode;\n\n        }else{\n            Node q = head;\n            while(q.next != null){\n                q = q.next;\n            }\n            newNode.next = q.next;\n            q.next = newNode;\n        }\n    }\n    public void insertAfter(Node p, int value) {\n        Node newNode = new Node(value, null);\n        insertAfter(p, newNode);\n    }\n\n    public void insertAfter(Node p, Node newNode) {\n        if (p == null) return;\n\n        newNode.next = p.next;\n        p.next = newNode;\n    }\n\n    public void insertBefore(Node p, int value) {\n        Node newNode = new Node(value, null);\n        insertBefore(p, newNode);\n    }\n\n    public void insertBefore(Node p, Node newNode) {\n        if (p == null) return;\n        if (head == p) {\n            insertToHead(newNode);\n            return;\n        }\n\n        Node q = head;\n        while (q != null && q.next != p) {\n            q = q.next;\n        }\n\n        if (q == null) {\n            return;\n        }\n\n        newNode.next = p;\n        q.next = newNode;\n\n    }\n\n    public void deleteByNode(Node p) {\n        if (p == null || head == null) return;\n\n        if (p == head) {\n            head = head.next;\n            return;\n        }\n\n        Node q = head;\n        while (q != null && q.next != p) {\n            q = q.next;\n        }\n\n        if (q == null) {\n            return;\n        }\n\n        q.next = q.next.next;\n    }\n\n    public void deleteByValue(int value) {\n        if (head == null) return;\n\n        Node p = head;\n        Node q = null;\n        while (p != null && p.data != value) {\n            q = p;\n            p = p.next;\n        }\n\n        if (p == null) return;\n\n        if (q == null) {\n            head = head.next;\n        } else {\n            q.next = q.next.next;\n        }\n\n        // 可重复删除指定value的代码\n        /*\n           if (head != null && head.data == value) {\n           head = head.next;\n           }\n\n           Node pNode = head;\n           while (pNode != null) {\n           if (pNode.next.data == data) {\n           pNode.next = pNode.next.next;\n           continue;\n           }\n           pNode = pNode.next;\n           }\n         */\n    }\n\n    public void printAll() {\n        Node p = head;\n        while (p != null) {\n            System.out.print(p.data + \" \");\n            p = p.next;\n        }\n        System.out.println();\n    }\n\n    //判断true or false\n    public boolean TFResult(Node left, Node right){\n        Node l = left;\n        Node r = right;\n\n        boolean flag=true;\n        System.out.println(\"left_:\"+l.data);\n        System.out.println(\"right_:\"+r.data);\n        while(l != null && r != null){\n           if (l.data == r.data){\n               l = l.next;\n               r = r.next;\n               continue;\n           }else{\n               flag=false;\n               break;\n           }\n\n        }\n\n        System.out.println(\"什么结果\");\n        return flag;\n       /* if (l==null && r==null){\n           System.out.println(\"什么结果\");\n           return true;\n        }else{\n           return false;\n        }*/\n    }\n    //　判断是否为回文 \n\n    public boolean palindrome(){\n       if (head == null){\n           return false;\n       }else{\n           System.out.println(\"开始执行找到中间节点\");\n           Node p = head;\n           Node q = head;\n           if (p.next == null){\n               System.out.println(\"只有一个元素\");\n               return true;\n           }\n           while( q.next != null && q.next.next != null){\n               p = p.next;\n               q = q.next.next;\n\n           }\n\n           System.out.println(\"中间节点\" + p.data);\n           System.out.println(\"开始执行奇数节点的回文判断\");\n           Node leftLink = null;\n           Node rightLink = null;\n           if(q.next == null){\n               //　p 一定为整个链表的中点，且节点数目为奇数\n               rightLink = p.next;\n               leftLink = inverseLinkList(p).next;\n               System.out.println(\"左边第一个节点\"+leftLink.data);\n               System.out.println(\"右边第一个节点\"+rightLink.data);\n\n           }else{\n               //p q　均为中点\n               rightLink = p.next;\n               leftLink = inverseLinkList(p);\n           }\n           return TFResult(leftLink, rightLink);\n\n       }\n    }\n\n    //带结点的链表翻转\n    public Node inverseLinkList_head(Node p){\n        //　Head　为新建的一个头结点\n        Node Head = new Node(9999,null);\n        // p　为原来整个链表的头结点,现在Head指向　整个链表\n        Head.next = p;\n        /*\n        带头结点的链表翻转等价于\n        从第二个元素开始重新头插法建立链表\n        */\n        Node Cur = p.next;\n        p.next = null;\n        Node next = null;\n\n        while(Cur != null){\n            next = Cur.next;\n            Cur.next = Head.next;\n            Head.next = Cur;\n            System.out.println(\"first \" + Head.data);\n\n            Cur = next;\n        }\n\n        //　返回左半部分的中点之前的那个节点\n        //　从此处开始同步像两边比较\n        return Head;\n\n    }\n\n    //无头结点的链表翻转\n    public Node inverseLinkList(Node p){\n\n        Node pre = null;\n        Node r = head;\n        System.out.println(\"z---\" + r.data);\n        Node next= null;\n        while(r !=p){\n            next = r.next;\n\n            r.next = pre;\n            pre = r;\n            r = next;\n        }\n\n        r.next = pre;\n        //　返回左半部分的中点之前的那个节点\n        //　从此处开始同步像两边比较\n        return r;\n\n    }\n    \n    public static Node createNode(int value) {\n        return new Node(value, null);\n    }\n\n    public static class Node {\n        private int data;\n        private Node next;\n\n        public Node(int data, Node next) {\n            this.data = data;\n            this.next = next;\n        }\n\n        public int getData() {\n            return data;\n        }\n    }\n    \n    public static void main(String[]args){\n\n        SinglyLinkedList link = new SinglyLinkedList(); \n        System.out.println(\"hello\");\n        //int data[] = {1};\n        //int data[] = {1,2};\n        //int data[] = {1,2,3,1};\n        //int data[] = {1,2,5};\n        //int data[] = {1,2,2,1};\n       // int data[] = {1,2,5,2,1};\n        int data[] = {1,2,5,3,1};\n\n        for(int i =0; i < data.length; i++){\n            //link.insertToHead(data[i]);\n            link.insertTail(data[i]);\n        }\n       // link.printAll();\n       // Node p = link.inverseLinkList_head(link.head);\n       // while(p != null){\n       //     System.out.println(\"aa\"+p.data);\n       //     p = p.next;\n       // }\n\n        System.out.println(\"打印原始:\");\n        link.printAll();\n        if (link.palindrome()){\n            System.out.println(\"回文\");\n        }else{\n            System.out.println(\"不是回文\");\n        }\n    }\n\n}\n"
  },
  {
    "path": "java/07_linkedlist/.gitkeep",
    "content": ""
  },
  {
    "path": "java/07_linkedlist/LinkedListAlgo.java",
    "content": "package linkedlist;\n\n/**\n * 1) 单链表反转\n * 2) 链表中环的检测\n * 3) 两个有序的链表合并\n * 4) 删除链表倒数第n个结点\n * 5) 求链表的中间结点\n *\n * Author: Zheng\n */\npublic class LinkedListAlgo {\n\n  // 单链表反转\n  public static Node reverse(Node list) {\n    Node curr = list, pre = null;\n    while (curr != null) {\n      Node next = curr.next;\n      curr.next = pre;\n      pre = curr;\n      curr = next;\n    }\n    return pre;\n  }\n\n  // 检测环\n  public static boolean checkCircle(Node list) {\n    if (list == null) return false;\n\n    Node fast = list.next;\n    Node slow = list;\n\n    while (fast != null && fast.next != null) {\n      fast = fast.next.next;\n      slow = slow.next;\n\n      if (slow == fast) return true;\n    }\n\n    return false;\n  }\n\n  // 有序链表合并\n  // public static Node mergeSortedLists(Node la, Node lb) {\n    // if (la == null) return lb;\n    // if (lb == null) return la;\n\n    // Node p = la;\n    // Node q = lb;\n    // Node head;\n    // if (p.data < q.data) {\n    //   head = p;\n    //   p = p.next;\n    // } else {\n    //   head = q;\n    //   q = q.next;\n    // }\n    // Node r = head;\n\n    // while (p != null && q != null) {\n    //   if (p.data < q.data) {\n    //     r.next = p;\n    //     p = p.next;\n    //   } else {\n    //     r.next = q;\n    //     q = q.next;\n    //   }\n    //   r = r.next;\n    // }\n\n    // if (p != null) {\n    //   r.next = p;\n    // } else {\n    //   r.next = q;\n    // }\n\n    // return head;\n   //}\n  \t\n  \t//-----------------------------------------\n\n    // 有序链表合并 Leetcode 21 \n    /**\n \t* Definition for singly-linked list.\n \t* public class ListNode {\n \t*     int val;\n \t*     ListNode next;\n \t*     ListNode(int x) { val = x; }\n \t* }\n\t*/\n   public ListNode mergeTwoLists(ListNode l1, ListNode l2) {\n        ListNode soldier = new ListNode(0); //利用哨兵结点简化实现难度 技巧三\n        ListNode p = soldier;\n        \n        while ( l1 != null && l2 != null ){\n            if ( l1.val < l2.val ){\n                p.next = l1;\n                l1 = l1.next;\n            }\n            else{\n                p.next = l2;\n                l2 = l2.next;\n            }\n            p = p.next;\n        }\n        \n        if (l1 != null) { p.next = l1; }\n        if (l2 != null) { p.next = l2; }\n        return soldier.next;   \n    }\n\n\n  // 删除倒数第K个结点\n  public static Node deleteLastKth(Node list, int k) {\n    Node fast = list;\n    int i = 1;\n    while (fast != null && i < k) {\n      fast = fast.next;\n      ++i;\n    }\n\n    if (fast == null) return list;\n\n    Node slow = list;\n    Node prev = null;\n    while (fast.next != null) {\n      fast = fast.next;\n      prev = slow;\n      slow = slow.next;\n    }\n\n    if (prev == null) {\n      list = list.next;\n    } else {\n      prev.next = prev.next.next;\n    }\n    return list;\n  }\n\n  // 求中间结点\n  public static Node findMiddleNode(Node list) {\n    if (list == null) return null;\n\n    Node fast = list;\n    Node slow = list;\n\n    while (fast != null && fast.next != null) {\n      fast = fast.next.next;\n      slow = slow.next;\n    }\n\n    return slow;\n  }\n\n  public static void printAll(Node list) {\n    Node p = list;\n    while (p != null) {\n      System.out.print(p.data + \" \");\n      p = p.next;\n    }\n    System.out.println();\n  }\n\n  public static Node createNode(int value) {\n    return new Node(value, null);\n  }\n\n  public static class Node {\n    private int data;\n    private Node next;\n\n    public Node(int data, Node next) {\n      this.data = data;\n      this.next = next;\n    }\n\n    public int getData() {\n      return data;\n    }\n  }\n\n}\n"
  },
  {
    "path": "java/08_stack/SampleBrowser.java",
    "content": "package algo.lesson08;\n\n/**\n * 使用前后栈实现浏览器的前进后退。\n * \n * @author chinalwb\n */\npublic class SampleBrowser {\n\n    public static void main(String[] args) {\n        SampleBrowser browser = new SampleBrowser();\n        browser.open(\"http://www.baidu.com\");\n        browser.open(\"http://news.baidu.com/\");\n        browser.open(\"http://news.baidu.com/ent\");\n        browser.goBack();\n        browser.goBack();\n        browser.goForward();\n        browser.open(\"http://www.qq.com\");\n        browser.goForward();\n        browser.goBack();\n        browser.goForward();\n        browser.goBack();\n        browser.goBack();\n        browser.goBack();\n        browser.goBack();\n        browser.checkCurrentPage();\n    }\n\n    private String currentPage;\n    private LinkedListBasedStack backStack;\n    private LinkedListBasedStack forwardStack;\n\n    public SampleBrowser() {\n        this.backStack = new LinkedListBasedStack();\n        this.forwardStack = new LinkedListBasedStack();\n    }\n\n    public void open(String url) {\n        if (this.currentPage != null) {\n            this.backStack.push(this.currentPage);\n            this.forwardStack.clear();\n        }\n        showUrl(url, \"Open\");\n    }\n\n    public boolean canGoBack() {\n        return this.backStack.size() > 0;\n    }\n\n    public boolean canGoForward() {\n        return this.forwardStack.size() > 0;\n    }\n\n    public String goBack() {\n        if (this.canGoBack()) {\n            this.forwardStack.push(this.currentPage);\n            String backUrl = this.backStack.pop();\n            showUrl(backUrl, \"Back\");\n            return backUrl;\n        }\n\n        System.out.println(\"* Cannot go back, no pages behind.\");\n        return null;\n    }\n\n    public String goForward() {\n        if (this.canGoForward()) {\n            this.backStack.push(this.currentPage);\n            String forwardUrl = this.forwardStack.pop();\n            showUrl(forwardUrl, \"Foward\");\n            return forwardUrl;\n        }\n\n        System.out.println(\"** Cannot go forward, no pages ahead.\");\n        return null;\n    }\n\n    public void showUrl(String url, String prefix) {\n        this.currentPage = url;\n        System.out.println(prefix + \" page == \" + url);\n    }\n\n    public void checkCurrentPage() {\n        System.out.println(\"Current page is: \" + this.currentPage);\n    }\n\n    /**\n     * A LinkedList based Stack implementation.\n     */\n    public static class LinkedListBasedStack {\n\n//        public static void main(String[] args) {\n//            LinkedListBasedStack stack = new LinkedListBasedStack();\n//            stack.push(\"A\");\n//            stack.push(\"B\");\n//            stack.push(\"C\");\n//            stack.pop();\n//            stack.push(\"D\");\n//            stack.push(\"E\");\n//            stack.pop();\n//            stack.push(\"F\");\n//            stack.print();\n//\n////        String data = stack.getTopData();\n////        System.out.println(\"Top data == \" + data);\n//        }\n\n        private int size;\n        private Node top;\n\n        static Node createNode(String data, Node next) {\n            return new Node(data, next);\n        }\n\n        public void clear() {\n            this.top = null;\n            this.size = 0;\n        }\n\n        public void push(String data) {\n            Node node = createNode(data, this.top);\n            this.top = node;\n            this.size++;\n        }\n\n        public String pop() {\n            Node popNode = this.top;\n            if (popNode == null) {\n                System.out.println(\"Stack is empty.\");\n                return null;\n            }\n            this.top = popNode.next;\n            if (this.size > 0) {\n                this.size--;\n            }\n            return popNode.data;\n        }\n\n        public String getTopData() {\n            if (this.top == null) {\n                return null;\n            }\n            return this.top.data;\n        }\n\n        public int size() {\n            return this.size;\n        }\n\n        public void print() {\n            System.out.println(\"Print stack:\");\n            Node currentNode = this.top;\n            while (currentNode != null) {\n                String data = currentNode.getData();\n                System.out.print(data + \"\\t\");\n                currentNode = currentNode.next;\n            }\n            System.out.println();\n        }\n\n        public static class Node {\n\n            private String data;\n            private Node next;\n\n            public Node(String data) {\n                this(data, null);\n            }\n\n            public Node(String data, Node next) {\n                this.data = data;\n                this.next = next;\n            }\n\n            public void setData(String data) {\n                this.data = data;\n            }\n\n            public String getData() {\n                return this.data;\n            }\n\n            public void setNext(Node next) {\n                this.next = next;\n            }\n\n            public Node getNext() {\n                return this.next;\n            }\n        }\n\n    }\n}\n"
  },
  {
    "path": "java/08_stack/StackBasedOnLinkedList.java",
    "content": "package stack;\n\n/**\n * 基于链表实现的栈。\n *\n * Author: Zheng\n */\npublic class StackBasedOnLinkedList {\n  private Node top = null;\n\n  public void push(int value) {\n    Node newNode = new Node(value, null);\n    // 判断是否栈空\n    if (top == null) {\n      top = newNode;\n    } else {\n      newNode.next = top;\n      top = newNode;\n    }\n  }\n\n  /**\n   * 我用-1表示栈中没有数据。\n   */\n  public int pop() {\n    if (top == null) return -1;\n    int value = top.data;\n    top = top.next;\n    return value;\n  }\n\n  public void printAll() {\n    Node p = top;\n    while (p != null) {\n      System.out.print(p.data + \" \");\n      p = p.next;\n    }\n    System.out.println();\n  }\n\n  private static class Node {\n    private int data;\n    private Node next;\n\n    public Node(int data, Node next) {\n      this.data = data;\n      this.next = next;\n    }\n\n    public int getData() {\n      return data;\n    }\n  }\n}\n"
  },
  {
    "path": "java/09_queue/ArrayQueue.java",
    "content": "package queue;\n\n/**\n * Created by wangzheng on 2018/10/9.\n */\n// 用数组实现的队列\npublic class ArrayQueue {\n  // 数组：items，数组大小：n\n  private String[] items;\n  private int n = 0;\n  // head表示队头下标，tail表示队尾下标\n  private int head = 0;\n  private int tail = 0;\n\n  // 申请一个大小为capacity的数组\n  public ArrayQueue(int capacity) {\n    items = new String[capacity];\n    n = capacity;\n  }\n\n  // 入队\n  public boolean enqueue(String item) {\n    // 如果tail == n 表示队列已经满了\n    if (tail == n) return false;\n    items[tail] = item;\n    ++tail;\n    return true;\n  }\n\n  // 出队\n  public String dequeue() {\n    // 如果head == tail 表示队列为空\n    if (head == tail) return null;\n    // 为了让其他语言的同学看的更加明确，把--操作放到单独一行来写了\n    String ret = items[head];\n    ++head;\n    return ret;\n  }\n\n  public void printAll() {\n    for (int i = head; i < tail; ++i) {\n      System.out.print(items[i] + \" \");\n    }\n    System.out.println();\n  }\n}\n"
  },
  {
    "path": "java/09_queue/CircularQueue.java",
    "content": "package queue;\n\n/**\n * Created by wangzheng on 2018/10/9.\n */\npublic class CircularQueue {\n  // 数组：items，数组大小：n\n  private String[] items;\n  private int n = 0;\n  // head表示队头下标，tail表示队尾下标\n  private int head = 0;\n  private int tail = 0;\n\n  // 申请一个大小为capacity的数组\n  public CircularQueue(int capacity) {\n    items = new String[capacity];\n    n = capacity;\n  }\n\n  // 入队\n  public boolean enqueue(String item) {\n    // 队列满了\n    if ((tail + 1) % n == head) return false;\n    items[tail] = item;\n    tail = (tail + 1) % n;\n    return true;\n  }\n\n  // 出队\n  public String dequeue() {\n    // 如果head == tail 表示队列为空\n    if (head == tail) return null;\n    String ret = items[head];\n    head = (head + 1) % n;\n    return ret;\n  }\n\n  public void printAll() {\n    if (0 == n) return;\n    for (int i = head; i % n != tail; i = (i + 1) % n) {\n      System.out.print(items[i] + \" \");\n    }\n    System.out.println();\n  }\n}\n"
  },
  {
    "path": "java/09_queue/DynamicArrayQueue.java",
    "content": "package queue;\n\n/**\n * Created by wangzheng on 2018/10/9.\n */\npublic class DynamicArrayQueue {\n  // 数组：items，数组大小：n\n  private String[] items;\n  private int n = 0;\n  // head表示队头下标，tail表示队尾下标\n  private int head = 0;\n  private int tail = 0;\n\n  // 申请一个大小为capacity的数组\n  public DynamicArrayQueue(int capacity) {\n    items = new String[capacity];\n    n = capacity;\n  }\n\n  // 入队操作，将item放入队尾\n  public boolean enqueue(String item) {\n    // tail == n表示队列末尾没有空间了\n    if (tail == n) {\n      // tail ==n && head==0，表示整个队列都占满了\n      if (head == 0) return false;\n      // 数据搬移\n      for (int i = head; i < tail; ++i) {\n        items[i-head] = items[i];\n      }\n      // 搬移完之后重新更新head和tail\n      tail -= head;\n      head = 0;\n    }\n\n    items[tail] = item;\n    tail++;\n    return true;\n  }\n\n  // 出队\n  public String dequeue() {\n    // 如果head == tail 表示队列为空\n    if (head == tail) return null;\n    // 为了让其他语言的同学看的更加明确，把--操作放到单独一行来写了\n    String ret = items[head];\n    ++head;\n    return ret;\n  }\n\n  public void printAll() {\n    for (int i = head; i < tail; ++i) {\n      System.out.print(items[i] + \" \");\n    }\n    System.out.println();\n  }\n}\n"
  },
  {
    "path": "java/09_queue/QueueBasedOnLinkedList.java",
    "content": "package queue;\n\n/**\n * 基于链表实现的队列\n *\n * Author: Zheng\n */\npublic class QueueBasedOnLinkedList {\n\n  // 队列的队首和队尾\n  private Node head = null;\n  private Node tail = null;\n\n  // 入队\n  public void enqueue(String value) {\n    if (tail == null) {\n      Node newNode = new Node(value, null);\n      head = newNode;\n      tail = newNode;\n    } else {\n      tail.next = new Node(value, null);\n      tail = tail.next;\n    }\n  }\n\n  // 出队\n  public String dequeue() {\n    if (head == null) return null;\n\n    String value = head.data;\n    head = head.next;\n    if (head == null) {\n      tail = null;\n    }\n    return value;\n  }\n\n  public void printAll() {\n    Node p = head;\n    while (p != null) {\n      System.out.print(p.data + \" \");\n      p = p.next;\n    }\n    System.out.println();\n  }\n\n  private static class Node {\n    private String data;\n    private Node next;\n\n    public Node(String data, Node next) {\n      this.data = data;\n      this.next = next;\n    }\n\n    public String getData() {\n      return data;\n    }\n  }\n\n}\n"
  },
  {
    "path": "java/11_sorts/InsertionSortAdd.java",
    "content": "package sorts;\n\nimport java.util.Arrays;\n\n/**\n * 插入排序（插入位置，从头至尾搜索）\n * @Author： ooooor\n */\npublic class InsertionSortAdd {\n\n    public static void main(String[] args) {\n        int[] data = new int[]{4, 6, 5, 3, 7, 1, 2};\n        fromStartToEnd(Arrays.copyOf(data, data.length));\n        System.out.println(Arrays.toString(data));\n    }\n\n    /**\n     * 查询插入位置时， 从头至尾搜索\n     * @param data\n     */\n    private static void fromStartToEnd(int[] data) {\n        for (int i=1; i < data.length; i++) {\n            int value = data[i];\n\n            int[] tmp = new int[2];\n            int change = i;\n            for (int j=0; j < i; j++) {\n                if(value >= data[j]) {\n                    continue;\n                }\n\n                int index = j%2;\n                if (change == i) {\n                    tmp[Math.abs(index-1)] = data[j];\n                    change = j;\n                }\n                tmp[index] = data[j+1];\n                if (0 == index) {\n                    data[j+1] = tmp[index+1];\n                } else {\n                    data[j+1] = tmp[index-1];\n                }\n            }\n            data[change] = value;\n        }\n    }\n\n}\n"
  },
  {
    "path": "java/11_sorts/Sorts.java",
    "content": "package sorts;\n\nimport java.util.Arrays;\n\n/**\n * 冒泡排序、插入排序、选择排序\n * <p>\n * Author: Zheng\n */\npublic class Sorts {\n\n    // 冒泡排序，a是数组，n表示数组大小\n    public static void bubbleSort(int[] a, int n) {\n        if (n <= 1) return;\n\n        for (int i = 0; i < n; ++i) {\n            // 提前退出标志位\n            boolean flag = false;\n            for (int j = 0; j < n - i - 1; ++j) {\n                if (a[j] > a[j + 1]) { // 交换\n                    int tmp = a[j];\n                    a[j] = a[j + 1];\n                    a[j + 1] = tmp;\n                    // 此次冒泡有数据交换\n                    flag = true;\n                }\n            }\n            if (!flag) break;  // 没有数据交换，提前退出\n        }\n    }\n\n    /**\n     * 冒泡排序改进:在每一轮排序后记录最后一次元素交换的位置,作为下次比较的边界,\n     * 对于边界外的元素在下次循环中无需比较.\n     */ \n    public static void bubbleSort2(int[] a, int n) {\n        if (n <= 1) return;\n\n        // 最后一次交换的位置\n        int lastExchange = 0;\n        // 无序数据的边界,每次只需要比较到这里即可退出\n        int sortBorder = n - 1;\n        for (int i = 0; i < n; i++) {\n            // 提前退出标志位\n            boolean flag = false;\n            for (int j = 0; j < sortBorder; j++) {\n                if (a[j] > a[j + 1]) {\n                    int tmp = a[j];\n                    a[j] = a[j + 1];\n                    a[j + 1] = tmp;\n                    // 此次冒泡有数据交换\n                    flag = true;\n                    // 更新最后一次交换的位置\n                    lastExchange = j;\n                }\n            }\n            sortBorder = lastExchange;\n            if (!flag) break;    // 没有数据交换，提前退出\n        }\n    }\n\n    // 插入排序，a表示数组，n表示数组大小\n    public static void insertionSort(int[] a, int n) {\n        if (n <= 1) return;\n\n        for (int i = 1; i < n; ++i) {\n            int value = a[i];\n            int j = i - 1;\n            // 查找要插入的位置并移动数据\n            for (; j >= 0; --j) {\n                if (a[j] > value) {\n                    a[j + 1] = a[j];\n                } else {\n                    break;\n                }\n            }\n            a[j + 1] = value;\n        }\n    }\n\n    // 选择排序，a表示数组，n表示数组大小\n    public static void selectionSort(int[] a, int n) {\n        if (n <= 1) return;\n\n        for (int i = 0; i < n - 1; ++i) {\n            // 查找最小值\n            int minIndex = i;\n            for (int j = i + 1; j < n; ++j) {\n                if (a[j] < a[minIndex]) {\n                    minIndex = j;\n                }\n            }\n\n            // 交换\n            int tmp = a[i];\n            a[i] = a[minIndex];\n            a[minIndex] = tmp;\n        }\n    }\n\n    public static void main(String[] args) {\n        int[] array = new int[]{3, 4, 2, 1, 5, 6, 7, 8};\n        bubbleSort2(array, array.length);\n        System.out.println(Arrays.toString(array));\n    }\n}\n"
  },
  {
    "path": "java/11_sorts/SortsAddOn.java",
    "content": "package sorts;\n\n/**\n * 向下冒泡算法 （或许比冒泡更易懂的排序算法？）\n * 希尔排序\n *\n * Author: wliu\n */\npublic class SortsAddOn {\n\n  public static void main(String[] args) {\n    int[] arr = {3, 2, 6, 4, 5, 1, 9, 20, 13, 16};\n    // bubbleDownSort(arr);\n    shellSort(arr);\n    print(arr);\n  }\n\n  /**\n   * 向下冒泡。可能比冒泡更易懂？\n   * \n   * 算法概要：\n   * 从0开始，用这个元素去跟后面的所有元素比较，如果发现这个元素大于后面的某个元素，则交换。\n   * 3 2 6 4 5 1\n   * 第一趟是从 index=0 也就是 3， 开始跟index=1及其后面的数字比较\n   *  3 大于 2，交换，变为 2 3 6 4 5 1，此时index=0的位置变为了2\n   *    接下来将用2跟index=2比较\n   *  2 不大于 6 不交换\n   *  2 不大于 4 不交换\n   *  2 不大于 5 不交换\n   *  2 大于 1，交换，变为 1 3 6 4 5 2，第一趟排序完成。\n   * \n   * 第二趟是从 index=1 也就是 3，开始跟index=2及其后面的数字比较\n   *  3 不大于 6 不交换\n   *  3 不大于 4 不交换\n   *  3 不大于 5 不交换\n   *  3 大于 2，交换，变为 1 2 6 4 5 3，第二趟排序完成。\n   * \n   * 第三趟是从 index=2 也就是 6，开始跟index=3及其后面的数字比较\n   *  6 大于 4，交换，变为 1 2 4 6 5 3, 此时 index = 2 的位置变为了4\n   *     接下来将用4跟index=4比较\n   *  4 不大于 5 不交换\n   *  4 大于 3，交换，变为 1 2 3 6 5 4，第三趟排序完成。\n   * \n   * 第四趟是从 index=3 也就是 6，开始跟index=4及其后面的数字比较\n   *  6 大于 5，交换，变为 1 2 3 5 6 4, 此时 index = 3 的位置变为了5\n   *     接下来将用5跟index=5比较\n   *  5 大于 4，交换，变为 1 2 3 4 6 5, 第四趟排序完成。\n   *\n   * 第五趟是从 index=4 也就是 6，开始跟index=5及其后面的数字比较\n   *  6 大于 5，交换，变为 1 2 3 4 5 6, 此时 index = 4 的位置变为了5\n   *     接下来将用5跟index=6比较\n   *  index = 6 已经不满足 index < length 的条件，整个排序完成。\n   */\n  private static void bubbleDownSort(int[] arr) {\n    int len = arr.length;\n    if (len == 1) return;\n\n    for (int i = 0; i < len; i++) {\n      for (int j = i + 1; j < len; j++) {\n        if (arr[i] > arr[j]) {\n          int tmp = arr[i];\n          arr[i] = arr[j];\n          arr[j] = tmp;\n        }\n      }\n    }\n  }\n\n\n  private static void shellSort(int[] arr) {\n    int len = arr.length;\n    if (len == 1) return;\n\n    int step = len / 2;\n    while (step >= 1) {\n      for (int i = step; i < len; i++) {\n        int value = arr[i];\n        int j = i - step;\n        for (; j >= 0; j -= step) {\n          if (value < arr[j]) {\n            arr[j+step] = arr[j];\n          } else {\n            break;\n          }\n        }\n        arr[j+step] = value;\n      }\n\n      step = step / 2;\n    }\n  }\n\n  private static void print(int[] arr) {\n    System.out.println(\"Print array:\");\n    for (int x : arr) {\n      System.out.print(x + \"\\t\");\n    }\n    System.out.println(\"\");\n  }\n}\n"
  },
  {
    "path": "java/12_sorts/KthSmallest.java",
    "content": "package sort;\n\n/**\n * @author wangjunwei87\n * @since 2019-03-10\n */\npublic class KthSmallest {\n\n    public static int kthSmallest(int[] arr, int k) {\n        if (arr == null || arr.length < k) {\n            return -1;\n        }\n\n        int partition = partition(arr, 0, arr.length - 1);\n        while (partition + 1 != k) {\n            if (partition + 1 < k) {\n                partition = partition(arr, partition + 1, arr.length - 1);\n            } else {\n                partition = partition(arr, 0, partition - 1);\n            }\n        }\n\n        return arr[partition];\n    }\n\n    private static int partition(int[] arr, int p, int r) {\n        int pivot = arr[r];\n\n        int i = p;\n        for (int j = p; j < r; j++) {\n            // 这里要是 <= ，不然会出现死循环，比如查找数组 [1,1,2] 的第二小的元素\n            if (arr[j] <= pivot) {\n                swap(arr, i, j);\n                i++;\n            }\n        }\n\n        swap(arr, i, r);\n\n        return i;\n    }\n\n    private static void swap(int[] arr, int i, int j) {\n        if (i == j) {\n            return;\n        }\n\n        int tmp = arr[i];\n        arr[i] = arr[j];\n        arr[j] = tmp;\n    }\n}\n"
  },
  {
    "path": "java/12_sorts/MergeSort.java",
    "content": "package sorts;\n\n/**\n * Created by wangzheng on 2018/10/16.\n */\npublic class MergeSort {\n\n  // 归并排序算法, a是数组，n表示数组大小\n  public static void mergeSort(int[] a, int n) {\n    mergeSortInternally(a, 0, n-1);\n  }\n\n  // 递归调用函数\n  private static void mergeSortInternally(int[] a, int p, int r) {\n    // 递归终止条件\n    if (p >= r) return;\n\n    // 取p到r之间的中间位置q,防止（p+r）的和超过int类型最大值\n    int q = p + (r - p)/2;\n    // 分治递归\n    mergeSortInternally(a, p, q);\n    mergeSortInternally(a, q+1, r);\n\n    // 将A[p...q]和A[q+1...r]合并为A[p...r]\n    merge(a, p, q, r);\n  }\n\n  private static void merge(int[] a, int p, int q, int r) {\n    int i = p;\n    int j = q+1;\n    int k = 0; // 初始化变量i, j, k\n    int[] tmp = new int[r-p+1]; // 申请一个大小跟a[p...r]一样的临时数组\n    while (i<=q && j<=r) {\n      if (a[i] <= a[j]) {\n        tmp[k++] = a[i++]; // i++等于i:=i+1\n      } else {\n        tmp[k++] = a[j++];\n      }\n    }\n\n    // 判断哪个子数组中有剩余的数据\n    int start = i;\n    int end = q;\n    if (j <= r) {\n      start = j;\n      end = r;\n    }\n\n    // 将剩余的数据拷贝到临时数组tmp\n    while (start <= end) {\n      tmp[k++] = a[start++];\n    }\n\n    // 将tmp中的数组拷贝回a[p...r]\n    for (i = 0; i <= r-p; ++i) {\n      a[p+i] = tmp[i];\n    }\n  }\n\n  /**\n   * 合并(哨兵)\n   *\n   * @param arr\n   * @param p\n   * @param q\n   * @param r\n   */\n  private static void mergeBySentry(int[] arr, int p, int q, int r) {\n    int[] leftArr = new int[q - p + 2];\n    int[] rightArr = new int[r - q + 1];\n\n    for (int i = 0; i <= q - p; i++) {\n      leftArr[i] = arr[p + i];\n    }\n    // 第一个数组添加哨兵（最大值）\n    leftArr[q - p + 1] = Integer.MAX_VALUE;\n\n    for (int i = 0; i < r - q; i++) {\n      rightArr[i] = arr[q + 1 + i];\n    }\n    // 第二个数组添加哨兵（最大值）\n    rightArr[r-q] = Integer.MAX_VALUE;\n\n    int i = 0;\n    int j = 0;\n    int k = p;\n    while (k <= r) {\n      // 当左边数组到达哨兵值时，i不再增加，直到右边数组读取完剩余值，同理右边数组也一样\n      if (leftArr[i] <= rightArr[j]) {\n        arr[k++] = leftArr[i++];\n      } else {\n        arr[k++] = rightArr[j++];\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "java/12_sorts/QuickSort.java",
    "content": "package sorts;\n\n/**\n * Created by wangzheng on 2018/10/16.\n */\npublic class QuickSort {\n\n  // 快速排序，a是数组，n表示数组的大小\n  public static void quickSort(int[] a, int n) {\n    quickSortInternally(a, 0, n-1);\n  }\n\n  // 快速排序递归函数，p,r为下标\n  private static void quickSortInternally(int[] a, int p, int r) {\n    if (p >= r) return;\n\n    int q = partition(a, p, r); // 获取分区点\n    quickSortInternally(a, p, q-1);\n    quickSortInternally(a, q+1, r);\n  }\n\n  private static int partition(int[] a, int p, int r) {\n    int pivot = a[r];\n    int i = p;\n    for(int j = p; j < r; ++j) {\n      if (a[j] < pivot) {\n        if (i == j) {\n          ++i;\n        } else {\n          int tmp = a[i];\n          a[i++] = a[j];\n          a[j] = tmp;\n        }\n      }\n    }\n\n    int tmp = a[i];\n    a[i] = a[r];\n    a[r] = tmp;\n\n    System.out.println(\"i=\" + i);\n    return i;\n  }\n}\n"
  },
  {
    "path": "java/12_sorts/Sorts.java",
    "content": "package com.study.sort;\n\nimport java.util.Arrays;\n\n/**\n * 冒泡，选择，插入，快速，归并\n *\n * @author ldb\n * @date 2019-10-08 16:09\n */\npublic class Sorts {\n\n    /**\n     * 冒泡排序\n     *\n     * @param arr\n     */\n    public static void bubbleSort(int[] arr) {\n        for (int i = 0; i < arr.length; i++) {\n            for (int j = 0; j < arr.length - 1 - i; j++) {\n                if (arr[j] > arr[j + 1]) {\n                    int temp = arr[j];\n                    arr[j] = arr[j + 1];\n                    arr[j + 1] = temp;\n                }\n            }\n        }\n    }\n\n    /**\n     * 优化冒泡排序\n     *\n     * @param arr\n     */\n    public static void bubbleSort2(int[] arr) {\n        for (int i = 0; i < arr.length - 1; i++) {\n            boolean flag = true;\n            for (int j = 0; j < arr.length - 1 - i; j++) {\n                if (arr[j] > arr[j + 1]) {\n                    int temp = arr[j];\n                    arr[j] = arr[j + 1];\n                    arr[j + 1] = temp;\n                    flag = false;\n                }\n            }\n            if (flag) {\n                break;\n            }\n        }\n    }\n\n    /**\n     * 插入排序\n     *\n     * @param arr\n     */\n    public static void insertSort(int[] arr) {\n        for (int i = 1; i < arr.length; i++) {\n            int val = arr[i];\n            int index = i - 1;\n            while (index >= 0 && arr[index] > val) {\n                arr[index + 1] = arr[index];\n                index--;\n            }\n            arr[index + 1] = val;\n        }\n    }\n\n    /**\n     * 插入排序\n     *\n     * @param arr\n     * @param n   表示数组有用大小\n     */\n    public static void insertSort(int[] arr, int n) {\n        for (int i = 1; i < n; i++) {\n            int val = arr[i];\n            int index = i - 1;\n            while (index >= 0 && arr[index] > val) {\n                arr[index + 1] = arr[index];\n                index--;\n            }\n            arr[index + 1] = val;\n        }\n    }\n\n    /**\n     * 选择排序\n     *\n     * @param arr\n     */\n    public static void selectSort(int[] arr) {\n\n        for (int i = 0; i < arr.length - 1; i++) {\n            int minIndex = i;\n            for (int j = i + 1; j < arr.length; j++) {\n                if (arr[minIndex] > arr[j]) {\n                    minIndex = j;\n                }\n            }\n            // 交换\n            int temp = arr[i];\n            arr[i] = arr[minIndex];\n            arr[minIndex] = temp;\n        }\n    }\n\n    /**\n     * 归并排序\n     *\n     * @param arr\n     */\n    public static void mergeSort(int[] arr, int left, int right) {\n        if (left >= right) {\n            return;\n        }\n        int q = (left + right) / 2;\n        mergeSort(arr, left, q);\n        mergeSort(arr, q + 1, right);\n        merge2(arr, left, q, right);\n\n    }\n\n    private static void merge2(int[] arr, int left, int q, int right) {\n        int[] leftArr = new int[q - left + 2];\n        int[] rightArr = new int[right - q + 1];\n\n        for (int i = 0; i <= q - left; i++) {\n            leftArr[i] = arr[left + i];\n        }\n        // 第一个数组添加哨兵（最大值）\n        leftArr[q - left + 1] = Integer.MAX_VALUE;\n\n        for (int i = 0; i < right - q; i++) {\n            rightArr[i] = arr[q + 1 + i];\n        }\n        // 第二个数组添加哨兵（最大值）\n        rightArr[right - q] = Integer.MAX_VALUE;\n\n        int i = 0;\n        int j = 0;\n        int k = left;\n        while (k <= right) {\n            // 当左边数组到达哨兵值时，i不再增加，直到右边数组读取完剩余值，同理右边数组也一样\n            if (leftArr[i] <= rightArr[j]) {\n                arr[k++] = leftArr[i++];\n            } else {\n                arr[k++] = rightArr[j++];\n            }\n        }\n    }\n\n    private static void merge(int[] arr, int left, int q, int right) {\n        int i = left;\n        int j = q + 1;\n        int k = 0;\n        int[] tmp = new int[right - left + 1];\n        while (i <= q && j <= right) {\n            if (arr[i] <= arr[j]) {\n                tmp[k++] = arr[i++];\n            } else {\n                tmp[k++] = arr[j++];\n            }\n        }\n        int start = i;\n        int end = q;\n        if (j <= right) {\n            start = j;\n            end = right;\n        }\n        while (start <= end) {\n            tmp[k++] = arr[start++];\n        }\n        for (int l = 0; l <= right - left; l++) {\n            arr[l + left] = tmp[l];\n        }\n\n    }\n\n    /**\n     * 快速排序\n     *\n     * @param arr\n     */\n    public static void quickSort(int[] arr, int left, int right) {\n        if (left >= right) {\n            return;\n        }\n        int q = partition2(arr, left, right);\n        quickSort(arr, left, q - 1);\n        quickSort(arr, q + 1, right);\n    }\n\n    private static int partition(int[] arr, int left, int right) {\n        int pivot = arr[right];\n        int i = left;\n        for (int j = left; j < right; j++) {\n            if (arr[j] < pivot) {\n                if (i == j) {\n                    ++i;\n                } else {\n                    int tmp = arr[i];\n                    arr[i++] = arr[j];\n                    arr[j] = tmp;\n                }\n            }\n        }\n        int tmp = arr[i];\n        arr[i] = arr[right];\n        arr[right] = tmp;\n        return i;\n    }\n\n    private static int partition2(int[] arr, int left, int right) {\n        // 三数取中法 , 随机数在这里写\n        int middle = (left + right) / 2;\n        int pivot = arr[middle];\n        // 交换到最右边\n        int val = arr[right];\n        arr[right] = pivot;\n        arr[middle] = val;\n        int i = left;\n        for (int j = left; j < right; j++) {\n            if (arr[j] < pivot) {\n                if (i == j) {\n                    ++i;\n                } else {\n                    int tmp = arr[i];\n                    arr[i++] = arr[j];\n                    arr[j] = tmp;\n                }\n            }\n        }\n        int tmp = arr[i];\n        arr[i] = arr[right];\n        arr[right] = tmp;\n        return i;\n    }\n\n    /**\n     * 三向切分快速排序\n     *\n     * @param arr\n     * @param left\n     * @param right\n     * @return\n     */\n    private static void quickSort3(int[] arr, int left, int right) {\n        if (left >= right) {\n            return;\n        }\n        int l = left;\n        int k = left + 1;\n        int r = right;\n        int pivot = arr[l];\n\n        while (k <= r) {\n            if (arr[k] < pivot) {\n                int tmp = arr[l];\n                arr[l] = arr[k];\n                arr[k] = tmp;\n                l++;\n                k++;\n            } else if (arr[k] == pivot) {\n                k++;\n            } else {\n                if (arr[r] > pivot) {\n                    r--;\n                } else if (arr[r] == pivot) {\n                    int tmp = arr[k];\n                    arr[k] = arr[r];\n                    arr[r] = tmp;\n                    k++;\n                    r--;\n                } else {\n                    int tmp = arr[l];\n                    arr[l] = arr[r];\n                    arr[r] = arr[k];\n                    arr[k] = tmp;\n                    l++;\n                    k++;\n                    r--;\n                }\n            }\n        }\n\n        quickSort(arr, left, l - 1);\n        quickSort(arr, r + 1, right);\n    }\n\n    /**\n     * 双轴快速排序\n     *\n     * @param arr\n     * @param left\n     * @param right\n     */\n    private static void quickSort4(int[] arr, int left, int right) {\n        if (left >= right) {\n            return;\n        }\n        int l = left;\n        int k = left + 1;\n        int r = right;\n        // 判断pivot1 与 pivot2 大小\n        if (arr[l] > arr[r]) {\n            int tmp = arr[l];\n            arr[l] = arr[r];\n            arr[r] = tmp;\n        }\n        int pivot1 = arr[l];\n        int pivot2 = arr[r];\n\n        while (k < r) {\n            if (arr[k] < pivot1) {\n                l++;\n                if (l != k) {\n                    int tmp = arr[l];\n                    arr[l] = arr[k];\n                    arr[k] = tmp;\n                }\n                k++;\n            } else if (arr[k] >= pivot1 && arr[k] <= pivot2) {\n                k++;\n            } else {\n                --r;\n                if (arr[r] > pivot2) {\n                } else if (arr[r] >= pivot1 && arr[r] <= pivot2) {\n                    int tmp = arr[k];\n                    arr[k] = arr[r];\n                    arr[r] = tmp;\n                    k++;\n                } else {\n                    l++;\n                    int tmp = arr[l];\n                    arr[l] = arr[r];\n                    arr[r] = arr[k];\n                    arr[k] = tmp;\n                    k++;\n                }\n            }\n        }\n\n        // 交换pivot1 和 pivot2\n        arr[left] = arr[l];\n        arr[l] = pivot1;\n        arr[right] = arr[r];\n        arr[r] = pivot2;\n\n        quickSort(arr, left, l - 1);\n        quickSort(arr, l + 1, r - 1);\n        quickSort(arr, r + 1, right);\n    }\n\n    /**\n     * O(n)  时间复杂度内求无序数组中的第 K  大元素。比如， 4 ， 2 ， 5 ， 12 ， 3  这样一组数据，第 3  大元素就是 4 。\n     *\n     * @param arr\n     */\n    public static int sort(int[] arr, int l, int r, int k) {\n        if (l >= r) {\n            return 0;\n        }\n        int p = partition(arr, l, r);\n        if ((p + 1) == k) {\n            return arr[p];\n        } else if ((p + 1) < k) {\n            return sort(arr, p + 1, r, k);\n        } else {\n            return sort(arr, l, p - 1, k);\n        }\n    }\n\n    public static void main(String[] args) {\n        int[] arr = {2, 1, 5, 6, 8, 4, 12, 11, 13, 15, 7, 9, 0, -1};\n//        bubbleSort(arr);\n//        bubbleSort2(arr);\n//        selectSort(arr);\n//        mergeSort(arr, 0, arr.length - 1);\n//        quickSort4(arr, 0, arr.length - 1);\n\n        Arrays.sort(arr);\n        print(arr);\n\n    }\n\n    public static void print(int[] arr) {\n        for (int i : arr) {\n            System.out.print(i + \" \");\n        }\n        System.out.println();\n    }\n\n\n}\n"
  },
  {
    "path": "java/13_sorts/BucketSort.java",
    "content": "/**\n * @Description:桶排序算法\n * @Author: Hoda\n * @Date: Create in 2019-06-01\n * @Modified By:\n * @Modified Date:\n */\npublic class BucketSort {\n\n    /**\n     * 桶排序\n     *\n     * @param arr 数组\n     * @param bucketSize 桶容量\n     */\n    public static void bucketSort(int[] arr, int bucketSize) {\n        if (arr.length < 2) {\n            return;\n        }\n\n        // 数组最小值\n        int minValue = arr[0];\n        // 数组最大值\n        int maxValue = arr[1];\n        for (int i = 0; i < arr.length; i++) {\n            if (arr[i] < minValue) {\n                minValue = arr[i];\n            } else if (arr[i] > maxValue) {\n                maxValue = arr[i];\n            }\n        }\n\n        // 桶数量\n        int bucketCount = (maxValue - minValue) / bucketSize + 1;\n        int[][] buckets = new int[bucketCount][bucketSize];\n        int[] indexArr = new int[bucketCount];\n\n        // 将数组中值分配到各个桶里\n        for (int i = 0; i < arr.length; i++) {\n            int bucketIndex = (arr[i] - minValue) / bucketSize;\n            if (indexArr[bucketIndex] == buckets[bucketIndex].length) {\n                ensureCapacity(buckets, bucketIndex);\n            }\n            buckets[bucketIndex][indexArr[bucketIndex]++] = arr[i];\n        }\n\n        // 对每个桶进行排序，这里使用了快速排序\n        int k = 0;\n        for (int i = 0; i < buckets.length; i++) {\n            if (indexArr[i] == 0) {\n                continue;\n            }\n            quickSortC(buckets[i], 0, indexArr[i] - 1);\n            for (int j = 0; j < indexArr[i]; j++) {\n                arr[k++] = buckets[i][j];\n            }\n        }\n    }\n\n    /**\n     * 数组扩容\n     *\n     * @param buckets\n     * @param bucketIndex\n     */\n    private static void ensureCapacity(int[][] buckets, int bucketIndex) {\n        int[] tempArr = buckets[bucketIndex];\n        int[] newArr = new int[tempArr.length * 2];\n        for (int j = 0; j < tempArr.length; j++) {\n            newArr[j] = tempArr[j];\n        }\n        buckets[bucketIndex] = newArr;\n    }\n\n    /**\n     * 快速排序递归函数\n     *\n     * @param arr\n     * @param p\n     * @param r\n     */\n    private static void quickSortC(int[] arr, int p, int r) {\n        if (p >= r) {\n            return;\n        }\n\n        int q = partition(arr, p, r);\n        quickSortC(arr, p, q - 1);\n        quickSortC(arr, q + 1, r);\n    }\n\n    /**\n     * 分区函数\n     *\n     * @param arr\n     * @param p\n     * @param r\n     * @return 分区点位置\n     */\n    private static int partition(int[] arr, int p, int r) {\n        int pivot = arr[r];\n        int i = p;\n        for (int j = p; j < r; j++) {\n            if (arr[j] <= pivot) {\n                swap(arr, i, j);\n                i++;\n            }\n        }\n\n        swap(arr, i, r);\n        return i;\n    }\n\n    /**\n     * 交换\n     *\n     * @param arr\n     * @param i\n     * @param j\n     */\n    private static void swap(int[] arr, int i, int j) {\n        if (i == j) {\n            return;\n        }\n\n        int tmp = arr[i];\n        arr[i] = arr[j];\n        arr[j] = tmp;\n    }\n}\n"
  },
  {
    "path": "java/13_sorts/CountingSort.java",
    "content": "package sorts;\n\n/**\n * 计数排序\n *\n * Author: ZHENG\n */\npublic class CountingSort {\n\n  // 计数排序，a是数组，n是数组大小。假设数组中存储的都是非负整数。\n  public static void countingSort(int[] a, int n) {\n    if (n <= 1) return;\n\n    // 查找数组中数据的范围\n    int max = a[0];\n    for (int i = 1; i < n; ++i) {\n      if (max < a[i]) {\n        max = a[i];\n      }\n    }\n\n    // 申请一个计数数组c，下标大小[0,max]\n    int[] c = new int[max + 1];\n\n    // 计算每个元素的个数，放入c中\n    for (int i = 0; i < n; ++i) {\n      c[a[i]]++;\n    }\n\n    // 依次累加\n    for (int i = 1; i < max + 1; ++i) {\n      c[i] = c[i-1] + c[i];\n    }\n\n    // 临时数组r，存储排序之后的结果\n    int[] r = new int[n];\n    // 计算排序的关键步骤了，有点难理解\n    for (int i = n - 1; i >= 0; --i) {\n      int index = c[a[i]]-1;\n      r[index] = a[i];\n      c[a[i]]--;\n    }\n\n    // 将结果拷贝会a数组\n    for (int i = 0; i < n; ++i) {\n      a[i] = r[i];\n    }\n  }\n\n}\n"
  },
  {
    "path": "java/13_sorts/RadixSort.java",
    "content": "/**\n * @Description:基数排序\n * @Author: Hoda\n * @Date: Create in 2019-07-25\n * @Modified By:\n * @Modified Date:\n */\npublic class RadixSort {\n\n    /**\n     * 基数排序\n     *\n     * @param arr\n     */\n    public static void radixSort(int[] arr) {\n        int max = arr[0];\n        for (int i = 0; i < arr.length; i++) {\n            if (arr[i] > max) {\n                max = arr[i];\n            }\n        }\n\n        // 从个位开始，对数组arr按\"指数\"进行排序\n        for (int exp = 1; max / exp > 0; exp *= 10) {\n            countingSort(arr, exp);\n        }\n    }\n\n    /**\n     * 计数排序-对数组按照\"某个位数\"进行排序\n     *\n     * @param arr\n     * @param exp 指数\n     */\n    public static void countingSort(int[] arr, int exp) {\n        if (arr.length <= 1) {\n            return;\n        }\n\n        // 计算每个元素的个数\n        int[] c = new int[10];\n        for (int i = 0; i < arr.length; i++) {\n            c[(arr[i] / exp) % 10]++;\n        }\n\n        // 计算排序后的位置\n        for (int i = 1; i < c.length; i++) {\n            c[i] += c[i - 1];\n        }\n\n        // 临时数组r，存储排序之后的结果\n        int[] r = new int[arr.length];\n        for (int i = arr.length - 1; i >= 0; i--) {\n            r[c[(arr[i] / exp) % 10] - 1] = arr[i];\n            c[(arr[i] / exp) % 10]--;\n        }\n\n        for (int i = 0; i < arr.length; i++) {\n            arr[i] = r[i];\n        }\n    }\n}\n"
  },
  {
    "path": "java/17_skiplist/SkipList.java",
    "content": "package skiplist;\n\n\n/**\n * 跳表的一种实现方法。\n * 跳表中存储的是正整数，并且存储的是不重复的。\n *\n * Author：ZHENG\n */\npublic class SkipList {\n\n  private static final float SKIPLIST_P = 0.5f;\n  private static final int MAX_LEVEL = 16;\n\n  private int levelCount = 1;\n\n  private Node head = new Node();  // 带头链表\n\n  public Node find(int value) {\n    Node p = head;\n    for (int i = levelCount - 1; i >= 0; --i) {\n      while (p.forwards[i] != null && p.forwards[i].data < value) {\n        p = p.forwards[i];\n      }\n    }\n\n    if (p.forwards[0] != null && p.forwards[0].data == value) {\n      return p.forwards[0];\n    } else {\n      return null;\n    }\n  }\n\n  public void insert(int value) {\n    int level = randomLevel();\n    Node newNode = new Node();\n    newNode.data = value;\n    newNode.maxLevel = level;\n    Node update[] = new Node[level];\n    for (int i = 0; i < level; ++i) {\n      update[i] = head;\n    }\n\n    // record every level largest value which smaller than insert value in update[]\n    Node p = head;\n    for (int i = level - 1; i >= 0; --i) {\n      while (p.forwards[i] != null && p.forwards[i].data < value) {\n        p = p.forwards[i];\n      }\n      update[i] = p;// use update save node in search path\n    }\n\n    // in search path node next node become new node forwords(next)\n    for (int i = 0; i < level; ++i) {\n      newNode.forwards[i] = update[i].forwards[i];\n      update[i].forwards[i] = newNode;\n    }\n\n    // update node hight\n    if (levelCount < level) levelCount = level;\n  }\n\n  public void delete(int value) {\n    Node[] update = new Node[levelCount];\n    Node p = head;\n    for (int i = levelCount - 1; i >= 0; --i) {\n      while (p.forwards[i] != null && p.forwards[i].data < value) {\n        p = p.forwards[i];\n      }\n      update[i] = p;\n    }\n\n    if (p.forwards[0] != null && p.forwards[0].data == value) {\n      for (int i = levelCount - 1; i >= 0; --i) {\n        if (update[i].forwards[i] != null && update[i].forwards[i].data == value) {\n          update[i].forwards[i] = update[i].forwards[i].forwards[i];\n        }\n      }\n    }\n\n    while (levelCount>1&&head.forwards[levelCount]==null){\n      levelCount--;\n    }\n\n  }\n\n  // 理论来讲，一级索引中元素个数应该占原始数据的 50%，二级索引中元素个数占 25%，三级索引12.5% ，一直到最顶层。\n  // 因为这里每一层的晋升概率是 50%。对于每一个新插入的节点，都需要调用 randomLevel 生成一个合理的层数。\n  // 该 randomLevel 方法会随机生成 1~MAX_LEVEL 之间的数，且 ：\n  //        50%的概率返回 1\n  //        25%的概率返回 2\n  //      12.5%的概率返回 3 ...\n  private int randomLevel() {\n    int level = 1;\n\n    while (Math.random() < SKIPLIST_P && level < MAX_LEVEL)\n      level += 1;\n    return level;\n  }\n\n  public void printAll() {\n    Node p = head;\n    while (p.forwards[0] != null) {\n      System.out.print(p.forwards[0] + \" \");\n      p = p.forwards[0];\n    }\n    System.out.println();\n  }\n\n  public class Node {\n    private int data = -1;\n    private Node forwards[] = new Node[MAX_LEVEL];\n    private int maxLevel = 0;\n\n    @Override\n    public String toString() {\n      StringBuilder builder = new StringBuilder();\n      builder.append(\"{ data: \");\n      builder.append(data);\n      builder.append(\"; levels: \");\n      builder.append(maxLevel);\n      builder.append(\" }\");\n\n      return builder.toString();\n    }\n  }\n\n}\n"
  },
  {
    "path": "java/17_skiplist/SkipList2.java",
    "content": "package com.study.skiplist;\n\nimport java.util.Random;\n\n/**\n * 1，跳表的一种实现方法，用于练习。跳表中存储的是正整数，并且存储的是不重复的。\n * 2，本类是参考作者zheng ，自己学习，优化了添加方法\n * 3，看完这个，我觉得再看ConcurrentSkipListMap 源码，会有很大收获\n * Author：ldb\n */\npublic class SkipList2 {\n\n    private static final int MAX_LEVEL = 16;\n    private int levelCount = 1;\n\n    /**\n     * 带头链表\n     */\n    private Node head = new Node(MAX_LEVEL);\n    private Random r = new Random();\n\n    public Node find(int value) {\n        Node p = head;\n        // 从最大层开始查找，找到前一节点，通过--i，移动到下层再开始查找\n        for (int i = levelCount - 1; i >= 0; --i) {\n            while (p.forwards[i] != null && p.forwards[i].data < value) {\n                // 找到前一节点\n                p = p.forwards[i];\n            }\n        }\n\n        if (p.forwards[0] != null && p.forwards[0].data == value) {\n            return p.forwards[0];\n        } else {\n            return null;\n        }\n    }\n\n    /**\n     * 优化了作者zheng的插入方法\n     *\n     * @param value 值\n     */\n    public void insert(int value) {\n        int level = head.forwards[0] == null ? 1 : randomLevel();\n        // 每次只增加一层，如果条件满足\n        if (level > levelCount) {\n            level = ++levelCount;\n        }\n        Node newNode = new Node(level);\n        newNode.data = value;\n        Node update[] = new Node[level];\n        for (int i = 0; i < level; ++i) {\n            update[i] = head;\n        }\n\n        Node p = head;\n        // 从最大层开始查找，找到前一节点，通过--i，移动到下层再开始查找\n        for (int i = levelCount - 1; i >= 0; --i) {\n            while (p.forwards[i] != null && p.forwards[i].data < value) {\n                // 找到前一节点\n                p = p.forwards[i];\n            }\n            // levelCount 会 > level，所以加上判断\n            if (level > i) {\n                update[i] = p;\n            }\n\n        }\n        for (int i = 0; i < level; ++i) {\n            newNode.forwards[i] = update[i].forwards[i];\n            update[i].forwards[i] = newNode;\n        }\n\n    }\n\n    /**\n     * 优化了作者zheng的插入方法2\n     *\n     * @param value 值\n     */\n    public void insert2(int value) {\n        int level = head.forwards[0] == null ? 1 : randomLevel();\n        // 每次只增加一层，如果条件满足\n        if (level > levelCount) {\n            level = ++levelCount;\n        }\n        Node newNode = new Node(level);\n        newNode.data = value;\n        Node p = head;\n        // 从最大层开始查找，找到前一节点，通过--i，移动到下层再开始查找\n        for (int i = levelCount - 1; i >= 0; --i) {\n            while (p.forwards[i] != null && p.forwards[i].data < value) {\n                // 找到前一节点\n                p = p.forwards[i];\n            }\n            // levelCount 会 > level，所以加上判断\n            if (level > i) {\n                if (p.forwards[i] == null) {\n                    p.forwards[i] = newNode;\n                } else {\n                    Node next = p.forwards[i];\n                    p.forwards[i] = newNode;\n                    newNode.forwards[i] = next;\n                }\n            }\n\n        }\n\n    }\n\n    /**\n     * 作者zheng的插入方法，未优化前，优化后参见上面insert()\n     *\n     * @param value\n     * @param level 0 表示随机层数，不为0，表示指定层数，指定层数\n     *              可以让每次打印结果不变动，这里是为了便于学习理解\n     */\n    public void insert(int value, int level) {\n        // 随机一个层数\n        if (level == 0) {\n            level = randomLevel();\n        }\n        // 创建新节点\n        Node newNode = new Node(level);\n        newNode.data = value;\n        // 表示从最大层到低层，都要有节点数据\n        newNode.maxLevel = level;\n        // 记录要更新的层数，表示新节点要更新到哪几层\n        Node update[] = new Node[level];\n        for (int i = 0; i < level; ++i) {\n            update[i] = head;\n        }\n\n        /**\n         *\n         * 1，说明：层是从下到上的，这里最下层编号是0，最上层编号是15\n         * 2，这里没有从已有数据最大层（编号最大）开始找，（而是随机层的最大层）导致有些问题。\n         *    如果数据量为1亿，随机level=1 ，那么插入时间复杂度为O（n）\n         */\n        Node p = head;\n        for (int i = level - 1; i >= 0; --i) {\n            while (p.forwards[i] != null && p.forwards[i].data < value) {\n                p = p.forwards[i];\n            }\n            // 这里update[i]表示当前层节点的前一节点，因为要找到前一节点，才好插入数据\n            update[i] = p;\n        }\n\n        // 将每一层节点和后面节点关联\n        for (int i = 0; i < level; ++i) {\n            // 记录当前层节点后面节点指针\n            newNode.forwards[i] = update[i].forwards[i];\n            // 前一个节点的指针，指向当前节点\n            update[i].forwards[i] = newNode;\n        }\n\n        // 更新层高\n        if (levelCount < level) levelCount = level;\n    }\n\n    public void delete(int value) {\n        Node[] update = new Node[levelCount];\n        Node p = head;\n        for (int i = levelCount - 1; i >= 0; --i) {\n            while (p.forwards[i] != null && p.forwards[i].data < value) {\n                p = p.forwards[i];\n            }\n            update[i] = p;\n        }\n\n        if (p.forwards[0] != null && p.forwards[0].data == value) {\n            for (int i = levelCount - 1; i >= 0; --i) {\n                if (update[i].forwards[i] != null && update[i].forwards[i].data == value) {\n                    update[i].forwards[i] = update[i].forwards[i].forwards[i];\n                }\n            }\n        }\n    }\n\n    /**\n     * 随机 level 次，如果是奇数层数 +1，防止伪随机\n     *\n     * @return\n     */\n    private int randomLevel() {\n        int level = 1;\n        for (int i = 1; i < MAX_LEVEL; ++i) {\n            if (r.nextInt() % 2 == 1) {\n                level++;\n            }\n        }\n        return level;\n    }\n\n    /**\n     * 打印每个节点数据和最大层数\n     */\n    public void printAll() {\n        Node p = head;\n        while (p.forwards[0] != null) {\n            System.out.print(p.forwards[0] + \" \");\n            p = p.forwards[0];\n        }\n        System.out.println();\n    }\n\n    /**\n     * 打印所有数据\n     */\n    public void printAll_beautiful() {\n        Node p = head;\n        Node[] c = p.forwards;\n        Node[] d = c;\n        int maxLevel = c.length;\n        for (int i = maxLevel - 1; i >= 0; i--) {\n            do {\n                System.out.print((d[i] != null ? d[i].data : null) + \":\" + i + \"-------\");\n            } while (d[i] != null && (d = d[i].forwards)[i] != null);\n            System.out.println();\n            d = c;\n        }\n    }\n\n    /**\n     * 跳表的节点，每个节点记录了当前节点数据和所在层数数据\n     */\n    public class Node {\n        private int data = -1;\n        /**\n         * 表示当前节点位置的下一个节点所有层的数据，从上层切换到下层，就是数组下标-1，\n         * forwards[3]表示当前节点在第三层的下一个节点。\n         */\n        private Node forwards[];\n\n        /**\n         * 这个值其实可以不用，看优化insert()\n         */\n        private int maxLevel = 0;\n\n        public Node(int level) {\n            forwards = new Node[level];\n        }\n\n        @Override\n        public String toString() {\n            StringBuilder builder = new StringBuilder();\n            builder.append(\"{ data: \");\n            builder.append(data);\n            builder.append(\"; levels: \");\n            builder.append(maxLevel);\n            builder.append(\" }\");\n            return builder.toString();\n        }\n    }\n\n    public static void main(String[] args) {\n        SkipList2 list = new SkipList2();\n        list.insert(1, 3);\n        list.insert(2, 3);\n        list.insert(3, 2);\n        list.insert(4, 4);\n        list.insert(5, 10);\n        list.insert(6, 4);\n        list.insert(8, 5);\n        list.insert(7, 4);\n        list.printAll_beautiful();\n        list.printAll();\n        /**\n         * 结果如下：\n         * \t\t\t\t\t\t\t\t\t    null:15-------\n         * \t\t\t\t\t\t\t\t\t    null:14-------\n         * \t\t\t\t\t\t\t\t\t    null:13-------\n         * \t\t\t\t\t\t\t\t\t    null:12-------\n         * \t\t\t\t\t\t\t\t\t    null:11-------\n         * \t\t\t\t\t\t\t\t\t    null:10-------\n         * \t\t\t\t\t\t\t\t\t\t   5:9-------\n         * \t\t\t\t\t\t\t\t\t\t   5:8-------\n         * \t\t\t\t\t\t\t\t\t\t   5:7-------\n         * \t\t\t\t\t\t\t\t\t\t   5:6-------\n         * \t\t\t\t\t\t\t\t\t\t   5:5-------\n         * \t\t\t\t\t\t\t\t\t\t   5:4-------\t\t\t\t\t 8:4-------\n         * \t\t\t\t\t\t\t     4:3-------5:3-------6:3-------7:3-------8:3-------\n         * 1:2-------2:2-------\t\t     4:2-------5:2-------6:2-------7:2-------8:2-------\n         * 1:1-------2:1-------3:1-------4:1-------5:1-------6:1-------7:1-------8:1-------\n         * 1:0-------2:0-------3:0-------4:0-------5:0-------6:0-------7:0-------8:0-------\n         * { data: 1; levels: 3 } { data: 2; levels: 3 } { data: 3; levels: 2 } { data: 4; levels: 4 }\n         * { data: 5; levels: 10 } { data: 6; levels: 4 } { data: 7; levels: 4 } { data: 8; levels: 5 }\n         */\n        // 优化后insert()\n\n        SkipList2 list2 = new SkipList2();\n        list2.insert2(1);\n        list2.insert2(2);\n        list2.insert2(6);\n        list2.insert2(7);\n        list2.insert2(8);\n        list2.insert2(3);\n        list2.insert2(4);\n        list2.insert2(5);\n        System.out.println();\n        list2.printAll_beautiful();\n\n\n    }\n\n}\n"
  },
  {
    "path": "java/18_hashtable/HashTable.java",
    "content": "/**\n * @Description:散列表实现\n * @Author: Hoda\n * @Date: Create in 2019-08-07\n * @Modified By:\n * @Modified Date:\n */\npublic class HashTable<K, V> {\n\n    /**\n     * 散列表默认长度\n     */\n    private static final int DEFAULT_INITAL_CAPACITY = 8;\n\n    /**\n     * 装载因子\n     */\n    private static final float LOAD_FACTOR = 0.75f;\n\n    /**\n     * 初始化散列表数组\n     */\n    private Entry<K, V>[] table;\n\n    /**\n     * 实际元素数量\n     */\n    private int size = 0;\n\n    /**\n     * 散列表索引数量\n     */\n    private int use = 0;\n\n    public HashTable() {\n        table = (Entry<K, V>[]) new Entry[DEFAULT_INITAL_CAPACITY];\n    }\n\n    static class Entry<K, V> {\n        K key;\n\n        V value;\n\n        Entry<K, V> next;\n\n        Entry(K key, V value, Entry<K, V> next) {\n            this.key = key;\n            this.value = value;\n            this.next = next;\n        }\n    }\n\n    /**\n     * 新增\n     *\n     * @param key\n     * @param value\n     */\n    public void put(K key, V value) {\n        int index = hash(key);\n        // 位置未被引用，创建哨兵节点\n        if (table[index] == null) {\n            table[index] = new Entry<>(null, null, null);\n        }\n\n        Entry<K, V> tmp = table[index];\n        // 新增节点\n        if (tmp.next == null) {\n            tmp.next = new Entry<>(key, value, null);\n            size++;\n            use++;\n            // 动态扩容\n            if (use >= table.length * LOAD_FACTOR) {\n                resize();\n            }\n        }\n        // 解决散列冲突，使用链表法\n        else {\n            do {\n                tmp = tmp.next;\n                // key相同，覆盖旧的数据\n                if (tmp.key == key) {\n                    tmp.value = value;\n                    return;\n                }\n            } while (tmp.next != null);\n\n            Entry<K, V> temp = table[index].next;\n            table[index].next = new Entry<>(key, value, temp);\n            size++;\n        }\n    }\n\n    /**\n     * 散列函数\n     * <p>\n     * 参考hashmap散列函数\n     *\n     * @param key\n     * @return\n     */\n    private int hash(Object key) {\n        int h;\n        return (key == null) ? 0 : ((h = key.hashCode()) ^ (h >>> 16)) % table.length;\n    }\n\n    /**\n     * 扩容\n     */\n    private void resize() {\n        Entry<K, V>[] oldTable = table;\n        table = (Entry<K, V>[]) new Entry[table.length * 2];\n        use = 0;\n        for (int i = 0; i < oldTable.length; i++) {\n            if (oldTable[i] == null || oldTable[i].next == null) {\n                continue;\n            }\n            Entry<K, V> e = oldTable[i];\n            while (e.next != null) {\n                e = e.next;\n                int index = hash(e.key);\n                if (table[index] == null) {\n                    use++;\n                    // 创建哨兵节点\n                    table[index] = new Entry<>(null, null, null);\n                }\n                table[index].next = new Entry<>(e.key, e.value, table[index].next);\n            }\n        }\n    }\n\n    /**\n     * 删除\n     *\n     * @param key\n     */\n    public void remove(K key) {\n        int index = hash(key);\n        Entry e = table[index];\n        if (e == null || e.next == null) {\n            return;\n        }\n\n        Entry pre;\n        Entry<K, V> headNode = table[index];\n        do {\n            pre = e;\n            e = e.next;\n            if (key == e.key) {\n                pre.next = e.next;\n                size--;\n                if (headNode.next == null) use--;\n                return;\n            }\n        } while (e.next != null);\n    }\n\n    /**\n     * 获取\n     *\n     * @param key\n     * @return\n     */\n    public V get(K key) {\n        int index = hash(key);\n        Entry<K, V> e = table[index];\n        if (e == null || e.next == null) {\n            return null;\n        }\n        while (e.next != null) {\n            e = e.next;\n            if (key == e.key) {\n                return e.value;\n            }\n        }\n        return null;\n    }\n}\n"
  },
  {
    "path": "java/20_hashtable/LRUBaseHashTable.java",
    "content": "import java.util.HashMap;\n\n/**\n * @Description:基于散列表的LRU算法\n * @Author: Hoda\n * @Date: Create in 2019-08-09\n * @Modified By:\n * @Modified Date:\n */\npublic class LRUBaseHashTable<K, V> {\n\n    /**\n     * 默认链表容量\n     */\n    private final static Integer DEFAULT_CAPACITY = 10;\n\n    /**\n     * 头结点\n     */\n    private DNode<K, V> headNode;\n\n    /**\n     * 尾节点\n     */\n    private DNode<K, V> tailNode;\n\n    /**\n     * 链表长度\n     */\n    private Integer length;\n\n    /**\n     * 链表容量\n     */\n    private Integer capacity;\n\n    /**\n     * 散列表存储key\n     */\n    private HashMap<K, DNode<K, V>> table;\n\n    /**\n     * 双向链表\n     */\n    static class DNode<K, V> {\n\n        private K key;\n\n        /**\n         * 数据\n         */\n        private V value;\n\n        /**\n         * 前驱指针\n         */\n        private DNode<K, V> prev;\n\n        /**\n         * 后继指针\n         */\n        private DNode<K, V> next;\n\n        DNode() {\n        }\n\n        DNode(K key, V value) {\n            this.key = key;\n            this.value = value;\n        }\n\n    }\n\n    public LRUBaseHashTable(int capacity) {\n        this.length = 0;\n        this.capacity = capacity;\n\n        headNode = new DNode<>();\n\n        tailNode = new DNode<>();\n\n        headNode.next = tailNode;\n        tailNode.prev = headNode;\n\n        table = new HashMap<>();\n    }\n\n    public LRUBaseHashTable() {\n        this(DEFAULT_CAPACITY);\n    }\n\n    /**\n     * 新增\n     *\n     * @param key\n     * @param value\n     */\n    public void add(K key, V value) {\n        DNode<K, V> node = table.get(key);\n        if (node == null) {\n            DNode<K, V> newNode = new DNode<>(key, value);\n            table.put(key, newNode);\n            addNode(newNode);\n\n            if (++length > capacity) {\n                DNode<K, V> tail = popTail();\n                table.remove(tail.key);\n                length--;\n            }\n        } else {\n            node.value = value;\n            moveToHead(node);\n        }\n    }\n\n    /**\n     * 将新节点加到头部\n     *\n     * @param newNode\n     */\n    private void addNode(DNode<K, V> newNode) {\n        newNode.next = headNode.next;\n        newNode.prev = headNode;\n\n        headNode.next.prev = newNode;\n        headNode.next = newNode;\n    }\n\n    /**\n     * 弹出尾部数据节点\n     */\n    private DNode<K, V> popTail() {\n        DNode<K, V> node = tailNode.prev;\n        removeNode(node);\n        return node;\n    }\n\n    /**\n     * 移除节点\n     *\n     * @param node\n     */\n    private void removeNode(DNode<K, V> node) {\n        node.prev.next = node.next;\n        node.next.prev = node.prev;\n    }\n\n    /**\n     * 将节点移动到头部\n     *\n     * @param node\n     */\n    private void moveToHead(DNode<K, V> node) {\n        removeNode(node);\n        addNode(node);\n    }\n\n    /**\n     * 获取节点数据\n     *\n     * @param key\n     * @return\n     */\n    public V get(K key) {\n        DNode<K, V> node = table.get(key);\n        if (node == null) {\n            return null;\n        }\n        moveToHead(node);\n        return node.value;\n    }\n\n    /**\n     * 移除节点数据\n     *\n     * @param key\n     */\n    public void remove(K key) {\n        DNode<K, V> node = table.get(key);\n        if (node == null) {\n            return;\n        }\n        removeNode(node);\n        length--;\n        table.remove(node.key);\n    }\n\n    private void printAll() {\n        DNode<K, V> node = headNode.next;\n        while (node.next != null) {\n            System.out.print(node.value + \",\");\n            node = node.next;\n        }\n        System.out.println();\n    }\n}\n"
  },
  {
    "path": "java/24_tree/BinarySearchTree.java",
    "content": "public class BinarySearchTree {\n  private Node tree;\n\n  public Node find(int data) {\n    Node p = tree;\n    while (p != null) {\n      if (data < p.data) p = p.left;\n      else if (data > p.data) p = p.right;\n      else return p;\n    }\n    return null;\n  }\n  \n  public void insert(int data) {\n    if (tree == null) {\n      tree = new Node(data);\n      return;\n    }\n\n    Node p = tree;\n    while (p != null) {\n      if (data > p.data) {\n        if (p.right == null) {\n          p.right = new Node(data);\n          return;\n        }\n        p = p.right;\n      } else { // data < p.data\n        if (p.left == null) {\n          p.left = new Node(data);\n          return;\n        }\n        p = p.left;\n      }\n    }\n  }\n\n  public void delete(int data) {\n    Node p = tree; // p指向要删除的节点，初始化指向根节点\n    Node pp = null; // pp记录的是p的父节点\n    while (p != null && p.data != data) {\n      pp = p;\n      if (data > p.data) p = p.right;\n      else p = p.left;\n    }\n    if (p == null) return; // 没有找到\n\n    // 要删除的节点有两个子节点\n    if (p.left != null && p.right != null) { // 查找右子树中最小节点\n      Node minP = p.right;\n      Node minPP = p; // minPP表示minP的父节点\n      while (minP.left != null) {\n        minPP = minP;\n        minP = minP.left;\n      }\n      p.data = minP.data; // 将minP的数据替换到p中\n      p = minP; // 下面就变成了删除minP了\n      pp = minPP;\n    }\n\n    // 删除节点是叶子节点或者仅有一个子节点\n    Node child; // p的子节点\n    if (p.left != null) child = p.left;\n    else if (p.right != null) child = p.right;\n    else child = null;\n\n    if (pp == null) tree = child; // 删除的是根节点\n    else if (pp.left == p) pp.left = child;\n    else pp.right = child;\n  }\n\n  public Node findMin() {\n    if (tree == null) return null;\n    Node p = tree;\n    while (p.left != null) {\n      p = p.left;\n    }\n    return p;\n  }\n\n  public Node findMax() {\n    if (tree == null) return null;\n    Node p = tree;\n    while (p.right != null) {\n      p = p.right;\n    }\n    return p;\n  }\n  \n  public static class Node {\n    private int data;\n    private Node left;\n    private Node right;\n\n    public Node(int data) {\n      this.data = data;\n    }\n  }\n}\n"
  },
  {
    "path": "java/28_sorts/HeapSort.java",
    "content": "/**\n * 堆排序\n */\npublic class HeapSort {\n\n    /**\n     * 排序\n     * <p>\n     * 堆元素是从数组下标0开始\n     *\n     * @param arr\n     */\n    public static void sort(int[] arr) {\n        if (arr.length <= 1) {\n            return;\n        }\n\n        // 1、建堆\n        buildHeap(arr);\n\n        // 2、排序\n        int k = arr.length - 1;\n        while (k > 0) {\n            // 将堆顶元素（最大）与最后一个元素交换位置\n            swap(arr, 0, k);\n            // 将剩下元素重新堆化，堆顶元素变成最大元素\n            heapify(arr, --k, 0);\n        }\n    }\n\n    /**\n     * 建堆\n     *\n     * @param arr\n     */\n    private static void buildHeap(int[] arr) {\n        // (arr.length - 1) / 2 为最后一个叶子节点的父节点\n        // 也就是最后一个非叶子节点，依次堆化直到根节点\n        for (int i = (arr.length - 1) / 2; i >= 0; i--) {\n            heapify(arr, arr.length - 1, i);\n        }\n    }\n\n    /**\n     * 堆化\n     *\n     * @param arr 要堆化的数组\n     * @param n   最后堆元素下标\n     * @param i   要堆化的元素下标\n     */\n    private static void heapify(int[] arr, int n, int i) {\n        while (true) {\n            // 最大值位置\n            int maxPos = i;\n            // 与左子节点（i * 2 + 1）比较，获取最大值位置\n            if (i * 2 + 1 <= n && arr[i] < arr[i * 2 + 1]) {\n                maxPos = i * 2 + 1;\n            }\n            // 最大值与右子节点（i * 2 + 2）比较，获取最大值位置\n            if (i * 2 + 2 <= n && arr[maxPos] < arr[i * 2 + 2]) {\n                maxPos = i * 2 + 2;\n            }\n            // 最大值是当前位置结束循环\n            if (maxPos == i) {\n                break;\n            }\n            // 与子节点交换位置\n            swap(arr, i, maxPos);\n            // 以交换后子节点位置接着往下查找\n            i = maxPos;\n        }\n    }\n\n}"
  },
  {
    "path": "java/30_graph/Graph.java",
    "content": "package com.study.graph;\n\nimport java.util.LinkedList;\nimport java.util.Queue;\n\n/**\n * @author ldb\n * @date 2019-10-23 15:10\n */\npublic class Graph {\n    private int v;\n    private LinkedList<Integer> adj[]; // 邻接表\n\n    public Graph(int v) {\n        this.v = v;\n        adj = new LinkedList[v];\n        for (int i = 0; i < v; ++i) {\n            adj[i] = new LinkedList<>();\n        }\n    }\n\n    /**\n     * 添加边\n     *\n     * @param s 顶点\n     * @param t 顶点\n     */\n    public void addEdge(int s, int t) { // 无向图一条边存两次\n        adj[s].add(t);\n        adj[t].add(s);\n\n    }\n\n    public void bfs(int s, int t) {\n        if (s == t) return;\n        // visited是用来记录已经被访问的顶点，用来避免顶点被重复访问。\n        boolean[] visited = new boolean[v];\n        visited[s] = true;\n        // queue是一个队列，用来存储已经被访问、但相连的顶点还没有被访问的顶点。\n        Queue<Integer> queue = new LinkedList<>();\n        queue.add(s);\n        // prev用来记录搜索路径。\n        int[] prev = new int[v];\n        for (int i = 0; i < v; ++i) {\n            prev[i] = -1;\n        }\n        while (queue.size() != 0) {\n            int w = queue.poll();\n            for (int i = 0; i < adj[w].size(); ++i) {\n                int q = adj[w].get(i);\n                if (!visited[q]) {\n                    prev[q] = w;\n                    if (q == t) {\n                        print(prev, s, t);\n                        return;\n                    }\n                    visited[q] = true;\n                    queue.add(q);\n                }\n            }\n        }\n    }\n\n    private void print(int[] prev, int s, int t) { // 递归打印 s->t 的路径\n        if (prev[t] != -1 && t != s) {\n            print(prev, s, prev[t]);\n        }\n        System.out.print(t + \" \");\n    }\n\n    public static void main(String[] args) {\n        Graph graph = new Graph(8);\n        graph.addEdge(0,1);\n        graph.addEdge(0,3);\n        graph.addEdge(1,2);\n        graph.addEdge(1,4);\n        graph.addEdge(2,5);\n        graph.addEdge(4,5);\n        graph.addEdge(4,6);\n        graph.addEdge(5,7);\n        graph.addEdge(6,7);\n//        graph.bfs(0,6);\n\n        // 深度优先\n        graph.dfs(0, 6);\n\n    }\n\n    boolean found = false; // 全局变量或者类成员变量\n\n    public void dfs(int s, int t) {\n        found = false;\n        boolean[] visited = new boolean[v];\n        int[] prev = new int[v];\n        for (int i = 0; i < v; ++i) {\n            prev[i] = -1;\n        }\n        recurDfs(s, t, visited, prev);\n        print(prev, s, t);\n    }\n\n    private void recurDfs(int w, int t, boolean[] visited, int[] prev) {\n        if (found == true) return;\n        visited[w] = true;\n        if (w == t) {\n            found = true;\n            return;\n        }\n        for (int i = 0; i < adj[w].size(); ++i) {\n            int q = adj[w].get(i);\n            if (!visited[q]) {\n                prev[q] = w;\n                recurDfs(q, t, visited, prev);\n            }\n        }\n    }\n}\n\n\n"
  },
  {
    "path": "java/32_BFRK",
    "content": "public static int bF(String a,String b) {\n\t\tint m=a.length(),n=b.length(),k;\n\t\tchar[] a1=a.toCharArray();\n\t\tchar[] b1=b.toCharArray();\n\t\tfor(int i=0;i<=m-n;i++) {\n\t\t\tk=0;\n\t\t\tfor(int j=0;j<n;j++) {\n\t\t\t\tif(a1[i+j]==b1[j]) {\n\t\t\t\t\tk++;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif(k==n) {\n\t\t\t\treturn i;\n\t\t\t}\n\t\t}\n\t\treturn -1;\n\t}\npublic static int rK(String a,String b) {\n\t\tint m=a.length(),n=b.length(),s,j;\n\t\tint[] hash=new int[m-n+1];\n\t\tint[] table=new int[26];\n\t\tchar[] a1=a.toCharArray();\n\t\tchar[] b1=b.toCharArray();\n\t\ts=1;\n\t\t//将26的次方存储在一个表里，取的时候直接用,虽然溢出，但没啥问题\n\t\tfor(j=0;j<26;j++) {\n\t\t\ttable[j]=s;\n\t\t\ts*=26;\n\t\t}\n\t\tfor(int i=0;i<=m-n;i++) {\n\t\t\ts=0;\n\t\t\tfor(j=0;j<n;j++) {\n\t\t\t\ts+=(a1[i+j]-'a')*table[n-1-j];\n\t\t\t}\n\t\t\thash[i]=s;\n\t\t}\n\t\ts=0;\n\t\tfor(j=0;j<n;j++) {\n\t\t\ts+=(b1[j]-'a')*table[n-1-j];\n\t\t}\n\t\tfor(j=0;j<m-n+1;j++) {\n\t\t\tif(hash[j]==s) {\n\t\t\t\treturn j;\n\t\t\t}\n\t\t}\n\t\treturn -1;\n\t}\n"
  },
  {
    "path": "java/36_ac_automata/ACAutoMata.java",
    "content": "\nimport java.util.HashMap;\nimport java.util.LinkedList;\nimport java.util.Map;\nimport java.util.Objects;\n\n/**\n * @author wayne\n */\npublic class ACAutoMata {\n    private ACNode root;\n\n    public ACAutoMata() {\n        this.root = new ACNode(\"/\");\n    }\n\n    private void insert (String pattern) {\n        ACNode node = this.root;\n        int len = pattern.length();\n        for (int i = 0; i < len; i++) {\n            String c = pattern.charAt(i) + \"\";\n            if(Objects.isNull(node.children.get(c))) {\n                node.children.put(c, new ACNode(c));\n            }\n            node = node.children.get(c);\n        }\n\n        node.isEndingChar = true;\n        node.length = pattern.length();\n    }\n\n    private void buildFailurePointer() {\n        ACNode root = this.root;\n        LinkedList<ACNode> queue = new LinkedList<>();\n        queue.add(root);\n\n        while (!queue.isEmpty()) {\n            ACNode p = queue.pop();\n\n            for(ACNode pc: p.children.values()){\n                if (Objects.isNull(pc)) {\n                    continue;\n                }\n\n                if(p == root) {\n                    pc.fail = root;\n                } else {\n                    ACNode q = p.fail;\n                    while (Objects.nonNull(q)) {\n                        ACNode qc = q.children.get(pc.data);\n                        if(Objects.nonNull(qc)) {\n                            pc.fail = qc;\n                            break;\n                        }\n                        q = q.fail;\n                    }\n                    if(Objects.isNull(q)) {\n                        pc.fail = root;\n                    }\n                }\n                queue.add(pc);\n            }\n        }\n    }\n\n    private Boolean match (String text) {\n        ACNode root = this.root;\n        ACNode p = root;\n\n        int n = text.length();\n        for(int i = 0; i < n; i++) {\n            String c = text.charAt(i) + \"\";\n            while(Objects.isNull(p.children.get(c)) && p != root){\n                p = p.fail;\n            }\n\n            p = p.children.get(c);\n            if(Objects.isNull(p)) {\n                p = root;\n            }\n\n            ACNode tmp = p;\n            while ( tmp != root) {\n                if (tmp.isEndingChar == true) {\n                    System.out.println(\"Start from \" + (i - p.length + 1));\n                    return true;\n                }\n                tmp = tmp.fail;\n            }\n        }\n\n        return false;\n    }\n\n    public static boolean match(String text, String[] patterns) {\n        ACAutoMata automata = new ACAutoMata();\n        for (String pattern: patterns) {\n            automata.insert(pattern);\n        }\n\n        automata.buildFailurePointer();\n        return automata.match(text);\n    }\n\n    public class ACNode {\n        private String data;\n        private Map<String, ACNode> children;\n        private Boolean isEndingChar;\n        private Integer length;\n        private ACNode fail;\n\n        public ACNode(String data) {\n            this.data = data;\n            this.children = new HashMap<>();\n            this.isEndingChar = false;\n            this.length = 0;\n            this.fail = null;\n        }\n    }\n\n    public static void main(String[] args) {\n        String[] patterns = {\"at\", \"art\", \"oars\", \"soar\"};\n        String text = \"soarsoars\";\n        System.out.println(match(text, patterns));\n\n        String[] patterns2 = {\"Fxtec Pro1\", \"谷歌Pixel\"};\n\n        String text2 = \"一家总部位于伦敦的公司Fxtex在MWC上就推出了一款名为Fxtec Pro1的手机，该机最大的亮点就是采用了侧滑式全键盘设计。DxOMark年度总榜发布 华为P20 Pro/谷歌Pixel 3争冠\";\n        System.out.println(match(text2, patterns2));\n    }\n}\n"
  },
  {
    "path": "javascript/.gitkeep",
    "content": ""
  },
  {
    "path": "javascript/05_array/.gitkeep",
    "content": ""
  },
  {
    "path": "javascript/05_array/Array.md",
    "content": "与 Java 、PHP 等语言不同，在 JavaScript 中，数组其实是一种特殊的对象。\r\n#### 数组的创建与读写\r\n以下两种方式都可创建数组：\r\n```\r\n// 字面量方式,常用\r\nvar num = [1,5,6,10];\r\nprint(num.length);   // 4\r\n\r\n// 构造函数方式\r\nvar num = new Array(1,5,6,10);\r\nprint(num.length);    // 4\r\n```\r\n值得注意的是，JavaScript 中的数组数据可以是不同类型，它的语法相对宽松，例如可以指定不同类型数据`var example = [1,\"Mike\",true,null];`另外，可以通过`Array.isArray()`来判断一个对象是否是数组，例如：\r\n```\r\nvar num = [1,5,6,10];\r\nprint(Array.isArray(num));        // true\r\n```\r\n如何读写数组呢？可以使用循环。\r\n```\r\nvar num = [1,5,6,10];\r\nfor (var i = 0; i < num.length; i++) {\r\n  console.log(num[i]+\" \");\r\n}\r\n```\r\n#### 数组的深复制与浅复制\r\n当我们把数组赋给另外一个数组，然后改变其中一个数组的值，另一数组也会随之改变，这就是数组的浅复制。而深复制指的就是不改变原来的数组而去创建一个新的数组，这种情况是经常使用的，为了不破坏原数组。下面的代码展示了这两种复制\r\n```\r\n// 浅复制\r\nvar num = [1,2,3,4,5];\r\nvar newNum = num;\r\nnum[0] = 10;\r\nconsole.log(newNum[0]);  // 10\r\n\r\n// 深复制\r\nfunction copy (arr1,arr2) {\r\n  for(var i=0;i<arr1.length;i++){\r\n    arr2[i] = arr1[i];\r\n  }\r\n}\r\nvar num = [1,5,6,7,9];\r\nvar newNum = [];\r\ncopy(num,newNum);\r\nnum[0] = 10;\r\nconsole.log(newNum[0]);    // 仍然为 1\r\n```\r\n#### 存取函数\r\nJavaScript 提供了一组用来访问数组元素的函数，叫存取函数。最常用的存取函数就是 indexOf() 函数，该函数返回指定查找的值在目标值中是否存在，如果存在，返回该值在数组中的索引，不存在则返回 -1。\r\n```\r\nvar word = [\"A\",\"B\",\"C\",\"D\"];\r\nvar result = word.indexOf(\"A\");\r\nconsole.log(result);    // 0\r\nvar test = word.indexOf(\"F\");\r\nconsole.log(test);   // -1\r\n```\r\n除此之外，还有 join 和 toString 函数，concat 和 splice 函数。前两个函数可以将数组转化为字符串，后面两个函数可以通过已有的数组创建新数组，其中 concat 方法通过合并多个数组来形成新数组，而 splice 方法是截取一个数组的子集作为一个新数组。它们的使用示例如下\r\n```\r\nvar arr = [\"Mike\",\"John\",\"Hexo\"];\r\nconsole.log(arr.join());   // Mike,John,Hexo\r\nconsole.log(arr.toString());   // Mike,John,Hexo\r\n\r\nvar arr1 = [1,10,\"Mike\"];\r\nvar arr2 = [8,7,6];\r\nvar cat = arr1.concat(arr2);\r\nconsole.log(cat);    // [1, 10, \"Mike\", 8, 7, 6]\r\nvar num = [1,2,3,4,5,6,7];\r\nvar a = num.splice(3,2);     // 3 表示索引，2 表示删除 2 个\r\nconsole.log(num);      // [1, 2, 3, 6, 7]    \r\n```\r\n#### 可变函数\r\n不去引用数组中的某个元素，就能改变数组内容，这种函数称它为可变函数。\r\n###### push() 和 unshift()、pop() 和 shift()\r\npush() 方法可以在数组末尾添加元素，而 unshift() 方法可以在数组开头添加元素；相对应的，pop 可以删除数组末尾的元素，而 shift 删除数组的第一个元素。\r\n```\r\nvar nums = [9,58,15,16,23];\r\nnums.push(111);\r\nconsole.log(nums);   // 9, 58, 15, 16, 23, 111\r\nnums.unshift(1);\r\nconsole.log(nums);   // 1, 9, 58, 15, 16, 23, 111\r\nnums.pop();\r\nconsole.log(nums);   // 1, 9, 58, 15, 16, 23\r\nnums.shift();\r\nconsole.log(nums);   // 9, 58, 15, 16, 23\r\n```\r\n###### splice()、sort()、reverse()\r\n前面提到的 splice 不仅可以用来删除元素，还可以添加元素进数组。用 sort 可以为数组排序，reverse 将数组内的元素翻转。\r\n```\r\nvar num = [98,99,100,101];\r\nnum.splice(1,0,89);     // 1 表示索引，0 表示不删除元素，89 表示将 89 这个元素添加进数组\r\nconsole.log(num);    // 98, 89, 99, 100, 101\r\nnum.reverse();\r\nconsole.log(num);    //  101, 100, 99, 89, 98\r\n```\r\n关于 sort 方法非常有意思，它只能对那些字符串类型的元素排列得比较准确，但如果是数字，结果就不那么令人满意了。看看例子.\r\n```\r\nvar str = [\"hello\",\"client\",\"zero\"];\r\nstr.sort();\r\nconsole.log(str);     // [\"client\", \"hello\", \"zero\"]  按照字母 a-z 排列准确\r\nvar nums = [1,200,51,66,88];\r\nnums.sort();\r\nconsole.log(nums);   // [1, 200, 51, 66, 88]  有趣的事情来了，因为 200 的 2 比 51 的 5 先，所以 200 排在 51 前头\r\n```\r\n那如何解决这种排序的错误呢？方法就是在调用 sort() 的时候传入一个函数，该函数可以比较出大小。\r\n```\r\nfunction compare(num1,num2){\r\n  return num1 - num2;\r\n}\r\nvar nums = [3,1,2,100,4,200];\r\nnums.sort(compare);\r\nconsole.log(nums);    // 1, 2, 3, 4, 100, 200\r\n```\r\n可以看到，已经排序正确了，compare 函数就是利用了两数相减，如果结果为正，那么被减数大于减数，如果结果为 0，则两数相等，而如果结果为负，说明被减数小于减数。\r\n#### 迭代器方法\r\n迭代函数通过对数组中的元素逐个应用，来操作返回相应的值。\r\n###### 不返回新数组的 forEach() 、every()、some()、reduce()\r\n```\r\n// 用 forEach 开方\r\nfunction square(num) {\r\n    console.log(num,num*num);\r\n}\r\nvar num = [1,2,3,4,5];\r\nnum.forEach(square);\r\nconsole.log(num);\r\n/*  1 1\r\n    2 4\r\n    3 9\r\n    4 16\r\n    5 25\r\n*/\r\n```\r\n```\r\n/*\r\nevery() 返回值为布尔类型，对于应用的所有元素，该函数返回 true，则该方法返回 true\r\n*/\r\nfunction isEven(num){\r\n  return num % 2 == 0;\r\n}\r\nvar num = [2,4,6,8,10];\r\nvar even = num.every(isEven);\r\nif(even){\r\n  console.log(\"所有的数字都是偶数\");\r\n}else{\r\n  console.log(\"不是所有的数字都是偶数\");\r\n}\r\n```\r\n```\r\n/*\r\nsome() 与 every() 的不同就是只要有一个元素使改函数返回 true ，那么该方法就返回 true\r\n*/\r\nfunction isEven(num){\r\n  return num % 2 == 0;\r\n}\r\nvar num = [1,2,3,4,5,6,7,8];\r\nvar someEven = num.some(isEven);\r\nif(someEven){\r\n  console.log(\"有些数字是偶数\");\r\n}else{\r\n  console.log(\"没有数字是偶数\");\r\n}\r\n```\r\n```\r\n/*\r\nreduce() 有两个功能，一是可以对数组元素进行求和，二是将数组元素连接成字符串。\r\n*/\r\nfucntion add(num1,num2){\r\n  return num1 + num2;\r\n}\r\nvar num = [1,2,3,4];\r\nvar sum = num.reduce(add);\r\nconsole.log(sum);    // 10\r\n\r\nfunction concat(str,i) {\r\n  return str + i;\r\n}\r\nvar words = [\"I am \",\"a \",\"coder \"];\r\nvar re = words.reduce(concat);\r\nconsole.log(re);     // I am a coder\r\n```\r\n###### 返回新数组的 map() 和 filter()\r\nmap 的作用与 forEach 是一样的，区别就是 map 函数返回的是一个新数组。\r\n```\r\nfunction addFive(grade){\r\n  return grade += 5;\r\n}\r\nvar grade = [77,82,88,95,90];\r\nvar result = grade.map(addFive);\r\nconsole.log(result);     // 82, 87, 93, 100, 95\r\n```\r\n而 filter 和 every 相似，区别在于当所有的元素使改函数为 true 时，它并不返回布尔类型，而是返回一个新数组。下面这个例子十分有趣，它随机产生10个 0 到 100 的数字作为分数，然后把大于 60 的及格分数筛选出来。\r\n```\r\nfunction passing(num){\r\n  return num >= 60;\r\n}\r\nvar grades = [];\r\nfor(var i = 0;i < 11;i++){\r\n  grade[i] = Math.floor(Math.random() * 101);\r\n}\r\nvar pass = grades.filter(passing);\r\nconsole.log(\"随机产生的 10 个同学的分数为：\");\r\nconsole.log(grades)；\r\nconsole.log(\"及格的分数有：\");\r\nconsole.log(pass)；\r\n```\r\n上述代码的输出结果为\r\n> 随机产生的 10 个同学的分数为：\r\n21, 4, 89, 45, 5, 51, 71, 7, 46, 53, 47\r\n及格的分数有：\r\n89, 71\r\n#### 二维数组\r\nJavaScript 可以通过在数组里在嵌套一个数组来形成二维数组。\r\n```\r\nvar grades = [\r\n  [88,86,82],\r\n  [91,82,83],\r\n  [77,72,79],\r\n  [86,80,82]\r\n];\r\nconsole.log(grades[1][2]);    // 83\r\n```\r\n#### 处理二维数组\r\n对于二维数组的处理可以分为两种，一种按列访问，一种是按行访问。\r\n按列访问，外层循环对应行，内层循环对应列。例如，上述的数组，每一行对应一个学生三门科目的成绩记录，可以通过相加所有成绩，然后除以科目数来得到该生的平均成绩。\r\n```\r\nvar grades = [\r\n  [88,86,82],\r\n  [91,82,83],\r\n  [77,72,79],\r\n  [86,80,82]\r\n];\r\nvar total = 0;\r\nvar average = 0.0;\r\nfor(var row = 0;row<grades.length;++row){\r\n  for(var col = 0;col<grades[row].length;++col){\r\n    total += grades[row][col];\r\n  }\r\n  average = total/grades[row].length;\r\n  console.log(\"student \"+parseInt(row+1)+\" average: \"+average.toFixed(2));   // toFixed 表示按照 2 位来保留小数\r\n  total = 0;\r\n  average = 0.0;\r\n}\r\n```\r\n上述代码的输出结果为\r\n> student 1 average: 85.33\r\nstudent 2 average: 85.33\r\nstudent 3 average: 76.00\r\nstudent 4 average: 82.67\r\n\r\n\r\n对于按行访问，则外层循环对应列，内层循环对应行，例如还是上述数组，现在的数组表示一个学生三场考试四门科目的各科分数，我们来求每场考试的平均成绩\r\n```\r\nvar grades = [\r\n  [88,86,82],\r\n  [91,82,83],\r\n  [77,72,79],\r\n  [86,80,82]\r\n];\r\nvar total = 0;\r\nvar average = 0.0;\r\n//这里假设每场考试的科目都一样，所以可以通过grades[0].length来获取考试数量\r\nfor(var col = 0;col <grades[0].length;++col ){\r\n  for(var row= 0;row<grades.length;++row){\r\n    total += grades[row][col];\r\n  }\r\n  average = total/grades.length;\r\n  console.log(\"exam \"+parseInt(col +1)+\" average: \"+average.toFixed(2));\r\n  total = 0;\r\n  average = 0.0;\r\n}\r\n```\r\n输出结果为：\r\n> exam 1 average: 85.50\r\nexam 2 average: 80.00\r\nexam 3 average: 81.50\r\n\r\n其实只要调整 for 循环的顺序就可以控制是按行还是按列来输出，此外，JavaScript 还可以处理一些参差不齐的数组，比如一个二维数组中的数组，有的是两个元素，有的是四个元素，并不是都相同，在这种情况下，JavaScript 依然可以处理运行而不报错，这是因为不管多或少，都可以通过 length 属性来计算。\r\n#### 对象数组\r\n如果你有阅读到这里，你应该可以发现上面的所有例子里数据类型都是基本数据类型，不是数字就是字符串。对象数组，顾名思义，就是数组里面的元素可以是对象，这个与 java 的语法很相似，基本上所有的编程语言都是相通的。看看下面这个例子：\r\n```\r\nfunction point(x,y){\r\n  this.x = x;\r\n  this.y = y;\r\n}\r\nfunction show(arr){\r\n  for(var i=0;i<arr.length;i++){\r\n    console.log(arr[i].x + \", \"+arr[i].y);\r\n  }\r\n}\r\nvar p1 = new Point(1,2);\r\nvar p2 = new Point(2,4);\r\nvar p3 = new Point(8,1);\r\nvar p4 = new Point(2,9);\r\nvar point = [p1,p2,p3,p4];\r\nfor(var i=0;i<point.lengh;i++){\r\n  console.log(\"Point \"+parseInt(i+1)+\": \"+point[i].x+\", \"+point[i].y);\r\n}\r\n```\r\n上述程序的输出结果为：\r\n> Point 1: 1, 2\r\nPoint 2: 2, 4\r\nPoint 3: 8, 1\r\nPoint 4: 2, 9\r\n\r\n也可以用之前的 push() 等操作方法来操作对象数组\r\n```\r\nvar p5 = new Point(11,13);\r\npoint.push(p5);\r\nconsole.log(\"添加了 p5 后：\");\r\nshow(point);     \r\npoint.shift();\r\nconsole.log(\"删除第一个元素后：\")\r\nshow(point);\r\n```\r\n输出结果为：\r\n> 添加了 p5 后：\r\n1,2\r\n2,4\r\n8,1\r\n2,9\r\n11,13\r\n删除第一个元素后：\r\n2,4\r\n8,1\r\n2,9\r\n11,13\r\n"
  },
  {
    "path": "javascript/06_linkedlist/.gitkeep",
    "content": ""
  },
  {
    "path": "javascript/06_linkedlist/SinglyLinkedList.js",
    "content": "/**\n * 1）单链表的插入、删除、查找操作；\n * 2）链表中存储的是int类型的数据；\n */\nclass Node {\n  constructor (element) {\n    this.element = element\n    this.next = null\n  }\n}\n\nclass LinkedList {\n  constructor () {\n    this.head = new Node('head')\n  }\n  // 根据value查找节点\n  findByValue (item) {\n    let currentNode = this.head.next\n    while (currentNode !== null && currentNode.element !== item) {\n      currentNode = currentNode.next\n    }\n    console.log(currentNode)\n    return currentNode === null ? -1 : currentNode\n  } \n  \n  // 根据index查找节点，下标从0开始\n  findByIndex (index) {\n    let currentNode = this.head.next\n    let pos = 0\n    while (currentNode !== null && pos !== index) {\n      currentNode = currentNode.next\n      pos++\n    }\n    console.log(currentNode)\n    return currentNode === null ? -1 : currentNode\n  }\n\n  // 向链表末尾追加节点\n  append(newElement) {\n    const newNode = new Node(newElement)\n    let currentNode = this.head\n    while(currentNode.next) {\n      currentNode = currentNode.next\n    }\n    currentNode.next = newNode\n  }\n  \n  // 指定元素向后插入\n  insert (newElement, element) {\n    const currentNode = this.findByValue(element)\n    if (currentNode === -1) {\n      console.log('未找到插入位置')\n      return\n    }\n    const newNode = new Node(newElement)\n    newNode.next = currentNode.next\n    currentNode.next = newNode\n  } \n  \n  // 查找前一个\n  findPrev (item) {\n    let currentNode = this.head\n    while (currentNode.next !== null && currentNode.next.element !== item) {\n      currentNode = currentNode.next\n    }\n    if (currentNode.next === null) {\n      return -1\n    }\n    return currentNode\n  } \n  \n  // 根据值删除\n  remove (item) {\n    const prevNode = this.findPrev(item)\n    if (prevNode === -1) {\n      console.log('未找到元素')\n      return\n    }\n    prevNode.next = prevNode.next.next\n  }\n  \n  // 遍历显示所有节点\n  display () {\n    let currentNode = this.head.next // 忽略头指针的值\n    while (currentNode !== null) {\n      console.log(currentNode.element)\n      currentNode = currentNode.next\n    }\n  }\n}\n// Test\nconst LList = new LinkedList()\nLList.append('chen')\nLList.append('curry')\nLList.append('sang')\nLList.append('zhao') // chen -> curry -> sang -> zhao\nconsole.log('-------------insert item------------')\nLList.insert('qian', 'chen') // 首元素后插入\nLList.insert('zhou', 'zhao') // 尾元素后插入\nLList.display() // chen -> qian -> curry -> sang -> zhao -> zhou\nconsole.log('-------------remove item------------')\nLList.remove('curry')\nLList.display() // chen -> qian -> sang -> zhao -> zhou\nconsole.log('-------------find by item------------')\nLList.findByValue('chen')\nconsole.log('-------------find by index------------')\nLList.findByIndex(2)\nconsole.log('-------------与头结点同值元素测试------------')\nLList.insert('head', 'sang')\nLList.display() // chen -> qian -> sang -> head -> zhao -> zhou\nLList.findPrev('head') // sang\nLList.remove('head')\nLList.display() // chen -> qian -> sang -> zhao -> zhou\n"
  },
  {
    "path": "javascript/07_linkedlist/.gitkeep",
    "content": ""
  },
  {
    "path": "javascript/07_linkedlist/LinkedListAlgo.js",
    "content": "/**\n * 1) 单链表反转\n * 2) 链表中环的检测\n * 3) 两个有序的链表合并\n * 4) 删除链表倒数第n个结点\n * 5) 求链表的中间结点\n *\n */\nclass Node {\n    constructor(element) {\n        this.element = element\n        this.next = null\n    }\n}\n\nclass LinkedList {\n    constructor() {\n        this.head = new Node('head')\n    }\n    // 根据value查找节点 \n    findByValue(item) {\n        let currentNode = this.head\n        while (currentNode !== null && currentNode.element !== item) {\n            currentNode = currentNode.next\n        }\n        return currentNode === null ? -1 : currentNode\n    }\n    // 根据index查找节点 \n    findByIndex(index) {\n        let currentNode = this.head\n        let pos = 0\n        while (currentNode !== null && pos !== index) {\n            currentNode = currentNode.next\n            pos++\n        }\n        return currentNode === null ? -1 : currentNode\n    }\n    // 指定元素向后插入\n    insert(newElement, element) {\n        const currentNode = this.findByValue(element)\n        if (currentNode === -1) {\n            console.log('未找到插入位置')\n            return\n        }\n        const newNode = new Node(newElement)\n        newNode.next = currentNode.next\n        currentNode.next = newNode\n    }\n    // 查找前一个\n    findPrev(item) {\n        let currentNode = this.head\n        while (currentNode.next !== null && currentNode.next.element !== item) {\n            currentNode = currentNode.next\n        }\n        if (currentNode.next === null) {\n            return -1\n        }\n        return currentNode\n    }\n    // 根据值删除\n    remove(item) {\n        const desNode = this.findByValue(item)\n        if (desNode === -1) {\n            console.log('未找到元素')\n            return\n        }\n        const prevNode = this.findPrev(item)\n        prevNode.next = desNode.next\n    }\n    // 遍历显示所有节点\n    display() {\n        //先检查是否为环\n        if(this.checkCircle()) return false\n\n        let currentNode = this.head\n        while (currentNode !== null) {\n            console.log(currentNode.element)\n            currentNode = currentNode.next\n        }\n    }\n\n    // 尾插法 反转单链表\n    reverseList() {\n        const root = new Node('head')\n        let currentNode = this.head.next\n        while (currentNode !== null) {\n            const next = currentNode.next\n            currentNode.next = root.next\n            root.next = currentNode\n            currentNode = next\n        }\n        this.head = root\n    }\n\n    //增强尾插法可读性，便于初学者理解\n    reverseList1(){\n      //head节点即哨兵，作用就是使所有链表，\n      // 包括空链表的头节点不为null,并使对单链表的插入、删除操作不需要区分是否为空表或是否在第一个位置进行，\n      // 从而与其他位置的插入、删除操作一致\n      //所以反转链表的时候不需要带上head节点\n      let currentNode=this.head.next\n      //第一个节点头结点让其指向null\n      let previousNode=null\n      while(currentNode!==null){\n        //务必先保留下一节点的指针地址\n        let nextNode=currentNode.next\n        //第一次是null\n        currentNode.next=previousNode\n        //此时将previousNode赋值为当前节点，\n        // 那么下次循环的时候，方便下次的currentNode指向previousNode\n        previousNode=currentNode\n        //抬走，下一个！\n        currentNode=nextNode\n      }\n    //最后将反转好的链表加上头节点\n    this.head.next=previousNode\n    }\n\n    // 自己一开始瞎想的。差距啊\n    reverseList2() {\n        let currentNode = this.head.next\n        let reverseList = new LinkedList()\n        const tempArr = []\n        if (currentNode === null || currentNode.next === null) {\n            console.log('节点数小于3，不反转')\n            return\n        }\n        while (currentNode !== null) {\n            tempArr.push(currentNode)\n            currentNode = currentNode.next\n        }\n        for (let i = tempArr.length - 1; i >= 0; i--) {\n            const key = i === tempArr.length - 1 ? 'head' : tempArr[i + 1].element\n            reverseList.insert(tempArr[i].element, key)\n        }\n        reverseList.display()\n        return reverseList\n    }\n    // 环验证\n    checkCircle() {\n        let fast = this.head.next\n        let slow = this.head\n        while (fast !== null && fast.next !== null) {\n            fast = fast.next.next\n            slow = slow.next\n            if (slow === fast) return true\n        }\n        return false\n    }\n    // 删除倒数第k个节点\n    removeByIndexFromEnd(index) {\n        //务必先判断是否是 环链表\n        if(this.checkCircle()) return false\n        let pos = 1\n        this.reverseList()\n        let currentNode = this.head.next\n        while (currentNode !== null && pos < index) {\n            currentNode = currentNode.next\n            pos++\n        }\n        if (currentNode === null) {\n            console.log('无法删除最后一个节点或者该节点不存在')\n            return false\n        }\n        this.remove(currentNode.element)\n        this.reverseList()\n    }\n    // 求中间节点\n    findMiddleNode() {\n        let fast = this.head\n        let slow = this.head\n        while (fast.next !== null && fast.next.next !== null) {\n            fast = fast.next.next\n            slow = slow.next\n        }\n        console.log(slow)\n        return slow\n    }\n}\n\n\nconst mergeSortedLists = (listA, listB) => {\n    if (!listA) {\n        return listB\n    }\n    if (!listB) {\n        return listA\n    }\n\n    let a = listA\n    let b = listB\n    let resultList = undefined\n    if (a.element < b.element) {\n        resultList = a\n        a = a.next\n    } else {\n        resultList = b\n        b = b.next\n    }\n    let currentNode = resultList\n    while (a !== null && b !== null) {\n        if (a.element < b.element) {\n            currentNode.next = a\n            a = a.next\n        } else {\n            currentNode.next = b\n            b = b.next\n        }\n        currentNode = currentNode.next\n    }\n\n    if (a != null) {\n        currentNode.next = a\n    } else {\n        currentNode.next = b\n    }\n    return resultList\n}\n\n// Test\nconst LList = new LinkedList()\nLList.insert('chen', 'head')\nLList.insert('curry', 'chen')\nLList.insert('sang', 'head')\nLList.insert('zhao', 'head')\nconsole.log('-------------start reverse------------')\nLList.reverseList()\nLList.display()\nconsole.log('-------------check circle------------')\nconsole.log(LList.checkCircle())\nconsole.log('-------------remove the one before last ------------')\nLList.removeByIndexFromEnd(2)\nLList.display()\n\nconst sortedList1 = new LinkedList()\nsortedList1.insert(9, 'head')\nsortedList1.insert(8, 'head')\nsortedList1.insert(7, 'head')\nsortedList1.insert(6, 'head')\nconst sortedList2 = new LinkedList()\nsortedList2.insert(21, 'head')\nsortedList2.insert(20, 'head')\nsortedList2.insert(19, 'head')\nsortedList2.insert(18, 'head')\nconsole.log('-------------sort two list ------------')\nlet sortedList = mergeSortedLists(sortedList1.head.next, sortedList2.head.next)\nwhile (sortedList !== null) {\n    console.log(sortedList.element)\n    sortedList = sortedList.next\n}"
  },
  {
    "path": "javascript/08_stack/SampleBrowser.js",
    "content": "/**\n * 使用前后栈实现浏览器的前进后退。\n * \n * Author nameczz\n */\nconst stack = require('./StackBasedOnLinkedList')\n\nclass SampleBrowser {\n    constructor() {\n        this.normalStack = new stack.CreatedStack()\n        this.backStack = new stack.CreatedStack()\n    }\n    // 正常浏览页面\n    pushNormal(name) {\n        this.normalStack.push(name)\n        this.backStack.clear()\n        this.displayAllStack()\n    }\n    // 后退\n    back() {\n        const value = this.normalStack.pop()\n        if (value !== -1) {\n            this.backStack.push(value)\n            this.displayAllStack()\n        } else {\n            console.log('无法后退')\n        }\n    }\n    // 前进\n    front() {\n        const value = this.backStack.pop()\n        if (value !== -1) {\n            this.normalStack.push(value)\n            this.displayAllStack()\n        } else {\n            console.log('无法前进')\n        }\n    }\n    // 打印栈内数据\n    displayAllStack() {\n        console.log('---后退页面---')\n        this.backStack.display()\n        console.log('---浏览页面---')\n        this.normalStack.display()\n    }\n}\n// Test\nconst browser = new SampleBrowser()\nbrowser.pushNormal('www.google.com')\nbrowser.pushNormal('www.baidu.com')\nbrowser.pushNormal('www.github.com')\n// 后退\nbrowser.back()\nbrowser.back()\nbrowser.front()\nbrowser.pushNormal('www.new.com')"
  },
  {
    "path": "javascript/08_stack/StackBasedOnLinkedList.js",
    "content": "/**\n * 基于链表实现的栈。\n *\n * Author: nameczz\n */\n\nclass Node {\n    constructor(element) {\n        this.element = element\n        this.next = null\n    }\n}\n\nclass StackBasedLinkedList {\n    constructor() {\n        this.top = null\n    }\n    push(value) {\n        const node = new Node(value)\n        if (this.top === null) {\n            this.top = node\n        } else {\n            node.next = this.top\n            this.top = node\n        }\n    }\n    pop() {\n        if (this.top === null) {\n            return -1\n        }\n        const value = this.top.element\n        this.top = this.top.next\n        return value\n    }\n    // 为了实现浏览器前进后退\n    clear() {\n        this.top = null\n    }\n    display() {\n        if (this.top !== null) {\n            let temp = this.top\n            while (temp !== null) {\n                console.log(temp.element)\n                temp = temp.next\n            }\n        }\n    }\n}\n// Test\nconst newStack = new StackBasedLinkedList()\nnewStack.push(1)\nnewStack.push(2)\nnewStack.push(3)\n// 获取元素\nlet res = 0\nconsole.log('-------获取pop元素------')\nwhile (res !== -1) {\n    res = newStack.pop()\n    console.log(res)\n}\n\nexports.CreatedStack = StackBasedLinkedList"
  },
  {
    "path": "javascript/09_queue/CircularQueueBasedOnLinkedList.js",
    "content": "/**\n * 基于链表实现的循环队列。\n *\n * Author: nameczz\n */\n\nclass Node {\n    constructor(element) {\n        this.element = element\n        this.next = null\n    }\n}\n\nclass CircularQueue {\n    constructor() {\n        this.head = null\n        this.tail = null\n    }\n\n    enqueue(value) {\n        if (this.head === null) {\n            this.head = new Node(value)\n            this.head.next = this.head\n            this.tail = this.head\n        } else {\n            const flag = this.head === this.tail\n            this.tail.next = new Node(value)\n            this.tail.next.next = this.head\n            this.tail = this.tail.next\n            if (flag) {\n                this.head.next = this.tail\n            }\n        }\n    }\n\n    dequeue() {\n        if(this.head == null) return -1\n\n        if (this.head === this.tail) {\n            const value = this.head.element\n            this.head = null\n            return value\n        } else {\n            const value = this.head.element\n            this.head = this.head.next\n            this.tail.next = this.head\n            return value\n        } \n    }\n\n    display() {\n        let res = 0\n        console.log('-------获取dequeue元素------')\n        while (res !== -1) {\n            res = this.dequeue()\n            console.log(res)\n        }\n    }\n}\n// Test\nconst newCircularQueue = new CircularQueue()\n// 插入元素\nnewCircularQueue.enqueue(1)\nnewCircularQueue.enqueue(2)\nnewCircularQueue.enqueue(3)\n// 获取元素\nnewCircularQueue.display()\nnewCircularQueue.enqueue(1)\nnewCircularQueue.display()\n\n// exports.CreatedStack = StackBasedLinkedList"
  },
  {
    "path": "javascript/09_queue/QueueBasedOnLinkedList.js",
    "content": "/**\n * 基于链表实现的队列。\n *\n * Author: nameczz\n */\n\nclass Node {\n    constructor(element) {\n        this.element = element\n        this.next = null\n    }\n}\n\nclass QueueBasedOnLinkedList {\n    constructor() {\n        this.head = null\n        this.tail = null\n    }\n\n    enqueue(value) {\n        if (this.head === null) {\n            this.head = new Node(value)\n            this.tail = this.head\n        } else {\n            this.tail.next = new Node(value)\n            this.tail = this.tail.next\n        }\n    }\n\n    dequeue() {\n        if (this.head !== null) {\n            const value = this.head.element\n            this.head = this.head.next\n            return value\n        } else {\n            return -1\n        }\n    }\n}\n// Test\nconst newQueue = new QueueBasedOnLinkedList()\n// 插入元素\nnewQueue.enqueue(1)\nnewQueue.enqueue(2)\nnewQueue.enqueue(3)\n// 获取元素\nlet res = 0\nconsole.log('-------获取dequeue元素------')\nwhile (res !== -1) {\n    res = newQueue.dequeue()\n    console.log(res)\n}\n"
  },
  {
    "path": "javascript/11_sorts/sort.js",
    "content": "/**\n * 冒泡，插入，选择排序\n *\n * Author: nameczz\n */\n\n// 冒泡排序\nconst bubbleSort = (arr) => {\n    if (arr.length <= 1) return\n    for (let i = 0; i < arr.length; i++) {\n        let hasChange = false\n        for (let j = 0; j < arr.length - i - 1; j++) {\n            if (arr[j] > arr[j + 1]) {\n                const temp = arr[j]\n                arr[j] = arr[j + 1]\n                arr[j + 1] = temp\n                hasChange = true\n            }\n        }\n        // 如果false 说明所有元素已经到位\n        if (!hasChange) break\n    }\n    console.log(arr)\n}\n\n// 插入排序\nconst insertionSort = (arr) => {\n    if (arr.length <= 1) return\n    for (let i = 1; i < arr.length; i++) {\n        const temp = arr[i]\n        let j = i - 1\n        // 若arr[i]前有大于arr[i]的值的化，向后移位，腾出空间，直到一个<=arr[i]的值\n        for (j; j >= 0; j--) {\n            if (arr[j] > temp) {\n                arr[j + 1] = arr[j]\n            } else {\n                break\n            }\n        }\n        arr[j + 1] = temp\n    }\n    console.log(arr)\n}\n\n// 选择排序\nconst selectionSort = (arr) => {\n    if (arr.length <= 1) return\n    // 需要注意这里的边界, 因为需要在内层进行 i+1后的循环，所以外层需要 数组长度-1\n    for (let i = 0; i < arr.length - 1; i++) {\n        let minIndex = i\n        for (let j = i + 1; j < arr.length; j++) {\n            if (arr[j] < arr[minIndex]) {\n                minIndex = j // 找到整个数组的最小值\n            }\n        }\n        const temp = arr[i]\n        arr[i] = arr[minIndex]\n        arr[minIndex] = temp\n    }\n    console.log(arr)\n}\n\nconst test = [4, 5, 6, 3, 2, 1]\nbubbleSort(test)\nconst testSort = [4, 1, 6, 3, 2, 1]\ninsertionSort(testSort)\nconst testSelect = [4, 8, 6, 3, 2, 1, 0, 12]\nselectionSort(testSelect)"
  },
  {
    "path": "javascript/12_sorts/KthNum.js",
    "content": "/**\n * 第k大的数\n * @param {array} arr \n * @param {number} k  \n */\nfunction kthNum(arr, k) {\n  const len = arr.length;\n  if (k > len) {\n    return -1;\n  }\n  let p = partition(arr, 0, len - 1);\n  while (p + 1 !== k) {\n    if (p + 1 > k) {\n      p = partition(arr, 0, p - 1);\n    } else {\n      p = partition(arr, p + 1, len - 1);\n    }\n  }\n  return arr[p];\n}\n\nfunction partition(arr, start, end) {\n  let i = start;\n  let pivot = arr[end];\n  for (let j = start; j < end; j++) {\n    if (arr[j] < pivot) {\n      swap(arr, i, j);\n      i += 1;\n    }\n  }\n  swap(arr, i, end);\n  return i;\n}\n\nfunction swap(arr, i, j) {\n  if (i === j) return;\n  let tmp = arr[i];\n  arr[i] = arr[j];\n  arr[j] = tmp;\n}\n"
  },
  {
    "path": "javascript/12_sorts/MergeSort.js",
    "content": "/**\n * 归并排序\n *\n * Author: nameczz\n */\n\nconst mergeArr = (left, right) => {\n    let temp = []\n    let leftIndex = 0\n    let rightIndex = 0\n    // 判断2个数组中元素大小，依次插入数组\n    while (left.length > leftIndex && right.length > rightIndex) {\n        if (left[leftIndex] <= right[rightIndex]) {\n            temp.push(left[leftIndex])\n            leftIndex++\n        } else {\n            temp.push(right[rightIndex])\n            rightIndex++\n        }\n    }\n    // 合并 多余数组\n    return temp.concat(left.slice(leftIndex)).concat(right.slice(rightIndex))\n}\n\nconst mergeSort = (arr) => {\n    // 当任意数组分解到只有一个时返回。\n    if (arr.length <= 1) return arr\n    const middle = Math.floor(arr.length / 2) // 找到中间值\n    const left = arr.slice(0, middle) // 分割数组\n    const right = arr.slice(middle)\n    // 递归 分解 合并\n    return mergeArr(mergeSort(left), mergeSort(right))\n}\n\nconst testArr = []\nlet i = 0\nwhile (i < 100) {\n    testArr.push(Math.floor(Math.random() * 1000))\n    i++\n}\n\nconst res = mergeSort(testArr)\nconsole.log(res)"
  },
  {
    "path": "javascript/12_sorts/QuickSort.js",
    "content": "/**\n * 快速排序\n *\n * Author: nameczz\n */\n\nconst swap = (arr, i, j) => {\n    const temp = arr[i]\n    arr[i] = arr[j]\n    arr[j] = temp\n}\n\n// 获取 pivot 交换完后的index\nconst partition = (arr, pivot, left, right) => {\n    const pivotVal = arr[pivot]\n    let startIndex = left\n    for (let i = left; i < right; i++) {\n        if (arr[i] < pivotVal) {\n            swap(arr, i, startIndex)\n            startIndex++\n        }\n    }\n    swap(arr, startIndex, pivot)\n    return startIndex\n}\n\nconst quickSort = (arr, left, right) => {\n    if (left < right) {\n        let pivot = right\n        let partitionIndex = partition(arr, pivot, left, right)\n        quickSort(arr, left, partitionIndex - 1 < left ? left : partitionIndex - 1)\n        quickSort(arr, partitionIndex + 1 > right ? right : partitionIndex + 1, right)\n    }\n\n}\n\n\nconst testArr = []\nlet i = 0\nwhile (i < 10) {\n    testArr.push(Math.floor(Math.random() * 1000))\n    i++\n}\nconsole.log('unsort', testArr)\nquickSort(testArr, 0, testArr.length - 1);\nconsole.log('sort', testArr)"
  },
  {
    "path": "javascript/13_sorts/bucketSort.js",
    "content": "// 思路：\n// 将数组中的数据，按桶进行划分，将相邻的数据划分在同一个桶中\n// 每个桶用插入排序算法（或者快速排序）进行排序\n// 最后整合每个桶中的数据\n\nfunction bucketSort(array, bucketSize = 5) {\n    if (array.length < 2) {\n        return array\n    }\n    const buckets = createBuckets(array, bucketSize)\n    return sortBuckets(buckets)\n}\n\nfunction createBuckets(array, bucketSize) {\n    let minValue = array[0]\n    let maxValue = array[0]\n    // 遍历数组，找到数组最小值与数组最大值\n    for (let i = 1; i < array.length; i++) {\n        if (array[i] < minValue) {\n            minValue = array[i]\n        } else if (array[i] > maxValue) {\n            maxValue = array[i]\n        }\n    }\n    // 根据最小值、最大值、桶的大小，计算得到桶的个数\n    const bucketCount = Math.floor((maxValue - minValue) / bucketSize) + 1\n    // 建立一个二维数组，将桶放入buckets中\n    const buckets = []\n    for (let i = 0; i < bucketCount; i++) {\n        buckets[i] = []\n    }\n    // 计算每一个值应该放在哪一个桶中\n    for (let i = 0; i < array.length; i++) {\n        const bucketIndex = Math.floor((array[i] - minValue) / bucketSize)\n        buckets[bucketIndex].push(array[i])\n    }\n    return buckets\n}\n\nfunction sortBuckets(buckets) {\n    const sortedArray = []\n    for (let i = 0; i < buckets.length; i++) {\n        if (buckets[i] != null) {\n            insertionSort(buckets[i])\n            sortedArray.push(...buckets[i])\n        }\n    }\n    return sortedArray\n}\n\n// 插入排序\nfunction insertionSort(array) {\n    const { length } = array\n    if (length <= 1) return\n\n    for (let i = 1; i < length; i++) {\n        let value = array[i]\n        let j = i - 1\n\n        while (j >= 0) {\n            if (array[j] > value) {\n                array[j + 1] = array[j] // 移动\n                j--\n            } else {\n                break\n            }\n        }\n        array[j + 1] = value // 插入数据\n    }\n}\n"
  },
  {
    "path": "javascript/13_sorts/countingSort.js",
    "content": "const countingSort = array => {\n    if (array.length <= 1) return\n\n    const max = findMaxValue(array)\n    const counts = new Array(max + 1)\n\n    // 计算每个元素的个数，放入到counts桶中\n    // counts下标是元素，值是元素个数\n    array.forEach(element => {\n        if (!counts[element]) {\n            counts[element] = 0\n        }\n        counts[element]++\n    })\n\n    // counts下标是元素，值是元素个数\n    // 例如： array: [6, 4, 3, 1], counts: [empty, 1, empty, 1, 1, empty, 1]\n    // i是元素, count是元素个数\n    let sortedIndex = 0\n    counts.forEach((count, i) => {\n        while (count > 0) {\n            array[sortedIndex] = i\n            sortedIndex++\n            count--\n        }\n    })\n    // return array\n}\n\nfunction findMaxValue(array) {\n    let max = array[0]\n    for (let i = 1; i < array.length; i++) {\n        if (array[i] > max) {\n            max = array[i]\n        }\n    }\n    return max\n}\n"
  },
  {
    "path": "javascript/15_binary/binaryFind.js",
    "content": "/**\n * 二分查找\n *\n * Author: nameczz\n */\n// 数组必须有序 不存在重复\nconst biaryFind = (sortedArr, target) => {\n    if (sortedArr.length === 0) return -1\n    let low = 0\n    let high = sortedArr.length - 1\n    while (low <= high) {\n        const mid = Math.floor((low + high) / 2)\n        if (target === sortedArr[mid]) {\n            return mid\n        } else if (target < sortedArr[mid]) {\n            high = mid - 1\n        } else {\n            low = mid + 1\n        }\n    }\n    return -1\n}\nconst arr = [1, 4, 5, 6, 7, 8, 10, 11, 23, 42, 44, 54, 56, 77, 102]\nconsole.log(biaryFind(arr, 44))\nconsole.log(biaryFind(arr, 1))\nconsole.log(biaryFind(arr, 102))\nconsole.log(biaryFind(arr, 1111))"
  },
  {
    "path": "javascript/16_binary/binary-find.js",
    "content": "/**\n * 二分查找\n *\n * Author: nameczz\n */\n\n\n// 查找第一个等于给定值\nconst biaryFindFirst = (sortedArr, target) => {\n    if (sortedArr.length === 0) return -1\n    let low = 0\n    let high = sortedArr.length - 1\n    while (low <= high) {\n        const mid = Math.floor((low + high) / 2)\n\n        if (target < sortedArr[mid]) {\n            high = mid - 1\n        } else if (target > sortedArr[mid]) {\n            low = mid + 1\n        } else {\n            if (mid === 0 || sortedArr[mid - 1] < target) return mid\n            high = mid - 1\n        }\n    }\n    return -1\n}\n\n// 查找最后一个相等的数\nconst biaryFindLast = (sortedArr, target) => {\n    if (sortedArr.length === 0) return -1\n    let low = 0\n    let high = sortedArr.length - 1\n    while (low <= high) {\n        const mid = Math.floor((low + high) / 2)\n        if (target < sortedArr[mid]) {\n            high = mid - 1\n        } else if (target > sortedArr[mid]) {\n            low = mid + 1\n        } else {\n            if (mid === sortedArr.length - 1 || sortedArr[mid + 1] > target) return mid\n            low = mid + 1\n        }\n    }\n    return -1\n}\n\n// 查找第一个大于等于给定值的元素\nconst biaryFindFistBig = (sortedArr, target) => {\n    if (sortedArr.length === 0) return -1\n    let low = 0\n    let high = sortedArr.length - 1\n    while (low <= high) {\n        const mid = Math.floor((low + high) / 2)\n        if (target <= sortedArr[mid]) {\n            if (mid === 0 || sortedArr[mid - 1] < target) return mid\n            high = mid - 1\n        } else {\n            low = mid + 1\n        }\n    }\n    return -1\n}\n\n// 查找最后一个小于等于给定值的元素\nconst biaryFindLastSmall = (sortedArr, target) => {\n    if (sortedArr.length === 0) return -1\n    let low = 0\n    let high = sortedArr.length - 1\n    while (low <= high) {\n        const mid = Math.floor((low + high) / 2)\n        if (target < sortedArr[mid]) {\n            high = mid - 1\n        } else {\n            if (mid === sortedArr.length - 1 || sortedArr[mid + 1] >= target) return mid\n            low = mid + 1\n        }\n    }\n    return -1\n}\n\nconst arr = [1, 2, 3, 4, 4, 4, 4, 4, 6, 7, 8, 8, 9]\nconst first = biaryFindFirst(arr, 4)\nconsole.log(`FindFirst: ${first}`)\n\nconst last = biaryFindLast(arr, 4)\nconsole.log(`FindLast: ${last}`)\nconst FisrtBig = biaryFindFistBig(arr, 5)\nconsole.log(`FindFisrtBig: ${FisrtBig}`)\nconst LastSmall = biaryFindLastSmall(arr, 4)\nconsole.log(`FindLastSmall: ${LastSmall}`)\n"
  },
  {
    "path": "javascript/17_skiplist/SkipList.js",
    "content": "/**\n * author dreamapplehappy\n * 关于代码的一些解释可以看一下这里：https://github.com/dreamapplehappy/blog/tree/master/2018/12/02\n */\n\nconst MAX_LEVEL = 16;\n\nclass Node {\n    constructor({\n        data = -1,\n        maxLevel = 0,\n        refer = new Array(MAX_LEVEL)\n    } = {}) {\n        this.data = data;\n        this.maxLevel = maxLevel;\n        this.refer = refer\n    }\n}\n\nclass SkipList{\n    constructor() {\n        this.head = new Node();\n        this.levelCount = 1;\n    }\n    randomLevel() {\n        let level = 1;\n        for (let i = 1; i < MAX_LEVEL; i++) {\n            if (Math.random() < 0.5) {\n                level++;\n            }\n        }\n        return level;\n    }\n    insert(value) {\n        const level = this.randomLevel();\n        const newNode = new Node();\n        newNode.data = value;\n        newNode.maxLevel = level;\n        const update = new Array(level).fill(new Node());\n        let p = this.head;\n        for(let i = level - 1; i >= 0; i--) {\n            while(p.refer[i] !== undefined && p.refer[i].data < value) {\n                p = p.refer[i];\n            }\n            update[i] = p;\n        }\n        for(let i = 0; i < level; i++) {\n            newNode.refer[i] = update[i].refer[i];\n            update[i].refer[i] = newNode;\n        }\n        if(this.levelCount < level) {\n            this.levelCount = level;\n        }\n    }\n\n    find(value) {\n        if(!value){return null}\n        let p = this.head;\n        for(let i = this.levelCount - 1; i >= 0; i--) {\n            while(p.refer[i] !== undefined && p.refer[i].data < value) {\n                p = p.refer[i];\n            }\n        }\n\n        if(p.refer[0] !== undefined && p.refer[0].data === value) {\n            return p.refer[0];\n        }\n        return null;\n    }\n\n    remove(value) {\n        let _node;\n        let p = this.head;\n        const update = new Array(new Node());\n        for(let i = this.levelCount - 1; i >= 0; i--) {\n            while(p.refer[i] !== undefined && p.refer[i].data < value){\n                p = p.refer[i];\n            }\n            update[i] = p;\n        }\n\n        if(p.refer[0] !== undefined && p.refer[0].data === value) {\n            _node = p.refer[0];\n            for(let i = 0; i <= this.levelCount - 1; i++) {\n                if(update[i].refer[i] !== undefined && update[i].refer[i].data === value) {\n                    update[i].refer[i] = update[i].refer[i].refer[i];\n                }\n            }\n            return _node;\n        }\n        return null;\n    }\n\n    printAll() {\n        let p = this.head;\n        while(p.refer[0] !== undefined) {\n            console.log(p.refer[0].data)\n            p = p.refer[0];\n        }\n    }\n}\n\ntest();\nfunction test() {\n    let list = new SkipList();\n    let length = 20000;\n    //顺序插入\n    for (let i = 1; i <= 10; i++) {\n        list.insert(i);\n    }\n    //输出一次\n    list.printAll();\n    console.time('create length-10')\n    //插入剩下的\n    for (let i = 11; i <= length - 10; i++) {\n        list.insert(i);\n    }\n    console.timeEnd('create length-10')\n    //搜索 10次\n    for (let j = 0; j < 10; j++) {\n        let key = Math.floor(Math.random() * length + 1);\n        console.log(key, list.find(key))\n    }\n    //搜索不存在的值\n    console.log('null:', list.find(length + 1));\n    //搜索5000次统计时间\n    console.time('search 5000');\n    for (let j = 0; j < 5000; j++) {\n        let key = Math.floor(Math.random() * length + 1);\n    }\n    console.timeEnd('search 5000');\n}\n\n"
  },
  {
    "path": "javascript/18_hashmap/HashTable.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"UTF-8\">\n    <title>Title</title>\n</head>\n<body>\n<script>\n    /*\n    最基本的散列表\n    */\n\n    class HashTable {\n        constructor() {\n            this.table=[];\n        }\n        //散列函数\n        loseHashCode(key){\n            var hash=0;\n            //从ASCII表中查到的ASCII值加到hash中\n            for (var i=0;i<key.length;i++){\n                hash+=key.charCodeAt(i);\n            }\n            //为了得到比较小的数值，我们会用hash和任意数除余\n            return hash%37;\n        }\n        //向散列表增加一个新的项\n        put(key,value){\n            var position=this.loseHashCode(key);\n            console.log(position+'-'+key);\n            this.table[position]=value;\n        }\n        //根据键从散列表删除值\n        remove(key){\n            this.table[this.loseHashCode(key)]=undefined;\n        }\n        //返回根据键值检索到的特定的值\n        get(key){\n            console.log(this.table[this.loseHashCode(key)])\n        }\n        print(){\n            for (var i=0;i<this.table.length;++i){\n                if (this.table[i]!==undefined){\n                    console.log(i+':'+this.table[i]);\n                }\n            }\n        }\n    }\n    var hash = new HashTable();\n    hash.put('Gandalf', 'gandalf@email.com');\n    hash.put('John', 'johnsnow@email.com');\n    hash.put('Tyrion', 'tyrion@email.com');\n    hash.remove('Gandalf')\n    hash.get('Gandalf')\n    hash.get('Tyrion')\n    hash.print()\n\n    //\n</script>\n</body>\n</html>\n"
  },
  {
    "path": "javascript/19_hashTable/hashtable.js",
    "content": "/****\n *  带碰撞处理的Hash表\n *  实际上在js中,单独实现一个Hash表感觉不是很有实用价值\n *  如果需要通常是直接将Object,Map,Set来当Hash表用\n *  \n * 总结：\n *  我写的这个实现把store 从Object换成Array不会有运行性能上的区别\n *  把hash函数改成生成一定范围的值的类型,然后初始化一个指定长度的数组因该会有一定的性能提升\n *  把store换成Map，然后修改相关实现会获得飞越性的提升，因为在js中Map的实现对这种类型的操作做了优化\n */\nclass HashTable {\n    constructor() {\n        //创建一个没有原型链的对象\n        this.store = Object.create(null);\n    }\n    /**\n     *  Donald E. Knuth在“计算机编程艺术第3卷”中提出的算法，主题是排序和搜索第6.4章。\n     * @param {*} string \n     *  翻译自别的语言的实现\n     *  需要注意的是由于js中没有int类型，number是dobule的标准实现\n     *  所以返回前的位运算实际和本来的设想不一致，也就是同样的实现，在别的语言中返回可能不同\n     */\n    hash(string) {\n        let len = string.length;\n        let hash = len;\n        for (let i = 0; i < len; i++) {\n            hash = ((hash << 5) ^ (hash >> 27)) ^ string.charCodeAt(i);\n        }\n        return hash & 0x7FFFFFFF;\n    }\n\n    isCresh(item) {\n        return Object.prototype.toString.call(item) === \"[object Map]\"\n    }\n    /**\n     * 约定item必须要有key\n     * @param {*} item \n     */\n    put(item) {\n        if (typeof item.key !== 'string') {\n            throw 'item must have key!'\n        }\n        let hash = this.hash(item.key);\n        //碰撞处理\n        let cresh = this.store[hash];\n        if (cresh) {\n            if (cresh.key === item.key) {\n                this.store[hash] = item;\n                return\n            }\n\n            if (!this.isCresh(cresh)) {\n                this.store[hash] = new Map();\n            }\n            this.store[hash].set(item.key, item);\n        } else {\n            this.store[hash] = item;\n        }\n    }\n    get(key) {\n        let hash = this.hash(key);\n        let value = this.store[hash] || null;\n        if (this.isCresh(value)) {\n            return value.get(key);\n        } else {\n            return value\n        }\n    }\n    remove(key) {\n        let hash = this.hash(key);\n        let value = this.store[hash];\n        if (!value) {\n            return null;\n        }\n        if (this.isCresh(value)) {\n            value.delete(key);\n        } else {\n            delete this.store[hash];\n        }\n    }\n    clear() {\n        this.store = {};\n    }\n    print() {\n        let values = Object.values(this.store);\n        values.forEach(element => {\n            if (this.isCresh(element)) {\n                element.forEach(item => {\n                    console.log(item);\n                });\n            } else {\n                console.log(element)\n            }\n        });\n    }\n}\n/**\n * 相比使用Object和Array做store 运行时的性能提升了三分之一\n * 但当前这种用法没有直接使用Map方便，而且直接使用Map会快的多\n */\nclass HashTableBaseMap {\n    constructor() {\n        this.store = new Map();\n    }\n    /**\n     *  Donald E. Knuth在“计算机编程艺术第3卷”中提出的算法，主题是排序和搜索第6.4章。\n     * @param {*} string \n     *  翻译自别的语言的实现\n     *  需要注意的是由于js中没有int类型，number是dobule的标准实现\n     *  所以返回前的位运算实际和本来的设想不一致，也就是同样的实现，在别的语言中返回可能不同\n     */\n    hash(string) {\n        let len = string.length;\n        let hash = len;\n        for (let i = 0; i < len; i++) {\n            hash = ((hash << 5) ^ (hash >> 27)) ^ string.charCodeAt(i);\n        }\n        return hash & 0x7FFFFFFF;\n    }\n\n    isCresh(item) {\n        return Object.prototype.toString.call(item) === \"[object Map]\"\n    }\n    /**\n     * 约定item必须要有key\n     * @param {*} item \n     */\n    put(item) {\n        if (typeof item.key !== 'string') {\n            throw 'item must have key!'\n        }\n        let hash = this.hash(item.key);\n        //碰撞处理\n        let cresh = this.store.get(hash);\n        if (cresh) {\n            if (cresh.key === item.key) {\n                this.store.set(hash, item);\n                return\n            }\n\n            if (!this.isCresh(cresh)) {\n                this.store[hash] = new Map();\n            }\n            this.store[hash].set(item.key, item);\n        } else {\n            this.store.set(hash, item);\n        }\n    }\n    get(key) {\n        let hash = this.hash(key);\n        let value = this.store.get(hash);\n        if (this.isCresh(value)) {\n            return value.get(key);\n        } else {\n            return value\n        }\n    }\n    remove(key) {\n        let hash = this.hash(key);\n        let value = this.store.get(hash);\n        if (!value) {\n            return null;\n        }\n        if (this.isCresh(value)) {\n            value.delete(key);\n        } else {\n            this.store.delete(hash)\n        }\n    }\n    clear() {\n        this.store = {};\n    }\n    print() {\n        this.store.forEach(element => {\n            if (this.isCresh(element)) {\n                element.forEach(item => {\n                    console.log(item);\n                });\n            } else {\n                console.log(element)\n            }\n        });\n    }\n}\n\n/**\n * 基础测试\n */\nfunction baseTest() {\n    let hashTable = new HashTable();\n    for (let i = 0; i < 10; i++) {\n        hashTable.put({\n            key: 'test' + i,\n            value: 'some value' + i\n        });\n    }\n    console.log('step1:')\n    //随机获取5次\n    for (let j = 0; j < 5; j++) {\n        let key = 'test' + Math.floor(Math.random() * 10);\n        console.log(key);\n        console.log(hashTable.get(key))\n    }\n    //获得一次空值\n    console.log('get null:', hashTable.get('test10'))\n    //修改一次值\n    hashTable.put({\n        key: 'test1',\n        value: 'change'\n    });\n    //删除一次值\n    hashTable.remove('test2');\n    console.log('step2:')\n    //输出修改后所有的\n    hashTable.print();\n}\n\n/**\n * 有序key存取，性能测试\n */\nfunction ordKeyTest() {\n    let length = 1000000;\n    console.time('create')\n    let hashTable = new HashTable();\n    for (let i = 0; i < length; i++) {\n        //24位长度有序key\n        hashTable.put({\n            key: 'someTestSoSoSoSoLongKey' + i,\n            value: 'some value' + i\n        });\n    }\n    console.timeEnd('create')\n\n    let get = 100000;\n    console.time('get')\n    for (let j = 0; j < get; j++) {\n        let key = 'test' + Math.floor(Math.random() * 999999);\n        hashTable.get(key)\n    }\n    console.timeEnd('get')\n}\n\n/**\n *  无序key性能测试\n *  这个查找稍微有点不准，会有一定量随机字符串重复\n *  实际结果，创建没有区别，大数据量下由于无序key有一些会碰撞，get的总体用的时间会多不少。\n */\nfunction randKeyTest() {\n    let length = 1000000;\n    let keyList = [];\n    for (let i = 0; i < length; i++) {\n        keyList.push(randomString());\n    }\n    console.time('create')\n    let hashTable = new HashTable();\n    for (let i = 0; i < length; i++) {\n        hashTable.put({\n            key: keyList[i],\n            value: 'some value' + i\n        });\n    }\n    console.timeEnd('create')\n    let get = 100000;\n    console.time('get')\n    for (let j = 0; j < get; j++) {\n        let key = keyList[Math.floor(Math.random() * 999999)];\n        hashTable.get(key)\n    }\n    console.timeEnd('get')\n}\n\n/**\n *  直接使用Object的性能测试\n *  有序就不测了，估计不会有区别，只看不使用hash的无序key\n *  结果:想达到同样的结果创建会比hash后的慢接近四分之三,获取用时差不多\n */\nfunction randKeyTestFromObj() {\n    let length = 1000000;\n    let keyList = [];\n    for (let i = 0; i < length; i++) {\n        keyList.push(randomString());\n    }\n    console.time('create')\n    let hashTable = {};\n    for (let i = 0; i < length; i++) {\n        let key = keyList[i];\n        hashTable[key] = {\n            key: key,\n            value: 'some value' + i\n        }\n    }\n    console.timeEnd('create')\n    let get = 100000;\n    console.time('get')\n    for (let j = 0; j < get; j++) {\n        let key = keyList[Math.floor(Math.random() * 999999)];\n        hashTable[key]\n    }\n    console.timeEnd('get')\n}\n/**\n *  直接使用Map的性能测试\n *  结果：创建用时差不多，但是获取快了一个数量级(十倍不止)\n */\nfunction randKeyTestFromMap() {\n    let length = 1000000;\n    let keyList = [];\n    for (let i = 0; i < length; i++) {\n        keyList.push(randomString());\n    }\n    console.time('create')\n    let hashTable = new Map();\n    for (let i = 0; i < length; i++) {\n        let key = keyList[i];\n        hashTable.set(key, {\n            key: key,\n            value: 'some value' + i\n        })\n    }\n    console.timeEnd('create')\n    let get = 100000;\n    console.time('get')\n    for (let j = 0; j < get; j++) {\n        let key = keyList[Math.floor(Math.random() * 999999)];\n        hashTable.get(key);\n    }\n    console.timeEnd('get')\n}\n\n//生成指定长度的字符串\nfunction randomString(len) {\n    len = len || 24;\n    var chars = 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678';\n    var maxPos = chars.length;\n    var pwd = '';\n    for (i = 0; i < len; i++) {\n        pwd += chars.charAt(Math.floor(Math.random() * maxPos));\n    }\n    return pwd;\n}"
  },
  {
    "path": "javascript/23_tree/binary_tree.js",
    "content": "class Node {\n    constructor(value) {\n        this.value = value;\n        this.left = null;\n        this.right = null;\n    }\n}\n/**\n * 搜索二叉树\n *  允许重复值添加\n *  实现有点弯弯绕\n */\nclass SearchTree {\n    constructor() {\n        this.root = null;\n    }\n    insert(num) {\n        let node = new Node(num);\n        if (this.root === null) {\n            this.root = node;\n            return\n        }\n        let prent = this.getPrev(num);\n        if (num < prent.value) {\n            prent.left = node;\n        } else {\n            prent.right = node;\n        }\n\n    }\n    remove(num) {\n        let point = this.root;\n        let prent = null;\n        let tree = this;\n\n        let res = null;\n        while (true) {\n            if (point.left) {\n                if (num < point.left.value || num < point.value) {\n                    prent = point;\n                    point = point.left;\n                    continue\n                }\n            }\n            if (point.right) {\n                if (num >= point.right.value || num >= point.value) {\n                    if (num === point.value) {\n                        delMethod(point, prent);\n                        if (prent === null) {\n                            point = this.root;\n                        } else {\n                            prent = prent;\n                            point = prent.right;\n                        }\n                        res = true;\n                        continue\n                    }\n                    prent = point;\n                    point = point.right;\n                    continue\n                }\n            }\n            if (point.value === num) {\n                res = true;\n                delMethod(point, prent);\n            }\n            break;\n        }\n        return res;\n        function delMethod(delNode, parent) {\n            let p = delNode; // p指向要删除的节点\n            let pp = parent; // pp记录的是p的父节点 \n\n            // 要删除的节点有两个子节点\n            if (p.left != null && p.right != null) { // 查找右子树中最小节点\n                let minP = p.right;\n                let minPP = p; // minPP表示minP的父节点\n                while (minP.left != null) {\n                    minPP = minP;\n                    minP = minP.left;\n                }\n                p.value = minP.value; // 将minP的数据替换到p中\n                p = minP; // 下面就变成了删除minP了\n                pp = minPP;\n            }\n\n            // 删除节点是叶子节点或者仅有一个子节点\n            let child; // p的子节点\n            if (p.left != null) child = p.left;\n            else if (p.right != null) child = p.right;\n            else child = null;\n\n            if (pp == null) {\n                tree.root = child\n            }\n            else if (pp.left == p) {\n                pp.left = child;\n            }\n            else {\n                pp.right = child;\n            }\n        }\n\n    }\n    //中序遍历\n    print() {\n        let point = this.root;\n        if (point) {\n            printAll(point.left)\n            console.log(point.value);\n            printAll(point.right)\n        }\n        function printAll(point) {\n            if (point == null) {\n                return\n            }\n            printAll(point.left);\n            console.log(point.value);\n            printAll(point.right)\n        }\n    }\n    find(num) {\n        if (this.root === null) {\n            this.root = node;\n            return\n        }\n        return this.getPrev(num, true);\n    }\n    //添加和查找的公用部分\n    getPrev(num, find = false) {\n        let point = this.root;\n        let res = [];\n        while (true) {\n            if (point.left) {\n                if (num < point.left.value || num < point.value) {\n                    point = point.left\n                    continue\n                }\n            }\n\n            if (point.right) {\n                if (num >= point.right.value || num >= point.value) {\n                    //搜索时如果有多个值则缓存\n                    if (find && num === point.value) {\n                        res.push(point.value);\n                    }\n                    point = point.right;\n                    continue\n                }\n            }\n            //如果是搜索\n            if (find) {\n                if (point.value === num) {\n                    res.push(point.value);\n                }\n\n                if (res.length === 0) {\n                    return null\n                }\n\n                if (res.length === 1) {\n                    return res[0];\n                }\n\n                return res;\n            }\n            //如果是添加 返回的是应该添加的那各节点的父节点\n            return point;\n        }\n    }\n}\n\n\n\nfunction baseTest() {\n    let searchTree = new SearchTree();\n    console.log('step 1:')\n    searchTree.insert(4);\n    searchTree.insert(1);\n    searchTree.insert(2);\n    searchTree.insert(5);\n\n    searchTree.print(); //1 2 4 5\n    console.log('step 2:')\n    console.log('5', searchTree.find(5)) //5\n    console.log('null:', searchTree.find(6)) //null\n    searchTree.insert(5);\n    searchTree.insert(5);\n    console.log('5,5,5:', searchTree.find(5))\n\n\n}\n//删除测试\nfunction delTest() {\n    let searchTree = new SearchTree();\n    console.log('add: 4 1 2 5 ')\n    searchTree.insert(4);\n    searchTree.insert(1);\n    searchTree.insert(2);\n    searchTree.insert(5);\n    searchTree.print(); //1 2 4 5\n    //console.log('del 3 null:', searchTree.remove(3));\n    console.log('del 1 true:', searchTree.remove(1));\n    // console.log('print 2 4 5:')\n    // searchTree.print();\n    // console.log('del 4 root true:', searchTree.remove(4));\n    // console.log('print 2 5:')\n    // searchTree.print();\n    // console.log('add: 3 7 1 5 5 5 ')\n    // searchTree.insert(3);\n    // searchTree.insert(7);\n    // searchTree.insert(1);\n    // searchTree.insert(5);\n    // searchTree.insert(5);\n    // searchTree.insert(5);\n    // console.log('print: 1 2 3 5 5 5 5 7 ')\n    // searchTree.print();\n    // console.log('del 5 true:', searchTree.remove(5));\n    // console.log('print: 1 2 3 7 ')\n    // searchTree.print();\n}\n\ndelTest();\n\n\n\n\n\n"
  },
  {
    "path": "javascript/28_heapsort/heap-sort.js",
    "content": "/**\n * 堆排序\n *\n * Author: nameczz\n */\n\n// 忽视数组的第一位\nclass HeapSort {\n    constructor(originArray) {\n        this.originArray = originArray\n        console.log(this.originArray)\n    }\n    buildHeap() {\n        const arr = this.originArray\n        const startIndex = Math.floor(arr.length)\n        for (let i = startIndex; i >= 1; i--) {\n            this.heapify(arr, arr.length, i)\n        }\n        return arr\n    }\n\n    heapify(arr, len, i) {\n        while (true) {\n            let maxPos = i\n            // 如果index i拥有叶左节点 并且左节点较大\n            if (i * 2 <= len && arr[i] < arr[i * 2]) {\n                maxPos = i * 2\n            }\n            // 如果index i拥有叶右节点 与Max节点比较大小，选出父/左/右中最大的一个\n            if (i * 2 + 1 <= len && arr[maxPos] < arr[i * 2 + 1]) {\n                maxPos = i * 2 + 1\n            }\n            if (maxPos === i) break // 循环直到i节点为最大值\n            this.swap(arr, i, maxPos) // 交换位置, 父节点为父/左/右中最大的一个\n            i = maxPos // i为左/右节点，并尝试向下查找更大的值\n        }\n    }\n\n    sort() {\n        const arr = this.buildHeap() // 先建堆\n        let len = arr.length - 1\n        while (len > 1) {\n            this.swap(arr, 1, len) // 交换顶元素和最后一位。顶元素永远是最大的。\n            len--\n            this.heapify(arr, len, 1) //剩下的元素重新建堆 直到len === 1 停止\n        }\n        console.log(arr)\n    }\n\n    swap(arr, i, max) {\n        let temp = arr[i]\n        arr[i] = arr[max]\n        arr[max] = temp\n    }\n}\n\nconst arr = [null]\nlet i = 0\nwhile (i <= 10) {\n    const num = Math.floor(Math.random() * 100)\n    arr.push(num)\n    i++\n}\nconst testHeap = new HeapSort(arr)\ntestHeap.sort()"
  },
  {
    "path": "javascript/28_heapsort/heap.js",
    "content": "/**\n * 优先队列的 堆实现\n */\nclass HeapNode {\n    constructor(num, item) {\n        this.sortNum = num;\n        this.content = item;\n    }\n}\n\nclass Heap {\n    constructor(arr = []) {\n        this.PRIVATE = {\n            swap(arr, i, j) {\n                let temp = arr[i]\n                arr[i] = arr[j]\n                arr[j] = temp\n            },\n            //从point往下 堆化\n            heapify(point = 1) {\n                let { swap, store } = this;\n                while (true) {\n                    let lPoint = point * 2;\n                    let rPoint = point * 2 + 1;\n                    if (store[lPoint] && store[point].sortNum < store[lPoint].sortNum) {\n                        swap(store, point, lPoint);\n                        point = lPoint;\n                        continue\n                    }\n                    if (store[rPoint] && store[point].sortNum < store[rPoint].sortNum) {\n                        swap(store, point, rPoint);\n                        point = rPoint;\n                        continue\n                    }\n                    break;\n                }\n\n            },\n            store: [null].concat(arr)\n        }\n        //建堆\n        //从最后一个非子叶节点遍历\n        for (let i = (this.PRIVATE.store.length / 2 | 0); i > 1; i--) {\n            this.PRIVATE.heapify(i);\n        }\n\n    }\n    insert(node) {\n        let store = this.PRIVATE.store;\n        let HeapUtil = this.PRIVATE;\n        store.push(node);\n\n        let point = store.length - 1;\n        let sub = point / 2 | 0;\n        while (sub > 0 && store[point].sortNum > store[sub].sortNum) { // 自下往上堆化\n            HeapUtil.swap(store, point, sub); // swap()函数作用：交换下标为i和i/2的两个元素\n            point = sub;\n            sub = sub / 2 | 0;\n        }\n    }\n    getMax() {\n        let store = this.PRIVATE.store;\n        let point = store.length - 1;\n        if (point === 0) {\n            return null;\n        }\n        let HeapUtil = this.PRIVATE;\n        //最大与末尾元素交换\n        HeapUtil.swap(store, point, 1);\n        let max = store.pop();\n        HeapUtil.heapify();\n        return max;\n    }\n\n}\n\nfunction HeapTest() {\n    let maxHeap = new Heap();\n    console.log('偶数个')\n    maxHeap.insert(new HeapNode(2, 'c'))\n    maxHeap.insert(new HeapNode(1, 'c'))\n    maxHeap.insert(new HeapNode(7, 'a'))\n    maxHeap.insert(new HeapNode(4, 'c'))\n    console.log('check:', isHeapArr(maxHeap.PRIVATE.store));\n    console.log('奇数个')\n    maxHeap.insert(new HeapNode(5, 'b'))\n    maxHeap.insert(new HeapNode(6, 'c'))\n    maxHeap.insert(new HeapNode(10, 'a'))\n    console.log('check:', isHeapArr(maxHeap.PRIVATE.store));\n    console.log('获取最大值：', maxHeap.getMax());\n    console.log('check:', isHeapArr(maxHeap.PRIVATE.store));\n    console.log('获取最大值：', maxHeap.getMax());\n    console.log('check:', isHeapArr(maxHeap.PRIVATE.store));\n\n}\nfunction createTest() {\n    console.log('随机创建测试:')\n    let arr = [];\n    let i = 0\n    while (i <= 10) {\n        const num = Math.floor(Math.random() * 100)\n        arr.push(new HeapNode(num, i))\n        i++\n    }\n    let heap = new Heap(arr);\n    console.log('check:', isHeapArr(heap.PRIVATE.store))\n}\n\nfunction isHeapArr(arr) {\n    for (let i = 1; i < arr.length; i++) {\n        let p = arr[i];\n        let l = arr[i * 2];\n        let r = arr[i * 2 + 1];\n        if (l > p || r > p) {\n            return false;\n        }\n    }\n    return true;\n}\n"
  },
  {
    "path": "javascript/35_trie/trie.js",
    "content": "\n\nclass TrieNode {\n    constructor(data){\n        this.data = data;\n        this.children = new Array(26);\n        this.isEndingChar = false\n    }\n}\n\nclass TrieTree {\n\n    constructor(data){\n        this.root = new TrieNode('/')\n    }\n\n    insert (text) {\n        let node = this.root;\n        for (let char of text) {\n            let index = char.charCodeAt() - 'a'.charCodeAt();\n            if(!node.children[index]) {\n                node.children[index] = new TrieNode(char);\n            }\n            node = node.children[index];\n        }\n\n        node.isEndingChar = true;\n    }\n\n    find (text) {\n        let node = this.root;\n\n        for(let char of text) {\n            let index = char.charCodeAt() - 'a'.charCodeAt();\n            if(node.children[index]) {\n                node = node.children[index];\n            } else {\n                return false;\n            }\n        }\n\n        return node.isEndingChar;\n    }\n}\n\nvar tree = new TrieTree();\nvar strs = [\"how\", \"hi\", \"her\", \"hello\", \"so\", \"see\"];\nfor(let str of strs) {\n    tree.insert(str);\n}\n\nfor(let str of strs) {\n    console.log(tree.find(str));\n}\n\nconsole.log(tree.find('world'));"
  },
  {
    "path": "javascript/36_ac_automata/ac_automata.js",
    "content": "\nMAX_LEN = 128;\n\nclass ACNode {\n    constructor(data){\n        this.data = data;\n        this.children = new Array(MAX_LEN);\n        this.isEndingChar = false;\n        this.length = 0;\n        this.fail = null;\n    }\n}\n\nclass ACTree {\n\n    constructor(data){\n        this.root = new ACNode('/')\n    }\n\n    insert (text) {\n        let node = this.root;\n        for (let char of text) {\n            let index = char.charCodeAt() + 1;\n            if(!node.children[index]) {\n                node.children[index] = new ACNode(char);\n            }\n            node = node.children[index];\n        }\n\n        node.isEndingChar = true;\n        node.length = text.length;\n    }\n\n    buildFailurePointer() {\n        let root = this.root;\n        let queue = [];\n        queue.push(root);\n\n        while (queue.length > 0) {\n            let p = queue.shift();\n\n            for (let i = 0; i < MAX_LEN; i++) {\n                let pc = p.children[i];\n                if (!pc) {\n                    continue;\n                } \n\n                if(p == root) {\n                    pc.fail = root;\n                } else {\n                    let q = p.fail;\n                    while (q) {\n                        let qc = q.children[pc.data.charCodeAt() + 1];\n                        if(qc) {\n                            pc.fail = qc;\n                            break;\n                        }\n                        q = q.fail;\n                    }\n                    if(!q) {\n                        pc.fail = root;\n                    }\n                }\n                queue.push(pc);\n            }\n        }\n    }\n\n    match (text) {\n        let root = this.root;\n        let n = text.length;\n        let p = root;\n\n        for(let i = 0; i < n; i++) {\n            let idx = text[i].charCodeAt() + 1;\n            while(!p.children[idx] && p != root){\n                p = p.fail;\n            }\n\n            p = p.children[idx];\n            if(!p) {\n                p = root;\n            }\n\n            let tmp = p;\n            while ( tmp != root) {\n                if (tmp.isEndingChar == true) {\n                    console.log(`Start from ${i - p.length + 1}, length: ${p.length}`);\n                }\n                tmp = tmp.fail;\n            }\n        }\n    }\n}\n\nlet automata = new ACTree();\nlet patterns = [\"at\", \"art\", \"oars\", \"soar\"];\nfor (let pattern of patterns) {\n    automata.insert(pattern);\n}\n\nautomata.buildFailurePointer()\nautomata.match(\"soarsoars\");"
  },
  {
    "path": "javascript/36_ac_automata/ac_automata_unicode.js",
    "content": "\nclass ACNode {\n    constructor(data){\n        this.data = data;\n        this.children = new Map();\n        this.isEndingChar = false;\n        this.length = 0;\n        this.fail = null;\n    }\n}\n\nclass ACTree {\n\n    constructor(data){\n        this.root = new ACNode('/')\n    }\n\n    insert (text) {\n        let node = this.root;\n        for (let char of text) {\n            if(!node.children.get(char)) {\n                node.children.set(char, new ACNode(char));\n            }\n            node = node.children.get(char);\n        }\n\n        node.isEndingChar = true;\n        node.length = text.length;\n    }\n\n    buildFailurePointer() {\n        let root = this.root;\n        let queue = [];\n        queue.push(root);\n\n        while (queue.length > 0) {\n            let p = queue.shift();\n\n            for(var pc of p.children.values()){\n                if (!pc) {\n                    continue;\n                } \n\n                if(p == root) {\n                    pc.fail = root;\n                } else {\n                    let q = p.fail;\n                    while (q) {\n                        let qc = q.children.get(pc.data);\n                        if(qc) {\n                            pc.fail = qc;\n                            break;\n                        }\n                        q = q.fail;\n                    }\n                    if(!q) {\n                        pc.fail = root;\n                    }\n                }\n                queue.push(pc);\n            }\n        }\n    }\n\n    match (text) {\n        let root = this.root;\n        let n = text.length;\n        let p = root;\n\n        for(let i = 0; i < n; i++) {\n            let char = text[i];\n            while(!p.children.get(char) && p != root){\n                p = p.fail;\n            }\n\n            p = p.children.get(char);\n            if(!p) {\n                p = root;\n            }\n\n            let tmp = p;\n            while ( tmp != root) {\n                if (tmp.isEndingChar == true) {\n                    console.log(`Start from ${i - p.length + 1}, length: ${p.length}`);\n                }\n                tmp = tmp.fail;\n            }\n        }\n    }\n}\n\nfunction match( text, patterns) {\n    let automata = new ACTree();\n    for (let pattern of patterns) {\n        automata.insert(pattern);\n    }\n\n    automata.buildFailurePointer();\n    automata.match(text);\n}\n\nlet patterns = [\"at\", \"art\", \"oars\", \"soar\"];\nlet text = \"soarsoars\";\nmatch(text, patterns);\n\nlet patterns2 = [\"Fxtec Pro1\", \"谷歌Pixel\"];\nlet text2 = \"一家总部位于伦敦的公司Fxtex在MWC上就推出了一款名为Fxtec Pro1的手机，该机最大的亮点就是采用了侧滑式全键盘设计。DxOMark年度总榜发布 华为P20 Pro/谷歌Pixel 3争冠\";\nmatch(text2, patterns2);\n\n"
  },
  {
    "path": "javascript/42_dynamic_programming/levenshtein_distance.js",
    "content": "function lsDist(str1, str2) {\n    var n = str1.length;\n    var m = str2.length;\n\n    var memo = new Array(n);\n    for(let i = 0; i < n; i++) {\n        memo[i] = new Array(m);\n        if (str1[i] === str2[0]) {\n            memo[i][0] = i - 0;\n        } else if(i === 0) {\n            memo[i][0] = 1;\n        } else {\n            memo[i][0] = memo[i - 1][0] + 1;\n        }\n    }\n\n    for(let j = 0; j < m; j++) {\n        if(str1[0] === str2[j]) {\n            memo[0][j] = j - 0;\n        } else if(j === 0) {\n            memo[0][j] = 1;\n        } else {\n            memo[0][j] = memo[0][j - 1] + 1;\n        }\n    }\n\n    for(let i = 1; i < n; i++) {\n        for(let j = 1; j < m; j++) {\n            if(str1[i] === str2[j]) {\n                memo[i][j] = Math.min(memo[i - 1][j] + 1, memo[i][j - 1] + 1, memo[i - 1][j - 1 ]);\n            } else {\n                memo[i][j] = Math.min(memo[i - 1][j] + 1, memo[i][j - 1] + 1, memo[i - 1][j - 1 ] + 1);\n            }\n        }\n    }\n    console.log(memo);\n\n    return memo[n - 1][m - 1];\n}\n\n\nvar s = \"mitcmu\";\nvar t = \"mtacnu\";\n\nconsole.log( lsDist(s, t) );\n\nvar s = \"kitten\";\nvar t = \"sitting\";\n\nconsole.log( lsDist(s, t) );\n\nvar s = \"flaw\";\nvar t = \"lawn\";\nconsole.log( lsDist(s, t) );\n"
  },
  {
    "path": "javascript/43_topological_sorting/dsf.js",
    "content": "\nfunction Graph() {\n    var graph = {\n        adj: new Map(),\n        addEdge: function (from, to){\n            if(!this.adj.get(from)) {\n                this.adj.set(from, [ to ]);\n            } else {\n                this.adj.get(from).push(to);\n            }\n        },\n        sortingByDsf: function(){\n            var inverseAdj = new Map();\n            var keys = this.adj.keys();\n            for(let key of keys) {\n                let blk = this.adj.get(key);\n                if(blk) {\n                    for(let v of blk) {\n                        if(!inverseAdj.get(v)) {\n                            inverseAdj.set(v, [key]);\n                        } else {\n                            inverseAdj.get(v).push(key);\n                        }\n                    }\n                }\n            }\n\n            let inKeys = inverseAdj.keys();\n            let vertexes = new Set([...keys, ...inKeys]);\n            let visited = [];\n            for(let vertex of vertexes) {\n                if(!visited.includes(vertex)) {\n                    visited.push(vertex);\n                    this.dsf(vertex, inverseAdj, visited);\n                }\n            }\n        },\n        dsf: function(vertex, inverseAdj, visited) {\n            if(!inverseAdj.get(vertex)) {\n                inverseAdj.set(vertex, []);\n            }\n\n            for(let v of inverseAdj.get(vertex)) {\n                if(visited.includes(v)) {\n                    continue;\n                }\n\n                visited.push(v);\n\n                this.dsf(v, inverseAdj, visited);\n            }\n\n            console.log(\"->\" + vertex);\n        }\n    }\n\n    return graph;\n}\n\nvar dag = new Graph();\ndag.addEdge(2, 1);\ndag.addEdge(3, 2);\ndag.addEdge(2, 4);\ndag.addEdge(4, 1);\ndag.sortingByDsf();\n\n\nvar dag2 = new Graph();\ndag2.addEdge(\"main\", \"parse_options\");\ndag2.addEdge(\"main\", \"tail_file\");\ndag2.addEdge(\"main\", \"tail_forever\");\ndag2.addEdge(\"tail_file\", \"pretty_name\");\ndag2.addEdge(\"tail_file\", \"write_header\");\ndag2.addEdge(\"tail_file\", \"tail\");\ndag2.addEdge(\"tail_forever\", \"recheck\");\ndag2.addEdge(\"tail_forever\", \"pretty_name\");\ndag2.addEdge(\"tail_forever\", \"write_header\");\ndag2.addEdge(\"tail_forever\", \"dump_remainder\");\ndag2.addEdge(\"tail\", \"tail_lines\");\ndag2.addEdge(\"tail\", \"tail_bytes\");\ndag2.addEdge(\"tail_lines\", \"start_lines\");\ndag2.addEdge(\"tail_lines\", \"dump_remainder\");\ndag2.addEdge(\"tail_lines\", \"file_lines\");\ndag2.addEdge(\"tail_lines\", \"pipe_lines\");\ndag2.addEdge(\"tail_bytes\", \"xlseek\");\ndag2.addEdge(\"tail_bytes\", \"start_bytes\");\ndag2.addEdge(\"tail_bytes\", \"dump_remainder\");\ndag2.addEdge(\"tail_bytes\", \"pipe_bytes\");\ndag2.addEdge(\"file_lines\", \"dump_remainder\");\ndag2.addEdge(\"recheck\", \"pretty_name\");\ndag2.sortingByDsf();\n"
  },
  {
    "path": "javascript/45_bitmap/bitmap.js",
    "content": "\nclass BitMap {\n    constructor(n) {\n        this.nbits = n;\n        this.blk = new Array(Math.floor(n / 16) + 1);\n        this.blk.fill(0);\n    }\n\n    get(k) {\n        if( k > this.nbits) return false; \n\n        let byteIndex = Math.floor(k / 16);\n        let bitIndex = k % 16;\n\n        return !((this.blk[byteIndex] & (1 << bitIndex)) === 0);\n    }\n\n    set(k) {\n        if( k > this.nbits) return; \n\n        let byteIndex = Math.floor(k / 16);\n        let bitIndex = k % 16;\n\n        this.blk[byteIndex] = this.blk[byteIndex] | (1 << bitIndex);\n\n    }\n}\n\nlet aBitMap = new BitMap(20);\n\naBitMap.set(1);\naBitMap.set(3);\naBitMap.set(5);\naBitMap.set(7);\naBitMap.set(9);\naBitMap.set(11);\naBitMap.set(13);\naBitMap.set(15);\naBitMap.set(17);\naBitMap.set(19);\n\nfor(let i = 0; i < 21; i++) {\n    console.log(aBitMap.get(i));\n}"
  },
  {
    "path": "kotlin/05_array/ArrayKt.kt",
    "content": "import kotlin.Array\n\n/**\n * 1) 数组的插入、删除、按照下标随机访问操作；\n * 2）数组中的数据是int类型的；\n *\n * Author: Zackratos\n */\n\nclass ArrayKt constructor(private val capacity: Int) {\n    // 定义整型数据data保存数据\n    private val data: IntArray = IntArray(capacity)\n    // 定义数组中实际个数\n    private var count: Int = 0\n\n    companion object {\n        @JvmStatic\n        fun main(args: Array<String>) {\n            val array = ArrayKt(5)\n            array.printAll()\n            array.insert(0, 3)\n            array.insert(0, 4)\n            array.insert(1, 5)\n            array.insert(3, 9)\n            array.insert(3, 10)\n            array.printAll()\n        }\n    }\n\n    // 根据索引，找到数据中的元素并返回\n    fun find(index: Int): Int {\n        if (index !in 0..(count - 1)) return -1\n        return data[index]\n    }\n\n    // 插入元素：头部插入，尾部插入\n    fun insert(index: Int, value: Int): Boolean {\n        // 数组空间已满\n        if (count == capacity) {\n            System.out.println(\"没有可插入的位置\")\n            return false\n        }\n        // 如果count还没满，那么就可以插入数据到数组中\n        // 位置不合法\n        if (index !in 0..count) {\n            System.out.println(\"位置不合法\")\n            return false\n        }\n        // 位置合法\n        (count downTo index + 1).forEach {\n            data[it] = data[it - 1]\n        }\n        data[index] = value\n        ++count\n        return true\n    }\n\n    // 根据索引，删除数组中元素\n    fun delete(index: Int): Boolean {\n        if (index !in 0..(count - 1)) return false\n        (index + 1 until count).forEach {\n            data[it - 1] = data[it]\n        }\n        --count\n        return true\n    }\n\n    fun printAll() {\n        (0 until count).forEach {\n            System.out.println(\"${data[it]} \")\n        }\n    }\n\n}"
  },
  {
    "path": "kotlin/05_array/DynamicArray.kt",
    "content": "/**\n * 动态扩容的数组\n */\nclass DynamicArray {\n    companion object {\n        // 默认容量\n        const val DEFAULT_CAPACITY = 10\n\n        // 最大容量\n        const val MAX_CAPACITY = Int.MAX_VALUE\n    }\n\n    // 当前已使用大小\n    private var usedSize = 0\n\n    // 当前容量大小\n    private var capacity = 0\n\n    // 数组容器\n    private var data: Array<Int>\n\n    init {\n        this.capacity = DEFAULT_CAPACITY\n        this.data = Array(this.capacity) { 0 }\n    }\n\n    /**\n     * 增加元素\n     */\n    fun add(value: Int) {\n        if (this.usedSize == this.capacity - 1) {\n            this.doubleCapacity()\n        }\n        this.data[this.usedSize] = value\n        ++this.usedSize\n    }\n\n    /**\n     * 移除元素\n     */\n    fun remove(value: Int) {\n        if (this.usedSize >= 0) {\n            var target = -1\n\n            // 查找目标所在位置\n            for (i in 0 until this.usedSize) {\n                if (this.data[i] == value) {\n                    target = i\n                    break\n                }\n            }\n\n            // 找到了\n            if (target >= 0) {\n                val size = this.usedSize - 1\n\n                // 把后续元素往前搬\n                for (i in target until size) {\n                    this.data[i] = this.data[i + 1]\n                }\n\n                // 最后一个元素位置置为空\n                this.data[size] = 0\n\n                // 更新已使用大小\n                this.usedSize = size\n            }\n        }\n    }\n\n    /**\n     * 通过索引设置元素的值\n     */\n    fun set(index: Int, value: Int) {\n        if (this.checkIndex(index)) {\n            this.data[index] = value\n            return\n        }\n\n        throw IllegalArgumentException(\"index must be in rang of 0..${this.usedSize}\")\n    }\n\n    /**\n     * 获取元素\n     */\n    fun get(index: Int): Int? {\n        if (this.checkIndex(index)) {\n            return this.data[index]\n        }\n\n        throw IllegalArgumentException(\"index must be in rang of 0..${this.usedSize}\")\n    }\n\n    /**\n     * 获取当前数组的大小\n     */\n    fun getSize(): Int = this.usedSize\n\n    private fun checkIndex(index: Int): Boolean {\n        return index >= 0 && index < this.usedSize\n    }\n\n    /**\n     * 按原容量的两倍进行扩容\n     */\n    private fun doubleCapacity() {\n        if (this.capacity < MAX_CAPACITY) {\n            this.capacity = Math.min(this.capacity * 2, MAX_CAPACITY)\n            val newArray = Array(this.capacity) { 0 }\n\n            for (i in 0 until this.usedSize) {\n                newArray[i] = this.data[i]\n            }\n\n            this.data = newArray\n        }\n    }\n}"
  },
  {
    "path": "kotlin/06_linkedlist/SinglyLinkedList.kt",
    "content": "/**\n * 1）单链表的插入、删除、查找操作；\n * 2）链表中存储的是int类型的数据；\n *\n * Author：Zackratos\n */\n\nclass SinglyLinkedList {\n\n    private var head: Node? = null\n\n    companion object {\n        @JvmStatic\n        fun main(args: Array<String>) {\n\n            val link = SinglyLinkedList()\n            println(\"hello\")\n            val data = intArrayOf(1, 2, 5, 3, 1)\n\n            for (i in data.indices) {\n                //link.insertToHead(data[i]);\n                link.insertTail(data[i])\n            }\n\n            println(\"打印原始:\")\n            link.printAll()\n            if (link.palindrome()) {\n                println(\"回文\")\n            } else {\n                println(\"不是回文\")\n            }\n        }\n    }\n\n    fun findByValue(value: Int): Node? {\n        var p = head\n        while (p != null && p.data != value) {\n            p = p.next\n        }\n\n        return p\n    }\n\n    fun findByIndex(index: Int): Node? {\n        var p = head\n        var pos = 0\n        while (p != null && pos != index) {\n            p = p.next\n            ++pos\n        }\n\n        return p\n    }\n\n    //无头结点\n    //表头部插入\n    //这种操作将于输入的顺序相反，逆序\n    fun insertToHead(value: Int) {\n        val newNode = Node(value, null)\n        insertToHead(newNode)\n    }\n\n    fun insertToHead(newNode: Node) {\n        if (head == null) {\n            head = newNode\n        } else {\n            newNode.next = head\n            head = newNode\n        }\n    }\n\n    //顺序插入\n    //链表尾部插入\n    fun insertTail(value: Int) {\n\n        val newNode = Node(value, null)\n        //空链表，可以插入新节点作为head，也可以不操作\n        if (head == null) {\n            head = newNode\n\n        } else {\n            var q = head\n            while (q?.next != null) {\n                q = q.next\n            }\n            newNode.next = q?.next\n            q?.next = newNode\n        }\n    }\n\n    fun insertAfter(p: Node?, value: Int) {\n        val newNode = Node(value, null)\n        insertAfter(p, newNode)\n    }\n\n    fun insertAfter(p: Node?, newNode: Node) {\n        if (p == null) return\n\n        newNode.next = p.next\n        p.next = newNode\n    }\n\n    fun insertBefore(p: Node?, value: Int) {\n        val newNode = Node(value, null)\n        insertBefore(p, newNode)\n    }\n\n    fun insertBefore(p: Node?, newNode: Node) {\n        if (p == null) return\n        if (head === p) {\n            insertToHead(newNode)\n            return\n        }\n\n        var q = head\n        while (q != null && q.next !== p) {\n            q = q.next\n        }\n\n        if (q == null) {\n            return\n        }\n\n        newNode.next = p\n        q.next = newNode\n\n    }\n\n    fun deleteByNode(p: Node?) {\n        if (p == null || head == null) return\n\n        if (p === head) {\n            head = head?.next\n            return\n        }\n\n        var q = head\n        while (q != null && q.next !== p) {\n            q = q.next\n        }\n\n        if (q == null) {\n            return\n        }\n\n        q.next = q.next?.next\n    }\n\n    fun deleteByValue(value: Int) {\n        if (head == null) return\n\n        var p = head\n        var q: Node? = null\n        while (p != null && p.data != value) {\n            q = p\n            p = p.next\n        }\n\n        if (p == null) return\n\n        if (q == null) {\n            head = head?.next\n        } else {\n            q.next = q.next?.next\n        }\n    }\n\n    fun printAll() {\n        var p = head\n        while (p != null) {\n            print(\"${p.data} \")\n            p = p.next\n        }\n        println()\n    }\n\n    //判断true or false\n    fun TFResult(left: Node?, right: Node?): Boolean {\n        var l: Node? = left\n        var r: Node? = right\n\n        println(\"left_:${l?.data}\")\n        println(\"right_:${r?.data}\")\n        while (l != null && r != null) {\n            if (l.data == r.data) {\n                l = l.next\n                r = r.next\n                continue\n            } else {\n                break\n            }\n\n        }\n\n        println(\"什么结果\")\n        return l == null && r == null\n    }\n\n    //　判断是否为回文\n    fun palindrome(): Boolean {\n        if (head == null) {\n            return false\n        } else {\n            println(\"开始执行找到中间节点\")\n            var p = head\n            var q = head\n            if (p?.next == null) {\n                println(\"只有一个元素\")\n                return true\n            }\n            while (q?.next != null && q.next?.next != null) {\n                p = p?.next\n                q = q.next?.next\n            }\n\n            println(\"中间节点${p?.data}\")\n            println(\"开始执行奇数节点的回文判断\")\n            val leftLink: Node?\n            val rightLink: Node?\n            if (q?.next == null) {\n                //　p 一定为整个链表的中点，且节点数目为奇数\n                rightLink = p?.next\n                leftLink = inverseLinkList(p)?.next\n                println(\"左边第一个节点${leftLink?.data}\")\n                println(\"右边第一个节点${rightLink?.data}\")\n\n            } else {\n                //p q　均为中点\n                rightLink = p?.next\n                leftLink = inverseLinkList(p)\n            }\n            return TFResult(leftLink, rightLink)\n\n        }\n    }\n\n    //带结点的链表翻转\n    fun inverseLinkList_head(p: Node): Node {\n        //　Head　为新建的一个头结点\n        val Head = Node(9999, null)\n        // p　为原来整个链表的头结点,现在Head指向　整个链表\n        Head.next = p\n        /*\n        带头结点的链表翻转等价于\n        从第二个元素开始重新头插法建立链表\n        */\n        var Cur = p.next\n        p.next = null\n        var next: Node?\n\n        while (Cur != null) {\n            next = Cur.next\n            Cur.next = Head.next\n            Head.next = Cur\n            println(\"first \" + Head.data)\n\n            Cur = next\n        }\n\n        //　返回左半部分的中点之前的那个节点\n        //　从此处开始同步像两边比较\n        return Head\n\n    }\n\n    //无头结点的链表翻转\n    fun inverseLinkList(p: Node?): Node? {\n\n        var pre: Node? = null\n        var r = head\n        println(\"z---${r?.data}\")\n        var next: Node?\n        while (r !== p) {\n            next = r?.next\n\n            r?.next = pre\n            pre = r\n            r = next\n        }\n\n        r?.next = pre\n        //　返回左半部分的中点之前的那个节点\n        //　从此处开始同步像两边比较\n        return r\n\n    }\n\n    fun createNode(value: Int): Node = Node(value, null)\n\n\n\n    class Node(var data: Int, var next: Node?)\n}"
  },
  {
    "path": "kotlin/07_linkedlist/LinkedListAlgo.kt",
    "content": "/**\n * 1) 单链表反转\n * 2) 链表中环的检测\n * 3) 两个有序的链表合并\n * 4) 删除链表倒数第n个结点\n * 5) 求链表的中间结点\n *\n * Author: Zackratos\n */\n\nobject LinkedListAlgo {\n\n    // 单链表反转\n    fun reverse(list: Node): Node? {\n        var curr: Node? = list\n        var pre: Node? = null\n        while (curr != null) {\n            val next = curr.next\n            curr.next = pre\n            pre = curr\n            curr = next\n        }\n        return pre\n    }\n\n    // 检测环\n    fun checkCircle(list: Node): Boolean {\n        var fast = list.next\n        var slow: Node? = list\n        while (fast != null && slow != null) {\n            fast = fast.next?.next\n            slow = slow.next\n            if (fast === slow)\n                return true\n        }\n\n        return false\n    }\n\n    // 有序链表合并\n    fun mergeSortedLists(la: Node, lb: Node): Node? {\n        var p: Node? = la\n        var q: Node? = lb\n        val head: Node?\n        if (p?.data ?: 0 < q?.data ?: 0) {\n            head = p\n            p = p?.next\n        } else {\n            head = q\n            q = q?.next\n        }\n        var r = head\n        while (p != null && q != null) {\n            if (p.data < q.data) {\n                r?.next = p\n                p = p.next\n            } else {\n                r?.next = q\n                q = q.next\n            }\n            r = r?.next\n        }\n        if (p != null) {\n            r?.next = p\n        } else {\n            r?.next = q\n        }\n        return head\n    }\n\n    // 删除倒数第 k 个结点\n    fun deleteLastKth(list: Node, k: Int): Node? {\n        var fast: Node? = list\n        var i = 1\n        while (fast != null && i < k) {\n            fast = fast.next\n            i++\n        }\n        if (fast == null) {\n            return when (i) {\n                k -> list.next\n                else -> list\n            }\n        }\n        var slow: Node? = list\n        var pre: Node? = null\n        while (fast != null) {\n            fast = fast.next\n            pre = slow\n            slow = slow?.next\n        }\n        pre?.next = pre?.next?.next\n        return list\n    }\n\n    // 求中间结点\n    fun findMiddleNode(list: Node): Node? {\n        var fast: Node? = list\n        var slow: Node? = list\n        while (fast?.next != null && fast.next?.next != null) {\n            fast = fast.next?.next\n            slow = slow?.next\n        }\n        return slow\n    }\n\n    class Node(var data: Int, var next: Node?)\n}"
  },
  {
    "path": "kotlin/08_stack/StackBasedOnLinkedList.kt",
    "content": "/**\n * 基于链表实现的栈。\n *\n * Author: Zackratos\n */\n\nclass StackBasedOnLinkedList {\n    \n    private var top: Node? = null\n\n    fun push(value: Int) {\n        val newNode = Node(value, null)\n        // 不管 top 是不是 null，都可以这么写\n        newNode.next = top\n        top = newNode\n    }\n\n    fun pop(): Int {\n        if (top == null) return -1\n        val node = top\n        top = top!!.next\n        return node!!.data\n    }\n\n    fun printAll() {\n        var p = top\n        while (p != null) {\n            print(\"${p.data} \")\n            p = p.next\n        }\n        println()\n    }\n\n    class Node(var data: Int, var next: Node?)\n\n}"
  },
  {
    "path": "notes/.gitkeep",
    "content": ""
  },
  {
    "path": "notes/10_recursion/readme.md",
    "content": "# 递归\n\n## 三个条件\n\n* 可分解为子问题\n* 子问题与原问题解法一致，只有规模上的不同\n* 有终止条件\n\n## 写递归代码\n\n* 整理出递推公式\n* 确定好终止条件\n* 「翻译」成代码\n\n关键：\n\n> 只要遇到递归，我们就把它抽象成一个递推公式，不用想一层层的调用关系，不要试图用人脑去分解每一个步骤。\n\n## 警惕\n\n* 堆栈溢出 <- 递归深度过大\n* 重复计算 <- 递归过程中的不同分支，重复计算相同子问题\n  * 保存子问题结果（map/dict）\n* 空间复杂度高 <- 递归函数调用带来的消耗\n\n## 递归改写非递归\n\n本质：人肉模拟函数调用堆栈。\n"
  },
  {
    "path": "notes/11_sorts/readme.md",
    "content": "# 排序（平方时间复杂度排序算法）\n\n| 排序算法 | 时间复杂度 | 是否基于比较 |\n|---------|----|----|\n| 冒泡、插入、选择 | $O(n^2)$ | [y] |\n| 快排、归并 | $O(n\\log n)$ | [y] |\n| 桶、基数、计数 | $O(n) | [x] |\n\n开篇问题：插入排序和冒泡排序的时间复杂度相同，都是 $O(n^2)$，在实际软件开发中，为什么我们更倾向于使用插入排序而不是冒泡排序？\n\n## 如何分析「排序算法」？\n\n### 算法执行效率\n\n1. 最好、最坏、平均情况的时间复杂度\n2. 时间复杂度的系数、低阶、常数——在渐进复杂度相同的情况下，需要比较系数、低阶和常数\n3. 比较和交换（移动）的次数——基于比较的排序算法的两种基本操作\n\n### 算法的内存消耗\n\n是否为原地排序算法（In-place sort algorithm），即算法的空间复杂度是否为 $O(1)$。\n\n### 排序的稳定性\n\n经过排序算法处理后，值相同的元素，在原序列和排序后序列中的相对位置保持不变，则称该排序算法是稳定的。\n\n> 待排序的 `item` 并不是简单的值，而是一个基于对象中的某个 `key` 进行排序时，排序的稳定性就有意义了。\n\n## 冒泡排序\n\n* 每次循环都从序列起始位置开始\n* 循环中的每个动作，都对比相邻两个元素的大小是否满足偏序要求，若不满足，则交换顺序\n\n![冒泡排序例图](https://static001.geekbang.org/resource/image/88/34/8890cbf63ea80455ce82490a23361134.jpg)\n\n分析：\n\n* 原地排序\n* 稳定排序（偏序关系是严格的偏序关系，如 `<` 或 `>`）\n* 时间复杂度\n  * 最好 $O(n)$\n  * 最坏 $O(n^2)$\n  * 平均 $O(n^2)$\n\n### 冒泡排序的平均时间复杂度非严格分析\n\n* 有序度：序列中满足偏序关系的两两组合的元素对的个数\n* 满有序度：排序完成的序列的有序度，它等于 $n(n - 1) / 2$\n* 逆序度：序列中不满足偏序关系的亮亮组合的元素对的个数\n\n显然，$\\text{逆序度} = \\text{满有序度} - \\text{有序度}$。\n\n在冒泡排序中，每产生一次「交换」操作，$\\text{逆序度}--$。于是，平均情况下，需要 $n(n - 1)/4$ 次交换操作，它已经是 $O(n^2)$ 了。因此，尽管比较操作的数量会大于交换操作的数量，但我们依然能说，冒泡排序的平均时间复杂度是 $O(n^2)$。\n\n> 分析过程不严格，但足够说明问题。\n\n## 插入排序\n\n1. 将待排序数列分为已排序区间和未排序区间\n2. 取未排序区间的第一个元素\n3. 遍历已排序区间，按照偏序关系，寻找合适的位置，插入未排序区间的第一个元素\n4. 重复 2 -- 3 直至未排序区间长度为零\n\n![插入排序例图](https://static001.geekbang.org/resource/image/fd/01/fd6582d5e5927173ee35d7cc74d9c401.jpg)\n\n分析：\n\n* 原地排序\n* 稳定排序（值相同的元素，往后插）\n* 时间复杂度\n  * 最好 $O(n)$\n  * 最坏 $O(n^2)$\n  * 平均 $O(n^2)$（乘法法则）\n\n## 选择排序\n\n1. 将待排序数列分为已排序区间和未排序区间\n2. 遍历未排序区间，取未排序区间的最小元素\n3. 交换上述最小元素与未排序区间中的第一个元素的位置\n4. 重复 2 -- 3 直至未排序区间长度为零\n\n![选择排序例图](https://static001.geekbang.org/resource/image/32/1d/32371475a0b08f0db9861d102474181d.jpg)\n\n分析：\n\n* 非原地排序\n* 非稳定排序\n* 时间复杂度\n  * 最好 $O(n^2)$\n  * 最坏 $O(n^2)$\n  * 平均 $O(n^2)$（乘法法则）\n\n## 开篇问题\n\n* 对同一份未排序序列数据，冒泡排序和插入排序所需的交换（移动）次数是一定的，且是相等的\n* 单次数据交换，冒泡排序所需的时间更长（三次赋值操作，插排只需要一次）\n\n另有插入排序的优化版本[希尔排序](https://zh.wikipedia.org/wiki/%E5%B8%8C%E5%B0%94%E6%8E%92%E5%BA%8F)。\n\n![小结](https://static001.geekbang.org/resource/image/34/50/348604caaf0a1b1d7fee0512822f0e50.jpg)\n"
  },
  {
    "path": "notes/12_sorts/.gitkeep",
    "content": ""
  },
  {
    "path": "notes/12_sorts/readme.md",
    "content": "# 排序（线性对数时间复杂度排序算法）\r\n\r\n开篇问题：如何在 $O(n)$ 时间复杂度内寻找一个无序数组中第 K 大的元素？\r\n\r\n## 归并排序\r\n\r\n* 归并排序使用了「分治」思想（Divide and Conquer）\r\n    * 分：把数组分成前后两部分，分别排序\r\n    * 合：将有序的两部分合并\r\n\r\n![归并排序分解图](https://static001.geekbang.org/resource/image/db/2b/db7f892d3355ef74da9cd64aa926dc2b.jpg)\r\n\r\n* 分治与递归\r\n    * 分治：解决问题的处理办法\r\n    * 递归：实现算法的手段\r\n    * ——分治算法经常用递归来实现\r\n* 递归实现：\r\n    * 终止条件：区间 `[first, last)` 内不足 2 个元素\r\n    * 递归公式：`merge_sort(first, last) = merge(merge_sort(first, mid), merge_sort(mid, last))`，其中 `mid = first + (last - first) / 2`\r\n\r\nC++ 实现：\r\n\r\n```cpp\r\ntemplate <typename FrwdIt,\r\n          typename T = typename std::iterator_traits<FrwdIt>::value_type,\r\n          typename BinaryPred = std::less<T>>\r\nvoid merge_sort(FrwdIt first, FrwdIt last, BinaryPred comp = BinaryPred()) {\r\n    const auto len = std::distance(first, last);\r\n    if (len <= 1) { return; }\r\n    auto cut = first + len / 2;\r\n    merge_sort(first, cut, comp);\r\n    merge_sort(cut, last, comp);\r\n    std::vector<T> tmp;\r\n    tmp.reserve(len);\r\n    detail::merge(first, cut, cut, last, std::back_inserter(tmp), comp);\r\n    std::copy(tmp.begin(), tmp.end(), first);\r\n}\r\n```\r\n\r\n这里涉及到一个 `merge` 的过程，它的实现大致是：\r\n\r\n```cpp\r\nnamespace detail {\r\ntemplate <typename InputIt1, typename InputIt2, typename OutputIt,\r\n          typename BinaryPred = std::less<typename std::iterator_traits<InputIt1>::value_type>>\r\nOutputIt merge(InputIt1 first1, InputIt1 last1,\r\n               InputIt2 first2, InputIt2 last2,\r\n               OutputIt d_first,\r\n               BinaryPred comp = BinaryPred()) {\r\n    for (; first1 != last1; ++d_first) {\r\n        if (first2 == last2) {\r\n            return std::copy(first1, last1, d_first);\r\n        }\r\n        if (comp(*first2, *first1)) {\r\n            *d_first = *first2;\r\n            ++first2;\r\n        } else {\r\n            *d_first = *first1;\r\n            ++first1;\r\n        }\r\n    }\r\n    return std::copy(first2, last2, d_first);\r\n}\r\n}  // namespace detail\r\n```\r\n\r\n![`merge` 的过程](https://static001.geekbang.org/resource/image/95/2f/95897ade4f7ad5d10af057b1d144a22f.jpg)\r\n\r\n### 算法分析\r\n\r\n* 稳定性\r\n    * 由于 `comp` 是严格偏序，所以 `!comp(*first2, *first1)` 时，取用 `first1` 的元素放入 `d_first` 保证了算法稳定性\r\n* 时间复杂度\r\n    * 定义 $T(n)$ 表示问题规模为 $n$ 时算法的耗时，\r\n    * 有递推公式：$T(n) = 2T(n/2) + n$\r\n    * 展开得 $T(n) = 2^{k}T(1) + k * n$\r\n    * 考虑 $k$ 是递归深度，它的值是 $\\log_2 n$，因此 $T(n) = n + n\\log_2 n$\r\n    * 因此，归并排序的时间复杂度为 $\\Theta(n\\log n)$\r\n* 空间复杂度\r\n    * 一般来说，空间复杂度是 $\\Theta(n)$\r\n\r\n## 快速排序（quick sort，快排）\r\n\r\n原理：\r\n\r\n* 在待排序区间 `[first, last)` 中选取一个元素，称为主元（pivot，枢轴）\r\n* 对待排序区间进行划分，使得 `[first, cut)` 中的元素满足 `comp(element, pivot)` 而 `[cut, last)` 中的元素不满足 `comp(element, pivot)`\r\n* 对划分的两个区间，继续划分，直到区间 `[first, last)` 内不足 2 个元素\r\n\r\n![快排分区示例](https://static001.geekbang.org/resource/image/4d/81/4d892c3a2e08a17f16097d07ea088a81.jpg)\r\n\r\n显然，这又是一个递归：\r\n\r\n* 终止条件：区间 `[first, last)` 内不足 2 个元素\r\n* 递归公式：`quick_sort(first, last) = quick_sort(first, cut) + quick_sort(cut, last)`\r\n\r\n```cpp\r\ntemplate <typename IterT, typename T = typename std::iterator_traits<IterT>::value_type>\r\nvoid quick_sort(IterT first, IterT last) {\r\n    if (std::distance(first, last) > 1) {\r\n        IterT prev_last = std::prev(last);\r\n        IterT cut = std::partition(first, prev_last, [prev_last](T v) { return v < *prev_last; });\r\n        std::iter_swap(cut, prev_last);\r\n        quick_sort(first, cut);\r\n        quick_sort(cut, last);\r\n    }\r\n}\r\n```\r\n\r\n> 一点优化（Liam Huang）：通过将 `if` 改为 `while` 同时修改 `last` 迭代器的值，可以节省一半递归调用的开销。\r\n\r\n```cpp\r\ntemplate <typename IterT, typename T = typename std::iterator_traits<IterT>::value_type>\r\nvoid quick_sort(IterT first, IterT last) {\r\n    while (std::distance(first, last) > 1) {\r\n        IterT prev_last = std::prev(last);\r\n        IterT cut = std::partition(first, prev_last, [prev_last](T v) { return v < *prev_last; });\r\n        std::iter_swap(cut, prev_last);\r\n        quick_sort(cut, last);\r\n        last = cut;\r\n    }\r\n}\r\n```\r\n\r\n如果不要求空间复杂度，分区函数实现起来很容易。\r\n\r\n![非原地分区](https://static001.geekbang.org/resource/image/66/dc/6643bc3cef766f5b3e4526c332c60adc.jpg)\r\n\r\n若要求原地分区，则不那么容易了。下面的实现实现了原地分区函数，并且能将所有相等的主元排在一起。\r\n\r\n```cpp\r\ntemplate <typename BidirIt,\r\n          typename T = typename std::iterator_traits<BidirIt>::value_type,\r\n          typename Compare = std::less<T>>\r\nstd::pair<BidirIt, BidirIt> inplace_partition(BidirIt first,\r\n                                              BidirIt last,\r\n                                             const T& pivot,\r\n                                           Compare comp = Compare()) {\r\n    BidirIt last_less, last_greater, first_equal, last_equal;\r\n    for (last_less = first, last_greater = first, first_equal = last;\r\n                                         last_greater != first_equal; ) {\r\n        if (comp(*last_greater, pivot)) {\r\n            std::iter_swap(last_greater++, last_less++);\r\n        } else if (comp(pivot, *last_greater)) {\r\n            ++last_greater;\r\n        } else {  // pivot == *last_greater\r\n            std::iter_swap(last_greater, --first_equal);\r\n        }\r\n    }\r\n    const auto cnt = std::distance(first_equal, last);\r\n    std::swap_ranges(first_equal, last, last_less);\r\n    first_equal    = last_less;\r\n    last_equal     = first_equal + cnt;\r\n    return {first_equal, last_equal};\r\n}\r\n```\r\n\r\n### 算法分析\r\n\r\n* 稳定性\r\n    * 由于 `inplace_partition` 使用了大量 `std::iter_swap` 操作，所以不是稳定排序\r\n* 时间复杂度\r\n    * 定义 $T(n)$ 表示问题规模为 $n$ 时算法的耗时，\r\n    * 有递推公式：$T(n) = 2T(n/2) + n$（假定每次分割都是均衡分割）\r\n    * 展开得 $T(n) = 2^{k}T(1) + k * n$\r\n    * 考虑 $k$ 是递归深度，它的值是 $\\log_2 n$，因此 $T(n) = n + n\\log_2 n$\r\n    * 因此，快速排序的时间复杂度为 $\\Theta(n\\log n)$\r\n* 空间复杂度\r\n    * 一般来说，空间复杂度是 $\\Theta(1)$，因此是原地排序算法\r\n\r\n## 开篇问题\r\n\r\n* 分区，看前半段元素数量\r\n    * 前半段元素数量 < K，对后半段进行分区\r\n    * 前半段元素数量 > K，对前半段进行分区\r\n    * 前半段元素数量 = K，前半段末位元素即是所求\r\n"
  },
  {
    "path": "notes/13_sorts/.gitkeep",
    "content": ""
  },
  {
    "path": "notes/13_sorts/readme.md",
    "content": "# 线性排序\r\n\r\n## 开篇问题\r\n\r\n如何按年龄给 100 万用户排序？\r\n\r\n## 桶排序（Bucket Sort）\r\n\r\n算法思想：\r\n\r\n* 按待排序数据的 key 分有序桶\r\n* 桶内排序\r\n* 有序桶依次输出\r\n\r\n![桶排序示例](https://static001.geekbang.org/resource/image/98/ae/987564607b864255f81686829503abae.jpg)\r\n\r\n### 算法分析\r\n\r\n* 时间复杂度 $O(n)$\r\n    * $n$ 个元素，分 $m$ 个有序桶，每个桶里平均 $k = n / m$ 个元素\r\n    * 桶内快排，复杂度 $O(k \\log k)$，$m$ 个桶一共 $O(n \\log k)$\r\n    * 当 $m$ 接近 $n$，例如当 $k = 4$ 时，这个复杂度近似 $O(n)$\r\n* 使用条件\r\n    * 数据易于分如有序桶\r\n    * 数据在各个有序桶之间分布均匀\r\n    * 适合外部排序——数据不全部载入磁盘\r\n\r\n## 计数排序（Counting Sort）\r\n\r\n计数排序可以视作是桶排序的一个特殊情况：\r\n\r\n* 数据的取值范围很小\r\n* 每个分桶内的元素 key 值都一样\r\n\r\n此时，由于分桶内的元素 key 值都一样，所以桶内的排序操作可以省略，以及桶的编号本身就能记录桶内元素的值。因此，算法只需遍历一遍所有的数据，统计每个取值上有多少元素即可。这个过程时间复杂度是 $O(n)$。\r\n\r\n* 假设待排序的数组 `A = {2, 5, 3, 0, 2, 3, 0, 3}`，我们有计数数组 `C = {2, 0, 2, 3, 0, 1}`\r\n\r\n接下来，我们要对 `C` 进行计数操作，具体来说，对从下标为 1 的元素开始累加 `C[i] += C[i - 1]`。\r\n\r\n* 计数累加 `C = {2, 2, 4, 7, 7, 8}`\r\n\r\n此时，`C` 中的元素表示「小于等于下标的元素的个数」。接下来，我们从尾至头扫描待排序数组 `A`，将其中元素依次拷贝到输出数组 `R` 的相应位置。我们注意到，`A[7] = 3` 而 `C[3] == 4` 。这意味着，待排序的数组中，包括 3 本身在内，不超过 3 的元素共有 4 个。因此，我们可以将这个 3 放置在 `R[C[3] - 1]` 的位置，而后将 `C[3]` 的计数减一——这是由于待排序数组中未处理的部分，不超过 3 的元素现在只剩下 3 个了。如此遍历整个待排序数组 `A`，即可得到排序后的结果 `R`。\r\n\r\n![计数排序示例](https://static001.geekbang.org/resource/image/1d/84/1d730cb17249f8e92ef5cab53ae65784.jpg)\r\n\r\n### 算法分析\r\n\r\n* 时间复杂度\r\n    * $n$ 个元素，最大值是 $k$，分 $k$ 个「桶」；时间复杂度 $O(n)$\r\n    * 桶内计数累加；时间复杂度 $O(k)$\r\n    * 摆放元素；时间复杂度 $O(n)$\r\n    * 当 $k < n$ 时，总体时间复杂度是 $O(n)$\r\n* 使用条件\r\n    * $k < n$\r\n    * 待排序数据的 key 是非负整数\r\n\r\n## 基数排序（Radix Sort）\r\n\r\n基数排序适用于等长数据的排序。对于不等长数据，可以在较短的数据后面做 padding，使得数据等长。\r\n\r\n* 先就 least significant digit 进行稳定排序——通常可以用桶排序或者计数排序；时间复杂度 $O(n)$\r\n* 而后依次向 greatest significant digit 移动，进行稳定排序\r\n\r\n![基数排序示例](https://static001.geekbang.org/resource/image/df/0c/df0cdbb73bd19a2d69a52c54d8b9fc0c.jpg)\r\n\r\n### 算法分析\r\n\r\n* 时间复杂度\r\n    * 对每一位的排序时间复杂度是 $O(n)$\r\n    * 总共 $k$ 位，因此总的时间复杂度是 $O(kn)$；考虑到 $k$ 是常数，因此总的时间复杂度是 $O(n)$\r\n* 使用条件\r\n    * 等长数据\r\n\r\n## 解答开篇\r\n\r\n桶排序。\r\n"
  },
  {
    "path": "notes/14_sorts/.gitkeep",
    "content": ""
  },
  {
    "path": "notes/14_sorts/readme.md",
    "content": "# 排序优化\r\n\r\n## 如何取舍排序算法？\r\n\r\n* 排序规模小 —— $O(n^2)$ 的算法（通常是插排）\r\n* 排序规模大 —— $O(n\\log n)$ 的算法（通常不用归并排序）\r\n\r\n## 如何优化快速排序？\r\n\r\n参考：[谈谈内省式排序算法](https://liam.page/2018/08/29/introspective-sort/)\r\n"
  },
  {
    "path": "notes/15_bsearch/.gitkeep",
    "content": ""
  },
  {
    "path": "notes/15_bsearch/readme.md",
    "content": "# 二分查找（上）\r\n\r\n## 算法描述\r\n\r\n二分查找（Binary Search）也叫折半查找，是针对有序数据集合的查找算法。其描述十分简单：\r\n\r\n* 折半取中，判断元素与目标元素的大小关系\r\n    * 小于——往前继续折半\r\n    * 大于——往后继续折半\r\n    * 等于——返回\r\n\r\n关于它的复杂度分析，参见[谈谈基于比较的排序算法的复杂度下界](https://liam.page/2018/08/28/lower-bound-of-comparation-based-sort-algorithm/)中的相关信息。它的复杂度是 $O(\\log n)$。\r\n\r\n## $O(\\log n)$ 的惊人之处\r\n\r\n在 42 亿个数据中用二分查找一个数据，最多需要比较 32 次。\r\n\r\n## 适用场景\r\n\r\n* 依赖顺序表结构\r\n* 数据本身必须有序\r\n* 数据量相对比较元素的开销要足够大——不然遍历即可\r\n* 数据量相对内存空间不能太大——不然顺序表装不下\r\n"
  },
  {
    "path": "notes/16_bsearch/.gitkeep",
    "content": ""
  },
  {
    "path": "notes/16_bsearch/readme.md",
    "content": "# 二分查找（下）\r\n\r\n本节课讨论二分的各种变体。实际上在针对上一节的代码中，已经实现了两个变体。本次实现四个变体：\r\n\r\n* 第一个等于给定值的元素\r\n* 最后一个等于给定值的元素\r\n* 第一个不小于给定值的元素\r\n* 最后一个不大于给定值的元素\r\n\r\n```cpp\r\n/**\r\n * Created by Liam Huang (Liam0205) on 2018/10/26.\r\n */\r\n\r\n#ifndef BSEARCH_BSEARCH_VARIENTS_HPP_\r\n#define BSEARCH_BSEARCH_VARIENTS_HPP_\r\n\r\n#include <iterator>\r\n#include <functional>\r\n\r\nenum class BsearchPolicy { UNSPECIFIED, FIRST, LAST, FIRST_NOT_LESS, LAST_NOT_GREATER };\r\n\r\n// Liam Huang: The algorithm works right with iterators that meet the ForwardIterator requirement,\r\n//             but with a bad time complexity. For better performance, iterators should meet\r\n//             the RandomAccessIterator requirement.\r\ntemplate <typename IterT,\r\n          typename ValueT = typename std::iterator_traits<IterT>::value_type,\r\n          typename Compare>\r\nIterT bsearch(IterT first,\r\n              IterT last,\r\n             ValueT target,\r\n            Compare comp,\r\n      BsearchPolicy policy = BsearchPolicy::UNSPECIFIED) {\r\n    IterT result = last;\r\n    while (std::distance(first, last) > 0) {\r\n        IterT mid = first + std::distance(first, last) / 2;\r\n        if (policy == BsearchPolicy::FIRST_NOT_LESS) {\r\n            if (!comp(*mid, target)) {\r\n                if (mid == first or comp(*(mid - 1), target)) {\r\n                    result = mid;\r\n                    break;\r\n                } else {\r\n                    last = mid;\r\n                }\r\n            } else {\r\n                first = mid + 1;\r\n            }\r\n        } else if (policy == BsearchPolicy::LAST_NOT_GREATER) {\r\n            if (comp(target, *mid)) {\r\n                last = mid;\r\n            } else {\r\n                if (std::distance(mid, last) == 1 or comp(target, *(mid + 1))) {\r\n                    result = mid;\r\n                    break;\r\n                } else {\r\n                    first = mid + 1;\r\n                }\r\n            }\r\n        } else {  // policy == UNSPECIFIED or FIRST or LAST\r\n            if (comp(*mid, target)) {\r\n                first = mid + 1;\r\n            } else if (comp(target, *mid)) {\r\n                last = mid;\r\n            } else {  // equal\r\n                if (policy == BsearchPolicy::FIRST) {\r\n                    if (mid == first or comp(*(mid - 1), *mid)) {\r\n                        result = mid;\r\n                        break;\r\n                    } else {\r\n                        last = mid;\r\n                    }\r\n                } else if (policy == BsearchPolicy::LAST) {\r\n                    if (std::distance(mid, last) == 1 or comp(*mid, *(mid + 1))) {\r\n                        result = mid;\r\n                        break;\r\n                    } else {\r\n                        first = mid + 1;\r\n                    }\r\n                } else {\r\n                    result = mid;\r\n                    break;\r\n                }\r\n            }\r\n        }\r\n    }\r\n    return result;\r\n}\r\n\r\ntemplate <typename IterT,\r\n          typename ValueT = typename std::iterator_traits<IterT>::value_type,\r\n          typename Compare = std::less<ValueT>>\r\nIterT bsearch(IterT first,\r\n              IterT last,\r\n             ValueT target,\r\n      BsearchPolicy policy = BsearchPolicy::UNSPECIFIED) {\r\n        return bsearch(first, last, target, Compare(), policy);\r\n}\r\n\r\n#endif  // BSEARCH_BSEARCH_VARIENTS_HPP_\r\n```\r\n"
  },
  {
    "path": "notes/17_skiplist/.gitkeep",
    "content": ""
  },
  {
    "path": "notes/17_skiplist/readme.md",
    "content": "# 跳表（Skip List）\r\n\r\n支持快速地：\r\n\r\n* 插入\r\n* 删除\r\n* 查找\r\n\r\n某些情况下，跳表甚至可以替代红黑树（Red-Black tree）。Redis 当中的有序集合（Sorted Set）是用跳表实现的。\r\n\r\n## 跳表的结构\r\n\r\n跳表是对链表的改进。对于单链表来说，即使内容是有序的，查找具体某个元素的时间复杂度也要达到 $O(n)$。对于二分查找来说，由于链表不支持随机访问，根据 `first` 和 `last` 确定 `cut` 时，必须沿着链表依次迭代 `std::distance(first, last) / 2` 步；特别地，计算 `std::(first, last)` 本身，就必须沿着链表迭代才行。此时，二分查找的效率甚至退化到了 $O(n \\log n)$，甚至还不如顺序遍历。\r\n\r\n![单链表查找示例](https://static001.geekbang.org/resource/image/e1/6d/e18303fcedc068e5a168de04df956f6d.jpg)\r\n\r\n跳表的核心思想是用空间换时间，构建足够多级数的索引，来缩短查找具体值的时间开销。\r\n\r\n![具有二级索引的跳表示例](https://static001.geekbang.org/resource/image/49/65/492206afe5e2fef9f683c7cff83afa65.jpg)\r\n\r\n例如对于一个具有 64 个有序元素的五级跳表，查找起来的过程大约如下图所示。\r\n\r\n![五级跳表示例](https://static001.geekbang.org/resource/image/46/a9/46d283cd82c987153b3fe0c76dfba8a9.jpg)\r\n\r\n## 复杂度分析\r\n\r\n对于一个每一级索引的跨度是下一级索引 $k$ 倍的跳表，每一次 `down` 操作，相当于将搜索范围缩小到「剩余的可能性的 $1 / k$」。因此，查找具体某个元素的时间复杂度大约需要 $\\lfloor \\log_k n\\rfloor + 1$ 次操作；也就是说时间复杂度是 $O(\\log n)$。\r\n\r\n![跳表查询过程示例](https://static001.geekbang.org/resource/image/d0/0c/d03bef9a64a0368e6a0d23ace8bd450c.jpg)\r\n\r\n前面说了，跳表是一种用空间换时间的数据结构。因此它的空间复杂度一定不小。我们考虑原链表有 $n$ 个元素，那么第一级索引就有 $n / k$ 个元素，剩余的索引依次有 $n / k^2$, $n / k^3$, ..., $1$ 个元素。总共的元素个数是一个等比数列求和问题，它的值是 $\\frac{n - 1}{k - 1}$。可见，不论 $k$ 是多少，跳表的空间复杂度都是 $O(n)$；但随着 $k$ 的增加，实际需要的额外节点数会下降。\r\n\r\n## 高效地插入和删除\r\n\r\n对于链表来说，插入或删除一个给定结点的时间复杂度是 $O(1)$。因此，对于跳表来说，插入或删除某个结点，其时间复杂度完全依赖于查找这类结点的耗时。而我们知道，在跳表中查找某个元素的时间复杂度是 $O(\\log n)$。因此，在跳表中插入或删除某个结点的时间复杂度是 $O(\\log n)$。\r\n\r\n![在跳表中插入一个元素](https://static001.geekbang.org/resource/image/65/6c/65379f0651bc3a7cfd13ab8694c4d26c.jpg)\r\n\r\n## 跳表索引的动态更新\r\n\r\n为了维护跳表的结构，在不断插入数据的过程中，有必要动态维护跳表的索引结构。一般来说，可以采用随机层级法。具体来说是引入一个输出整数的随机函数。当随机函数输出 $K$，则更新从第 $1$ 级至第 $K$ 级的索引。为了保证索引结构和数据规模大小的匹配，一般采用二项分布的随机函数。\r\n\r\n![在跳表中插入一个元素并更新索引](https://static001.geekbang.org/resource/image/a8/a7/a861445d0b53fc842f38919365b004a7.jpg)\r\n"
  },
  {
    "path": "notes/18_hashtable/.gitkeep",
    "content": ""
  },
  {
    "path": "notes/18_hashtable/readme.md",
    "content": "# 散列表\n\n散列表是数组的一种扩展，利用数组下标的随机访问特性。\n\n## 散列思想\n\n* 键/关键字/Key：用来标识一个数据\n* 散列函数/哈希函数/Hash：将 Key 映射到数组下标的函数\n* 散列值/哈希值：Key 经过散列函数得到的数值\n\n![](https://static001.geekbang.org/resource/image/92/73/92c89a57e21f49d2f14f4424343a2773.jpg)\n\n本质：利用散列函数将关键字映射到数组下标，而后利用数组随机访问时间复杂度为 $\\Theta(1)$ 的特性快速访问。\n\n## 散列函数\n\n* 形式：`hash(key)`\n* 基本要求\n  1. 散列值是非负整数\n  1. 如果 `key1 == key2`，那么 `hash(key1) == hash(key2)`\n  1. 如果 `key1 != key2`，那么 `hash(key1) != hash(key2)`\n\n第 3 个要求，实际上不可能对任意的 `key1` 和 `key2` 都成立。因为通常散列函数的输出范围有限而输入范围无限。\n\n## 散列冲突\n\n* 散列冲突：`key1 != key2` 但 `hash(key1) == hash(key2)`\n\n散列冲突会导致不同键值映射到散列表的同一个位置。为此，我们需要解决散列冲突带来的问题。\n\n### 开放寻址法\n\n如果遇到冲突，那就继续寻找下一个空闲的槽位。\n\n#### 线性探测\n\n插入时，如果遇到冲突，那就依次往下寻找下一个空闲的槽位。（橙色表示已被占用的槽位，黄色表示空闲槽位）\n\n![](https://static001.geekbang.org/resource/image/5c/d5/5c31a3127cbc00f0c63409bbe1fbd0d5.jpg)\n\n查找时，如果目标槽位上不是目标数据，则依次往下寻找；直至遇见目标数据或空槽位。\n\n![](https://static001.geekbang.org/resource/image/91/ff/9126b0d33476777e7371b96e676e90ff.jpg)\n\n删除时，标记为 `deleted`，而不是直接删除。\n\n#### 平方探测（Quadratic probing）\n\n插入时，如果遇到冲突，那就往后寻找下一个空闲的槽位，其步长为 $1^2$, $2^2$, $3^2$, $\\ldots$。\n\n查找时，如果目标槽位上不是目标数据，则依次往下寻找，其步长为 $1^2$, $2^2$, $3^2$, $\\ldots$；直至遇见目标数据或空槽位。\n\n删除时，标记为 `deleted`，而不是直接删除。\n\n#### 装载因子（load factor）\n\n$\\text{load factor} = \\frac{size()}{capacity()}$\n\n### 链表法\n\n所有散列值相同的 key 以链表的形式存储在同一个槽位中。\n\n![](https://static001.geekbang.org/resource/image/a4/7f/a4b77d593e4cb76acb2b0689294ec17f.jpg)\n\n插入时，不论是否有冲突，直接插入目标位置的链表。\n\n查找时，遍历目标位置的链表来查询。\n\n删除时，遍历目标位置的链表来删除。\n"
  },
  {
    "path": "notes/19_hashtable/.gitkeep",
    "content": ""
  },
  {
    "path": "notes/19_hashtable/readme.md",
    "content": "# 散列表\n\n核心：散列表的效率并不总是 $O(1)$，仅仅是在理论上能达到 $O(1)$。实际情况中，恶意攻击者可以通过精心构造数据，使得散列表的性能急剧下降。\n\n如何设计一个工业级的散列表？\n\n## 散列函数\n\n* 不能过于复杂——避免散列过程耗时\n* 散列函数的结果要尽可能均匀——最小化散列冲突\n\n## 装载因子过大怎么办\n\n动态扩容。涉及到 rehash，效率可能很低。\n\n![](https://static001.geekbang.org/resource/image/67/43/67d12e07a7d673a9c1d14354ad029443.jpg)\n\n如何避免低效扩容？\n\n——将 rehash 的步骤，均摊到每一次插入中去：\n\n* 申请新的空间\n* 不立即使用\n* 每次来了新的数据，往新表插入数据\n* 同时，取出旧表的一个数据，插入新表\n\n![](https://static001.geekbang.org/resource/image/6d/cb/6d6736f986ec4b75dabc5472965fb9cb.jpg)\n\n## 解决冲突\n\n开放寻址法，优点：\n\n* 不需要额外空间\n* 有效利用 CPU 缓存\n* 方便序列化\n\n开放寻址法，缺点：\n\n* 查找、删除数据时，涉及到 `delete` 标志，相对麻烦\n* 冲突的代价更高\n* 对装载因子敏感\n\n链表法，优点：\n\n* 内存利用率较高——链表的优点\n* 对装载因子不敏感\n\n链表法，缺点：\n\n* 需要额外的空间（保存指针）\n* 对 CPU 缓存不友好\n\n——将链表改造成更高效的数据结构，例如跳表、红黑树\n\n## 举个栗子（JAVA 中的 HashMap）\n\n* 初始大小：16\n* 装载因子：超过 0.75 时动态扩容\n* 散列冲突：优化版的链表法（当槽位冲突元素超过 8 时使用红黑树，否则使用链表）\n"
  },
  {
    "path": "notes/20_hashtable/.gitkeep",
    "content": ""
  },
  {
    "path": "notes/20_hashtable/readme.md",
    "content": "# 散列表\n\n散列表和链表的组合？为什么呢？\n\n* 链表：涉及查找的操作慢，不连续存储；\n* 顺序表：支持随机访问，连续存储。\n\n散列表 + 链表：结合优点、规避缺点。\n\n## 结合散列表的 LRU 缓存淘汰算法\n\n缓存的操作接口：\n\n* 向缓存添加数据\n* 从缓存删除数据\n* 在缓存中查找数据\n\n然而——不管是添加还是删除，都涉及到查找数据。因此，单纯的链表效率低下。\n\n魔改一把！\n\n![](https://static001.geekbang.org/resource/image/ea/6e/eaefd5f4028cc7d4cfbb56b24ce8ae6e.jpg)\n\n* `prev` 和 `next`：双向链表——LRU 的链表\n* `hnext`：单向链表——解决散列冲突的链表\n\n操作：\n\n* 在缓存中查找数据：利用散列表\n* 从缓存中删除数据：先利用散列表寻找数据，然后删除——改链表就好了，效率很高\n* 向缓存中添加数据：先利用散列表寻找数据，如果找到了，LRU 更新；如果没找到，直接添加在 LRU 链表尾部\n\n## Java: LinkedHashMap\n\n遍历时，按照访问顺序遍历。实现结构，与上述 LRU 的结构完全相同——只不过它不是缓存，不限制容量大小。\n"
  },
  {
    "path": "object-c/.gitkeep",
    "content": ""
  },
  {
    "path": "object-c/05_array/.gitkeep",
    "content": ""
  },
  {
    "path": "object-c/05_array/MyArray.h",
    "content": "//\n//  MyArray.h\n//  algo\n//\n//  Created by Wenru Dong on 2018/10/3.\n//  Copyright © 2018年 Wenru Dong. All rights reserved.\n//\n\n#import <Foundation/Foundation.h>\n\n@interface MyArray : NSObject\n\n- (instancetype)initWithCapacity:(NSUInteger)capacity;\n- (id)objectAtIndexedSubscript:(NSUInteger)index;\n- (void)removeObjectAtIndex:(NSUInteger)index;\n- (void)insertObject:(id)anObject atIndex:(NSUInteger)index;\n- (void)addObject:(id)anObject;\n- (void)printAll;\n@end\n"
  },
  {
    "path": "object-c/05_array/MyArray.m",
    "content": "//\n//  MyArray.m\n//  algo\n//\n//  Created by Wenru Dong on 2018/10/3.\n//  Copyright © 2018年 Wenru Dong. All rights reserved.\n//\n\n#import \"MyArray.h\"\n\n@implementation MyArray\n{\n    @private\n    NSMutableArray *_data;\n    NSUInteger _capacity;\n    NSUInteger _count;\n}\n\n- (instancetype)initWithCapacity:(NSUInteger)capacity {\n    self = [super init];\n    if (self) {\n        _data = [NSMutableArray arrayWithCapacity:capacity];\n        _capacity = capacity;\n        _count = 0;\n    }\n    return self;\n}\n\n- (id)objectAtIndexedSubscript:(NSUInteger)index {\n    if (index >= _count) return nil;\n    return _data[index];\n}\n\n- (void)removeObjectAtIndex:(NSUInteger)index {\n    if (index >= _count) {\n        [NSException raise:NSRangeException format:@\"Index out of range.\"];\n    }\n    for (NSUInteger i = index + 1; i < _data.count; i++) {\n        _data[i-1] = _data[i];\n    }\n    _count--;\n}\n\n- (void)insertObject:(nonnull id)anObject atIndex:(NSUInteger)index {\n    if (index >= _count || _count == _capacity) {\n        [NSException raise:NSRangeException format:@\"Index out of range.\"];\n    }\n    for (NSUInteger i = _count - 1; i >= index; i--) {\n        _data[i+1] = _data[i];\n    }\n    _data[index] = anObject;\n    _count++;\n}\n\n// insertToTail\n- (void)addObject:(nonnull id)anObject {\n    if (_count == _capacity) {\n        [NSException raise:NSRangeException format:@\"Array is full.\"];\n    }\n    [_data addObject:anObject];\n    _count++;\n}\n\n- (void)printAll {\n    for (id obj in _data) {\n        NSLog(@\"%@\", obj);\n    }\n}\n\n@end\n"
  },
  {
    "path": "object-c/06_linkedlist/.gitkeep",
    "content": ""
  },
  {
    "path": "object-c/06_linkedlist/ListNode.h",
    "content": "//\n//  ListNode.h\n//  algo\n//\n//  Created by Wenru Dong on 2018/10/6.\n//  Copyright © 2018年 Wenru Dong. All rights reserved.\n//\n\n#import <Foundation/Foundation.h>\n\n@interface ListNode : NSObject\n\n@property int value;\n@property ListNode *next;\n\n- (instancetype)initWithValue:(int)value;\n+ (instancetype)nodeWithValue:(int)value;\n\n@end\n"
  },
  {
    "path": "object-c/06_linkedlist/ListNode.m",
    "content": "//\n//  ListNode.m\n//  algo\n//\n//  Created by Wenru Dong on 2018/10/6.\n//  Copyright © 2018年 Wenru Dong. All rights reserved.\n//\n\n#import \"ListNode.h\"\n\n@implementation ListNode\n\n- (instancetype)initWithValue:(int)value {\n    if (self = [super init]) {\n        _value = value;\n    }\n    return self;\n}\n\n+ (instancetype)nodeWithValue:(int)value {\n    return [[self alloc] initWithValue:value];\n}\n\n- (NSString*)debugDescription {\n    return [NSString stringWithFormat:@\"%d\", _value];\n}\n\n@end\n"
  },
  {
    "path": "object-c/06_linkedlist/SinglyLinkedList.h",
    "content": "//\n//  SinglyLinkedList.h\n//  algo\n//\n//  Created by Wenru Dong on 2018/10/6.\n//  Copyright © 2018年 Wenru Dong. All rights reserved.\n//\n\n#import <Foundation/Foundation.h>\n#import \"ListNode.h\"\n\n@interface SinglyLinkedList : NSObject\n\n@property ListNode* head;\n\n- (ListNode*)nodeWithValue:(int)value;\n- (ListNode*)nodeAtIndex:(NSUInteger)index;\n\n- (void)insertNodeWithValue:(int)value;\n- (void)insertNode:(nonnull ListNode*)node;\n+ (void)insertNodeWithValue:(int)value afterNode:(nonnull ListNode*)node;\n+ (void)insertNode:(nonnull ListNode*)aNode afterNode:(nonnull ListNode*)node;\n- (void)insertNodeWithValue:(int)value beforeNode:(nonnull ListNode*)node;\n- (void)insertNode:(nonnull ListNode*)aNode beforeNode:(nonnull ListNode*)node;\n\n- (void)deleteNode:(nonnull ListNode*)node;\n- (void)deleteNodesWithValue:(int)value;\n\n@end\n"
  },
  {
    "path": "object-c/06_linkedlist/SinglyLinkedList.m",
    "content": "//\n//  SinglyLinkedList.m\n//  algo\n//\n//  Created by Wenru Dong on 2018/10/6.\n//  Copyright © 2018年 Wenru Dong. All rights reserved.\n//\n\n#import \"SinglyLinkedList.h\"\n\n@implementation SinglyLinkedList\n\n- (ListNode*)nodeWithValue:(int)value {\n    ListNode* current = _head;\n    while (current && current.value != value) {\n        current = current.next;\n    }\n    return current;\n}\n\n- (ListNode*)nodeAtIndex:(NSUInteger)index {\n    ListNode* current = _head;\n    NSUInteger position = 0;\n    while (current && position != index) {\n        current = current.next;\n        position++;\n    }\n    return current;\n}\n\n- (void)insertNodeWithValue:(int)value {\n    ListNode* aNode = [ListNode nodeWithValue:value];\n    [self insertNode:aNode];\n}\n\n- (void)insertNode:(nonnull ListNode *)node {\n    node.next = _head;\n    _head = node;\n}\n\n+ (void)insertNodeWithValue:(int)value afterNode:(nonnull ListNode *)node {\n    ListNode* aNode = [ListNode nodeWithValue:value];\n    [SinglyLinkedList insertNode:aNode afterNode:node];\n}\n\n+ (void)insertNode:(nonnull ListNode *)aNode afterNode:(nonnull ListNode *)node {\n    aNode.next = node.next;\n    node.next = aNode;\n}\n\n- (void)insertNodeWithValue:(int)value beforeNode:(nonnull ListNode *)node {\n    ListNode* aNode = [ListNode nodeWithValue:value];\n    [self insertNode:aNode beforeNode:node];\n}\n\n- (void)insertNode:(nonnull ListNode *)aNode beforeNode:(nonnull ListNode *)node {\n    ListNode* fakeHead = [ListNode nodeWithValue:0];\n    fakeHead.next = _head;\n    ListNode* current = fakeHead;\n    while (current.next && current.next != node) {\n        current = current.next;\n    }\n    if (current.next == nil) {\n        return;\n    }\n    aNode.next = node;\n    current.next = aNode;\n}\n\n- (void)deleteNode:(nonnull ListNode *)node {\n    if (node.next) {\n        node.value = node.next.value;\n        node.next = node.next.next;\n        return;\n    }\n    if (_head == nil) return;\n    ListNode* current = _head;\n    while (current.next && current.next != node) {\n        current = current.next;\n    }\n    current.next = nil;\n}\n\n- (void)deleteNodesWithValue:(int)value {\n    ListNode* fakeHead = [ListNode nodeWithValue:value+1];\n    fakeHead.next = _head;\n    ListNode* prev = fakeHead;\n    ListNode* current = _head;\n    while (current) {\n        if (current.value != value) {\n            prev.next = current;\n            prev = prev.next;\n        }\n        current = current.next;\n    }\n    if (prev.next) {\n        prev.next = nil;\n    }\n    _head = fakeHead.next;\n}\n\n- (NSString*)debugDescription {\n    NSMutableString* info = [[NSMutableString alloc] init];\n    ListNode* current = _head;\n    if (current) {\n        [info appendString:current.debugDescription];\n    }\n    current = current.next;\n    while (current) {\n        [info appendString:@\"->\"];\n        [info appendString:current.debugDescription];\n        current = current.next;\n    }\n    return [NSString stringWithString:info];\n}\n\n@end\n"
  },
  {
    "path": "object-c/06_linkedlist/SinglyLinkedListTests.m",
    "content": "//\n//  SinglyLinkedListTests.m\n//  SinglyLinkedListTests\n//\n//  Created by Wenru Dong on 2018/10/6.\n//  Copyright © 2018年 Wenru Dong. All rights reserved.\n//\n\n#import <XCTest/XCTest.h>\n#import \"ListNode.h\"\n#import \"SinglyLinkedList.h\"\n\n@interface SinglyLinkedListTests : XCTestCase\n\n@end\n\n@implementation SinglyLinkedListTests\n{\n    SinglyLinkedList* _list;\n    NSArray* _nodes;\n}\n- (void)setUp {\n    [super setUp];\n    ListNode* node1 = [ListNode nodeWithValue:1];\n    ListNode* node2 = [ListNode nodeWithValue:2];\n    ListNode* node3 = [ListNode nodeWithValue:3];\n    ListNode* node4 = [ListNode nodeWithValue:4];\n    ListNode* node5 = [ListNode nodeWithValue:5];\n    ListNode* node6 = [ListNode nodeWithValue:6];\n    node1.next = node2;\n    node2.next = node3;\n    node3.next = node4;\n    node4.next = node5;\n    node5.next = node6;\n    \n    _list = [[SinglyLinkedList alloc] init];\n    _list.head = node1;\n    _nodes = [NSArray arrayWithObjects:node1, node2, node3, node4, node5, node6, nil];\n}\n\n- (void)tearDown {\n    // Put teardown code here. This method is called after the invocation of each test method in the class.\n    [super tearDown];\n}\n\n- (void)testNodeWithValue {\n    XCTAssertEqualObjects([_list nodeWithValue:1], _list.head);\n    XCTAssertNil([_list nodeWithValue:10]);\n}\n\n- (void)testNodeAtIndex {\n    XCTAssertEqualObjects([_list nodeAtIndex:4], _nodes[4]);\n    XCTAssertNil([_list nodeAtIndex:10]);\n}\n\n- (void)testInsertNodeWithValue {\n    [_list insertNodeWithValue:9];\n    XCTAssertEqual(_list.head.value, 9);\n    XCTAssertEqual(_list.head.next.value, 1);\n}\n\n- (void)testInsertNode {\n    ListNode* aNode = [ListNode nodeWithValue:7];\n    [_list insertNode:aNode];\n    XCTAssertEqualObjects(_list.head, aNode);\n}\n\n- (void)testInsertNodeWithValueAfterNode {\n    [SinglyLinkedList insertNodeWithValue:12 afterNode:_nodes[3]];\n    XCTAssertEqual([[_list nodeAtIndex:4] value], 12);\n}\n\n- (void)testInsertNodeAfterNode {\n    ListNode* aNode = [ListNode nodeWithValue:28];\n    [SinglyLinkedList insertNode:aNode afterNode:_nodes[5]];\n    ListNode* prevNode = (ListNode *)_nodes[5];\n    XCTAssertEqualObjects(aNode, prevNode.next);\n}\n\n- (void)testInsertNodeBeforeNode {\n    ListNode* aNode = [ListNode nodeWithValue:27];\n    ListNode* prevNode = (ListNode *)_nodes[3];\n    [_list insertNode:aNode beforeNode:_nodes[4]];\n    XCTAssertEqualObjects(aNode, prevNode.next);\n}\n\n- (void)testInsertNodeBeforeUnconnectedNode {\n    ListNode* aNode = [ListNode nodeWithValue:27];\n    ListNode* floatingNode = [ListNode nodeWithValue:36];\n    [_list insertNode:aNode beforeNode:floatingNode];\n    for (NSUInteger i = 0; i < 6; i++) {\n        XCTAssertEqualObjects([_list nodeAtIndex:i], _nodes[i]);\n    }\n}\n\n- (void)testDeleteNode {\n    [_list deleteNode:_nodes[0]];\n    XCTAssertEqual(_list.head.value, 2);\n    [_list deleteNode:_nodes[5]];\n    ListNode* lastNode = (ListNode *)_nodes[4];\n    XCTAssertNil(lastNode.next);\n}\n\n- (void)testDeleteNodesWithValue {\n    ListNode* firstNode = [ListNode nodeWithValue:1];\n    ListNode* secondNode = [ListNode nodeWithValue:1];\n    [_list insertNode:firstNode];\n    [_list insertNode:secondNode];\n    [_list deleteNodesWithValue:1];\n    for (NSUInteger i = 1; i < 6; i++) {\n        XCTAssertEqualObjects([_list nodeAtIndex:i-1], _nodes[i]);\n    }\n}\n\n- (void)testDebugDescription {\n    XCTAssertEqualObjects(_list.debugDescription, @\"1->2->3->4->5->6\");\n}\n\n//- (void)testPerformanceExample {\n//    // This is an example of a performance test case.\n//    [self measureBlock:^{\n//        // Put the code you want to measure the time of here.\n//    }];\n//}\n\n@end\n"
  },
  {
    "path": "object-c/07_linkedlist/.gitkeep",
    "content": ""
  },
  {
    "path": "object-c/08_stack/LinkedStack.h",
    "content": "//\n//  LinkedStack.h\n//  algo\n//\n//  Created by Wenru Dong on 2018/10/8.\n//  Copyright © 2018年 Wenru Dong. All rights reserved.\n//\n//  Stack based upon linked list\n//  基于链表实现的栈\n\n#import <Foundation/Foundation.h>\n\n@interface LinkedStack : NSObject\n\n- (BOOL)isEmpty;\n- (void)push:(int)value;\n- (int)pop;\n\n@end\n"
  },
  {
    "path": "object-c/08_stack/LinkedStack.m",
    "content": "//\n//  LinkedStack.m\n//  algo\n//\n//  Created by Wenru Dong on 2018/10/8.\n//  Copyright © 2018年 Wenru Dong. All rights reserved.\n//\n\n#import \"LinkedStack.h\"\n#import \"ListNode.h\"\n\n@implementation LinkedStack\n{\n    @private\n    ListNode* _top;\n}\n\n- (BOOL)isEmpty {\n    return _top == nil;\n}\n\n- (void)push:(int)value {\n    ListNode *newTop = [ListNode nodeWithValue:value];\n    newTop.next = _top;\n    _top = newTop;\n}\n\n- (int)pop {\n    if ([self isEmpty]) {\n        [NSException raise:NSRangeException format:@\"The stack is empty.\"];\n    }\n    int value = _top.value;\n    _top = _top.next;\n    return value;\n}\n\n- (NSString *)debugDescription {\n    NSMutableString *info = [[NSMutableString alloc] init];\n    ListNode *current = _top;\n    while (current) {\n        [info appendString:[NSString stringWithFormat:@\"%d]\", current.value]];\n        current = current.next;\n    }\n    return [NSString stringWithString:info];\n}\n\n@end\n"
  },
  {
    "path": "object-c/08_stack/LinkedStackTests.m",
    "content": "//\n//  LinkedStackTests.m\n//  LinkedStackTests\n//\n//  Created by Wenru Dong on 2018/10/8.\n//  Copyright © 2018年 Wenru Dong. All rights reserved.\n//  \n\n#import <XCTest/XCTest.h>\n#import \"LinkedStack.h\"\n\n@interface LinkedStackTests : XCTestCase\n\n@end\n\n@implementation LinkedStackTests\n\n- (void)setUp {\n    [super setUp];\n    // Put setup code here. This method is called before the invocation of each test method in the class.\n}\n\n- (void)tearDown {\n    // Put teardown code here. This method is called after the invocation of each test method in the class.\n    [super tearDown];\n}\n\n- (void)testPush {\n    LinkedStack *stack = [[LinkedStack alloc] init];\n    for (int i = 0; i < 10; i++) {\n        [stack push:i];\n    }\n    XCTAssertEqualObjects([stack debugDescription], @\"9]8]7]6]5]4]3]2]1]0]\");\n}\n\n- (void)testPop {\n    LinkedStack *stack = [[LinkedStack alloc] init];\n    for (int i = 0; i < 10; i++) {\n        [stack push:i];\n    }\n    [stack pop];\n    XCTAssertEqualObjects([stack debugDescription], @\"8]7]6]5]4]3]2]1]0]\");\n    for (int i = 0; i < 9; i++) {\n        [stack pop];\n    }\n    XCTAssertThrowsSpecificNamed([stack pop], NSException, NSRangeException, @\"The stack is empty.\");\n}\n\n//- (void)testPerformanceExample {\n//    // This is an example of a performance test case.\n//    [self measureBlock:^{\n//        // Put the code you want to measure the time of here.\n//    }];\n//}\n\n@end\n"
  },
  {
    "path": "object-c/08_stack/ListNode.h",
    "content": "//\n//  ListNode.h\n//  algo\n//\n//  Created by Wenru Dong on 2018/10/6.\n//  Copyright © 2018年 Wenru Dong. All rights reserved.\n//\n\n#import <Foundation/Foundation.h>\n\n@interface ListNode : NSObject\n\n@property int value;\n@property ListNode *next;\n\n- (instancetype)initWithValue:(int)value;\n+ (instancetype)nodeWithValue:(int)value;\n\n@end\n"
  },
  {
    "path": "object-c/08_stack/ListNode.m",
    "content": "//\n//  ListNode.m\n//  algo\n//\n//  Created by Wenru Dong on 2018/10/6.\n//  Copyright © 2018年 Wenru Dong. All rights reserved.\n//\n\n#import \"ListNode.h\"\n\n@implementation ListNode\n\n- (instancetype)initWithValue:(int)value {\n    if (self = [super init]) {\n        _value = value;\n    }\n    return self;\n}\n\n+ (instancetype)nodeWithValue:(int)value {\n    return [[self alloc] initWithValue:value];\n}\n\n- (NSString*)debugDescription {\n    return [NSString stringWithFormat:@\"%d\", _value];\n}\n\n@end\n"
  },
  {
    "path": "object-c/08_stack/stack_practice/ArrayStack.h",
    "content": "\n/**\n 栈实现\n \n Author: Smallfly\n */\n\n#import <Foundation/Foundation.h>\n\n@interface Stack : NSObject\n\n- (id)initWithCapacity:(NSUInteger)count;\n\n- (BOOL)isEmpty;\n- (id)top;\n- (NSUInteger)size;\n\n- (BOOL)push:(id)obj;\n- (id)pop;\n\n@end\n"
  },
  {
    "path": "object-c/08_stack/stack_practice/ArrayStack.m",
    "content": "#import \"ArrayStack.h\"\n\n@implementation Stack {\n    NSMutableArray *_arr;\n    NSUInteger _capacity;\n    NSUInteger _count;\n}\n\n- (id)initWithCapacity:(NSUInteger)capacity {\n    self = [super init];\n    _capacity = capacity;\n    _arr = [[NSMutableArray alloc] initWithCapacity:capacity];\n    return self;\n}\n\n- (BOOL)isEmpty {\n    return _arr.count == 0;\n}\n\n- (BOOL)isFull {\n    return _arr.count == _capacity;\n}\n\n- (id)top {\n    if ([self isEmpty]) return nil;\n    NSUInteger index = _arr.count - 1;\n    return _arr[index];\n}\n\n- (NSUInteger)size {\n    return _arr.count;\n}\n\n- (BOOL)push:(id)obj {\n    if (!obj) return NO;\n    if (_arr.count == _capacity) return NO;\n    [_arr addObject:obj];\n    return YES;\n}\n\n- (id)pop {\n    if ([self isEmpty]) return nil;\n    NSUInteger index = _arr.count - 1;\n    id obj = _arr[index];\n    [_arr removeLastObject];\n    return obj;\n}\n\n@end\n"
  },
  {
    "path": "object-c/08_stack/stack_practice/BalancedParentheses.h",
    "content": "/**\n 判断括号是否匹配 {} [] ()\n \n Author: Smallfly\n */\n#import <Foundation/Foundation.h>\n\n@interface BalancedParentheses : NSObject\n\n- (BOOL)checkForParenthessBlanced:(NSString *)express;\n\n@end\n"
  },
  {
    "path": "object-c/08_stack/stack_practice/BalancedParentheses.m",
    "content": "\n#import \"BalancedParentheses.h\"\n#import \"ArrayStack.h\"\n\nconst NSDictionary *parenthesesDict() {\n    return @{@\"(\": @\")\", @\"{\": @\"}\", @\"[\": @\"]\"};\n}\n\n@implementation BalancedParentheses {\n    Stack *_stack;\n}\n\n- (instancetype)init {\n    self = [super init];\n    _stack = [[Stack alloc] initWithCapacity:100];\n    return self;\n}\n\n- (BOOL)checkForParenthessBlanced:(NSString *)express {\n    NSInteger midIndex = express.length / 2;\n    for (int i = 0; i < express.length; ++i) {\n        NSString *ele = [express substringWithRange:NSMakeRange(i, 1)];\n        if (i < midIndex) {\n            // 前半部分把与 ele 匹配的括号加入栈\n            [_stack push:parenthesesDict()[ele]];\n        } else {\n            // 后半部分检查栈顶的元素与当前元素是否相同\n            NSString *topEle = [_stack pop];\n            if (![topEle isEqualToString:ele]) {\n                return NO;\n            }\n        }\n    }\n    return YES;\n}\n\n@end\n"
  },
  {
    "path": "object-c/08_stack/stack_practice/FourOperation.h",
    "content": "\n/**\n 整型四则运算\n \n Author: Smallfly\n */\n\n#import <Foundation/Foundation.h>\n\n@interface FourOperation : NSObject\n\n+ (FourOperation *)shared;\n\n/**\n 整型四则运算\n\n @param expression 运算表达式，注意操作数和运算符之间要有空格\n @return 计算结果\n */\n- (NSNumber *)caculateExpression:(NSString *)expression;\n\n@end\n"
  },
  {
    "path": "object-c/08_stack/stack_practice/FourOperation.m",
    "content": "#import \"FourOperation.h\"\n#import \"ArrayStack.h\"\n\nconst NSDictionary *operationPriorityDict() {\n    return @{@\"*\": @1, @\"/\": @1, @\"+\": @0, @\"-\": @0};\n}\n\n@implementation FourOperation {\n    @private\n    Stack *_optStack;\n    Stack *_numStack;\n    NSNumberFormatter *_numFormatter;\n}\n\n+ (FourOperation *)shared {\n    static FourOperation* single = nil;\n    static dispatch_once_t onceToken;\n    dispatch_once(&onceToken, ^{\n        single = [FourOperation new];\n    });\n    return single;\n}\n\n- (instancetype)init {\n    self = [super init];\n    _optStack = [[Stack alloc] initWithCapacity:100];\n    _numStack = [[Stack alloc] initWithCapacity:100];\n    _numFormatter = [NSNumberFormatter new];\n    return self;\n}\n\n- (id)caculateExpression:(NSString *)expression {\n    NSArray *elements = [expression componentsSeparatedByString:@\" \"];\n    \n    for (NSString *obj in elements) {\n        NSNumber *numb = [_numFormatter numberFromString:obj];\n        if (numb) { // 运算数\n            [_numStack push:numb];\n        } else { // 操作符\n            \n            // 如果栈顶操作符优先级大于等于当前操作符\n            while ([self _topOperationPriorityIsHigherOrEqualToOperation:obj]) {\n                \n                // 取出栈顶的操作符和两个操作数做一次运算\n                NSNumber *res = [self _excuteOnceCaculate];\n                \n                // 计算结果存入栈\n                [_numStack push:res];\n            }\n            \n            [_optStack push:obj];\n        }\n    }\n    \n    // 如果操作符存在栈中，依次取出做运算\n    NSNumber *res = nil;\n    while ([_optStack top]) {\n        res = [self _excuteOnceCaculate];\n        [_numStack push:res];\n    }\n    return res;\n}\n\n- (NSInteger)_getPriority:(NSString *)opt {\n    return [[operationPriorityDict() objectForKey:opt] integerValue];\n}\n\n- (BOOL)_topOperationPriorityIsHigherOrEqualToOperation:(NSString *)opt {\n    NSString *topOpt = [_optStack top];\n    if (!topOpt) return NO;\n    NSInteger curPriority = [self _getPriority:opt];\n    NSInteger topPriority = [self _getPriority:topOpt];\n    return curPriority <= topPriority;\n}\n\n- (NSNumber *)_excuteOnceCaculate {\n    NSNumber *numRight = [_numStack pop];\n    NSNumber *numLeft = [_numStack pop];\n    NSString *topOpt = [_optStack pop];\n    NSInteger result = [self _caculeteWithNumberLeft:numLeft numberRight:numRight operation:topOpt];\n    NSNumber *res = [NSNumber numberWithInteger:result];\n    return res;\n}\n\n- (NSInteger)_caculeteWithNumberLeft:(NSNumber *)numLeft numberRight:(NSNumber *)numRight operation:(NSString *)opt {\n    if (!numLeft || !numRight || !opt) return 0;\n    NSInteger left = [numLeft integerValue];\n    NSInteger right = [numRight integerValue];\n    if ([opt isEqualToString:@\"+\"]) {\n        return left + right;\n    } else if ([opt isEqualToString:@\"-\"]) {\n        return left - right;\n    } else if ([opt isEqualToString:@\"*\"]) {\n        return left * right;\n    } else if ([opt isEqualToString:@\"/\"]) {\n        return left / right;\n    } else {\n        return 0;\n    }\n}\n\n@end\n"
  },
  {
    "path": "object-c/08_stack/stack_practice/main.m",
    "content": "\n#import <Foundation/Foundation.h>\n#import \"FourOperation.h\"\n#import \"BalancedParentheses.h\"\n\nint main(int argc, const char * argv[]) {\n    @autoreleasepool {\n        // 测试四则运算\n        NSNumber *a = [[FourOperation shared] caculateExpression:@\"10 - 4 / 2 * 3 + 3 - 6 / 2\"];\n        NSNumber *b = [[FourOperation shared] caculateExpression:@\"10 - 3\"];\n        NSNumber *c = [[FourOperation shared] caculateExpression:@\"2 * 3\"];\n        NSLog(@\"FourOperation: %ld\\t%ld\\t%ld\\t\", a.integerValue, b.integerValue, c.integerValue);\n        \n        // 测试括号匹配\n        BalancedParentheses *balancedCheck = [BalancedParentheses new];\n        BOOL result = [balancedCheck checkForParenthessBlanced:@\"([{{{}}}])\"];\n        NSLog(@\"BalancedParentheses: %d\", result);\n    }\n    return 0;\n}\n"
  },
  {
    "path": "object-c/11_Sort/Sort.h",
    "content": "//\n//  Sort.h\n//  test1231231\n//\n//  Created by Scarlett Che on 2018/12/12.\n//  Copyright © 2018 Scarlett Che. All rights reserved.\n//\n\n#import <Foundation/Foundation.h>\n\nNS_ASSUME_NONNULL_BEGIN\n\n@interface Sort : NSObject\n//  冒泡排序\n+ (NSArray *)bubbleSortWithArray:(NSArray *)array;\n\n//  插入排序\n+ (NSArray *)insertionSortWithArray:(NSArray *)array;\n\n//  选择排序\n+ (NSArray *)selectionSortWithArray:(NSArray *)array;\n@end\n\nNS_ASSUME_NONNULL_END\n"
  },
  {
    "path": "object-c/11_Sort/Sort.m",
    "content": "//\n//  Sort.m\n//  test1231231\n//\n//  Created by Scarlett Che on 2018/12/12.\n//  Copyright © 2018 Scarlett Che. All rights reserved.\n//\n\n#import \"Sort.h\"\n\n@implementation Sort\n//  冒泡排序\n+ (NSArray *)bubbleSortWithArray:(NSArray *)array {\n    if (array.count <= 1) {\n        return array;\n    }\n    \n    NSMutableArray *aryM = array.mutableCopy;\n    \n    for (int i = 0; i < aryM.count - 1; i++) {\n        BOOL flag = NO; //  提前结束标记\n        for (int j = 0; j < aryM.count - i - 1; j++) {\n            NSInteger value1 = [aryM[j] integerValue];\n            NSInteger value2 = [aryM[j + 1] integerValue];\n            \n            if (value1 > value2) {\n                flag = YES;\n                [aryM exchangeObjectAtIndex:j withObjectAtIndex:j+1];\n            }\n        }\n        \n        \n        if (flag == NO) {\n            //  提前结束\n            break;\n        }\n    }\n    return aryM.copy;\n}\n\n//  插入排序\n+ (NSArray *)insertionSortWithArray:(NSArray *)array {\n    NSMutableArray *aryU = array.mutableCopy;\n    \n    for (int i = 1; i < aryU.count; i++) {\n        NSInteger value = [aryU[i] integerValue];\n        \n        for (int j = 0; j < i; j ++) {\n            NSInteger sortedValue = [aryU[j] integerValue];\n            if (value < sortedValue) {\n                id obj = aryU[i];\n                [aryU removeObjectAtIndex:i];\n                [aryU insertObject:obj atIndex:j];\n                break;\n            }\n        }\n    }\n    return aryU.copy;\n}\n\n//  选择排序\n+ (NSArray *)selectionSortWithArray:(NSArray *)array {\n    if (array.count <= 1) {\n        return array;\n    }\n    \n    NSMutableArray *aryM = array.mutableCopy;\n    for (int i = 0; i < array.count - 1; i++) {\n        NSInteger minIndex = NSNotFound;\n        NSInteger minValue = NSNotFound;\n        for (int j = i + 1; j < array.count - 1; j++) {\n            NSInteger tmp = [array[j] integerValue];\n            if (tmp < minValue) {\n                minValue = tmp;\n                minIndex = j;\n            }\n        }\n        \n        if (minIndex != NSNotFound && minValue != NSNotFound && minValue < [array[i] integerValue]) {\n            [aryM exchangeObjectAtIndex:minIndex withObjectAtIndex:i];\n        }\n        \n    }\n    return array;\n}\n@end\n"
  },
  {
    "path": "object-c/33_bm_match/BM.h",
    "content": "//\n//  BM.h\n//  BM-Match\n//\n//  Created by Smallfly on 2018/12/9.\n//  Copyright © 2018 Smallfly. All rights reserved.\n//\n\n#import <Foundation/Foundation.h>\n\nNS_ASSUME_NONNULL_BEGIN\n\n@interface BM : NSObject\n- (instancetype)initWithA:(NSString *)a andB:(NSString *)b;\n- (NSInteger)startMatch;\n- (void)startMatchCompeletion:(void (^)(NSInteger))completion;\n@end\n\nNS_ASSUME_NONNULL_END\n"
  },
  {
    "path": "object-c/33_bm_match/BM.m",
    "content": "//\n//  BM.m\n//  BM-Match\n//\n//  Created by Smallfly on 2018/12/9.\n//  Copyright © 2018 Smallfly. All rights reserved.\n//\n\n#import \"BM.h\"\n\n#define SIZE 256\n\n@interface BM()\n@property (nonatomic, strong) NSString *a; // 主串\n@property (nonatomic, strong) NSString *b; // 匹配串\n\n@property (nonatomic, strong) NSMutableArray *bc; // 匹配串，哈希表，存储字符在匹配串中的下标\n\n@property (nonatomic, strong) NSMutableArray *suffix;\n@property (nonatomic, strong) NSMutableArray *prifix;\n\n@end\n\n@implementation BM\n\n- (instancetype)initWithA:(NSString *)a andB:(NSString *)b {\n    self = [super init];\n    if (!self) return nil;\n    _a = a;\n    _b = b;\n    _bc = [NSMutableArray new];\n    _suffix = [NSMutableArray new];\n    _prifix = [NSMutableArray new];\n    [self generateBC];\n    [self generateGS];\n    return self;\n}\n\n// 构建坏字符哈希表，记录每个字符在匹配串中最后出现的位置\n- (void)generateBC {\n    for (int i = 0; i < SIZE; ++i) {\n        [_bc addObject:@-1];\n    }\n    for (int i = 0; i < _b.length; ++i) {\n        int ascii = (int)[_b characterAtIndex:i]; // char to ASCII\n        _bc[ascii] = [NSNumber numberWithInteger:i]; // save b's char index\n    }\n}\n\n- (NSInteger)bm {\n    NSInteger i = 0; // 主串和匹配串对齐的第一个字符\n    NSUInteger n = _a.length;\n    NSUInteger m = _b.length;\n    while (i <= n - m) {\n        NSInteger j;\n        // 从后往前匹配\n        for (j = m - 1; j >= 0; --j) {\n            int aValue = (int)[_a characterAtIndex:(i + j)];\n            int bValue = (int)[_b characterAtIndex:j];\n            if (aValue != bValue) break; // 找到坏字符下标 j 停止\n        }\n        if (j < 0) {\n            return i; // 匹配成功，返回所在的位置\n        }\n        \n        // 坏字符在匹配串中最后出现的位置\n        id numberInHashTableBC = _bc[(int)[_a characterAtIndex:(i + j)]];\n        NSInteger x = j - [numberInHashTableBC integerValue];\n        NSInteger y = 0;\n        if (j < m - 1) {\n            y = [self moveByGSBy:j];\n        }\n        i = i + MAX(x, y);\n        \n        // 这一步比较难理解，不直接滑到过 j，是因为在 j 之前可能存在于坏字符相同的字符\n        // 这个于坏字符相同的字符，在匹配串中的最大下标是 numberInHashTableBC\n//        i = i + (j - [numberInHashTableBC integerValue]);\n    }\n    \n    return -1;\n}\n\n// 好后缀匹配移动\n- (NSInteger)moveByGSBy:(NSInteger)j {\n    NSUInteger m = _b.length;\n    NSInteger k = m - 1 - j; // 好后缀的长度\n    NSInteger t = [_suffix[k] integerValue];\n    if (t != -1) return j - t + 1; // 匹配串的前缀，是否匹配好后缀，关键\n    for (NSInteger r = j+1; r <= m-1; ++r) {\n        if ([_prifix[m-r] boolValue]) { // 关键\n            return r;\n        }\n    }\n    return m;\n}\n\n- (void)generateGS {\n    NSUInteger m = _b.length;\n    for (NSInteger i = 0; i < m; ++i) {\n        _prifix[i] = @(NO);\n        _suffix[i] = @(-1);\n    }\n    for (NSInteger i = 0; i < m - 1; ++i) { // 从 b 中取两个字符对比\n        NSInteger j = i;\n        NSInteger k = 0; // 公共后缀的长度\n        int jValue = (int)[_b characterAtIndex:j];\n        int bmValue = (int)[_b characterAtIndex:(m-1-k)];\n        while (j >= 0 && jValue == bmValue) { // 与 b[0, m-1] 求公共子串\n            ++k;\n            --j;\n            _suffix[k] = [NSNumber numberWithInteger:(j+1)]; //j+1 代表公共子串在 b 中的起始下标\n        }\n        if (j == -1) _prifix[k] = @(YES);\n    }\n}\n\n#pragma mark -\n\n// 同步\n- (NSInteger)startMatch {\n    return [self bm];\n}\n\n// 异步\n- (void)startMatchCompeletion:(void (^)(NSInteger))completion {\n    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{\n        completion([self bm]);\n    });\n}\n\n@end\n"
  },
  {
    "path": "object-c/33_bm_match/main.m",
    "content": "//\n//  main.m\n//  BM-Match\n//\n//  Created by Smallfly on 2018/12/9.\n//  Copyright © 2018 Smallfly. All rights reserved.\n//\n\n#import <Foundation/Foundation.h>\n#import \"BM.h\"\n\nint main(int argc, const char * argv[]) {\n    @autoreleasepool {\n        BM *bm = [[BM alloc] initWithA:@\"abacadc\" andB:@\"adc\"];\n        \n        [bm startMatchCompeletion:^(NSInteger index) {\n            NSLog(@\"异步查找到下标：%ld\\n\", index);\n        }];\n        \n        NSLog(@\"同步查找到下标：%ld\\n\", [bm startMatch]);\n    }\n    return 0;\n}\n"
  },
  {
    "path": "php/.gitignore",
    "content": ".idea\nvendor\n*my*\nQueue"
  },
  {
    "path": "php/05_array/.gitkeep",
    "content": ""
  },
  {
    "path": "php/05_array/array.php",
    "content": "<?php\n/**\n * Created by PhpStorm.\n * User: leo\n * Date: 2018/10/5\n * Time: 9:13\n */\n\n/**\n * 简单数组类\n */\nclass MyArray\n{\n    //数据\n    private $data;\n    //容量\n    private $capacity;\n    //长度\n    private $length;\n\n    /**\n     * MyArray constructor.\n     * @param $capacity\n     */\n    public function __construct($capacity)\n    {\n        $capacity = intval($capacity);\n        if($capacity <= 0) {\n            return null;\n        }\n        $this->data = array();\n        $this->capacity = $capacity;\n        $this->length = 0;\n    }\n\n    /**\n     * 数组是否已满\n     * @return bool\n     */\n    public function checkIfFull()\n    {\n        if($this->length == $this->capacity) {\n            return true;\n        }\n        return false;\n    }\n\n    /**\n     * 判断索引index是否超出数组范围\n     * @param $index\n     * @return bool\n     */\n    private function checkOutOfRange($index)\n    {\n        if($index >= $this->length) {\n           return true;\n        }\n        return false;\n    }\n\n    /**\n     * 在索引index位置插入值value，返回错误码，0为插入成功\n     * @param $index\n     * @param $value\n     * @return int\n     */\n    public function insert($index, $value)\n    {\n        $index = intval($index);\n        $value = intval($value);\n        if ($index < 0) {\n            return 1;\n        }\n\n        if ($this->checkIfFull()) {\n            return 2;\n        }\n\n        for ($i = $this->length - 1; $i >= $index; $i--) {\n            $this->data[$i + 1] = $this->data[$i];\n        }\n\n        $this->data[$index] = $value;\n        $this->length++;\n        return 0;\n    }\n\n    /**\n     * 删除索引index上的值，并返回\n     * @param $index\n     * @return array\n     */\n    public function delete($index)\n    {\n        $value = 0;\n        $index = intval($index);\n        if ($index < 0) {\n            $code = 1;\n            return [$code, $value];\n        }\n        if ($this->checkOutOfRange($index)) {\n            $code = 2;\n            return [$code, $value];\n        }\n\n        $value = $this->data[$index];\n        for ($i = $index; $i < $this->length - 1; $i++) {\n            $this->data[$i] = $this->data[$i + 1];\n        }\n        $this->length--;\n        return [0, $value];\n    }\n\n    /**\n     * 查找索引index的值\n     * @param $index\n     * @return array\n     */\n    public function find($index)\n    {\n        $value = 0;\n        $index = intval($index);\n        if ($index < 0) {\n            $code = 1;\n            return [$code, $value];\n        }\n        if ($this->checkOutOfRange($index)) {\n            $code = 2;\n            return [$code, $value];\n        }\n        return [0, $this->data[$index]];\n    }\n\n    public function printData()\n    {\n        $format = \"\";\n        for ($i = 0; $i < $this->length; $i++) {\n            $format .= \"|\" . $this->data[$i];\n        }\n        print($format . \"\\n\");\n    }\n}\n"
  },
  {
    "path": "php/05_array/array_test.php",
    "content": "<?php\n/**\n * Created by PhpStorm.\n * User: leo\n * Date: 2018/10/5\n * Time: 9:13\n */\nrequire \"./array.php\";\n$myArr1 = new MyArray(10);\nfor($i=0;$i<9;$i++) {\n    $myArr1->insert($i, $i+1);\n}\n$myArr1->printData();\n\n$code = $myArr1->insert(6, 999);\necho \"insert at 6: code:{$code}\\n\";\n$myArr1->printData();\n\nlist($code, $value) = $myArr1->delete(6);\necho \"delete at 6: code:{$code}, value:{$value}\\n\";\n$myArr1->printData();\n\n$code = $myArr1->insert(11, 999);\necho \"insert at 11: code:{$code}\\n\";\n$myArr1->printData();\n\nlist($code, $value) = $myArr1->delete(0);\necho \"delete at 0: code:{$code}, value:{$value}\\n\";\n$myArr1->printData();\n\nlist($code, $value) = $myArr1->find(0);\necho \"find at 0: code:{$code}, value:{$value}\\n\";"
  },
  {
    "path": "php/06_linkedlist/.gitkeep",
    "content": ""
  },
  {
    "path": "php/06_linkedlist/SingleLinkedList.php",
    "content": "<?php\n/**\n * User: lide01\n * Date: 2018/10/8 11:55\n * Desc:\n */\n\nnamespace Algo_06;\n\n\n/**\n * 单链表\n *\n * Class SingleLinkedList\n *\n * @package Algo_06\n */\nclass SingleLinkedList\n{\n    /**\n     * 单链表头结点（哨兵节点）\n     *\n     * @var SingleLinkedListNode\n     */\n    public $head;\n\n    /**\n     * 单链表长度\n     *\n     * @var\n     */\n    private $length;\n\n    /**\n     * 初始化单链表\n     *\n     * SingleLinkedList constructor.\n     *\n     * @param null $head\n     */\n    public function __construct($head = null)\n    {\n        if (null == $head) {\n            $this->head = new SingleLinkedListNode();\n        } else {\n            $this->head = $head;\n        }\n\n        $this->length = 0;\n    }\n\n    /**\n     * 获取链表长度\n     *\n     * @return int\n     */\n    public function getLength()\n    {\n        return $this->length;\n    }\n\n    /**\n     * 插入数据 采用头插法 插入新数据\n     *\n     * @param $data\n     *\n     * @return SingleLinkedListNode|bool\n     */\n    public function insert($data)\n    {\n        return $this->insertDataAfter($this->head, $data);\n    }\n\n    /**\n     * 删除节点\n     *\n     * @param SingleLinkedListNode $node\n     *\n     * @return bool\n     */\n    public function delete(SingleLinkedListNode $node)\n    {\n        if (null == $node) {\n            return false;\n        }\n\n        // 获取待删除节点的前置节点\n        $preNode = $this->getPreNode($node);\n        if (empty($preNode)) {\n            return false;\n        }\n\n        // 修改指针指向\n        $preNode->next = $node->next;\n        unset($node);\n\n        $this->length--;\n        return true;\n    }\n\n    /**\n     * 通过索引获取节点\n     *\n     * @param int $index\n     *\n     * @return SingleLinkedListNode|null\n     */\n    public function getNodeByIndex($index)\n    {\n        if ($index >= $this->length) {\n            return null;\n        }\n\n        $cur = $this->head->next;\n        for ($i = 0; $i < $index; ++$i) {\n            $cur = $cur->next;\n        }\n\n        return $cur;\n    }\n\n    /**\n     * 获取某个节点的前置节点\n     *\n     * @param SingleLinkedListNode $node\n     *\n     * @return SingleLinkedListNode|bool|null\n     */\n    public function getPreNode(SingleLinkedListNode $node)\n    {\n        if (null == $node) {\n            return false;\n        }\n\n        $curNode = $this->head;\n        $preNode = $this->head;\n        // 遍历找到前置节点 要用全等判断是否是同一个对象\n        // http://php.net/manual/zh/language.oop5.object-comparison.php\n        while ($curNode !== $node) {\n            if ($curNode == null) {\n                return null;\n            }\n            $preNode = $curNode;\n            $curNode = $curNode->next;\n        }\n\n        return $preNode;\n    }\n\n    /**\n     * 输出单链表 当data的数据为可输出类型\n     *\n     * @return bool\n     */\n    public function printList()\n    {\n        if (null == $this->head->next) {\n            return false;\n        }\n\n        $curNode = $this->head;\n        // 防止链表带环，控制遍历次数\n        $listLength = $this->getLength();\n        while ($curNode->next != null && $listLength--) {\n            echo $curNode->next->data . ' -> ';\n\n            $curNode = $curNode->next;\n        }\n        echo 'NULL' . PHP_EOL;\n\n        return true;\n    }\n\n    /**\n     * 输出单链表 当data的数据为可输出类型\n     *\n     * @return bool\n     */\n    public function printListSimple()\n    {\n        if (null == $this->head->next) {\n            return false;\n        }\n\n        $curNode = $this->head;\n        while ($curNode->next != null) {\n            echo $curNode->next->data . ' -> ';\n\n            $curNode = $curNode->next;\n        }\n        echo 'NULL' . PHP_EOL;\n\n        return true;\n    }\n\n    /**\n     * 在某个节点后插入新的节点 (直接插入数据)\n     *\n     * @param SingleLinkedListNode $originNode\n     * @param                      $data\n     *\n     * @return SingleLinkedListNode|bool\n     */\n    public function insertDataAfter(SingleLinkedListNode $originNode, $data)\n    {\n        // 如果originNode为空，插入失败\n        if (null == $originNode) {\n            return false;\n        }\n\n        // 新建单链表节点\n        $newNode = new SingleLinkedListNode();\n        // 新节点的数据\n        $newNode->data = $data;\n\n        // 新节点的下一个节点为源节点的下一个节点\n        $newNode->next = $originNode->next;\n        // 在originNode后插入newNode\n        $originNode->next = $newNode;\n\n        // 链表长度++\n        $this->length++;\n\n        return $newNode;\n    }\n\n    /**\n     * 在某个节点前插入新的节点（很少使用）\n     *\n     * @param SingleLinkedListNode $originNode\n     * @param                      $data\n     *\n     * @return SingleLinkedListNode|bool\n     */\n    public function insertDataBefore(SingleLinkedListNode $originNode, $data)\n    {\n        // 如果originNode为空，插入失败\n        if (null == $originNode) {\n            return false;\n        }\n\n        // 先找到originNode的前置节点，然后通过insertDataAfter插入\n        $preNode = $this->getPreNode($originNode);\n\n        return $this->insertDataAfter($preNode, $data);\n    }\n\n    /**\n     * 在某个节点后插入新的节点\n     *\n     * @param SingleLinkedListNode $originNode\n     * @param SingleLinkedListNode $node\n     *\n     * @return SingleLinkedListNode|bool\n     */\n    public function insertNodeAfter(SingleLinkedListNode $originNode, SingleLinkedListNode $node)\n    {\n        // 如果originNode为空，插入失败\n        if (null == $originNode) {\n            return false;\n        }\n\n        $node->next = $originNode->next;\n        $originNode->next = $node;\n\n        $this->length++;\n\n        return $node;\n    }\n\n    /**\n     * 构造一个有环的链表\n     */\n    public function buildHasCircleList()\n    {\n        $data = [1, 2, 3, 4, 5, 6, 7, 8];\n\n        $node0 = new SingleLinkedListNode($data[0]);\n        $node1 = new SingleLinkedListNode($data[1]);\n        $node2 = new SingleLinkedListNode($data[2]);\n        $node3 = new SingleLinkedListNode($data[3]);\n        $node4 = new SingleLinkedListNode($data[4]);\n        $node5 = new SingleLinkedListNode($data[5]);\n        $node6 = new SingleLinkedListNode($data[6]);\n        $node7 = new SingleLinkedListNode($data[7]);\n\n        $this->insertNodeAfter($this->head, $node0);\n        $this->insertNodeAfter($node0, $node1);\n        $this->insertNodeAfter($node1, $node2);\n        $this->insertNodeAfter($node2, $node3);\n        $this->insertNodeAfter($node3, $node4);\n        $this->insertNodeAfter($node4, $node5);\n        $this->insertNodeAfter($node5, $node6);\n        $this->insertNodeAfter($node6, $node7);\n\n        $node7->next = $node4;\n    }\n}"
  },
  {
    "path": "php/06_linkedlist/SingleLinkedListNode.php",
    "content": "<?php\n/**\n * User: lide01\n * Date: 2018/10/8 11:38\n * Desc:\n */\n\nnamespace Algo_06;\n\n\n/**\n * 单链表节点\n *\n * Class SingleLinkedListNode\n *\n * @package Algo_06\n */\nclass SingleLinkedListNode\n{\n    /**\n     * 节点中的数据域\n     *\n     * @var null\n     */\n    public $data;\n\n    /**\n     * 节点中的指针域，指向下一个节点\n     *\n     * @var SingleLinkedListNode\n     */\n    public $next;\n\n    /**\n     * SingleLinkedListNode constructor.\n     *\n     * @param null $data\n     */\n    public function __construct($data = null)\n    {\n        $this->data = $data;\n        $this->next = null;\n    }\n}"
  },
  {
    "path": "php/06_linkedlist/main.php",
    "content": "<?php\n/**\n * User: lide01\n * Date: 2018/10/8 11:55\n * Desc:\n */\n\nnamespace Algo_06;\nrequire_once '../vendor/autoload.php';\n\n/**\n * 判断链表保存的字符串是否是回文\n *\n * @param SingleLinkedList $list\n *\n * @return bool\n */\nfunction isPalindrome(SingleLinkedList $list)\n{\n    if ($list->getLength() <= 1) {\n        return true;\n    }\n\n    $pre = null;\n    $slow = $list->head->next;\n    $fast = $list->head->next;\n    $remainNode = null;\n\n    // 找单链表中点 以及 反转前半部分链表\n    while ($fast != null && $fast->next != null) {\n        $fast = $fast->next->next;\n\n        // 单链表反转关键代码 三个指针\n        $remainNode = $slow->next;\n        $slow->next = $pre;\n        $pre = $slow;\n        $slow = $remainNode;\n    }\n    // 链表长度为偶数的情况\n    if ($fast != null) {\n        $slow = $slow->next;\n    }\n\n    // 开始逐个比较\n    while ($slow != null) {\n        if ($slow->data != $pre->data) {\n            return false;\n        }\n        $slow = $slow->next;\n        $pre = $pre->next;\n    }\n\n    return true;\n}\n\n$list = new SingleLinkedList();\n$list->insert('a');\n$list->insert('b');\n$list->insert('c');\n$list->insert('c');\n$list->insert('b');\n$list->insert('a');\n\nvar_dump(isPalindrome($list));"
  },
  {
    "path": "php/07_linkedlist/.gitkeep",
    "content": ""
  },
  {
    "path": "php/07_linkedlist/main.php",
    "content": "<?php\n/**\n * User: lide01\n * Date: 2018/10/9 14:06\n * Desc:\n */\n\nnamespace Algo_07;\nrequire_once '../vendor/autoload.php';\n\nuse Algo_06\\SingleLinkedList;\n\n\n/**\n * 单链表相关算法\n *\n * Class SingleLinkedListAlgo\n *\n * reverse 单链表反转\n * checkCircle 链表中环的检测\n * mergerSortedList 两个有序的链表合并\n * deleteLastKth 删除链表倒数第n个结点\n * findMiddleNode 求链表的中间结点\n *\n * @package Algo_07\n */\nClass SingleLinkedListAlgo\n{\n    /**\n     * 单链表\n     *\n     * @var\n     */\n    public $list;\n\n    /**\n     * 构造函数设置$list\n     *\n     * SingleLinkedListAlgo constructor.\n     *\n     * @param SingleLinkedList $list\n     */\n    public function __construct(SingleLinkedList $list = null)\n    {\n        $this->list = $list;\n    }\n\n    /**\n     * 设置单链表\n     *\n     * @param SingleLinkedList $list\n     */\n    public function setList(SingleLinkedList $list)\n    {\n        $this->list = $list;\n    }\n\n    /**\n     * 单链表反转\n     *\n     * 三个指针反转\n     * preNode 指向前一个结点\n     * curNode 指向当前结点\n     * remainNode 指向当前结点的下一个节点（保存未逆序的链表，为了在断开curNode的next指针后能找到后续节点）\n     *\n     * @return bool\n     */\n    public function reverse()\n    {\n        if (null == $this->list || null == $this->list->head || null == $this->list->head->next) {\n            return false;\n        }\n\n        $preNode = null;\n        $curNode = $this->list->head->next;\n        $remainNode = null;\n\n        // 保存头结点，稍后指向反转后的链表\n        $headNode = $this->list->head;\n        // 断开头结点的next指针\n        $this->list->head->next = null;\n\n        while ($curNode != null) {\n            $remainNode = $curNode->next;\n            $curNode->next = $preNode;\n            $preNode = $curNode;\n            $curNode = $remainNode;\n        }\n\n        // 头结点指向反转后的链表\n        $headNode->next = $preNode;\n\n        return true;\n    }\n\n    /**\n     * 判断链表是否有环\n     *\n     * 快慢指针判断是否有环\n     * @link http://t.cn/ROxpgQ1\n     *\n     * @return bool\n     */\n    public function checkCircle()\n    {\n        if (null == $this->list || null == $this->list->head || null == $this->list->head->next) {\n            return false;\n        }\n\n        $slow = $this->list->head->next;\n        $fast = $this->list->head->next;\n\n        while ($fast != null && $fast->next != null) {\n            $fast = $fast->next->next;\n            $slow = $slow->next;\n\n            // 如果慢指针跟快指针相遇了说明有环 解释在上面的链接中\n            if ($slow === $fast) {\n                return true;\n            }\n        }\n\n        return false;\n    }\n\n    /**\n     * 合并两个有序链表\n     *\n     * @param SingleLinkedList $listA\n     * @param SingleLinkedList $listB\n     *\n     * @return SingleLinkedList|\\Algo_06\\SingleLinkedListNode\n     */\n    public function mergerSortedList(SingleLinkedList $listA, SingleLinkedList $listB)\n    {\n        if (null == $listA) {\n            return $listB;\n        }\n        if (null == $listB) {\n            return $listA;\n        }\n\n        $pListA = $listA->head->next;\n        $pListB = $listB->head->next;\n        $newList = new SingleLinkedList();\n        $newHead = $newList->head;\n        $newRootNode = $newHead;\n\n        while ($pListA != null && $pListB != null) {\n            if ($pListA->data <= $pListB->data) {\n                $newRootNode->next = $pListA;\n                $pListA = $pListA->next;\n            } else {\n                $newRootNode->next = $pListB;\n                $pListB = $pListB->next;\n            }\n\n            $newRootNode = $newRootNode->next;\n        }\n\n        // 如果第一个链表未处理完，拼接到新链表后面\n        if ($pListA != null) {\n            $newRootNode->next = $pListA;\n        }\n        // 如果第二个链表未处理完，拼接到新链表后面\n        if ($pListB != null) {\n            $newRootNode->next = $pListB;\n        }\n\n        return $newList;\n    }\n\n    /**\n     * 删除链表倒数第n个结点\n     *\n     * @param $index\n     *\n     * @return bool\n     */\n    public function deleteLastKth($index)\n    {\n        if (null == $this->list || null == $this->list->head || null == $this->list->head->next) {\n            return false;\n        }\n\n        $i = 1;\n        $slow = $this->list->head;\n        $fast = $this->list->head;\n        while ($fast != null && $i < $index) {\n            $fast = $fast->next;\n            ++$i;\n        }\n\n        if (null == $fast) {\n            return true;\n        }\n\n        $pre = null;\n        while($fast->next != null) {\n            $pre = $slow;\n            $slow = $slow->next;\n            $fast = $fast->next;\n        }\n\n        if (null == $pre) {\n            $this->list->head->next = $slow->next;\n        } else {\n            $pre->next = $pre->next->next;\n        }\n\n        return true;\n    }\n\n    /**\n     * 寻找中间节点\n     *\n     * 快慢指针遍历\n     *\n     * @return \\Algo_06\\SingleLinkedListNode|bool|null\n     */\n    public function findMiddleNode()\n    {\n        if (null == $this->list || null == $this->list->head || null == $this->list->head->next) {\n            return false;\n        }\n\n        $slow = $this->list->head->next;\n        $fast = $this->list->head->next;\n\n        while ($fast != null && $fast->next != null) {\n            $fast = $fast->next->next;\n            $slow = $slow->next;\n        }\n\n        return $slow;\n    }\n}\n\necho '---------------------- 单链表反转 ----------------------' . PHP_EOL . PHP_EOL;\n$list = new SingleLinkedList();\n$list->insert(1);\n$list->insert(2);\n$list->insert(3);\n$list->insert(4);\n$list->insert(5);\n$list->insert(6);\n$list->insert(7);\n\n// 单链表反转\n$listAlgo = new SingleLinkedListAlgo($list);\n$listAlgo->list->printList();\n$listAlgo->reverse();\n$listAlgo->list->printList();\necho '--------------------------------------------------------' . PHP_EOL . PHP_EOL;\n\necho '---------------------- 链表中环的检测 ----------------------'. PHP_EOL . PHP_EOL;\n// 链表中环的检测\n$listCircle = new SingleLinkedList();\n$listCircle->buildHasCircleList();\n$listAlgo->setList($listCircle);\nvar_dump($listAlgo->checkCircle());\necho '------------------------------------------------------------' . PHP_EOL . PHP_EOL;\n\necho '---------------------- 两个有序的链表合并 ----------------------' . PHP_EOL . PHP_EOL;\n// 两个有序的链表合并\n$listA = new SingleLinkedList();\n$listA->insert(9);\n$listA->insert(7);\n$listA->insert(5);\n$listA->insert(3);\n$listA->insert(1);\n$listA->printList();\n\n$listB = new SingleLinkedList();\n$listB->insert(10);\n$listB->insert(8);\n$listB->insert(6);\n$listB->insert(4);\n$listB->insert(2);\n$listB->printList();\n\n$listAlgoMerge = new SingleLinkedListAlgo();\n$newList = $listAlgoMerge->mergerSortedList($listA, $listB);\n$newList->printListSimple();\necho '----------------------------------------------------------------'. PHP_EOL . PHP_EOL;\n\necho '---------------------- 删除链表倒数第n个结点 ----------------------' . PHP_EOL . PHP_EOL;\n// 删除链表倒数第n个结点\n$listDelete = new SingleLinkedList();\n$listDelete->insert(1);\n$listDelete->insert(2);\n$listDelete->insert(3);\n$listDelete->insert(4);\n$listDelete->insert(5);\n$listDelete->insert(6);\n$listDelete->insert(7);\n$listDelete->printList();\n$listAlgo->setList($listDelete);\n$listAlgo->deleteLastKth(3);\nvar_dump($listAlgo->list->printListSimple());\necho '------------------------------------------------------------------'. PHP_EOL . PHP_EOL;\n\necho '---------------------- 求链表的中间结点 ----------------------' . PHP_EOL . PHP_EOL;\n// 求链表的中间结点\n$listAlgo->setList($list);\n$middleNode = $listAlgo->findMiddleNode();\nvar_dump($middleNode->data);\necho '-------------------------------------------------------------'. PHP_EOL . PHP_EOL;\n"
  },
  {
    "path": "php/08_stack/.gitkeep",
    "content": ""
  },
  {
    "path": "php/08_stack/Compute.php",
    "content": "<?php\n\n\n\n// 四则运算 +-*/()\nfunction expression($str)\n{\n    $str = str_replace(' ','',$str);\n    $arr = preg_split('/([\\+\\-\\*\\/\\(\\)])/', $str, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);\n\n    $numStack = [];  // 存放数字\n    $operStack = []; // 存放运算符\n    $operStack[] = NULL;\n\n    for ($i = 0; $i < count($arr); $i++){\n        if (ord($arr[$i]) >= 48 && ord($arr[$i]) <= 57){\n            array_push($numStack, $arr[$i]);\n            continue;\n        }\n        switch ($arr[$i]){\n            case '+':\n            case '-':\n                $arrLen = count($operStack);\n                while ($operStack[$arrLen-1] === '*' || $operStack[$arrLen-1] === '/' || $operStack[$arrLen-1] === '-'){\n                    compute($numStack, $operStack);\n                    $arrLen--;\n                }\n                array_push($operStack, $arr[$i]);\n                break;\n            case '*':\n                $arrLen = count($operStack);\n                while ($operStack[$arrLen-1] === '/'){\n                    compute($numStack, $operStack);\n                    $arrLen--;\n                }\n                array_push($operStack, $arr[$i]);\n                break;\n\n            case '/':\n            case '(':\n                array_push($operStack, $arr[$i]);\n                break;\n            case ')':\n                $arrLen = count($operStack);\n                while ($operStack[$arrLen-1] !== '('){\n                    compute($numStack, $operStack);\n                    $arrLen--;\n                }\n                array_pop($operStack);\n                break;\n            default:\n                throw new \\Exception(\"不支持的运算符\", 1);\n                break;\n        }\n    }\n    \n    $arrLen = count($operStack);\n    while ($operStack[$arrLen-1] !== NULL){  \n        compute($numStack, $operStack);\n        $arrLen--;\n    }\n    echo array_pop($numStack);\n}\n\n//数字栈长度减一，运算符栈长度减一\nfunction compute(&$numStack, &$operStack){\n    $num = array_pop($numStack);\n    switch (array_pop($operStack)) {\n        case '*':\n            array_push($numStack, array_pop($numStack) * $num);\n            break;\n        case '/':\n            array_push($numStack, array_pop($numStack) / $num);\n            break;\n        case '+':\n            array_push($numStack, array_pop($numStack) + $num);\n            break;\n        case '-':\n            array_push($numStack, array_pop($numStack) - $num);\n            break;\n        case '(':\n            throw new \\Exception(\"不匹配的(\", 2);\n            break;\n    }\n}\nexpression('-1+2-(1+2*3)');\necho PHP_EOL;\neval('echo -1+2-(1+2*3);');\n"
  },
  {
    "path": "php/08_stack/StackOnLinkedList.php",
    "content": "<?php\n/**\n * User: lide01\n * Date: 2018/10/11 19:37\n * Desc:\n */\n\nnamespace Algo_08;\n\n\nuse Algo_06\\SingleLinkedListNode;\n\nclass StackOnLinkedList\n{\n    /**\n     * 头指针\n     *\n     * @var SingleLinkedListNode\n     */\n    public $head;\n\n    /**\n     * 栈长度\n     *\n     * @var\n     */\n    public $length;\n\n    /**\n     *\n     * StackOnLinkedList constructor.\n     */\n    public function __construct()\n    {\n        $this->head = new SingleLinkedListNode();\n        $this->length = 0;\n    }\n\n    /**\n     * 出栈\n     *\n     * @return bool\n     */\n    public function pop()\n    {\n        if (0 == $this->length) {\n            return false;\n        }\n\n        $this->head->next = $this->head->next->next;\n        $this->length--;\n\n        return true;\n    }\n\n    /**\n     * 入栈\n     *\n     * @param $data\n     *\n     * @return SingleLinkedListNode|bool\n     */\n    public function push($data)\n    {\n        return $this->pushData($data);\n    }\n\n    /**\n     * 入栈 node\n     *\n     * @param SingleLinkedListNode $node\n     *\n     * @return bool\n     */\n    public function pushNode(SingleLinkedListNode $node)\n    {\n        if (null == $node) {\n            return false;\n        }\n\n        $node->next = $this->head->next;\n        $this->head->next = $node;\n\n        $this->length++;\n        return true;\n    }\n\n    /**\n     * 入栈 data\n     *\n     * @param $data\n     *\n     * @return SingleLinkedListNode|bool\n     */\n    public function pushData($data)\n    {\n        $node = new SingleLinkedListNode($data);\n\n        if (!$this->pushNode($node)) {\n            return false;\n        }\n\n        return $node;\n    }\n\n    /**\n     * 获取栈顶元素\n     *\n     * @return SingleLinkedListNode|bool|null\n     */\n    public function top()\n    {\n        if (0 == $this->length) {\n            return false;\n        }\n\n        return $this->head->next;\n    }\n\n    /**\n     * 打印栈\n     */\n    public function printSelf()\n    {\n        if (0 == $this->length) {\n            echo 'empty stack' . PHP_EOL;\n            return;\n        }\n\n        echo 'head.next -> ';\n        $curNode = $this->head;\n        while ($curNode->next) {\n            echo $curNode->next->data . ' -> ';\n\n            $curNode = $curNode->next;\n        }\n        echo 'NULL' . PHP_EOL;\n    }\n\n    /**\n     * 获取栈长度\n     *\n     * @return int\n     */\n    public function getLength()\n    {\n        return $this->length;\n    }\n\n    /**\n     * 判断栈是否为空\n     *\n     * @return bool\n     */\n    public function isEmpty()\n    {\n        return $this->length > 0 ? false : true;\n    }\n}"
  },
  {
    "path": "php/08_stack/main.php",
    "content": "<?php\n/**\n * User: lide01\n * Date: 2018/10/11 20:01\n * Desc:\n */\n\nnamespace Algo_08;\n\nrequire_once '../vendor/autoload.php';\n\n$stack = new StackOnLinkedList();\n$stack->pushData(1);\n$stack->pushData(2);\n$stack->pushData(3);\n$stack->pushData(4);\nvar_dump($stack->getLength());\n$stack->printSelf();\n\n$topNode = $stack->top();\nvar_dump($topNode->data);\n\n$stack->pop();\n$stack->printSelf();\n$stack->pop();\n$stack->printSelf();\n\nvar_dump($stack->getLength());\n\n$stack->pop();\n$stack->pop();\n$stack->printSelf();"
  },
  {
    "path": "php/09_queue/QueueOnLinkedList.php",
    "content": "<?php\n/**\n * User: Hkesd\n * Date: 2018/10/13 11:26\n * Desc:\n */\n\nnamespace Algo_09;\n\nuse Algo_06\\SingleLinkedListNode;\n\n/**\n * 队列 链表实现\n *\n * Class QueueOnLinkedList\n */\nclass QueueOnLinkedList\n{\n    /**\n     * 队列头节点\n     *\n     * @var SingleLinkedListNode\n     */\n    public $head;\n\n    /**\n     * 队列尾节点\n     *\n     * @var null\n     */\n    public $tail;\n\n    /**\n     * 队列长度\n     *\n     * @var int\n     */\n    public $length;\n\n    /**\n     * QueueOnLinkedList constructor.\n     */\n    public function __construct()\n    {\n        $this->head = new SingleLinkedListNode();\n        $this->tail = $this->head;\n\n        $this->length = 0;\n    }\n\n    /**\n     * 入队\n     *\n     * @param $data\n     */\n    public function enqueue($data)\n    {\n        $newNode = new SingleLinkedListNode();\n        $newNode->data = $data;\n\n        $this->tail->next = $newNode;\n        $this->tail = $newNode;\n\n        $this->length++;\n    }\n\n    /**\n     * 出队\n     *\n     * @return SingleLinkedListNode|bool|null\n     */\n    public function dequeue()\n    {\n        if (0 == $this->length) {\n            return false;\n        }\n\n        $node = $this->head->next;\n        $this->head->next = $this->head->next->next;\n\n        $this->length--;\n\n        return $node;\n    }\n\n    /**\n     * 获取队列长度\n     *\n     * @return int\n     */\n    public function getLength()\n    {\n        return $this->length;\n    }\n\n    /**\n     * 打印队列\n     */\n    public function printSelf()\n    {\n        if (0 == $this->length) {\n            echo 'empty queue' . PHP_EOL;\n            return;\n        }\n\n        echo 'head.next -> ';\n        $curNode = $this->head;\n        while ($curNode->next) {\n            echo $curNode->next->data . ' -> ';\n\n            $curNode = $curNode->next;\n        }\n        echo 'NULL' . PHP_EOL;\n    }\n}"
  },
  {
    "path": "php/09_queue/Sequential.php",
    "content": "<?php\n\nclass LoopQueue\n{   \n    private $MaxSzie;\n    private $data = [];\n    private $head = 0;\n    private $tail = 0;\n\n    /**\n     * 初始化队列大小 最后的位置不存放数据,实际大小 = size++\n     */\n    public function __construct($size = 10)\n    {\n        $this->MaxSzie = ++$size;\n    }\n\n    /**\n     * 队列满条件  ($this->tail+1) % $this->MaxSzie == $this->head\n     */\n    public function enQueue($data)\n    {\n        if (($this->tail+1) % $this->MaxSzie == $this->head)\n            return -1;\n\n        $this->data[$this->tail] = $data;\n        $this->tail = (++$this->tail) % $this->MaxSzie;\n    }\n\n    public function deQueue()\n    {\n        if ($this->head == $this->tail)\n            return NULL;\n        \n        $data = $this->data[$this->head];\n        unset($this->data[$this->head]);\n        $this->head = (++$this->head) % $this->MaxSzie;\n        return $data;\n    }\n\n    public function getLength()\n    {\n        return ($this->tail - $this->head + $this->MaxSzie) % $this->MaxSzie;\n    }\n}\n\n$queue = new LoopQueue(4);\n// var_dump($queue);\n$queue->enQueue(1);\n$queue->enQueue(2);\n$queue->enQueue(3);\n$queue->enQueue(4);\n// $queue->enQueue(5);\nvar_dump($queue->getLength());\n$queue->deQueue();\n$queue->deQueue();\n$queue->deQueue();\n$queue->deQueue();\n$queue->deQueue();\nvar_dump($queue);\n"
  },
  {
    "path": "php/09_queue/main.php",
    "content": "<?php\n/**\n * User: Hkesd\n * Date: 2018/10/13 11:46\n * Desc:\n */\n\nnamespace Algo_09;\n\nrequire_once \"../vendor/autoload.php\";\n\n$queue = new QueueOnLinkedList();\n$queue->enqueue(1);\n$queue->enqueue(2);\n$queue->enqueue(3);\n$queue->enqueue(4);\n$queue->enqueue(5);\n$queue->printSelf();\nvar_dump($queue->getLength());\n\n$queue->dequeue();\n$queue->printSelf();\n$queue->dequeue();\n$queue->dequeue();\n$queue->dequeue();\n$queue->printSelf();\n\n$queue->dequeue();\n$queue->printSelf();\n"
  },
  {
    "path": "php/10_heap/Heap.php",
    "content": "<?php\n/**\n * Created by PhpStorm.\n * User: 764432054@qq.com\n * Date: 2019/9/5\n * Time: 9:01\n * 堆的基本操作(大顶堆，小顶堆 几种堆化方式，堆排序，动态数据流查top k ,查中位数)\n *\n */\nnamespace Algo_10;\n\nclass Heap\n{\n    public $dataArr = [];\n    public $count = 0;\n    public $size; //堆的大小 0表示不限制大小自动扩容\n    public $heapType = 1; //1 表示大根堆  0表示小根堆\n\n    public function __construct($size = 0, $heapType = 1)\n    {\n        $this->size = $size;\n        $this->heapType = $heapType;\n    }\n\n    /**\n     * @param $data\n     * 插入并堆化\n     */\n    public function insert($data)\n    {\n        if ($this->isFull()) {\n            return false;\n        }\n        $this->dataArr[$this->count + 1] = $data;\n        $this->count++;\n        if ($this->heapType) {\n            $this->bigHeapLast();\n        } else {\n            $this->smallHeapLast();\n        }\n    }\n\n    /**\n     * @return bool\n     * 堆是否满\n     */\n    public function isFull()\n    {\n        if ($this->size == 0) {\n            return false;\n        }\n        if ($this->count >= $this->size) {\n            return true;\n        }\n        return false;\n    }\n    public function isEmpty(){\n        return empty($this->count)?true:false;\n    }\n    //返回堆顶的元素\n    public function peak(){\n        if($this->isEmpty()){\n            return null;\n        }\n        return $this->dataArr[1];\n    }\n\n\n    //只插入\n    public function insertOnly($data)\n    {\n        if ($this->isFull()) {\n            return false;\n        }\n        $this->dataArr[$this->count + 1] = $data;\n        $this->count++;\n    }\n\n\n    /**\n     * 删除堆顶的元素\n     * 把最后1个元素插入到堆顶\n     * 然后从堆顶开始堆化\n     * 返回堆化后的堆顶元素\n     */\n    public function deleteFirst()\n    {\n        $first = $this->dataArr[1];\n        $last = array_pop($this->dataArr);\n        if($this->isEmpty()){\n            return null;\n        }\n        $this->count--;\n        $i = 1;\n        $this->dataArr[$i] = $last;\n        if ($this->heapType) {\n            $this->bigHeapFirst();\n        } else {\n            $this->smallHeapFirst();\n        }\n        return $first;\n\n    }\n\n    /**\n     * 从某一个结点开始向下堆化\n     */\n    protected function heapFromOneToDown($i)\n    {\n        //大根堆\n        if ($this->heapType) {\n            $maxPos = $i;\n            while (true) {\n                if (2 * $i <= $this->count) {\n                    if ($this->dataArr[$maxPos] < $this->dataArr[2 * $i]) {\n                        $maxPos = 2 * $i;\n                    }\n                }\n                if (2 * $i + 1 <= $this->count) {\n                    if ($this->dataArr[$maxPos] < $this->dataArr[2 * $i + 1]) {\n                        $maxPos = 2 * $i + 1;\n                    }\n                }\n                //不需要交换\n                if ($i == $maxPos) {\n                    break;\n                }\n                $tmp = $this->dataArr[$maxPos];\n                $this->dataArr[$maxPos] = $this->dataArr[$i];\n                $this->dataArr[$i] = $tmp;\n                //继续往下堆化\n                $i = $maxPos;\n\n            }\n        } else {\n            //小根堆\n            $minPos = $i;\n            while (true) {\n                if (2 * $i <= $this->count) {\n                    if ($this->dataArr[$minPos] > $this->dataArr[2 * $i]) {\n                        $minPos = 2 * $i;\n                    }\n                }\n                if (2 * $i + 1 <= $this->count) {\n                    if ($this->dataArr[$minPos] > $this->dataArr[2 * $i + 1]) {\n                        $minPos = 2 * $i + 1;\n                    }\n                }\n                //不需要交换\n                if ($i == $minPos) {\n                    break;\n                }\n                $tmp = $this->dataArr[$minPos];\n                $this->dataArr[$minPos] = $this->dataArr[$i];\n                $this->dataArr[$i] = $tmp;\n                //继续往下堆化\n                $i = $minPos;\n\n            }\n        }\n\n    }\n\n\n    /**\n     * 对于1个完全不符合堆性质的 整体堆化\n     */\n    public function heapAll()\n    {\n        for ($i = intval($this->count / 2); $i >= 1; $i--) {\n            $this->heapFromOneToDown($i);\n        }\n    }\n\n    /**\n     * 堆排序\n     * 把堆顶部的元素和数组尾部元素交换\n     */\n    public function heapSort()\n    {\n        $sorted = 0;//已经有序的个数\n        while ($sorted < $this->count) {\n            $i = 1;\n            $head = $this->dataArr[$i];\n            $this->dataArr[$i] = $this->dataArr[$this->count - $sorted];\n            $this->dataArr[$this->count - $sorted] = $head;\n            $sorted++;\n\n            while (true) {\n                $maxPos = $i;\n                if (2 * $i <= $this->count - $sorted && $this->dataArr[$maxPos] < $this->dataArr[2 * $i]) {\n                    $maxPos = 2 * $i;\n                }\n                if (2 * $i + 1 <= $this->count - $sorted && $this->dataArr[$maxPos] < $this->dataArr[2 * $i + 1]) {\n                    $maxPos = 2 * $i + 1;\n                }\n                if ($i == $maxPos) {\n                    break;\n                }\n                $tmp = $this->dataArr[$i];\n                $this->dataArr[$i] = $this->dataArr[$maxPos];\n                $this->dataArr[$maxPos] = $tmp;\n                $i = $maxPos;\n            }\n        }\n\n    }\n\n    /**\n     *小顶堆 堆化\n     * 插入时\n     * 堆化最后1个元素\n     */\n    public function smallHeapLast()\n    {\n        $i = $this->count;\n        while (true) {\n            $smallPos = $i;\n            $parent = intval($i / 2);\n            if ($parent >= 1) {\n                if ($this->dataArr[$smallPos] < $this->dataArr[$parent]) {\n                    $smallPos = $parent;\n                }\n            }\n            if ($smallPos == $i) {\n                break;\n            }\n            $tmp = $this->dataArr[$smallPos];\n            $this->dataArr[$smallPos] = $this->dataArr[$i];\n            $this->dataArr[$i] = $tmp;\n            $i = $smallPos;\n        }\n    }\n\n    /**\n     * 小根堆\n     * 堆化根部元素(第一个元素)\n     */\n    public function smallHeapFirst()\n    {\n        $i = 1;\n        while (true) {\n            $smallpos = $i;\n            $left = 2 * $i;\n            if ($left <= $this->count) {\n                if ($this->dataArr[$smallpos] > $this->dataArr[$left]) {\n                    $smallpos = $left;\n                }\n            }\n            $right = $left + 1;\n            if ($right <= $this->count) {\n                if ($this->dataArr[$smallpos] > $this->dataArr[$right]) {\n                    $smallpos = $right;\n                }\n            }\n            if ($smallpos == $i) {\n                break;\n            }\n            $tmp = $this->dataArr[$i];\n            $this->dataArr[$i] = $this->dataArr[$smallpos];\n            $this->dataArr[$smallpos] = $tmp;\n            $i = $smallpos;\n        }\n\n    }\n\n    /**\n     * 大根堆\n     * 堆化根部元素(第一个元素)\n     */\n    public function bigHeapFirst()\n    {\n        $i = 1;\n        while (true) {\n            $maxpos = $i;\n            $left = 2 * $i;\n            if ($left <= $this->count) {\n                if ($this->dataArr[$maxpos] < $this->dataArr[$left]) {\n                    $maxpos = $left;\n                }\n            }\n            $right = $left + 1;\n            if ($right <= $this->count) {\n                if ($this->dataArr[$maxpos] < $this->dataArr[$right]) {\n                    $maxpos = $right;\n                }\n            }\n            if ($maxpos == $i) {\n                break;\n            }\n            $tmp = $this->dataArr[$i];\n            $this->dataArr[$i] = $this->dataArr[$maxpos];\n            $this->dataArr[$maxpos] = $tmp;\n            $i = $maxpos;\n        }\n\n    }\n    //大根堆，  插入节点后放到数组最后面，然后从插入的节点自下而上开始堆化\n    //这里只堆化插入元素相关的节点(就是说，如果没插入这个元素，这个是一个堆)\n    public function bigHeapLast()\n    {\n        $i = $this->count;\n        while (intval($i / 2) > 0 && $this->dataArr[$i] > $this->dataArr[intval($i / 2)]) {\n            $tmp = $this->dataArr[$i];\n            $this->dataArr[$i] = $this->dataArr[intval($i / 2)];\n            $this->dataArr[intval($i / 2)] = $tmp;\n            $i = $i / 2;\n        }\n    }\n\n    /**\n     * @param $data\n     */\n    public function topn($data)\n    {\n        //堆满了\n        if ($this->isFull()) {\n            if ($data > $this->dataArr[1]) {\n                $this->dataArr[1] = $data;\n                $this->smallHeapFirst();\n            }\n        } else {\n            $this->dataArr[$this->count + 1] = $data;\n            $this->count++;\n            $this->smallHeapLast();\n\n        }\n        return $this->dataArr[1];\n\n    }\n\n\n\n}\n\n\n\n\n"
  },
  {
    "path": "php/10_heap/findmiddle.php",
    "content": "<?php\n/**\n * Created by PhpStorm.\n * User: 764432054@qq.com\n * 动态数据流实时获取中位数\n */\nnamespace Algo_10;\n\nrequire_once '../vendor/autoload.php';\n\n$arr = [9, 8, 11, 4, 2, 6, 5, 1, -1, 3, 20, 10];\n//$arr=[9,8,11,4,2,6,5,100];\n\nfindMiddle($arr);\n\n//动态数据实时获取中位数\nfunction findMiddle($arr)\n{\n    //大顶堆\n    $bigHeap = new Heap(0, 1);\n    //小顶堆\n    $smallHeap = new Heap(0, 0);\n\n    foreach ($arr as $k => $v) {\n        if ($bigHeap->isEmpty()) {\n            $bigHeap->insert($v);\n        } else {\n            $bigPeak = $bigHeap->peak();\n            if ($v < $bigPeak) {\n                $bigHeap->insert($v);\n            } else {\n                $smallHeap->insert($v);\n            }\n\n            if ($bigHeap->count - $smallHeap->count > 1) {\n                $bigPeak = $bigHeap->deleteFirst();\n                $smallHeap->insert($bigPeak);\n            } elseif ($smallHeap->count - $bigHeap->count > 1) {\n                $smallPeak = $smallHeap->deleteFirst();\n                $bigHeap->insert($smallPeak);\n            }\n\n        }\n        //实时获取中位数\n        echo \"现在的中位数为:\".implode(',', midPeak($bigHeap, $smallHeap)) . PHP_EOL;\n    }\n\n\n}\n\nfunction midPeak($heap1, $heap2)\n{\n    if ($heap1->count == $heap2->count) {\n        $midArr = [$heap1->peak(), $heap2->peak()];\n    } elseif ($heap2->count > $heap1->count) {\n        $midArr = [$heap2->peak()];\n    } else {\n        $midArr = [$heap1->peak()];\n    }\n    return $midArr;\n}\n"
  },
  {
    "path": "php/10_heap/main.php",
    "content": "<?php\n/**\n * Created by PhpStorm.\n * User: 764432054@qq.com\n * 堆的基本操作\n */\n\nnamespace Algo_10;\n\nrequire_once '../vendor/autoload.php';\n$arr=[50,3,60,70,45,20,100,0,58];\n\n$heap=new Heap();\nforeach ($arr as $v){\n    $heap->insert($v);\n}\n\nwhile(($r=$heap->deleteFirst())!==null){\n    echo $r.\" \";\n}\necho PHP_EOL;\n\n$heap1=new Heap(10);\n\nforeach ($arr as $v){\n    $heap1->insertOnly($v);\n}\n\n\n\n$heap1->heapAll();\n//堆化后的\nprint_r($heap1->dataArr);\n//堆排序\n$heap1->heapSort();\nprint_r($heap1->dataArr);\n"
  },
  {
    "path": "php/10_heap/topn.php",
    "content": "<?php\n/**\n * Created by PhpStorm.\n * User: 764432054@qq.com\n\n *动态数据集合求top n\n */\nnamespace Algo_10;\n\nrequire_once '../vendor/autoload.php';\n\n$static_data=[2,5,3,1,0,7,6,10];\n\n\n//第3大\n/*\n2,5,3               2\n2,5,3 1             2\n2,5,3,1,0           2\n2,5,3,1,0,7         3\n2,5,3,1,0,7,6       5\n2,5,3,1,0,7,6,10    6\n\n维持1个小顶堆 大小为3即可\n*/\n$heap=new Heap(3);\nforeach ($static_data as $v){\n    echo \"现在的第3大=>\".$heap->topn($v).PHP_EOL;\n}\n"
  },
  {
    "path": "php/11_sort/Sort.php",
    "content": "<?php\n// 冒泡排序\nfunction bubbleSort(&$arr)\n{\n    $length = count($arr);\n    if ($length <= 1) return;\n\n    for ($i = 0; $i < $length; $i++) {\n        $flag = false;\n        for ($j = 0; $j < $length - $i - 1; $j++) {\n            if ($arr[$j] > $arr[$j + 1]) {\n                $tmp = $arr[$j];\n                $arr[$j] = $arr[$j + 1];\n                $arr[$j + 1] = $tmp;\n                $flag = true;\n            }\n        }\n        if (!$flag) {\n            break;\n        }\n    }\n}\n\n// 插入排序\nfunction insertionSort(&$arr)\n{\n    $n = count($arr);\n    if ($n <= 1) return;\n\n    for ($i = 1; $i < $n; ++$i) {\n        $value = $arr[$i];\n        $j = $i - 1;\n        // 查找插入的位置\n        for (; $j >= 0; --$j) {\n            if ($arr[$j] > $value) {\n                $arr[$j + 1] = $arr[$j];  // 数据移动\n            } else {\n                break;\n            }\n        }\n        $arr[$j + 1] = $value; // 插入数据\n    }\n}\n\n// 选择排序\nfunction selectionSort(&$arr)\n{\n    $length = count($arr);\n    if ($length <= 1) return;\n\n    for ($i = 0; $i < $length - 1; $i++) {\n        //先假设最小的值的位置\n        $p = $i;\n        for ($j = $i + 1; $j < $length; $j++) {\n            if ($arr[$p] > $arr[$j]) {\n                $p = $j;\n            }\n        }\n        $tmp = $arr[$p];\n        $arr[$p] = $arr[$i];\n        $arr[$i] = $tmp;\n    }\n}\n\n$arr = [1,4,6,2,3,5,4];\ninsertionSort($arr);\nvar_dump($arr);\n"
  },
  {
    "path": "php/12_sort/mergeSort.php",
    "content": "<?php\n/*\n * 归并排序\n */\n\n$arr = [4, 5, 6, 1, 3, 2];\n$length = count($arr);\n\n$p = 0;\n$r = $length - 1;\n\n$result = mergeSort($arr, $p, $r);\n\nvar_dump($result);\n\n\nfunction mergeSort(array $arr, $p, $r)\n{\n    return mergeSortRecursive($arr, $p, $r);\n}\n\n// 递归调用函数\nfunction mergeSortRecursive(array $arr, $p, $r)\n{\n    // 递归终止条件\n    if ($p >= $r) {\n        return [$arr[$r]];\n    }\n\n    // 取 p 到 r 之间的中间位置 q\n    $q = (int)(($p + $r) / 2);\n\n    // 分治递归\n    $left = mergeSortRecursive($arr, $p, $q);\n    $right = mergeSortRecursive($arr, $q + 1, $r);\n    return merge($left, $right);\n}\n\n// 合并\nfunction merge(array $left, array $right)\n{\n    $tmp = [];\n    $i = $j = 0;\n\n    $leftLength = count($left);\n    $rightLength = count($right);\n\n    do {\n        if ($left[$i] <= $right[$j]) {\n            $tmp[] = $left[$i++];\n        } else {\n            $tmp[] = $right[$j++];\n        }\n    } while ($i < $leftLength && $j < $rightLength);\n\n    $start = $i;\n    $end = $leftLength;\n    $copyArr = $left;\n\n    // 判断哪个子数组中有剩余的数据\n    if ($j < $rightLength) {\n        $start = $j;\n        $end = $rightLength;\n        $copyArr = $right;\n    }\n\n    // 将剩余的数据拷贝到临时数组 tmp\n    do {\n        $tmp[] = $copyArr[$start++];\n    } while ($start < $end);\n\n    return $tmp;\n}"
  },
  {
    "path": "php/12_sort/quicksort.php",
    "content": "<?php\n\nfunction quickSort(array &$a)\n{\n    $n = count($a);\n\n    quickSortInternally($a, 0, $n-1);\n}\n\nfunction quickSortInternally(array &$a, int $l, int $r)\n{\n    if ($l >= $r) return;\n\n    $q = partition($a, $l, $r);\n    quickSortInternally($a, $l, $q-1);\n    quickSortInternally($a, $q+1, $r);\n}\n\nfunction partition(&$a, $l, $r): int\n{\n    $pivot = $a[$r];\n    $i = $l;\n\n    for ($j = $l; $j < $r; ++$j) {\n        if ($a[$j] < $pivot) {\n            [$a[$j], $a[$i]] = [$a[$i], $a[$j]];\n            ++$i;\n      }\n    }\n\n    [$a[$r], $a[$i]] = [$a[$i], $a[$r]];\n\n    return $i;\n}\n\n$a1 = [1,4,6,2,3,5,4];\n$a2 = [2, 2, 2, 2];\n$a3 = [4, 3, 2, 1];\n$a4 = [5, -1, 9, 3, 7, 8, 3, -2, 9];\nquickSort($a1);\nprint_r($a1);\nquickSort($a2);\nprint_r($a2);\nquickSort($a3);\nprint_r($a3);\nquickSort($a4);\nprint_r($a4);"
  },
  {
    "path": "php/13_sort/bucketSort.php",
    "content": "<?php\n\nrequire_once '../12_sort/quickSort.php';\n\n\n$numbers = [11,23,45,67,88,99,22,34,56,78,90,12,34,5,6,91,92,93,93,94,95,94,95,96,97,98,99,100];\n$size = 10;\nvar_dump(bucketSort($numbers));//加载了quickSort文件，请忽略前几个打印\n\n\n/**\n * 桶排序\n * 假设一个桶只能放置10个元素\n * 当一个桶内元素过多，需要继续分桶\n * @param array $numbers\n * @param [type] $size\n *\n * @return void\n * @date 2018/11/25\n * @author yuanliandu \n */\nfunction bucketSort(array $numbers) {\n    $min = min($numbers);\n    $max = max($numbers);\n    $length = count($numbers);\n    $bucketNumber =  ceil(($max-$min)/$length) + 1;\n    $buckets = [];\n    foreach($numbers as $key => $value) {\n        $index = ceil(($value-$min)/$length);\n        $buckets[$index][] = $value; \n    }\n    \n    $result = [];\n    for($i=0;$i<$bucketNumber;$i++) {\n        $bucket = $buckets[$i];\n        $length = count($bucket);\n        //如果桶内元素为空，跳过这个桶\n        if($length == 0) {\n            continue;\n        }\n        if( $length > 10) {\n            $bucket = bucketSort($bucket);\n        }\n\n       quickSort($bucket,0,count($bucket)-1);\n       $result = array_merge($result,$bucket);\n   }\n   return $result;\n}\n\n"
  },
  {
    "path": "php/13_sort/countingSort.php",
    "content": "<?php\n\n/**\n * 计数排序\n * 五分制\n * 13个人\n */\n$score = [0,1,5,3,2,4,1,2,4,2,1,4,4];\n\nvar_dump(countingSort($score));die();\nfunction countingSort(array $score) {\n\n    $length = count($score);\n    if($length <= 1) {return $score;}\n    \n    /**\n     * 统计每个分数的人数\n     */\n    $temp = [];\n    $countScore = [];\n    foreach ($score as $key => $value) {\n        $countScore[$value]++;\n    }\n    \n    /**\n     * 顺序求和\n     */\n    for($i=1;$i<=5;$i++) {\n        $countScore[$i] += $countScore[$i-1];\n    }\n    /**\n     * 排序\n     */\n    foreach ($score as $key => $value) {\n        $countScore[$value] --;\n        $temp[$countScore[$value]] = $value;\n    }\n    //copy\n    for($i=0;$i<$length;$i++) {\n        $score[$i] = $temp[$i];\n    }\n    return $score;\n}"
  },
  {
    "path": "php/13_sort/radixSort.php",
    "content": "<?php\n\n/**\n * 基数排序\n * 先根据个位排序、百位、千位........\n */\n$numbers = [\n    1234,\n    4321,\n    12,\n    31,\n    412,\n];\n$max = (string) max($numbers);//求出最大数字\n$loop =  strlen($max);//计算最大数字的长度，决定循环次数\n\nfor($i=0;$i<$loop;$i++) {\n    radixSort($numbers,$i);\n}\nvar_dump($numbers);\n\n/**\n * 基数排序\n * @param array $numbers\n * @param [type] $loop\n *\n * @return void\n * @date 2018/11/26\n * @author yuanliandu \n */\nfunction radixSort(array &$numbers,$loop) {\n\n    $divisor = pow(10,$loop);//除数  主要决定比较个位数、百位.....\n    $buckets = (new \\SplFixedArray(10))->toArray();\n    foreach ($numbers as $key => $value) {\n        $index = ($value/$divisor)%10;//计算该数字在哪个桶中\n        $buckets[$index][] = $value;\n    }\n    /**\n     * 从桶中取出数字\n     */\n    $k=0;\n    for($i=0;$i<10;$i++) {\n        while(count($buckets[$i]) > 0) {\n            $numbers[$k++] = array_shift($buckets[$i]);\n        }\n    }\n}\n"
  },
  {
    "path": "php/15_binary/binary.php",
    "content": "<?php\n\n\n/**\n * 二分查找 查找=find的元素\n * @param array $numbers\n * @param [type] $find\n *\n * @return void\n * @date 2018/11/26\n * @author yuanliandu \n */\nfunction binarySearch(array $numbers, $find)\n{\n    $low = 0;\n    $high = count($numbers) - 1;\n    return search($numbers, $low, $high, $find);\n}\n\nfunction search(array $numbers, $low, $high, $find)\n{\n    /**\n     * notice1 循环退出条件\n     */\n    if ($low > $high) {\n        return -1;\n    }\n\n    /**\n     * notice2 mid计算\n     */\n    $mid = $low + (($high - $low) >> 1);\n    if ($numbers[$mid] > $find) {\n        //notice3 high值更新\n        return search($numbers, $low, $mid -1, $find);\n    } elseif ($numbers[$mid] < $find) {\n        //notice4 low值更新\n        return search($numbers, $mid + 1, $high, $find);\n    } else {\n        return $mid;\n    }\n}\n\n/**\n * 求数字的平方根，保留6位小数\n * @param [type] $number\n *\n * @return void\n * @date 2018/11/26\n * @author yuanliandu \n */\nfunction squareRoot($number)\n{\n    if ($number < 0) {\n        return  -1;\n    } elseif ($number < 1) {\n        $min = $number;\n        $max = 1;\n    } else {\n        $min = 1;\n        $max = $number;\n    }\n    $mid = $min + ($max - $min) / 2;\n    while (getDecimalPlaces($mid) < 6) {\n        $square = $mid * $mid;\n        if ($square > $number) {\n            $max = $mid;\n        } elseif ($square == $number) {\n            return $mid;\n        } else {\n            $min = $mid;\n        }\n        $mid = $min + ($max - $min) / 2;\n    }\n    return $mid;\n}\n\n/**\n * 计算数字小数点后有几位数字\n * @param [type] $number\n *\n * @return void\n * @date 2018/11/27\n * @author yuanliandu <yuanliandu@qq.com>\n */\nfunction getDecimalPlaces($number)\n{\n    $temp = explode('.', $number);\n    if (isset($temp[1])) {\n        return strlen($temp[1]);\n    }\n\n    return 0;\n}\n\n// 测试二分查找给定值\n$numbers = [0, 1, 2, 3, 3, 4, 5, 6, 7, 9];\n$find = 1;\nvar_dump(binarySearch($numbers,$find));\n\n//测试求平方根\nvar_dump(squareRoot(3));"
  },
  {
    "path": "php/16_binary/binary.php",
    "content": "<?php\n\n/**\n * 找到第一个=value的元素\n * @param array $numbers\n *\n * @return void\n * @date 2018/11/27\n * @author yuanliandu <yuanliandu@qq.com>\n */\nfunction findFirstEqual(array $numbers,$find) {\n    $length = count($numbers);\n    $low = 0;\n    $high = $length - 1;\n    while($low <= $high) {\n        $mid = $low + (($high-$low)>>1);\n        if($numbers[$mid] > $find) {\n            $high = $mid - 1;\n        }else if($numbers[$mid] < $find) {\n            $low = $mid + 1;\n        }else {\n            /**\n             * 如果是第一个元素，或之前一个元素不等于我们要找的值\n             * 我们就找到了第一个=find的element\n             */\n            if($mid==0 || $numbers[$mid-1]!=$find) {\n                return $mid;\n            }else {\n                $high = $mid - 1;\n            }\n        }\n    }\n\n    return -1;\n}\n\n/**\n * 找到最后一个=find的元素\n * @param array $numbers\n * @param [type] $find\n *\n * @return void\n * @date 2018/11/27\n * @author yuanliandu <yuanliandu@qq.com>\n */\nfunction findLastEqual(array $numbers,$find) {\n    $length = count($numbers);\n    $low = 0;\n    $high = $length - 1;\n    while($low <= $high) {\n        $mid = $low + (($high-$low)>>1);\n        if($numbers[$mid] > $find) {\n            $high = $mid - 1;\n        }else if($numbers[$mid] < $find) {\n            $low = $mid + 1;\n        }else {\n            /**\n             * 如果mid是最后一个元素的index\n             * 或mid后一个元素!=我们要找的值\n             * 则找到了最后一个=find的value\n             */\n            if($mid==$length-1 || $numbers[$mid+1]!=$find) {\n                return $mid;\n            }else {\n                $low = $mid + 1;\n            }\n        }\n    }\n\n    return -1;\n}\n\n/**\n * 找到第一个大于等于find的元素\n * @param array $numbers\n * @param [type] $find\n *\n * @return void\n * @date 2018/11/27\n * @author yuanliandu <yuanliandu@qq.com>\n */\nfunction findFirstGreaterEqual(array $numbers,$find) {\n    $length = count($numbers);\n    $low = 0;\n    $high = $length - 1;\n    while($low <= $high) {\n        $mid = $low + (($high-$low)>>1);\n        if($numbers[$mid] >= $find) {\n            if ($mid == 0 || $numbers[$mid-1] < $find) {\n                return $mid;\n            }else {\n                $high = $mid - 1;\n            }\n        }else  {\n            $low  = $mid + 1;\n        }\n    }\n    return -1;\n}\n\n/**\n * 找到最后一个小于等于find的元素\n * @param array $numbers\n * @param [type] $find\n *\n * @return void\n * @date 2018/11/27\n * @author yuanliandu <yuanliandu@qq.com>\n */\nfunction findLastLessEqual(array $numbers,$find) {\n    $length = count($numbers);\n    $low = 0;\n    $high = $length - 1;\n    while($low <= $high) {\n        $mid = $low + (($high-$low)>>1);\n        if($numbers[$mid] <= $find) {\n           if($mid==$length-1 || $numbers[$mid+1]> $find) {\n               return $mid;\n           }\n           $low = $mid + 1;\n        }else  {\n            $high = $mid - 1;\n        }\n    }\n    return -1;\n}\n\n\n\n\n\n/**\n * 循环数组中找指定元素\n * @param array $numbers\n * @param [type] $find\n *\n * @return void\n * @date 2018/11/27\n * @author yuanliandu <yuanliandu@qq.com>\n */\nfunction searchCircularArray(array $numbers,$find) {\n    $length = count($numbers);\n    $low = 0;\n    $high = $length - 1;\n\n    while($low <= $high) {\n        $mid = $low + (($high-$low)>>1);\n        if($numbers[$mid] === $find) {\n            return $mid;\n        }\n\n        if($numbers[$low] > $numbers[$mid]) {\n            // 后半部分是有序数组\n           if(($numbers[$mid] < $find) && ($numbers[$high] >= $find)) {\n               if($numbers[$high] === $find)  return $high;\n                //在后半个区间内\n                $low = $mid + 1;\n           }else {\n               $high = $mid - 1;\n           }\n        }else {\n           // 前半部分是有序的\n           if(($numbers[$low] <= $find) && ($numbers[$mid] > $find)) {\n               // 在有序区间内\n               if($numbers[$low] === $find)  return $low;\n               $high = $mid - 1;\n           }else {\n               $low = $mid + 1;\n           }\n        }\n        \n    }\n    return -1;\n}\n\n/***\n * 测试\n */\n$numbers = [1,2,3,3,3,4,5,6,8,11,13];\n$find = 3;\n\nvar_dump(findFirstEqual($numbers,$find));//找到第一个等于find的元素\nvar_dump(findFirstGreaterEqual($numbers,$find));//找到第一个大于等于find的元素\nvar_dump(findLastEqual($numbers,$find));//找到最后一个=find的元素\nvar_dump(findLastLessEqual($numbers,$find));//找到最后一个小于等于find的元素\n\n\n//测试在循环数组中找到指定数字\n$numbers = [9,10,1,2,3,4,5,6,7,8];\n$find = 2;\nvar_dump(searchCircularArray($numbers,$find));\n"
  },
  {
    "path": "php/17_skiplist/skipList.php",
    "content": "<?php\r\n\r\nclass SNode\r\n{\r\n    //数据域\r\n    public $data;\r\n\r\n    //指针域，引用SNode对象\r\n    public $next = [];\r\n\r\n    public function __construct($data = null)\r\n    {\r\n        $this->data = $data;\r\n    }\r\n\r\n    //获取当前节点索引层数\r\n    public function getMaxLevel()\r\n    {\r\n        return count($this->next) - 1;\r\n    }\r\n}\r\n\r\nclass SkipList\r\n{\r\n    //索引最大层数\r\n    public $indexLevel;\r\n\r\n    //头节点\r\n    protected $head;\r\n\r\n    public function __construct(int $indexLevel)\r\n    {\r\n        $this->indexLevel = max($indexLevel, 0);\r\n        $this->head = new SNode();\r\n    }\r\n\r\n    public function addData($data)\r\n    {\r\n        $newNode = new SNode($data);\r\n        for ($level = $this->getRandomLevel(), $node = $this->head; $level >= 0; $level--) {\r\n            while (isset($node->next[$level]) && $data < $node->next[$level]->data) {\r\n                $node = $node->next[$level];\r\n            }\r\n            if (isset($node->next[$level])) {\r\n                $newNode->next[$level] = $node->next[$level];\r\n            }\r\n            $node->next[$level] = $newNode;\r\n        }\r\n        return $newNode;\r\n    }\r\n\r\n    public function deleteData($data)\r\n    {\r\n        $deleted = false;\r\n        for ($level = $this->head->getMaxLevel(), $node = $this->head; $level >= 0; $level--) {\r\n            while (isset($node->next[$level]) && $data < $node->next[$level]->data) {\r\n                $node = $node->next[$level];\r\n            }\r\n            if (isset($node->next[$level]) && $data == $node->next[$level]->data) {\r\n                $node->next[$level] = isset($node->next[$level]->next[$level]) ?\r\n                    $node->next[$level]->next[$level] : null;\r\n                $deleted = true;\r\n            }\r\n        }\r\n        return $deleted;\r\n    }\r\n\r\n    public function findData($data)\r\n    {\r\n        for ($level = $this->head->getMaxLevel(), $node = $this->head; $level >= 0; $level--) {\r\n            while (isset($node->next[$level]) && $data < $node->next[$level]->data) {\r\n                $node = $node->next[$level];\r\n            }\r\n            if (isset($node->next[$level]) && $data == $node->next[$level]->data) {\r\n                return $node->next[$level];\r\n            }\r\n        }\r\n        return false;\r\n    }\r\n\r\n    protected function getRandomLevel()\r\n    {\r\n        return mt_rand(0, $this->indexLevel);\r\n    }\r\n}\r\n\r\n/**\r\n * 示例\r\n */\r\n\r\n$indexLevel = 2;\r\n\r\n$skipList = new SkipList($indexLevel);\r\n\r\nfor ($i = 10; $i >= 0; $i--) {\r\n    $skipList->addData($i);\r\n}\r\n\r\n//打印0到10组成的跳表\r\nvar_dump($skipList);\r\n\r\n//返回SNode对象\r\nvar_dump($skipList->findData(5));\r\n\r\n$skipList->deleteData(5);\r\n\r\n//返回false\r\nvar_dump($skipList->findData(5));"
  },
  {
    "path": "php/24_tree/Tree.php",
    "content": "<?php\n\nnamespace Algo_24;\n\nclass Tree\n{\n\n    /**\n     * 树的根节点\n     * @var [type]\n     */\n    public $head = null;\n\n    /**\n     * [__construct description]\n     * @param TreeNode|null $head [description]\n     */\n    public function __construct($headData = null)\n    {\n        if ($headData != null) {\n            $this->head = new TreeNode($headData);\n        }\n    }\n\n    /**\n     * 查找数据\n     * @param  [type] $data [数据]\n     * @return [type]       [description]\n     */\n    public function find($data)\n    {\n        if ($this->head == null) {\n            return null;\n        }\n\n        $node = $this->head;\n\n        while ($node != null) {\n            if ($node->data == $data) {\n                return $node;\n            } elseif ($data > $node->data) {\n                $node = $node->right;\n            } else {\n                $node = $node->left;\n            }\n        }\n\n        return null;\n    }\n\n    /**\n     * 插入数据\n     * @param  [type] $data [数据]\n     * @return [type]       [description]\n     */\n    public function insert($data)\n    {\n        if ($this->head == null) {\n            $this->head = new TreeNode($data);\n            return true;\n        }\n\n        $node = $this->head;\n\n        while ($node != null) {\n            if ($data > $node->data) {\n                if ($node->right == null) {\n                    $node->right = new TreeNode($data);\n                    return true;\n                }\n                $node = $node->right;\n            } else {\n                if ($node->left == null) {\n                    $node->left = new TreeNode($data);\n                    return true;\n                }\n                $node = $node->left;\n            }\n        }\n    }\n\n    /**\n     * 删除节点\n     * @param  [type] $data [节点]\n     * @return [type]       [description]\n     */\n    public function delete($data)\n    {\n        // 找到需要删除节点\n        $node = $this->head;\n        $pnode = null;\n        while ($node != null) {\n            if ($node->data == $data) {\n                break;\n            } elseif ($data > $node->data) {\n                $pnode = $node;\n                $node = $node->right;\n            } else {\n                $pnode = $node;\n                $node = $node->left;\n            }\n        }        \n        if ($node == null) {\n            return false;\n        }\n        // 要删除的节点有两个子节点\n        // 查找右子树中最小节点\n        if ($node->left != null && $node->right != null) {\n            $minPP = $node;\n            $minP = $node->right;\n            while ($minP->left != null) {\n                $minPP = $minP;\n                $minP = $minP->left;\n            }\n            $node->data = $minP->data;\n            $node = $minP;\n            // 删除掉右子树中的最小节点\n            $minPP->left = null;\n        }\n\n        if ($node->left != null) {\n            $child = $node->left;\n        } elseif ($node->right != null) {\n            $child = $node->right;\n        } else {\n            $child = null;\n        }\n\n        if ($pnode == null) {\n            // 删除的是根节点\n            $node = $child;\n        } elseif ($pnode->left == $node) {\n            $pnode->left = $child;\n        } else {\n            $pnode->right = $child;\n        }\n    }\n\n    /**\n     * 前序遍历\n     * @return [type] [description]\n     */\n    public function preOrder($node)\n    {\n        if ($node == null) {\n            return ;            \n        }\n        echo $node->data . '->';\n        $this->preOrder($node->left);\n        $this->preOrder($node->right);\n    }\n\n    /**中序遍历\n     * @param $node\n     *\n     */\n    public function inOrder($node){\n        if(empty($node)){\n            return;\n        }\n        $this->inOrder($node->left);\n        echo $node->data . ' ';\n        $this->inOrder($node->right);\n\n    }\n\n    /**\n     * @param $node\n     * 后续遍历\n     */\n    public function postOrder($node){\n        if(empty($node)){\n            return;\n        }\n        $this->postOrder($node->left);\n        $this->postOrder($node->right);\n        echo $node->data . ' ';\n\n    }\n    /**\n     * @param $queue\n     * @param int $index 从队列(数组)的那个位置开始处理\n     * 层级遍历\n     * 首先把节点放入数组，记录放入数组的根节点个数index，把节点的左右子放入数组\n     * 开始遍历数组queue(从index开始,子节点已经入队列的节点元素不再处理)，把左右子节点放入queue,index++\n     * 持续上述过程，当节点没有子节点时，入队列过程结束，queue里节点的顺序即为层级遍历元素节点的顺序\n     *\n     * 完全二叉树\n     */\n    public function levelOrder($queue, $index = 0)\n    {\n        for ($i = $index; $i < count($queue); $i++) {\n            $node = $queue[$i];\n            if ($node->left) {\n                $queue[] = $node->left;\n            } else {\n                return $queue;\n            }\n            if ($node->right) {\n                $queue[] = $node->right;\n            } else {\n                return $queue;\n            }\n            $index++;\n        }\n        return $queue;\n\n    }\n}"
  },
  {
    "path": "php/24_tree/TreeNode.php",
    "content": "<?php\n\nnamespace Algo_24;\n\nclass TreeNode\n{\n\n    /**\n     * 节点中的数据\n     * @var [type]\n     */\n    public $data;\n\n    /**\n     * 左节点\n     * @var [type]\n     */\n    public $left;\n\n    /**\n     * 右节点\n     * @var [type]\n     */\n    public $right;\n\n    /**\n     * [__construct description]\n     * @param [type] $data [description]\n     */\n    public function __construct($data = null)\n    {\n        $this->data = $data;\n        $this->left = null;\n        $this->right = null;\n    }\n}"
  },
  {
    "path": "php/24_tree/levelOrder.php",
    "content": "<?php\n/**\n * Created by PhpStorm.\n * User: 764432054@qq.com\n * Date: 2019/9/22\n * Time: 23:30\n *\n *二叉树的层级遍历\n */\n\n\nnamespace Algo_24;\n\nrequire_once '../vendor/autoload.php';\n\n$tree=new Tree(20);\n$tree->insert(16);\n$tree->insert(30);\n$tree->insert(12);\n$tree->insert(19);\n\n$tree->insert(10);\n$tree->insert(15);\n$tree->insert(18);\n$tree->insert(21);\n$tree->insert(38);\n\n\n$q=$tree->levelOrder([$tree->head]);\n\nforeach ($q as $n){\n    echo $n->data.\" \";\n}\necho PHP_EOL;"
  },
  {
    "path": "php/24_tree/main.php",
    "content": "<?php\n\nnamespace Algo_24;\n\nrequire_once '../vendor/autoload.php';\n\n\n$tree = new Tree();\n\n$tree->insert(20);\n$tree->insert(30);\n$tree->insert(40);\n$tree->insert(10);\n$tree->insert(21);\n$tree->insert(22);\n\n$tree->preOrder($tree->head);\necho PHP_EOL;\n\n$tree->inOrder($tree->head);\necho PHP_EOL;\n\n$tree->postOrder($tree->head);\necho PHP_EOL;\n\n\nprint_r($tree->find(30));\necho PHP_EOL;\n\n\n$tree->delete(30);\n$tree->preOrder($tree->head);\necho PHP_EOL;"
  },
  {
    "path": "php/38_divide_and_conquer/matrix_production.php",
    "content": "<?php\r\n\r\nfunction popRow(&$matrix)\r\n{\r\n    return empty($matrix) ? [] : [array_shift($matrix)];\r\n}\r\n\r\nfunction popColumn(&$matrix)\r\n{\r\n    $column = [];\r\n\r\n    foreach ($matrix as $key => &$row) {\r\n        $data = array_shift($row);\r\n        if (empty($data) || empty($row)) {\r\n            unset($matrix[$key]);\r\n        }\r\n        $column[] = [$data];\r\n    }\r\n    return $column;\r\n}\r\n\r\nfunction countProduction($row, $column)\r\n{\r\n    for ($i = 0, $sum = 0; $i < count($row[0]); $i++) {\r\n        $sum += $row[0][$i] * $column[$i][0];\r\n    }\r\n    return $sum;\r\n}\r\n\r\nfunction merger($value1, $value2, $value3, $value4)\r\n{\r\n    if (empty($value2) && empty($value3)) {\r\n        return $value1;\r\n    } else {\r\n        $array12 = array_merge([$value1], !is_array($value2) ? [$value2] : $value2);\r\n        if (!is_array($value3)) {\r\n            $array34 = array_merge([$value3], !is_array($value4) ? [$value4] : $value4);\r\n            return [$array12, $array34];\r\n        } else {\r\n            for ($i = 0, $array34 = []; $i < count($value3); $i++) {\r\n                $array34[] = array_merge($value3[$i], $value4[$i]);\r\n            }\r\n            return array_merge([$array12], $array34);\r\n        }\r\n    }\r\n}\r\n\r\nfunction matrixProduction($matrix1, $matrix2)\r\n{\r\n    $row = popRow($matrix1);\r\n    $column = popColumn($matrix2);\r\n\r\n    if (empty($row) || empty($column)) {\r\n        return [];\r\n    }\r\n\r\n    $value1 = countProduction($row, $column);\r\n    $value2 = matrixProduction($row, $matrix2);\r\n    $value3 = matrixProduction($matrix1, $column);\r\n    $value4 = matrixProduction($matrix1, $matrix2);\r\n\r\n    return merger($value1, $value2, $value3, $value4);\r\n}\r\n\r\n$matrix1 = [\r\n    [1, 2, 3],\r\n    [4, 5, 6],\r\n    [7, 8, 9],\r\n];\r\n\r\n$matrix2 = [\r\n    [1, 2, 3],\r\n    [4, 5, 6],\r\n    [7, 8, 9],\r\n];\r\n\r\nvar_dump(matrixProduction($matrix1, $matrix2));\r\n"
  },
  {
    "path": "php/39_backtracking/queens.php",
    "content": "<?php\n\n/**\n * 8皇后解法，共92种解法 回溯思想\n * Class Queen\n */\nclass Queen\n{\n    public $result = [];\n\n    function cal8queens($row)\n    {\n        if ($row == 8) {\n            $this->printQueens();\n            return;\n        }\n\n        //每一行有8中放法\n        for($column = 0; $column < 8; $column++) {\n            if ($this->isOk($row, $column)) {\n                $this->result[$row] = $column;\n                $this->cal8queens($row + 1);\n            }\n        }\n    }\n\n    //row行的column列是否合适\n    function isOk($row, $column)\n    {\n        $leftup = $column - 1;\n        $rightdown = $column + 1;\n\n        for ($i = $row - 1; $i >= 0; $i--) {\n            //判断上一行的 column 列是否有值\n            if ($this->result[$i] == $column) {\n                return false;\n            }\n\n            //左上角是否有值\n            if ($leftup >= 0 && $this->result[$i] == $leftup) {\n                return false;\n            }\n\n            //右下角是否有值\n            if ($rightdown < 8 && $this->result[$i] == $rightdown) {\n                return false;\n            }\n\n            $leftup--;\n            $rightdown++;\n\n        }\n\n        return true;\n    }\n\n    //打印\n    function printQueens()\n    {\n        for ($row = 0; $row < 8; $row++) {\n            for ($column = 0; $column < 8; $column++) {\n                if ($this->result[$row] == $column) {\n                    echo 'Q';\n                } else {\n                    echo '*';\n                }\n            }\n            echo '<br>';\n        }\n    }\n}\n\n$queen = new Queen();\n$queen->cal8queens(0);\n"
  },
  {
    "path": "php/README.md",
    "content": "## 数据结构与算法之美PHP实现\n\n### 项目运行\n* 依赖composer自动加载，php目录下执行`composer dump-autoload` || `sh buildAutoLoad.sh`\n* 项目代码均在mac&php7环境下跑通\n\n### 项目实现\n#### 06_linkedlist\n* 单链表php实现\n* 回文判断\n\n#### 07_linkedlist\n* reverse 单链表反转\n* checkCircle 链表中环的检测\n* mergerSortedList 两个有序的链表合并\n* deleteLastKth 删除链表倒数第n个结点\n* findMiddleNode 求链表的中间结点\n\n#### 08_stack\n* 链栈实现\n\n#### 09_stack\n* 队列链表实现\n#### 10_heap\n* main 堆的基本操作,堆排序\n* findmiddle  动态数据流求中位数\n* topn 动态数据流求top k\n#### 24_tree\n* main 二叉树的基本操作 前中后序遍历\n* levelOrder 二叉树的层级遍历\n"
  },
  {
    "path": "php/Stack/Compute.php",
    "content": "<?php\n\n\n\n// 四则运算 +-*/()\nfunction expression($str)\n{\n    $str = str_replace(' ','',$str);\n    $arr = preg_split('/([\\+\\-\\*\\/\\(\\)])/', $str, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);\n\n    $numStack = [];  // 存放数字\n    $operStack = []; // 存放运算符\n    $operStack[] = NULL;\n\n    for ($i = 0; $i < count($arr); $i++){\n        if (ord($arr[$i]) >= 48 && ord($arr[$i] <= 57)){\n            array_push($numStack, $arr[$i]);\n            continue;\n        }\n        switch ($arr[$i]){\n            case '+':\n            case '-':\n                $arrLen = count($operStack);\n                while ($operStack[$arrLen-1] === '*' || $operStack[$arrLen-1] === '/' || $operStack[$arrLen-1] === '-'){\n                    compute($numStack, $operStack);\n                    $arrLen--;\n                }\n                array_push($operStack, $arr[$i]);\n                break;\n            case '*':\n            case '/':\n            case '(':\n                array_push($operStack, $arr[$i]);\n                break;\n            case ')':\n                $arrLen = count($operStack);\n                while ($operStack[$arrLen-1] !== '('){\n                    compute($numStack, $operStack);\n                    $arrLen--;\n                }\n                array_pop($operStack);\n                break;\n            default:\n                throw new \\Exception(\"不支持的运算符\", 1);\n                break;\n        }\n    }\n    \n    $arrLen = count($operStack);\n    while ($operStack[$arrLen-1] !== NULL){  \n        compute($numStack, $operStack);\n        $arrLen--;\n    }\n    echo array_pop($numStack);\n}\n\n//数字栈长度减一，运算符栈长度减一\nfunction compute(&$numStack, &$operStack){\n    $num = array_pop($numStack);\n    switch (array_pop($operStack)) {\n        case '*':\n            array_push($numStack, array_pop($numStack) * $num);\n            break;\n        case '/':\n            array_push($numStack, array_pop($numStack) / $num);\n            break;\n        case '+':\n            array_push($numStack, array_pop($numStack) + $num);\n            break;\n        case '-':\n            array_push($numStack, array_pop($numStack) - $num);\n            break;\n        \n    }\n}\nexpression('-1+2-(1+2*3)');\necho PHP_EOL;\neval('echo -1+2-(1+2*3);');"
  },
  {
    "path": "php/buildAutoLoad.sh",
    "content": "#!/bin/bash\n\ncomposer dump-autoload"
  },
  {
    "path": "php/composer.json",
    "content": "{\n    \"name\": \"algo/php\",\n    \"description\": \"数据结构与算法之美php实现\",\n    \"type\": \"project\",\n    \"require\": {},\n    \"autoload\": {\n        \"psr-4\": {\n            \"Algo_06\\\\\": \"06_linkedlist/\",\n            \"Algo_07\\\\\": \"07_linkedlist/\",\n            \"Algo_08\\\\\": \"08_stack/\",\n            \"Algo_09\\\\\": \"09_queue/\",\n            \"Algo_24\\\\\": \"24_tree/\",\n            \"Algo_10\\\\\": \"10_heap/\"\n        }\n    }\n}\n"
  },
  {
    "path": "python/.gitkeep",
    "content": ""
  },
  {
    "path": "python/05_array/.gitkeep",
    "content": ""
  },
  {
    "path": "python/05_array/myarray.py",
    "content": "#\n# 1) Insertion, deletion and random access of array\n# 2) Assumes int for element type\n#\n# Author: Wenru\n#\n\n\nclass MyArray:\n    \"\"\"A simple wrapper around List.\n    You cannot have -1 in the array.\n    \"\"\"\n\n    def __init__(self, capacity: int):\n        self._data = []\n        self._capacity = capacity\n\n    def __getitem__(self, position: int) -> object:\n        return self._data[position]\n\n    def __setitem__(self, index: int, value: object):\n        self._data[index] = value\n\n    def __len__(self) -> int:\n        return len(self._data)\n\n    def __iter__(self):\n        for item in self._data:\n            yield item\n\n    def find(self, index: int) -> object:\n        try:\n            return self._data[index]\n        except IndexError:\n            return None\n\n    def delete(self, index: int) -> bool:\n        try:\n            self._data.pop(index)\n            return True\n        except IndexError:\n            return False\n\n    def insert(self, index: int, value: int) -> bool:\n        if len(self) >= self._capacity:\n            return False\n        else:\n            return self._data.insert(index, value)\n\n    def print_all(self):\n        for item in self:\n            print(item)\n\n\ndef test_myarray():\n    array = MyArray(5)\n    array.insert(0, 3)\n    array.insert(0, 4)\n    array.insert(1, 5)\n    array.insert(3, 9)\n    array.insert(3, 10)\n    assert array.insert(0, 100) is False\n    assert len(array) == 5\n    assert array.find(1) == 5\n    assert array.delete(4) is True\n    array.print_all()\n\n\nif __name__ == \"__main__\":\n    test_myarray()\n"
  },
  {
    "path": "python/06_linkedlist/.gitkeep",
    "content": ""
  },
  {
    "path": "python/06_linkedlist/LRUCache.py",
    "content": "# Definition for singly-linked list.\nclass DbListNode(object):\n    def __init__(self, x, y):\n        self.key = x\n        self.val = y\n        self.next = None\n        self.prev = None\n\n\nclass LRUCache:\n    '''\n    leet code: 146\n        运用你所掌握的数据结构，设计和实现一个  LRU (最近最少使用) 缓存机制。\n        它应该支持以下操作： 获取数据 get 和 写入数据 put 。\n        获取数据 get(key) - 如果密钥 (key) 存在于缓存中，则获取密钥的值（总是正数），否则返回 -1。\n        写入数据 put(key, value) - 如果密钥不存在，则写入其数据值。\n            当缓存容量达到上限时，它应该在写入新数据之前删除最近最少使用的数据值，从而为新的数据值留出空间\n\n    哈希表+双向链表\n    哈希表: 查询 O(1)\n    双向链表: 有序, 增删操作 O(1)\n\n    Author: Ben\n    '''\n\n    def __init__(self, capacity: int):\n        self.cap = capacity\n        self.hkeys = {}\n        # self.top和self.tail作为哨兵节点, 避免越界\n        self.top = DbListNode(None, -1)\n        self.tail = DbListNode(None, -1)\n        self.top.next = self.tail\n        self.tail.prev = self.top\n\n    def get(self, key: int) -> int:\n\n        if key in self.hkeys.keys():\n            # 更新结点顺序\n            cur = self.hkeys[key]\n            # 跳出原位置\n            cur.next.prev = cur.prev\n            cur.prev.next = cur.next\n            # 最近用过的置于链表首部\n            top_node = self.top.next\n            self.top.next = cur\n            cur.prev = self.top\n            cur.next = top_node\n            top_node.prev = cur\n\n            return self.hkeys[key].val\n        return -1\n\n    def put(self, key: int, value: int) -> None:\n        if key in self.hkeys.keys():\n            cur = self.hkeys[key]\n            cur.val = value\n            # 跳出原位置\n            cur.prev.next = cur.next\n            cur.next.prev = cur.prev\n\n            # 最近用过的置于链表首部\n            top_node = self.top.next\n            self.top.next = cur\n            cur.prev = self.top\n            cur.next = top_node\n            top_node.prev = cur\n        else:\n            # 增加新结点至首部\n            cur = DbListNode(key, value)\n            self.hkeys[key] = cur\n            # 最近用过的置于链表首部\n            top_node = self.top.next\n            self.top.next = cur\n            cur.prev = self.top\n            cur.next = top_node\n            top_node.prev = cur\n            if len(self.hkeys.keys()) > self.cap:\n                self.hkeys.pop(self.tail.prev.key)\n                # 去掉原尾结点\n                self.tail.prev.prev.next = self.tail\n                self.tail.prev = self.tail.prev.prev\n\n    def __repr__(self):\n        vals = []\n        p = self.top.next\n        while p.next:\n            vals.append(str(p.val))\n            p = p.next\n        return '->'.join(vals)\n\n\nif __name__ == '__main__':\n    cache = LRUCache(2)\n    cache.put(1, 1)\n    cache.put(2, 2)\n    print(cache)\n    cache.get(1)  # 返回  1\n    cache.put(3, 3)  # 该操作会使得密钥 2 作废\n    print(cache)\n    cache.get(2)  # 返回 -1 (未找到)\n    cache.put(4, 4)  # 该操作会使得密钥 1 作废\n    print(cache)\n    cache.get(1)  # 返回 -1 (未找到)\n    cache.get(3)  # 返回  3\n    print(cache)\n    cache.get(4)  # 返回  4\n    print(cache)\n"
  },
  {
    "path": "python/06_linkedlist/palindrome.py",
    "content": "\"\"\"\n    check a single-linked list whether a palindrome\n\"\"\"\n\nimport sys\n# 引用当前文件夹下的single_linked_list\nsys.path.append('singly_linked_list')\nfrom singly_linked_list import SinglyLinkedList\n\ndef reverse(head):\n    reverse_head = None\n    while head:\n        next = head._next\n        head._next = reverse_head\n        reverse_head = head\n        head = next\n\n    return reverse_head\n\ndef is_palindrome(l):\n    l.print_all()\n    slow = l._head\n    fast = l._head\n    position = 0\n    while fast and fast._next:\n        slow = slow._next\n        fast = fast._next._next\n        position += 1\n\n    reverse_node = reverse(slow)\n    head_node = l._head\n    is_palin = True\n    while (head_node and reverse_node):\n        if (head_node.data == reverse_node.data):\n            head_node = head_node._next\n            reverse_node = reverse_node._next\n        else:\n            is_palin = False\n            break\n\n    return is_palin\n\nif __name__ == '__main__':\n    # the result should be False, True, True, True, True\n    test_str_arr = ['ab', 'aa', 'aba', 'abba', 'abcba']\n    for str in test_str_arr:\n        l = SinglyLinkedList()\n        for i in str:\n            l.insert_value_to_head(i)\n\n        print(is_palindrome(l))\n\n\n\n"
  },
  {
    "path": "python/06_linkedlist/singlyLinkedList.py",
    "content": "# 1.单链表的插入、删除、查找操作；\n# 2.链表中存储的数据类型是Int\n#\n# Author:Lee\n\n\nclass Node(object):\n    \"\"\"链表结构的Node节点\"\"\"\n\n    def __init__(self, data, next_node=None):\n        \"\"\"Node节点的初始化方法.\n        参数:\n            data:存储的数据\n            next:下一个Node节点的引用地址\n        \"\"\"\n        self.__data = data\n        self.__next = next_node\n\n    @property\n    def data(self):\n        \"\"\"Node节点存储数据的获取.\n        返回:\n            当前Node节点存储的数据\n        \"\"\"\n        return self.__data\n\n    @data.setter\n    def data(self, data):\n        \"\"\"Node节点存储数据的设置方法.\n        参数:\n            data:新的存储数据\n        \"\"\"\n        self.__data = data\n\n    @property\n    def next_node(self):\n        \"\"\"获取Node节点的next指针值.\n        返回:\n            next指针数据\n        \"\"\"\n        return self.__next\n\n    @next_node.setter\n    def next_node(self, next_node):\n        \"\"\"Node节点next指针的修改方法.\n        参数:\n            next:新的下一个Node节点的引用\n        \"\"\"\n        self.__next = next_node\n\n\nclass SinglyLinkedList(object):\n    \"\"\"单向链表\"\"\"\n\n    def __init__(self):\n        \"\"\"单向列表的初始化方法.\"\"\"\n        self.__head = None\n\n    def find_by_value(self, value):\n        \"\"\"按照数据值在单向列表中查找.\n        参数:\n            value:查找的数据\n        返回:\n            Node\n        \"\"\"\n        node = self.__head\n        while (node is not None) and (node.data != value):\n            node = node.next_node\n        return node\n\n    def find_by_index(self, index):\n        \"\"\"按照索引值在列表中查找.\n        参数:\n            index:索引值\n        返回:\n            Node\n        \"\"\"\n        node = self.__head\n        pos = 0\n        while (node is not None) and (pos != index):\n            node = node.next_node\n            pos += 1\n        return node\n\n    def insert_to_head(self, value):\n        \"\"\"在链表的头部插入一个存储value数值的Node节点.\n        参数:\n            value:将要存储的数据\n        \"\"\"\n        node = Node(value)\n        node.next_node = self.__head\n        self.__head = node\n\n    def insert_after(self, node, value):\n        \"\"\"在链表的某个指定Node节点之后插入一个存储value数据的Node节点.\n        参数:\n            node:指定的一个Node节点\n            value:将要存储在新Node节点中的数据\n        \"\"\"\n        if node is None:  # 如果指定在一个空节点之后插入数据节点，则什么都不做\n            return\n\n        new_node = Node(value)\n        new_node.next_node = node.next\n        node.next = new_node\n\n    def insert_before(self, node, value):\n        \"\"\"在链表的某个指定Node节点之前插入一个存储value数据的Node节点.\n        参数:\n            node:指定的一个Node节点\n            value:将要存储在新的Node节点中的数据\n        \"\"\"\n        if (node is None) or (self.__head is None):  # 如果指定在一个空节点之前或者空链表之前插入数据节点，则什么都不做\n            return\n\n        if node == self.__head:  # 如果是在链表头之前插入数据节点，则直接插入\n            self.insert_to_head(value)\n            return\n\n        new_node = Node(value)\n        pro = self.__head\n        not_found = False  # 如果在整个链表中都没有找到指定插入的Node节点，则该标记量设置为True\n        while pro.next_node != node:  # 寻找指定Node之前的一个Node\n            if pro.next_node is None:  # 如果已经到了链表的最后一个节点，则表明该链表中没有找到指定插入的Node节点\n                not_found = True\n                break\n            else:\n                pro = pro.next_node\n        if not not_found:\n            pro.next_node = new_node\n            new_node.next_node = node\n\n    def delete_by_node(self, node):\n        \"\"\"在链表中删除指定Node的节点.\n        参数:\n            node:指定的Node节点\n        \"\"\"\n        if self.__head is None:  # 如果链表是空的，则什么都不做\n            return\n\n        if node == self.__head:  # 如果指定删除的Node节点是链表的头节点\n            self.__head = node.next_node\n            return\n\n        pro = self.__head\n        not_found = False  # 如果在整个链表中都没有找到指定删除的Node节点，则该标记量设置为True\n        while pro.next_node != node:\n            if pro.next_node is None:  # 如果已经到链表的最后一个节点，则表明该链表中没有找到指定删除的Node节点\n                not_found = True\n                break\n            else:\n                pro = pro.next_node\n        if not not_found:\n            pro.next_node = node.next_node\n\n    def delete_by_value(self, value):\n        \"\"\"在链表中删除指定存储数据的Node节点.\n        参数:\n            value:指定的存储数据\n        \"\"\"\n        if self.__head is None:  # 如果链表是空的，则什么都不做\n            return\n\n        if self.__head.data == value:  # 如果链表的头Node节点就是指定删除的Node节点\n            self.__head = self.__head.next_node\n\n        pro = self.__head\n        node = self.__head.next_node\n        not_found = False\n        while node.data != value:\n            if node.next_node is None:  # 如果已经到链表的最后一个节点，则表明该链表中没有找到执行Value值的Node节点\n                not_found = True\n                break\n            else:\n                pro = node\n                node = node.next_node\n        if not_found is False:\n            pro.next_node = node.next_node\n\n    def delete_last_n_node(self, n):\n        \"\"\"删除链表中倒数第N个节点.\n        主体思路：\n            设置快、慢两个指针，快指针先行，慢指针不动；当快指针跨了N步以后，快、慢指针同时往链表尾部移动，\n            当快指针到达链表尾部的时候，慢指针所指向的就是链表的倒数第N个节点\n        参数:\n            n:需要删除的倒数第N个序数\n        \"\"\"\n        fast = self.__head\n        slow = self.__head\n        step = 0\n\n        while step <= n:\n            fast = fast.next_node\n            step += 1\n\n        while fast.next_node is not None:\n            tmp = slow\n            fast = fast.next_node\n            slow = slow.next_node\n\n        tmp.next_node = slow.next_node\n\n    def find_mid_node(self):\n        \"\"\"查找链表中的中间节点.\n        主体思想:\n            设置快、慢两种指针，快指针每次跨两步，慢指针每次跨一步，则当快指针到达链表尾部的时候，慢指针指向链表的中间节点\n        返回:\n            node:链表的中间节点\n        \"\"\"\n        fast = self.__head\n        slow = self.__head\n\n        while fast.next_node is not None:\n            fast = fast.next_node.next_node\n            slow = slow.next_node\n\n        return slow\n\n    def create_node(self, value):\n        \"\"\"创建一个存储value值的Node节点.\n        参数:\n            value:将要存储在Node节点中的数据\n        返回:\n            一个新的Node节点\n        \"\"\"\n        return Node(value)\n\n    def print_all(self):\n        \"\"\"打印当前链表所有节点数据.\"\"\"\n        pos = self.__head\n        if pos is None:\n            print(\"当前链表还没有数据\")\n            return\n        while pos.next_node is not None:\n            print(str(pos.data) + \" --> \", end=\"\")\n            pos = pos.next_node\n        print(str(pos.data))\n\n    def reversed_self(self):\n        \"\"\"翻转链表自身.\"\"\"\n        if self.__head is None or self.__head.next is None:  # 如果链表为空，或者链表只有一个节点\n            return\n\n        pre = self.__head\n        node = self.__head.next\n        while node is not None:\n            pre, node = self.__reversed_with_two_node(pre, node)\n\n        self.__head.next = None\n        self.__head = pre\n\n    def __reversed_with_two_node(self, pre, node):\n        \"\"\"翻转相邻两个节点.\n        参数:\n            pre:前一个节点\n            node:当前节点\n        返回:\n            (pre,node):下一个相邻节点的元组\n        \"\"\"\n        tmp = node.next_node\n        node.next_node = pre\n        pre = node  # 这样写有点啰嗦，但是能让人更能看明白\n        node = tmp\n        return pre, node\n\n    def has_ring(self):\n        \"\"\"检查链表中是否有环.\n        主体思想：\n            设置快、慢两种指针，快指针每次跨两步，慢指针每次跨一步，如果快指针没有与慢指针相遇而是顺利到达链表尾部\n            说明没有环；否则，存在环\n        返回:\n            True:有环\n            False:没有环\n        \"\"\"\n        fast = self.__head\n        slow = self.__head\n\n        while (fast.next_node is not None) and (fast is not None):\n            fast = fast.next_node\n            slow = slow.next_node\n            if fast == slow:\n                return True\n\n        return False\n"
  },
  {
    "path": "python/06_linkedlist/singly_linked_list.py",
    "content": "\"\"\"\n    1) Insertion, deletion and search of singly-linked list;\n    2) Assumes int type for data in list nodes.\n\n    Author: Wenru\n\"\"\"\nfrom typing import Optional\n\n\nclass Node:\n\n    def __init__(self, data: int, next_node=None):\n        self.data = data\n        self._next = next_node\n\n\nclass SinglyLinkedList:\n\n    def __init__(self):\n        self._head = None\n\n    def find_by_value(self, value: int) -> Optional[Node]:\n        p = self._head\n        while p and p.data != value:\n            p = p._next\n        return p\n\n    def find_by_index(self, index: int) -> Optional[Node]:\n        p = self._head\n        position = 0\n        while p and position != index:\n            p = p._next\n            position += 1\n        return p\n\n    def insert_value_to_head(self, value: int):\n        new_node = Node(value)\n        self.insert_node_to_head(new_node)\n\n    def insert_node_to_head(self, new_node: Node):\n        if new_node:\n            new_node._next = self._head\n            self._head = new_node\n\n    def insert_value_after(self, node: Node, value: int):\n        new_node = Node(value)\n        self.insert_node_after(node, new_node)\n\n    def insert_node_after(self, node: Node, new_node: Node):\n        if not node or not new_node:\n            return\n        new_node._next = node._next\n        node._next = new_node\n\n    def insert_value_before(self, node: Node, value: int):\n        new_node = Node(value)\n        self.insert_node_before(node, new_node)\n\n    def insert_node_before(self, node: Node, new_node: Node):\n        if not self._head or not node or not new_node:\n            return\n        if self._head == node:\n            self.insert_node_to_head(new_node)\n            return\n        current = self._head\n        while current._next and current._next != node:\n            current = current._next\n        if not current._next:  # node is not even in the list\n            return\n        new_node._next = node\n        current._next = new_node\n\n    def delete_by_node(self, node: Node):\n        if not self._head or not node:\n            return\n        if node._next:\n            node.data = node._next.data\n            node._next = node._next._next\n            return\n        # node is the last one or not in the list\n        current = self._head\n        while current and current._next != node:\n            current = current._next\n        if not current:  # node not in the list\n            return\n        current._next = node._next\n\n    def delete_by_value(self, value: int):\n        if not self._head or not value:\n            return\n        fake_head = Node(value + 1)\n        fake_head._next = self._head\n        prev, current = fake_head, self._head\n        while current:\n            if current.data != value:\n                prev._next = current\n                prev = prev._next\n            current = current._next\n        if prev._next:\n            prev._next = None\n        self._head = fake_head._next  # in case head.data == value\n\n    def __repr__(self) -> str:\n        nums = []\n        current = self._head\n        while current:\n            nums.append(current.data)\n            current = current._next\n        return \"->\".join(str(num) for num in nums)\n\n    # 重写__iter__方法，方便for关键字调用打印值\n    def __iter__(self):\n        node = self._head\n        while node:\n            yield node.data\n            node = node._next\n\n    def print_all(self):\n        current = self._head\n        if current:\n            print(f\"{current.data}\", end=\"\")\n            current = current._next\n        while current:\n            print(f\"->{current.data}\", end=\"\")\n            current = current._next\n        print(\"\\n\", flush=True)\n\n\nif __name__ == \"__main__\":\n    l = SinglyLinkedList()\n    for i in range(15):\n        l.insert_value_to_head(i)\n    node9 = l.find_by_value(9)\n    l.insert_value_before(node9, 20)\n    l.insert_value_before(node9, 16)\n    l.insert_value_before(node9, 16)\n    l.delete_by_value(16)\n    node11 = l.find_by_index(3)\n    l.delete_by_node(node11)\n    l.delete_by_node(l._head)\n    l.delete_by_value(13)\n    print(l)\n    for value in l:\n        print(value)\n"
  },
  {
    "path": "python/07_linkedlist/.gitkeep",
    "content": ""
  },
  {
    "path": "python/07_linkedlist/linked_list_algo.py",
    "content": "\"\"\"\n    1) Reverse singly-linked list\n    2) Detect cycle in a list\n    3) Merge two sorted lists\n    4) Remove nth node from the end\n    5) Find middle node\n\n    Author: Wenru\n\"\"\"\n\nfrom typing import Optional\n\n\nclass Node:\n    \n    def __init__(self, data: int, next=None):\n        self.data = data\n        self._next = next\n\n# Reverse singly-linked list\n# 单链表反转\n# Note that the input is assumed to be a Node, not a linked list.\ndef reverse(head: Node) -> Optional[Node]:\n    reversed_head = None\n    current = head\n    while current:\n        reversed_head, reversed_head._next, current = current, reversed_head, current._next\n    return reversed_head\n\n# Detect cycle in a list\n# 检测环\ndef has_cycle(head: Node) -> bool:\n    slow, fast = head, head\n    while fast and fast._next:\n        slow = slow._next\n        fast = fast._next._next\n        if slow == fast:\n            return True\n    return False\n\n# Merge two sorted linked list\n# 有序链表合并\ndef merge_sorted_list(l1: Node, l2: Node) -> Optional[Node]:\n    if l1 and l2:\n        p1, p2 = l1, l2\n        fake_head = Node(None)\n        current = fake_head\n        while p1 and p2:\n            if p1.data <= p2.data:\n                current._next = p1\n                p1 = p1._next\n            else:\n                current._next = p2\n                p2 = p2._next\n            current = current._next\n        current._next = p1 if p1 else p2\n        return fake_head._next\n    return l1 or l2\n\n# Remove nth node from the end\n# 删除倒数第n个节点。假设n大于0\ndef remove_nth_from_end(head: Node, n: int) -> Optional[Node]:\n    fast = head\n    count = 0\n    while fast and count < n:\n        fast = fast._next\n        count += 1\n    if not fast and count < n:  # not that many nodes\n        return head\n    if not fast and count == n:\n        return head._next\n    \n    slow = head\n    while fast._next:\n        fast, slow = fast._next, slow._next\n    slow._next = slow._next._next\n    return head\n\ndef find_middle_node(head: Node) -> Optional[Node]:\n    slow, fast = head, head\n    fast = fast._next if fast else None\n    while fast and fast._next:\n        slow, fast = slow._next, fast._next._next\n    return slow\n\ndef print_all(head: Node):\n    nums = []\n    current = head\n    while current:\n        nums.append(current.data)\n        current = current._next\n    print(\"->\".join(str(num) for num in nums))\n"
  },
  {
    "path": "python/08_stack/linked_stack.py",
    "content": "\"\"\"\n    Stack based upon linked list\n    基于链表实现的栈\n    \n    Author: Wenru\n\"\"\"\n\nfrom typing import Optional\n\nclass Node:\n    \n    def __init__(self, data: int, next=None):\n        self._data = data\n        self._next = next\n    \n\nclass LinkedStack:\n    \"\"\"A stack based upon singly-linked list.\n    \"\"\"\n    def __init__(self):\n        self._top: Node = None\n    \n    def push(self, value: int):\n        new_top = Node(value)\n        new_top._next = self._top\n        self._top = new_top\n    \n    def pop(self) -> Optional[int]:\n        if self._top:\n            value = self._top._data\n            self._top = self._top._next\n            return value\n    \n    def __repr__(self) -> str:\n        current = self._top\n        nums = []\n        while current:\n            nums.append(current._data)\n            current = current._next\n        return \" \".join(f\"{num}]\" for num in nums)\n\nif __name__ == \"__main__\":\n    stack = LinkedStack()\n    for i in range(9):\n        stack.push(i)\n    print(stack)\n    for _ in range(3):\n        stack.pop()\n    print(stack)"
  },
  {
    "path": "python/08_stack/simple_browser.py",
    "content": "\"\"\"\n    a simple browser realize\n    Author: zhenchao.zhu\n    解答：我们使用两个栈，X 和 Y，我们把首次浏览的页面依次压入栈 X，当点击后退按钮时，再依次从栈 X 中出栈，\n    并将出栈的数据依次放入栈 Y。当我们点击前进按钮时，我们依次从栈 Y 中取出数据，放入栈 X 中。\n    当栈 X 中没有数据时，那就说明没有页面可以继续后退浏览了。当栈 Y 中没有数据，\n    那就说明没有页面可以点击前进按钮浏览了。\n\"\"\"\n\nimport sys\n# 引用当前文件夹下的single_linked_list\nsys.path.append('linked_stack.py')\nfrom linked_stack import LinkedStack\n#from .linked_stack import LinkedStack\n\nclass NewLinkedStack(LinkedStack):\n\n    def is_empty(self):\n        return not self._top\n\n\nclass Browser():\n\n    def __init__(self):\n        self.forward_stack = NewLinkedStack()\n        self.back_stack = NewLinkedStack()\n\n    def can_forward(self):\n        if self.back_stack.is_empty():\n            return False\n\n        return True\n\n    def can_back(self):\n        if self.forward_stack.is_empty():\n            return False\n\n        return True\n\n    def open(self, url):\n        print(\"Open new url %s\" % url, end=\"\\n\")\n        self.forward_stack.push(url)\n\n    def back(self):\n        if self.forward_stack.is_empty():\n            return\n\n        top = self.forward_stack.pop()\n        self.back_stack.push(top)\n        print(\"back to %s\" % top, end=\"\\n\")\n\n    def forward(self):\n        if self.back_stack.is_empty():\n            return\n\n        top = self.back_stack.pop()\n        self.forward_stack.push(top)\n        print(\"forward to %s\" % top, end=\"\\n\")\n\n\nif __name__ == '__main__':\n\n    browser = Browser()\n    browser.open('a')\n    browser.open('b')\n    browser.open('c')\n    if browser.can_back():\n        browser.back()\n\n    if browser.can_forward():\n        browser.forward()\n\n    browser.back()\n    browser.back()\n    browser.back()\n"
  },
  {
    "path": "python/09_queue/array_queue.py",
    "content": "\"\"\"\n    Queue based upon array\n    用数组实现的队列\n\n    Author: Wenru\n\"\"\"\n\nfrom typing import Optional\n\nclass ArrayQueue:\n    \n    def __init__(self, capacity: int):\n        self._items = []\n        self._capacity = capacity\n        self._head = 0\n        self._tail = 0\n\n    def enqueue(self, item: str) -> bool:\n        if self._tail == self._capacity:\n            if self._head == 0:\n                return False\n            else:\n                for i in range(0, self._tail - self._head):\n                    self._items[i] = self._items[i + self._head]\n                self._tail = self._tail - self._head\n                self._head = 0\n        \n        self._items.insert(self._tail, item)\n        self._tail += 1\n        return True\n    \n    def dequeue(self) -> Optional[str]:\n        if self._head != self._tail:\n            item = self._items[self._head]\n            self._head += 1\n            return item\n        else:\n            return None\n    \n    def __repr__(self) -> str:\n        return \" \".join(item for item in self._items[self._head : self._tail])\n\n"
  },
  {
    "path": "python/09_queue/circular_queue.py",
    "content": "\"\"\"\n    Author: Wenru\n\"\"\"\n\nfrom typing import Optional\nfrom itertools import chain\n\nclass CircularQueue:\n\n    def __init__(self, capacity):\n        self._items = []\n        self._capacity = capacity + 1\n        self._head = 0\n        self._tail = 0\n    \n    def enqueue(self, item: str) -> bool:\n        if (self._tail + 1) % self._capacity == self._head:\n            return False\n        \n        self._items.append(item)\n        self._tail = (self._tail + 1) % self._capacity\n        return True\n    \n    def dequeue(self) -> Optional[str]:\n        if self._head != self._tail:\n            item = self._items[self._head]\n            self._head = (self._head + 1) % self._capacity\n            return item\n    \n    def __repr__(self) -> str:\n        if self._tail >= self._head:\n            return \" \".join(item for item in self._items[self._head : self._tail])\n        else:\n            return \" \".join(item for item in chain(self._items[self._head:], self._items[:self._tail]))\n\nif __name__ == \"__main__\":\n    q = CircularQueue(5)\n    for i in range(5):\n        q.enqueue(str(i))\n    q.dequeue()\n    q.dequeue()\n    q.enqueue(str(5))\n    print(q)\n"
  },
  {
    "path": "python/09_queue/dynamic_array_queue.py",
    "content": "\"\"\"\n    Author: Wenru\n\"\"\"\n\nfrom typing import Optional\n\nclass DynamicArrayQueue:\n\n    def __init__(self, capacity: int):\n        self._items = []\n        self._capacity = capacity\n        self._head = 0\n        self._tail = 0\n    \n    def enqueue(self, item: str) -> bool:\n        if self._tail == self._capacity:\n            if self._head == 0: return False\n            \n            self._items[0 : self._tail - self._head] = self._items[self._head : self._tail]\n            self._tail -= self._head\n            self._head = 0\n        \n        if self._tail == len(self._items):\n            self._items.append(item)\n        else:\n            self._items[self._tail] = item\n        self._tail += 1\n        return True\n\n    def dequeue(self) -> Optional[str]:\n        if self._head != self._tail:\n            item = self._items[self._head]\n            self._head += 1\n            return item\n    \n    def __repr__(self) -> str:\n        return \" \".join(item for item in self._items[self._head:self._tail])\n\nif __name__ == \"__main__\":\n    q = DynamicArrayQueue(10)\n    for i in range(10):\n        q.enqueue(str(i))\n    print(q)\n\n    for _ in range(3):\n        q.dequeue()\n    print(q)\n\n    q.enqueue(\"7\")\n    q.enqueue(\"8\")\n    print(q)"
  },
  {
    "path": "python/09_queue/linked_queue.py",
    "content": "\"\"\"\n    Queue based upon linked list\n\n    Author: Wenru\n\"\"\"\n\nfrom typing import Optional\n\nclass Node:\n    \n    def __init__(self, data: str, next=None):\n        self.data = data\n        self._next = next\n\nclass LinkedQueue:\n\n    def __init__(self):\n        self._head: Optional[Node] = None\n        self._tail: Optional[Node] = None\n    \n    def enqueue(self, value: str):\n        new_node = Node(value)\n        if self._tail:\n            self._tail._next = new_node\n        else:\n            self._head = new_node\n        self._tail = new_node\n    \n    def dequeue(self) -> Optional[str]:\n        if self._head:\n            value = self._head.data\n            self._head = self._head._next\n            if not self._head:\n                self._tail = None\n            return value\n    \n    def __repr__(self) -> str:\n        values = []\n        current = self._head\n        while current:\n            values.append(current.data)\n            current = current._next\n        return \"->\".join(value for value in values)\n\n\nif __name__ == \"__main__\":\n    q = LinkedQueue()\n    for i in range(10):\n        q.enqueue(str(i))\n    print(q)\n\n    for _ in range(3):\n        q.dequeue()\n    print(q)\n\n    q.enqueue(\"7\")\n    q.enqueue(\"8\")\n    print(q)\n"
  },
  {
    "path": "python/11_sorts/sorts.py",
    "content": "\"\"\"\n    Bubble sort, insertion sort and selection sort\n    冒泡排序、插入排序、选择排序\n\n    Author: Wenru\n\"\"\"\n\nfrom typing import List\n\n\n# 冒泡排序\ndef bubble_sort(a: List[int]):\n    length = len(a)\n    if length <= 1:\n        return\n\n    for i in range(length):\n        made_swap = False\n        for j in range(length - i - 1):\n            if a[j] > a[j + 1]:\n                a[j], a[j + 1] = a[j + 1], a[j]\n                made_swap = True\n        if not made_swap:\n            break\n\n\n# 插入排序\ndef insertion_sort(a: List[int]):\n    length = len(a)\n    if length <= 1:\n        return\n\n    for i in range(1, length):\n        value = a[i]\n        j = i - 1\n        while j >= 0 and a[j] > value:\n            a[j + 1] = a[j]\n            j -= 1\n        a[j + 1] = value\n\n\n# 选择排序\ndef selection_sort(a: List[int]):\n    length = len(a)\n    if length <= 1:\n        return\n\n    for i in range(length):\n        min_index = i\n        min_val = a[i]\n        for j in range(i, length):\n            if a[j] < min_val:\n                min_val = a[j]\n                min_index = j\n        a[i], a[min_index] = a[min_index], a[i]\n\n\ndef test_bubble_sort():\n    test_array = [1, 1, 1, 1]\n    bubble_sort(test_array)\n    assert test_array == [1, 1, 1, 1]\n    test_array = [4, 1, 2, 3]\n    bubble_sort(test_array)\n    assert test_array == [1, 2, 3, 4]\n    test_array = [4, 3, 2, 1]\n    bubble_sort(test_array)\n    assert test_array == [1, 2, 3, 4]\n\n\ndef test_insertion_sort():\n    test_array = [1, 1, 1, 1]\n    insertion_sort(test_array)\n    assert test_array == [1, 1, 1, 1]\n    test_array = [4, 1, 2, 3]\n    insertion_sort(test_array)\n    assert test_array == [1, 2, 3, 4]\n    test_array = [4, 3, 2, 1]\n    insertion_sort(test_array)\n    assert test_array == [1, 2, 3, 4]\n\n\ndef test_selection_sort():\n    test_array = [1, 1, 1, 1]\n    selection_sort(test_array)\n    assert test_array == [1, 1, 1, 1]\n    test_array = [4, 1, 2, 3]\n    selection_sort(test_array)\n    assert test_array == [1, 2, 3, 4]\n    test_array = [4, 3, 2, 1]\n    selection_sort(test_array)\n    assert test_array == [1, 2, 3, 4]\n\n\nif __name__ == \"__main__\":\n    array = [5, 6, -1, 4, 2, 8, 10, 7, 6]\n    bubble_sort(array)\n    print(array)\n\n    array = [5, 6, -1, 4, 2, 8, 10, 7, 6]\n    insertion_sort(array)\n    print(array)\n\n    array = [5, 6, -1, 4, 2, 8, 10, 7, 6]\n    selection_sort(array)\n    print(array)\n"
  },
  {
    "path": "python/12_sorts/merge_sort.py",
    "content": "\"\"\"\n    Author: Wenru\n\"\"\"\n\nfrom typing import List\n\n\ndef merge_sort(a: List[int]):\n    _merge_sort_between(a, 0, len(a) - 1)\n\n\ndef _merge_sort_between(a: List[int], low: int, high: int):\n    # The indices are inclusive for both low and high.\n    if low < high:\n        mid = low + (high - low) // 2\n        _merge_sort_between(a, low, mid)\n        _merge_sort_between(a, mid + 1, high)\n        _merge(a, low, mid, high)\n\n\ndef _merge(a: List[int], low: int, mid: int, high: int):\n    # a[low:mid], a[mid+1, high] are sorted.\n    i, j = low, mid + 1\n    tmp = []\n    while i <= mid and j <= high:\n        if a[i] <= a[j]:\n            tmp.append(a[i])\n            i += 1\n        else:\n            tmp.append(a[j])\n            j += 1\n    start = i if i <= mid else j\n    end = mid if i <= mid else high\n    tmp.extend(a[start:end + 1])\n    a[low:high + 1] = tmp\n\n\ndef test_merge_sort():\n    a1 = [3, 5, 6, 7, 8]\n    merge_sort(a1)\n    assert a1 == [3, 5, 6, 7, 8]\n    a2 = [2, 2, 2, 2]\n    merge_sort(a2)\n    assert a2 == [2, 2, 2, 2]\n    a3 = [4, 3, 2, 1]\n    merge_sort(a3)\n    assert a3 == [1, 2, 3, 4]\n    a4 = [5, -1, 9, 3, 7, 8, 3, -2, 9]\n    merge_sort(a4)\n    assert a4 == [-2, -1, 3, 3, 5, 7, 8, 9, 9]\n\n\nif __name__ == \"__main__\":\n    a1 = [3, 5, 6, 7, 8]\n    a2 = [2, 2, 2, 2]\n    a3 = [4, 3, 2, 1]\n    a4 = [5, -1, 9, 3, 7, 8, 3, -2, 9]\n    merge_sort(a1)\n    print(a1)\n    merge_sort(a2)\n    print(a2)\n    merge_sort(a3)\n    print(a3)\n    merge_sort(a4)\n    print(a4)\n"
  },
  {
    "path": "python/12_sorts/quick_sort.py",
    "content": "\"\"\"\n    Author: Wenru\n\"\"\"\n\nfrom typing import List\nimport random\n\n\ndef quick_sort(a: List[int]):\n    _quick_sort_between(a, 0, len(a) - 1)\n\n\ndef _quick_sort_between(a: List[int], low: int, high: int):\n    if low < high:\n        # get a random position as the pivot\n        k = random.randint(low, high)\n        a[low], a[k] = a[k], a[low]\n\n        m = _partition(a, low, high)  # a[m] is in final position\n        _quick_sort_between(a, low, m - 1)\n        _quick_sort_between(a, m + 1, high)\n\n\ndef _partition(a: List[int], low: int, high: int):\n    pivot, j = a[low], low\n    for i in range(low + 1, high + 1):\n        if a[i] <= pivot:\n            j += 1\n            a[j], a[i] = a[i], a[j]  # swap\n    a[low], a[j] = a[j], a[low]\n    return j\n\n\ndef test_quick_sort():\n    a1 = [3, 5, 6, 7, 8]\n    quick_sort(a1)\n    assert a1 == [3, 5, 6, 7, 8]\n    a2 = [2, 2, 2, 2]\n    quick_sort(a2)\n    assert a2 == [2, 2, 2, 2]\n    a3 = [4, 3, 2, 1]\n    quick_sort(a3)\n    assert a3 == [1, 2, 3, 4]\n    a4 = [5, -1, 9, 3, 7, 8, 3, -2, 9]\n    quick_sort(a4)\n    assert a4 == [-2, -1, 3, 3, 5, 7, 8, 9, 9]\n\n\nif __name__ == \"__main__\":\n    a1 = [3, 5, 6, 7, 8]\n    a2 = [2, 2, 2, 2]\n    a3 = [4, 3, 2, 1]\n    a4 = [5, -1, 9, 3, 7, 8, 3, -2, 9]\n    quick_sort(a1)\n    print(a1)\n    quick_sort(a2)\n    print(a2)\n    quick_sort(a3)\n    print(a3)\n    quick_sort(a4)\n    print(a4)\n"
  },
  {
    "path": "python/12_sorts/quicksort_twoway.py",
    "content": "import random\n\n\ndef QuickSort(arr):\n    # 双向排序: 提高非随机输入的性能\n    # 不需要额外的空间,在待排序数组本身内部进行排序\n    # 基准值通过random随机选取\n    # 入参: 待排序数组, 数组开始索引 0, 数组结束索引 len(array)-1\n    if arr is None or len(arr) < 1:\n        return arr\n\n    def swap(arr, low, upper):\n        tmp = arr[low]\n        arr[low] = arr[upper]\n        arr[upper] = tmp\n        return arr\n\n    def QuickSort_TwoWay(arr, low, upper):\n        # 小数组排序i可以用插入或选择排序\n        # if upper-low < 50 : return arr\n        # 基线条件: low index = upper index; 也就是只有一个值的区间\n        if low >= upper:\n            return arr\n        # 随机选取基准值, 并将基准值替换到数组第一个元素\n        swap(arr, low, int(random.uniform(low, upper)))\n        temp = arr[low]\n        # 缓存边界值, 从上下边界同时排序\n        i, j = low, upper\n        while True:\n            # 第一个元素是基准值,所以要跳过\n            i += 1\n            # 在小区间中, 进行排序\n            # 从下边界开始寻找大于基准值的索引\n            while i <= upper and arr[i] <= temp:\n                i += 1\n            # 从上边界开始寻找小于基准值的索引\n            # 因为j肯定大于i, 所以索引值肯定在小区间中\n            while arr[j] > temp:\n                j -= 1\n            # 如果小索引大于等于大索引, 说明排序完成, 退出排序\n            if i >= j:\n                break\n            swap(arr, i, j)\n        # 将基准值的索引从下边界调换到索引分割点\n        swap(arr, low, j)\n        QuickSort_TwoWay(arr, low, j - 1)\n        QuickSort_TwoWay(arr, j + 1, upper)\n        return arr\n\n    return QuickSort_TwoWay(arr, 0, len(arr) - 1)\n\n\nif __name__ == \"__main__\":\n    a1 = [3, 5, 6, 7, 8]\n    a2 = [2, 2, 2, 2]\n    a3 = [4, 3, 2, 1]\n    a4 = [5, -1, 9, 3, 7, 8, 3, -2, 9]\n    QuickSort(a1)\n    print(a1)\n    QuickSort(a2)\n    print(a2)\n    QuickSort(a3)\n    print(a3)\n    QuickSort(a4)\n    print(a4)\n"
  },
  {
    "path": "python/14_sorts/counting_sort.py",
    "content": "\"\"\"\n    计数排序\n\n    Author: Wenru\n\"\"\"\n\nfrom typing import List\nimport itertools\n\ndef counting_sort(a: List[int]):\n    if len(a) <= 1: return\n    \n    # a中有counts[i]个数不大于i\n    counts = [0] * (max(a) + 1)\n    for num in a:\n        counts[num] += 1\n    counts = list(itertools.accumulate(counts))\n\n    # 临时数组，储存排序之后的结果\n    a_sorted = [0] * len(a)\n    for num in reversed(a):\n        index = counts[num] - 1\n        a_sorted[index] = num\n        counts[num] -= 1\n    \n    a[:] = a_sorted\n\n\nif __name__ == \"__main__\":\n    a1 = [1, 2, 3, 4]\n    counting_sort(a1)\n    print(a1)\n\n    a2 = [1, 1, 1, 1]\n    counting_sort(a2)\n    print(a2)\n\n    a3 = [4, 5, 0, 9, 3, 3, 1, 9, 8, 7]\n    counting_sort(a3)\n    print(a3)\n"
  },
  {
    "path": "python/15_bsearch/bsearch.py",
    "content": "\"\"\"\n    Author: Wenru\n\"\"\"\n\nfrom typing import List\n\ndef bsearch(nums: List[int], target: int) -> int:\n    \"\"\"Binary search of a target in a sorted array\n    without duplicates. If such a target does not exist,\n    return -1, othewise, return its index.\n    \"\"\"\n    low, high = 0, len(nums) - 1\n    while low <= high:\n        mid = low + (high - low) // 2\n        if nums[mid] == target:\n            return mid\n        elif nums[mid] < target:\n            low = mid + 1\n        else:\n            high = mid - 1\n    \n    return -1\n"
  },
  {
    "path": "python/15_bsearch/bsearch_recursion.py",
    "content": "\"\"\"\n    Author: dreamkong\n\"\"\"\n\nfrom typing import List\n\n\ndef bsearch(nums: List[int], target: int) -> int:\n    return bsearch_internally(nums, 0, len(nums)-1, target)\n\n\ndef bsearch_internally(nums: List[int], low: int, high: int, target: int) -> int:\n    if low > high:\n        return -1\n\n    mid = low+int((high-low) >> 2)\n    if nums[mid] == target:\n        return mid\n    elif nums[mid] < target:\n        return bsearch_internally(nums, mid+1, high, target)\n    else:\n        return bsearch_internally(nums, low, mid-1, target)\n"
  },
  {
    "path": "python/16_bsearch/bsearch_variants.py",
    "content": "\"\"\"\n    Author: Wenru\n    Fix: nzjia\n\"\"\"\n\nfrom typing import List\n\ndef bsearch_left(nums: List[int], target: int) -> int:\n    \"\"\"Binary search of the index of the first element\n    equal to a given target in the ascending sorted array.\n    If not found, return -1.\n    \"\"\"\n    low, high = 0, len(nums) - 1\n    while low <= high:\n        mid = low + (high - low) // 2\n        if nums[mid] < target:\n            low = mid + 1\n        else:\n            high = mid - 1\n    if low < len(nums) and nums[low] == target:\n        return low\n    else:\n        return -1\n\n\ndef bsearch_right(nums: List[int], target: int) -> int:\n    \"\"\"Binary search of the index of the last element\n    equal to a given target in the ascending sorted array.\n    If not found, return -1.\n    \"\"\"\n    low, high = 0, len(nums) - 1\n    while low <= high:\n        mid = low + (high - low) // 2\n        if nums[mid] <= target:\n            low = mid + 1\n        else:\n            high = mid - 1\n    if high >= 0 and nums[high] == target:\n        return high\n    else:\n        return -1\n\n\ndef bsearch_left_not_less(nums: List[int], target: int) -> int:\n    \"\"\"Binary search of the index of the first element\n    not less than a given target in the ascending sorted array.\n    If not found, return -1.\n    \"\"\"\n    low, high = 0, len(nums) - 1\n    while low <= high:\n        mid = low + (high - low) // 2\n        if nums[mid] < target:\n            low = mid + 1\n        else:\n            high = mid - 1\n    if low < len(nums) and nums[low] >= target:\n        return low\n    else:\n        return -1\n\ndef bsearch_right_not_greater(nums: List[int], target: int) -> int:\n    \"\"\"Binary search of the index of the last element\n    not greater than a given target in the ascending sorted array.\n    If not found, return -1.\n    \"\"\"\n    low, high = 0, len(nums) - 1\n    while low <= high:\n        mid = low + (high - low) // 2\n        if nums[mid] <= target:\n            low = mid + 1\n        else:\n            high = mid - 1\n    if high >= 0 and nums[high] <= target:\n        return high\n    else:\n        return -1\n\nif __name__ == \"__main__\":\n    a = [1, 1, 2, 3, 4, 6, 7, 7, 7, 7, 10, 22]\n\n    print(bsearch_left(a, 0) == -1)\n    print(bsearch_left(a, 7) == 6)\n    print(bsearch_left(a, 30) == -1)\n\n    print(bsearch_right(a, 0) == -1)\n    print(bsearch_right(a, 7) == 9)\n    print(bsearch_right(a, 30) == -1)\n\n    print(bsearch_left_not_less(a, 0) == 0)\n    print(bsearch_left_not_less(a, 5) == 5)\n    print(bsearch_left_not_less(a, 30) == -1)\n\n    print(bsearch_right_not_greater(a, 0) == -1)\n    print(bsearch_right_not_greater(a, 6) == 5)\n    print(bsearch_right_not_greater(a, 30) == 11)\n"
  },
  {
    "path": "python/17_skiplist/skip_list.py",
    "content": "\"\"\"\n    An implementation of skip list.\n    The list stores positive integers without duplicates.\n\n    跳表的一种实现方法。\n    跳表中储存的是正整数，并且储存的是不重复的。\n\n    Author: Wenru\n\"\"\"\n\nfrom typing import Optional\nimport random\n\nclass ListNode:\n\n    def __init__(self, data: Optional[int] = None):\n        self._data = data\n        self._forwards = []   # Forward pointers\n\nclass SkipList:\n\n    _MAX_LEVEL = 16\n\n    def __init__(self):\n        self._level_count = 1\n        self._head = ListNode()\n        self._head._forwards = [None] * type(self)._MAX_LEVEL\n\n    def find(self, value: int) -> Optional[ListNode]:\n        p = self._head\n        for i in range(self._level_count - 1, -1, -1):   # Move down a level\n            while p._forwards[i] and p._forwards[i]._data < value:\n                p = p._forwards[i]   # Move along level\n        \n        return p._forwards[0] if p._forwards[0] and p._forwards[0]._data == value else None\n\n    def insert(self, value: int):\n        level = self._random_level()\n        if self._level_count < level: self._level_count = level\n        new_node = ListNode(value)\n        new_node._forwards = [None] * level\n        update = [self._head] * level     # update is like a list of prevs\n\n        p = self._head\n        for i in range(level - 1, -1, -1):\n            while p._forwards[i] and p._forwards[i]._data < value:\n                p = p._forwards[i]\n            \n            update[i] = p     # Found a prev\n\n        for i in range(level):\n            new_node._forwards[i] = update[i]._forwards[i]   # new_node.next = prev.next\n            update[i]._forwards[i] = new_node     # prev.next = new_node\n        \n    def delete(self, value):\n        update = [None] * self._level_count\n        p = self._head\n        for i in range(self._level_count - 1, -1, -1):\n            while p._forwards[i] and p._forwards[i]._data < value:\n                p = p._forwards[i]\n            update[i] = p\n        \n        if p._forwards[0] and p._forwards[0]._data == value:\n            for i in range(self._level_count - 1, -1, -1):\n                if update[i]._forwards[i] and update[i]._forwards[i]._data == value:\n                    update[i]._forwards[i] = update[i]._forwards[i]._forwards[i]     # Similar to prev.next = prev.next.next\n\n    def _random_level(self, p: float = 0.5) -> int:\n        level = 1\n        while random.random() < p and level < type(self)._MAX_LEVEL:\n            level += 1\n        return level\n\n    def __repr__(self) -> str:\n        values = []\n        p = self._head\n        while p._forwards[0]:\n            values.append(str(p._forwards[0]._data))\n            p = p._forwards[0]\n        return \"->\".join(values)\n\n\nif __name__ == \"__main__\":\n    l = SkipList()\n    for i in range(10):\n        l.insert(i)\n    print(l)\n    p = l.find(7)\n    print(p._data)\n    l.delete(3)\n    print(l)"
  },
  {
    "path": "python/17_skiplist/skip_list_comments.py",
    "content": "import random\n\n\nclass SkipListNode(object):\n    def __init__(self, val, high=1):\n        # 节点存储的值\n        self.data = val\n        # 节点对应索引层的深度\n        self.deeps = [None] * high\n\n\nclass SkipList(object):\n    \"\"\"\n        An implementation of skip list.\n        The list stores positive integers without duplicates.\n        跳表的一种实现方法。\n        跳表中储存的是正整数，并且储存的是不重复的。\n        Author: Ben\n    \"\"\"\n\n    def __init__(self):\n        # 索引层的最大深度\n        self.__MAX_LEVEL = 16\n        # 跳表的高度\n        self._high = 1\n        # 每一索引层的首节点, 默认值为None\n        self._head = SkipListNode(None, self.__MAX_LEVEL)\n\n    def find(self, val):\n        cur = self._head\n        # 从索引的顶层, 逐层定位要查找的值\n        # 索引层上下是对应的, 下层的起点是上一个索引层中小于插入值的最大值对应的节点\n        for i in range(self._high - 1, -1, -1):\n            # 同一索引层内, 查找小于插入值的最大值对应的节点\n            while cur.deeps[i] and cur.deeps[i].data < val:\n                cur = cur.deeps[i]\n\n        if cur.deeps[0] and cur.deeps[0].data == val:\n            return cur.deeps[0]\n        return None\n\n    def insert(self, val):\n        '''\n        新增时, 通过随机函数获取要更新的索引层数,\n        要对低于给定高度的索引层添加新结点的指针\n        '''\n        high = self.randomLevel()\n        if self._high < high:\n            self._high = high\n        # 申请新结点\n        newNode = SkipListNode(val, high)\n        # cache用来缓存对应索引层中小于插入值的最大节点\n        cache = [self._head] * high\n        cur = self._head\n\n        # 在低于随机高度的每一个索引层寻找小于插入值的节点\n        for i in range(high - 1, -1, -1):\n            # 每个索引层内寻找小于带插入值的节点\n            # ! 索引层上下是对应的, 下层的起点是上一个索引层中小于插入值的最大值对应的节点\n            while cur.deeps[i] and cur.deeps[i].data < val:\n                cur = cur.deeps[i]\n            cache[i] = cur\n\n        # 在小于高度的每个索引层中插入新结点\n        for i in range(high):\n            # new.next = prev.next \\ prev.next = new.next\n            newNode.deeps[i] = cache[i].deeps[i]\n            cache[i].deeps[i] = newNode\n\n    def delete(self, val):\n        '''\n        删除时, 要将每个索引层中对应的节点都删掉\n        '''\n        # cache用来缓存对应索引层中小于插入值的最大节点\n        cache = [None] * self._high\n        cur = self._head\n        # 缓存每一个索引层定位小于插入值的节点\n        for i in range(self._high - 1, -1, -1):\n            while cur.deeps[i] and cur.deeps[i].data < val:\n                cur = cur.deeps[i]\n            cache[i] = cur\n        # 如果给定的值存在, 更新索引层中对应的节点\n        if cur.deeps[0] and cur.deeps[0].data == val:\n            for i in range(self._high):\n                if cache[i].deeps[i] and cache[i].deeps[i].data == val:\n                    cache[i].deeps[i] = cache[i].deeps[i].deeps[i]\n\n    def randomLevel(self, p=0.25):\n        '''\n        #define ZSKIPLIST_P 0.25      /* Skiplist P = 1/4 */\n        https://github.com/antirez/redis/blob/unstable/src/t_zset.c\n        '''\n        high = 1\n        for _ in range(self.__MAX_LEVEL - 1):\n            if random.random() < p:\n                high += 1\n        return high\n\n    def __repr__(self):\n        vals = []\n        p = self._head\n        while p.deeps[0]:\n            vals.append(str(p.deeps[0].data))\n            p = p.deeps[0]\n        return '->'.join(vals)\n\n\nif __name__ == '__main__':\n    sl = SkipList()\n    for i in range(100):\n        sl.insert(i)\n    print(sl)\n    p = sl.find(7)\n    print(p.data)\n    sl.delete(37)\n    print(sl)\n    sl.delete(37.5)\n    print(sl)\n"
  },
  {
    "path": "python/23_binarytree/binary_search_tree.py",
    "content": "#!/usr/bin/python\n# -*- coding: UTF-8 -*-\n\nfrom queue import Queue\nimport math\n\n\nclass TreeNode:\n    def __init__(self, val=None):\n        self.val = val\n        self.left = None\n        self.right = None\n        self.parent = None\n\n\nclass BinarySearchTree:\n    def __init__(self, val_list=[]):\n        self.root = None\n        for n in val_list:\n            self.insert(n)\n\n    def insert(self, data):\n        \"\"\"\n        插入\n        :param data:\n        :return:\n        \"\"\"\n        assert(isinstance(data, int))\n\n        if self.root is None:\n            self.root = TreeNode(data)\n        else:\n            n = self.root\n            while n:\n                p = n\n                if data < n.val:\n                    n = n.left\n                else:\n                    n = n.right\n\n            new_node = TreeNode(data)\n            new_node.parent = p\n            \n            if data < p.val:\n                p.left = new_node\n            else:\n                p.right = new_node\n\n        return True\n\n    def search(self, data):\n        \"\"\"\n        搜索\n        返回bst中所有值为data的节点列表\n        :param data:\n        :return:\n        \"\"\"\n        assert(isinstance(data, int))\n\n        # 所有搜索到的节点\n        ret = []\n\n        n = self.root\n        while n:\n            if data < n.val:\n                n = n.left\n            else:\n                if data == n.val:\n                    ret.append(n)\n                n = n.right\n\n        return ret\n\n    def delete(self, data):\n        \"\"\"\n        删除\n        :param data:\n        :return:\n        \"\"\"\n        assert (isinstance(data, int))\n\n        # 通过搜索得到需要删除的节点\n        del_list = self.search(data)\n\n        for n in del_list:\n            # 父节点为空，又不是根节点，已经不在树上，不用再删除\n            if n.parent is None and n != self.root:\n                continue\n            else:\n                self._del(n)\n\n    def _del(self, node):\n        \"\"\"\n        删除\n        所删除的节点N存在以下情况：\n        1. 没有子节点：直接删除N的父节点指针\n        2. 有一个子节点：将N父节点指针指向N的子节点\n        3. 有两个子节点：找到右子树的最小节点M，将值赋给N，然后删除M\n        :param data:\n        :return:\n        \"\"\"\n        # 1\n        if node.left is None and node.right is None:\n            # 情况1和2，根节点和普通节点的处理方式不同\n            if node == self.root:\n                self.root = None\n            else:\n                if node.val < node.parent.val:\n                    node.parent.left = None\n                else:\n                    node.parent.right = None\n\n                node.parent = None\n        # 2\n        elif node.left is None and node.right is not None:\n            if node == self.root:\n                self.root = node.right\n                self.root.parent = None\n                node.right = None\n            else:\n                if node.val < node.parent.val:\n                    node.parent.left = node.right\n                else:\n                    node.parent.right = node.right\n\n                node.right.parent = node.parent\n                node.parent = None\n                node.right = None\n        elif node.left is not None and node.right is None:\n            if node == self.root:\n                self.root = node.left\n                self.root.parent = None\n                node.left = None\n            else:\n                if node.val < node.parent.val:\n                    node.parent.left = node.left\n                else:\n                    node.parent.right = node.left\n\n                node.left.parent = node.parent\n                node.parent = None\n                node.left = None\n        # 3\n        else:\n            min_node = node.right\n            # 找到右子树的最小值节点\n            if min_node.left:\n                min_node = min_node.left\n\n            if node.val != min_node.val:\n                node.val = min_node.val\n                self._del(min_node)\n            # 右子树的最小值节点与被删除节点的值相等，再次删除原节点\n            else:\n                self._del(min_node)\n                self._del(node)\n\n    def get_min(self):\n        \"\"\"\n        返回最小值节点\n        :return:\n        \"\"\"\n        if self.root is None:\n            return None\n\n        n = self.root\n        while n.left:\n            n = n.left\n        return n.val\n\n    def get_max(self):\n        \"\"\"\n        返回最大值节点\n        :return:\n        \"\"\"\n        if self.root is None:\n            return None\n\n        n = self.root\n        while n.right:\n            n = n.right\n        return n.val\n\n    def in_order(self):\n        \"\"\"\n        中序遍历\n        :return:\n        \"\"\"\n        if self.root is None:\n            return []\n\n        return self._in_order(self.root)\n\n    def _in_order(self, node):\n        if node is None:\n            return []\n\n        ret = []\n        n = node\n        ret.extend(self._in_order(n.left))\n        ret.append(n.val)\n        ret.extend(self._in_order(n.right))\n        \n        return ret\n\n    def __repr__(self):\n        # return str(self.in_order())\n        print(str(self.in_order()))\n        return self._draw_tree()\n\n    def _bfs(self):\n        \"\"\"\n        bfs\n        通过父子关系记录节点编号\n        :return:\n        \"\"\"\n        if self.root is None:\n            return []\n\n        ret = []\n        q = Queue()\n        # 队列[节点，编号]\n        q.put((self.root, 1))\n\n        while not q.empty():\n            n = q.get()\n\n            if n[0] is not None:\n                ret.append((n[0].val, n[1]))\n                q.put((n[0].left, n[1]*2))\n                q.put((n[0].right, n[1]*2+1))\n\n        return ret\n\n    def _draw_tree(self):\n        \"\"\"\n        可视化\n        :return:\n        \"\"\"\n        nodes = self._bfs()\n        \n        if not nodes:\n            print('This tree has no nodes.')\n            return\n\n        layer_num = int(math.log(nodes[-1][1], 2)) + 1\n        \n        prt_nums = []\n        \n        for i in range(layer_num):\n            prt_nums.append([None]*2**i)\n\n        for v, p in nodes:\n            row = int(math.log(p ,2))\n            col = p % 2**row\n            prt_nums[row][col] = v\n\n        prt_str = ''\n        for l in prt_nums:\n            prt_str += str(l)[1:-1] + '\\n'\n\n        return prt_str\n\n\nif __name__ == '__main__':\n    nums = [4, 2, 5, 6, 1, 7, 3]\n    bst = BinarySearchTree(nums)\n    print(bst)\n\n    # 插入\n    bst.insert(1)\n    bst.insert(4)\n    print(bst)\n\n    # 搜索\n    for n in bst.search(2):\n        print(n.parent.val, n.val)\n\n    # 删除\n    bst.insert(6)\n    bst.insert(7)\n    print(bst)\n    bst.delete(7)\n    print(bst)\n    bst.delete(6)\n    print(bst)\n    bst.delete(4)\n    print(bst)\n\n    # min max\n    print(bst.get_max())\n    print(bst.get_min())\n"
  },
  {
    "path": "python/23_binarytree/binary_tree.py",
    "content": "\"\"\"\n    Pre-order, in-order and post-order traversal of binary trees.\n\n    Author: Wenru Dong\n\"\"\"\nfrom typing import TypeVar, Generic, Generator, Optional\n\nT = TypeVar(\"T\")\n\nclass TreeNode(Generic[T]):\n    def __init__(self, value: T):\n        self.val = value\n        self.left = None\n        self.right = None\n    \n\n# Pre-order traversal\ndef pre_order(root: Optional[TreeNode[T]]) -> Generator[T, None, None]:\n    if root:\n        yield root.val\n        yield from pre_order(root.left)\n        yield from pre_order(root.right)\n\n# In-order traversal\ndef in_order(root: Optional[TreeNode[T]]) -> Generator[T, None, None]:\n    if root:\n        yield from in_order(root.left)\n        yield root.val\n        yield from in_order(root.right)\n\n# Post-order traversal\ndef post_order(root: Optional[TreeNode[T]]) -> Generator[T, None, None]:\n    if root:\n        yield from post_order(root.left)\n        yield from post_order(root.right)\n        yield root.val\n\n\nif __name__ == \"__main__\":\n\n    singer = TreeNode(\"Taylor Swift\")\n\n    genre_country = TreeNode(\"Country\")\n    genre_pop = TreeNode(\"Pop\")\n\n    album_fearless = TreeNode(\"Fearless\")\n    album_red = TreeNode(\"Red\")\n    album_1989 = TreeNode(\"1989\")\n    album_reputation = TreeNode(\"Reputation\")\n\n    song_ls = TreeNode(\"Love Story\")\n    song_wh = TreeNode(\"White Horse\")\n    song_wanegbt = TreeNode(\"We Are Never Ever Getting Back Together\")\n    song_ikywt = TreeNode(\"I Knew You Were Trouble\")\n    song_sio = TreeNode(\"Shake It Off\")\n    song_bb = TreeNode(\"Bad Blood\")\n    song_lwymmd = TreeNode(\"Look What You Made Me Do\")\n    song_g = TreeNode(\"Gorgeous\")\n\n    singer.left, singer.right = genre_country, genre_pop\n    genre_country.left, genre_country.right = album_fearless, album_red\n    genre_pop.left, genre_pop.right = album_1989, album_reputation\n    album_fearless.left, album_fearless.right = song_ls, song_wh\n    album_red.left, album_red.right = song_wanegbt, song_ikywt\n    album_1989.left, album_1989.right = song_sio, song_bb\n    album_reputation.left, album_reputation.right = song_lwymmd, song_g\n\n    print(list(pre_order(singer)))\n    print(list(in_order(singer)))\n    print(list(post_order(singer)))\n"
  },
  {
    "path": "python/24_tree/binary_search_tree.py",
    "content": "\"\"\"\n    Binary search tree\n\n    Author: Wenru Dong\n\"\"\"\nfrom typing import Optional\n\nclass TreeNode:\n    def __init__(self, value: int):\n        self.val = value\n        self.left = None\n        self.right = None\n\nclass BinarySearchTree:\n    def __init__(self):\n        self._root = None\n    \n    def find(self, value: int) -> Optional[TreeNode]:\n        node = self._root\n        while node and node.val != value:\n            node = node.left if node.val > value else node.right\n        return node\n    \n    def insert(self, value: int):\n        if not self._root:\n            self._root = TreeNode(value)\n            return\n        parent = None\n        node = self._root\n        while node:\n            parent = node\n            node = node.left if node.val > value else node.right\n        new_node = TreeNode(value)\n        if parent.val > value:\n            parent.left = new_node\n        else:\n            parent.right = new_node\n\n    def delete(self, value: int):\n        node = self._root\n        parent = None\n        while node and node.val != value:\n            parent = node\n            node = node.left if node.val > value else node.right\n        if not node: return\n        \n        # 要删除的节点有两个子节点\n        if node.left and node.right:\n            successor = node.right\n            successor_parent = node\n            while successor.left:\n                successor_parent = successor\n                successor = successor.left\n            node.val = successor.val\n            parent, node = successor_parent, successor\n        \n        # 删除节点是叶子节点或者仅有一个子节点\n        child = node.left if node.left else node.right\n        if not parent:\n            self._root = child\n        elif parent.left == node:\n            parent.left = child\n        else:\n            parent.right = child\n"
  },
  {
    "path": "python/26_red_black_tree/red_black_tree.py",
    "content": "#!/usr/bin/python\n# -*- coding: UTF-8 -*-\n\nfrom queue import Queue\nimport pygraphviz as pgv\nimport random\n\nOUTPUT_PATH = 'E:/'\n\n\nclass TreeNode:\n    def __init__(self, val=None, color=None):\n        self.val = val\n        assert color in ['r', 'b']\n        self.color = 'red' if color == 'r' else 'black'\n\n        self.left = None\n        self.right = None\n        self.parent = None\n\n    def is_black(self):\n        return self.color == 'black'\n\n    def set_black(self):\n        self.color = 'black'\n        return\n\n    def set_red(self):\n        self.color = 'red'\n\n\nclass RedBlackTree:\n    \"\"\"\n    红黑树实现\n    参考资料：\n    1. 《算法导论》\n    第13章 红黑树\n    13.3 插入 p178\n    13.4 删除 p183\n\n    2. 红黑树（二）：删除\n    https://zhuanlan.zhihu.com/p/25402654\n    \"\"\"\n    def __init__(self, val_list=None):\n        self.root = None\n        self.black_leaf = TreeNode(color='b')  # 共用的黑色叶子节点\n\n        # 可用数组初始化\n        if type(val_list) is list:\n            for n in val_list:\n                assert type(n) is int\n                self.insert(n)\n\n    def search(self, val):\n        \"\"\"\n        搜索\n        :param val:\n        :return:\n        \"\"\"\n        if self.root is None:\n            return None\n\n        n = self.root\n        while n != self.black_leaf:\n            if val < n.val:\n                n = n.left\n            elif val > n.val:\n                n = n.right\n            else:\n                return n\n        return None\n\n    def insert(self, val):\n        \"\"\"\n        插入\n        :param val:\n        :return:\n        \"\"\"\n        assert type(val) is int\n\n        new_node = TreeNode(val, 'r')  # 新插入的节点为红色\n\n        # 根节点\n        if self.root is None:\n            self.root = new_node\n        else:\n            n = self.root\n            while n != self.black_leaf:  # 黑色叶子节点\n                p = n\n                if val < n.val:\n                    n = n.left\n                elif val > n.val:\n                    n = n.right\n                else:\n                    raise KeyError('val:{} already exists')  # 该值已存在，插入失败\n\n            if val < p.val:\n                p.left = new_node\n            else:\n                p.right = new_node\n            new_node.parent = p\n\n        new_node.left = new_node.right = self.black_leaf\n        # 插入后调整\n        self._insert_fixup(new_node)\n\n    def _insert_fixup(self, node):\n        \"\"\"\n        插入调整\n        参考资料：《算法导论》 13.3 p178-179\n        :param node:\n        :return:\n        \"\"\"\n        n = node\n        while n is not self.root and not n.parent.is_black():\n            # 父p 叔u 祖父g\n            p = self.parent(n)\n            u = self.bro(p)\n            g = self.parent(p)\n\n            if not u.is_black():        # case 1\n                p.set_black()           # case 1\n                u.set_black()           # case 1\n                g.set_red()             # case 1\n                n = g                   # case 1\n                continue\n\n            if p == g.left:     # p为左结点\n                if n == p.right:        # case 2\n                    self.rotate_l(p)    # case 2\n                    n, p = p, n         # case 2\n                p.set_black()           # case 3\n                g.set_red()             # case 3\n                self.rotate_r(g)        # case 3\n            else:               # p为右节点\n                if n == p.left:         # case 2\n                    self.rotate_r(p)    # case 2\n                    n, p = p, n         # case 2\n                p.set_black()           # case 3\n                g.set_red()             # case 3\n                self.rotate_l(g)        # case 3\n\n        # 根节点强制置黑，有两种情况根节点是红色：\n        # 1. 新插入时是红色\n        # 2. 经过case 1调整过后变红色\n        self.root.color = 'black'\n\n    def delete(self, val):\n        \"\"\"\n        删除\n        :param val:\n        :return:\n        \"\"\"\n        assert type(val) is int\n\n        n = self.search(val)\n        if n is None:\n            print('can not find any nodes with value: {}'.format(val))\n            return\n\n        self._delete_node(n)\n\n    def _delete_node(self, node):\n        \"\"\"\n        删除节点内部实现\n        参考资料：《算法导论》 13.4 p183-184\n        实现方式有微调，当n有2个子节点时，将s拷贝至n，转为删除s(s最多有一个子节点)\n        :param node:\n        :return:\n        \"\"\"\n        n = node\n\n        # n的子节点个数等于2\n        if self.children_count(n) == 2:\n            # 寻找n的后继s\n            s = n.right\n            while s.left != self.black_leaf:\n                s = s.left\n            n.val = s.val\n            # 将删除n转化为删除s\n            n = s\n\n        # n的子节点个数小于2\n        if n.left == self.black_leaf:\n            c = n.right\n        else:\n            c = n.left\n        self._transplant(n, c)\n\n        # 删除的节点是黑色，需要调整\n        if n.is_black():\n            self._delete_fixup(c)\n        return\n\n    def _delete_fixup(self, node):\n        \"\"\"\n        删除调整\n        参考资料：《算法导论》 13.4 p185-187\n        :param node:\n        :return:\n        \"\"\"\n        n = node\n        while n != self.root and n.is_black():\n            p = self.parent(n)\n            b = self.bro(n)\n\n            # 左右节点对称\n            if p.left == n:\n                if not b.is_black():\n                    b.set_black()                   # case 1\n                    p.set_red()                     # case 1\n                    self.rotate_l(p)                # case 1\n                    # new bro after rotate\n                    b = self.bro(n)                 # case 1\n\n                if b.left.is_black() and b.right.is_black():\n                    b.set_red()                     # case 2\n                    n = p                           # case 2\n                else:\n                    if b.right.is_black():\n                        b.left.set_black()          # case 3\n                        b.set_red()                 # case 3\n                        self.rotate_r(b)            # case 3\n                        # new bro after rotate\n                        b = self.bro(n)             # case 3\n\n                    # 注意，因为p可能是红或黑，所以不能直接赋值颜色，只能copy\n                    b.color = p.color               # case 4\n                    p.set_black()                   # case 4\n                    b.right.set_black()             # case 4\n                    self.rotate_l(p)                # case 4\n                    # trick, 调整结束跳出while\n                    n = self.root                   # case 4\n            else:\n                if not b.is_black():\n                    b.set_black()                   # case 1\n                    p.set_red()                     # case 1\n                    self.rotate_r(p)                # case 1\n                    # new bro after rotate\n                    b = self.bro(n)                 # case 1\n\n                if b.left.is_black() and b.right.is_black():\n                    b.set_red()                     # case 2\n                    n = p                           # case 2\n                else:\n                    if b.left.is_black():\n                        b.right.set_black()         # case 3\n                        b.set_red()                 # case 3\n                        self.rotate_l(b)            # case 3\n                        # new bro after rotate\n                        b = self.bro(n)             # case 3\n\n                    # 注意，因为p可能是红或黑，所以不能直接赋值颜色，只能copy\n                    b.color = p.color               # case 4\n                    p.set_black()                   # case 4\n                    b.left.set_black()              # case 4\n                    self.rotate_r(p)                # case 4\n                    # trick, 调整结束跳出while\n                    n = self.root                   # case 4\n\n        # 将n设为黑色，从上面while循环跳出，情况有两种\n        # 1. n是根节点，直接无视附加的黑色\n        # 2. n是红色的节点，则染黑\n        n.set_black()\n\n    def _transplant(self, n1, n2):\n        \"\"\"\n        节点移植， n2 -> n1\n        :param n1: 原节点\n        :param n2: 移植节点\n        :return:\n        \"\"\"\n        if n1 == self.root:\n            if n2 != self.black_leaf:\n                self.root = n2\n                n2.parent = None\n            else:\n                self.root = None    # 只有删除根节点时会进来\n        else:\n            p = self.parent(n1)\n            if p.left == n1:\n                p.left = n2\n            else:\n                p.right = n2\n\n            n2.parent = p\n\n    def rotate_l(self, node):\n        \"\"\"\n        左旋\n        :param node:\n        :return:\n        \"\"\"\n        if node is None:\n            return\n\n        if node.right is self.black_leaf:\n            return\n            # raise Exception('try rotate left , but the node \"{}\" has no right child'.format(node.val))\n\n        p = self.parent(node)\n        x = node\n        y = node.right\n\n        # node为根节点时，p为None，旋转后要更新根节点指向\n        if p is not None:\n            if x == p.left:\n                p.left = y\n            else:\n                p.right = y\n        else:\n            self.root = y\n\n        x.parent, y.parent = y, p\n\n        if y.left != self.black_leaf:\n            y.left.parent = x\n\n        x.right, y.left = y.left, x\n\n    def rotate_r(self, node):\n        \"\"\"\n        右旋\n        :param node:\n        :return:\n        \"\"\"\n        if node is None:\n            return\n\n        if node.left is self.black_leaf:\n            return\n            # raise Exception('try rotate right , but the node \"{}\" has no left child'.format(node.val))\n\n        p = self.parent(node)\n        x = node\n        y = node.left\n\n        # 同左旋\n        if p is not None:\n            if x == p.left:\n                p.left = y\n            else:\n                p.right = y\n        else:\n            self.root = y\n\n        x.parent, y.parent = y, p\n\n        if y.right is not None:\n            y.right.parent = x\n\n        x.left, y.right = y.right, x\n\n    @staticmethod\n    def bro(node):\n        \"\"\"\n        获取兄弟节点\n        :param node:\n        :return:\n        \"\"\"\n        if node is None or node.parent is None:\n            return None\n        else:\n            p = node.parent\n            if node == p.left:\n                return p.right\n            else:\n                return p.left\n\n    @staticmethod\n    def parent(node):\n        \"\"\"\n        获取父节点\n        :param node:\n        :return:\n        \"\"\"\n        if node is None:\n            return None\n        else:\n            return node.parent\n\n    def children_count(self, node):\n        \"\"\"\n        获取子节点个数\n        :param node:\n        :return:\n        \"\"\"\n        return 2 - [node.left, node.right].count(self.black_leaf)\n\n    def draw_img(self, img_name='Red_Black_Tree.png'):\n        \"\"\"\n        画图\n        用pygraphviz画出节点和箭头\n        箭头的红色和黑色分别代表左和右\n        :param img_name:\n        :return:\n        \"\"\"\n        if self.root is None:\n            return\n\n        tree = pgv.AGraph(directed=True, strict=True)\n\n        q = Queue()\n        q.put(self.root)\n\n        while not q.empty():\n            n = q.get()\n            if n != self.black_leaf:  # 黑色叶子的连线由各个节点自己画\n                tree.add_node(n.val, color=n.color)\n                #  画父节点箭头\n                # if n.parent is not None:\n                #     tree.add_edge(n.val, n.parent.val)\n\n                for c in [n.left, n.right]:\n                    q.put(c)\n                    color = 'red' if c == n.left else 'black'\n                    if c != self.black_leaf:\n                        tree.add_edge(n.val, c.val, color=color)\n                    else:\n                        tree.add_edge(n.val, 'None', color=color)\n\n        tree.graph_attr['epsilon'] = '0.01'\n        tree.layout('dot')\n        tree.draw(OUTPUT_PATH + img_name)\n        return True\n\n\nif __name__ == '__main__':\n    rbt = RedBlackTree()\n\n    # insert\n    nums = list(range(1, 25))\n    # random.shuffle(nums)\n    for num in nums:\n        rbt.insert(num)\n\n    # search\n    search_num = 23\n    n = rbt.search(search_num)\n    if n is not None:\n        print(n)\n    else:\n        print('node {} not found'.format(search_num))\n\n    # delete\n    rbt.delete(4)\n\n    # draw image\n    rbt.draw_img('rbt.png')\n"
  },
  {
    "path": "python/28_binary_heap/binary_heap.py",
    "content": "#!/usr/bin/python\n# -*- coding: UTF-8 -*-\n\nimport math\nimport random\n\n\nclass BinaryHeap:\n    \"\"\"\n    大顶堆\n    \"\"\"\n    def __init__(self, data=None, capacity=100):\n        self._data = []\n        self._capacity = capacity\n        if type(data) is list:\n            if len(data) > self._capacity:\n                raise Exception('Heap oversize, capacity:{}, data size:{}'.format(self._capacity, len(data)))\n            self._type_assert(data)\n            self._data = data\n\n        self._length = len(self._data)\n\n    def heapify(self):\n        \"\"\"\n        堆化\n        :return:\n        \"\"\"\n        self._heapify(self._data, self._length-1)\n\n    def _heapify(self, data, tail_idx):\n        \"\"\"\n        堆化内部实现\n        :param data: 需要堆化的数据\n        :param tail_idx: 尾元素的索引\n        :return:\n        \"\"\"\n        # heapify data[:tail_idx+1]\n        if tail_idx <= 0:\n            return\n\n        # idx of the Last Parent node\n        lp = (tail_idx - 1) // 2\n\n        for i in range(lp, -1, -1):\n            self._heap_down(data, i, tail_idx)\n\n    @staticmethod\n    def _heap_down(data, idx, tail_idx):\n        \"\"\"\n        将指定的位置堆化\n        :param data: 需要堆化的数据\n        :param idx: data: 中需要堆化的位置\n        :param tail_idx: 尾元素的索引\n        :return:\n        \"\"\"\n        assert type(data) is list\n\n        lp = (tail_idx - 1) // 2\n        # top-down\n        while idx <= lp:\n            # Left and Right Child index\n            lc = 2 * idx + 1\n            rc = lc + 1\n\n            # right child exists\n            if rc <= tail_idx:\n                tmp = lc if data[lc] > data[rc] else rc\n            else:\n                tmp = lc\n\n            if data[tmp] > data[idx]:\n                data[tmp], data[idx] = data[idx], data[tmp]\n                idx = tmp\n            else:\n                break\n\n    def insert(self, num):\n        \"\"\"\n        插入\n        :param num:\n        :return:\n        \"\"\"\n        if self._length < self._capacity:\n            if self._insert(self._data, num):\n                self._length += 1\n                return True\n        return False\n\n    @staticmethod\n    def _insert(data, num):\n        \"\"\"\n        堆中插入元素的内部实现\n        :param data:\n        :param num:\n        :return:\n        \"\"\"\n        assert type(data) is list\n        assert type(num) is int\n\n        data.append(num)\n        length = len(data)\n\n        # idx of New Node\n        nn = length - 1\n        # bottom-up\n        while nn > 0:\n            p = (nn-1) // 2\n            if data[nn] > data[p]:\n                data[nn], data[p] = data[p], data[nn]\n                nn = p\n            else:\n                break\n\n        return True\n\n    def get_top(self):\n        \"\"\"\n        取堆顶\n        :return:\n        \"\"\"\n        if self._length <= 0:\n            return None\n        return self._data[0]\n\n    def remove_top(self):\n        \"\"\"\n        取堆顶\n        :return:\n        \"\"\"\n        ret = None\n        if self._length > 0:\n            ret = self._remove_top(self._data)\n            self._length -= 1\n        return ret\n\n    @staticmethod\n    def _remove_top(data):\n        \"\"\"\n        取堆顶内部实现\n        :param data:\n        :return:\n        \"\"\"\n        assert type(data) is list\n\n        length = len(data)\n        if length == 0:\n            return None\n\n        data[0], data[-1] = data[-1], data[0]\n        ret = data.pop()\n        length -= 1\n\n        # length == 0 or == 1, return\n        if length > 1:\n            BinaryHeap._heap_down(data, 0, length-1)\n\n        return ret\n\n    @staticmethod\n    def _type_assert(nums):\n        assert type(nums) is list\n        for n in nums:\n            assert type(n) is int\n\n    @staticmethod\n    def _draw_heap(data):\n        \"\"\"\n        格式化打印\n        :param data:\n        :return:\n        \"\"\"\n        length = len(data)\n\n        if length == 0:\n            return 'empty heap'\n\n        ret = ''\n        for i, n in enumerate(data):\n            ret += str(n)\n            # 每行最后一个换行\n            if i == 2**int(math.log(i+1, 2)+1) - 2 or i == len(data) - 1:\n                ret += '\\n'\n            else:\n                ret += ', '\n\n        return ret\n\n    def __repr__(self):\n        return self._draw_heap(self._data)\n\n\nif __name__ == '__main__':\n    nums = list(range(10))\n    random.shuffle(nums)\n\n    bh = BinaryHeap(nums)\n    print('--- before heapify ---')\n    print(bh)\n\n    # heapify\n    bh.heapify()\n    print('--- after heapify ---')\n    print(bh)\n\n    # insert\n    print('--- insert ---')\n    if bh.insert(8):\n        print('insert success')\n    else:\n        print('insert fail')\n    print(bh)\n\n    # get top\n    print('--- get top ---')\n    print('get top of the heap: {}'.format(bh.get_top()))\n    bh.remove_top()\n    print(bh)\n"
  },
  {
    "path": "python/28_binary_heap/binary_heap_sort.py",
    "content": "#!/usr/bin/python\n# -*- coding: UTF-8 -*-\n\nfrom binary_heap import BinaryHeap\n\n\nclass BinaryHeapSort(BinaryHeap):\n    def __init__(self):\n        super(BinaryHeapSort, self).__init__()\n\n    def sort(self, nums):\n        \"\"\"\n        排序\n        1. 堆化，大顶堆\n        2. 排序，从后往前遍历，首尾元素互换，子数组堆化\n        :param nums:\n        :return:\n        \"\"\"\n        assert type(nums) is list\n        length = len(nums)\n\n        if length <= 1:\n            return\n\n        self._type_assert(nums)\n\n        # heapify\n        self._heapify(nums, length-1)\n\n        # sort\n        for i in range(length-1, 0, -1):\n            nums[0], nums[i] = nums[i], nums[0]\n            self._heap_down(nums, 0, i-1)\n\n        return\n\n\nif __name__ == '__main__':\n    bhs = BinaryHeapSort()\n    nums = [3, 5, 2, 6, 1, 7, 6]\n\n    print('--- before sort ---')\n    print(nums)\n\n    bhs.sort(nums)\n    print('--- after sort ---')\n    print(nums)\n"
  },
  {
    "path": "python/28_binary_heap/heap.py",
    "content": "#!/usr/bin/python\n# -*- coding: UTF-8 -*-\n\nimport math\nimport random\n\n\nclass Heap:\n    def __init__(self, nums=None, capacity=100):\n        self._data = []\n        self._capacity = capacity\n        if type(nums) == list and len(nums) <= self._capacity:\n            for n in nums:\n                assert type(n) is int\n                self._data.append(n)\n        self._length = len(self._data)\n        self._heapify()\n\n    def _heapify(self):\n        if self._length <= 1:\n            return\n\n        # idx of the Last Parent node\n        lp = (self._length - 2) // 2\n\n        for i in range(lp, -1, -1):\n            self._heap_down(i)\n\n    def _heap_down(self, idx):\n        pass\n\n    def insert(self, num):\n        pass\n\n    def get_top(self):\n        if self._length <= 0:\n            return None\n        return self._data[0]\n\n    def remove_top(self):\n        if self._length <= 0:\n            return None\n\n        self._data[0], self._data[-1] = self._data[-1], self._data[0]\n        ret = self._data.pop()\n        self._length -= 1\n        self._heap_down(0)\n\n        return ret\n\n    def get_data(self):\n        return self._data\n\n    def get_length(self):\n        return self._length\n\n    @staticmethod\n    def _draw_heap(data):\n        \"\"\"\n        格式化打印\n        :param data:\n        :return:\n        \"\"\"\n        length = len(data)\n\n        if length == 0:\n            return 'empty heap'\n\n        ret = ''\n        for i, n in enumerate(data):\n            ret += str(n)\n            # 每行最后一个换行\n            if i == 2 ** int(math.log(i + 1, 2) + 1) - 2 or i == len(data) - 1:\n                ret += '\\n'\n            else:\n                ret += ', '\n\n        return ret\n\n    def __repr__(self):\n        return self._draw_heap(self._data)\n\n\nclass MaxHeap(Heap):\n    def _heap_down(self, idx):\n        if self._length <= 1:\n            return\n\n        lp = (self._length - 2) // 2\n\n        while idx <= lp:\n            lc = 2 * idx + 1\n            rc = lc + 1\n\n            if rc <= self._length-1:\n                tmp = lc if self._data[lc] > self._data[rc] else rc\n            else:\n                tmp = lc\n\n            if self._data[tmp] > self._data[idx]:\n                self._data[tmp], self._data[idx] = self._data[idx], self._data[tmp]\n                idx = tmp\n            else:\n                break\n\n    def insert(self, num):\n        if self._length >= self._capacity:\n            return False\n\n        self._data.append(num)\n        self._length += 1\n\n        nn = self._length - 1\n        while nn > 0:\n            p = (nn-1) // 2\n\n            if self._data[nn] > self._data[p]:\n                self._data[nn], self._data[p] = self._data[p], self._data[nn]\n                nn = p\n            else:\n                break\n\n        return True\n\n\nclass MinHeap(Heap):\n    def _heap_down(self, idx):\n        if self._length <= 1:\n            return\n\n        lp = (self._length - 2) // 2\n\n        while idx <= lp:\n            lc = 2 * idx + 1\n            rc = lc + 1\n\n            if rc <= self._length-1:\n                tmp = lc if self._data[lc] < self._data[rc] else rc\n            else:\n                tmp = lc\n\n            if self._data[tmp] < self._data[idx]:\n                self._data[tmp], self._data[idx] = self._data[idx], self._data[tmp]\n                idx = tmp\n            else:\n                break\n\n    def insert(self, num):\n        if self._length >= self._capacity:\n            return False\n\n        self._data.append(num)\n        self._length += 1\n\n        nn = self._length - 1\n        while nn > 0:\n            p = (nn-1) // 2\n\n            if self._data[nn] < self._data[p]:\n                self._data[nn], self._data[p] = self._data[p], self._data[nn]\n                nn = p\n            else:\n                break\n\n        return True\n\n\nif __name__ == '__main__':\n    nums = list(range(10))\n    random.shuffle(nums)\n\n    max_h = MaxHeap(nums)\n    print('--- max heap ---')\n    print(max_h)\n\n    print('--- min heap ---')\n    min_h = MinHeap(nums)\n    print(min_h)\n"
  },
  {
    "path": "python/28_binary_heap/priority_queue.py",
    "content": "#!/usr/bin/python\n# -*- coding: UTF-8 -*-\n\nimport math\n\n\nclass QueueNode:\n    def __init__(self, priority, data=None):\n        assert type(priority) is int and priority >= 0\n        self.priority = priority\n        self.data = data\n\n    def __repr__(self):\n        return str((self.priority, self.data))\n\n\nclass PriorityQueue:\n    def __init__(self, capacity=100):\n        self._capacity = capacity\n        self._q = []\n        self._length = 0\n\n    def enqueue(self, priority, data=None):\n        if self._length >= self._capacity:\n            return False\n\n        new_node = QueueNode(priority, data)\n        self._q.append(new_node)\n        self._length += 1\n\n        # update queue\n        nn = self._length - 1\n        while nn > 0:\n            p = (nn - 1) // 2\n            if self._q[nn].priority < self._q[p].priority:\n                self._q[nn], self._q[p] = self._q[p], self._q[nn]\n                nn = p\n            else:\n                break\n\n        return True\n\n    def dequeue(self):\n        if self._length <= 0:\n            raise Exception('the queue is empty....')\n\n        self._q[0], self._q[-1] = self._q[-1], self._q[0]\n        ret = self._q.pop()\n        self._length -= 1\n\n        if self._length > 1:\n            # update queue\n            lp = (self._length - 2) // 2\n            idx = 0\n\n            while idx <= lp:\n                lc = 2 * idx + 1\n                rc = lc + 1\n\n                if rc <= self._length - 1:\n                    tmp = lc if self._q[lc].priority < self._q[rc].priority else rc\n                else:\n                    tmp = lc\n\n                if self._q[tmp].priority < self._q[idx].priority:\n                    self._q[tmp], self._q[idx] = self._q[idx], self._q[tmp]\n                    idx = tmp\n                else:\n                    break\n        return ret\n\n    def get_length(self):\n        return self._length\n\n    @staticmethod\n    def _draw_heap(data):\n        \"\"\"\n        格式化打印\n        :param data:\n        :return:\n        \"\"\"\n        length = len(data)\n\n        if length == 0:\n            return 'empty'\n\n        ret = ''\n        for i, n in enumerate(data):\n            ret += str(n)\n            # 每行最后一个换行\n            if i == 2 ** int(math.log(i + 1, 2) + 1) - 2 or i == len(data) - 1:\n                ret += '\\n'\n            else:\n                ret += ', '\n\n        return ret\n\n    def __repr__(self):\n        def formater(node):\n            assert type(node) is QueueNode\n            return node.priority, node.data\n\n        data = list(map(formater, self._q))\n        return self._draw_heap(data)\n\n\nif __name__ == '__main__':\n    pq = PriorityQueue()\n    pq.enqueue(5, 'Watch TV')\n    pq.enqueue(2, 'Learning')\n    pq.enqueue(10, 'Go Sleep')\n    pq.enqueue(0, 'Go Home')\n    pq.enqueue(7, 'Mobile Games')\n    print(pq)\n\n    while pq.get_length() > 0:\n        print(pq.dequeue())\n"
  },
  {
    "path": "python/28_binary_heap/top_k.py",
    "content": "#!/usr/bin/python\n# -*- coding: UTF-8 -*-\n\nimport random\nfrom heap import MinHeap\n\n\ndef top_k(nums, k):\n    \"\"\"\n    返回数组的前k大元素\n    :param nums:\n    :param k:\n    :return:\n    \"\"\"\n    if len(nums) <= k:\n        return nums\n\n    min_h = MinHeap(nums[:k], k)\n    for i in range(k, len(nums)):\n        tmp = min_h.get_top()\n        if nums[i] > tmp:\n            min_h.remove_top()\n            min_h.insert(nums[i])\n\n    return min_h.get_data()\n\n\nif __name__ == '__main__':\n    nums = []\n    k = 3\n\n    for i in range(20):\n        nums.append(random.randint(1, 100))\n\n    print('--- nums ---')\n    print(nums)\n\n    print('--- top {} ---'.format(k))\n    print(top_k(nums, k))\n"
  },
  {
    "path": "python/28_heap/heap.py",
    "content": "\"\"\"\n    Max-heap\n\n    Author: Wenru Dong\n\"\"\"\n\nfrom typing import Optional, List\n\nclass Heap:\n    def __init__(self, capacity: int):\n        self._data = [0] * (capacity + 1)\n        self._capacity = capacity\n        self._count = 0\n    \n    @classmethod\n    def _parent(cls, child_index: int) -> int:\n        \"\"\"The parent index.\"\"\"\n        return child_index // 2\n    \n    @classmethod\n    def _left(cls, parent_index: int) -> int:\n        \"\"\"The left child index.\"\"\"\n        return 2 * parent_index\n    \n    @classmethod\n    def _right(cls, parent_index: int) -> int:\n        \"\"\"The right child index.\"\"\"\n        return 2 * parent_index + 1\n\n    def _siftup(self) -> None:\n        i, parent = self._count, Heap._parent(self._count)\n        while parent and self._data[i] > self._data[parent]:\n            self._data[i], self._data[parent] = self._data[parent], self._data[i]\n            i, parent = parent, Heap._parent(parent)\n\n    @classmethod\n    def _siftdown(cls, a: List[int], count: int, root_index: int = 1) -> None:\n        i = larger_child_index = root_index\n        while True:\n            left, right = cls._left(i), cls._right(i)\n            if left <= count and a[i] < a[left]:\n                larger_child_index = left\n            if right <= count and a[larger_child_index] < a[right]:\n                larger_child_index = right\n            if larger_child_index == i: break\n            a[i], a[larger_child_index] = a[larger_child_index], a[i]\n            i = larger_child_index\n\n    def insert(self, value: int) -> None:\n        if self._count >= self._capacity: return\n        self._count += 1\n        self._data[self._count] = value\n        self._siftup()\n\n    def remove_max(self) -> Optional[int]:\n        if self._count:\n            result = self._data[1]\n            self._data[1] = self._data[self._count]\n            self._count -= 1\n            Heap._siftdown(self._data, self._count)\n            return result\n\n    @classmethod\n    def build_heap(cls, a: List[int]) -> None:\n        \"\"\"Data in a needs to start from index 1.\"\"\"\n        for i in range((len(a) - 1)//2, 0, -1):\n            cls._siftdown(a, len(a) - 1, i)\n    \n    @classmethod\n    def sort(cls, a: List[int]) -> None:\n        \"\"\"Data in a needs to start from index 1.\"\"\"\n        cls.build_heap(a)\n        k = len(a) - 1\n        while k > 1:\n            a[1], a[k] = a[k], a[1]\n            k -= 1\n            cls._siftdown(a, k)\n\n    def __repr__(self):\n        return self._data[1 : self._count + 1].__repr__()\n\n\nif __name__ == \"__main__\":\n    hp = Heap(10)\n    hp.insert(3)\n    hp.insert(9)\n    hp.insert(1)\n    hp.insert(8)\n    hp.insert(7)\n    hp.insert(3)\n    print(hp)\n    for _ in range(6):\n        print(hp.remove_max())\n    a = [0, 6, 3, 4, 0, 9, 2, 7, 5, -2, 8, 1, 6, 10]\n    Heap.sort(a)\n    print(a[1:])"
  },
  {
    "path": "python/28_heap/min_heap.py",
    "content": "class Heap(object):\n    '''\n    索引从0开始的小顶堆\n    参考: https://github.com/python/cpython/blob/master/Lib/heapq.py\n\n    author: Ben\n    '''\n\n    def __init__(self, nums):\n        self._heap = nums\n\n    def _siftup(self, pos):\n        '''\n        从上向下的堆化\n        将pos节点的子节点中的最值提升到pos位置\n        '''\n        start = pos\n        startval = self._heap[pos]\n        n = len(self._heap)\n        # 完全二叉树特性\n        child = pos * 2 + 1\n        # 比较叶子节点\n        while child < n:\n            right = child + 1\n            # 平衡二叉树的特性, 大的都在右边\n            if right < n and not self._heap[right] > self._heap[child]:\n                child = right\n            self._heap[pos] = self._heap[child]\n            pos = child\n            child = pos * 2 + 1\n        self._heap[pos] = startval\n\n        # 此时只有pos是不确定的\n        self._siftdown(start, pos)\n\n    def _siftdown(self, start, pos):\n        '''\n        最小堆: 大于start的节点, 除pos外已经是最小堆\n        以pos为叶子节点, start为根节点之间的元素进行排序. 将pos叶子节点交换到正确的排序位置\n        操作: 从叶子节点开始, 当父节点的值大于子节点时, 父节点的值降低到子节点\n        '''\n        startval = self._heap[pos]\n        while pos > start:\n            parent = (pos - 1) >> 1\n            parentval = self._heap[parent]\n            if parentval > startval:\n                self._heap[pos] = parentval\n                pos = parent\n                continue\n            break\n        self._heap[pos] = startval\n\n    def heapify(self):\n        '''\n        堆化: 从后向前(从下向上)的方式堆化, _siftup中pos节点的子树已经是有序的,\n        这样要排序的节点在慢慢减少\n        1. 因为n/2+1到n的节点是叶子节点(完全二叉树的特性), 它们没有子节点,\n        所以, 只需要堆化n/2到0的节点, 以对应的父节点为根节点, 将最值向上筛选,\n        然后交换对应的根节点和查找到的最值\n        2. 因为开始时待排序树的根节点还没有排序, 为了保证根节点的有序,\n        需要将子树中根节点交换到正确顺序\n        '''\n        n = len(self._heap)\n        for i in reversed(range(n // 2)):\n            self._siftup(i)\n\n    def heappop(self):\n        '''\n        弹出堆首的最值 O(logn)\n        '''\n        tail = self._heap.pop()\n        # 为避免破环完全二叉树特性, 将堆尾元素填充到堆首\n        # 此时, 只有堆首是未排序的, 只需要一次从上向下的堆化\n        if self._heap:\n            peak = self._heap[0]\n            self._heap[0] = tail\n            self._siftup(0)\n            return peak\n        return tail\n\n    def heappush(self, val):\n        '''\n        添加元素到堆尾 O(logn)\n        '''\n        n = len(self._heap)\n        self._heap.append(val)\n        # 此时只有堆尾的节点是未排序的, 将添加的节点迭代到正确的位置\n        self._siftdown(0, n)\n\n    def __repr__(self):\n        vals = [str(i) for i in self._heap]\n        return '>'.join(vals)\n\n\nif __name__ == '__main__':\n    h = Heap([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11])\n    h.heapify()\n    print(h)\n    print(h.heappop())\n    print(h)\n    h.heappush(3.5)\n    print(h)\n    h.heappush(0.1)\n    print(h)\n    h.heappush(0.5)\n    print(h)\n    print(h.heappop())\n    print(h)\n"
  },
  {
    "path": "python/31_bfs_dfs/bfs_dfs.py",
    "content": "\"\"\"\n    Breadth-first search and depth-first search.\n\n    Author: Wenru Dong\n\"\"\"\n\nfrom typing import List, Optional, Generator, IO\nfrom collections import deque\n\nclass Graph:\n    \"\"\"Undirected graph.\"\"\"\n    def __init__(self, num_vertices: int):\n        self._num_vertices = num_vertices\n        self._adjacency = [[] for _ in range(num_vertices)]\n\n    def add_edge(self, s: int, t: int) -> None:\n        self._adjacency[s].append(t)\n        self._adjacency[t].append(s)\n\n    def _generate_path(self, s: int, t: int, prev: List[Optional[int]]) -> Generator[str, None, None]:\n        if prev[t] or s != t:\n            yield from self._generate_path(s, prev[t], prev)\n        yield str(t)\n\n    def bfs(self, s: int, t: int) -> IO[str]:\n        \"\"\"Print out the path from Vertex s to Vertex t\n        using bfs.\n        \"\"\"\n        if s == t: return\n\n        visited = [False] * self._num_vertices\n        visited[s] = True\n        q = deque()\n        q.append(s)\n        prev = [None] * self._num_vertices\n\n        while q:\n            v = q.popleft()\n            for neighbour in self._adjacency[v]:\n                if not visited[neighbour]:\n                    prev[neighbour] = v\n                    if neighbour == t:\n                        print(\"->\".join(self._generate_path(s, t, prev)))\n                        return\n                    visited[neighbour] = True\n                    q.append(neighbour)\n\n    def dfs(self, s: int, t: int) -> IO[str]:\n        \"\"\"Print out a path from Vertex s to Vertex t\n        using dfs.\n        \"\"\"\n        found = False\n        visited = [False] * self._num_vertices\n        prev = [None] * self._num_vertices\n\n        def _dfs(from_vertex: int) -> None:\n            nonlocal found\n            if found: return\n            visited[from_vertex] = True\n            if from_vertex == t:\n                found = True\n                return\n            for neighbour in self._adjacency[from_vertex]:\n                if not visited[neighbour]:\n                    prev[neighbour] = from_vertex\n                    _dfs(neighbour)\n        \n        _dfs(s)\n        print(\"->\".join(self._generate_path(s, t, prev)))\n\n\nif __name__ == \"__main__\":\n    \n    graph = Graph(8)\n\n    graph.add_edge(0, 1)\n    graph.add_edge(0, 3)\n    graph.add_edge(1, 2)\n    graph.add_edge(1, 4)\n    graph.add_edge(2, 5)\n    graph.add_edge(3, 4)\n    graph.add_edge(4, 5)\n    graph.add_edge(4, 6)\n    graph.add_edge(5, 7)\n    graph.add_edge(6, 7)\n\n    graph.bfs(0, 7)\n    graph.dfs(0, 7)\n"
  },
  {
    "path": "python/31_bfs_dfs/graph.py",
    "content": "# -*- coding:utf-8 -*-\n\n\nclass Undigraph(object):\n\n\n    def __init__(self, vertex_num):\n        self.v_num = vertex_num\n        self.adj_tbl = []\n        for i in range(self.v_num + 1):\n            self.adj_tbl.append([])\n\n    def add_edge(self, s, t):\n        if s > self.v_num or t > self.v_num:\n            return False\n        self.adj_tbl[s].append(t)\n        self.adj_tbl[t].append(s)\n        return True\n    \n    def __len__(self):\n        return self.v_num\n\n    def __getitem__(self, ind):\n        if ind > self.v_num:\n            raise IndexError(\"No Such Vertex!\")\n        return self.adj_tbl[ind]\n\n    def __repr__(self):\n        return str(self.adj_tbl)\n\n    def __str__(self):\n        return str(self.adj_tbl)\n\n\nclass Digraph(object):\n\n\n    def __init__(self, vertex_num):\n        self.v_num = vertex_num\n        self.adj_tbl = []\n        for i in range(self.v_num + 1):\n            self.adj_tbl.append([])\n    \n    def add_edge(self, frm, to):\n        if frm > self.v_num or to > self.v_num:\n            return False\n        self.adj_tbl[frm].append(to)\n\n    def __len__(self):\n        return self.v_num\n\n    def __getitem__(self, ind):\n        if ind > self.v_num:\n            raise IndexError(\"No such vertex!\")\n        return self.ajd_tbl[ind]\n\n    def __repr__(self):\n        return str(self.adj_tbl)\n\n    def __str__(self):\n        return str(self.adj_tbl)\n\n\nif __name__ == '__main__':\n    ug = Undigraph(10)\n    ug.add_edge(1, 9)\n    ug.add_edge(1, 3)\n    ug.add_edge(3, 2)\n    print(ug.adj_tbl)\n    \n    dg = Digraph(10)\n    dg.add_edge(1, 9)\n    dg.add_edge(1, 3)\n    dg.add_edge(3, 4)\n    print(dg.adj_tbl)\n"
  },
  {
    "path": "python/31_bfs_dfs/graph_application.py",
    "content": "# -*- coding:utf-8 -*-\n\n\n\nfrom collections import deque\nfrom graph import Undigraph\n\ndef find_vertex_by_degree(graph, s, degree):\n    if len(graph) <= 1:\n        return []\n    if degree == 0:\n        return [s]\n    d_vertices = []\n    queue = deque()\n    prev = [-1] * len(graph)\n    visited = [False] * len(graph)\n    visited[s] = True\n    queue.append(s)\n    while len(queue) > 0:\n        sz = len(queue)\n        for i in range(sz):\n            v = queue.popleft()\n            for adj_v in graph[v]:\n                if not visited[adj_v]:\n                    prev[adj_v] = v\n                    visited[adj_v] = True\n                    queue.append(adj_v)\n        degree -= 1\n        if degree == 0 and len(queue) != 0:\n            return queue \n\n   \nif __name__ == '__main__':\n    g = Undigraph(8)\n    g.add_edge(0, 1)\n    g.add_edge(0, 3)\n    g.add_edge(1, 2)\n    g.add_edge(1, 4)\n    g.add_edge(2, 5)\n    g.add_edge(3, 4)\n    g.add_edge(4, 5)\n    g.add_edge(4, 6)\n    g.add_edge(5, 7)\n    g.add_edge(6, 7)\n    print(find_vertex_by_degree(g, 0, 4))\n"
  },
  {
    "path": "python/32_bf_rk/bf_rk.py",
    "content": "#!/usr/bin/python\n# -*- coding: UTF-8 -*-\n\nfrom time import time\n\n\ndef bf(main, pattern):\n    \"\"\"\n    字符串匹配，bf暴搜\n    :param main: 主串\n    :param pattern: 模式串\n    :return:\n    \"\"\"\n    n = len(main)\n    m = len(pattern)\n\n    if n <= m:\n        return 0 if pattern == main else -1\n\n    for i in range(n-m+1):\n        for j in range(m):\n            if main[i+j] == pattern[j]:\n                if j == m-1:\n                    return i\n                else:\n                    continue\n            else:\n                break\n    return -1\n\n\ndef simple_hash(s, start, end):\n    \"\"\"\n    计算子串的哈希值\n    每个字符取acs-ii码后求和\n    :param s:\n    :param start:\n    :param end:\n    :return:\n    \"\"\"\n    assert start <= end\n\n    ret = 0\n    for c in s[start: end+1]:\n        ret += ord(c)\n    return ret\n\n\ndef rk(main, pattern):\n    n = len(main)\n    m = len(pattern)\n\n    if n <= m:\n        return 0 if pattern == main else -1\n\n    # 子串哈希值表\n    hash_memo = [None] * (n-m+1)\n    hash_memo[0] = simple_hash(main, 0, m-1)\n    for i in range(1, n-m+1):\n        hash_memo[i] = hash_memo[i-1] - simple_hash(main, i-1, i-1) + simple_hash(main, i+m-1, i+m-1)\n\n    # 模式串哈希值\n    hash_p = simple_hash(pattern, 0, m-1)\n\n    for i, h in enumerate(hash_memo):\n        # 可能存在哈希冲突\n        if h == hash_p:\n            if pattern == main[i:i+m]:\n                return i\n            else:\n                continue\n    return -1\n\n\nif __name__ == '__main__':\n    m_str = 'a'*10000\n    p_str = 'a'*200+'b'\n\n    print('--- time consume ---')\n    t = time()\n    print('[bf] result:', bf(m_str, p_str))\n    print('[bf] time cost: {0:.5}s'.format(time()-t))\n\n    t = time()\n    print('[rk] result:', rk(m_str, p_str))\n    print('[rk] time cost: {0:.5}s'.format(time()-t))\n\n    print('')\n    print('--- search ---')\n    m_str = 'thequickbrownfoxjumpsoverthelazydog'\n    p_str = 'jump'\n    print('[bf] result:', bf(m_str, p_str))\n    print('[rk] result:', rk(m_str, p_str))\n"
  },
  {
    "path": "python/33_bm/bm.py",
    "content": "\"\"\"\n    Boyer-Moore string-search algorithm.\n\n    Author: Wenru Dong\n\"\"\"\n\nfrom typing import List, Tuple\n\nSIZE = 256\n\ndef _generate_bad_character_table(pattern: str) -> List[int]:\n    bc = [-1] * SIZE\n    for i, char in enumerate(pattern):\n        bc[ord(char)] = i\n    return bc\n\n\ndef _generate_good_suffix_table(pattern: str) -> Tuple[List[bool], List[int]]:\n    m = len(pattern)\n    # prefix[k] records whether the last k-character suffix of pattern\n    # can match with the first k-character prefix of pattern.\n    # suffix[k] records the starting index of the last substring of\n    # pattern that can match with the last k-character suffix of pattern.\n    prefix, suffix = [False] * m, [-1] * m\n    # For each substring patter[:i+1], we find the common suffix with\n    # pattern, and the starting index of this common suffix.\n    # This way we can re-write previous suffix[k] to record the index\n    # as large as possible, hence the last substring.\n    for i in range(m - 1):\n        j = i  # starting index of the common suffix\n        k = 0  # length of the common suffix\n        while j >= 0 and pattern[j] == pattern[~k]:\n            j -= 1\n            k += 1\n            suffix[k] = j + 1\n        if j == -1: prefix[k] = True\n    return (prefix, suffix)\n\n\ndef _move_by_good_suffix(bad_character_index: int, suffix: List[int], prefix: List[bool]) -> int:\n    k = len(suffix) - 1 - bad_character_index\n    if suffix[k] != -1: return bad_character_index - suffix[k] + 1\n    # Test from k - 1\n    for r, can_match_prefix in enumerate(reversed(prefix[:k]), bad_character_index + 2): \n        if can_match_prefix: return r\n    return len(suffix)\n\n\ndef bm(s: str, pattern: str) -> int:\n    bc = _generate_bad_character_table(pattern)\n    prefix, suffix = _generate_good_suffix_table(pattern)\n    n, m = len(s), len(pattern)\n    i = 0\n    while i <= n - m:\n        j = m - 1  # bad character index in pattern\n        while j >= 0: \n            if s[i + j] != pattern[j]: break\n            j -= 1\n        if j < 0: return i\n        \n        x = j - bc[ord(s[i + j])]\n        y = 0\n        if j < m - 1:\n            y = _move_by_good_suffix(j, suffix, prefix)\n        i += max(x, y)\n    return -1\n\n\nif __name__ == \"__main__\":\n\n    s = \"Here is a simple example\"\n    pattern = \"example\"\n    print(bm(s, pattern))\n\n    s = \"abcdcccdc\"\n    pattern = \"cccd\"\n    print(s.find(pattern) == bm(s, pattern))"
  },
  {
    "path": "python/33_bm/bm_.py",
    "content": "#!/usr/bin/python\n# -*- coding: UTF-8 -*-\n\nSIZE = 256\n\n\ndef bm(main, pattern):\n    \"\"\"\n    BM算法\n    匹配规则：\n    1. 坏字符规则\n    2. 好字符规则\n    :param main:\n    :param pattern:\n    :return:\n    \"\"\"\n    assert type(main) is str and type(pattern) is str\n    n, m = len(main), len(pattern)\n\n    if n <= m:\n        return 0 if main == pattern else -1\n\n    # bc\n    bc = [-1] * SIZE\n    generate_bc(pattern, m, bc)\n\n    # gs\n    suffix = [-1] * m\n    prefix = [False] * m\n    generate_gs(pattern, m, suffix, prefix)\n\n    i = 0\n    while i < n-m+1:\n        j = m - 1\n        while j >= 0:\n            if main[i+j] != pattern[j]:\n                break\n            else:\n                j -= 1\n\n        # pattern整个已被匹配，返回\n        if j == -1:\n            return i\n\n        # 1. bc规则计算后移位数\n        x = j - bc[ord(main[i+j])]\n\n        # 2. gs规则计算后移位数\n        y = 0\n        if j != m - 1:    # 存在gs\n            y = move_by_gs(j, m, suffix, prefix)\n\n        i += max(x, y)\n\n    return -1\n\n\ndef generate_bc(pattern, m, bc):\n    \"\"\"\n    生成坏字符哈希表\n    :param pattern:\n    :param m:\n    :param bc:\n    :return:\n    \"\"\"\n    for i in range(m):\n        bc[ord(pattern[i])] = i\n\n\ndef generate_gs(pattern, m, suffix, prefix):\n    \"\"\"\n    好后缀预处理\n    :param pattern:\n    :param m:\n    :param suffix:\n    :param prefix:\n    :return:\n    \"\"\"\n    for i in range(m-1):\n        k = 0   # pattern[:i+1]和pattern的公共后缀长度\n        for j in range(i, -1, -1):\n            if pattern[j] == pattern[m-1-k]:\n                k += 1\n                suffix[k] = j\n                if j == 0:\n                    prefix[k] = True\n            else:\n                break\n\n\ndef move_by_gs(j, m, suffix, prefix):\n    \"\"\"\n    通过好后缀计算移动值\n    需要处理三种情况：\n    1. 整个好后缀在pattern仍能找到\n    2. 好后缀里存在 *后缀子串* 能和pattern的 *前缀* 匹配\n    3. 其他\n    :param j:\n    :param m:\n    :param suffix:\n    :param prefix:\n    :return:\n    \"\"\"\n    k = m - 1 - j           # j指向从后往前的第一个坏字符，k是此次匹配的好后缀的长度\n\n    if suffix[k] != -1:             # 1. 整个好后缀在pattern剩余字符中仍有出现\n        return j - suffix[k] + 1\n    else:\n        for r in range(j+2, m):     # 2. 后缀子串从长到短搜索\n            if prefix[m-r]:\n                return r\n        return m                    # 3. 其他情况\n\n\nif __name__ == '__main__':\n    print('--- search ---')\n    m_str = 'dfasdeeeetewtweyyyhtruuueyytewtweyyhtrhrth'\n    p_str = 'eyytewtweyy'\n    print('[Built-in Functions] result:', m_str.find(p_str))\n    print('[bm] result:', bm(m_str, p_str))\n"
  },
  {
    "path": "python/34_kmp/kmp.py",
    "content": "\"\"\"\n    KMP algorithm\n\n    Author: Wenru Dong\n\"\"\"\n\nfrom typing import List\n\ndef kmp(s: int, pattern: int) -> int:\n    m = len(pattern)\n    partial_match_table = _get_partial_match_table(pattern)\n    j = 0\n    for i in range(len(s)):\n        while j >= 0 and s[i] != pattern[j]:\n            j = partial_match_table[j]\n        j += 1\n        if j == m:\n            return i - m + 1\n    return -1\n\n\ndef _get_partial_match_table(pattern: int) -> List[int]:\n    # Denote πᵏ(i) as π applied to i for k times,\n    # i.e., π²(i) = π(π(i)).\n    # Then we have the result:\n    #    π(i) = πᵏ(i-1) + 1,\n    # where k is the smallest integer such that\n    # pattern[πᵏ(i-1)+1] == pattern[i].\n\n    # The value of π means the maximum length\n    # of proper prefix/suffix.\n    # The index of π means the length of the prefix\n    # considered for pattern.\n    # For example, π[2] means we are considering the first 2 characters\n    # of the pattern.\n    # If π[2] == 1, it means for the prefix of the pattern, P[0]P[1],\n    # it has a maximum length proper prefix of 1, which is also the\n    # suffix of P[0]P[1].\n    # We also add a π[0] == -1 for easier handling of boundary\n    # condition.\n    \n    m = len(pattern)\n    π = [0] * (m + 1)\n    π[0] = k = -1  # We use k here to represent πᵏ(i)\n    for i in range(1, m + 1):\n        while k >= 0 and pattern[k] != pattern[i - 1]:\n            k = π[k]\n        k += 1\n        π[i] = k\n    return π\n\n\nif __name__ == \"__main__\":\n\n    s = \"abc abcdab abcdabcdabde\"\n    pattern = \"bcdabd\"\n    print(kmp(s, pattern), s.find(pattern))\n\n    s = \"hello\"\n    pattern = \"ll\"\n    print(kmp(s, pattern), s.find(pattern))"
  },
  {
    "path": "python/34_kmp/kmp_.py",
    "content": "#!/usr/bin/python\n# -*- coding: UTF-8 -*-\n\n\ndef kmp(main, pattern):\n    \"\"\"\n    kmp字符串匹配\n    :param main:\n    :param pattern:\n    :return:\n    \"\"\"\n    assert type(main) is str and type(pattern) is str\n\n    n, m = len(main), len(pattern)\n\n    if m == 0:\n        return 0\n    if n <= m:\n        return 0 if main == pattern else -1\n\n    # 求解next数组\n    next = get_next(pattern)\n\n    j = 0\n    for i in range(n):\n        # 在pattern[:j]中，从长到短递归去找最长的和后缀子串匹配的前缀子串\n        while j > 0 and main[i] != pattern[j]:\n            j = next[j-1] + 1   # 如果next[j-1] = -1，则要从起始字符取匹配\n\n        if main[i] == pattern[j]:\n            if j == m-1:\n                return i-m+1\n            else:\n                j += 1\n    return -1\n\n\ndef get_next(pattern):\n    \"\"\"\n    next数组生成\n\n    注意：\n    理解的难点在于next[i]根据next[0], next[1]…… next[i-1]的求解\n    next[i]的值依赖于前面的next数组的值，求解思路：\n    1. 首先取出前一个最长的匹配的前缀子串，其下标就是next[i-1]\n    2. 对比下一个字符，如果匹配，直接赋值next[i]为next[i-1]+1，因为i-1的时候已经是最长\n    *3. 如果不匹配，需要递归去找次长的匹配的前缀子串，这里难理解的就是递归地方式，next[i-1]\n        是i-1的最长匹配前缀子串的下标结尾，则 *next[next[i-1]]* 是其次长匹配前缀子串的下标\n        结尾\n    *4. 递归的出口，就是在次长前缀子串的下一个字符和当前匹配 或 遇到-1，遇到-1则说明没找到任\n        何匹配的前缀子串，这时需要找pattern的第一个字符对比\n\n    ps: next[m-1]的数值其实没有任何意义，求解时可以不理。网上也有将next数组往右平移的做法。\n    :param pattern:\n    :return:\n    \"\"\"\n    m = len(pattern)\n    next = [-1] * m\n\n    next[0] = -1\n\n    # for i in range(1, m):\n    for i in range(1, m-1):\n        j = next[i-1]       # 取i-1时匹配到的最长前缀子串\n        while j != -1 and pattern[j+1] != pattern[i]:\n            j = next[j]     # 次长的前缀子串的下标，即是next[next[i-1]]\n\n        # 根据上面跳出while的条件，当j=-1时，需要比较pattern[0]和当前字符\n        # 如果j!=-1，则pattern[j+1]和pattern[i]一定是相等的\n        if pattern[j+1] == pattern[i]:  # 如果接下来的字符也是匹配的，那i的最长前缀子串下标是next[i-1]+1\n            j += 1\n        next[i] = j\n\n    return next\n\n\nif __name__ == '__main__':\n    m_str = \"aabbbbaaabbababbabbbabaaabb\"\n    p_str = \"abbabbbabaa\"\n\n    print('--- search ---')\n    print('[Built-in Functions] result:', m_str.find(p_str))\n    print('[kmp] result:', kmp(m_str, p_str))\n"
  },
  {
    "path": "python/35_trie/trie.py",
    "content": "\"\"\"\n    Author: Wenru Dong\n\"\"\"\n\nclass TrieNode:\n    def __init__(self, data: str):\n        self._data = data\n        self._children = [None] * 26\n        self._is_ending_char = False\n    \n\nclass Trie:\n    def __init__(self):\n        self._root = TrieNode(\"/\")\n\n    def insert(self, text: str) -> None:\n        node = self._root\n        for index, char in map(lambda x: (ord(x) - ord(\"a\"), x), text):\n            if not node._children[index]:\n                node._children[index] = TrieNode(char)\n            node = node._children[index]\n        node._is_ending_char = True\n    \n    def find(self, pattern: str) -> bool:\n        node = self._root\n        for index in map(lambda x: ord(x) - ord(\"a\"), pattern):\n            if not node._children[index]: return False\n            node = node._children[index]\n        return node._is_ending_char\n\n\nif __name__ == \"__main__\":\n\n    strs = [\"how\", \"hi\", \"her\", \"hello\", \"so\", \"see\"]\n    trie = Trie()\n    for s in strs:\n        trie.insert(s)\n    \n    for s in strs:\n        print(trie.find(s))\n    \n    print(trie.find(\"swift\"))"
  },
  {
    "path": "python/35_trie/trie_.py",
    "content": "#!/usr/bin/python\n# -*- coding: UTF-8 -*-\n\nfrom queue import Queue\nimport pygraphviz as pgv\n\nOUTPUT_PATH = 'E:/'\n\n\nclass Node:\n    def __init__(self, c):\n        self.data = c\n        self.is_ending_char = False\n        # 使用有序数组，降低空间消耗，支持更多字符\n        self.children = []\n\n    def insert_child(self, c):\n        self._insert_child(Node(c))\n\n    def _insert_child(self, node):\n        \"\"\"\n        插入一个子节点\n        :param c:\n        :return:\n        \"\"\"\n        v = ord(node.data)\n        idx = self._find_insert_idx(v)\n        length = len(self.children)\n\n        if idx == length:\n            self.children.append(node)\n        else:\n            self.children.append(None)\n            for i in range(length, idx, -1):\n                self.children[i] = self.children[i-1]\n            self.children[idx] = node\n\n    def has_child(self, c):\n        return True if self.get_child(c) is not None else False\n\n    def get_child(self, c):\n        \"\"\"\n        搜索子节点并返回\n        :param c:\n        :return:\n        \"\"\"\n        start = 0\n        end = len(self.children) - 1\n        v = ord(c)\n\n        while start <= end:\n            mid = (start + end)//2\n            if v == ord(self.children[mid].data):\n                return self.children[mid]\n            elif v < ord(self.children[mid].data):\n                end = mid - 1\n            else:\n                start = mid + 1\n        # 找不到返回None\n        return None\n\n    def _find_insert_idx(self, v):\n        \"\"\"\n        二分查找，找到有序数组的插入位置\n        :param v:\n        :return:\n        \"\"\"\n        start = 0\n        end = len(self.children) - 1\n\n        while start <= end:\n            mid = (start + end)//2\n            if v < ord(self.children[mid].data):\n                end = mid - 1\n            else:\n                if mid + 1 == len(self.children) or v < ord(self.children[mid+1].data):\n                    return mid + 1\n                else:\n                    start = mid + 1\n        # v < self.children[0]\n        return 0\n\n    def __repr__(self):\n        return 'node value: {}'.format(self.data) + '\\n' \\\n               + 'children:{}'.format([n.data for n in self.children])\n\n\nclass Trie:\n    def __init__(self):\n        self.root = Node(None)\n\n    def gen_tree(self, string_list):\n        \"\"\"\n        创建trie树\n\n        1. 遍历每个字符串的字符，从根节点开始，如果没有对应子节点，则创建\n        2. 每一个串的末尾节点标注为红色(is_ending_char)\n        :param string_list:\n        :return:\n        \"\"\"\n        for string in string_list:\n            n = self.root\n            for c in string:\n                if n.get_child(c) is None:\n                    n.insert_child(c)\n                n = n.get_child(c)\n            n.is_ending_char = True\n\n    def search(self, pattern):\n        \"\"\"\n        搜索\n\n        1. 遍历模式串的字符，从根节点开始搜索，如果途中子节点不存在，返回False\n        2. 遍历完模式串，则说明模式串存在，再检查树中最后一个节点是否为红色，是\n           则返回True，否则False\n        :param pattern:\n        :return:\n        \"\"\"\n        assert type(pattern) is str and len(pattern) > 0\n\n        n = self.root\n        for c in pattern:\n            if n.get_child(c) is None:\n                return False\n            n = n.get_child(c)\n\n        return True if n.is_ending_char is True else False\n\n    def draw_img(self, img_name='Trie.png'):\n        \"\"\"\n        画出trie树\n        :param img_name:\n        :return:\n        \"\"\"\n        if self.root is None:\n            return\n\n        tree = pgv.AGraph('graph foo {}', strict=False, directed=False)\n\n        # root\n        nid = 0\n        color = 'black'\n        tree.add_node(nid, color=color, label='None')\n\n        q = Queue()\n        q.put((self.root, nid))\n        while not q.empty():\n            n, pid = q.get()\n            for c in n.children:\n                nid += 1\n                q.put((c, nid))\n                color = 'red' if c.is_ending_char is True else 'black'\n                tree.add_node(nid, color=color, label=c.data)\n                tree.add_edge(pid, nid)\n\n        tree.graph_attr['epsilon'] = '0.01'\n        tree.layout('dot')\n        tree.draw(OUTPUT_PATH + img_name)\n        return True\n\n\nif __name__ == '__main__':\n    string_list = ['abc', 'abd', 'abcc', 'accd', 'acml', 'P@trick', 'data', 'structure', 'algorithm']\n\n    print('--- gen trie ---')\n    print(string_list)\n    trie = Trie()\n    trie.gen_tree(string_list)\n    # trie.draw_img()\n\n    print('\\n')\n    print('--- search result ---')\n    search_string = ['a', 'ab', 'abc', 'abcc', 'abe', 'P@trick', 'P@tric', 'Patrick']\n    for ss in search_string:\n        print('[pattern]: {}'.format(ss), '[result]: {}'.format(trie.search(ss)))\n"
  },
  {
    "path": "python/36_ac_automata/ac_automata.py",
    "content": "\"\"\"\n    Aho-Corasick Algorithm\n\n    Author: Wenru Dong\n\"\"\"\n\nfrom collections import deque\nfrom typing import List\n\nclass ACNode:\n    def __init__(self, data: str):\n        self._data = data\n        self._children = [None] * 26\n        self._is_ending_char = False\n        self._length = -1\n        self._suffix = None\n    \n\nclass ACAutomata:\n    def __init__(self):\n        self._root = ACNode(\"/\")\n\n    def _build_suffix_link(self) -> None:\n        q = deque()\n        q.append(self._root)\n        while q:\n            node = q.popleft()\n            for child in node._children:\n                if child:\n                    if node == self._root:\n                        child._suffix = self._root\n                    else:\n                        suffix = node._suffix\n                        while suffix:\n                            suffix_child = suffix._children[ord(child._data) - ord(\"a\")]\n                            if suffix_child:\n                                child._suffix = suffix_child\n                                break\n                            suffix = suffix._suffix\n                        if not suffix:\n                            child._suffix = self._root\n                    q.append(child)\n\n    def _insert(self, text: str) -> None:\n        node = self._root\n        for index, char in map(lambda x: (ord(x) - ord(\"a\"), x), text):\n            if not node._children[index]:\n                node._children[index] = ACNode(char)\n            node = node._children[index]\n        node._is_ending_char = True\n        node._length = len(text)\n\n    def insert(self, patterns: List[str]) -> None:\n        for pattern in patterns:\n            self._insert(pattern)\n        self._build_suffix_link()\n\n    def match(self, text: str) -> None:\n        node = self._root\n        for i, char in enumerate(text):\n            index = ord(char) - ord(\"a\")\n            while not node._children[index] and node != self._root:\n                node = node._suffix\n            node = node._children[index]\n            if not node:\n                node = self._root\n            tmp = node\n            while tmp != self._root:\n                if tmp._is_ending_char:\n                    print(f\"匹配起始下标{i - tmp._length + 1}，长度{tmp._length}\")\n                tmp = tmp._suffix\n\n\nif __name__ == \"__main__\":\n\n    patterns = [\"at\", \"art\", \"oars\", \"soar\"]\n    ac = ACAutomata()\n    ac.insert(patterns)\n    \n    ac.match(\"soarsoars\")\n"
  },
  {
    "path": "python/36_ac_automata/ac_automata_.py",
    "content": "#!/usr/bin/python\n# -*- coding: UTF-8 -*-\n\nfrom trie_ import Node, Trie\nfrom queue import Queue\n\n\nclass ACNode(Node):\n    def __init__(self, c: str):\n        super(ACNode, self).__init__(c)\n        self.fail = None\n        self.length = 0\n\n    def insert_child(self, c: str):\n        self._insert_child(ACNode(c))\n\n\nclass ACTrie(Trie):\n    def __init__(self):\n        self.root = ACNode(None)\n\n\ndef ac_automata(main: str, ac_trie: ACTrie) -> list:\n    root = ac_trie.root\n    build_failure_pointer(ac_trie)\n\n    ret = []\n    p = root\n    for i, c in enumerate(main):\n        while p != root and not p.has_child(c):\n            p = p.fail\n\n        if p.has_child(c):  # a char matched, try to find all potential pattern matched\n            q = p.get_child(c)\n            while q != root:\n                if q.is_ending_char:\n                    ret.append((i-q.length+1, i))\n                    # ret.append(main[i-q.length+1:i+1])\n                q = q.fail\n            p = p.get_child(c)\n\n    return ret\n\n\ndef build_failure_pointer(ac_trie: ACTrie) -> None:\n    root = ac_trie.root\n\n    # queue: [(node, node.length) ....]\n    node_queue = Queue()\n    node_queue.put((root, root.length))\n\n    root.fail = None\n    while not node_queue.empty():\n        p, length = node_queue.get()\n        for pc in p.children:\n            pc.length = length + 1\n            if p == root:\n                pc.fail = root\n            else:\n                q = p.fail\n                # same as kmp\n                while q != root and not q.has_child(pc.data):\n                    q = q.fail\n\n                # cases now:\n                # 1. q == root\n                # 2. q != root and q.has_child(pc.data)\n                if q.has_child(pc.data):\n                    pc.fail = q.get_child(pc.data)\n                else:\n                    pc.fail = root\n            node_queue.put((pc, pc.length))\n\n\nif __name__ == '__main__':\n    ac_trie = ACTrie()\n    ac_trie.gen_tree(['fuck', 'shit', 'TMD', '傻叉'])\n\n    print('--- ac automata ---')\n    m_str = 'fuck you, what is that shit, TMD你就是个傻叉傻叉傻叉叉'\n    print('original str  : {}'.format(m_str))\n\n    filter_range_list = ac_automata(m_str, ac_trie)\n    str_filtered = m_str\n    for start, end in filter_range_list:\n        str_filtered = str_filtered.replace(str_filtered[start:end+1], '*'*(end+1-start))\n\n    print('after filtered: {}'.format(str_filtered))\n"
  },
  {
    "path": "python/38_divide_and_conquer/merge_sort_counting.py",
    "content": "#!/usr/bin/python\n# -*- coding: UTF-8 -*-\n\ninversion_num = 0\n\n\ndef merge_sort_counting(nums, start, end):\n    if start >= end:\n        return\n\n    mid = (start + end)//2\n    merge_sort_counting(nums, start, mid)\n    merge_sort_counting(nums, mid+1, end)\n    merge(nums, start, mid, end)\n\n\ndef merge(nums, start, mid, end):\n    global inversion_num\n    i = start\n    j = mid+1\n    tmp = []\n    while i <= mid and j <= end:\n        if nums[i] <= nums[j]:\n            inversion_num += j - mid - 1\n            tmp.append(nums[i])\n            i += 1\n        else:\n            tmp.append(nums[j])\n            j += 1\n\n    while i <= mid:\n        # 这时nums[i]的逆序数是整个nums[mid+1: end+1]的长度\n        inversion_num += end - mid\n        tmp.append(nums[i])\n        i += 1\n\n    while j <= end:\n        tmp.append(nums[j])\n        j += 1\n\n    nums[start: end+1] = tmp\n\n\nif __name__ == '__main__':\n    print('--- count inversion number using merge sort ---')\n    # nums = [5, 0, 4, 2, 3, 1, 6, 8, 7]\n    nums = [5, 0, 4, 2, 3, 1, 3, 3, 3, 6, 8, 7]\n    print('nums  : {}'.format(nums))\n    merge_sort_counting(nums, 0, len(nums)-1)\n    print('sorted: {}'.format(nums))\n    print('inversion number: {}'.format(inversion_num))\n"
  },
  {
    "path": "python/39_back_track/01_bag.py",
    "content": "#!/usr/bin/python\n# -*- coding: UTF-8 -*-\n\nfrom typing import List\n\n# 背包选取的物品列表\npicks = []\npicks_with_max_value = []\n\n\ndef bag(capacity: int, cur_weight: int, items_info: List, pick_idx: int):\n    \"\"\"\n    回溯法解01背包，穷举\n    :param capacity: 背包容量\n    :param cur_weight: 背包当前重量\n    :param items_info: 物品的重量和价值信息\n    :param pick_idx: 当前物品的索引\n    :return:\n    \"\"\"\n    # 考察完所有物品，或者在中途已经装满\n    if pick_idx >= len(items_info) or cur_weight == capacity:\n        global picks_with_max_value\n        if get_value(items_info, picks) > \\\n                get_value(items_info, picks_with_max_value):\n            picks_with_max_value = picks.copy()\n    else:\n        item_weight = items_info[pick_idx][0]\n        if cur_weight + item_weight <= capacity:    # 选\n            picks[pick_idx] = 1\n            bag(capacity, cur_weight + item_weight, items_info, pick_idx + 1)\n\n        picks[pick_idx] = 0                         # 不选\n        bag(capacity, cur_weight, items_info, pick_idx + 1)\n\n\ndef get_value(items_info: List, pick_items: List):\n    values = [_[1] for _ in items_info]\n    return sum([a*b for a, b in zip(values, pick_items)])\n\n\nif __name__ == '__main__':\n    # [(weight, value), ...]\n    items_info = [(3, 5), (2, 2), (1, 4), (1, 2), (4, 10)]\n    capacity = 8\n\n    print('--- items info ---')\n    print(items_info)\n\n    print('\\n--- capacity ---')\n    print(capacity)\n\n    picks = [0] * len(items_info)\n    bag(capacity, 0, items_info, 0)\n\n    print('\\n--- picks ---')\n    print(picks_with_max_value)\n\n    print('\\n--- value ---')\n    print(get_value(items_info, picks_with_max_value))\n"
  },
  {
    "path": "python/39_back_track/eight_queens.py",
    "content": "#!/usr/bin/python\n# -*- coding: UTF-8 -*-\n\n# 棋盘尺寸\nBOARD_SIZE = 8\n\nsolution_count = 0\nqueen_list = [0] * BOARD_SIZE\n\n\ndef eight_queens(cur_column: int):\n    \"\"\"\n    输出所有符合要求的八皇后序列\n    用一个长度为8的数组代表棋盘的列，数组的数字则为当前列上皇后所在的行数\n    :return:\n    \"\"\"\n    if cur_column >= BOARD_SIZE:\n        global solution_count\n        solution_count += 1\n        # 解\n        print(queen_list)\n    else:\n        for i in range(BOARD_SIZE):\n            if is_valid_pos(cur_column, i):\n                queen_list[cur_column] = i\n                eight_queens(cur_column + 1)\n\n\ndef is_valid_pos(cur_column: int, pos: int) -> bool:\n    \"\"\"\n    因为采取的是每列放置1个皇后的做法\n    所以检查的时候不必检查列的合法性，只需要检查行和对角\n    1. 行：检查数组在下标为cur_column之前的元素是否已存在pos\n    2. 对角：检查数组在下标为cur_column之前的元素，其行的间距pos - QUEEN_LIST[i]\n       和列的间距cur_column - i是否一致\n    :param cur_column:\n    :param pos:\n    :return:\n    \"\"\"\n    i = 0\n    while i < cur_column:\n        # 同行\n        if queen_list[i] == pos:\n            return False\n        # 对角线\n        if cur_column - i == abs(pos - queen_list[i]):\n            return False\n        i += 1\n    return True\n\n\nif __name__ == '__main__':\n    print('--- eight queens sequence ---')\n    eight_queens(0)\n\n    print('\\n--- solution count ---')\n    print(solution_count)\n"
  },
  {
    "path": "python/39_back_track/permutations.py",
    "content": "#!/usr/bin/python\n# -*- coding: UTF-8 -*-\n\nfrom typing import List\n\npermutations_list = []  # 全局变量，用于记录每个输出\n\n\ndef permutations(nums: List, n: int, pick_count: int):\n    \"\"\"\n    从nums选取n个数的全排列\n\n    回溯法，用一个栈记录当前路径信息\n    当满足n==0时，说明栈中的数已足够，输出并终止遍历\n    :param nums:\n    :param n:\n    :param pick_count:\n    :return:\n    \"\"\"\n    if n == 0:\n        print(permutations_list)\n    else:\n        for i in range(len(nums) - pick_count):\n            permutations_list[pick_count] = nums[i]\n            nums[i], nums[len(nums) - pick_count - 1] = nums[len(nums) - pick_count - 1], nums[i]\n            permutations(nums, n-1, pick_count+1)\n            nums[i], nums[len(nums) - pick_count - 1] = nums[len(nums) - pick_count - 1], nums[i]\n\n\nif __name__ == '__main__':\n    nums = [1, 2, 3, 4]\n    n = 3\n    print('--- list ---')\n    print(nums)\n\n    print('\\n--- pick num ---')\n    print(n)\n\n    print('\\n--- permutation list ---')\n    permutations_list = [0] * n\n    permutations(nums, n, 0)\n\n"
  },
  {
    "path": "python/39_back_track/regex.py",
    "content": "#!/usr/bin/python\n# -*- coding: UTF-8 -*-\n\nis_match = False\n\n\ndef rmatch(r_idx: int, m_idx: int, regex: str, main: str):\n    global is_match\n    if is_match:\n        return\n\n    if r_idx >= len(regex):     # 正则串全部匹配好了\n        is_match = True\n        return\n\n    if m_idx >= len(main) and r_idx < len(regex):   # 正则串没匹配完，但是主串已经没得匹配了\n        is_match = False\n        return\n\n    if regex[r_idx] == '*':     # * 匹配1个或多个任意字符，递归搜索每一种情况\n        for i in range(m_idx, len(main)):\n            rmatch(r_idx+1, i+1, regex, main)\n    elif regex[r_idx] == '?':   # ? 匹配0个或1个任意字符，两种情况\n        rmatch(r_idx+1, m_idx+1, regex, main)\n        rmatch(r_idx+1, m_idx, regex, main)\n    else:                       # 非特殊字符需要精确匹配\n        if regex[r_idx] == main[m_idx]:\n            rmatch(r_idx+1, m_idx+1, regex, main)\n\n\nif __name__ == '__main__':\n    regex = 'ab*eee?d'\n    main = 'abcdsadfkjlekjoiwjiojieeecd'\n    rmatch(0, 0, regex, main)\n    print(is_match)\n"
  },
  {
    "path": "python/39_backtracking/backtracking.py",
    "content": "\"\"\"\n    Author: Wenru Dong\n\"\"\"\n\nfrom typing import List\n\ndef eight_queens() -> None:\n    solutions = []\n\n    def backtracking(queens_at_column: List[int], index_sums: List[int], index_diffs: List[int]) -> None:\n        row = len(queens_at_column)\n        if row == 8:\n            solutions.append(queens_at_column)\n            return\n        for col in range(8):\n            if col in queens_at_column or row + col in index_sums or row - col in index_diffs: continue\n            backtracking(queens_at_column + [col], index_sums + [row + col], index_diffs + [row - col])\n    \n    backtracking([], [], [])\n    print(*(\" \" + \" \".join(\"*\" * i + \"Q\" + \"*\" * (8 - i - 1) + \"\\n\" for i in solution) for solution in solutions), sep=\"\\n\")\n\n\nif __name__ == \"__main__\":\n\n    eight_queens()\n"
  },
  {
    "path": "python/40_dynamic_programming/01_bag.py",
    "content": "#!/usr/bin/python\n# -*- coding: UTF-8 -*-\n\nfrom typing import List, Tuple\n\n\ndef bag(items_info: List[int], capacity: int) -> int:\n    \"\"\"\n    固定容量的背包，计算能装进背包的物品组合的最大重量\n\n    :param items_info: 每个物品的重量\n    :param capacity: 背包容量\n    :return: 最大装载重量\n    \"\"\"\n    n = len(items_info)\n    memo = [[-1]*(capacity+1) for i in range(n)]\n    memo[0][0] = 1\n    if items_info[0] <= capacity:\n        memo[0][items_info[0]] = 1\n\n    for i in range(1, n):\n        for cur_weight in range(capacity+1):\n            if memo[i-1][cur_weight] != -1:\n                memo[i][cur_weight] = memo[i-1][cur_weight]   # 不选\n                if cur_weight + items_info[i] <= capacity:    # 选\n                    memo[i][cur_weight + items_info[i]] = 1\n\n    for w in range(capacity, -1, -1):\n        if memo[-1][w] != -1:\n            return w\n\n\ndef bag_with_max_value(items_info: List[Tuple[int, int]], capacity: int) -> int:\n    \"\"\"\n    固定容量的背包，计算能装进背包的物品组合的最大价值\n\n    :param items_info: 物品的重量和价值\n    :param capacity: 背包容量\n    :return: 最大装载价值\n    \"\"\"\n    n = len(items_info)\n    memo = [[-1]*(capacity+1) for i in range(n)]\n    memo[0][0] = 0\n    if items_info[0][0] <= capacity:\n        memo[0][items_info[0][0]] = items_info[0][1]\n\n    for i in range(1, n):\n        for cur_weight in range(capacity+1):\n            if memo[i-1][cur_weight] != -1:\n                memo[i][cur_weight] = memo[i-1][cur_weight]\n                if cur_weight + items_info[i][0] <= capacity:\n                    memo[i][cur_weight + items_info[i][0]] = max(memo[i][cur_weight + items_info[i][0]],\n                                                                 memo[i-1][cur_weight] + items_info[i][1])\n    return max(memo[-1])\n\n\nif __name__ == '__main__':\n    # [weight, ...]\n    items_info = [2, 2, 4, 6, 3]\n    capacity = 9\n    print(bag(items_info, capacity))\n\n    # [(weight, value), ...]\n    items_info = [(3, 5), (2, 2), (1, 4), (1, 2), (4, 10)]\n    capacity = 8\n    print(bag_with_max_value(items_info, capacity))\n"
  },
  {
    "path": "python/40_dynamic_programming/knapsack.py",
    "content": "\"\"\"\n    Author: Wenru Dong\n\"\"\"\n\nfrom typing import List\n\ndef knapsack01(weights: List[int], values: List[int], capacity: int) -> int:\n    # Denote the state as (i, c), where i is the stage number,\n    # and c is the capacity available. Denote f(i, c) to be the\n    # maximum value when the capacity available is c, and Item 0\n    # to Item i-1 are to be packed.\n    # The goal is to find f(n-1, W), where W is the total capacity.\n    # Then the DP functional equation is:\n    #   f(i, c) = max(xᵢvᵢ + f(i-1, c-xᵢwᵢ)), xᵢ ∈ D, i ≥ 0,\n    #   f(-1, c) = 0, 0 ≤ c ≤ W,\n    # where\n    #                  /  {0},    if wᵢ > c\n    #   D = D(i, c) = \n    #                  \\  {0, 1}, if wᵢ ≤ c\n\n    prev = [0] * (capacity + 1)\n    for w, v in zip(weights, values):\n        prev = [c >= w and max(prev[c], prev[c-w] + v) for c in range(capacity + 1)]\n    return prev[-1]\n\n\nif __name__ == \"__main__\":\n    # To find the maximum weight that can be packed,\n    # set values equal to the weights\n    print(knapsack01([2, 2, 4, 6, 3], [2, 2, 4, 6, 3], 9))\n"
  },
  {
    "path": "python/40_dynamic_programming/yh_triangle.py",
    "content": "#!/usr/bin/python\n# -*- coding: UTF-8 -*-\n\nfrom typing import List\n\nLayer_nums = List[int]\n\n\ndef yh_triangle(nums: List[Layer_nums]) -> int:\n    \"\"\"\n    从根节点开始向下走，过程中经过的节点，只需存储经过它时最小的路径和\n    :param nums:\n    :return:\n    \"\"\"\n    assert len(nums) > 0\n    n = len(nums)   # 层数\n    memo = [[0]*n for i in range(n)]\n    memo[0][0] = nums[0][0]\n\n    for i in range(1, n):\n        for j in range(i+1):\n            # 每一层首尾两个数字，只有一条路径可以到达\n            if j == 0:\n                memo[i][j] = memo[i-1][j] + nums[i][j]\n            elif j == i:\n                memo[i][j] = memo[i-1][j-1] + nums[i][j]\n            else:\n                memo[i][j] = min(memo[i-1][j-1] + nums[i][j], memo[i-1][j] + nums[i][j])\n    return min(memo[n-1])\n\n\ndef yh_triangle_space_optimization(nums: List[Layer_nums]) -> int:\n    assert len(nums) > 0\n    n = len(nums)\n    memo = [0] * n\n    memo[0] = nums[0][0]\n\n    for i in range(1, n):\n        for j in range(i, -1, -1):\n            if j == i:\n                memo[j] = memo[j-1] + nums[i][j]\n            elif j == 0:\n                memo[j] = memo[j] + nums[i][j]\n            else:\n                memo[j] = min(memo[j-1] + nums[i][j], memo[j] + nums[i][j])\n    return min(memo)\n\n\ndef yh_triangle_bottom_up(nums: List[Layer_nums]) -> int:\n    assert len(nums) > 0\n    n = len(nums)\n    memo = nums[-1].copy()\n\n    for i in range(n-1, 0, -1):\n        for j in range(i):\n            memo[j] = min(memo[j] + nums[i-1][j], memo[j+1] + nums[i-1][j])\n    return memo[0]\n\n\nif __name__ == '__main__':\n    nums = [[3], [2, 6], [5, 4, 2], [6, 0, 3, 2]]\n    print(yh_triangle(nums))\n    print(yh_triangle_space_optimization(nums))\n    print(yh_triangle_bottom_up(nums))\n"
  },
  {
    "path": "python/41_dynamic_programming/coins_problem.py",
    "content": "#!/usr/bin/python\n# -*- coding: UTF-8 -*-\n\nfrom typing import List\n\n\ndef coins_dp(values: List[int], target: int) -> int:\n    # memo[i]表示target为i的时候，所需的最少硬币数\n    memo = [0] * (target+1)\n    # 0元的时候为0个\n    memo[0] = 0\n\n    for i in range(1, target+1):\n        min_num = 999999\n        # 对于values中的所有n\n        # memo[i]为 min(memo[i-n1], memo[i-n2], ...) + 1\n        for n in values:\n            if i >= n:\n                min_num = min(min_num, 1 + memo[i-n])\n            else:   # values中的数值要从小到大排序\n                break\n        memo[i] = min_num\n\n    # print(memo)\n    return memo[-1]\n\n\nmin_num = 999999\ndef coins_backtracking(values: List[int], target: int, cur_value: int, coins_count: int):\n    if cur_value == target:\n        global min_num\n        min_num = min(coins_count, min_num)\n    else:\n        for n in values:\n            if cur_value + n <= target:\n                coins_backtracking(values, target, cur_value+n, coins_count+1)\n\n\nif __name__ == '__main__':\n    values = [1, 3, 5]\n    target = 23\n    print(coins_dp(values, target))\n    coins_backtracking(values, target, 0, 0)\n    print(min_num)\n\n"
  },
  {
    "path": "python/41_dynamic_programming/min_dist.py",
    "content": "\"\"\"\n    Author: Wenru Dong\n\"\"\"\n\nfrom typing import List\nfrom itertools import accumulate\n\ndef min_dist(weights: List[List[int]]) -> int:\n    \"\"\"Find the minimum weight path from the weights matrix.\"\"\"\n    m, n = len(weights), len(weights[0])\n    table = [[0] * n for _ in range(m)]\n    # table[i][j] is the minimum distance (weight) when\n    # there are i vertical moves and j horizontal moves\n    # left.\n    table[0] = list(accumulate(reversed(weights[-1])))\n    for i, v in enumerate(accumulate(row[-1] for row in reversed(weights))):\n        table[i][0] = v\n    for i in range(1, m):\n        for j in range(1, n):\n            table[i][j] = weights[~i][~j] + min(table[i - 1][j], table[i][j - 1])\n    return table[-1][-1]\n\n\ndef min_dist_recur(weights: List[List[int]]) -> int:\n    m, n = len(weights), len(weights[0])\n    table = [[0] * n for _ in range(m)]\n    def min_dist_to(i: int, j: int) -> int:\n        if i == j == 0: return weights[0][0]\n        if table[i][j]: return table[i][j]\n        min_left = float(\"inf\") if j - 1 < 0 else min_dist_to(i, j - 1)\n        min_up = float(\"inf\") if i - 1 < 0 else min_dist_to(i - 1, j)\n        return weights[i][j] + min(min_left, min_up)\n    return min_dist_to(m - 1, n - 1)\n\n\nif __name__ == \"__main__\":\n    weights = [[1, 3, 5, 9], [2, 1, 3, 4], [5, 2, 6, 7], [6, 8, 4, 3]]\n    print(min_dist(weights))\n    print(min_dist_recur(weights))\n"
  },
  {
    "path": "python/42_dynamic_programming/longest_increasing_subsequence.py",
    "content": "#!/usr/bin/python\n# -*- coding: UTF-8 -*-\n\nfrom typing import List\n\n\ndef longest_increasing_subsequence(nums: List[int]) -> int:\n    \"\"\"\n    最长子上升序列的一种DP解法，从回溯解法转化，思路类似于有限物品的背包问题\n    每一次决策都算出当前可能的lis的长度，重复子问题合并，合并策略是lis的末尾元素最小\n    时间复杂度：O(n^2)\n    空间复杂度：O(n^2)，可优化至O(n)\n\n    没leetcode上的参考答案高效，提供另一种思路作为参考\n    https://leetcode.com/problems/longest-increasing-subsequence/solution/\n    :param nums:\n    :return:\n    \"\"\"\n    if not nums:\n        return 0\n\n    n = len(nums)\n    # memo[i][j] 表示第i次决策，长度为j的lis的 最小的 末尾元素数值\n    # 每次决策都根据上次决策的所有可能转化，空间上可以类似背包优化为O(n)\n    memo = [[-1] * (n+1) for _ in range(n)]\n\n    # 第一列全赋值为0，表示每次决策都不选任何数\n    for i in range(n):\n        memo[i][0] = 0\n    # 第一次决策选数组中的第一个数\n    memo[0][1] = nums[0]\n\n    for i in range(1, n):\n        for j in range(1, n+1):\n            # case 1: 长度为j的lis在上次决策后存在，nums[i]比长度为j-1的lis末尾元素大\n            if memo[i-1][j] != -1 and nums[i] > memo[i-1][j-1]:\n                memo[i][j] = min(nums[i], memo[i-1][j])\n\n            # case 2: 长度为j的lis在上次决策后存在，nums[i]比长度为j-1的lis末尾元素小/等\n            if memo[i-1][j] != -1 and nums[i] <= memo[i-1][j-1]:\n                memo[i][j] = memo[i-1][j]\n\n            if memo[i-1][j] == -1:\n                # case 3: 长度为j的lis不存在，nums[i]比长度为j-1的lis末尾元素大\n                if nums[i] > memo[i-1][j-1]:\n                    memo[i][j] = nums[i]\n                # case 4: 长度为j的lis不存在，nums[i]比长度为j-1的lis末尾元素小/等\n                break\n\n    for i in range(n, -1, -1):\n        if memo[-1][i] != -1:\n            return i\n\n\nif __name__ == '__main__':\n    # 要求输入的都是大于0的正整数(可优化至支持任意整数)\n    nums = [2, 9, 3, 6, 5, 1, 7]\n    print(longest_increasing_subsequence(nums))\n"
  },
  {
    "path": "python/42_dynamic_programming/min_edit_dist.py",
    "content": "\"\"\"\n    Author: Wenru Dong\n\"\"\"\n\ndef levenshtein_dp(s: str, t: str) -> int:\n    m, n = len(s), len(t)\n    table = [[0] * (n) for _ in range(m)]\n\n    for i in range(n):\n        if s[0] == t[i]:\n            table[0][i] = i - 0\n        elif i != 0:\n            table[0][i] = table[0][i - 1] + 1\n        else:\n            table[0][i] = 1\n\n    for i in range(m):\n        if s[i] == t[0]:\n            table[i][0] = i - 0\n        elif i != 0:\n            table[i][0] = table[i - 1][0] + 1\n        else:\n            table[i][0] = 1\n\n    for i in range(1, m):\n        for j in range(1, n):\n            table[i][j] = min(1 + table[i - 1][j], 1 + table[i][j - 1], int(s[i] != t[j]) + table[i - 1][j - 1])\n\n    print(table)\n    return table[-1][-1]\n\n\ndef common_substring_dp(s: str, t: str) -> int:\n    m, n = len(s), len(t)\n    table = [[0] * (n + 1) for _ in range(m + 1)]\n    for i in range(1, m + 1):\n        for j in range(1, n + 1):\n            table[i][j] = max(table[i - 1][j], table[i][j - 1], int(s[i - 1] == t[j - 1]) + table[i - 1][j - 1])\n    return table[-1][-1]\n\n\nif __name__ == \"__main__\":\n    s = \"mitcmu\"\n    t = \"mtacnu\"\n\n    print(levenshtein_dp(s, t))\n    print(common_substring_dp(s, t))\n\n    s = \"kitten\"\n    t = \"sitting\"\n\n    print(levenshtein_dp(s, t))\n    print(common_substring_dp(s, t))\n\n    s = \"flaw\"\n    t = \"lawn\"\n\n    print(levenshtein_dp(s, t))\n    print(common_substring_dp(s, t))"
  },
  {
    "path": "python/43_topological_sorting/topological_sorting.py",
    "content": "\"\"\"\n    Author: Wenru Dong\n\"\"\"\n\nfrom collections import deque\nfrom itertools import filterfalse\n\nclass Graph:\n    def __init__(self, num_vertices: int):\n        self._num_vertices = num_vertices\n        self._adjacency = [[] for _ in range(num_vertices)]\n    \n    def add_edge(self, s: int, t: int) -> None:\n        self._adjacency[s].append(t)\n\n    def tsort_by_kahn(self) -> None:\n        in_degree = [0] * self._num_vertices\n        for v in range(self._num_vertices):\n            if len(self._adjacency[v]):\n                for neighbour in self._adjacency[v]:\n                    in_degree[neighbour] += 1\n        q = deque(filterfalse(lambda x: in_degree[x], range(self._num_vertices)))\n        while q:\n            v = q.popleft()\n            print(f\"{v} -> \", end=\"\")\n            for neighbour in self._adjacency[v]:\n                in_degree[neighbour] -= 1\n                if not in_degree[neighbour]:\n                    q.append(neighbour)\n        print(\"\\b\\b\\b   \")\n\n    def tsort_by_dfs(self) -> None:\n        inverse_adjacency = [[] for _ in range(self._num_vertices)]\n        for v in range(self._num_vertices):\n            if len(self._adjacency[v]):\n                for neighbour in self._adjacency[v]:\n                    inverse_adjacency[neighbour].append(v)\n        visited = [False] * self._num_vertices\n\n        def dfs(vertex: int) -> None:\n            if len(inverse_adjacency[vertex]):\n                for v in inverse_adjacency[vertex]:\n                    if not visited[v]:\n                        visited[v] = True\n                        dfs(v)\n            print(f\"{vertex} -> \", end=\"\")\n        \n        for v in range(self._num_vertices):\n            if not visited[v]:\n                visited[v] = True\n                dfs(v)\n        \n        print(\"\\b\\b\\b   \")\n\n\nif __name__ == \"__main__\":\n\n    dag = Graph(4)\n    dag.add_edge(1, 0)\n    dag.add_edge(2, 1)\n    dag.add_edge(1, 3)\n    dag.tsort_by_kahn()\n    dag.tsort_by_dfs()\n"
  },
  {
    "path": "python/44_shortest_path/dijkstra.py",
    "content": "#!/usr/bin/python\n# -*- coding: UTF-8 -*-\n\nfrom typing import List, Generator\nimport heapq\n\n\nclass Graph:\n    def __init__(self, vertex_count: int) -> None:\n        self.adj = [[] for _ in range(vertex_count)]\n\n    def add_edge(self, s: int, t: int, w: int) -> None:\n        edge = Edge(s, t, w)\n        self.adj[s].append(edge)\n\n    def __len__(self) -> int:\n        return len(self.adj)\n\n\nclass Vertex:\n    def __init__(self, v: int, dist: int) -> None:\n        self.id = v\n        self.dist = dist\n\n    def __gt__(self, other) -> bool:\n        return self.dist > other.dist\n\n    def __repr__(self) -> str:\n        return str((self.id, self.dist))\n\n\nclass Edge:\n    def __init__(self, source: int, target: int, weight: int) -> None:\n        self.s = source\n        self.t = target\n        self.w = weight\n\n\nclass VertexPriorityQueue:\n    def __init__(self) -> None:\n        self.vertices = []\n\n    def get(self) -> Vertex:\n        return heapq.heappop(self.vertices)\n\n    def put(self, v: Vertex) -> None:\n        self.vertices.append(v)\n        self.update_priority()\n\n    def empty(self) -> bool:\n        return len(self.vertices) == 0\n\n    def update_priority(self) -> None:\n        heapq.heapify(self.vertices)\n\n    def __repr__(self) -> str:\n        return str(self.vertices)\n\n\ndef dijkstra(g: Graph, s: int, t: int) -> int:\n    size = len(g)\n\n    pq = VertexPriorityQueue()  # 节点队列\n    in_queue = [False] * size   # 已入队标记\n    vertices = [                # 需要随时更新离s的最短距离的节点列表\n        Vertex(v, float('inf')) for v in range(size)\n    ]\n    predecessor = [-1] * size   # 先驱\n\n    vertices[s].dist = 0\n    pq.put(vertices[s])\n    in_queue[s] = True\n\n    while not pq.empty():\n        v = pq.get()\n        if v.id == t:\n            break\n        for edge in g.adj[v.id]:\n            if v.dist + edge.w < vertices[edge.t].dist:\n                # 当修改了pq中的元素的优先级后：\n                # 1. 有入队操作：触发了pq的堆化，此后出队可以取到优先级最高的顶点\n                # 2. 无入队操作：此后出队取到的顶点可能不是优先级最高的，会有bug\n                # 为确保正确，需要手动更新一次\n                vertices[edge.t].dist = v.dist + edge.w\n                predecessor[edge.t] = v.id\n                pq.update_priority()        # 更新堆结构\n            if not in_queue[edge.t]:\n                pq.put(vertices[edge.t])\n                in_queue[edge.t] = True\n\n    for n in print_path(s, t, predecessor):\n        if n == t:\n            print(t)\n        else:\n            print(n, end=' -> ')\n    return vertices[t].dist\n\n\ndef print_path(s: int, t: int, p: List[int]) -> Generator[int, None, None]:\n    if t == s:\n        yield s\n    else:\n        yield from print_path(s, p[t], p)\n        yield t\n\n\nif __name__ == '__main__':\n    g = Graph(6)\n    g.add_edge(0, 1, 10)\n    g.add_edge(0, 4, 15)\n    g.add_edge(1, 2, 15)\n    g.add_edge(1, 3, 2)\n    g.add_edge(2, 5, 5)\n    g.add_edge(3, 2, 1)\n    g.add_edge(3, 5, 12)\n    g.add_edge(4, 5, 10)\n    print(dijkstra(g, 0, 5))\n\n    # 下面这个用例可以暴露更新队列元素优先级的问题\n    # g = Graph(4)\n    # g.add_edge(0, 1, 18)\n    # g.add_edge(0, 2, 3)\n    # g.add_edge(2, 1, 1)\n    # g.add_edge(1, 3, 5)\n    # g.add_edge(2, 3, 8)\n    # g.add_edge(0, 3, 15)\n    # print(dijkstra(g, 0, 3))\n"
  },
  {
    "path": "python/44_shortest_path/shortest_path.py",
    "content": "\"\"\"\n    Dijkstra algorithm\n\n    Author: Wenru Dong\n\"\"\"\n\nfrom dataclasses import dataclass\nfrom queue import PriorityQueue\n\n@dataclass\nclass Edge:\n    start_id: int\n    end_id: int\n    weight: int\n\n@dataclass(order=True)\nclass Vertex:\n    distance_to_start = float(\"inf\")\n    vertex_id: int\n\nclass Graph:\n    def __init__(self, num_vertices: int):\n        self._num_vertices = num_vertices\n        self._adjacency = [[] for _ in range(num_vertices)]\n    \n    def add_edge(self, from_vertex: int, to_vertex: int, weight: int) -> None:\n        self._adjacency[from_vertex].append(Edge(from_vertex, to_vertex, weight))\n\n    def dijkstra(self, from_vertex: int, to_vertex: int) -> None:\n        vertices = [Vertex(i) for i in range(self._num_vertices)]\n        vertices[from_vertex].distance_to_start = 0\n        visited = [False] * self._num_vertices\n        predecessor = [-1] * self._num_vertices\n        q = PriorityQueue()\n        q.put(vertices[from_vertex])\n        visited[from_vertex] = True\n        while not q.empty():\n            min_vertex = q.get()\n            if min_vertex.vertex_id == to_vertex:\n                break\n            for edge in self._adjacency[min_vertex.vertex_id]:\n                next_vertex = vertices[edge.end_id]\n                if min_vertex.distance_to_start + edge.weight < next_vertex.distance_to_start:\n                    next_vertex.distance_to_start = min_vertex.distance_to_start + edge.weight\n                    predecessor[next_vertex.vertex_id] = min_vertex.vertex_id\n                    if not visited[next_vertex.vertex_id]:\n                        q.put(next_vertex)\n                        visited[next_vertex.vertex_id] = True\n            \n        path = lambda x: path(predecessor[x]) + [str(x)] if from_vertex != x else [str(from_vertex)]\n        print(\"->\".join(path(to_vertex)))\n\n\nif __name__ == \"__main__\":\n    graph = Graph(6)\n    graph.add_edge(0, 1, 10)\n    graph.add_edge(0, 4, 15)\n    graph.add_edge(1, 2, 15)\n    graph.add_edge(1, 3, 2)\n    graph.add_edge(2, 5, 5)\n    graph.add_edge(3, 2, 1)\n    graph.add_edge(3, 5, 12)\n    graph.add_edge(4, 5, 10)\n    graph.dijkstra(0, 5)\n"
  },
  {
    "path": "python/45_bitmap/bitmap.py",
    "content": "\"\"\"\n    Author: Wenru Dong\n\"\"\"\n\nfrom typing import Optional\n\nclass Bitmap:\n    def __init__(self, num_bits: int):\n        self._num_bits = num_bits\n        self._bytes = bytearray(num_bits // 8 + 1)\n    \n    def setbit(self, k: int) -> None:\n        if k > self._num_bits or k < 1: return\n        self._bytes[k // 8] |= (1 << k % 8)\n    \n    def getbit(self, k: int) -> Optional[bool]:\n        if k > self._num_bits or k < 1: return\n        return self._bytes[k // 8] & (1 << k % 8) != 0\n\n\nif __name__ == \"__main__\":\n    bitmap = Bitmap(10)\n    bitmap.setbit(1)\n    bitmap.setbit(3)\n    bitmap.setbit(6)\n    bitmap.setbit(7)\n    bitmap.setbit(8)\n\n    for i in range(1, 11):\n        print(bitmap.getbit(i))"
  },
  {
    "path": "python/array.py",
    "content": "# 1.数组的插入、删除、按照下标随机访问操作；\r\n# 2.数组中的数据类型是Int\r\n#\r\n# Author:Lee\r\n\r\nclass Array():\r\n\r\n    def __init__(self):\r\n        '''数组类初始化方法.'''\r\n        self.__data = []  # 数据存储List\r\n\r\n    def find(self, index):\r\n        '''数组的查找方法.\r\n\r\n        参数：\r\n            index:将要查找的数据的下标\r\n\r\n        返回：\r\n            如果查找成功，则返回找到的数据\r\n            如果查找失败，则返回False\r\n        '''\r\n        if index > len(self.__data) or index < 0:\r\n            return False\r\n        else:\r\n            return self.__data[index]\r\n\r\n    def delete(self, index):\r\n        '''数组的删除方法.\r\n\r\n        参数：\r\n            index:将要删除的数据的下标\r\n\r\n        返回：\r\n            如果删除成功，则返回True\r\n            如果删除失败，则返回False\r\n        '''\r\n        if index > len(self.__data) or index < 0:\r\n            return False\r\n        else:\r\n            self.__data.pop(index)\r\n            return True\r\n\r\n    def insert(self, index, value):\r\n        '''数组插入数据操作.\r\n\r\n        参数：\r\n            index:将要插入的下标\r\n            value：将要插入的数据\r\n\r\n        返回：\r\n            如果插入成功，则返回True\r\n            如果插入失败，则返回False\r\n        '''\r\n        if index > len(self.__data) or index < 0:\r\n            return False\r\n        else:\r\n            self.__data.insert(index, value)\r\n            return True\r\n\r\n    def insertToTail(self, value):\r\n        '''直接在数组尾部插入数据.\r\n\r\n        参数：\r\n            value:将要插入的数据\r\n        '''\r\n        self.__data.append(value)\r\n\r\n    def printAll(self):\r\n        '''打印当前数组所有数据'''\r\n        print(self.__data)\r\n"
  },
  {
    "path": "rust/05_array/main.rs",
    "content": "struct NewArray {\n    array: Vec<i32>,\n    count: i32,\n}\n\nimpl NewArray {\n    fn new(capacity: usize) -> NewArray {\n        NewArray { array: Vec::with_capacity(capacity), count: 0 }\n    }\n\n    fn find(&self, index: usize) -> i32 {\n        if index > self.count as usize { return -1; }\n        self.array[index]\n    }\n\n    fn insert(&mut self, index: usize, value: i32) -> bool {\n        let array_count = self.count as usize;\n        if index > array_count || array_count == self.array.capacity() { return false; }\n\n        if index == array_count {\n            self.array.push(value);\n        } else {\n            let tmp_arr = self.array.clone();\n\n            self.array = Vec::with_capacity(self.array.capacity());\n            for i in 0..index {\n                self.array.push(tmp_arr[i]);\n            }\n\n            self.array.push(value);\n            for i in index..array_count {\n                self.array.push(tmp_arr[i]);\n            }\n        }\n\n        self.count += 1;\n        true\n    }\n\n    fn remove(&mut self, index: usize) -> i32 {\n        if index >= self.count as usize { return -1; }\n\n        let result = self.array[index];\n        let tmp_arr = self.array.clone();\n\n        self.array = Vec::with_capacity(self.array.capacity());\n        for i in 0..index {\n            self.array.push(tmp_arr[i]);\n        }\n\n        for i in index+1..self.count as usize {\n            self.array.push(tmp_arr[i]);\n        }\n\n        self.count -=1;\n        result\n    }\n}\n\nfn main() {\n    let mut new_array = NewArray::new(10);\n    assert_eq!(new_array.insert(0, 3), true);\n    assert_eq!(new_array.insert(1, 2), true);\n    assert_eq!(new_array.insert(2, 8), true);\n    assert_eq!(new_array.insert(0, 9), true);\n    assert_eq!(new_array.insert(5, 7), false);\n    assert_eq!(new_array.insert(4, 5), true);\n    assert_eq!(new_array.find(3), 8);\n    assert_eq!(new_array.find(12), -1);\n    assert_eq!(new_array.remove(1), 3);\n    assert_eq!(new_array.remove(9), -1);\n}\n"
  },
  {
    "path": "rust/07_linkedlist/linked_list_cycle.rs",
    "content": "use super::util::linked_list::{ListNode, to_list};\n\npub fn has_cycle(head: Option<Box<ListNode>>) -> bool {\n    let mut fast_p = &head;\n    let mut slow_p = &head;\n\n    while fast_p.is_some() && fast_p.as_ref().unwrap().next.is_some() {\n        slow_p = &slow_p.as_ref().unwrap().next;\n        fast_p = &fast_p.as_ref().unwrap().next.as_ref().unwrap().next;\n\n        if slow_p == fast_p { return true; }\n    }\n    false\n}\n\nfn main() {\n    println!(\"{:?}\", has_cycle(to_list(vec![1, 2, 3, 4, 5])));\n}\n"
  },
  {
    "path": "rust/07_linkedlist/merge_two_sorted_lists.rs",
    "content": "use super::util::linked_list::{ListNode, to_list};\n\npub fn merge_two_lists(l1: Option<Box<ListNode>>, l2: Option<Box<ListNode>>) -> Option<Box<ListNode>> {\n    match (l1, l2) {\n        (Some(node1), None) => Some(node1),\n        (None, Some(node2)) => Some(node2),\n        (Some(mut node1), Some(mut node2)) => {\n            if node1.val < node2.val {\n                let n = node1.next.take();\n                node1.next = Solution::merge_two_lists(n, Some(node2));\n                Some(node1)\n            } else {\n                let n = node2.next.take();\n                node2.next = Solution::merge_two_lists(Some(node1), n);\n                Some(node2)\n            }\n        },\n        _ => None,\n    }\n}\n\nfn main() {\n    println!(\"{:?}\", merge_two_lists(to_list(vec![1, 3, 4]), to_list(vec![1, 2, 4])));\n}\n"
  },
  {
    "path": "rust/07_linkedlist/middle_of_the_linked_list.rs",
    "content": "use super::util::linked_list::{ListNode, to_list};\n\npub fn middle_node(head: Option<Box<ListNode>>) -> Option<Box<ListNode>> {\n    let mut fast_p = &head;\n    let mut slow_p = &head;\n\n    while fast_p.is_some() && fast_p.as_ref().unwrap().next.is_some() {\n        slow_p = &slow_p.as_ref().unwrap().next;\n        fast_p = &fast_p.as_ref().unwrap().next.as_ref().unwrap().next;\n    }\n    slow_p.clone()\n}\n\nfn main() {\n    println!(\"{:?}\", middle_node(to_list(vec![1, 3, 4])));\n}\n"
  },
  {
    "path": "rust/07_linkedlist/remove_nth_node_from_end_of_list.rs",
    "content": "use super::util::linked_list::{ListNode, to_list};\n\npub fn remove_nth_from_end(head: Option<Box<ListNode>>, n: i32) -> Option<Box<ListNode>> {\n    let mut dummy = Some(Box::new(ListNode { val: 0, next: head }));\n    let mut cur = &mut dummy;\n    let mut length = 0;\n\n    while let Some(_node) = cur.as_mut() {\n        cur = &mut cur.as_mut().unwrap().next;\n        if let Some(_inner_node) = cur { length += 1; }\n    }\n\n    let mut new_cur = dummy.as_mut();\n    let idx = length - n;\n\n    for _ in 0..idx {\n        new_cur = new_cur.unwrap().next.as_mut();\n    }\n\n    let next = new_cur.as_mut().unwrap().next.as_mut().unwrap().next.take();\n    new_cur.as_mut().unwrap().next = next;\n\n    dummy.unwrap().next\n}\n\nfn main() {\n    println!(\"{:?}\", remove_nth_from_end(to_list(vec![1, 3, 4])));\n}\n"
  },
  {
    "path": "rust/07_linkedlist/reverse_linked_list.rs",
    "content": "use super::util::linked_list::{ListNode, to_list};\n\npub fn reverse_list(head: Option<Box<ListNode>>) -> Option<Box<ListNode>> {\n    let mut prev = None;\n    let mut curr = head;\n\n    while let Some(mut boxed_node) = curr.take() {\n        let next = boxed_node.next.take();\n        boxed_node.next = prev.take();\n\n        prev = Some(boxed_node);\n        curr = next;\n    }\n\n    prev\n}\n\nfn main() {\n    println!(\"{:?}\", reverse_list(to_list(vec![1, 2, 3, 4, 5])));\n}\n"
  },
  {
    "path": "rust/07_linkedlist/util/linked_list.rs",
    "content": "// Definition for singly-linked list.\n#[derive(PartialEq, Eq, Clone, Debug)]\npub struct ListNode {\n    pub val: i32,\n    pub next: Option<Box<ListNode>>\n}\n\nimpl ListNode {\n    #[inline]\n    fn new(val: i32) -> Self {\n        ListNode {\n            next: None,\n            val\n        }\n    }\n}\n\npub fn to_list(vec: Vec<i32>) -> Option<Box<ListNode>> {\n    let mut current = None;\n    for &v in vec.iter().rev() {\n        let mut node = ListNode::new(v);\n        node.next = current;\n        current = Some(Box::new(node));\n    }\n    current\n}\n\n#[macro_export]\nmacro_rules! linked {\n    ($($e:expr),*) => {to_list(vec![$($e.to_owned()), *])};\n    ($($e:expr,)*) => {to_list(vec![$($e.to_owned()), *])};\n}\n"
  },
  {
    "path": "rust/08_stack/simple_browser.rs",
    "content": "mod stack_based_on_linked_list;\n\nuse stack_based_on_linked_list::{LinkedListStack};\n\n#[derive(Hash, Eq, PartialEq, Debug, Default, Clone)]\nstruct Browser {\n    forward_stack: LinkedListStack,\n    back_stack: LinkedListStack,\n    current_page: String,\n}\n\nimpl Browser {\n    fn new() -> Self {\n        Default::default()\n    }\n\n    fn open(&mut self, url: &String) {\n        if !self.current_page.is_empty() {\n            self.back_stack.push(self.current_page.clone());\n            self.forward_stack.clear();\n        }\n        self.show_url(&url, \"Open\".to_string());\n    }\n\n    fn go_back(&mut self) -> String {\n        if self.can_go_back() {\n            self.forward_stack.push(self.current_page.clone());\n            let back_url = self.back_stack.pop();\n            self.show_url(&back_url, \"Back\".to_string());\n            return back_url;\n        }\n\n        println!(\"Can not go back, there is no page behind.\");\n        \"-1\".to_string()\n    }\n\n    fn go_forward(&mut self) -> String {\n        if self.can_go_forward() {\n            self.back_stack.push(self.current_page.clone());\n            let forward_url = self.forward_stack.pop();\n            self.show_url(&forward_url, \"Forward\".to_string());\n            return forward_url;\n        }\n\n        println!(\"Can not go forward, there is no page behind.\");\n        \"-1\".to_string()\n    }\n\n    fn can_go_back(&self) -> bool {\n        !self.back_stack.is_empty()\n    }\n\n    fn can_go_forward(&self) -> bool {\n        !self.forward_stack.is_empty()\n    }\n\n    fn show_url(&mut self, url: &String, prefix: String) {\n        self.current_page = url.to_string();\n        println!(\"{:?} page == {:?}\", prefix, url);\n    }\n\n    fn check_current_page(&self) {\n        println!(\"current page == {:?}\", self.current_page);\n    }\n}\nfn main() {\n    let mut browser = Browser::new();\n    browser.open(&\"http://www.baidu.com\".to_string());\n    browser.open(&\"http://news.baidu.com/\".to_string());\n    browser.open(&\"http://news.baidu.com/ent\".to_string());\n    browser.go_back();\n    browser.go_back();\n    browser.go_forward();\n    browser.open(&\"http://www.qq.com\".to_string());\n    browser.go_forward();\n    browser.go_back();\n    browser.go_forward();\n    browser.go_back();\n    browser.go_back();\n    browser.go_back();\n    browser.go_back();\n    browser.check_current_page();\n}\n"
  },
  {
    "path": "rust/08_stack/stack_based_on_array.rs",
    "content": "#[derive(Debug)]\nstruct ArrayStack {\n    data: Vec<i32>,\n    top: i32,\n}\n\nimpl ArrayStack {\n    fn new() -> Self {\n        ArrayStack { data: Vec::with_capacity(32), top: -1 }\n    }\n\n    fn push(&mut self, x: i32) {\n        self.top += 1;\n        if self.top == self.data.capacity() as i32 {\n            let tmp_arr = self.data.clone();\n            self.data = Vec::with_capacity(self.data.capacity() * 2);\n            for d in tmp_arr.into_iter() {\n                self.data.push(d);\n            }\n        }\n        self.data.push(x);\n    }\n\n    fn pop(&mut self) {\n        if self.is_empty() { return; }\n        self.top -= 1;\n        self.data.pop();\n    }\n\n    fn top(&self) -> i32 {\n        if self.is_empty() { return -1; }\n        *self.data.last().unwrap()\n    }\n\n    fn is_empty(&self) -> bool {\n        if self.top < 0 { true } else { false }\n    }\n}\n\nfn main() {\n    let mut stack = ArrayStack::new();\n    stack.push(-2);\n    stack.push(0);\n    stack.push(-3);\n    stack.pop();\n    println!(\"{:?}\", stack.top());\n}\n"
  },
  {
    "path": "rust/08_stack/stack_based_on_linked_list.rs",
    "content": "#[derive(Hash, Eq, PartialEq, Debug, Default, Clone)]\npub struct ListNode {\n    val: String,\n    next: Option<Box<ListNode>>,\n}\n\n#[derive(Hash, Eq, PartialEq, Debug, Default, Clone)]\npub struct LinkedListStack {\n    node: Option<Box<ListNode>>,\n}\n\nimpl ListNode {\n    fn new(val: String) -> Self {\n        ListNode { val: val, next: None }\n    }\n}\n\nimpl LinkedListStack {\n    pub fn new() -> Self {\n        Default::default()\n    }\n\n    pub fn push(&mut self, x: String) {\n        let mut n = ListNode::new(x);\n        n.next = self.node.clone();\n        self.node = Some(Box::new(n));\n    }\n\n    pub fn pop(&mut self) -> String {\n        if self.is_empty() { return \"-1\".to_string(); }\n\n        let val = self.node.as_ref().unwrap().val.clone();\n        self.node = self.node.as_mut().unwrap().next.take();\n        val.to_string()\n    }\n\n    pub fn print_all(&mut self) {\n        let mut list = String::from(\"\");\n\n        while let Some(n) = self.node.as_mut() {\n            list.push_str(&(n.val).to_string());\n            list.push_str(\"-->\");\n            self.node = n.next.take();\n        }\n        println!(\"{:?}\", list);\n    }\n\n    pub fn clear(&mut self) {\n        self.node = None;\n    }\n\n    pub fn is_empty(&self) -> bool {\n        if self.node.is_none() { true } else { false }\n    }\n}\n//\n// fn main() {\n//     let mut stack = LinkedListStack::new();\n//     stack.push(\"https://www.baidu.com\".to_string());\n//     stack.push(\"https://www.google.com\".to_string());\n//     stack.pop();\n//     stack.push(\"https://twitter.com\".to_string());\n//     stack.print_all();\n// }\n"
  },
  {
    "path": "rust/09_queue/array_queue.rs",
    "content": "#[derive(Debug)]\nstruct ArrayQueue {\n    queue: Vec<i32>,\n    head: i32,\n    tail: i32,\n}\n\nimpl ArrayQueue {\n    fn new(n: usize) -> Self {\n        ArrayQueue {\n            queue: Vec::with_capacity(n),\n            head: 0,\n            tail: 0,\n        }\n    }\n\n    fn enqueue(&mut self, num: i32) -> bool {\n        let c = self.queue.capacity() as i32;\n        // queue is full\n        if self.head == 0 && self.tail == c { return false; }\n        if self.tail == c {\n            for i in 0..(self.tail-self.head) as usize {\n                self.queue[i] = self.queue[self.head as usize + i];\n            }\n            self.tail -= self.head;\n            self.head = 0;\n            self.queue[self.tail as usize] = num;\n        } else {\n            self.queue.push(num);\n        }\n\n        self.tail += 1;\n        true\n    }\n\n    fn dequeue(&mut self) -> i32 {\n        if self.head == self.tail { return -1; }\n\n        let shift = self.queue[self.head as usize];\n        self.head += 1;\n        shift\n    }\n\n    fn print_all(&self) {\n        let mut s = String::from(\"\");\n        for i in self.head..self.tail {\n            s.push(self.queue[i as usize] as u8 as char);\n            s.push_str(\"->\");\n        }\n        println!(\"{:?}\", s);\n    }\n}\n\nfn main() {\n    let mut queue = ArrayQueue::new(3);\n    queue.enqueue(2);\n    queue.enqueue(2);\n    queue.enqueue(2);\n    queue.enqueue(2);\n    queue.dequeue();\n    queue.dequeue();\n    queue.enqueue(4);\n    queue.dequeue();\n    queue.print_all();\n}\n"
  },
  {
    "path": "rust/09_queue/circle_queue.rs",
    "content": "#[derive(Debug)]\nstruct CircleQueue {\n    queue: Vec<i32>,\n    head: i32,\n    tail: i32,\n    n: i32,\n}\n\nimpl CircleQueue {\n    fn new(n: i32) -> Self {\n        CircleQueue {\n            queue: vec![-1; n as usize],\n            head: 0,\n            tail: 0,\n            n: n,\n        }\n    }\n\n    fn enqueue(&mut self, num: i32) -> bool {\n        if (self.tail + 1) % self.n == self.head { return false; }\n        self.queue[self.tail as usize] = num;\n        self.tail = (self.tail + 1) % self.n;\n        true\n    }\n\n    fn dequeue(&mut self) -> i32 {\n        if self.head == self.tail { return -1; }\n\n        let shift = self.queue[self.head as usize];\n        self.head = (self.head + 1) % self.n;\n        shift\n    }\n\n    fn print_all(&self) {\n        let mut s = String::from(\"\");\n        for i in self.head..self.tail {\n            s.push(self.queue[i as usize] as u8 as char);\n            s.push_str(\"->\");\n        }\n        println!(\"{:?}\", s);\n    }\n}\n\nfn main() {\n    let mut queue = CircleQueue::new(10);\n    queue.enqueue(2);\n    queue.enqueue(2);\n    queue.enqueue(2);\n    queue.enqueue(2);\n    queue.dequeue();\n    queue.dequeue();\n    queue.enqueue(4);\n    queue.dequeue();\n    queue.print_all();\n}\n"
  },
  {
    "path": "rust/09_queue/linked_list_queue.rs",
    "content": "#![feature(box_into_raw_non_null)]\n\nuse std::ptr::NonNull;\n\n#[derive(Debug)]\npub struct LinkedListQueue {\n    head: Option<NonNull<Node>>,\n    tail: Option<NonNull<Node>>,\n}\n\npub struct Node {\n    next: Option<NonNull<Node>>,\n    element: i32,\n}\n\nimpl Node {\n    fn new(element: i32) -> Self {\n        Node {\n            next: None,\n            element,\n        }\n    }\n\n    fn into_element(self: Box<Self>) -> i32 {\n        self.element\n    }\n}\n\nimpl LinkedListQueue {\n    pub fn new() -> Self {\n        LinkedListQueue {\n            head: None,\n            tail: None,\n        }\n    }\n\n    pub fn dequeue(&mut self) -> i32 {\n        self.head.map(|node| unsafe {\n            let node = Box::from_raw(node.as_ptr());\n            self.head = node.next;\n            node\n        }).map(Node::into_element).unwrap()\n    }\n\n    pub fn enqueue(&mut self, elt: i32) {\n        let mut node = Box::new(Node::new(elt));\n        unsafe {\n            node.next = None;\n            let node = Some(Box::into_raw_non_null(node));\n\n            match self.tail {\n                None => self.head = node,\n                Some(tail) => (*tail.as_ptr()).next = node,\n            }\n\n            self.tail = node;\n        }\n    }\n}\n\nfn main() {\n    let mut m = LinkedListQueue::new();\n    m.enqueue(4);\n    m.enqueue(4);\n    m.enqueue(4);\n    m.dequeue();\n    m.dequeue();\n    println!(\"{:?}\", m);\n}\n"
  },
  {
    "path": "rust/11_sorts/bubble_sort.rs",
    "content": "/// 冒泡排序\n/// 时间复杂度:O(n2)，原地排序算法, 稳定排序算法\n// 冒泡排序\nfn bubble_sort(mut nums: Vec<i32>) -> Vec<i32> {\n    if nums.is_empty() { return vec![]; }\n\n    let n = nums.len();\n    for i in 0..n {\n        // 提前退出标志\n        let mut swap = false;\n        for j in 0..n-i-1 {\n            if nums[j] > nums[j+1] {\n                // 此次冒泡有数据交换\n                swap = true;\n                let tmp = nums[j];\n                nums[j] = nums[j+1];\n                nums[j+1] = tmp;\n            }\n        }\n        // 若没有数据交换，提前退出\n        if !swap { break; }\n    }\n    nums\n}\n\nfn main() {\n    let nums = vec![4, 5, 6, 1, 2, 3];\n    println!(\"{:?}\", bubble_sort(nums));\n}\n"
  },
  {
    "path": "rust/11_sorts/insertion_sort.rs",
    "content": "/// 插入排序\n/// 时间复杂度:O(n2)，原地排序算法, 稳定排序算法\n// 插入排序\nfn insertion_sort(mut nums: Vec<i32>) -> Vec<i32> {\n    if nums.is_empty() { return vec![]; }\n\n    for i in 1..nums.len() {\n        let value = nums[i];\n        let mut j = (i - 1) as i32;\n        // 查找要插入的位置并移动数据\n        while j >= 0 {\n            if nums[j as usize] > value {\n                nums[(j+1) as usize] = nums[j as usize];\n            } else {\n                break;\n            }\n            j -= 1;\n        }\n        nums[(j+1) as usize] = value;\n    }\n    nums\n}\n\nfn main() {\n    let nums = vec![4, 5, 6, 1, 2, 3];\n    println!(\"{:?}\", insert_on_sort(nums));\n}\n"
  },
  {
    "path": "rust/11_sorts/selection_sort.rs",
    "content": "/// 选择排序\n/// 时间复杂度:O(n2)，原地排序算法, 不稳定排序算法\n// 选择排序\nfn selection_sort(mut nums: Vec<i32>) -> Vec<i32> {\n    if nums.is_empty() { return vec![]; }\n\n    for i in 0..nums.len() {\n        let mut mini_index = i;\n        // 查找最小 index\n        for j in i+1..nums.len() {\n            if nums[j] < nums[mini_index] {\n                mini_index = j;\n            }\n        }\n        // 交换数据\n        let tmp = nums[i];\n        nums[i] = nums[mini_index];\n        nums[mini_index] = tmp;\n    }\n    nums\n}\n\nfn main() {\n    let nums = vec![4, 5, 6, 1, 2, 3];\n    println!(\"{:?}\", selection_sort(nums));\n}\n"
  },
  {
    "path": "rust/12_sorts/kth_largest.rs",
    "content": "pub fn kth_largest(mut nums: Vec<i32>, k: i32) -> Option<i32> {\n    if nums.is_empty() || k >= nums.len() as i32 { return None; }\n\n    let end = nums.len() - 1;\n    let k = k as usize;\n    // 分区点\n    let mut pivot = partition(&mut nums, 0, end);\n    while pivot + 1 != k {\n        if k > pivot + 1 {\n            pivot = partition(&mut nums, pivot + 1, end);\n        } else {\n            pivot = partition(&mut nums, 0, pivot - 1);\n        }\n    }\n\n    Some(nums[pivot])\n}\n\nfn partition(nums: &mut Vec<i32>, start: usize, end: usize) -> usize {\n    let pivot = nums[end];\n    let mut i = start;\n    for j in start..end {\n        if nums[j] >= pivot { // if nums[j] <= pivot then swap, search kth smallest\n            swap(nums, i, j);\n            i += 1;\n        }\n    }\n\n    swap(nums, i, end);\n    i\n}\n\nfn swap(nums: &mut Vec<i32>, i: usize, j: usize) {\n    if i == j { return; }\n\n    let tmp = nums[i];\n    nums[i] = nums[j];\n    nums[j] = tmp;\n}\n\nfn main() {\n    let nums = vec![8, 10, 2, 3, 6,1, 5];\n    println!(\"{:?}\", kth_largest(nums, 3));\n}\n"
  },
  {
    "path": "rust/12_sorts/merge_sort.rs",
    "content": "/// 归并排序\n/// 时间复杂度 O(nlogn), 空间复杂度：O(n), 稳定排序\n// 归并排序\npub fn merge_sort(mut nums: Vec<i32>) -> Vec<i32> {\n    if nums.is_empty() { return nums; }\n\n    let n = nums.len() - 1;\n    merge_sort_internally(&mut nums, 0, n);\n    nums\n}\n\nfn merge_sort_internally(nums: &mut Vec<i32>, start: usize, end: usize) {\n    if start >= end { return; }\n\n    let middle = start + (end - start) / 2;\n    merge_sort_internally(nums, start, middle);\n    merge_sort_internally(nums, middle+1, end);\n\n    // merge two array\n    merge(nums, start, middle, end);\n}\n\nfn merge(nums: &mut Vec<i32>, start: usize, middle: usize, end: usize) {\n    let mut i = start;\n    let mut j = middle + 1;\n    let mut tmp = vec![];\n    while i <= middle && j <= end {\n        if nums[i] <= nums[j] {\n            tmp.push(nums[i]);\n            i += 1;\n        } else {\n            tmp.push(nums[j]);\n            j += 1;\n        }\n    }\n\n    let mut s = i;\n    let mut r = middle;\n    if j <= end {\n        s = j;\n        r = end;\n    }\n\n    while s <= r {\n        tmp.push(nums[s]);\n        s += 1;\n    }\n\n    for i in 0..=(end-start) {\n        nums[start+i] = tmp[i];\n    }\n}\n\nfn main() {\n    let nums = vec![8, 10, 2, 3, 6,1, 5];\n    println!(\"{:?}\", merge_sort(nums));\n}\n"
  },
  {
    "path": "rust/12_sorts/quick_sort.rs",
    "content": "/// 快排\n/// 时间复杂度：O(nlogn), 空间复杂度： O(1), 不稳定排序\n// 快排\npub fn quick_sort(mut nums: Vec<i32>) -> Vec<i32> {\n    if nums.is_empty() { return nums; }\n\n    let n = nums.len() - 1;\n    quick_sort_internally(&mut nums, 0, n);\n    nums\n}\n\nfn quick_sort_internally(nums: &mut Vec<i32>, start: usize, end: usize) {\n    if start >= end { return; }\n\n    // 分区点\n    let pivot = partition(nums, start, end);\n    if pivot != 0 {\n        quick_sort_internally(nums, start, pivot-1);\n    }\n    quick_sort_internally(nums, pivot+1, end);\n}\n\nfn partition(nums: &mut Vec<i32>, start: usize, end: usize) -> usize {\n    let pivot = nums[end];\n    let mut i = start;\n    for j in start..end {\n        if nums[j] < pivot {\n            swap(nums, i, j);\n            i += 1;\n        }\n    }\n\n    swap(nums, i, end);\n    i\n}\n\nfn swap(nums: &mut Vec<i32>, i: usize, j: usize) {\n    if i == j { return; }\n\n    let tmp = nums[i];\n    nums[i] = nums[j];\n    nums[j] = tmp;\n}\n\nfn main() {\n    let nums = vec![8, 10, 2, 3, 6,1, 5];\n    println!(\"{:?}\", quick_sort(nums));\n}\n"
  },
  {
    "path": "rust/13_sorts/bucket_sort.rs",
    "content": "/// 桶排序\n/// 时间复杂度：O(n), 稳定排序\n// 桶排序\npub fn bucket_sort(mut nums: Vec<i32>, step: i32) -> Vec<i32> {\n    let (mut min, mut max) = (nums[0], nums[0]);\n    for i in 0..nums.len() {\n        if min > nums[i] { min = nums[i]; }\n        if max < nums[i] { max = nums[i]; }\n    }\n\n    // 设置需要的桶的数量\n    let bucket_num = (max - min) / step + 1;\n    let mut bucket_list: Vec<Vec<i32>> = vec![vec![]; bucket_num as usize];\n    // 将 nums 数组中元素分别装入桶中\n    for i in 0..nums.len() {\n        // 计算桶 index\n        let index = (nums[i] - min) / step;\n        bucket_list[index as usize].push(nums[i]);\n    }\n\n    let mut index = 0;\n    for i in 0..bucket_num {\n        let bucket = &bucket_list[i as usize];\n        // 对每个桶中元素使用快排进行排序\n        let new_bucket = quick_sort(bucket.to_vec());\n        // 将已经排序好的桶中元素拷贝到 nums 数组中\n        for num in new_bucket.into_iter() {\n            nums[index as usize] = num;\n            index += 1;\n        }\n    }\n    nums\n}\n\npub fn quick_sort(mut nums: Vec<i32>) -> Vec<i32> {\n    if nums.is_empty() { return nums; }\n\n    let n = nums.len() - 1;\n    quick_sort_internally(&mut nums, 0, n);\n    nums\n}\n\nfn quick_sort_internally(nums: &mut Vec<i32>, start: usize, end: usize) {\n    if start >= end { return; }\n\n    // 分区点\n    let pivot = partition(nums, start, end);\n    if pivot != 0 {\n        quick_sort_internally(nums, start, pivot-1);\n    }\n    quick_sort_internally(nums, pivot+1, end);\n}\n\nfn partition(nums: &mut Vec<i32>, start: usize, end: usize) -> usize {\n    let pivot = nums[end];\n    let mut i = start;\n    for j in start..end {\n        if nums[j] < pivot {\n            swap(nums, i, j);\n            i += 1;\n        }\n    }\n\n    swap(nums, i, end);\n    i\n}\n\nfn swap(nums: &mut Vec<i32>, i: usize, j: usize) {\n    if i == j { return; }\n\n    let tmp = nums[i];\n    nums[i] = nums[j];\n    nums[j] = tmp;\n}\n\nfn main() {\n    let nums = vec![2, 5, 3, 0, 2, 3, 0, 3];\n    let m = bucket_sort(nums, 3);\n    println!(\"{:?}\", m);\n}\n"
  },
  {
    "path": "rust/13_sorts/counting_sort.rs",
    "content": "/// 计数排序\n/// 时间复杂度：O(n), 稳定排序\n// 计数排序\npub fn counting_sort(mut nums: Vec<i32>) -> Vec<i32> {\n    if nums.len() <= 1 { return nums; }\n\n    let nums_len = nums.len();\n    // 获取最大数\n    let mut max = nums[0];\n    \n    let mut tmp = vec![0; nums_len];\n\n    for i in 1..nums_len {\n        if max < nums[i] { max = nums[i]; }\n    }\n    // 申请一个长度为 max + 1 的新数组\n    let mut bucket = vec![0; (max+1) as usize];\n\n    for i in 0..nums_len {\n        bucket[nums[i] as usize] += 1;\n    }\n\n    // 对 bucket 中元素进行累加，针对 nums 数组中每个元素，小于等于这个元素的个数\n    // 如，nums 数组中元素 3，小于等于 3 的个数为 7 个\n    for i in 1..bucket.len() {\n        bucket[i] += bucket[i-1];\n    }\n\n    // 排序\n    // 1. 申请一个与 nums 等长的数组，用于存储排序后的内容；\n    // 2. 对数组 nums 从后向前迭代\n    // 1). 从 bucket 数组中取出下标为 nums 数组中当前元素的值，如 nums 中当前元素为3，则从 bucket\n    //   中取出下标为 3 的元素（即：在 nums 数组中元素 3 的位置应该为 7，index 为 6）\n    // 2). 将元素 3 存入临时数组，此时元素 3 的个数变为 6 个\n    // 3). 依次类推，直到将所有元素存入临时数组 tmp 中，完成排序\n    for i in (0..nums_len).rev() {\n        let index = bucket[nums[i] as usize] - 1;\n        tmp[index] = nums[i];\n        bucket[nums[i] as usize] -= 1;\n    }\n\n    for i in 0..tmp.len() {\n        nums[i] = tmp[i];\n    }\n    nums\n}\n\nfn main() {\n    let nums = vec![2, 5, 3, 0, 2, 3, 0, 3];\n    let m = counting_sort(nums);\n    println!(\"{:?}\", m);\n}\n"
  },
  {
    "path": "rust/13_sorts/radix_sort.rs",
    "content": "/// 基数排序\n/// 时间复杂度：O(n), 稳定排序\n// 基数排序\npub fn radix_sort(mut nums: Vec<i64>) -> Vec<i64> {\n    let max_bit = max_bit(&nums);\n    // 申请一个长度为 10 的桶\n    let mut bucket = vec![0; 10];\n    let mut tmp = vec![0; nums.len()];\n    let mut radix = 1;\n    let nums_len = nums.len();\n\n    // 迭代 max_bit 次\n    for _i in 0..max_bit {\n        // 每次比较前先将桶清空\n        for j in 0..bucket.len() { bucket[j] = 0; }\n\n        for j in 0..nums_len {\n            let index = ((nums[j] / radix) % 10) as usize;\n            bucket[index] += 1;\n        }\n\n        for j in 1..bucket.len() { bucket[j] += bucket[j-1]; }\n\n        // 对 nums 进行排序\n        for j in (0..nums_len).rev() {\n            let index = ((nums[j] / radix) % 10) as usize;\n            tmp[(bucket[index]-1) as usize] = nums[j];\n            bucket[index] -= 1;\n        }\n\n        for j in 0..nums_len {\n            nums[j] = tmp[j];\n        }\n\n        radix *= 10;\n    }\n    nums\n}\n\nfn max_bit(nums: &Vec<i64>) -> i32 {\n    let mut max = nums[0];\n    let mut bit = 1; // 预防数据库中元素全部是 0 的情况\n    for &num in nums.iter() {\n        if num > max { max = num; }\n    }\n\n    while max >= 10 {\n        max = max / 10;\n        bit += 1;\n    }\n    bit\n}\n\nfn main() {\n    // let nums = vec![1, 10, 100, 1000, 98, 67, 3, 28, 67, 888, 777];\n    let nums = vec![13111111111, 13299999999, 13311111111, 13133333333, 13922222222, 13722222222];\n    println!(\"{:?}\", radix_sort(nums));\n}\n"
  },
  {
    "path": "rust/13_sorts/sort_string.rs",
    "content": "fn sort_string(s: String) -> Vec<Vec<char>> {\n    let mut bucket: Vec<Vec<char>> = vec![vec![]; 3];\n    for ch in s.as_bytes() {\n        if ch as u8 >= 48 && ch as u8 <= 57 {\n            bucket[0].push(ch);\n        } else if ch as u8  >= 65 && ch as u8 <= 90 {\n            bucket[1].push(ch);\n        } else {\n            bucket[2].push(ch);\n        }\n    }\n\n    bucket\n}\nfn main() {\n    let s = \"DaFBCA789\".to_string();\n    println!(\"{:?}\", sort_string(s));\n}\n"
  },
  {
    "path": "rust/15_binary_search/binary_search.rs",
    "content": "// 二分查找\npub fn binary_search(nums: Vec<i32>, value: i32) -> i32 {\n    if nums.is_empty() { return -1; }\n\n    let mut low = 0;\n    let mut high = nums.len() - 1;\n\n    while low <= high {\n        let mid = low + ((high - low) >> 1);\n        if nums[mid] == value { return mid as i32; }\n\n        if nums[mid] < value {\n            low = mid + 1;\n        } else {\n            high = mid -1;\n        }\n    }\n    -1\n}\n\n// 二分查找递归\npub fn binary_search_recursion(nums: Vec<i32>, value: i32) -> i32 {\n    if nums.is_empty() { return -1; }\n\n    _recursion(&nums, 0, nums.len()-1, value)\n}\n\nfn _recursion(nums: &Vec<i32>, low: usize, high: usize, value: i32) -> i32 {\n    if low > high { return -1; }\n\n    let mid = low + ((high - low) >> 1);\n    if nums[mid] == value { return mid as i32; }\n\n    if nums[mid] < value {\n        return _recursion(nums, mid+1, high, value);\n    } else {\n        return _recursion(nums, low, mid-1, value);\n    }\n}\n\nfn main() {\n    let nums1 = vec![8,11,19,23,27,33,45,55,67,98];\n    let nums2 = vec![8,11,19,23,27,33,45,55,67,98];\n    println!(\"{:?}\", binary_search(nums1, 23));\n    println!(\"{:?}\", binary_search_recursion(nums2, 23));\n}\n"
  },
  {
    "path": "rust/15_binary_search/sqrtx.rs",
    "content": "// leetcode: https://leetcode.com/problems/sqrtx/\n\npub fn my_sqrt(x: i32, precision: f32) -> f32 {\n    if x == 0 || x == 1 { return x as f32; }\n\n    let mut left = 0f32;\n    let mut right = x as f32;\n    let mut res = 0f32;\n\n    while left <= right {\n        let mid: f32 = (right - left) / 2.0 + left;\n\n        if (right - left).abs() < precision { return mid; }\n\n        if mid > x as f32 / mid {\n            right = mid;\n        } else {\n            left = mid;\n        }\n        res = mid\n    }\n\n    res\n}\n\nfn main() {\n    println!(\"{:?}\", my_sqrt(8, 0.000001));\n}\n"
  },
  {
    "path": "rust/16_binary_search/binary_search.rs",
    "content": "// 查找第一个给定值的元素\nfn find_first_eq(nums: Vec<i32>, value: i32) -> i32 {\n    if nums.is_empty() { return -1; }\n\n    let mut start = 0;\n    let mut end = nums.len() - 1;\n\n    while start <= end {\n        let mid = start + ((end - start) >> 1);\n        if nums[mid] <= value {\n            if mid == 0 || (nums[mid] == value && nums[mid-1] != value) { return mid as i32; }\n            start = mid + 1;\n        } else {\n            end = mid - 1;\n        }\n    }\n    -1\n}\n\n// 查找最后一个给定值的元素\nfn find_last_eq(nums: Vec<i32>, value: i32) -> i32 {\n    if nums.is_empty() { return -1; }\n\n    let mut start = 0;\n    let mut end = nums.len() - 1;\n\n    while start <= end {\n        let mid = start + ((end - start) >> 1);\n        if nums[mid] <= value {\n            if mid == end || (nums[mid] == value && nums[mid+1] != value) { return mid as i32; }\n            start = mid + 1;\n        } else {\n            end = mid - 1;\n        }\n    }\n    -1\n}\n\n// 查找第一个大于等于给定值的元素\nfn find_first_ge(nums: Vec<i32>, value: i32) -> i32 {\n    if nums.is_empty() { return -1; }\n\n    let mut start = 0;\n    let mut end = nums.len() - 1;\n\n    while start <= end {\n        let mid = start + ((end - start) >> 1);\n        if nums[mid] < value {\n            start = mid + 1;\n        } else {\n            if mid == end || (nums[mid] >= value && nums[mid-1] < value) { return mid as i32; }\n            end = mid - 1;\n        }\n    }\n    -1\n}\n\n// 查找最后一个小于等于给定值的元素\nfn find_last_le(nums: Vec<i32>, value: i32) -> i32 {\n    if nums.is_empty() { return -1; }\n\n    let mut start = 0;\n    let mut end = nums.len() - 1;\n\n    while start <= end {\n        let mid = start + ((end - start) >> 1);\n        if nums[mid] <= value {\n            if mid == 0 || (nums[mid] <= value && nums[mid+1] > value) { return mid as i32; }\n            start = mid + 1;\n        } else {\n            end = mid - 1;\n        }\n    }\n    -1\n}\n\nfn main() {\n    let nums1 = vec![1, 3, 5, 6, 8, 8, 8, 11, 18];\n    let first_eq = find_first_eq(nums1, 8);\n    println!(\"{:?}\", first_eq);\n\n    let nums2 = vec![1, 3, 5, 6, 8, 8, 8, 11, 18];\n    let last_eq = find_last_eq(nums2, 8);\n    println!(\"{:?}\", last_eq);\n\n    let nums3 = vec![1, 3, 5, 6, 8, 8, 8, 11, 18];\n    let find_first_ge = find_first_ge(nums3, 5);\n    println!(\"{:?}\", find_first_ge);\n\n    let nums4 = vec![1, 3, 5, 6, 8, 8, 8, 11, 18];\n    let find_last_le = find_last_le(nums4, 17);\n    println!(\"{:?}\", find_last_le);\n}\n"
  },
  {
    "path": "rust/16_binary_search/search_in_rotated_sorted_array.rs",
    "content": "// leetcode 33 Search in Rotated Sorted Array (https://leetcode.com/problems/search-in-rotated-sorted-array/)\npub fn search(nums: Vec<i32>, target: i32) -> i32 {\n    if nums.is_empty() { return -1; }\n\n    let mut low = 0;\n    let mut high = nums.len() - 1;\n\n    while low <= high {\n        let mid = low + ((high - low) >> 1);\n        if nums[mid] == target { return mid as i32; }\n\n        // left is order\n        if nums[low] <= nums[mid] {\n            // target is in left array\n            if nums[low] <= target && target <= nums[mid] {\n                high = mid - 1;\n            }  else {\n                low = mid + 1;\n            }\n        } else {\n            if nums[mid] <= target && target <= nums[high] {\n                low = mid + 1;\n            } else {\n                high = mid - 1;\n            }\n        }\n    }\n    -1\n}\n\nfn main() {\n    let nums = vec![4,5,6,7,0,1,2];\n    let n = search(nums, 0);\n    println!(\"{:?}\", n);\n}\n"
  },
  {
    "path": "rust/19_hash_table/hash_table.rs",
    "content": "#[derive(Debug,Default)]\npub struct MyHashTable<'a> {\n    table: Vec<Option<&'a str>>,\n    capacity: usize,\n}\n\nimpl<'a> MyHashTable<'a> {\n    fn new() -> MyHashTable<'a> {\n        MyHashTable {\n            table: vec![None; 16],\n            capacity: 16,\n        }\n    }\n\n    pub fn insert(&mut self, key: &'a str, value: &'a str) {\n        let pos = self.hash(key) as usize;\n        self.table[pos] = Some(value);\n    }\n\n    pub fn get(&self, key: &'a str) -> Option<&'a str> {\n        let pos = self.hash(key) as usize;\n        self.table[pos]\n    }\n\n    pub fn remove(&mut self, key: &'a str) -> Option<&'a str> {\n        let pos = self.hash(key) as usize;\n        let value = self.table[pos];\n        self.table[pos] = None;\n        value\n    }\n\n    fn hash(&self, key: &'a str) -> i32 {\n        let h = self.hash_code(key);\n        (h ^ (h >> 16)) & (self.capacity as i32 - 1)\n    }\n\n    fn hash_code(&self, key: &'a str) -> i32 {\n        let mut hash = 0;\n        for ch in key.chars() {\n            hash += 31 * hash + ch as i32;\n        }\n        hash as i32\n    }\n}\nfn main() {\n    let mut hash_table = MyHashTable::new();\n    hash_table.insert(\"hello\", \"rust\");\n    println!(\"{:?}\", hash_table);\n    hash_table.insert(\"hi\", \"C++\");\n    println!(\"{:?}\", hash_table);\n    let m = hash_table.get(\"hello\");\n    println!(\"{:?}\", m);\n    let n = hash_table.remove(\"hi\");\n    println!(\"{:?}\", n);\n    println!(\"{:?}\", hash_table);\n}\n"
  },
  {
    "path": "rust/23_binary_tree/inorder_traversal.rs",
    "content": "// leetcode: https://leetcode.com/problems/binary-tree-inorder-traversal/\nuse super::util::tree::{TreeNode, to_tree};\n\n// 中序遍历（Recursive Approach）\npub fn inorder_traversal(root: Option<Rc<RefCell<TreeNode>>>) -> Vec<i32> {\n    let mut result: Vec<i32> = vec![];\n    if root.is_none() { return result; }\n\n    _inorder(root, &mut result);\n    result\n}\n\nfn _inorder(root: Option<Rc<RefCell<TreeNode>>>, result: &mut Vec<i32>) {\n    match root {\n        Some(node) => {\n            _inorder(node.borrow().left.clone(), result);\n            result.push(node.borrow().val);\n            _inorder(node.borrow().right.clone(), result);\n        },\n        None => { return; }\n    }\n}\n\n// 中序遍历（Iterating method using Stack）\npub fn inorder_traversal(root: Option<Rc<RefCell<TreeNode>>>) -> Vec<i32> {\n    let mut result = vec![];\n    if root.is_none() { return result; }\n\n    let mut stack: Vec<Rc<RefCell<TreeNode>>> = Vec::new();\n    let mut r = root.clone();\n\n    while r.is_some() || !stack.is_empty() {\n        while let Some(node) = r {\n            stack.push(node.clone());\n            r = node.borrow().left.clone();\n        }\n        r = stack.pop();\n        if let Some(node) = r {\n            result.push(node.borrow().val);\n            r = node.borrow().right.clone();\n        }\n    }\n    result\n}\n"
  },
  {
    "path": "rust/23_binary_tree/level_order_traversal.rs",
    "content": "// leetcode https://leetcode.com/problems/binary-tree-level-order-traversal/\n\nuse super::util::tree::{TreeNode, to_tree};\nuse std::collections::VecDeque;\n\npub fn level_order(root: Option<Rc<RefCell<TreeNode>>>) -> Vec<Vec<i32>> {\n    let mut result: Vec<Vec<i32>> = vec![];\n    if root.is_none() { return result; }\n\n    let mut deque: VecDeque<Option<Rc<RefCell<TreeNode>>>> = VecDeque::new();\n    deque.push_back(root);\n\n    while !deque.is_empty() {\n        let mut current_level = vec![];\n        let mut added = false;\n        let level_size = deque.len();\n\n        for i in 0..level_size {\n            let n = deque.pop_front();\n            if let Some(Some(node)) = n {\n                current_level.push(node.borrow().val);\n                added = true;\n                if node.borrow().left.is_some() { deque.push_back(node.borrow().left.clone()); }\n                if node.borrow().right.is_some() { deque.push_back(node.borrow().right.clone()); }\n            }\n        }\n\n        if !added { break; }\n\n        result.push(current_level);\n    }\n\n    result\n}\n"
  },
  {
    "path": "rust/23_binary_tree/postorder_traversal.rs",
    "content": "// https://leetcode.com/problems/binary-tree-postorder-traversal/\nuse super::util::tree::{TreeNode, to_tree};\n\n// 后续遍历（Recursive Approach）\npub fn postorder_traversal(root: Option<Rc<RefCell<TreeNode>>>) -> Vec<i32> {\n    let mut result: Vec<i32> = vec![];\n    if root.is_none() { return result; }\n\n    _postorder(root, &mut result);\n    result\n}\n\nfn _postorder(root: Option<Rc<RefCell<TreeNode>>>, result: &mut Vec<i32>) {\n    match root {\n        Some(node) => {\n            _postorder(node.borrow().left.clone(), result);\n            _postorder(node.borrow().right.clone(), result);\n            result.push(node.borrow().val);\n        },\n        None => { return; }\n    }\n}\n\n// 后序遍历（Iterating method using Stack）\npub fn postorder_traversal(root: Option<Rc<RefCell<TreeNode>>>) -> Vec<i32> {\n    let mut result = vec![];\n    if root.is_none() { return result; }\n\n    let mut stack1: Vec<Option<Rc<RefCell<TreeNode>>>> = Vec::new();\n    let mut stack2: Vec<Option<Rc<RefCell<TreeNode>>>> = Vec::new();\n    stack1.push(root);\n\n    while let Some(Some(node)) = stack1.pop() {\n        if node.borrow().left.is_some() {\n            stack1.push(node.borrow().left.clone());\n        }\n        if node.borrow().right.is_some() {\n            stack1.push(node.borrow().right.clone());\n        }\n        stack2.push(Some(node));\n    }\n\n    while let Some(Some(node)) = stack2.pop() {\n        result.push(node.borrow().val);\n    }\n\n    result\n}\n"
  },
  {
    "path": "rust/23_binary_tree/preorder_traversal.rs",
    "content": "//leetcode: https://leetcode.com/problems/binary-tree-preorder-traversal/\nuse super::util::tree::{TreeNode, to_tree};\n\n// 前序遍历（Recursive Approach）\npub fn preorder_traversal(root: Option<Rc<RefCell<TreeNode>>>) -> Vec<i32> {\n    let mut result: Vec<i32> = vec![];\n    if root.is_none() { return result; }\n\n    _preorder(root, &mut result);\n    result\n}\n\nfn _preorder(root: Option<Rc<RefCell<TreeNode>>>, result: &mut Vec<i32>) {\n    match root {\n        Some(node) => {\n            result.push(node.borrow().val);\n            _preorder(node.borrow().left.clone(), result);\n            _preorder(node.borrow().right.clone(), result);\n        },\n        None => { return; }\n    }\n}\n\n// 前序遍历（Iterating method using Stack）\npub fn inorder_traversal(root: Option<Rc<RefCell<TreeNode>>>) -> Vec<i32> {\n    let mut result = vec![];\n    if root.is_none() { return result; }\n\n    let mut stack: Vec<Rc<RefCell<TreeNode>>> = Vec::new();\n    let mut r = root.clone();\n\n    while r.is_some() || !stack.is_empty() {\n        while let Some(node) = r {\n            result.push(node.borrow().val);\n            stack.push(node.clone());\n            r = node.borrow().left.clone();\n        }\n        r = stack.pop();\n        if let Some(node) = r {\n            r = node.borrow().right.clone();\n        }\n    }\n    result\n}\n"
  },
  {
    "path": "rust/23_binary_tree/util/tree.rs",
    "content": "use std::rc::Rc;\nuse std::cell::RefCell;\n\n#[derive(Debug, PartialEq, Eq)]\npub struct TreeNode {\n    pub val: i32,\n    pub left: Option<Rc<RefCell<TreeNode>>>,\n    pub right: Option<Rc<RefCell<TreeNode>>>,\n}\n\nimpl TreeNode {\n    #[inline]\n    pub fn new(val: i32) -> Self {\n        TreeNode {\n            val,\n            left: None,\n            right: None\n        }\n    }\n}\n\npub fn to_tree(vec: Vec<Option<i32>>) -> Option<Rc<RefCell<TreeNode>>> {\n    use std::collections::VecDeque;\n    let head = Some(Rc::new(RefCell::new(TreeNode::new(vec[0].unwrap()))));\n    let mut queue = VecDeque::new();\n    queue.push_back(head.as_ref().unwrap().clone());\n\n    for children in vec[1..].chunks(2) {\n        let parent = queue.pop_front().unwrap();\n        if let Some(v) = children[0] {\n            parent.borrow_mut().left = Some(Rc::new(RefCell::new(TreeNode::new(v))));\n            queue.push_back(parent.borrow().left.as_ref().unwrap().clone());\n        }\n        if children.len() > 1 {\n            if let Some(v) = children[1] {\n                parent.borrow_mut().right = Some(Rc::new(RefCell::new(TreeNode::new(v))));\n                queue.push_back(parent.borrow().right.as_ref().unwrap().clone());\n            }\n        }\n    }\n    head\n}\n\n#[macro_export]\nmacro_rules! tree {\n    () => {\n        None\n    };\n    ($($e:expr),*) => {\n        {\n            let vec = vec![$(stringify!($e)), *];\n            let vec = vec.into_iter().map(|v| v.parse::<i32>().ok()).collect::<Vec<_>>();\n            to_tree(vec)\n        }\n    };\n    ($($e:expr,)*) => {(tree![$($e),*])};\n}\n"
  },
  {
    "path": "rust/24_binary_tree/insert_in_binary_tree.rs",
    "content": "// https://leetcode.com/problems/insert-into-a-binary-search-tree/\nuse super::util::tree::{TreeNode, to_tree};\n\npub fn insert_into_bst(root: Option<Rc<RefCell<TreeNode>>>, val: i32) -> Option<Rc<RefCell<TreeNode>>> {\n    insert(&root, val);\n    root\n}\nfn insert(node: &Option<Rc<RefCell<TreeNode>>>, val: i32) {\n    if let Some(n) = node {\n        let mut n = n.borrow_mut();\n        let target = if val > n.val { &mut n.right } else { &mut n.left };\n\n        if target.is_some() {\n            return insert(target, val);\n        }\n\n        *target = Some(Rc::new(RefCell::new(TreeNode::new(val))));\n    }\n}\n"
  },
  {
    "path": "rust/24_binary_tree/max_depth_in_binary_tree.rs",
    "content": "// https://leetcode.com/problems/maximum-depth-of-binary-tree/\nuse super::util::tree::{TreeNode, to_tree};\n\n// max depth BFS\npub fn max_depth(root: Option<Rc<RefCell<TreeNode>>>) -> i32 {\n    if root.is_none() { return 0; }\n\n    let mut depth = 0;\n    let mut deque: VecDeque<Option<Rc<RefCell<TreeNode>>>> = VecDeque::new();\n    deque.push_back(root);\n\n    while !deque.is_empty() {\n        let level_size = deque.len();\n        let mut added = false;\n        depth += 1;\n        for _i in 0..level_size {\n            added = true;\n            if let Some(Some(node)) = deque.pop_front() {\n                if node.borrow().left.is_some() { deque.push_back(node.borrow().left.clone());}\n                if node.borrow().right.is_some() { deque.push_back(node.borrow().right.clone());}\n            }\n        }\n        if !added { break; }\n    }\n    depth\n}\n\n// max depth DFS\npub fn max_depth(root: Option<Rc<RefCell<TreeNode>>>) -> i32 {\n    match root {\n        Some(node) => {\n            let left = Self::max_depth(node.borrow().left.clone());\n            let right = Self::max_depth(node.borrow().right.clone());\n            1 + left.max(right)\n        },\n        _ => 0,\n    }\n}\n"
  },
  {
    "path": "rust/24_binary_tree/search_in_binary_tree.rs",
    "content": "// https://leetcode.com/problems/search-in-a-binary-search-tree/\nuse super::util::tree::{TreeNode, to_tree};\n\n// Iterating method\npub fn search_bst(root: Option<Rc<RefCell<TreeNode>>>, val: i32) -> Option<Rc<RefCell<TreeNode>>> {\n    let mut r = root.clone();\n    while let Some(node) = r {\n        if node.borrow().val == val { return Some(node); }\n        if node.borrow().val > val {\n            r = node.borrow().left.clone();\n        } else {\n            r = node.borrow().right.clone();\n        }\n    }\n    None\n}\n\n// Recursive Approach\npub fn search_bst(root: Option<Rc<RefCell<TreeNode>>>, val: i32) -> Option<Rc<RefCell<TreeNode>>> {\n    if let Some(n) = &root {\n        if n.borrow().val > val { return Self::search_bst(n.borrow().left.clone(), val); }\n        if n.borrow().val < val { return Self::search_bst(n.borrow().right.clone(), val); }\n    }\n    root\n}\n"
  },
  {
    "path": "rust/24_binary_tree/util/tree.rs",
    "content": "use std::rc::Rc;\nuse std::cell::RefCell;\n\n#[derive(Debug, PartialEq, Eq)]\npub struct TreeNode {\n    pub val: i32,\n    pub left: Option<Rc<RefCell<TreeNode>>>,\n    pub right: Option<Rc<RefCell<TreeNode>>>,\n}\n\nimpl TreeNode {\n    #[inline]\n    pub fn new(val: i32) -> Self {\n        TreeNode {\n            val,\n            left: None,\n            right: None\n        }\n    }\n}\n\npub fn to_tree(vec: Vec<Option<i32>>) -> Option<Rc<RefCell<TreeNode>>> {\n    use std::collections::VecDeque;\n    let head = Some(Rc::new(RefCell::new(TreeNode::new(vec[0].unwrap()))));\n    let mut queue = VecDeque::new();\n    queue.push_back(head.as_ref().unwrap().clone());\n\n    for children in vec[1..].chunks(2) {\n        let parent = queue.pop_front().unwrap();\n        if let Some(v) = children[0] {\n            parent.borrow_mut().left = Some(Rc::new(RefCell::new(TreeNode::new(v))));\n            queue.push_back(parent.borrow().left.as_ref().unwrap().clone());\n        }\n        if children.len() > 1 {\n            if let Some(v) = children[1] {\n                parent.borrow_mut().right = Some(Rc::new(RefCell::new(TreeNode::new(v))));\n                queue.push_back(parent.borrow().right.as_ref().unwrap().clone());\n            }\n        }\n    }\n    head\n}\n\n#[macro_export]\nmacro_rules! tree {\n    () => {\n        None\n    };\n    ($($e:expr),*) => {\n        {\n            let vec = vec![$(stringify!($e)), *];\n            let vec = vec.into_iter().map(|v| v.parse::<i32>().ok()).collect::<Vec<_>>();\n            to_tree(vec)\n        }\n    };\n    ($($e:expr,)*) => {(tree![$($e),*])};\n}\n"
  },
  {
    "path": "rust/28_heap/build_heap.rs",
    "content": "// 建堆，自底向上堆化\npub fn build_heap_down_up(nums: &mut Vec<i32>) {\n    for i in 1..nums.len() {\n        heapify_down_up(nums, i);\n    }\n}\n\nfn heapify_down_up(nums: &mut Vec<i32>, idx: usize) {\n    let mut idx = idx;\n    let mut parent_idx = idx - 1 >> 1;\n    while nums[idx] > nums[parent_idx] {\n        swap(nums, idx, parent_idx);\n        idx = parent_idx;\n        if idx == 0 { break; }\n        parent_idx = idx - 1 >> 1;\n    }\n}\n\n// 建堆，自上向下堆化\npub fn build_heap_up_down(nums: &mut Vec<i32>) {\n    let nums_len = nums.len();\n    for i in (0..nums_len).rev() {\n        heapify_up_down(nums, i, nums_len);\n    }\n}\n\nfn heapify_up_down(nums: &mut Vec<i32>, idx: usize, nums_len: usize) {\n    let mut idx = idx;\n    loop {\n        let mut max_pos = idx;\n        if 2 * idx + 1 < nums_len && nums[idx] < nums[2 * idx + 1] { max_pos = 2 * idx + 1; }\n        if 2 * idx + 2 < nums_len && nums[max_pos] < nums[2 * idx + 2] { max_pos = 2 * idx + 2; }\n\n        if max_pos == idx { break; }\n        swap(nums, idx, max_pos);\n        idx = max_pos;\n    }\n}\n\nfn swap(nums: &mut Vec<i32>, idx: usize, parent_idx: usize) {\n    let tmp = nums[parent_idx];\n    nums[parent_idx] = nums[idx];\n    nums[idx] = tmp;\n}\n\nfn main() {\n    let mut nums = vec![1, 4, 5, 7, 8, 13, 16, 19, 20];\n    build_heap_down_up(&mut nums);\n    println!(\"{:?}\", nums);\n    let mut nums1 = vec![1, 4, 5, 7, 8, 13, 16, 19, 20];\n    build_heap_up_down(&mut nums1);\n    println!(\"{:?}\", nums1);\n}\n"
  },
  {
    "path": "rust/28_heap/heap.rs",
    "content": "#[derive(Debug)]\nstruct Heap {\n    data: Vec<Option<i32>>,\n    capacity: usize,\n    count: i32,\n}\n\nimpl Heap {\n    pub fn new(capacity: usize) -> Self {\n        Heap {\n            data: vec![None; capacity],\n            capacity: capacity,\n            count: 0\n        }\n    }\n\n    pub fn insert(&mut self, x: i32) -> bool {\n        // 堆已满\n        if self.capacity as i32 == self.count { return false; }\n\n        self.data[self.count as usize] = Some(x);\n        if self.count == 0 {\n            self.count += 1;\n            return true;\n        }\n\n        let mut idx = self.count as usize;\n        // 子节点大于父节点，子节点与父节点交换\n        // 自底向上堆化\n        let mut parent_idx = ((idx - 1) >> 1) as usize;\n        while parent_idx > 0 && self.data[idx] > self.data[parent_idx] {\n            self.swap(idx, parent_idx);\n            idx = parent_idx;\n            parent_idx = ((idx - 1) >> 1) as usize;\n        }\n        self.count += 1;\n        true\n    }\n\n    pub fn remove_max(&mut self) -> Option<i32> {\n        // 堆已空\n        if self.count == 0 { return None; }\n\n        let max_value = self.data[0];\n        // 将最后一个叶子节点移至堆顶\n        self.data[0] = self.data[(self.count - 1) as usize];\n        self.data[(self.count - 1) as usize] = None;\n\n        self.heapify();\n        self.count -= 1;\n        max_value\n    }\n\n    // 堆化，自上向下堆化\n    fn heapify(&mut self) {\n        let mut idx = 0usize;\n        loop {\n            let mut max_pos = idx;\n            if (2 * idx + 1) as i32 <= self.count && self.data[idx] < self.data[2 * idx + 1] { max_pos = 2 * idx + 1; }\n            if (2 * idx + 2) as i32 <= self.count && self.data[max_pos] < self.data[2 * idx + 2] { max_pos = 2 * idx + 2; }\n\n            if max_pos == idx { break; }\n            self.swap(idx, max_pos);\n            idx = max_pos;\n        }\n    }\n\n    fn swap(&mut self, idx: usize, parent_idx: usize) {\n        let tmp = self.data[parent_idx];\n        self.data[parent_idx] = self.data[idx];\n        self.data[idx] = tmp;\n    }\n}\n\nfn main() {\n    let mut h = Heap::new(16);\n    h.insert(33);\n    h.insert(27);\n    h.insert(21);\n    h.insert(16);\n    h.insert(13);\n    h.insert(15);\n    h.insert(9);\n    h.insert(5);\n    h.insert(6);\n    h.insert(7);\n    h.insert(8);\n    h.insert(1);\n    h.insert(2);\n    h.insert(22);\n    println!(\"{:?}\", h);\n    h.remove_max();\n    println!(\"{:?}\", h);\n    h.remove_max();\n    println!(\"{:?}\", h);\n    h.remove_max();\n    println!(\"{:?}\", h);\n}\n"
  },
  {
    "path": "rust/28_heap/sort_heap.rs",
    "content": "pub fn sort(nums: &mut Vec<i32>) {\n    build_heap(nums);\n    for i in (0..nums.len()).rev() {\n        swap(nums, 0, i);\n        heapify(nums, 0, i);\n    }\n}\n\nfn build_heap(nums: &mut Vec<i32>) {\n    let nums_len = nums.len();\n    for i in (0..nums_len).rev() {\n        heapify(nums, i, nums_len);\n    }\n}\n\nfn heapify(nums: &mut Vec<i32>, idx: usize, nums_len: usize) {\n    let mut idx = idx;\n    loop {\n        let mut max_pos = idx;\n        if 2 * idx + 1 < nums_len && nums[idx] < nums[2 * idx + 1] { max_pos = 2 * idx + 1; }\n        if 2 * idx + 2 < nums_len && nums[max_pos] < nums[2 * idx + 2] { max_pos = 2 * idx + 2; }\n\n        if max_pos == idx { break; }\n        swap(nums, idx, max_pos);\n        idx = max_pos;\n    }\n}\n\nfn swap(nums: &mut Vec<i32>, idx: usize, parent_idx: usize) {\n    let tmp = nums[parent_idx];\n    nums[parent_idx] = nums[idx];\n    nums[idx] = tmp;\n}\n\nfn main() {\n    let mut nums = vec![9, 6, 3, 1, 5];\n    sort(&mut nums);\n    println!(\"{:?}\", nums);\n}\n"
  },
  {
    "path": "rust/29_heap/get_median.rs",
    "content": "use std::collections::BinaryHeap;\n\n// 动态数组取位数\n// 对数组进行从小到大排序，数组下标为 n/2 的数据即为中位数\nfn get_median(nums: &mut Vec<i32>, x: i32) -> i32 {\n    let nums_len = nums.len();\n    let mid = nums_len >> 1;\n    let mut max_heap = BinaryHeap::new();\n    let mut min_heap = BinaryHeap::new();\n    nums.sort();\n\n    // 将数组前半部分数据放入大顶堆\n    // 数组后半部分数据入入小顶堆\n    for i in 0..nums_len {\n        if i < mid {\n            max_heap.push(nums[i]);\n        } else {\n            min_heap.push(-nums[i]);\n        }\n    }\n\n    nums.push(x);\n\n    // 校验待插入数据\n    // 若此数据小于大顶堆中顶数据，则将此数据插入大顶堆\n    // 若此数据大于大顶堆中顶数据，将此数据插入小顶堆\n    if x <= *max_heap.peek().unwrap() {\n        max_heap.push(x);\n    } else {\n        min_heap.push(-x);\n    }\n\n    // 平衡两个堆\n    // 大顶堆的数据个数一定小于等于小顶堆数据个数\n    // 小顶堆数据个数一定是等于或者比大顶堆数据个数多1个\n    // 不满足上述两个条件，即进行堆平衡\n    if max_heap.len() > min_heap.len() {\n        min_heap.push(-max_heap.pop().unwrap());\n    } else if min_heap.len() - max_heap.len() >= 2 {\n        max_heap.push(-min_heap.pop().unwrap());\n    }\n\n    -*min_heap.peek().unwrap()\n}\n\nfn main() {\n    let mut nums = vec![12, 45, 30, 77, 5, 6, 7, 8];\n    let m = get_median(&mut nums, 9);\n    println!(\"{:?}\", m); // 9\n    let n = get_median(&mut nums, 20);\n    println!(\"{:?}\", n); // 12\n    let h = get_median(&mut nums, 11);\n    println!(\"{:?}\", h); // 11\n    let i = get_median(&mut nums, 10);\n    println!(\"{:?}\", i); // 11\n}\n"
  },
  {
    "path": "rust/29_heap/get_top_k.rs",
    "content": "use std::collections::BinaryHeap;\n\n// 动态数组取 top k 元素\nfn get_top_k(nums: &mut Vec<i32>, k: i32, x: i32) -> Vec<i32> {\n    let nums_len = nums.len() as i32;\n    if nums_len <= k { return nums.clone(); }\n\n    let mut heap = BinaryHeap::new();\n\n    // 先将数组的前k个数据放入堆中\n    for _i in 0..k {\n        heap.push(-nums[k as usize]);\n    }\n\n    // 对数组中其它数据进行迭代，若数据大于堆顶元素，将堆顶元素移除，将此数据放入堆中\n    for i in k + 1..nums_len {\n        if -nums[i as usize] < *heap.peek().unwrap() {\n            heap.pop();\n            heap.push(-nums[i as usize]);\n        }\n    }\n\n    // 向数组中插入新数据\n    nums.push(x);\n\n    // 新插入的数据若大于堆顶元素，将堆顶元素移除，将此数据放入堆中\n    if -x < *heap.peek().unwrap() {\n        heap.pop();\n        heap.push(-x);\n    }\n\n    // let m: Vec<i32> = heap.iter().map(|h| h * -1).collect();\n    // m\n\n    heap.iter().map(|h| h * -1).collect::<Vec<i32>>()\n}\n\nfn main() {\n    let mut nums = vec![4, 5, 7, 9, 10, 6, 11];\n    let m = get_top_k(&mut nums, 3, 23);\n    println!(\"{:?}\", m);\n}\n"
  },
  {
    "path": "rust/29_heap/merge_sorted_array.rs",
    "content": "use std::collections::BinaryHeap;\n\nfn merge_sorted_array(nums1: &mut Vec<i32>, nums2: &mut Vec<i32>, nums3: &mut Vec<i32>) -> Vec<i32> {\n    let mut new_nums = vec![];\n    let mut heap = BinaryHeap::new();\n\n    // Rust heap 是大顶堆，将待入堆的数值取反后再入堆，堆顶即为最小值，即达到小顶堆效果\n    heap.push(-nums1[0]);\n    heap.push(-nums2[0]);\n    heap.push(-nums3[0]);\n\n    while !nums1.is_empty() || !nums2.is_empty() || !nums3.is_empty() {\n        if heap.is_empty() { break; }\n        let num = -heap.pop().unwrap();\n        new_nums.push(num);\n\n        if !nums1.is_empty() && num == nums1[0] {\n            nums1.remove(0);\n            if !nums1.is_empty() { heap.push(-nums1[0]); }\n        } else if !nums2.is_empty() && num == nums2[0] {\n            nums2.remove(0);\n            if !nums2.is_empty() { heap.push(-nums2[0]); }\n        } else if !nums3.is_empty() && num == nums2[0] {\n            nums3.remove(0);\n            if !nums3.is_empty() { heap.push(-nums3[0]); }\n        }\n    }\n    new_nums\n}\n\nfn main() {\n    let mut nums1 = vec![4, 5, 20, 90, 95, 100];\n    let mut nums2 = vec![1, 6, 7, 8, 11, 23, 67, 89];\n    let mut nums3 = vec![2, 5, 9, 30, 45];\n    let new_nums = merge_sorted_array(&mut nums1, &mut nums2, &mut nums3);\n\n    println!(\"{:?}\", new_nums);\n}\n"
  },
  {
    "path": "rust/31_graph/graph_search.rs",
    "content": "use std::collections::LinkedList;\nuse std::collections::VecDeque;\n\n// 无向图\n#[derive(Debug)]\nstruct Graph {\n    v:   i32,\n    linked_vec: Vec<LinkedList<i32>>, // 邻接表\n}\n\nimpl Graph {\n    fn new(v: i32) -> Self {\n        Graph {\n            v: v,\n            linked_vec: vec![LinkedList::new(); v as usize],\n        }\n    }\n\n    // 无向图的每条边存两次\n    fn add_edge(&mut self, s: i32, t: i32) {\n        self.linked_vec[s as usize].push_back(t);\n        self.linked_vec[t as usize].push_back(s);\n    }\n\n    fn bfs(&self, s: i32, t: i32) {\n        if s == t { return; }\n\n        let mut prev    = vec![-1; self.v as usize];\n        let mut visited = vec![false; self.v as usize];\n        let mut queue   = VecDeque::new();\n\n        visited[s as usize] = true;\n        queue.push_back(s);\n\n        while !queue.is_empty() {\n            let w = queue.pop_front().unwrap();\n            for item in self.linked_vec[w as usize].iter() {\n                if visited[*item as usize] { continue; }\n                prev[*item as usize] = w;\n                if *item == t {\n                    self.draw(&prev, s, t);\n                    return;\n                }\n                visited[*item as usize] = true;\n                queue.push_back(*item);\n            }\n        }\n    }\n\n    fn dfs(&self, s: i32, t: i32) {\n        let mut found   = false;\n        let mut prev    = vec![-1; self.v as usize];\n        let mut visited = vec![false; self.v as usize];\n\n        self.recur_dfs(s, t, &mut visited, &mut prev, &mut found);\n        self.draw(&prev, s, t);\n    }\n\n    fn recur_dfs(&self,\n                 s:       i32,\n                 t:       i32,\n                 visited: &mut Vec<bool>,\n                 prev:    &mut Vec<i32>,\n                 found:   &mut bool) {\n        if *found == true { return; }\n        visited[s as usize] = true;\n        if s == t {\n            *found = true;\n            return;\n        }\n        for item in self.linked_vec[s as usize].iter() {\n            if visited[*item as usize] { continue; }\n            prev[*item as usize] = s;\n            self.recur_dfs(*item, t, visited, prev, found);\n        }\n    }\n\n    // 递归打印路径\n    fn draw(&self, prev: &Vec<i32>, s: i32, t: i32) {\n        if prev[t as usize] != -1 && s != t {\n            self.draw(prev, s, prev[t as usize]);\n        }\n\n        println!(\"{} ->\", t);\n    }\n}\n\nfn main() {\n    let mut graph = Graph::new(8);\n    graph.add_edge(0, 1);\n    graph.add_edge(0, 3);\n    graph.add_edge(1, 2);\n    graph.add_edge(1, 4);\n    graph.add_edge(2, 5);\n    graph.add_edge(3, 4);\n    graph.add_edge(4, 5);\n    graph.add_edge(4, 6);\n    graph.add_edge(5, 7);\n    graph.add_edge(6, 7);\n\n    // Graph { v: 8, linked_vec: [[1, 3], [0, 2, 4], [1, 5], [0, 4], [1, 3, 5, 6], [2, 4, 7], [4, 7], [5, 6]] }\n    println!(\"{:?}\", graph);\n    graph.bfs(0, 7);\n    println!(\"bfs=============\");\n    graph.bfs(1, 3);\n    println!(\"bfs=============\");\n\n    graph.dfs(0, 7);\n    println!(\"dfs=============\");\n    graph.dfs(1, 3);\n    println!(\"dfs=============\");\n}\n"
  },
  {
    "path": "rust/32_string/bf_rk.rs",
    "content": "use std::collections::HashMap;\n\nfn bf(primary: &str, pattern: &str) -> i32 {\n    if primary.is_empty() || pattern.is_empty() || primary.len() < pattern.len() { return -1; }\n\n    let primary_chars: Vec<char> = primary.chars().collect();\n    let pattern_chars: Vec<char> = pattern.chars().collect();\n    for i in 0..(primary.len() - pattern.len() + 1) {\n        if pattern_chars == primary_chars[i..i + pattern.len()].to_vec() {\n            return i as i32;\n        }\n    }\n    -1\n}\n\n// 通过哈希算法对主串中的 n-m+1 个子串分别求哈希值，\n// 逐个与模式串的哈希值比较大小。如果某个子串的哈希值与模式串相等，那就说明对应的子串和模式串匹配\nfn rk(primary: &str, pattern: &str) -> i32 {\n    if primary.is_empty() || pattern.is_empty() || primary.len() < pattern.len() { return -1; }\n\n    let primary_chars: Vec<char> = primary.chars().collect();\n    let pattern_chars: Vec<char> = pattern.chars().collect();\n    let base: i128 = 26;\n    let m = pattern.len();\n    let n = primary.len();\n    let mut pow_vec = vec![];\n    let mut hash = HashMap::new();\n\n    // 存储 26 的 n 次方到数组中，方便后面调用\n    for i in 0..m {\n        pow_vec.push(base.pow(i as u32));\n    }\n\n    // 计算子串的 hash 值\n    let mut p_value = 0;\n    for i in 0..m {\n        p_value += (pattern_chars[i] as i128 - 'a' as i128) * pow_vec[m-1-i];\n    }\n\n    // 计算主串的 n-m+1 个子串的 hash 值\n    for i in 0..(n - m + 1) {\n        // 计算主串中 index 为 0 的子串的 hash 值\n        let mut value = 0;\n        if i == 0 {\n            for i in 0..m {\n                value += (primary_chars[i] as i128 - 'a' as i128) * pow_vec[m-1-i];\n            }\n        } else {\n            // 计算 index 为 i 的子串的 hash 值\n            // 计算公式: hash[i] = (hash[i-1] - 26^(m-1) * (primary_chars[i-1] - 'a')) * 26 + (26^0 * (primary_chars[i+m-1] - 'a'))\n            value = (hash[&((i-1) as i32)] - base.pow((m-1) as u32) * (primary_chars[i-1] as i128 - 'a' as i128)) * base + ((primary_chars[i+m-1]) as i128 - 'a' as i128);\n        }\n\n        // hash 值相等，比较两个串内容是否相等，避免 hash 碰撞\n        if value == p_value && pattern_chars == primary_chars[i..i+m].to_vec() {\n            return i as i32;\n        }\n\n        hash.insert(i as i32, value);\n    }\n\n    -1\n}\n\nfn main() {\n    let primary = \"thequickbrownfoxjumpsoverthelazydog\";\n    let pattern = \"jump\";\n    let result = bf(primary, pattern);\n    println!(\"{}\", result); // 16\n\n    let result2 = rk(primary, pattern);\n    println!(\"{:?}\", result2); // 16\n}\n"
  },
  {
    "path": "rust/33_string/bm.rs",
    "content": "// 生成模式串散列表\nfn generate_bc(pattern: &str) -> Vec<i32> {\n    let mut bc: Vec<i32> = vec![-1; 256];\n    let pattern_chars: Vec<char> = pattern.chars().collect();\n    for (index, item) in pattern_chars.iter().enumerate() {\n        bc[(*item as u8) as usize] = index as i32;\n    }\n    bc\n}\n\n// 计算 suffix 数组与 prefix 数组\nfn generate_gs(pattern: &str) -> (Vec<i32>, Vec<bool>) {\n    let m = pattern.len();\n    let mut suffix: Vec<i32> = vec![-1; m];\n    let mut prefix: Vec<bool> = vec![false; m];\n    let pattern_chars: Vec<char> = pattern.chars().collect();\n\n    for i in 0..m-1 {\n        let mut j = i as i32;\n        let mut k = 0;\n\n        while j >= 0 && pattern_chars[j as usize] == pattern_chars[m-k-1] {\n            j -= 1;\n            k += 1;\n            suffix[k] = j + 1;\n        }\n\n        if j == -1 { prefix[k] = true; }\n    }\n\n    (suffix, prefix)\n}\n\nfn move_by_gs(bad_char_start_index: usize, pattern_len: usize, suffix: &Vec<i32>, prefix: &Vec<bool>) -> i32 {\n    // 好后缀长度\n    let k = pattern_len - bad_char_start_index - 1;\n    // 完全匹配\n    if suffix[k] != -1 { return (bad_char_start_index + 1 - suffix[k] as usize) as i32; }\n\n    for i in pattern_len+2..bad_char_start_index {\n        if prefix[pattern_len-i] { return i as i32; }\n    }\n    // 没有匹配\n    pattern_len as i32\n}\n\nfn bm_search(primary: &str, pattern: &str) -> i32 {\n    if primary.is_empty() || pattern.is_empty() || pattern.len() > primary.len() { return 0; }\n\n    let primary_chars: Vec<char> = primary.chars().collect();\n    let pattern_chars: Vec<char> = pattern.chars().collect();\n    let bc = generate_bc(pattern);\n    let (suffix, prefix) = generate_gs(pattern);\n    let n = primary.len();\n    let m = pattern.len();\n    let mut i = 0;\n\n    while i <= n - m {\n        let mut j = (m-1) as i32;\n        while j >=0 {\n            if primary_chars[i+j as usize] != pattern_chars[j as usize] { break; }\n            j -= 1\n        }\n        if j < 0 { return i as i32; }\n        let step_for_bc = j as i32 - bc[(primary_chars[i+j as usize] as u8) as usize];\n        let mut step_for_gs = 0;\n        if j < (m-1) as i32 {\n            step_for_gs = move_by_gs(j as usize, m, &suffix, &prefix);\n        }\n        i = (i as i32 + step_for_bc.max(step_for_gs)) as usize;\n    }\n    -1\n}\n\nfn main() {\n    let primary = \"abcacabcbcabcabc\";\n    let pattern = \"cabcab\";\n    let m = bm_search(primary, pattern);\n    println!(\"{:?}\", m);\n}\n"
  },
  {
    "path": "rust/34_string/kmp.rs",
    "content": "fn kmp_search(primary: &str, pattern: &str) -> Vec<i32> {\n    if primary.is_empty() || pattern.is_empty() || pattern.len() > primary.len() { return vec![0]; }\n\n    let primary_chars: Vec<char> = primary.chars().collect();\n    let pattern_chars: Vec<char> = pattern.chars().collect();\n    let max_match_lengths = get_failure_function(pattern);\n    let mut count = 0;\n    let m = pattern.len();\n    let mut positions = vec![];\n\n    for i in 0..primary.len() {\n        while count > 0 && pattern_chars[count as usize] != primary_chars[i] {\n            count = max_match_lengths[(count-1) as usize];\n        }\n\n        if pattern_chars[count as usize] == primary_chars[i] {\n            count += 1;\n        }\n\n        if count as usize == m {\n            positions.push((i - m + 1) as i32);\n            count = max_match_lengths[(count-1) as usize];\n        }\n    }\n\n    positions\n}\n\nfn get_failure_function(pattern: &str) -> Vec<i32> {\n    let m = pattern.len();\n    let mut max_match_lengths: Vec<i32> = vec![0; m];\n    let mut max_length: i32 = 0;\n    let pattern_chars: Vec<char> = pattern.chars().collect();\n\n    for i in 1..m {\n        while max_length > 0 && pattern_chars[max_length as usize] != pattern_chars[i] {\n            max_length = max_match_lengths[(max_length-1) as usize];\n        }\n\n        if pattern_chars[i] == pattern_chars[max_length as usize] {\n            max_length += 1;\n        }\n\n        max_match_lengths[i] = max_length;\n    }\n\n    max_match_lengths\n}\n\nfn main() {\n    let primary1 = \"abbaabbaaba\";\n    let pattern1 = \"abbaaba\";\n    println!(\"{:?}\", kmp_search(primary1, pattern1)); // 4\n\n    let primary = \"abc abcdab abcdabcdabde\";\n    let pattern = \"bcdabd\";\n    println!(\"{:?}\", kmp_search(primary, pattern)); // 16\n}\n"
  },
  {
    "path": "rust/35_trie/trie.rs",
    "content": "// [leetcode 208](https://leetcode.com/problems/implement-trie-prefix-tree/)\n\n#[derive(Default, Debug)]\nstruct Trie {\n    is_ending: bool,\n    nodes:     [Option<Box<Trie>>; 26],\n}\n\nimpl Trie {\n    fn new() -> Self {\n        Default::default()\n    }\n\n    fn insert(&mut self, word: &str) {\n        let mut curr = self;\n        for i in word.chars().map(|c| (c as usize - 'a' as usize) as usize) {\n            curr = curr.nodes[i].get_or_insert_with(|| Box::new(Trie::new()));\n        }\n        curr.is_ending = true;\n    }\n\n    fn find(&self, word: &str) -> bool {\n        let mut curr = self;\n        for i in word.chars().map(|c| (c as usize - 'a' as usize) as usize) {\n            match curr.nodes[i].as_ref() {\n                Some(node) => { curr = node; },\n                None => { return false; },\n            }\n        }\n        curr.is_ending\n    }\n}\n\nfn main() {\n    let mut m = Trie::new();\n    m.insert(\"hello\");\n    m.insert(\"she\");\n    println!(\"{:?}\", m);\n    let r = m.search(\"hello\");\n    println!(\"{}\", r); // true\n}\n"
  },
  {
    "path": "rust/38_divide_and_conquer/merge_sort_count.rs",
    "content": "fn merge_sort_count(mut nums: Vec<i32>) -> i32 {\n    let mut count = 0;\n    let n = nums.len() - 1;\n    merge_sort(&mut nums, 0, n, &mut count);\n    count\n}\n\nfn merge_sort(nums: &mut Vec<i32>, low: usize, high: usize, count: &mut i32) {\n    if low >= high { return; }\n\n    let middle = low + ((high - low) >> 1);\n    merge_sort(nums, low, middle, count);\n    merge_sort(nums, middle+1, high, count);\n\n    merge(nums, low, middle, high, count);\n}\n\nfn merge(nums:   &mut Vec<i32>,\n         low:    usize,\n         middle: usize,\n         high:   usize,\n         count:  &mut i32) {\n    let mut i = low;\n    let mut j = middle + 1;\n    let mut tmp = vec![];\n\n    while i <= middle && j <= high {\n        if nums[i] <= nums[j] {\n            tmp.push(nums[i]);\n            i += 1;\n        } else {\n            // count += &(middle - i + 1);\n            *count += (middle - i + 1) as i32;\n            tmp.push(nums[j]);\n            j += 1;\n        }\n    }\n\n    while i <= middle {\n        tmp.push(nums[i]);\n        i += 1;\n    }\n\n    while j <= high {\n        tmp.push(nums[j]);\n        j += 1;\n    }\n\n   for i in 0..=(high-low) {\n       nums[low+1] = tmp[i];\n   }\n}\nfn main() {\n    let nums = vec![1, 5, 6, 2, 3, 4];\n    let m = merge_sort_count(nums);\n    println!(\"{:?}\", m);\n}\n"
  },
  {
    "path": "rust/39_back_tracking/bag.rs",
    "content": "use std::collections::HashMap;\n\nfn solve_bag(items: Vec<i32>, capacity: i32) -> HashMap<i32, Vec<i32>> {\n    let pick_idx       = 0;\n    let current_weight = 0;\n    let mut picks      = vec![-1; items.len()];\n    let mut max_values = vec![-1; items.len()];\n    let mut result     = HashMap::new();\n\n    bag(pick_idx,\n        current_weight,\n        &items,\n        capacity,\n        &mut picks,\n        &mut max_values,\n        &mut result,);\n\n    result\n}\n\nfn bag(pick_idx:       i32,\n       current_weight: i32,\n       items:          &Vec<i32>,\n       capacity:       i32,\n       picks:          &mut Vec<i32>,\n       max_values:     &mut Vec<i32>,\n       result:         &mut HashMap<i32, Vec<i32>>) {\n   if current_weight == capacity || pick_idx == items.len() as i32 {\n       if get_value(items, &picks) > get_value(items, max_values) {\n           for i in 0..picks.len() {\n            max_values[i] = picks[i];\n           }\n           result.insert(get_value(items, max_values), picks.to_vec());\n       }\n       return;\n   }\n\n   // 选\n   if current_weight + items[pick_idx as usize] <= capacity {\n       picks[pick_idx as usize] = 1;\n       bag(pick_idx + 1,\n           current_weight + items[pick_idx as usize],\n           items,\n           capacity,\n           picks,\n           max_values,\n           result);\n   }\n\n   // 不选\n   picks[pick_idx as usize] = 0;\n   bag(pick_idx + 1,\n       current_weight,\n       items,\n       capacity,\n       picks,\n       max_values,\n       result);\n\n}\n\nfn get_value(items: &Vec<i32>, picks: &Vec<i32>) -> i32 {\n    let mut result = 0;\n    for i in 0..picks.len() {\n        if picks[i] == 1 { result += items[i]; }\n    }\n    result\n}\n\nfn main() {\n    let items = vec![2, 2, 4, 6, 3];\n    let m = solve_bag(items, 10);\n    println!(\"{:?}\", m); // {10: [1, 1, 0, 1, 0], 8: [1, 1, 1, 0, 0]}\n}\n"
  },
  {
    "path": "rust/39_back_tracking/bag_exec.rs",
    "content": "use std::collections::HashMap;\n\nfn solve_bag(items: Vec<(i32, i32)>, capacity: i32) -> HashMap<i32, Vec<i32>> {\n    let pick_idx       = 0;\n    let current_weight = 0;\n    let mut picks      = vec![-1; items.len()];\n    let mut max_values = vec![-1; items.len()];\n    let mut result     = HashMap::new();\n\n    bag(pick_idx,\n        current_weight,\n        &items,\n        capacity,\n        &mut picks,\n        &mut max_values,\n        &mut result,);\n\n    result\n}\n\nfn bag(pick_idx:       i32,\n       current_weight: i32,\n       items:          &Vec<(i32, i32)>,\n       capacity:       i32,\n       picks:          &mut Vec<i32>,\n       max_values:     &mut Vec<i32>,\n       result:         &mut HashMap<i32, Vec<i32>>) {\n   if current_weight == capacity || pick_idx == items.len() as i32 {\n       if get_value(items, &picks) > get_value(items, max_values) {\n           for i in 0..picks.len() {\n            max_values[i] = picks[i];\n           }\n           result.insert(get_value(items, max_values), picks.to_vec());\n       }\n       return;\n   }\n\n   // 选\n   let item_weight = items[pick_idx as usize].0;\n   if current_weight + item_weight <= capacity {\n       picks[pick_idx as usize] = 1;\n       bag(pick_idx + 1,\n           current_weight + item_weight,\n           items,\n           capacity,\n           picks,\n           max_values,\n           result);\n   }\n\n   // 不选\n   picks[pick_idx as usize] = 0;\n   bag(pick_idx + 1,\n       current_weight,\n       items,\n       capacity,\n       picks,\n       max_values,\n       result);\n\n}\n\nfn get_value(items: &Vec<(i32, i32)>, picks: &Vec<i32>) -> i32 {\n    let mut result = 0;\n    for i in 0..picks.len() {\n        if picks[i] == 1 { result += items[i].1; }\n    }\n    result\n}\n\nfn main() {\n    // [(weight, value)...]\n    let items = vec![(3, 5), (2, 2), (1, 4), (1, 2), (4, 10)];\n    let m = solve_bag(items, 10);\n    println!(\"{:?}\", m); // {13: [1, 1, 1, 1, 0], 21: [1, 1, 1, 0, 1]}\n}\n"
  },
  {
    "path": "rust/39_back_tracking/n_queens.rs",
    "content": "# leetcode 51 [N-queens])https://leetcode.com/problems/n-queens/)\n\npub fn solve_n_queens(n: i32) -> Vec<Vec<String>> {\n    let mut board = vec![vec!['.'; n as usize]; n as usize];\n    let mut solution = vec![];\n    schedule_queens(&mut board, &mut solution, n as usize, 0);\n    solution\n}\n\nfn schedule_queens(board: &mut Vec<Vec<char>>, solution: &mut Vec<Vec<String>>, len: usize, row: usize) {\n    for col in 0..len {\n        if !collision(&board, len, row, col) {\n            board[row][col] = 'Q';\n            if row == len - 1 {\n                solution.push(board.iter().map(|vec| vec.iter().collect()).collect());\n            } else {\n                schedule_queens(board, solution, len, row+1);\n            }\n            board[row][col] = '.';\n        }\n    }\n}\n\n#[inline(always)]\nfn collision(board: &Vec<Vec<char>>, len: usize, row: usize, col: usize) -> bool {\n    for i in 0..row {\n        if board[i][col] == 'Q' { return true }\n    }\n    let (mut i, mut j) = (row as i32 - 1, col as i32 - 1);\n    while i >= 0 && j >= 0 {\n        if board[i as usize][j as usize] == 'Q' { return true }\n        i -= 1; j -= 1;\n    }\n    let (mut i, mut j) = (row as i32 - 1, col as i32 + 1);\n    while i >= 0 && j < len as i32 {\n        if board[i as usize][j as usize] == 'Q' { return true}\n        i -= 1; j += 1;\n    }\n    false\n}\n\nfn main() {\n    let m = solve_n_queens(8);\n    println!(\"{:?}\", m);\n}\n"
  },
  {
    "path": "rust/39_back_tracking/regex.rs",
    "content": "fn regex_match(text: &str, regex: &str) -> bool {\n    let mut matched = false;\n    let text_chars = text.chars().collect();\n    let regex_chars = regex.chars().collect();\n    rematch(&text_chars, &regex_chars, 0, 0, &mut matched);\n    matched\n}\n\nfn rematch(text_chars:  &Vec<char>,\n           regex_chars: &Vec<char>,\n           t_idx:       usize,\n           r_idx:       usize,\n           matched:     &mut bool) {\n    // 已经匹配好了，直接返回；\n    if *matched { return; }\n\n    // 正则串已经全部匹配\n    if r_idx >= regex_chars.len() {\n        *matched = true;\n        return;\n    }\n\n    // 主串已经匹配完，但是正则串还没有全部进行匹配\n    if t_idx >= text_chars.len() && r_idx < regex_chars.len() {\n        *matched = false;\n        return;\n    }\n\n    // * 匹配1个或多个字符，递归进行匹配\n    if regex_chars[r_idx] == '*' {\n        for i in t_idx..text_chars.len() {\n            rematch(text_chars, regex_chars, i+1, t_idx+1, matched);\n        }\n    // ? 匹配0个或1个字符，分两种情况进行匹配\n    } else if regex_chars[r_idx] == '?' {\n        rematch(text_chars, regex_chars, t_idx+1, r_idx+1, matched);\n        rematch(text_chars, regex_chars, t_idx, r_idx+1, matched);\n    // 非特殊字符，精确匹配\n    } else if regex_chars[r_idx] == text_chars[t_idx] {\n        rematch(text_chars, regex_chars, t_idx+1, r_idx+1, matched);\n    }\n}\n\nfn main() {\n    let text = \"abcdsadfkjlekjoiwjiojieeecd\";\n    let regex = \"ab*eee?d\";\n    let m = regex_match(text, regex);\n    println!(\"{}\", m);\n}\n"
  },
  {
    "path": "rust/40_dynamic_programming/bag.rs",
    "content": "fn knapsack(items: Vec<i32>, capacity: i32) -> i32 {\n    let mut states = vec![vec![false; (capacity + 1) as usize]; items.len()];\n    let mut result = vec![];\n    states[0][0] = true;\n    if items[0] <= capacity { states[0][items[0] as usize] = true; }\n    for i in 1..items.len() {\n        for j in 0..=capacity as usize {\n            if states[i-1][j] { states[i][j] = true; }\n        }\n\n        for j in 0..=(capacity - items[i]) as usize {\n            if states[i-1][j] { states[i][j + items[i] as usize] = true; }\n        }\n    }\n\n    let mut idx = capacity;\n    while idx <= capacity {\n       if states[items.len()-1][idx as usize] { break; }\n       idx += 1;\n    }\n    for i in (1..items.len()).rev() {\n        if idx - items[i] >= 0 && states[i-1][(idx-items[i]) as usize] {\n            idx -= items[i];\n            result.push(items[i]);\n        }\n    }\n\n    if idx != 0 { result.push(items[0]); }\n    println!(\"{:?}\", result);\n\n    for i in (0..=capacity as usize).rev() {\n        if states[items.len()-1][i] { return i as i32; }\n    }\n    0\n}\n\nfn main() {\n    let items = vec![2, 2, 4, 6, 3];\n    let capacity = 9;\n    let m = knapsack(items, capacity);\n    println!(\"{}\", m);\n}\n"
  },
  {
    "path": "rust/40_dynamic_programming/knapsack.rs",
    "content": "fn knapsack(items: Vec<(i32, i32)>, capacity: i32) -> i32 {\n    let mut states = vec![-1; (capacity + 1) as usize];\n    let mut result = vec![];\n    states[0] = 0;\n    if items[0].0 <= capacity { states[items[0].0 as usize] = items[0].1; }\n    for i in 1..items.len() {\n        for j in 0..=(capacity - items[i].0) as usize {\n            if states[j] >= 0 {\n                let value = states[j] + items[i].1;\n                if value > states[j+items[i].0 as usize] {\n                    states[j+items[i].0 as usize] = value;\n                    result.push(items[i].0);\n                }\n            }\n        }\n    }\n\n    let mut max_value = -1;\n    for i in (0..=capacity as usize).rev() {\n        if states[i] >= max_value {\n            max_value = states[i];\n        }\n    }\n    max_value\n}\n\nfn main() {\n    let items = vec![(2, 3), (2, 4), (4, 8), (6, 9), (3, 6)];\n    let capacity = 9;\n    let m = knapsack(items, capacity);\n    println!(\"{}\", m);\n}\n"
  },
  {
    "path": "rust/40_dynamic_programming/triangle.rs",
    "content": "# leetcode [minimum_total](https://leetcode.com/problems/triangle/)\n\npub fn minimum_total(mut triangle: Vec<Vec<i32>>) -> i32 {\n    if triangle.len() == 0 { return 0; }\n\n    for i in (0..triangle.len() - 1).rev() {\n        for j in 0..triangle[i].len() {\n            triangle[i][j] = triangle[i+1][j].min(triangle[i+1][j+1]) + triangle[i][j];\n        }\n    }\n    triangle[0][0]\n}\n\nfn main() {\n    let triangle = vec![\n        vec![2],\n        vec![3, 4],\n        vec![6, 5, 7],\n        vec![4, 1, 8, 3],\n    ];\n\n    println!(\"{:?}\", minimum_total(triangle));\n}\n"
  },
  {
    "path": "rust/41_dynamic_programming/coin_change.rs",
    "content": "fn coin_change(coins: Vec<i32>, amount: i32) -> i32 {\n    let mut dp = vec![amount+1; (amount+1) as usize];\n    dp[0] = 0;\n    for i in 1..=amount as usize {\n        for &coin in coins.iter() {\n            if i as i32 >= coin {\n                dp[i] = dp[i].min(dp[i-coin as usize] + 1);\n            }\n        }\n    }\n\n    let last = *dp.last().unwrap();\n    if last > amount { -1 } else { last }\n}\nfn main() {\n    let coins = vec![1, 3, 5];\n\n   let m = coin_change(coins, 9);\n   println!(\"{}\", m); // 3\n}\n"
  },
  {
    "path": "rust/41_dynamic_programming/min_dis_path.rs",
    "content": "fn min_dis_path(matrix: Vec<Vec<i32>>) -> i32 {\n    let m_len = matrix.len();\n    if m_len == 0 { return 0; }\n\n    let mut states = vec![vec![0; m_len]; m_len];\n    let mut sum = 0;\n    // 初始化第一行数据\n    for j in 0..m_len {\n        sum += matrix[0][j];\n        states[0][j] = sum;\n    }\n\n    sum = 0;\n    // 初始化第一列数据\n    for i in 0..m_len {\n        sum += matrix[i][0];\n        states[i][0] = sum;\n    }\n\n    for i in 1..m_len {\n        for j in 1..m_len {\n            states[i][j] = matrix[i][j] + states[i-1][j].min(states[i][j-1])\n        }\n    }\n\n    states[m_len-1][m_len-1]\n}\nfn main() {\n    let matrix = vec![\n        vec![1, 3, 5, 9],\n        vec![2, 1, 3, 4],\n        vec![5, 2, 6, 7],\n        vec![6, 8, 4, 3],\n    ];\n\n   let m = min_dis_path(matrix);\n   println!(\"{}\", m);\n}\n"
  },
  {
    "path": "rust/42_dynamic_programming/edit_distance.rs",
    "content": "// leetcode 72 [edit_distance](https://leetcode.com/problems/edit-distance/)\nfn edit_distance(word1: &str, word2: &str) -> i32 {\n    let word1_chars: Vec<char> = word1.chars().collect();\n    let word2_chars: Vec<char> = word2.chars().collect();\n    let m = word1.len();\n    let n = word2.len();\n    let mut dp = vec![vec![0; m+1]; n+1];\n\n    // 初始化第一行\n    for i in 0..=m { dp[i][0] = i; }\n    // 初始化第一列\n    for j in 0..=n { dp[0][j] = j; }\n\n    for i in 1..=m {\n        for j in 1..=n {\n            let mut step = 0;\n            if word1_chars[i-1] != word2_chars[j-1] { step = 1; }\n            dp[i][j] = (dp[i][j-1] + 1).min(dp[i-1][j] + 1).min(dp[i-1][j-1] + step);\n        }\n    }\n\n    dp[m][n] as i32\n}\nfn main() {\n    let word1 = \"mitcmu\";\n    let word2 = \"mtacnu\";\n    let m = edit_distance(word1, word2);\n    println!(\"{:?}\", m);\n}\n"
  },
  {
    "path": "rust/42_dynamic_programming/longest_increasing_subsequence.rs",
    "content": "// leetcode 300 [longest_increasing_subsequence](https://leetcode.com/problems/longest-increasing-subsequence)\nfn longest_increasing_subsequence(nums: Vec<i32>) -> i32 {\n    if nums.len() <= 1 { return nums.len() as i32; }\n\n    let mut dp = vec![1; nums.len()];\n    let mut max_list = 1;\n\n    for i in 0..nums.len() {\n        for j in 0..i {\n            if nums[i] > nums[j] {\n                dp[i] = dp[i].max(dp[j]+1);\n            }\n        }\n        max_list = max_list.max(dp[i]);\n    }\n    max_list\n}\nfn main() {\n    let nums = vec![2, 9, 3, 6, 5, 1, 7];\n    let m = longest_increasing_subsequence(nums);\n    println!(\"{:?}\", m);\n}\n"
  },
  {
    "path": "scala/.gitignore",
    "content": "target/\n"
  },
  {
    "path": "scala/build.sbt",
    "content": "lazy val root = (project in file(\".\"))\n  .settings(\n    name := \"algo-scala\",\n    version := \"1.0\",\n    scalaVersion := \"2.12.8\",\n    libraryDependencies += \"org.scalatest\" %% \"scalatest\" % \"3.0.5\" % \"test\"\n  )\n"
  },
  {
    "path": "scala/project/build.properties",
    "content": "sbt.version=1.2.1\n"
  },
  {
    "path": "scala/src/main/scala/ch05_array/ArrayDemo.scala",
    "content": "package ch05_array\n\nclass ArrayDemo(capacity: Int) {\n\n  var data: Array[Char] = new Array[Char](capacity)\n  var length: Int = 0\n\n  def find(index: Int): Char = {\n    if (index < 0 || index > length) {\n      return 0.toChar\n    }\n    data(index)\n  }\n\n  def insert(index: Int, value: Char): Boolean = {\n    if (length == capacity) {\n      return false\n    }\n\n    if (index < 0 || index >= capacity) {\n      return false\n    }\n\n    for (i <- length until index by -1) {\n      data(i) = data(i - 1)\n    }\n    data(index) = value\n    length += 1\n\n    true\n  }\n\n  def delete(index: Int): Char = {\n    if (length == 0) {\n      throw new IllegalStateException(\"array is empty\")\n    }\n    if (index >= length) {\n      throw new IllegalStateException(\"index out of range, current data length is \" + length)\n    }\n    val result = data(index)\n    for (i <- index until length-1) {\n      data(i) = data(i + 1)\n    }\n\n    length -= 1\n    result\n  }\n\n  def print: String = {\n    data.subSequence(0, length).toString\n  }\n\n}\n"
  },
  {
    "path": "scala/src/main/scala/ch06_linkedlist/SinglyLinkedList.scala",
    "content": "package ch06_linkedlist\n\nimport scala.util.control.Breaks._\n\n// the model class for the linked list\nclass Node(var data: Int, var next: Option[Node])\n\nclass SinglyLinkedList(var headOpt: Option[Node]) {\n\n  //define constructor without param\n  def this() = this(None)\n\n\n  def findByValue(value: Int): Option[Node] = {\n    headOpt.flatMap(head => {\n      var node = head\n      while (!node.data.equals(value) && node.next.isDefined) {\n        node = node.next.get\n      }\n\n      if (node.data.equals(value)) {\n        return Some(node)\n      } else {\n        //arrive the end of the chain\n        return None\n      }\n    })\n  }\n\n  def insertToHead(value: Int): Unit = {\n    val newNode = new Node(value, None)\n    insertToHead(newNode)\n  }\n\n  def insertToHead(newNode: Node): Unit = {\n    headOpt match {\n      case None =>\n        //it's an empty linked list, make new node as head\n        headOpt = Some(newNode)\n      case Some(head) =>\n        newNode.next = Some(head)\n        headOpt = Some(newNode)\n    }\n  }\n\n  def insertTail(value: Int): Unit = {\n    val newNode = new Node(value, None)\n    insertTail(newNode)\n  }\n\n  def insertTail(newNode: Node): Unit = {\n    headOpt match {\n      case None =>\n        //it's an empty linked list, make new node as head\n        headOpt = Some(newNode)\n      case Some(head) =>\n        //need to start to find from head to current tail\n        var node = head\n        while (node.next.isDefined) {\n          node = node.next.get\n        }\n        //now node is the tail as node.next is None\n        //add new tail\n        node.next = Some(newNode)\n    }\n  }\n\n  def insertAfter(existNode: Node, value: Int): Unit = {\n    val newNode = new Node(value, None)\n    insertAfter(existNode, newNode)\n  }\n\n  def insertAfter(existNode: Node, newNode: Node): Unit = {\n    existNode.next match {\n      case None =>\n        //exist node is tail\n        newNode.next = None\n        existNode.next = Some(newNode)\n      case Some(next) =>\n        newNode.next = Some(next)\n        existNode.next = Some(newNode)\n    }\n  }\n\n  def insertBefore(existNode: Node, value: Int): Unit = {\n    val newNode = new Node(value, None)\n    insertBefore(existNode, newNode)\n  }\n\n  def insertBefore(existNode: Node, newNode: Node): Unit = {\n    headOpt match {\n      case None =>\n        throw new IllegalStateException(\"head node should not be None\")\n      case Some(head) =>\n        if (existNode.equals(head)) {\n          insertToHead(newNode)\n        }\n        var node = head\n        while (node.next.isDefined && !node.next.get.equals(existNode)) {\n          node = node.next.get\n        }\n\n        if (node.next.isEmpty) {\n          throw new IllegalArgumentException(\"existNode \" + existNode + \" does not exist in this chain\")\n        }\n\n        newNode.next = node.next\n        node.next = Some(newNode)\n    }\n  }\n\n  def deleteByNode(node: Node): Unit = {\n    headOpt.map(head => {\n      if (head.equals(node)) {\n        //deleting head\n        headOpt = node.next\n      } else {\n        var p: Node = head\n        while (p.next.isDefined && !p.next.get.equals(node)) {\n          p = p.next.get\n        }\n\n        if (p.next.isEmpty) {\n          throw new IllegalArgumentException(\"could not find given node\")\n        }\n        p.next = node.next\n      }\n\n    })\n  }\n\n  //inverse the link from given node to head\n  def inverseLink(node: Node): Node = {\n    if (headOpt.isEmpty) {\n      throw new IllegalArgumentException(\"list is empty\")\n    }\n\n    var pre: Option[Node] = None\n    var next: Option[Node] = None\n    var current: Option[Node] = headOpt\n\n    while (current.isDefined && !current.get.equals(node)) {\n      next = current.get.next\n      current.get.next = pre\n\n      pre = current\n      current = next\n    }\n\n    current.get.next = pre\n\n    current.get\n\n  }\n\n  def isPalindrome(): Boolean = {\n    headOpt match {\n      case None => false\n      case Some(head) =>\n        var p: Node = head\n        var q: Node = head\n\n        if (p.next.isEmpty) {\n          //we only got 1 element in the chain\n          return true\n        }\n\n        //start to find middle of the chain\n        while (q.next.isDefined && q.next.get.next.isDefined) {\n          p = p.next.get\n          q = q.next.get.next.get\n        }\n        var leftLink: Option[Node] = None\n        var rightLink: Option[Node] = None\n        q.next match {\n          case None =>\n            //p is in the middle of an odd numbers of chain\n            rightLink = p.next\n            leftLink = inverseLink(p).next\n          case Some(_) =>\n            //p and p.next is in the middle of the even numbers of chain\n            rightLink = p.next\n            leftLink = Some(inverseLink(p))\n        }\n\n        compareLinkedNodes(leftLink, rightLink)\n    }\n  }\n\n  def compareLinkedNodes(leftLink: Option[Node], rightLink: Option[Node]): Boolean = {\n    var left = leftLink\n    var right = rightLink\n\n    breakable {\n      while (left.isDefined && right.isDefined) {\n        if (!left.get.data.equals(right.get.data)) {\n          break\n        }\n        left = left.get.next\n        right = right.get.next\n      }\n    }\n    //make sure we have loop until the end of the chain\n    left.isEmpty && right.isEmpty\n  }\n\n  def mkString(): String = {\n    headOpt.map(head => {\n      var node = head\n      val result = new StringBuilder\n\n      while (node.next.isDefined) {\n        result.append(node.data)\n        node = node.next.get\n      }\n      result.append(node.data)\n\n      result.mkString\n    }).getOrElse(\"\")\n  }\n\n}\n"
  },
  {
    "path": "scala/src/main/scala/ch07_linkedlist/LinkedListAlgo.scala",
    "content": "package ch07_linkedlist\n\nimport ch06_linkedlist.Node\n\nobject LinkedListAlgo {\n\n  //reverse a linked list\n  def reverse(head: Node): Node = {\n    if (head.next.isEmpty) {\n      //single node of linked list\n      return head\n    }\n    var prev: Option[Node] = None\n    var current: Option[Node] = Some(head)\n    var next: Option[Node] = None\n\n    while (current.get.next.isDefined) {\n      next = current.get.next\n      current.get.next = prev\n\n      prev = current\n      current = next\n    }\n    //for the tail of the node, make it point to previous node to complete the reverse\n    current.get.next = prev\n\n    current.get\n  }\n\n  /**\n    *\n    * @param head\n    * @return Some(Node) a node in a circle or None\n    */\n  def checkCircle(head: Node): Option[Node] = {\n    var fast = head\n    var slow = head\n\n    while (fast.next.isDefined && fast.next.get.next.isDefined) {\n      fast = fast.next.get.next.get\n      slow = slow.next.get\n\n      if (fast.equals(slow)) {\n        return Some(slow)\n      }\n    }\n    None\n  }\n\n  /**\n    * calculate the length of the circle\n    *\n    * @param node - some node in the circle\n    * @return circle length\n    */\n  def calculateCircleLength(node: Node): Int = {\n    var length = 1\n    var cursor = node.next.get\n\n    while (cursor != node) {\n      length += 1\n      cursor = cursor.next.get\n    }\n\n    length\n  }\n\n  def findCircleEntrance(head: Node): Option[Node] = {\n    checkCircle(head).map(node => {\n      val length = calculateCircleLength(node)\n      var fast = head\n      var slow = head\n      //fast move length steps\n      for (i <- 0 until length) {\n        fast = fast.next.get\n      }\n\n      while (slow != fast) {\n        fast = fast.next.get\n        slow = slow.next.get\n      }\n\n      slow\n    })\n  }\n\n  //assuming nodeA and nodeB are all sorted list in ascending order\n  def mergeSortedList(nodeA: Option[Node], nodeB: Option[Node]): Option[Node] = {\n    if (nodeA.isEmpty && nodeB.isEmpty) {\n      return None\n    }\n    if (nodeA.isEmpty && nodeB.isDefined) {\n      return nodeB\n    }\n    if (nodeA.isDefined && nodeB.isEmpty) {\n      return nodeA\n    }\n    //now we have both nodeA and nodeB defined\n    var head: Option[Node] = None\n    var leftCursor = nodeA\n    var rightCursor = nodeB\n\n    //calculate head and we are sure both leftCursor and rightCursor has data\n    if (leftCursor.get.data < rightCursor.get.data) {\n      head = leftCursor\n      leftCursor = leftCursor.get.next\n    } else {\n      head = rightCursor\n      rightCursor = rightCursor.get.next\n    }\n\n    var mergedCursor: Option[Node] = head\n    while (leftCursor.isDefined && rightCursor.isDefined) {\n      if (leftCursor.get.data < rightCursor.get.data) {\n        mergedCursor.get.next = leftCursor\n        leftCursor = leftCursor.get.next\n      } else {\n        mergedCursor.get.next = rightCursor\n        rightCursor = rightCursor.get.next\n      }\n      mergedCursor = mergedCursor.get.next\n    }\n    //we have loop over at least one chain\n    //we just put the other chain in to the merged chain\n    if (leftCursor.isDefined) {\n      mergedCursor.get.next = leftCursor\n    } else {\n      mergedCursor.get.next = rightCursor\n    }\n\n    head\n  }\n\n  def deleteLastKthNode(headOpt: Option[Node], k: Int): Option[Node] = {\n    require(k > 0, \"k must greater than 0\")\n    headOpt match {\n      case None => None\n      case Some(head) =>\n        var index = 0\n        var slow = headOpt\n        var fast = headOpt\n        while (fast.get.next.isDefined && index < k) {\n          //move fast cursor to k\n          fast = fast.get.next\n          index += 1\n        }\n\n        if (fast.get.next.isEmpty && index + 1 == k) {\n          //deleting the head element\n          return head.next\n        }\n\n        require(index.equals(k), \"given linked list should contains at least k elements \")\n        while (fast.get.next.isDefined) {\n          fast = fast.get.next\n          slow = slow.get.next\n        }\n\n        //fast cursor is in the end of the chain\n        //slow is the previous pos of k element\n        //do the operation\n        slow.get.next = slow.get.next.get.next\n    }\n\n    headOpt\n  }\n\n  //form all the chain value as string\n  def mkStringForChain(node: Node): String = {\n    val result = new StringBuilder\n\n    var p = node\n\n    while (p.next.isDefined) {\n      result.append(p.data)\n      p = p.next.get\n    }\n    result.append(p.data)\n    result.mkString\n  }\n}\n"
  },
  {
    "path": "scala/src/main/scala/ch08_stack/BrowserDemo.scala",
    "content": "package ch08_stack\n\nclass BrowserDemo(var currentPageOpt: Option[String], val backStack: StackDemo[String],\n                  val forwardStack: StackDemo[String]) {\n\n  def this() = this(None, new StackDemo[String], new StackDemo[String])\n\n  def open(page: String) = {\n    currentPageOpt.foreach(backStack.push)\n    forwardStack.clear()\n    currentPageOpt = Some(page)\n  }\n\n  def canGoBack(): Boolean = backStack.size > 0\n\n  def goBack(): Unit = {\n    backStack.pop().foreach(page => {\n      forwardStack.push(currentPageOpt.get)\n      currentPageOpt = Some(page.data)\n    })\n  }\n\n  def canGoForward(): Boolean = forwardStack.size > 0\n\n  def goForward(): Unit = {\n    forwardStack.pop().foreach(page => {\n      backStack.push(currentPageOpt.get)\n      currentPageOpt = Some(page.data)\n    })\n  }\n}\n"
  },
  {
    "path": "scala/src/main/scala/ch08_stack/StackDemo.scala",
    "content": "package ch08_stack\n\nclass Node[T](var data: T, var next: Option[Node[T]])\n\nclass StackDemo[T] {\n\n\n  var headOpt: Option[Node[T]] = None\n  var size = 0\n\n  def clear(): Unit = {\n    headOpt = None\n    size = 0\n  }\n\n  def push(data: T) = {\n    val newHead = new Node(data, headOpt)\n    headOpt = Some(newHead)\n    size += 1\n  }\n\n  def pop(): Option[Node[T]] = {\n    headOpt match {\n      case None => None\n      case Some(head) =>\n        headOpt = head.next\n        size -= 1\n        Some(head)\n\n    }\n  }\n}\n"
  },
  {
    "path": "scala/src/main/scala/ch09_queue/ArrayQueue.scala",
    "content": "package ch09_queue\n\nimport scala.reflect.ClassTag\n\nclass ArrayQueue[T: ClassTag](capacity: Int) extends DemoQueue[T] {\n\n  var items: Array[T] = new Array[T](capacity)\n  var head = 0\n  var tail = 0\n\n  override def enqueue(data: T): Unit = {\n    require(tail < capacity, \"queue is full\")\n    items(tail) = data\n    tail += 1\n    size += 1\n  }\n\n  override def dequeue(): Option[T] = {\n    if (head < tail) {\n      val result = Some(items(head))\n      head += 1\n      size -= 1\n      result\n    } else {\n      None\n    }\n  }\n}\n"
  },
  {
    "path": "scala/src/main/scala/ch09_queue/CircularQueue.scala",
    "content": "package ch09_queue\n\nimport scala.reflect.ClassTag\n\nclass CircularQueue[T: ClassTag](capacity: Int) extends DemoQueue[T] {\n\n  var items: Array[T] = new Array[T](capacity)\n  var head = 0\n  var tail = 0\n\n\n  override def enqueue(data: T): Unit = {\n    require((tail + 1) % capacity != head, \"queue is full\")\n    items(tail) = data\n    tail = (tail + 1) % capacity\n    size += 1\n  }\n\n  override def dequeue(): Option[T] = {\n    if (head == tail) {\n      None\n    } else {\n      size -= 1\n      val result = Some(items(head))\n      head = (head + 1) % capacity\n      result\n    }\n  }\n}\n"
  },
  {
    "path": "scala/src/main/scala/ch09_queue/DemoQueue.scala",
    "content": "package ch09_queue\n\ntrait DemoQueue[T] {\n\n  var size = 0\n\n  def enqueue(data: T): Unit\n\n  def dequeue(): Option[T]\n}\n"
  },
  {
    "path": "scala/src/main/scala/ch09_queue/DynamicArrayQueue.scala",
    "content": "package ch09_queue\n\nimport scala.reflect.ClassTag\n\nclass DynamicArrayQueue[T: ClassTag](val capacity: Int) extends ArrayQueue[T](capacity) {\n\n  override def enqueue(data: T): Unit = {\n    if (tail == capacity) {\n      //tail is the end of\n      require(head > 0, \"queue is full\")\n      for (i <- Range(head, tail)) {\n        items(i - head) = items(head)\n      }\n      tail = tail - head\n      head = 0\n    }\n    super.enqueue(data)\n  }\n}\n"
  },
  {
    "path": "scala/src/main/scala/ch09_queue/LinkedListQueue.scala",
    "content": "package ch09_queue\n\nclass Node[T](var data: T, var next: Option[Node[T]])\n\nclass LinkListQueue[T]() extends DemoQueue[T] {\n\n  var headOpt: Option[Node[T]] = None\n  var tailOpt: Option[Node[T]] = None\n\n  override\n  def enqueue(data: T): Unit = {\n    val node = new Node(data, None)\n    size += 1\n    headOpt match {\n      case None => headOpt = Some(node)\n      case Some(_) =>\n    }\n    tailOpt match {\n      case None => tailOpt = Some(node)\n      case Some(tail) =>\n        tail.next = Some(node)\n        tailOpt = Some(node)\n    }\n  }\n\n  override\n  def dequeue(): Option[T] = {\n    headOpt.map(head => {\n      size -= 1\n      headOpt = head.next\n      if (headOpt.isEmpty) {\n        //head is empty reach the end of the queue\n        tailOpt = None\n      }\n      head.data\n    })\n  }\n\n}\n"
  },
  {
    "path": "scala/src/main/scala/ch10_recursive/RecursiveDemo.scala",
    "content": "package ch10_recursive\n\nimport scala.collection.mutable\n\nobject RecursiveDemo {\n\n  def calculateStepWays(steps: Int): Int = {\n    //use knownResults to avoid duplicated computing\n    val knownResults = mutable.HashMap.empty[Int, Int]\n    steps match {\n      case 1 => 1\n      case 2 => 2\n      case _ => knownResults.get(steps) match {\n        case Some(result) => result\n        case None => {\n          val result = calculateStepWays(steps - 1) + calculateStepWays(steps - 2)\n          knownResults.put(steps, result)\n          result\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "scala/src/main/scala/ch11_sorts/Sorts.scala",
    "content": "package ch11_sorts\n\nimport scala.util.control.Breaks.{break, breakable}\n\n/**\n  * 冒泡排序、插入排序、选择排序\n  *\n  * Author: yangchuz\n  */\nobject Sorts {\n\n  def bubbleSort(items: Array[Int]): Array[Int] = {\n    val length = items.length\n    breakable {\n      for (i <- Range(0, length)) {\n        var exit = true\n        for (j <- Range(0, length - i - 1)) {\n          if (items(j + 1) < items(j)) {\n            val temp = items(j + 1)\n            items(j + 1) = items(j)\n            items(j) = temp\n            exit = false\n          }\n        }\n        if (exit) {\n          break\n        }\n      }\n    }\n    items\n  }\n\n  def insertSort(items: Array[Int]): Array[Int] = {\n    val length = items.length\n    for (i <- Range(1, length)) {\n      val value = items(i)\n      var j = i - 1\n      breakable {\n        while (j >= 0) {\n          if (items(j) > value) {\n            items(j + 1) = items(j)\n          } else {\n            break\n          }\n          j -= 1\n        }\n      }\n      items(j + 1) = value\n    }\n    items\n  }\n\n  def selectionSort(items: Array[Int]): Array[Int] = {\n    val length = items.length\n    for (i <- Range(0, length)) {\n      var minIndex = i\n      for (j <- Range(i + 1, length)) {\n        if (items(j) < items(minIndex)) {\n          minIndex = j\n        }\n      }\n\n      //put the min value to the front\n      val temp = items(i)\n      items(i) = items(minIndex)\n      items(minIndex) = temp\n    }\n    items\n  }\n}\n"
  },
  {
    "path": "scala/src/main/scala/ch12_sorts/MergeSort.scala",
    "content": "package ch12_sorts\n\nobject MergeSort {\n\n  def mergeSort(items: Array[Int]): Array[Int] = {\n    _mergeSort(items, 0, items.length - 1)\n    items\n  }\n\n\n  private[this] def _mergeSort(items: Array[Int], p: Int, r: Int): Unit = {\n    if (p >= r) {\n      return\n    }\n\n    val q = p + (r - p) / 2\n    _mergeSort(items, p, q)\n    _mergeSort(items, q + 1, r)\n    _merge(items, p, q, r)\n\n  }\n\n  private[this] def _merge(items: Array[Int], p: Int, q: Int, r: Int): Unit = {\n    //start of first half\n    var i = p\n    //start of second half\n    var j = q + 1\n    var k = 0\n    //temp array to hold the data\n    val tempArray = new Array[Int](r - p + 1)\n    while (i <= q && j <= r) {\n      if (items(i) <= items(j)) {\n        tempArray(k) = items(i)\n        i += 1\n      } else {\n        tempArray(k) = items(j)\n        j += 1\n      }\n      k += 1\n    }\n\n    var start = i\n    var end = q\n\n    if (j <= r) {\n      start = j\n      end = r\n    }\n\n    for (n <- start to end) {\n      tempArray(k) = items(n)\n      k += 1\n    }\n\n    //copy tempArray back to items\n    for (n <- 0 to r - p) {\n      items(p + n) = tempArray(n)\n    }\n  }\n}\n"
  },
  {
    "path": "scala/src/main/scala/ch12_sorts/QuickSort.scala",
    "content": "package ch12_sorts\n\nobject QuickSort {\n\n  //find the K th smallest element int the array\n  def findKthElement(items: Array[Int], k: Int): Int = {\n    _findKthElement(items, k, 0, items.length - 1)\n  }\n\n  private[this] def _findKthElement(items: Array[Int], k: Int, p: Int, r: Int): Int = {\n    val q = _partition(items, p, r)\n\n    if (k == q + 1) {\n      items(q)\n    } else if (k < q + 1) {\n      _findKthElement(items, k, p, q - 1)\n    } else {\n      _findKthElement(items, k, q + 1, r)\n    }\n  }\n\n  def quickSort(items: Array[Int]): Array[Int] = {\n    _quickSort(items, 0, items.length - 1)\n    items\n  }\n\n  private[this] def _quickSort(items: Array[Int], p: Int, r: Int): Unit = {\n    if (p >= r) {\n      return\n    }\n    val q = _partition(items, p, r)\n    _quickSort(items, p, q - 1)\n    _quickSort(items, q + 1, r)\n  }\n\n  private[this] def _partition(items: Array[Int], p: Int, r: Int): Int = {\n    val pivot = items(r)\n    var i = p\n    for (j <- Range(p, r)) {\n      if (items(j) < pivot) {\n        val temp = items(i)\n        items(i) = items(j)\n        items(j) = temp\n        i += 1\n      }\n    }\n\n    val temp = items(i)\n    items(i) = items(r)\n    items(r) = temp\n\n    i\n  }\n}\n"
  },
  {
    "path": "scala/src/main/scala/ch15_bsearch/BSearch.scala",
    "content": "package ch15_bsearch\n\nimport scala.math.abs\n\nobject BSearch {\n\n  def search(items: Array[Int], target: Int): Int = {\n    var low = 0\n    var high = items.length - 1\n    while (low <= high) {\n      val mid = low + (high - low) / 2\n      if (items(mid) == target) {\n        return mid\n      } else if (items(mid) > target) {\n        high = mid - 1\n      } else {\n        low = mid + 1\n      }\n    }\n\n    -1\n  }\n\n  def sqrt(x: Double, precision: Double): Double = {\n\n    require(precision > 0, \"precision must > 0\")\n    require(x > 0, \"input value for sqrt must > 0\")\n    var low = 0.0\n    var high = x\n    val actualPrecision = precision / 10\n\n    if (x > 0 && x < 1) {\n      low = x\n      high = 1\n    }\n    while (high - low > actualPrecision) {\n      val mid = low + (high - low) / 2\n      if (abs(mid * mid - x) < actualPrecision) {\n        //find it\n        return mid\n      } else if (mid * mid > x) {\n        high = mid\n      } else {\n        low = mid\n      }\n    }\n    throw new IllegalStateException(\"could not determine the sqrt value for \" + x)\n\n  }\n}\n"
  },
  {
    "path": "scala/src/main/scala/ch15_bsearch/BSearchRecursive.scala",
    "content": "package ch15_bsearch\n\nobject BSearchRecursive {\n\n  def search(items: Array[Int], target: Int): Int = {\n    _search(items, target, 0, items.length - 1)\n  }\n\n  private[this] def _search(items: Array[Int], target: Int, low: Int, high: Int): Int = {\n    if (low > high) {\n      return -1\n    }\n\n    val mid = low + (high - low) / 2\n    if (items(mid) == target) {\n      mid\n    } else if (items(mid) > target) {\n      _search(items, target, low, mid - 1)\n    } else {\n      _search(items, target, mid + 1, high)\n    }\n  }\n}\n"
  },
  {
    "path": "scala/src/main/scala/ch16_bsearch/BSearch.scala",
    "content": "package ch16_bsearch\n\nobject BSearch {\n\n  //find the first index of given value\n  //-1 if not found\n  def findFirstValue(items: Array[Int], target: Int): Int = {\n    require(items.length > 0, \"given array is empty\")\n    var low = 0\n    var high = items.length - 1\n    while (low <= high) {\n      val mid = low + (high - low) / 2\n      if (items(mid) > target) {\n        high = mid - 1\n      } else if (items(mid) < target) {\n        low = mid + 1\n      } else {\n        //find the value in the array\n        if (mid == 0 || items(mid - 1) != target) {\n          return mid\n        } else {\n          high = mid - 1\n        }\n      }\n    }\n    -1\n  }\n\n  def findLastValue(items: Array[Int], target: Int): Int = {\n    var low = 0\n    var high = items.length - 1\n    while (low <= high) {\n      val mid = low + (high - low) / 2\n      if (items(mid) > target) {\n        high = mid - 1\n      } else if (items(mid) < target) {\n        low = mid + 1\n      } else {\n        //find the target value\n        if (mid == items.length - 1 || items(mid + 1) != target) {\n          return mid\n        } else {\n          low = mid + 1\n        }\n      }\n    }\n    -1\n  }\n\n  def findFirstGreaterThan(items: Array[Int], target: Int): Int = {\n    var low = 0\n    var high = items.length\n    while (low <= high) {\n      val mid = low + (high - low) / 2\n      if (items(mid) >= target) {\n        //find the range\n        if (mid == 0 || items(mid - 1) < target) {\n          return mid\n        } else {\n          high = mid - 1\n        }\n      } else {\n        low = mid + 1\n      }\n    }\n    -1\n  }\n\n  def findLastSmallerThan(items: Array[Int], target: Int): Int = {\n    var low = 0\n    var high = items.length - 1\n    while (low <= high) {\n      var mid = low + (high - low) / 2\n      if (items(mid) <= target) {\n        //find the range\n        if (mid == items.length - 1 || items(mid + 1) > target) {\n          return mid\n        } else {\n          low = mid + 1\n        }\n      } else {\n        high = mid - 1\n      }\n    }\n    -1\n  }\n}\n"
  },
  {
    "path": "scala/src/main/scala/ch17_skip_list/SkipList.scala",
    "content": "package ch17_skip_list\n\nimport scala.util.Random\n\nclass Node(var data: Int, var forwards: Array[Node], var maxLevel: Int)\n\nclass SkipList(var head: Node, var skipListLevel: Int) {\n\n  def this() = this(new Node(-1, new Array[Node](16), 0), 1)\n\n  val MAX_LEVEL = 16\n  val random = new Random()\n\n  def find(value: Int): Option[Node] = {\n    var p = head\n    for (i <- skipListLevel - 1 to 0 by -1) {\n      while (p.forwards(i) != null && p.forwards(i).data < value) {\n        p = p.forwards(i)\n      }\n    }\n    if (p.forwards(0) != null && p.forwards(0).data == value) {\n      Some(p.forwards(0))\n    } else {\n      None\n    }\n  }\n\n  def insert(value: Int): Unit = {\n    //init the new node\n    val level = randomLevel()\n    val newNode = new Node(value, new Array[Node](level), level)\n\n    //use updtes array to record all nodes in all level before the inserted node\n    val updates: Array[Node] = new Array[Node](level)\n    var p = head\n    for (i <- level - 1 to 0 by -1) {\n      while (p.forwards(i) != null && p.forwards(i).data < value) {\n        p = p.forwards(i)\n      }\n      updates(i) = p\n    }\n\n    for (i <- Range(0, level)) {\n      newNode.forwards(i) = updates(i).forwards(i)\n      updates(i).forwards(i) = newNode\n    }\n\n    if (level > skipListLevel) {\n      skipListLevel = level\n    }\n  }\n\n  def delete(value: Int): Unit = {\n    var p = head\n    val updates: Array[Node] = new Array[Node](skipListLevel)\n\n    //try to locate the given node with the value\n    for (i <- skipListLevel - 1 to 0 by -1) {\n      while (p.forwards(i) != null && p.forwards(i).data < value) {\n        p = p.forwards(i)\n      }\n      updates(i) = p\n    }\n\n    if (p.forwards(0) != null && p.forwards(0).data == value) {\n      //find the value node, start to delete the node from the skip list\n      for (i <- skipListLevel - 1 to 0 by -1) {\n        if (updates(i).forwards(i) != null && updates(i).forwards(i).data == value) {\n          updates(i).forwards(i) = updates(i).forwards(i).forwards(i)\n        }\n      }\n    }\n\n  }\n\n  def randomLevel(): Int = {\n    var level = 1\n    for (i <- Range(1, MAX_LEVEL)) {\n      if (random.nextInt() % 2 == 1) {\n        level += 1\n      }\n    }\n\n    level\n  }\n\n  def mkString(): String = {\n    val builder = new StringBuilder\n    var p = head\n    while (p.forwards(0) != null) {\n      p = p.forwards(0)\n      builder.append(p.data)\n    }\n\n    builder.mkString\n  }\n}\n\n\n"
  },
  {
    "path": "scala/src/main/scala/ch20_linked_hash_map/LRUCache.scala",
    "content": "package ch20_linked_hash_map\n\nclass Node[K, V](var key: Option[K], var data: Option[V], var prev: Option[Node[K, V]], var next: Option[Node[K, V]],\n                 var hNext: Option[Node[K, V]]) {\n\n  def this(key: Option[K], data: Option[V]) = this(key, data, None, None, None)\n}\n\n/**\n  * LRU cache - https://leetcode.com/problems/lru-cache/ see unit test from LRUCacheTest\n  *\n  * @author email2liyang@gmail.com\n  */\nclass LRUCache[K, V](var head: Node[K, V], var tail: Node[K, V], var table: Array[Node[K, V]],\n                     capacity: Int = 1000, var elementCount: Int = 0) {\n\n  head.next = Some(tail)\n  tail.prev = Some(head)\n\n  def this(capacity: Int) = this(new Node(None, None), new Node(None, None), new Array[Node[K, V]](capacity), capacity)\n\n  def get(key: K): Option[V] = {\n    val index = indexFor(key.hashCode())\n    var hNode = table(index)\n    if (hNode == null) {\n      None\n    } else {\n      while (!hNode.key.get.equals(key) && hNode.hNext.isDefined) {\n        hNode = hNode.hNext.get\n      }\n      if (hNode.key.get.equals(key)) {\n        //move this to the end of the linked list\n        moveHNodeToTail(hNode)\n        hNode.data\n      } else {\n        None\n      }\n    }\n  }\n\n\n  //put data into the linked hash map\n  //1: check if the data exist in the linked list\n  //2: if it's not exist , append it in the linked list\n  //3: if it's exist in the list, move it to the tail of the linked list\n  //4: return old value if it's exist\n  def put(key: K, value: V): Option[V] = {\n\n    if (elementCount == capacity) {\n      deleteLRUElement()\n    }\n\n    val node = new Node(Some(key), Some(value))\n    val index = indexFor(key.hashCode())\n    var hNode = table(index)\n    var result: Option[V] = None\n    if (hNode == null) {\n      //if it's not exist , append it in the linked list\n      node.prev = tail.prev\n      node.next = Some(tail)\n      tail.prev.get.next = Some(node)\n      tail.prev = Some(node)\n      table(index) = node\n      elementCount += 1\n    } else {\n      //we find a key conflict in the hash table\n      //start to loop the hNode to match the key\n      while (!hNode.key.get.equals(key) && hNode.hNext.isDefined) {\n        hNode = hNode.hNext.get\n      }\n\n      if (hNode.key.get.equals(key)) {\n        //find the old data from the hash table\n        result = hNode.data\n        hNode.data = Some(value)\n        //move the node to the tail of the linked list\n        moveHNodeToTail(hNode)\n        //hNext pointer stay untouched\n      } else {\n        //could not find the old data\n        //put the new node into the tail of the linked list\n        node.prev = tail.prev\n        node.next = Some(tail)\n        tail.prev.get.next = Some(node)\n        tail.prev = Some(node)\n\n        //put it the tail of the hash table's list\n        //iterator to the end of hNode\n        while (hNode.hNext.isDefined) {\n          hNode = hNode.hNext.get\n        }\n        hNode.hNext = Some(node)\n        elementCount += 1\n      }\n    }\n    result\n  }\n\n  private[this] def moveHNodeToTail(hNode: Node[K, V]) = {\n    hNode.prev.get.next = hNode.next\n    hNode.next.get.prev = hNode.prev\n    hNode.prev = tail.prev\n    hNode.next = Some(tail)\n    tail.prev.get.next = Some(hNode)\n    tail.prev = Some(hNode)\n  }\n\n  private[this] def deleteLRUElement(): Unit = {\n    //cache is full, start to delete element from the head\n    val node = head.next.get\n\n    //delete it from head\n    node.next.get.prev = Some(head)\n    head.next = node.next\n\n    //deal with hNext in the table\n    val index = indexFor(node.key.get.hashCode())\n    var hNode = table(index)\n    //deal with first element in the hash table\n    if (hNode.key.get.equals(node.key.get)) {\n      hNode.hNext match {\n        case Some(n) => table(index) = n\n        case None => table(index) = null\n      }\n    } else {\n      //deal with not first element in the hash table\n      var hNodePrev = hNode\n      hNode = hNode.next.get\n      while (!hNode.key.get.equals(node.key.get)) {\n        hNode = hNode.next.get\n        hNodePrev = hNodePrev.next.get\n      }\n      //now hNodePrev is the previous hNode in the hashtable\n      //remove the hNode\n      hNodePrev.next = hNode.next\n\n      hNode.next match {\n        case Some(n) => n.prev = Some(hNodePrev)\n        case None =>\n      }\n    }\n\n    elementCount -= 1\n  }\n\n  private[this] def indexFor(hash: Int): Int = {\n    hash % table.length\n  }\n}\n"
  },
  {
    "path": "scala/src/main/scala/ch23_binary_tree/BinaryTree.scala",
    "content": "package ch23_binary_tree\n\nimport scala.collection.mutable\n\nclass Node[T](var data: T, var left: Option[Node[T]], var right: Option[Node[T]])\n\nclass BinaryTree[T] {\n\n  def preOrder(root: Option[Node[T]]): String = {\n    val result = new StringBuilder\n\n    if (root.isDefined) {\n      result.append(root.map(_.data.toString).get)\n      result.append(preOrder(root.get.left))\n      result.append(preOrder(root.get.right))\n    }\n    result.mkString\n  }\n\n  def inOrder(root: Option[Node[T]]): String = {\n    val result = new StringBuilder\n\n    if (root.isDefined) {\n      result.append(inOrder(root.get.left))\n      result.append(root.map(_.data.toString).get)\n      result.append(inOrder(root.get.right))\n    }\n    result.mkString\n  }\n\n  def postOrder(root: Option[Node[T]]): String = {\n    val result = new StringBuilder\n\n    if (root.isDefined) {\n      result.append(postOrder(root.get.left))\n      result.append(postOrder(root.get.right))\n      result.append(root.map(_.data.toString).get)\n    }\n    result.mkString\n  }\n\n  def levelOrder(root: Option[Node[T]]): String = {\n    val result = new StringBuilder\n    val queue = new mutable.Queue[Node[T]]()\n    if (root.isDefined) {\n      queue += root.get\n      while (!queue.isEmpty) {\n        val node = queue.dequeue()\n        result.append(node.data.toString)\n        if (node.left.isDefined) {\n          queue += node.left.get\n        }\n        if (node.right.isDefined) {\n          queue += node.right.get\n        }\n      }\n    }\n\n    result.mkString\n  }\n}\n"
  },
  {
    "path": "scala/src/main/scala/ch24_binary_search_tree/BinarySearchTree.scala",
    "content": "package ch24_binary_search_tree\n\nimport ch23_binary_tree.{BinaryTree, Node}\n\nimport scala.util.control.Breaks._\n\nclass BinarySearchTree(var root: Option[Node[Int]]) extends BinaryTree[Int] {\n\n  def insert(data: Int): Node[Int] = {\n    val dataNode = new Node(data, None, None)\n    root match {\n      case None => root = Some(dataNode)\n      case Some(_) => {\n        var p = root\n        breakable {\n          while (p.isDefined) {\n            if (data > p.get.data) {\n              p.get.right match {\n                case None => {\n                  p.get.right = Some(dataNode)\n                  break\n                }\n                case Some(_) => p = p.get.right\n              }\n            }\n            if (data < p.get.data) {\n              p.get.left match {\n                case None => {\n                  p.get.left = Some(dataNode)\n                  break\n                }\n                case Some(_) => p = p.get.left\n              }\n            }\n          }\n        }\n      }\n    }\n\n    dataNode\n  }\n\n  def find(data: Int): Option[Node[Int]] = {\n    var p = root\n    breakable {\n      while (p.isDefined) {\n        if (data > p.get.data) {\n          p = p.get.right\n        } else if (data < p.get.data) {\n          p = p.get.left\n        } else {\n          //find the value\n          break\n        }\n      }\n    }\n    p\n  }\n\n  def delete(data: Int): Unit = {\n    //there are 3 scenarios\n    //1: data is leaf node\n    //2: data has one child node\n    //3: data has two child nodes, we need to find out the smallest node from right branch\n    var p = root\n    var pp: Option[Node[Int]] = None //parent node of deleted node\n\n    //find matching node to delete\n    breakable {\n      while (p.isDefined) {\n        if (data > p.get.data) {\n          pp = p\n          p = p.get.right\n        } else if (data < p.get.data) {\n          pp = p\n          p = p.get.left\n        } else {\n          //find the value\n          break\n        }\n      }\n    }\n\n    if (p.isEmpty) {\n      //find nothing\n      return\n    }\n\n    //now we find the node to delete\n    //scenario 3\n    if (p.get.left.isDefined && p.get.right.isDefined) {\n      //need to find out the smallest node in right branch\n      var minPP = p\n      var minP = p.get.right\n      while (minP.get.left.isDefined) {\n        minPP = minP\n        minP = minP.get.left\n      }\n\n      //assign the smallest value in the right branch to the node to be deleted\n      p.get.data = minP.get.data\n      //now problems becomes delete the minP in the tree\n      //minP must not have any left child node\n      //minP may or may not have right child node\n      //it will fall back to scenario 1 or 2\n      p = minP\n      pp = minPP\n    }\n\n    //child is the child of p\n    var child: Option[Node[Int]] = None\n    if (p.get.left.isDefined) {\n      child = p.get.left\n    } else if (p.get.right.isDefined) {\n      child = p.get.right\n    }\n\n    //starting the node deletion\n    pp match {\n      case None => root = child\n      case Some(parentNode) => {\n        if (parentNode.left == p) {\n          parentNode.left = child\n        } else if (parentNode.right == p) {\n          parentNode.right = child\n        }\n      }\n    }\n\n  }\n\n  def height(): Int = {\n    _height(root)\n  }\n\n  private[this] def _height(nodeOpt: Option[Node[Int]]): Int = {\n    nodeOpt match {\n      case None => 0\n      case Some(node) => {\n        if (node.left.isEmpty && node.right.isEmpty) {\n          1\n        } else {\n          scala.math.max(_height(node.left), _height(node.right)) + 1\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "scala/src/main/scala/ch28_heap/Heap.scala",
    "content": "package ch28_heap\n\nimport scala.util.control.Breaks._\n\nclass Heap(val capacity: Int, var elementCount: Int = 0) {\n\n  def this(arrayParam: Array[Int], bottomUp: Boolean) = {\n    this(arrayParam.length + 1)\n    if (bottomUp) {\n      arrayParam.foreach(this.insert)\n    } else {\n      //copy data into array of heap\n      for (i <- arrayParam.indices) {\n        array(i + 1) = arrayParam(i)\n        elementCount = arrayParam.length\n      }\n      for (i <- elementCount / 2 + 1 to 1 by -1) {\n        heapifyTopDown(i, elementCount - 1)\n      }\n    }\n  }\n\n  require(capacity > 0, \"capacity should be > 0\")\n  val array: Array[Int] = new Array[Int](capacity)\n\n  def insert(data: Int): Unit = {\n    if (elementCount == capacity - 1) {\n      throw new IllegalStateException(\"heap full\")\n    }\n\n    elementCount += 1\n    array(elementCount) = data\n\n    //heapify bottom up\n    //compare the element with it's parent node i/2 until parent node > child node\n    //this will make sure the root element of the tree is the biggest value\n    var i = elementCount\n    while (i / 2 > 0 && array(i) > array(i / 2)) {\n      val temp = array(i)\n      array(i) = array(i / 2)\n      array(i / 2) = temp\n      i = i / 2\n    }\n\n  }\n\n  def removeMax(): Int = {\n    require(elementCount > 0, \"heap is empty\")\n    val result = array(1)\n    array(1) = array(elementCount)\n    elementCount -= 1\n    heapifyTopDown(1, elementCount)\n    result\n  }\n\n  //heapify from top to bottom\n  //start from the top to compare with it's child nodes\n  //swap if child node > parent node\n  //stop at child node <= parent node\n  private[this] def heapifyTopDown(startIndex: Int, stopIndex: Int) = {\n    var pointer = startIndex\n    breakable {\n      while (true) {\n        var maxPos = pointer\n        if (pointer * 2 <= stopIndex && array(pointer * 2) > array(maxPos)) {\n          maxPos = pointer * 2\n        }\n        if (pointer * 2 <= stopIndex && array(pointer * 2 + 1) > array(maxPos)) {\n          maxPos = pointer * 2 + 1\n        }\n        if (maxPos == pointer) {\n          break\n        }\n        //swap the parent and child\n        val temp = array(pointer)\n        array(pointer) = array(maxPos)\n        array(maxPos) = temp\n        //start a new round\n        pointer = maxPos\n      }\n    }\n  }\n\n}\n\nobject Heap {\n  def heapSort(array: Array[Int]): Array[Int] = {\n    val result = new Array[Int](array.length)\n    val heap = new Heap(array, true)\n    for (i <- result.length - 1 to 0 by -1) {\n      result(i) = heap.removeMax()\n    }\n    result\n  }\n}"
  },
  {
    "path": "scala/src/main/scala/ch29_heap_solutions/FileMerger.scala",
    "content": "package ch29_heap_solutions\n\nimport java.io.{BufferedWriter, File, FileWriter}\n\nimport scala.collection.mutable\nimport scala.io.Source\nimport scala.util.control.Breaks._\n\nobject FileMerger {\n\n  /**\n    * each given file has sorted String as content, we need to merge them together\n    *\n    * @param smallFiles - small files with sorted content\n    * @return merged file\n    */\n  def mergeFiles(smallFiles: List[File]): File = {\n    //init output file\n    val output = File.createTempFile(\"merged-file\", \".txt\")\n    val writer = new BufferedWriter(new FileWriter(output))\n    //init small top heap\n    val priorityQueue = new mutable.PriorityQueue[(Char, Source)]()(Ordering.by((_: (Char, Source))._1).reverse)\n    val sources = smallFiles.toArray.map(smallFile => Source.fromFile(smallFile))\n    //init fill the priority queue from each file\n    sources.foreach(source => priorityQueue.enqueue((source.next(), source)))\n\n    breakable {\n      while (true) {\n        val next = priorityQueue.dequeue()\n        val output: Char = next._1\n        val source = next._2\n        writer.append(output)\n        if (source.hasNext) {\n          priorityQueue.enqueue((source.next(), source))\n        }\n        //determine the end of merge\n        if (sources.forall(!_.hasNext) && priorityQueue.isEmpty) {\n          break\n        }\n      }\n    }\n\n    writer.close()\n    output\n  }\n}\n"
  },
  {
    "path": "scala/src/main/scala/ch29_heap_solutions/MiddleNumberKeeper.scala",
    "content": "package ch29_heap_solutions\n\nimport scala.collection.mutable\n\nclass MiddleNumberKeeper(val percent: Double) {\n\n  def this() = this(0.5)\n\n  val bigTop = new mutable.PriorityQueue[Int]()\n  val smallTop = new mutable.PriorityQueue[Int]()(scala.math.Ordering.Int.reverse)\n\n\n  def put(num: Int): Unit = {\n    if (smallTop.nonEmpty && num >= smallTop.head) {\n      smallTop += num\n      adjustHeap()\n      return\n    }\n\n    //for any other scenario, we just put the item to bitTop then adjustHeap\n    bigTop += num\n    adjustHeap()\n  }\n\n  def get(): Option[Int] = {\n    bigTop.headOption\n  }\n\n  private[this] def adjustHeap(): Unit = {\n    val totalLength = smallTop.length + bigTop.length\n    //deal with bigTop\n    while (bigTop.length.doubleValue() / totalLength - percent > 0.0001) {\n      //move item from bigTop to smallTop\n      smallTop += bigTop.dequeue()\n    }\n\n    //deal with smallTop\n    while (smallTop.length.doubleValue() / totalLength - (1.0D - percent) > 0.0001) {\n      bigTop += smallTop.dequeue()\n    }\n  }\n}\n"
  },
  {
    "path": "scala/src/main/scala/ch29_heap_solutions/TopKItemsKeeper.scala",
    "content": "package ch29_heap_solutions\n\nimport scala.collection.mutable\n\n/**\n  * keep the top k items in the the class\n  */\nclass TopKItemsKeeper(val itemsToKeepCount: Int) {\n\n\n  //have a smallest value top heap\n  val queue = new mutable.PriorityQueue[Int]()(scala.math.Ordering.Int.reverse)\n\n  def put(item: Int): Unit = {\n    if (queue.length < itemsToKeepCount) {\n      queue += item\n      return\n    }\n\n    //queue already have the k items\n    if (item.compareTo(queue.head) > 0) {\n      queue.dequeue()\n      queue += item\n    }\n  }\n\n  def get(): List[Int] = {\n    queue.clone().dequeueAll\n  }\n}\n"
  },
  {
    "path": "scala/src/main/scala/ch31_graph/Graph.scala",
    "content": "package ch31_graph\n\nimport scala.collection.mutable\nimport scala.collection.mutable.ArrayBuffer\nimport scala.util.control.Breaks._\n\nclass Graph(vertex: Array[String]) {\n\n  require(vertex.nonEmpty, \"nonEmpty vertex required\")\n  val adjacency = new Array[mutable.MutableList[String]](vertex.length)\n  for (i <- Range(0, vertex.length)) {\n    adjacency(i) = new mutable.MutableList[String]()\n  }\n\n  def addEdge(startNode: String, endNode: String): Unit = {\n    adjacency(vertex.indexOf(startNode)) += endNode\n    adjacency(vertex.indexOf(endNode)) += startNode\n  }\n\n  def getEdges(node: String): Array[String] = {\n    adjacency(vertex.indexOf(node)).toArray\n  }\n\n  def breathFirstSearch(startNode: String, destNode: String): Option[Array[String]] = {\n    var result: Option[Array[String]] = None\n    val queue = new mutable.Queue[String]()\n    val visited = new mutable.HashSet[String]()\n    val explored = new ArrayBuffer[String]()\n\n    //put starting node into the queue\n    queue += startNode\n    breakable {\n      while (queue.nonEmpty) {\n        val node = queue.dequeue()\n        if (!visited.contains(node)) {\n          explored += node\n          visited += node\n\n          if (node.equals(destNode)) {\n            result = Some(explored.toArray)\n            break\n          }\n          queue ++= adjacency(vertex.indexOf(node))\n        }\n\n      }\n    }\n\n    result\n  }\n\n  def depthFirstSearch(startNode: String, destNode: String): Option[Array[String]] = {\n    var found = false\n    val visited = new mutable.HashSet[String]()\n    val explored = new ArrayBuffer[String]()\n\n    def _dfs(start: String): Unit = {\n      if (found) {\n        return\n      }\n      if (!visited.contains(start)) {\n        visited += start\n        explored += start\n        val destsForStart: mutable.MutableList[String] = adjacency(vertex.indexOf(start))\n\n        breakable {\n          for (i <- destsForStart.indices) {\n            val node = destsForStart(i)\n            if (node.equals(destNode)) {\n              found = true\n              if (!explored.contains(node)) {\n                explored += node\n              }\n              break()\n            }\n            _dfs(node)\n          }\n        }\n      }\n    }\n\n    _dfs(startNode)\n    if (found) {\n      Some(explored.toArray)\n    } else {\n      None\n    }\n  }\n}\n"
  },
  {
    "path": "scala/src/main/scala/ch32_matching/BruteForce.scala",
    "content": "package ch32_matching\n\nimport scala.util.control.Breaks._\n\nobject BruteForce {\n\n  def firstIndexOf(main: Array[Char], sub: Array[Char]): Int = {\n\n    require(main != null, \"main array required\")\n    require(sub != null, \"sub array required\")\n    require(main.length >= sub.length, \"sub array should be small than main array\")\n    var result = -1\n    breakable {\n      for (i <- 0 until (main.length - sub.length)) {\n        if (main.slice(i, i + sub.length) sameElements sub) {\n          result = i\n          break\n        }\n      }\n    }\n    result\n  }\n\n}\n"
  },
  {
    "path": "scala/src/main/scala/ch32_matching/RabinKarp.scala",
    "content": "package ch32_matching\n\nimport scala.util.control.Breaks.{break, breakable}\n\nobject RabinKarp {\n\n  def firstIndexOf(main: Array[Char], sub: Array[Char]): Int = {\n    require(main != null, \"main array required\")\n    require(sub != null, \"sub array required\")\n    require(main.length >= sub.length, \"sub array should be small than main array\")\n    val baseNums: Array[Long] = new Array[Long](sub.length)\n    for (i <- sub.indices) {\n      baseNums(i) = scala.math.pow(128, i).longValue()\n    }\n\n    val subHash = hash(sub, baseNums)\n    var result = -1\n    breakable {\n      for (i <- 0 until (main.length - sub.length)) {\n        if (hash(main.slice(i, i + sub.length), baseNums).equals(subHash)\n            && main.slice(i, i + sub.length).sameElements(sub)) {\n          result = i\n          break\n        }\n      }\n    }\n    result\n\n  }\n\n  def hash(array: Array[Char], baseNums: Array[Long]): Long = {\n    var hash = 0L\n    for (i <- array.indices) {\n      hash += array(i).toInt * baseNums(array.length - 1 - i)\n    }\n    hash\n  }\n\n}\n"
  },
  {
    "path": "scala/src/main/scala/ch35_tire_tree/TrieTree.scala",
    "content": "package ch35_tire_tree\n\nimport scala.collection.mutable.ArrayBuffer\nimport scala.util.control.Breaks._\n\nclass TrieNode(var data: Char, var children: Array[TrieNode], var isEndNode: Boolean = false) {\n\n  def this(data: Char) = this(data, new Array[TrieNode](26))\n\n}\n\nclass TrieTree {\n\n  val root = new TrieNode('/') //root node without any data\n\n  def insert(text: Array[Char]): Unit = {\n    var p = root\n    for (i <- text.indices) {\n      val index = text(i) - 'a'\n      if (p.children(index) == null) {\n        val node = new TrieNode(text(i))\n        p.children(index) = node\n      }\n      p = p.children(index)\n    }\n    //now p is pointing the leaf node\n    p.isEndNode = true\n  }\n\n  def find(text: Array[Char]): Boolean = {\n    var p = root\n\n    breakable {\n      for (i <- text.indices) {\n        val index = text(i) - 'a'\n        if (p.children(index) == null) {\n          break\n        }\n        p = p.children(index)\n      }\n    }\n\n    p.isEndNode\n  }\n\n  def prefix(prefix: Array[Char]): Boolean = {\n    var p = root\n    var level = -1\n    breakable {\n      for (i <- prefix.indices) {\n        val index = prefix(i) - 'a'\n        if (p.children(index) == null) {\n          break\n        }\n        p = p.children(index)\n        level = i\n      }\n    }\n\n    //should not reach leaf node and should have at least 1 matching level\n    !p.isEndNode && level > -1\n  }\n\n  def suggestion(text: Array[Char]): Option[Array[String]] = {\n    var p = root\n    var level = -1\n    breakable {\n      for (i <- text.indices) {\n        val index = text(i) - 'a'\n        if (p.children(index) == null) {\n          break\n        }\n        p = p.children(index)\n        level = i\n      }\n    }\n    //have prefix matching\n    if (!p.isEndNode && level > -1) {\n      //now the problem becomes print all the children from p\n      Some(_children(p).map(str => \"%s%s\".format(text.slice(0, text.length - 1).mkString(\"\"), str)).toArray)\n    } else {\n      None\n    }\n\n  }\n\n  def _children(p: TrieNode): ArrayBuffer[String] = {\n    if (p.isEndNode) {\n      return ArrayBuffer(p.data.toString)\n    }\n\n    //p is not leaf node\n    val arrayBuffer = new ArrayBuffer[String]()\n    for (i <- p.children.indices) {\n      if (p.children(i) != null) {\n        _children(p.children(i)).foreach(subStr =>\n          arrayBuffer.append(\"%s%s\".format(p.data.toString, subStr))\n        )\n      }\n    }\n\n    arrayBuffer\n  }\n\n\n}\n"
  },
  {
    "path": "scala/src/main/scala/ch39_back_tracking/BagWeight.scala",
    "content": "package ch39_back_tracking\n\nclass BagWeight(maxBagItemCount: Int, maxBagWeight: Int) {\n\n  def calculateMaxWeight(items: Array[Int]): Int = {\n    var maxWeight = 0\n\n    def _calcMaxWeight(itemIndex: Int, currentWeight: Int): Unit = {\n      if (currentWeight == maxBagWeight || itemIndex == items.length) {\n        if (currentWeight > maxWeight) {\n          maxWeight = currentWeight\n        }\n      } else {\n        //check next item\n        _calcMaxWeight(itemIndex + 1, currentWeight)\n        if (currentWeight + items(itemIndex) <= maxBagWeight) {\n          _calcMaxWeight(itemIndex + 1, currentWeight + items(itemIndex))\n        }\n      }\n    }\n\n    _calcMaxWeight(0, 0)\n    maxWeight\n  }\n}\n"
  },
  {
    "path": "scala/src/main/scala/ch39_back_tracking/EightQueens.scala",
    "content": "package ch39_back_tracking\n\nimport scala.util.control.Breaks._\n\nclass EightQueens {\n\n  //use array index to identify the row,the value of the row to identify the column\n  val result = new Array[Int](8)\n  var count = 0\n\n  def calc8Queues(row: Int): Unit = {\n    if (row == 8) {\n      //everything is done\n      print8Queens()\n      return\n    }\n\n    for (column <- Range(0, 8)) {\n      if (isOkOnColumn(row, column)) {\n        result(row) = column //place the column value into the array\n        calc8Queues(row + 1) //calculate next row\n      }\n    }\n\n  }\n\n  def isOkOnColumn(row: Int, column: Int): Boolean = {\n    var ok = true\n    var leftUp = column - 1\n    var rightUp = column + 1\n\n    breakable {\n      //will compare all the rows above current row\n      for (i <- row - 1 to 0 by -1) {\n        //check current column\n        if (result(i) == column) {\n          ok = false\n          break\n        }\n        //check left up\n        if (leftUp >= 0) {\n          if (result(i) == leftUp) {\n            ok = false\n            break\n          }\n        }\n        //check right up\n        if (rightUp < 8) {\n          if (result(i) == rightUp) {\n            ok = false\n            break\n          }\n        }\n        //move leftUp and rightUp\n        leftUp -= 1\n        rightUp += 1\n      }\n    }\n\n    ok\n  }\n\n  def print8Queens(): Unit = {\n    count +=1\n    for (row <- Range(0, 8)) {\n      for (column <- Range(0, 8)) {\n        if (result(row) == column) {\n          print(\"Q \")\n        } else {\n          print(\"* \")\n        }\n      }\n      //new line for next row\n      println(\"\")\n    }\n    println(count+\"==============\")\n  }\n}\n"
  },
  {
    "path": "scala/src/main/scala/ch39_back_tracking/NQueens.scala",
    "content": "package ch39_back_tracking\n\nimport scala.util.control.Breaks.{break, breakable}\n\nclass NQueens(numberOfQueens:Int) {\n  //use array index to identify the row,the value of the row to identify the column\n  val result = new Array[Int](numberOfQueens)\n  var count = 0\n\n  def calcNQueues(row: Int): Unit = {\n    if (row == numberOfQueens) {\n      //everything is done\n      printNQueens()\n      return\n    }\n\n    for (column <- Range(0, numberOfQueens)) {\n      if (isOkOnColumn(row, column)) {\n        result(row) = column //place the column value into the array\n        calcNQueues(row + 1) //calculate next row\n      }\n    }\n\n  }\n\n  def isOkOnColumn(row: Int, column: Int): Boolean = {\n    var ok = true\n    var leftUp = column - 1\n    var rightUp = column + 1\n\n    breakable {\n      //will compare all the rows above current row\n      for (i <- row - 1 to 0 by -1) {\n        //check current column\n        if (result(i) == column) {\n          ok = false\n          break\n        }\n        //check left up\n        if (leftUp >= 0) {\n          if (result(i) == leftUp) {\n            ok = false\n            break\n          }\n        }\n        //check right up\n        if (rightUp < numberOfQueens) {\n          if (result(i) == rightUp) {\n            ok = false\n            break\n          }\n        }\n        //move leftUp and rightUp\n        leftUp -= 1\n        rightUp += 1\n      }\n    }\n\n    ok\n  }\n\n  def printNQueens(): Unit = {\n    count +=1\n    for (row <- Range(0, numberOfQueens)) {\n      for (column <- Range(0, numberOfQueens)) {\n        if (result(row) == column) {\n          print(\"Q \")\n        } else {\n          print(\"* \")\n        }\n      }\n      //new line for next row\n      println(\"\")\n    }\n    println(count+\"==============\")\n  }\n}\n"
  },
  {
    "path": "scala/src/main/scala/ch39_back_tracking/Sudoku.scala",
    "content": "package ch39_back_tracking\n\nimport scala.util.control.Breaks._\n\nclass Sudoku {\n\n  def resolve(grid: Array[Array[Int]]): Unit = {\n    printGrid(grid)\n    println(\"\")\n    if (resolve(grid, 0, 0)) {\n      printGrid(grid)\n    } else {\n      println(\"no result\")\n      printGrid(grid)\n    }\n  }\n\n  private[this] def resolve(grid: Array[Array[Int]], row: Int, column: Int): Boolean = {\n    if (row == 8 && column == 9) {\n      //find the result\n      return true\n    }\n\n    if (column == 9) {\n      //move to next line\n      return resolve(grid, row + 1, 0)\n    }\n\n    if (grid(row)(column) != 0) {\n      //given number, resolve next one\n      return resolve(grid, row, column + 1)\n    }\n\n    //start the real resolve\n    for (num <- 1 to 9) {\n      if (isOk(grid, row, column, num)) {\n        grid(row)(column) = num\n        if (resolve(grid, row, column + 1)) {\n          return true\n        }\n      }\n    }\n\n    //do not find anything, reset given row and column\n    grid(row)(column) = 0\n    false\n  }\n\n  def isOk(grid: Array[Array[Int]], row: Int, column: Int, num: Int): Boolean = {\n    isRowOk(grid, row, num) && isColumnOk(grid, column, num) && isSmallBoxOk(grid, row, column, num)\n  }\n\n  def isRowOk(grid: Array[Array[Int]], row: Int, num: Int): Boolean = {\n    var isOk = true\n    breakable {\n      for (column <- Range(0, 9)) {\n        if (grid(row)(column) == num) {\n          isOk = false\n          break\n        }\n      }\n    }\n    isOk\n  }\n\n  def isColumnOk(grid: Array[Array[Int]], column: Int, num: Int): Boolean = {\n    var isOk = true\n    breakable {\n      for (row <- Range(0, 9)) {\n        if (grid(row)(column) == num) {\n          isOk = false\n          break\n        }\n      }\n    }\n    isOk\n  }\n\n  def isSmallBoxOk(grid: Array[Array[Int]], row: Int, column: Int, num: Int): Boolean = {\n    val rowOffSet = (row / 3) * 3\n    val columnOffSet = (column / 3) * 3\n    var isOk = true\n    breakable {\n      for (i <- Range(0, 3)) {\n        for (j <- Range(0, 3)) {\n          if (grid(i + rowOffSet)(j + columnOffSet) == num) {\n            isOk = false\n            break\n          }\n        }\n      }\n    }\n    isOk\n  }\n\n  def printGrid(grid: Array[Array[Int]]): Unit = {\n    for (i <- Range(0, 9)) {\n      if (i % 3 == 0) {\n        println(\"-------------------------\")\n      }\n      for (j <- Range(0, 9)) {\n        if (j % 3 == 0) {\n          print(\"| \")\n        }\n        print(grid(i)(j) + \" \")\n      }\n      println(\"| \")\n\n    }\n    println(\"-------------------------\")\n  }\n\n}\n"
  },
  {
    "path": "scala/src/main/scala/ch43_topology_sort/GraphTopology.scala",
    "content": "package ch43_topology_sort\n\nimport scala.collection.mutable\nimport scala.collection.mutable.ArrayBuffer\n\nclass GraphTopology(vertex: Int) {\n\n  //define the graph\n  val adjacency = new Array[mutable.MutableList[Int]](vertex)\n  for (i <- Range(0, vertex)) {\n    adjacency(i) = new mutable.MutableList[Int]()\n  }\n\n  def addEdge(startIndex: Int, targetIndex: Int) = {\n    adjacency(startIndex) += targetIndex\n  }\n\n  def topologySortByKahn(): Array[Int] = {\n    val seq = new mutable.ArrayBuffer[Int]()\n    //inDegrees contains all the inDegree for a given node\n    val inDegrees = new Array[Int](vertex)\n    for (i <- Range(0, vertex)) {\n      for (j <- adjacency(i).indices) {\n        val index = adjacency(i).get(j).get\n        inDegrees(index) += 1\n      }\n    }\n\n    val queue = new mutable.Queue[Int]()\n    for (i <- inDegrees.indices) {\n      if (inDegrees(i) == 0) {\n        // means there is no inDegree for this node,\n        // this could be the starting point of the dependency graph\n        queue += i\n      }\n    }\n\n    //start to navigating the graph from the starting point\n    while (queue.nonEmpty) {\n      val index = queue.dequeue()\n\n      //push to the result\n      seq += index\n\n      for (i <- adjacency(index).indices) {\n        val inDegreeIndex = adjacency(index).get(i).get\n        inDegrees(inDegreeIndex) -= 1\n\n        if (inDegrees(inDegreeIndex) == 0) {\n          queue += inDegreeIndex\n        }\n      }\n    }\n\n    seq.toArray\n  }\n\n  def topologySortByDFS(): Array[Int] = {\n    val inverseAdj = new Array[mutable.MutableList[Int]](vertex)\n    for (i <- Range(0, vertex)) {\n      inverseAdj(i) = new mutable.MutableList[Int]()\n    }\n\n    //build the inverse adj\n    for (i <- Range(0, vertex)) {\n      for (j <- adjacency(i).indices) {\n        val index = adjacency(i).get(j).get\n        inverseAdj(index) += i\n      }\n    }\n\n    val visited = new Array[Boolean](vertex)\n    val seq = new ArrayBuffer[Int]()\n    for (i <- Range(0, vertex)) {\n      if (!visited(i)) {\n        visited(i) = true\n        //call dfs\n        seq ++= dfs(i, inverseAdj, visited)\n      }\n    }\n\n    seq.toArray\n  }\n\n  def dfs(index: Int, inverseAdj: Array[mutable.MutableList[Int]], visited: Array[Boolean]): ArrayBuffer[Int] = {\n    val seq = new ArrayBuffer[Int]()\n\n    for (i <- inverseAdj(index).indices) {\n      val sourceIndex = inverseAdj(index).get(i).get\n      if (!visited(sourceIndex)) {\n        visited(sourceIndex) = true\n        seq ++= dfs(sourceIndex, inverseAdj, visited)\n      }\n    }\n    seq += index\n    seq\n  }\n}\n\n\n"
  },
  {
    "path": "scala/src/test/scala/ch05_array/ArrayDemoSpec.scala",
    "content": "package ch05_array\n\nimport org.scalatest.{FlatSpec, Matchers}\n\nclass ArrayDemoSpec extends FlatSpec with Matchers {\n\n  behavior of \"ArrayDemoTest\"\n\n  it should \"find value after insert\" in {\n    val demo = new ArrayDemo(10)\n    assert(demo.insert(0, 'a'))\n    assert(demo.insert(1, 'b'))\n    assert(demo.insert(2, 'c'))\n    assert(demo.insert(3, 'd'))\n    assert(demo.insert(4, 'e'))\n    demo.print should equal(\"abcde\")\n\n    assert(demo.insert(2, 'x'))\n    demo.print should equal(\"abxcde\")\n  }\n\n  it should \"not insert value if capacity is full \" in {\n    val demo = new ArrayDemo(10)\n    for (i <- Range(0, 10)) {\n      demo.insert(i, (i + 97).toChar)\n    }\n\n    assert(!demo.insert(1, 'a'))\n  }\n\n  it should \"not insert if index is negative\" in {\n    val demo = new ArrayDemo(10)\n    assert(!demo.insert(-1, 'a'))\n  }\n\n  it should \"not insert if index is exceed capacity\" in {\n    val demo = new ArrayDemo(10)\n    assert(!demo.insert(10, 'a'))\n    assert(!demo.insert(11, 'a'))\n  }\n\n  it should \"not find after delete\" in {\n    val demo = new ArrayDemo(10)\n    assert(demo.insert(0, 'a'))\n    assert(demo.insert(1, 'b'))\n    assert(demo.insert(2, 'c'))\n    assert(demo.insert(3, 'd'))\n    assert(demo.insert(4, 'e'))\n    demo.print should equal(\"abcde\")\n\n\n    assert(demo.insert(2, 'x'))\n    demo.print should equal(\"abxcde\")\n\n    demo.delete(2) should equal('x')\n    demo.find(2) should not equal ('x')\n    demo.print should equal(\"abcde\")\n  }\n\n  it should \"not delete for empty array\" in {\n    val demo = new ArrayDemo(10)\n    assertThrows[IllegalStateException] {\n      demo.delete(2) should equal('x')\n    }\n  }\n\n}\n"
  },
  {
    "path": "scala/src/test/scala/ch06_linkedlist/NodeTest.scala",
    "content": "package ch06_linkedlist\n\nimport org.scalatest.{FlatSpec, Matchers}\n\nclass NodeTest extends FlatSpec with Matchers {\n\n  behavior of \"NodeTest\"\n  it should \"create node with constructor\" in {\n    val node1 = new Node(1, None)\n    val node2 = new Node(2, Some(node1))\n    val node3 = new Node(2, Some(node2))\n\n    node1.next should be(None)\n    node1.next = Some(node3)\n    node1.next should equal(Some(node3))\n  }\n\n}\n"
  },
  {
    "path": "scala/src/test/scala/ch06_linkedlist/SinglyLinkedListTest.scala",
    "content": "package ch06_linkedlist\n\nimport org.scalatest.{FlatSpec, Matchers}\n\nclass SinglyLinkedListTest extends FlatSpec with Matchers {\n\n  behavior of \"SinglyLinkedListTest\"\n\n  it should \"insertToHead for new values\" in {\n    val list: SinglyLinkedList = new SinglyLinkedList()\n\n    list.insertToHead(0)\n    list.insertToHead(1)\n    list.insertToHead(2)\n    list.insertToHead(3)\n\n    list.mkString() should equal(\"3210\")\n\n  }\n\n  it should \"insertTail for new values\" in {\n    val list: SinglyLinkedList = new SinglyLinkedList()\n\n    list.insertTail(0)\n    list.insertTail(1)\n    list.insertTail(2)\n    list.insertTail(3)\n\n    list.mkString() should equal(\"0123\")\n\n  }\n\n  it should \"find by value\" in {\n    val list: SinglyLinkedList = new SinglyLinkedList()\n\n    list.insertTail(0)\n    list.insertTail(1)\n    list.insertTail(2)\n    list.insertTail(3)\n\n    list.mkString() should equal(\"0123\")\n    val node1 = list.findByValue(1).get\n    node1.next.get.data should equal(2)\n  }\n\n  it should \"insert after node and can find it back\" in {\n    val list: SinglyLinkedList = new SinglyLinkedList()\n\n    list.insertTail(0)\n    list.insertTail(1)\n    list.insertTail(3)\n    list.insertTail(4)\n\n    val node1 = list.findByValue(1).get\n    list.insertAfter(node1, 2)\n\n    list.mkString() should equal(\"01234\")\n  }\n\n  it should \"insert before node and can find it back\" in {\n    val list: SinglyLinkedList = new SinglyLinkedList()\n\n    list.insertTail(0)\n    list.insertTail(1)\n    list.insertTail(3)\n    list.insertTail(4)\n\n    val node3 = list.findByValue(3).get\n    list.insertBefore(node3, 2)\n\n    list.mkString() should equal(\"01234\")\n  }\n\n  it should \"delete desired node\" in {\n    val list: SinglyLinkedList = new SinglyLinkedList()\n\n    list.insertTail(0)\n    list.insertTail(1)\n    list.insertTail(2)\n    list.insertTail(3)\n\n    val node1 = list.findByValue(1).get\n    list.deleteByNode(node1)\n\n    list.mkString() should equal(\"023\")\n  }\n\n  it should \"delete head node \" in {\n    val list: SinglyLinkedList = new SinglyLinkedList()\n\n    list.insertTail(0)\n    list.insertTail(1)\n    list.insertTail(2)\n    list.insertTail(3)\n\n    val node0 = list.findByValue(0).get\n    list.deleteByNode(node0)\n\n    list.mkString() should equal(\"123\")\n  }\n\n  it should \"inverseLink to head node\" in {\n    val list: SinglyLinkedList = new SinglyLinkedList()\n\n    for (i <- 0 to 10) {\n      list.insertTail(i)\n    }\n\n    val node8 = list.findByValue(8)\n\n    val inverseNode = list.inverseLink(node8.get)\n\n    var node = inverseNode\n    val result = new StringBuilder\n    while (node.next.nonEmpty) {\n      result.append(node.data)\n      node = node.next.get\n    }\n    result.append(node.data)\n\n    result.mkString should equal(\"876543210\")\n  }\n\n  it should \"be isPalindrome for 0123210\" in {\n    val list: SinglyLinkedList = new SinglyLinkedList()\n    for (i <- 0 to 3) {\n      list.insertTail(i)\n    }\n\n    for (i <- 2 to 0 by -1) {\n      list.insertTail(i)\n    }\n\n    list.mkString() should equal(\"0123210\")\n    assert(list.isPalindrome())\n  }\n\n  it should \"be isPalindrome for 01233210\" in {\n    val list: SinglyLinkedList = new SinglyLinkedList()\n    for (i <- 0 to 3) {\n      list.insertTail(i)\n    }\n\n    for (i <- 3 to 0 by -1) {\n      list.insertTail(i)\n    }\n\n    list.mkString() should equal(\"01233210\")\n    assert(list.isPalindrome())\n  }\n\n  it should \"not be isPalindrome for 012332100\" in {\n    val list: SinglyLinkedList = new SinglyLinkedList()\n    for (i <- 0 to 3) {\n      list.insertTail(i)\n    }\n\n    for (i <- 3 to 0 by -1) {\n      list.insertTail(i)\n    }\n\n    list.insertTail(0)\n\n    list.mkString() should equal(\"012332100\")\n    assert(!list.isPalindrome())\n  }\n\n  it should \"be isPalindrome for large numbers\" in {\n    val list: SinglyLinkedList = new SinglyLinkedList()\n    val num = 5000\n    for (i <- 0 to num) {\n      list.insertTail(i)\n    }\n\n    for (i <- num to 0 by -1) {\n      list.insertTail(i)\n    }\n\n    assert(list.isPalindrome())\n  }\n\n}\n"
  },
  {
    "path": "scala/src/test/scala/ch07_linkedlist/LinkedListAlgoTest.scala",
    "content": "package ch07_linkedlist\n\nimport ch06_linkedlist.{Node, SinglyLinkedList}\nimport org.scalatest.{FlatSpec, Matchers}\n\nclass LinkedListAlgoTest extends FlatSpec with Matchers {\n\n  behavior of \"LinkedListAlgoTest\"\n\n  it should \"reverse a node for long linked list\" in {\n    val list = new SinglyLinkedList()\n\n    for (i <- 0 to 9) {\n      list.insertTail(i)\n    }\n    LinkedListAlgo.mkStringForChain(list.headOpt.get) should equal((0 to 9).toArray.mkString(\"\"))\n\n    val reverseNode = LinkedListAlgo.reverse(list.headOpt.get)\n\n    LinkedListAlgo.mkStringForChain(reverseNode) should equal((9 to 0 by -1).toArray.mkString(\"\"))\n  }\n\n  it should \"reverse a node for linked list with 1 node\" in {\n    val list = new SinglyLinkedList()\n\n    list.insertTail(100)\n\n    LinkedListAlgo.mkStringForChain(list.headOpt.get) should equal(\"100\")\n\n    val reverseNode = LinkedListAlgo.reverse(list.headOpt.get)\n\n    LinkedListAlgo.mkStringForChain(reverseNode) should equal(\"100\")\n  }\n\n  it should \"check circle for circled linked list\" in {\n\n    val node1 = new Node(1, None)\n    val node2 = new Node(2, None)\n    val node3 = new Node(3, None)\n    val node4 = new Node(4, None)\n    val node5 = new Node(5, None)\n    val node6 = new Node(6, None)\n\n    //1->2->3->4->5->6->3 it's a circle\n    //node1 is the head\n    node1.next = Some(node2)\n    node2.next = Some(node3)\n    node3.next = Some(node4)\n    node4.next = Some(node5)\n    node5.next = Some(node6)\n    node6.next = Some(node3)\n\n    assert(LinkedListAlgo.checkCircle(node1).isDefined)\n  }\n\n  it should \"check circle for none circled linked list\" in {\n\n    val node1 = new Node(1, None)\n    val node2 = new Node(2, None)\n    val node3 = new Node(3, None)\n    val node4 = new Node(4, None)\n    val node5 = new Node(5, None)\n    val node6 = new Node(6, None)\n\n    //1->2->3->4->5->6->null it's not a circle\n    //node1 is the head\n    node1.next = Some(node2)\n    node2.next = Some(node3)\n    node3.next = Some(node4)\n    node4.next = Some(node5)\n    node5.next = Some(node6)\n\n    assert(LinkedListAlgo.checkCircle(node1).isEmpty)\n  }\n\n  it should \"calculate circle length\" in {\n    val node1 = new Node(1, None)\n    val node2 = new Node(2, None)\n    val node3 = new Node(3, None)\n    val node4 = new Node(4, None)\n    val node5 = new Node(5, None)\n    val node6 = new Node(6, None)\n\n    //1->2->3->4->5->6->3 it's a circle\n    //node1 is the head\n    node1.next = Some(node2)\n    node2.next = Some(node3)\n    node3.next = Some(node4)\n    node4.next = Some(node5)\n    node5.next = Some(node6)\n    node6.next = Some(node3)\n\n    val node = LinkedListAlgo.checkCircle(node1).get\n\n    val length = LinkedListAlgo.calculateCircleLength(node)\n\n    length should equal(4)\n  }\n\n  it should \"find entrance of the  circle\" in {\n    val node1 = new Node(1, None)\n    val node2 = new Node(2, None)\n    val node3 = new Node(3, None)\n    val node4 = new Node(4, None)\n    val node5 = new Node(5, None)\n    val node6 = new Node(6, None)\n\n    //1->2->3->4->5->6->3 it's a circle\n    //node1 is the head\n    node1.next = Some(node2)\n    node2.next = Some(node3)\n    node3.next = Some(node4)\n    node4.next = Some(node5)\n    node5.next = Some(node6)\n    node6.next = Some(node3)\n\n    val node = LinkedListAlgo.findCircleEntrance(node1)\n\n    node.get.data should equal(3)\n  }\n\n  it should \"merge 2 sorted list into 1\" in {\n    val node1 = new Node(1, None)\n    val node2 = new Node(2, None)\n    val node3 = new Node(3, None)\n    val node4 = new Node(4, None)\n    val node5 = new Node(5, None)\n    val node6 = new Node(6, None)\n    //1->2->3->4->5->6->null\n    //node1 is the head\n    node1.next = Some(node2)\n    node2.next = Some(node3)\n    node3.next = Some(node4)\n    node4.next = Some(node5)\n    node5.next = Some(node6)\n\n    val nodeA = new Node(2, None)\n    val nodeB = new Node(4, None)\n    val nodeC = new Node(6, None)\n    val nodeD = new Node(8, None)\n    val nodeE = new Node(10, None)\n    //2->4->6->8->10->null\n    //nodeA is the head\n    nodeA.next = Some(nodeB)\n    nodeB.next = Some(nodeC)\n    nodeC.next = Some(nodeD)\n    nodeD.next = Some(nodeE)\n\n    val node = LinkedListAlgo.mergeSortedList(Some(node1), Some(nodeA))\n\n    assert(node.isDefined)\n\n    LinkedListAlgo.mkStringForChain(node.get) should equal(\"122344566810\")\n  }\n\n  it should \"merge empty list into exist list \" in {\n    val node1 = new Node(1, None)\n    val node2 = new Node(2, None)\n    val node3 = new Node(3, None)\n    val node4 = new Node(4, None)\n    val node5 = new Node(5, None)\n    val node6 = new Node(6, None)\n    //1->2->3->4->5->6->null\n    //node1 is the head\n    node1.next = Some(node2)\n    node2.next = Some(node3)\n    node3.next = Some(node4)\n    node4.next = Some(node5)\n    node5.next = Some(node6)\n\n    val nodeA = LinkedListAlgo.mergeSortedList(Some(node1), None)\n    assert(nodeA.isDefined)\n    LinkedListAlgo.mkStringForChain(nodeA.get) should equal(\"123456\")\n\n    val nodeB = LinkedListAlgo.mergeSortedList(None, Some(node1))\n    assert(nodeB.isDefined)\n    LinkedListAlgo.mkStringForChain(nodeB.get) should equal(\"123456\")\n  }\n\n  it should \"merge 2 empty lists \" in {\n    val node = LinkedListAlgo.mergeSortedList(None, None)\n    assert(node.isEmpty)\n  }\n\n  it should \"delete last 2nd element \" in {\n    val list = new SinglyLinkedList()\n\n    for (i <- 0 to 9) {\n      list.insertTail(i)\n    }\n    LinkedListAlgo.mkStringForChain(list.headOpt.get) should equal((0 to 9).toArray.mkString(\"\"))\n\n    val node = LinkedListAlgo.deleteLastKthNode(list.headOpt, 2)\n    assert(node.isDefined)\n    LinkedListAlgo.mkStringForChain(node.get) should equal(\"012345679\")\n  }\n\n  it should \"delete last element \" in {\n    val list = new SinglyLinkedList()\n\n    for (i <- 0 to 9) {\n      list.insertTail(i)\n    }\n    LinkedListAlgo.mkStringForChain(list.headOpt.get) should equal((0 to 9).toArray.mkString(\"\"))\n\n    val node = LinkedListAlgo.deleteLastKthNode(list.headOpt, 1)\n    assert(node.isDefined)\n    LinkedListAlgo.mkStringForChain(node.get) should equal(\"012345678\")\n  }\n\n  it should \"delete first element \" in {\n    val list = new SinglyLinkedList()\n\n    for (i <- 0 to 9) {\n      list.insertTail(i)\n    }\n    LinkedListAlgo.mkStringForChain(list.headOpt.get) should equal((0 to 9).toArray.mkString(\"\"))\n\n    val node = LinkedListAlgo.deleteLastKthNode(list.headOpt, 10)\n    assert(node.isDefined)\n    LinkedListAlgo.mkStringForChain(node.get) should equal(\"123456789\")\n  }\n\n  it should \"delete firs only element \" in {\n    val list = new SinglyLinkedList()\n    list.insertTail(0)\n\n    val node = LinkedListAlgo.deleteLastKthNode(list.headOpt, 1)\n    assert(node.isEmpty)\n  }\n\n  it should \"throw exception if k < 0 \" in {\n    val list = new SinglyLinkedList()\n\n    for (i <- 0 to 9) {\n      list.insertTail(i)\n    }\n    assertThrows[IllegalArgumentException] {\n      LinkedListAlgo.deleteLastKthNode(list.headOpt, -1)\n    }\n  }\n\n  it should \"throw exception if k greater than list length \" in {\n    val list = new SinglyLinkedList()\n\n    for (i <- 0 to 9) {\n      list.insertTail(i)\n    }\n    assertThrows[IllegalArgumentException] {\n      LinkedListAlgo.deleteLastKthNode(list.headOpt, 15)\n    }\n  }\n\n}\n"
  },
  {
    "path": "scala/src/test/scala/ch08_stack/BrowserDemoTest.scala",
    "content": "package ch08_stack\n\nimport org.scalatest.{FlatSpec, Matchers}\n\nclass BrowserDemoTest extends FlatSpec with Matchers {\n\n  behavior of \"BrowserDemoTest\"\n\n  it should \"canGoBack\" in {\n    val browser = new BrowserDemo()\n    assert(!browser.canGoBack())\n    browser.open(\"a\")\n    assert(!browser.canGoBack())\n    browser.open(\"b\")\n    assert(browser.canGoBack())\n    browser.currentPageOpt.get should equal(\"b\")\n  }\n\n  it should \"goBack\" in {\n    val browser = new BrowserDemo()\n    browser.open(\"a\")\n    browser.open(\"b\")\n    browser.currentPageOpt.get should equal(\"b\")\n    assert(browser.canGoBack())\n    browser.goBack()\n    assert(!browser.canGoBack())\n    browser.currentPageOpt.get should equal(\"a\")\n  }\n\n  it should \"canGoForward\" in {\n    val browser = new BrowserDemo()\n    browser.open(\"a\")\n    browser.open(\"b\")\n    browser.currentPageOpt.get should equal(\"b\")\n    assert(browser.canGoBack())\n    browser.goBack()\n    assert(!browser.canGoBack())\n    browser.currentPageOpt.get should equal(\"a\")\n    assert(browser.canGoForward())\n  }\n\n  it should \"goForward\" in {\n    val browser = new BrowserDemo()\n    browser.open(\"a\")\n    browser.open(\"b\")\n    browser.currentPageOpt.get should equal(\"b\")\n    assert(browser.canGoBack())\n    browser.goBack()\n    assert(!browser.canGoBack())\n    browser.currentPageOpt.get should equal(\"a\")\n    assert(browser.canGoForward())\n    browser.goForward()\n    browser.currentPageOpt.get should equal(\"b\")\n  }\n\n  it should \"open new page to clear forward stack\" in {\n    val browser = new BrowserDemo()\n    browser.open(\"a\")\n    browser.open(\"b\")\n    browser.currentPageOpt.get should equal(\"b\")\n    assert(browser.canGoBack())\n    browser.goBack()\n    assert(!browser.canGoBack())\n    browser.currentPageOpt.get should equal(\"a\")\n    assert(browser.canGoForward())\n    browser.open(\"c\")\n    assert(!browser.canGoForward())\n    browser.goBack()\n    browser.currentPageOpt.get should equal(\"a\")\n  }\n\n\n\n}\n"
  },
  {
    "path": "scala/src/test/scala/ch08_stack/StackDemoTest.scala",
    "content": "package ch08_stack\n\nimport org.scalatest.{FlatSpec, Matchers}\n\nclass StackDemoTest extends FlatSpec with Matchers {\n\n  behavior of \"StackDemoTest\"\n\n  it should \"push/pop should be FILO\" in {\n    val stack = new StackDemo[String]\n    val num = 100\n    for (i <- 1 to num) {\n      stack.push(i.toString)\n    }\n\n    for (i <- num to 1 by -1) {\n      stack.pop().get.data should equal(i.toString)\n    }\n  }\n\n  it should \"pop should also work for empty stack\" in {\n    val stack = new StackDemo[Int]\n    val num = 100\n    for (i <- num to 1 by -1) {\n      assert(stack.pop().isEmpty)\n    }\n  }\n\n}\n"
  },
  {
    "path": "scala/src/test/scala/ch09_queue/ArrayQueueTest.scala",
    "content": "package ch09_queue\n\nclass ArrayQueueTest extends DemoQueueTest {\n\n  override def getInstance() = new ArrayQueue[Int](15)\n}\n"
  },
  {
    "path": "scala/src/test/scala/ch09_queue/CircularQueueTest.scala",
    "content": "package ch09_queue\n\nclass CircularQueueTest extends DemoQueueTest {\n\n  override def getInstance(): DemoQueue[Int] = new CircularQueue[Int](15)\n}\n"
  },
  {
    "path": "scala/src/test/scala/ch09_queue/DemoQueueTest.scala",
    "content": "package ch09_queue\n\nimport org.scalatest.{FlatSpec, Matchers}\n\nabstract class DemoQueueTest extends FlatSpec with Matchers {\n\n  def getInstance(): DemoQueue[Int]\n\n  behavior of \"test\"\n\n  it should \"dequeue nothing for empty queue\" in {\n    val queue = getInstance()\n    assert(queue.dequeue().isEmpty)\n  }\n\n  it should \"enqueue/dequeue should be FIFO\" in {\n    val queue = getInstance()\n    for (i <- Range(0, 10)) {\n      queue.enqueue(i)\n    }\n\n    queue.size should equal(10)\n\n    for (i <- Range(0, 10)) {\n      queue.dequeue().get should equal(i)\n    }\n  }\n}\n"
  },
  {
    "path": "scala/src/test/scala/ch09_queue/DynamicArrayQueueTest.scala",
    "content": "package ch09_queue\n\nclass DynamicArrayQueueTest extends DemoQueueTest {\n\n  override def getInstance(): DemoQueue[Int] = new DynamicArrayQueue[Int](15)\n\n  it should \"copy data when tail reach the end of the queue\" in {\n    val queue = getInstance()\n    for (i <- Range(0, 15)) {\n      queue.enqueue(i)\n    }\n    queue.size should equal(15)\n    queue.dequeue().get should equal(0)\n\n    //enqueue another one\n    queue.enqueue(30)\n    queue.size should equal(15)\n\n  }\n}\n"
  },
  {
    "path": "scala/src/test/scala/ch09_queue/LinkedListQueueTest.scala",
    "content": "package ch09_queue\n\nclass LinkedListQueueTest extends DemoQueueTest {\n\n  override def getInstance() = new LinkListQueue[Int]\n\n}\n"
  },
  {
    "path": "scala/src/test/scala/ch10_recursive/RecursiveDemoTest.scala",
    "content": "package ch10_recursive\n\nimport org.scalatest.{FlatSpec, Matchers}\n\nclass RecursiveDemoTest extends FlatSpec with Matchers {\n\n  behavior of \"RecursiveDemoTest\"\n\n  it should \"calculateStepWays\" in {\n    RecursiveDemo.calculateStepWays(1) should equal(1)\n    RecursiveDemo.calculateStepWays(2) should equal(2)\n    RecursiveDemo.calculateStepWays(3) should equal(3)\n    RecursiveDemo.calculateStepWays(4) should equal(5)\n    RecursiveDemo.calculateStepWays(5) should equal(8)\n  }\n\n}\n"
  },
  {
    "path": "scala/src/test/scala/ch11_sorts/SortsTest.scala",
    "content": "package ch11_sorts\n\nimport ch12_sorts.{MergeSort, QuickSort}\nimport ch28_heap.Heap\nimport org.scalatest.{FlatSpec, Matchers}\n\nimport scala.util.Random\n\nclass SortsTest extends FlatSpec with Matchers {\n\n  behavior of \"SortsTest in ch11\"\n\n  it should \"bubbleSort int arrays\" in {\n    var array = Array(4, 5, 6, 3, 2, 1)\n    array = Sorts.bubbleSort(array)\n    array.mkString(\"\") should equal(\"123456\")\n\n    array = Array(4)\n    array = Sorts.bubbleSort(array)\n    array.mkString(\"\") should equal(\"4\")\n  }\n\n  it should \"insertSort int arrays\" in {\n    var array = Array(4, 5, 6, 1, 3, 2)\n    array = Sorts.insertSort(array)\n    array.mkString(\"\") should equal(\"123456\")\n\n    array = Array(4)\n    array = Sorts.insertSort(array)\n    array.mkString(\"\") should equal(\"4\")\n  }\n\n  it should \"selectionSort int arrays\" in {\n    var array = Array(4, 5, 6, 1, 3, 2)\n    array = Sorts.insertSort(array)\n    array.mkString(\"\") should equal(\"123456\")\n\n    array = Array(4)\n    array = Sorts.insertSort(array)\n    array.mkString(\"\") should equal(\"4\")\n  }\n\n\n  it should \"compare the sort algo\" in {\n    val length = 50000\n    val array = new Array[Int](length)\n    val rnd = new Random()\n    for (i <- Range(0, length)) {\n      array(i) = rnd.nextInt()\n    }\n\n    timed(\"bubbleSort\", Sorts.bubbleSort, array.clone())\n    timed(\"insertSort\", Sorts.insertSort, array.clone())\n    timed(\"selectionSort\", Sorts.selectionSort, array.clone())\n    timed(\"mergeSort\", MergeSort.mergeSort, array.clone())\n    timed(\"quickSort\", QuickSort.quickSort, array.clone())\n    timed(\"heapSort\", Heap.heapSort, array.clone())\n  }\n\n  def reportElapsed(name: String, time: Long): Unit = println(name + \" takes in \" + time + \"ms\")\n\n  def timed(name: String, f: (Array[Int]) => Unit, array: Array[Int]): Unit = {\n    val start = System.currentTimeMillis()\n    try f(array) finally reportElapsed(name, System.currentTimeMillis - start)\n  }\n}\n"
  },
  {
    "path": "scala/src/test/scala/ch12_sorts/MergeSortTest.scala",
    "content": "package ch12_sorts\n\n\nimport org.scalatest.{FlatSpec, Matchers}\n\nclass MergeSortTest extends FlatSpec with Matchers {\n  behavior of \"SortsTest in ch12\"\n\n  it should \"mergeSort int arrays\" in {\n    var array = Array(4, 5, 6, 3, 2, 1)\n    array = MergeSort.mergeSort(array)\n    array.mkString(\"\") should equal(\"123456\")\n\n    array = Array(4)\n    array = MergeSort.mergeSort(array)\n    array.mkString(\"\") should equal(\"4\")\n\n    array = Array(4, 2)\n    array = MergeSort.mergeSort(array)\n    array.mkString(\"\") should equal(\"24\")\n  }\n}\n"
  },
  {
    "path": "scala/src/test/scala/ch12_sorts/QuickSortTest.scala",
    "content": "package ch12_sorts\n\nimport org.scalatest.{FlatSpec, Matchers}\n\nclass QuickSortTest extends FlatSpec with Matchers {\n\n  behavior of \"QuickSortTest\"\n\n  it should \"quickSort\" in {\n    var array = Array(4, 5, 6, 3, 2, 1)\n    array = QuickSort.quickSort(array)\n    array.mkString(\"\") should equal(\"123456\")\n\n    array = Array(4)\n    array = QuickSort.quickSort(array)\n    array.mkString(\"\") should equal(\"4\")\n\n    array = Array(4, 2)\n    array = QuickSort.quickSort(array)\n    array.mkString(\"\") should equal(\"24\")\n  }\n\n  it should \"find the Kth element in the array\" in {\n    val array = Array(4, 2, 5, 12, 3)\n\n    QuickSort.findKthElement(array, 3) should equal(4)\n    QuickSort.findKthElement(array, 5) should equal(12)\n    QuickSort.findKthElement(array, 1) should equal(2)\n  }\n\n}\n"
  },
  {
    "path": "scala/src/test/scala/ch15_bsearch/BSearchRecursiveTest.scala",
    "content": "package ch15_bsearch\n\nimport ch12_sorts.QuickSort\nimport org.scalatest.{FlatSpec, Matchers}\n\nimport scala.util.Random\n\nclass BSearchRecursiveTest extends FlatSpec with Matchers {\n\n  behavior of \"BSearchRecursiveTest\"\n\n  it should \"search with exist value\" in {\n    val length = 50000\n    val array = new Array[Int](length)\n    val rnd = new Random()\n    for (i <- Range(0, length)) {\n      array(i) = rnd.nextInt()\n    }\n\n    val target = array(2698)\n\n    BSearchRecursive.search(QuickSort.quickSort(array), target) should be > -1\n  }\n\n}\n"
  },
  {
    "path": "scala/src/test/scala/ch15_bsearch/BSearchTest.scala",
    "content": "package ch15_bsearch\n\nimport ch12_sorts.QuickSort\nimport org.scalatest.{FlatSpec, Matchers}\n\nimport scala.util.Random\n\nclass BSearchTest extends FlatSpec with Matchers {\n\n  behavior of \"BSearchTest\"\n\n  it should \"search with exist value\" in {\n    val length = 50000\n    val array = new Array[Int](length)\n    val rnd = new Random()\n    for (i <- Range(0, length)) {\n      array(i) = rnd.nextInt()\n    }\n\n    val target = array(2698)\n\n    BSearch.search(QuickSort.quickSort(array), target) should be > -1\n  }\n\n  it should \"calculate sqrt value -1 \" in {\n    val x = 4\n    val precision = 0.000001\n    BSearch.sqrt(x, precision) should equal(2.0)\n  }\n\n  it should \"calculate sqrt value -2 \" in {\n    val x = 0.04\n    val precision = 0.000001\n    BSearch.sqrt(x, precision) should equal(0.2 +- precision)\n  }\n\n}\n"
  },
  {
    "path": "scala/src/test/scala/ch16_bsearch/BSearchTest.scala",
    "content": "package ch16_bsearch\n\nimport org.scalatest.{FlatSpec, Matchers}\n\nclass BSearchTest extends FlatSpec with Matchers {\n\n  behavior of \"BSearchTest\"\n\n  it should \"findFirstValue\" in {\n    val items = Array(1, 3, 4, 5, 6, 8, 8, 8, 11, 18)\n    BSearch.findFirstValue(items, 8) should equal(5)\n  }\n\n  it should \"findLastValue\" in {\n    val items = Array(1, 3, 4, 5, 6, 8, 8, 8, 11, 18)\n    BSearch.findLastValue(items, 8) should equal(7)\n  }\n\n  it should \"findFirstGreaterThan\" in {\n    val items = Array(1, 3, 4, 5, 6, 8, 8, 8, 11, 18)\n    BSearch.findFirstGreaterThan(items, 2) should equal(1)\n    BSearch.findFirstGreaterThan(items, 8) should equal(5)\n  }\n\n  it should \"findLastSmallerThan\" in {\n    val items = Array(1, 3, 4, 5, 6, 8, 8, 8, 11, 18)\n    BSearch.findLastSmallerThan(items, 2) should equal(0)\n    BSearch.findLastSmallerThan(items, 8) should equal(7)\n  }\n\n}\n"
  },
  {
    "path": "scala/src/test/scala/ch17_skip_list/SkipListTest.scala",
    "content": "package ch17_skip_list\n\nimport org.scalatest.{FlatSpec, Matchers}\n\nimport scala.util.Random\n\nclass SkipListTest extends FlatSpec with Matchers {\n\n  behavior of \"SkipListTest\"\n\n  it should \"insert skip list\" in {\n    val list = new SkipList()\n    for (i <- Range(0, 10)) {\n      list.insert(i)\n    }\n\n    list.mkString() should equal(\"0123456789\")\n  }\n\n  it should \"delete skip list\" in {\n    val list = new SkipList()\n    for (i <- Range(0, 10)) {\n      list.insert(i)\n    }\n\n    list.delete(5)\n    list.mkString() should equal(\"012346789\")\n  }\n\n  it should \"find value in skip list\" in {\n    val list = new SkipList()\n    val length = 5000\n    val array = new Array[Int](length)\n    val rnd = new Random()\n    for (i <- Range(0, length)) {\n      array(i) = rnd.nextInt(length)\n      list.insert(array(i))\n    }\n\n    assert(list.find(array(rnd.nextInt(length - 1))).isDefined)\n    assert(list.find(array(rnd.nextInt(length - 1)) + length + 1).isEmpty)\n\n  }\n}\n"
  },
  {
    "path": "scala/src/test/scala/ch20_linked_hash_map/LRUCacheTest.scala",
    "content": "package ch20_linked_hash_map\n\nimport org.scalatest.{FlatSpec, Matchers}\n\nclass LRUCacheTest extends FlatSpec with Matchers {\n\n  behavior of \"LRUCacheTest\"\n\n  it should \"put data and get back\" in {\n    val cache = new LRUCache[Int, Int](2)\n    cache.put(1, 1)\n    cache.put(2, 2)\n    cache.get(1) should equal(Some(1)) // returns 1\n    cache.put(3, 3) // evicts key 2\n    cache.get(2) should equal(None) //should not find\n    cache.put(4, 4) // evicts key 1\n    cache.get(1) should equal(None) //should not find\n    cache.get(3) should equal(Some(3)) // returns 3\n    cache.get(4) should equal(Some(4)) // returns 4\n  }\n\n}\n"
  },
  {
    "path": "scala/src/test/scala/ch23_binary_tree/BinaryTreeTest.scala",
    "content": "package ch23_binary_tree\n\nimport org.scalatest.{BeforeAndAfterEach, FlatSpec, Matchers}\n\nclass BinaryTreeTest extends FlatSpec with BeforeAndAfterEach with Matchers {\n\n  /*\n   *         A\n   *        / \\\n   *       B   C\n   *      / \\ / \\\n   *     D  E F  G\n   *\n   */\n  var root: Option[Node[String]] = None\n  val tree = new BinaryTree[String]\n\n  override def beforeEach() {\n    val D = new Node[String](\"D\", None, None)\n    val E = new Node[String](\"E\", None, None)\n    val B = new Node[String](\"B\", Some(D), Some(E))\n    val F = new Node[String](\"F\", None, None)\n    val G = new Node[String](\"G\", None, None)\n    val C = new Node[String](\"C\", Some(F), Some(G))\n    val A = new Node[String](\"A\", Some(B), Some(C))\n    root = Some(A)\n  }\n\n  override protected def afterEach(): Unit = {\n    root = None\n  }\n\n  behavior of \"BinaryTreeTest\"\n\n  it should \"postOrder\" in {\n    tree.postOrder(root) should equal(\"DEBFGCA\")\n  }\n\n  it should \"inOrder\" in {\n    tree.inOrder(root) should equal(\"DBEAFCG\")\n  }\n\n  it should \"preOrder\" in {\n    tree.preOrder(root) should equal(\"ABDECFG\")\n  }\n\n  it should \"levelOrder\" in {\n    tree.levelOrder(root) should equal(\"ABCDEFG\")\n  }\n}\n"
  },
  {
    "path": "scala/src/test/scala/ch24_binary_search_tree/BinarySearchTreeTest.scala",
    "content": "package ch24_binary_search_tree\n\nimport org.scalatest.{FlatSpec, Matchers}\n\nclass BinarySearchTreeTest extends FlatSpec with Matchers {\n\n  /*\n   *                 33\n   *              /      \\\n   *             17       50\n   *           /    \\   /   \\\n   *          13    18 34   58\n   *           \\     \\      / \\\n   *            16   25    51  66\n   *                /  \\    \\\n   *               19  27    55\n   *\n   */\n  behavior of \"BinarySearchTreeTest\"\n\n  it should \"insert\" in {\n    val tree = new BinarySearchTree(None)\n    val nums = Array(33, 17, 50, 13, 18, 34, 58, 16, 25, 51, 66, 19, 27, 55)\n    nums.foreach(tree.insert)\n    tree.inOrder(tree.root) should equal(nums.sorted.mkString(\"\"))\n    tree.preOrder(tree.root) should equal(\"3317131618251927503458515566\")\n    tree.postOrder(tree.root) should equal(\"1613192725181734555166585033\")\n    tree.levelOrder(tree.root) should equal(\"3317501318345816255166192755\")\n  }\n\n  it should \"find \" in {\n    val tree = new BinarySearchTree(None)\n    val nums = Array(33, 17, 50, 13, 18, 34, 58, 16, 25, 51, 66, 19, 27, 55)\n    nums.foreach(tree.insert)\n\n    nums.foreach(num => {\n      assert(tree.find(num).isDefined)\n      tree.find(num).get.data should equal(num)\n    })\n    assert(tree.find(100).isEmpty)\n  }\n\n  it should \"delete\" in {\n    val tree = new BinarySearchTree(None)\n    val nums = Array(33, 17, 50, 13, 18, 34, 58, 16, 25, 51, 66, 19, 27, 55)\n    nums.foreach(tree.insert)\n    tree.delete(13)\n    tree.inOrder(tree.root) should equal(nums.sorted.tail.mkString(\"\"))\n    tree.delete(18)\n    tree.inOrder(tree.root) should equal(\"1617\" + nums.sorted.slice(4, nums.size).mkString(\"\"))\n    tree.delete(66)\n    tree.inOrder(tree.root) should equal(\"1617\" + nums.sorted.slice(4, nums.size - 1).mkString(\"\"))\n  }\n\n  it should \"calc height of a tree -1\" in {\n    val tree = new BinarySearchTree(None)\n    val nums = Array(33, 17, 50, 13, 18, 34, 58, 16, 25, 51, 66, 19, 27, 55)\n    nums.foreach(tree.insert)\n    tree.height() should equal(5)\n  }\n\n  it should \"calc height of a tree -2\" in {\n    val tree = new BinarySearchTree(None)\n    val nums = Array(33, 17, 50, 13, 18, 34, 88).sorted\n    nums.foreach(tree.insert)\n    tree.height() should equal(7)\n  }\n\n  it should \"calc height of a tree -3\" in {\n    val tree = new BinarySearchTree(None)\n    val nums = Array(33).sorted\n    nums.foreach(tree.insert)\n    tree.height() should equal(1)\n  }\n\n}\n"
  },
  {
    "path": "scala/src/test/scala/ch28_heap/HeapTest.scala",
    "content": "package ch28_heap\n\nimport org.scalatest.{FlatSpec, Matchers}\n\nclass HeapTest extends FlatSpec with Matchers {\n\n  behavior of \"HeapTest\"\n\n  it should \"insert and removeMax\" in {\n    val nums = Array(33, 27, 21, 16, 13, 15, 19, 5, 6, 7, 8, 1, 2, 12)\n    val heap = new Heap(nums.length + 1)\n    nums.foreach(heap.insert)\n\n    heap.removeMax() should equal(33)\n    heap.removeMax() should equal(27)\n  }\n\n  it should \"build heap from array - bottom up\" in {\n    val nums = Array(33, 27, 21, 16, 13, 15, 19, 5, 6, 7, 8, 1, 2, 12)\n    val heap = new Heap(nums, true)\n\n    heap.removeMax() should equal(33)\n    heap.removeMax() should equal(27)\n  }\n\n  it should \"build heap from array - top down\" in {\n    val nums = Array(33, 27, 21, 16, 13, 15, 19, 5, 6, 7, 8, 1, 2, 12)\n    val heap = new Heap(nums, false)\n\n    heap.removeMax() should equal(33)\n    heap.removeMax() should equal(27)\n  }\n\n  it should \"sort array\" in {\n    val nums = Array(33, 27, 21, 16, 13, 15, 19, 5, 6, 7, 8, 1, 2, 12)\n    val result = Heap.heapSort(nums)\n\n    result.mkString(\"\") should equal(nums.sorted.mkString(\"\"))\n  }\n\n}\n"
  },
  {
    "path": "scala/src/test/scala/ch29_heap_solutions/FileMergerTest.scala",
    "content": "package ch29_heap_solutions\n\nimport java.io.{BufferedWriter, File, FileWriter}\n\nimport org.scalatest.{FlatSpec, Matchers}\n\nimport scala.io.Source\nimport scala.util.Random\n\nclass FileMergerTest extends FlatSpec with Matchers {\n\n  behavior of \"FileMergerTest\"\n\n  it should \"mergeFiles\" in {\n    val num = 10\n    val contentCount = 10\n    val random = Random.alphanumeric\n    val files = new Array[File](num)\n    for (i <- Range(0, num)) {\n      val file = File.createTempFile(i + \"-small\", \".txt\")\n      files(i) = file\n      val writer = new BufferedWriter(new FileWriter(file))\n      val content = random.take((i + 1) * contentCount).toArray.slice(i * contentCount, (i + 1) * contentCount)\n\n      writer.write(content.sorted)\n      writer.flush()\n      writer.close()\n    }\n    println(\"small files below\")\n    files.foreach(printFile)\n\n    val mergedFile = FileMerger.mergeFiles(files.toList)\n\n    val raw = Source.fromFile(mergedFile).toArray\n    raw should equal(raw.sorted)\n    raw.length should equal(num * contentCount)\n\n    println(\"\")\n    println(\"merged file below\")\n    printFile(mergedFile)\n\n    //clean up\n    files.foreach(_.delete())\n    mergedFile.delete()\n\n  }\n\n  def printFile(file: File): Unit = {\n    val source = Source.fromFile(file)\n    source.getLines().foreach(println)\n  }\n\n}\n"
  },
  {
    "path": "scala/src/test/scala/ch29_heap_solutions/MiddleNumberKeeperTest.scala",
    "content": "package ch29_heap_solutions\n\nimport org.scalatest.{FlatSpec, Matchers}\n\nclass MiddleNumberKeeperTest extends FlatSpec with Matchers {\n\n  behavior of \"MiddleNumberKeeperTest\"\n\n  it should \"get middle of the array\" in {\n    val numKeeper = new MiddleNumberKeeper()\n    for (i <- Range(0, 10)) {\n      numKeeper.put(i)\n    }\n\n    numKeeper.get().get should equal(4)\n  }\n\n  it should \"get 90% position of the array\" in {\n    val numKeeper = new MiddleNumberKeeper(0.9)\n    for (i <- Range(0, 9)) {\n      numKeeper.put(i)\n    }\n    numKeeper.put(9)\n\n    numKeeper.get().get should equal(8)\n  }\n\n}\n"
  },
  {
    "path": "scala/src/test/scala/ch29_heap_solutions/TopKItemsKeeperTest.scala",
    "content": "package ch29_heap_solutions\n\nimport org.scalatest.{FlatSpec, Matchers}\n\nimport scala.util.Random\n\nclass TopKItemsKeeperTest extends FlatSpec with Matchers {\n\n  behavior of \"TopKItemsKeeperTest\"\n\n  it should \"put and get top K from the keeper\" in {\n    val length = 50\n    val k = 5\n    val topKItemsKeeper = new TopKItemsKeeper(k)\n    val nums = new Array[Int](length)\n    for (i <- Range(0, length)) {\n      nums(i) = Random.nextInt\n    }\n\n    nums.foreach(topKItemsKeeper.put)\n    val ordering = scala.math.Ordering.Int.reverse\n    topKItemsKeeper.get().toArray.sorted(ordering) should equal(nums.sorted(ordering).slice(0, k))\n  }\n\n}\n"
  },
  {
    "path": "scala/src/test/scala/ch31_graph/GraphTest.scala",
    "content": "package ch31_graph\n\nimport org.scalatest.{FlatSpec, Matchers}\n\nclass GraphTest extends FlatSpec with Matchers {\n\n  /*\n        0 - 1 - 2\n        |   |   |\n        3 - 4 - 5\n            |   |\n            6 - 7\n   */\n\n  behavior of \"GraphTest\"\n\n  def initGraph: Graph = {\n    val num = 8\n    val vertex = new Array[String](num)\n    for (i <- Range(0, num)) {\n      vertex(i) = i.toString\n    }\n    val graph = new Graph(vertex)\n\n    graph.addEdge(\"0\", \"1\")\n    graph.addEdge(\"1\", \"2\")\n    graph.addEdge(\"0\", \"3\")\n    graph.addEdge(\"1\", \"4\")\n    graph.addEdge(\"2\", \"5\")\n    graph.addEdge(\"3\", \"4\")\n    graph.addEdge(\"4\", \"5\")\n    graph.addEdge(\"4\", \"6\")\n    graph.addEdge(\"5\", \"7\")\n    graph.addEdge(\"6\", \"7\")\n    graph\n  }\n\n  it should \"construct the graph\" in {\n    val graph: Graph = initGraph\n\n    graph.getEdges(\"0\").sorted should equal(Array(\"1\", \"3\"))\n    graph.getEdges(\"1\").sorted should equal(Array(\"0\", \"2\", \"4\"))\n    graph.getEdges(\"2\").sorted should equal(Array(\"1\", \"5\"))\n    graph.getEdges(\"3\").sorted should equal(Array(\"0\", \"4\"))\n    graph.getEdges(\"4\").sorted should equal(Array(\"1\", \"3\", \"5\", \"6\"))\n    graph.getEdges(\"5\").sorted should equal(Array(\"2\", \"4\", \"7\"))\n    graph.getEdges(\"6\").sorted should equal(Array(\"4\", \"7\"))\n    graph.getEdges(\"7\").sorted should equal(Array(\"5\", \"6\"))\n  }\n\n  it should \"do breath first search in graph\" in {\n    val graph: Graph = initGraph\n    graph.breathFirstSearch(\"0\", \"4\").get should equal(Array(\"0\", \"1\", \"3\", \"2\", \"4\"))\n    graph.breathFirstSearch(\"1\", \"5\").get should equal(Array(\"1\", \"0\", \"2\", \"4\", \"3\", \"5\"))\n    assert(graph.breathFirstSearch(\"1\", \"8\").isEmpty)\n  }\n\n  it should \"do depth first search in graph\" in {\n    val graph: Graph = initGraph\n    graph.depthFirstSearch(\"0\", \"4\").get should equal(Array(\"0\", \"1\", \"2\", \"5\", \"4\"))\n    graph.depthFirstSearch(\"1\", \"5\").get should equal(Array(\"1\", \"0\", \"3\", \"4\", \"5\"))\n    assert(graph.depthFirstSearch(\"1\", \"8\").isEmpty)\n  }\n}\n"
  },
  {
    "path": "scala/src/test/scala/ch32_matching/BruteForceTest.scala",
    "content": "package ch32_matching\n\nimport org.scalatest.{FlatSpec, Matchers}\n\nimport scala.util.Random\n\nclass BruteForceTest extends FlatSpec with Matchers {\n\n  behavior of \"BruteForceTest\"\n\n  it should \"find firstIndexOf a sub string\" in {\n    val random = Random.alphanumeric\n    val main = random.take(1000).toArray\n    val index = Random.nextInt(950)\n    val sub = random.take(1000).toArray.slice(index, index + 50)\n\n    BruteForce.firstIndexOf(main, sub) should equal(index)\n  }\n\n}\n"
  },
  {
    "path": "scala/src/test/scala/ch32_matching/RabinKarpTest.scala",
    "content": "package ch32_matching\n\nimport org.scalatest.{FlatSpec, Matchers}\n\nimport scala.util.Random\n\nclass RabinKarpTest extends FlatSpec with Matchers {\n\n  it should \"find firstIndexOf a sub string\" in {\n    val random = Random.alphanumeric\n    val main = random.take(1000).toArray\n    val index = Random.nextInt(950)\n    val sub = random.take(1000).toArray.slice(index, index + 50)\n\n    RabinKarp.firstIndexOf(main, sub) should equal(index)\n  }\n}\n"
  },
  {
    "path": "scala/src/test/scala/ch35_tire_tree/TrieTreeTest.scala",
    "content": "package ch35_tire_tree\n\nimport org.scalatest.{FlatSpec, Matchers}\n\nclass TrieTreeTest extends FlatSpec with Matchers {\n\n  behavior of \"TrieTreeTest\"\n\n  it should \"insert data into the tree and find it back\" in {\n    val texts = Array(\"how\", \"hi\", \"her\", \"hello\", \"so\", \"see\")\n    val tree = new TrieTree()\n    for (i <- texts.indices) {\n      tree.insert(texts(i).toCharArray)\n    }\n\n    for (i <- texts.indices) {\n      assert(tree.find(texts(i).toCharArray))\n    }\n  }\n\n  it should \"find prefix\" in {\n    val texts = Array(\"how\", \"hi\", \"her\", \"hello\", \"so\", \"see\")\n    val tree = new TrieTree()\n    for (i <- texts.indices) {\n      tree.insert(texts(i).toCharArray)\n    }\n\n    for (i <- texts.indices) {\n      assert(tree.prefix(texts(i).toCharArray.slice(0, 1)))\n    }\n\n    assert(!tree.prefix(\"howlo\".toCharArray))\n    assert(!tree.prefix(\"aaa\".toCharArray))\n  }\n\n  it should \"give suggestion\" in {\n    val texts = Array(\"how\", \"hi\", \"her\", \"hello\", \"so\", \"see\")\n    val tree = new TrieTree()\n    for (i <- texts.indices) {\n      tree.insert(texts(i).toCharArray)\n    }\n\n    tree.suggestion(\"he\".toCharArray).get should contain(\"her\")\n    tree.suggestion(\"he\".toCharArray).get should contain(\"hello\")\n    tree.suggestion(\"he\".toCharArray).get.size should equal(2)\n  }\n\n}\n"
  },
  {
    "path": "scala/src/test/scala/ch39_back_tracking/BagWeightTest.scala",
    "content": "package ch39_back_tracking\n\nimport org.scalatest.FlatSpec\n\nclass BagWeightTest extends FlatSpec {\n\n  behavior of \"BagWeightTest\"\n\n  it should \"calculateMaxWeight\" in {\n    val bagWeight = new BagWeight(5,9)\n    val maxWeight = bagWeight.calculateMaxWeight(Array(1,2,3,5,6))\n    println(maxWeight)\n  }\n\n}\n"
  },
  {
    "path": "scala/src/test/scala/ch39_back_tracking/EightQueensTest.scala",
    "content": "package ch39_back_tracking\n\nimport org.scalatest.FlatSpec\n\nclass EightQueensTest extends FlatSpec {\n\n  behavior of \"EightQueensTest\"\n\n  it should \"calc8Queues\" in {\n    val eightQueens = new EightQueens()\n    eightQueens.calc8Queues(0)\n  }\n\n}\n"
  },
  {
    "path": "scala/src/test/scala/ch39_back_tracking/NQueensTest.scala",
    "content": "package ch39_back_tracking\n\nimport org.scalatest.FlatSpec\n\nclass NQueensTest extends FlatSpec {\n\n  behavior of \"NQueensTest\"\n\n  it should \"calc8Queues\" in {\n    val eightQueens = new NQueens(8)\n    eightQueens.calcNQueues(0)\n\n  }\n\n  it should \"calc4Queues\" in {\n    val eightQueens = new NQueens(4)\n    eightQueens.calcNQueues(0)\n\n  }\n\n}\n"
  },
  {
    "path": "scala/src/test/scala/ch39_back_tracking/SudokuTest.scala",
    "content": "package ch39_back_tracking\n\nimport org.scalatest.FlatSpec\n\nclass SudokuTest extends FlatSpec {\n\n  behavior of \"SudokuTest\"\n\n  it should \"resolve - 1\" in {\n    val grid = Array(\n      Array(0, 0, 0, 0, 0, 0, 0, 0, 0),\n      Array(0, 0, 0, 0, 0, 0, 0, 0, 0),\n      Array(0, 0, 0, 0, 0, 0, 0, 0, 0),\n      Array(0, 0, 0, 0, 0, 0, 0, 0, 0),\n      Array(0, 0, 0, 0, 0, 0, 0, 0, 0),\n      Array(0, 0, 0, 0, 0, 0, 0, 0, 0),\n      Array(0, 0, 0, 0, 0, 0, 0, 0, 0),\n      Array(0, 0, 0, 0, 0, 0, 0, 0, 0),\n      Array(0, 0, 0, 0, 0, 0, 0, 0, 0)\n    )\n    val sudoku = new Sudoku()\n    sudoku.resolve(grid)\n  }\n\n  it should \"resolve - 2\" in {\n    val grid = Array(\n      Array(0, 0, 7, 2, 5, 6, 4, 0 ,0),\n      Array(4, 0, 0, 0, 0, 0, 0, 0, 5),\n      Array(0, 1, 0, 0, 3, 9, 0, 6, 0),\n      Array(0, 0, 0, 5, 0, 8, 0, 0, 0),\n      Array(0, 0, 8, 0, 6, 0, 2, 0, 0),\n      Array(0, 9, 0, 1, 0, 7, 0, 0, 0),\n      Array(0, 3, 0, 0, 7, 0, 0, 9, 0),\n      Array(2, 0, 0, 0, 0, 0, 0, 0, 4),\n      Array(0, 0, 6, 3, 1, 2, 7, 0, 8)\n    )\n    val sudoku = new Sudoku()\n    sudoku.resolve(grid)\n  }\n\n  it should \"resolve - 3\" in {\n    val grid = Array(\n      Array(3, 8, 0, 0, 0, 0, 0, 0, 1),\n      Array(0, 0, 6, 4, 0, 0, 7, 8, 5),\n      Array(0, 0, 9, 0, 2, 0, 3, 0, 0),\n      Array(0, 6, 0, 0, 9, 0, 0, 0, 0),\n      Array(8, 0, 0, 3, 0, 2, 0, 0, 9),\n      Array(0, 0, 0, 0, 4, 0, 0, 7, 0),\n      Array(0, 0, 1, 0, 7, 0, 5, 0, 0),\n      Array(4, 9, 5, 0, 0, 6, 1, 0, 0),\n      Array(0, 0, 0, 0, 0, 0, 0, 9, 2)\n    )\n    val sudoku = new Sudoku()\n    sudoku.resolve(grid)\n  }\n\n  it should \"print grid\" in {\n    val grid = Array(\n      Array(0, 0, 0, 0, 0, 0, 0, 0, 0),\n      Array(0, 0, 0, 0, 0, 0, 0, 0, 0),\n      Array(0, 0, 0, 0, 0, 0, 0, 0, 0),\n      Array(0, 0, 0, 0, 0, 0, 0, 0, 0),\n      Array(0, 0, 0, 0, 0, 0, 0, 0, 0),\n      Array(0, 0, 0, 0, 0, 0, 0, 0, 0),\n      Array(0, 0, 0, 0, 0, 0, 0, 0, 0),\n      Array(0, 0, 0, 0, 0, 0, 0, 0, 0),\n      Array(0, 0, 0, 0, 0, 0, 0, 0, 0)\n    )\n\n    val sudoku = new Sudoku()\n    sudoku.printGrid(grid)\n  }\n\n}\n"
  },
  {
    "path": "scala/src/test/scala/ch43_topology_sort/GraphTopologyTest.scala",
    "content": "package ch43_topology_sort\n\nimport org.scalatest.{FlatSpec, Matchers}\n\nclass GraphTopologyTest extends FlatSpec with Matchers {\n\n  behavior of \"GraphTopologyTest\"\n\n  /*\n     a -> b\n     |    |\n    \\|/  \\|/\n     c -> d -> e\n   */\n  it should \"topologySortByKahn - 1\" in {\n    val nodes = Array(\"a\", \"b\", \"c\", \"d\", \"e\")\n    val graphTopology = new GraphTopology(nodes.length)\n    graphTopology.addEdge(0, 1)\n    graphTopology.addEdge(0, 2)\n    graphTopology.addEdge(1, 3)\n    graphTopology.addEdge(2, 3)\n    graphTopology.addEdge(3, 4)\n\n    val seq = graphTopology.topologySortByKahn()\n    seq.map(nodes(_)).mkString(\",\") should equal(\"a,b,c,d,e\")\n  }\n\n  /*\n     a -> d ---\n          |    |\n         \\|/  \\|/\n          e -> c\n   */\n  it should \"topologySortByKahn - 2\" in {\n    val nodes = Array(\"a\", \"b\", \"c\", \"d\", \"e\")\n    val graphTopology = new GraphTopology(nodes.length)\n    graphTopology.addEdge(0, 3)\n    graphTopology.addEdge(3, 4)\n    graphTopology.addEdge(3, 2)\n    graphTopology.addEdge(4, 2)\n\n    val seq = graphTopology.topologySortByKahn()\n    seq.map(nodes(_)).mkString(\",\") should equal(\"a,b,d,e,c\")\n  }\n\n  /*\n     a -> d <- b\n          |   /|\\\n         \\|/   |\n          e -> c\n   */\n  it should \"topologySortByKahn - 3\" in {\n    val nodes = Array(\"a\", \"b\", \"c\", \"d\", \"e\")\n    val graphTopology = new GraphTopology(nodes.length)\n    graphTopology.addEdge(0, 3)\n    graphTopology.addEdge(3, 4)\n    graphTopology.addEdge(4, 2)\n    graphTopology.addEdge(2, 1)\n    graphTopology.addEdge(1, 3)\n\n    val seq = graphTopology.topologySortByKahn()\n    seq.map(nodes(_)).mkString(\",\") should equal(\"a\")\n  }\n\n  /*\n     a -> b\n     |    |\n    \\|/  \\|/\n     c -> d -> e\n   */\n  it should \"topologySortByDFS - 1\" in {\n    val nodes = Array(\"a\", \"b\", \"c\", \"d\", \"e\")\n    val graphTopology = new GraphTopology(nodes.length)\n    graphTopology.addEdge(0, 1)\n    graphTopology.addEdge(0, 2)\n    graphTopology.addEdge(1, 3)\n    graphTopology.addEdge(2, 3)\n    graphTopology.addEdge(3, 4)\n\n    val seq = graphTopology.topologySortByDFS()\n    seq.map(nodes(_)).mkString(\",\") should equal(\"a,b,c,d,e\")\n  }\n\n  /*\n     a -> d ---\n          |    |\n         \\|/  \\|/\n          e -> c\n   */\n  it should \"topologySortByDFS - 2\" in {\n    val nodes = Array(\"a\", \"b\", \"c\", \"d\", \"e\")\n    val graphTopology = new GraphTopology(nodes.length)\n    graphTopology.addEdge(0, 3)\n    graphTopology.addEdge(3, 4)\n    graphTopology.addEdge(3, 2)\n    graphTopology.addEdge(4, 2)\n\n    val seq = graphTopology.topologySortByDFS()\n    seq.map(nodes(_)).mkString(\",\") should equal(\"a,b,d,e,c\")\n  }\n\n  /*\n     a -> d <- b\n          |   /|\\\n         \\|/   |\n          e -> c\n   */\n  it should \"topologySortByDFS - 3\" in {\n    val nodes = Array(\"a\", \"b\", \"c\", \"d\", \"e\")\n    val graphTopology = new GraphTopology(nodes.length)\n    graphTopology.addEdge(0, 3)\n    graphTopology.addEdge(3, 4)\n    graphTopology.addEdge(4, 2)\n    graphTopology.addEdge(2, 1)\n    graphTopology.addEdge(1, 3)\n\n    val seq = graphTopology.topologySortByKahn()\n    seq.map(nodes(_)).mkString(\",\") should equal(\"a\")\n  }\n}\n"
  },
  {
    "path": "swift/05_array/MyArray.swift",
    "content": "//\n// Created by Jiandan on 2018/10/10.\n// Copyright (c) 2018 Jiandan. All rights reserved.\n//\n\nimport Foundation\n\n// <Element> Swift 泛型，此数组支持不同数据类型\npublic struct MyArray<Element> {\n    private var data: [Element]\n    private var capacity = 0 // 数组长度\n    private var count = 0 // 已保存的数据个数\n\n    /// 构造方法\n    /// - parameter defaultElement: 默认元素，用来占位\n    /// - parameter capacity: 数组长度\n    init(defaultElement: Element, capacity: Int) {\n        data = [Element](repeating: defaultElement, count: capacity)\n        self.capacity = capacity\n    }\n    \n    // 根据 index，查找元素\n    func find(at index: Int) -> Element? {\n        // index 必须在 [0, count)\n        guard index >= 0, index < count else {\n            return nil\n        }\n        \n        return data[index]\n    }\n    \n    // 根据 index，删除元素\n    mutating func delete(at index: Int) -> Bool {\n        // index 必须在 [0, count)\n        guard index >= 0, index < count else {\n            return false\n        }\n        \n        // [index, count - 1) 从 index 开始，元素分别向前移动一位\n        for i in index ..< count - 1 {\n            data[i] = data[i+1]\n        }\n        count -= 1\n        return true\n    }\n    \n    // 根据 index 插入元素\n    mutating func insert(value: Element, at index: Int) -> Bool {\n        // index 必须在 [0, count)\n        guard index >= 0, index < count, count < capacity else {\n            return false\n        }\n        \n        // count - 1 ~ index\n        for i in (index ... count - 1).reversed() {\n            data[i + 1] = data[i]\n        }\n        \n        data[index] = value\n        count += 1\n        return true\n    }\n    \n    // 添加元素\n    mutating func add(value: Element) -> Bool {\n        guard count < capacity else {\n            return false\n        }\n        data[count] = value\n        count += 1\n        return true\n    }\n    \n    func printAll() {\n        print(\"\\(data)\")\n    }\n}\n"
  },
  {
    "path": "swift/06_linkedlist/SinglyLinkedList.swift",
    "content": "//\n//  Created by Jiandan on 2018/10/12.\n//  Copyright © 2018 Jiandan. All rights reserved.\n//\n\nimport Foundation\n\nclass Node<T> {\n    var value: T?\n    var next: Node?\n    \n    init(){}\n    \n    init(value: T) {\n        self.value = value\n    }\n}\n\n/// 单链表\n/// 实现插入、删除、查找操作\nclass List<Element: Equatable> {\n    private var dummy = Node<Element>() // 哨兵结点，不存储数据\n    var size: Int {\n        var num = 0\n        var tmpNode = dummy.next\n        while tmpNode != nil {\n            num += 1\n            tmpNode = tmpNode!.next\n        }\n        return num\n    }\n    var isEmpty: Bool { return size > 0 }\n    \n    /// find node with value\n    func node(with value: Element) -> Node<Element>? {\n        var node = dummy.next\n        while node != nil {\n            if node!.value == value {\n                return node\n            }\n            node = node!.next\n        }\n        \n        return nil\n    }\n    // 约定：链表的 index 从 1 开始\n    func node(at index: Int) -> Node<Element>? {\n        var num = 1\n        var node = dummy.next\n        while node != nil {\n            if num == index {\n                return node\n            }\n            node = node!.next\n            num += 1\n        }\n        return nil\n    }\n    \n    func insertToHead(value: Element) {\n        let newNode = Node(value: value)\n        return insertToHead(node: newNode)\n    }\n    \n    func insertToHead(node: Node<Element>) {\n        node.next = dummy.next\n        dummy.next = node\n    }\n    \n    func insert(after node: Node<Element>, newValue: Element) {\n        let newNode = Node(value: newValue)\n        return insert(after: node, newNode: newNode)\n    }\n\n    func insert(after node: Node<Element>, newNode: Node<Element>) {\n        newNode.next = node.next\n        node.next = newNode\n    }\n    \n    func insert(before node: Node<Element>, newValue: Element) {\n        let newNode = Node(value: newValue)\n        return insert(before: node, newNode: newNode)\n    }\n    \n    func insert(before node: Node<Element>, newNode: Node<Element>) {\n        var lastNode = dummy\n        var tmpNode = dummy.next\n\n        while tmpNode != nil {\n            if tmpNode === node {\n                newNode.next = tmpNode\n                lastNode.next = newNode\n                break\n            }\n            lastNode = tmpNode!\n            tmpNode = tmpNode!.next\n        }\n    }\n    \n    func delete(node: Node<Element>) {\n        var lastNode = dummy\n        var tmpNode = dummy.next\n\n        while tmpNode != nil {\n            if tmpNode === node {\n                lastNode.next = tmpNode!.next\n                break\n            }\n\n            lastNode = tmpNode!\n            tmpNode = tmpNode!.next\n        }\n    }\n    /// 删除首个 value 符合要求的结点\n    func delete(value: Element) {\n        var lastNode = dummy\n        var tmpNode = dummy.next\n        while tmpNode != nil {\n            if tmpNode!.value == value {\n                lastNode.next = tmpNode!.next\n                break\n            }\n\n            lastNode = tmpNode!\n            tmpNode = tmpNode!.next\n        }\n    }\n}\n"
  },
  {
    "path": "swift/07_linkedlist/LinkedListAlgo.swift",
    "content": "//\n// Created by Jiandan on 2018/10/13.\n// Copyright (c) 2018 Jiandan. All rights reserved.\n//\n\n/**\n * 1) 单链表反转\n * 2) 链表中环的检测\n * 3) 两个有序的链表合并\n * 4) 删除链表倒数第n个结点\n * 5) 求链表的中间结点\n */\nimport Foundation\n/// 单链表反转\nfunc reverseSinglyLinkedList<Element>(head: Node<Element>) -> Node<Element>? {\n    var reverseHead: Node<Element>?, currentNode: Node<Element>?, prevNode: Node<Element>?\n    currentNode = head\n    while currentNode != nil {\n        let nextNode = currentNode!.next\n        if nextNode == nil {\n            reverseHead = currentNode\n        }\n        currentNode!.next = prevNode\n        prevNode = currentNode\n        currentNode = nextNode\n    }\n    return reverseHead\n}\n/// 检测环\nfunc hasCircle<Element>(head: Node<Element>) -> Bool {\n    var fast = head.next\n    var slow: Node<Element>? = head\n    while fast != nil {\n        if fast === slow {\n            return true\n        }\n        fast = fast!.next?.next\n        slow = slow!.next\n    }\n    return false\n}\n/// 两个有序的链表合并\nfunc mergeSortedLists<Element: Comparable>(headA: Node<Element>?, headB: Node<Element>?) -> Node<Element>? {\n    guard let headA = headA else {\n        return headB\n    }\n    guard let headB = headB else {\n        return headA\n    }\n    \n    var head: Node<Element>?, tail: Node<Element>?\n    var nodeA: Node<Element>? = headA, nodeB: Node<Element>? = headB\n    if nodeA!.value! < nodeB!.value! {\n        head = nodeA\n        nodeA = nodeA!.next\n    } else {\n        head = nodeB\n        nodeB = nodeB!.next\n    }\n    tail = head\n    \n    while nodeA != nil, nodeB != nil {\n        if nodeA!.value! < nodeB!.value! {\n            tail!.next = nodeA\n            nodeA = nodeA!.next\n        } else {\n            tail!.next = nodeB\n            nodeB = nodeB!.next\n        }\n        tail = tail!.next\n    }\n    \n    if nodeA != nil {\n        tail?.next = nodeA\n    } else {\n        tail?.next = nodeB\n    }\n\n    return head\n}\n\n/// 删除倒数第n个结点\nfunc deleteNode<Element>(at lastNum: Int, in head: Node<Element>) {\n    var slow: Node<Element>? = head\n    var fast: Node<Element>? = head\n    var num = 1\n    while fast != nil, num < lastNum {\n        fast = fast!.next\n        num += 1\n    }\n\n    var prevNode: Node<Element>?\n    while fast != nil {\n        prevNode = slow\n        fast = fast!.next\n        slow = slow!.next\n    }\n    prevNode?.next = slow?.next\n}\n\n/// 求链表的中间结点\nfunc halfNode<Element>(in head: Node<Element>) -> Node<Element>? {\n    var slow: Node<Element>? = head\n    var fast: Node<Element>? = head\n\n    while fast?.next != nil, fast?.next?.next != nil {\n        fast = fast!.next?.next\n        slow = slow!.next\n    }\n    return slow\n}\n"
  },
  {
    "path": "swift/08_stack/Browser.swift",
    "content": "//\n// Created by Jiandan on 2018/10/12.\n// Copyright (c) 2018 Jiandan. All rights reserved.\n//\n\nimport Foundation\n\nstruct Page {\n    /// 存储前进 url\n    private var forwardArray = [String]()\n    /// 存储后退 url\n    private var backArray = [String]()\n    \n    var currentURL: String? { return forwardArray.last }\n\n    init(url: String) {\n        forwardArray.append(url)\n    }\n    /// 前进\n    mutating func goForward(url: String) {\n        forwardArray.append(url)\n    }\n    /// 后退\n    mutating func goBack() {\n        backArray.append(forwardArray.popLast()!)\n    }\n}\n"
  },
  {
    "path": "swift/08_stack/BrowserDemo.swift",
    "content": "//\n// Created by Jiandan on 2018/10/12.\n// Copyright (c) 2018 Jiandan. All rights reserved.\n//\n\nimport Foundation\n\nstruct Page {\n    /// 存储前进 url\n    private var forwardArray = [String]()\n    /// 存储后退 url\n    private var backArray = [String]()\n    \n    var currentURL: String { return forwardArray.last! }\n\n    init(url: String) {\n        forwardArray.append(url)\n    }\n    /// 前进\n    mutating func goForward(url: String) {\n        forwardArray.append(url)\n    }\n    /// 后退\n    mutating func goBack() {\n        backArray.append(forwardArray.popLast()!)\n    }\n}\n"
  },
  {
    "path": "swift/08_stack/Stack.swift",
    "content": "//\n// Created by Jiandan on 2018/10/12.\n// Copyright (c) 2018 Jiandan. All rights reserved.\n//\n\nimport Foundation\n\nprotocol Stack {\n    /// 持有的数据类型\n    associatedtype Element\n    /// 是否为空\n    var isEmpty: Bool { get }\n    /// 队列大小\n    var size: Int { get }\n    /// 返回队列头部元素\n    var peek: Element? { get }\n    /// 入栈\n    mutating func push(newElement: Element) -> Bool\n    /// 出栈\n    mutating func pop() -> Element?\n}\n"
  },
  {
    "path": "swift/08_stack/StackBasedOnLinkedList.swift",
    "content": "//\n// Created by Jiandan on 2018/10/12.\n// Copyright (c) 2018 Jiandan. All rights reserved.\n//\n\nimport Foundation\n\nstruct StackBasedOnLinkedList<Element>: Stack {\n    private var head = Node<Element>() // 哨兵结点，不存储内容\n    \n    // MARK: Protocol: Stack\n    \n    var isEmpty: Bool { return head.next == nil }\n    \n    var size: Int {\n        var count = 0\n        var cur = head.next\n        while cur != nil {\n            count += 1\n            cur = cur?.next\n        }\n        return count\n    }\n    \n    var peek: Element? { return head.next?.value }\n    \n    func push(newElement: Element) -> Bool {\n        let node = Node(value: newElement)\n        node.next = head.next\n        head.next = node\n        return true\n    }\n    \n    func pop() -> Element? {\n        let node = head.next\n        head.next = node?.next\n        return node?.value\n    }\n}\n"
  },
  {
    "path": "swift/09_queue/ArrayQueue.swift",
    "content": "//\n// Created by Jiandan on 2018/10/11.\n// Copyright (c) 2018 Jiandan. All rights reserved.\n//\n\nimport Foundation\n\n/// 用数组实现的队列\nstruct ArrayQueue<Element>: Queue {\n    /// 数组\n    private var items: [Element]\n    /// 数组最大长度\n    private var capacity = 0\n    /// 队头下标\n    private var head = 0\n    /// 队尾下标\n    private var tail = 0\n    \n    /// 构造方法\n    /// - parameter defaultElement: 默认元素\n    /// - parameter capacity: 数组长度\n    init(defaultElement: Element, capacity: Int) {\n        self.capacity = capacity\n        items = [Element](repeating: defaultElement, count: capacity)\n    }\n    \n    // MARK: Protocol: Queue\n    \n    var isEmpty: Bool { return head == tail }\n    \n    var size: Int { return tail - head }\n    \n    var peek: Element? { return isEmpty ? nil : items[head] }\n    \n    // 没有数据搬移的实现，即实现了一个有界序列\n//    mutating func enqueue(newElement: Element) -> Bool {\n//        // 整个队列都占满了\n//        if tail == capacity {\n//            return false\n//        }\n//\n//        items[tail] = newElement\n//        tail += 1\n//        return true\n//    }\n    // 有数据搬移的实现，即实现了一个无界序列\n    mutating func enqueue(newElement: Element) -> Bool {\n        // 如果 tail == capacity 表示队列末尾没有空间了\n        if tail == capacity {\n            // 整个队列都占满了\n            if head == 0 { return false }\n            // 数据搬移\n            for i in head ..< tail {\n                items[i - head] = items[i]\n            }\n            // 搬移完之后重新更新 head 和 tail\n            tail -= head\n            head = 0\n        }\n\n        items[tail] = newElement\n        tail += 1\n        return true\n    }\n    \n    mutating func dequeue() -> Element? {\n        if isEmpty {\n            return nil\n        }\n        \n        let item = items[head]\n        head += 1\n        return item\n    }\n}\n\n/// 使用2个数组实现无界队列，用到 Swift 中 Array 较多的方法\n/// 来源：《iOS 面试之道》（故胤道长，唐巧）\nstruct ArrayQueue2<Element>: Queue {\n    /// 输入数组，主要负责入队\n    var inArray = [Element]()\n    /// 输出数组，主要负责出队\n    var outArray = [Element]()\n    \n    var isEmpty: Bool { return inArray.isEmpty && outArray.isEmpty }\n    \n    var size: Int { return inArray.count + outArray.count }\n    \n    // 当 outArray 为空时，返回 inArray 首个元素，否则返回 outArray 末尾元素\n    var peek: Element? { return outArray.isEmpty ? inArray.first : outArray.last }\n    \n    mutating func enqueue(newElement: Element) -> Bool {\n        // inArray 添加元素\n        inArray.append(newElement)\n        return true\n    }\n    \n    mutating func dequeue() -> Element? {\n        if outArray.isEmpty {\n            // 将 inArray 倒序存入 outArray 中\n            outArray = inArray.reversed()\n            // 清空 inArray\n            inArray.removeAll()\n        }\n        // 弹出 outArray 最后一个元素\n        return outArray.popLast()\n    }\n}\n"
  },
  {
    "path": "swift/09_queue/CircularQueue.swift",
    "content": "//\n// Created by Jiandan on 2018/10/11.\n// Copyright (c) 2018 Jiandan. All rights reserved.\n//\n\nimport Foundation\n\n/// 循环队列\nstruct CircularQueue<Element>: Queue {\n    /// 数组\n    private var items: [Element]\n    /// 数组最大长度\n    private var capacity = 0\n    /// 队头下标\n    private var head = 0\n    /// 队尾下标\n    private var tail = 0\n\n    /// 构造方法\n    /// - parameter defaultElement: 默认元素\n    /// - parameter capacity: 数组长度\n    init(defaultElement: Element, capacity: Int) {\n        self.capacity = capacity\n        items = [Element](repeating: defaultElement, count: capacity)\n    }\n\n    // MARK: Protocol: Queue\n\n    var isEmpty: Bool { return head == tail }\n\n    var size: Int {\n        if tail >= head {\n            return tail - head\n        } else {\n            return (tail + 1) + (capacity - head)\n        }\n    }\n\n    var peek: Element? { return isEmpty ? nil : items[head] }\n\n    mutating func enqueue(newElement: Element) -> Bool {\n        // 整个队列都占满了\n        if (tail + 1) % capacity == head {\n            return false\n        }\n\n        items[tail] = newElement\n        tail = (tail + 1) % capacity\n        return true\n    }\n\n    mutating func dequeue() -> Element? {\n        if isEmpty {\n            return nil\n        }\n\n        let item = items[head]\n        head = (head + 1) % capacity\n        return item\n    }\n}\n"
  },
  {
    "path": "swift/09_queue/Queue.swift",
    "content": "//\n// Created by Jiandan on 2018/10/11.\n// Copyright (c) 2018 Jiandan. All rights reserved.\n//\n\nimport Foundation\n\nprotocol Queue {\n    /// 持有的数据类型\n    associatedtype Element\n    /// 是否为空\n    var isEmpty: Bool { get }\n    /// 队列大小\n    var size: Int { get }\n    /// 返回队列头部元素\n    var peek: Element? { get }\n    /// 入队\n    mutating func enqueue(newElement: Element) -> Bool\n    /// 出队\n    mutating func dequeue() -> Element?\n}\n"
  },
  {
    "path": "swift/09_queue/QueueBasedOnLinkedList.swift",
    "content": "//\n// Created by Jiandan on 2018/10/11.\n// Copyright (c) 2018 Jiandan. All rights reserved.\n//\n\nimport Foundation\n\nstruct QueueBasedOnLinkedList<Element>: Queue {\n    /// 队首\n    var head: Node<Element>?\n    /// 队尾\n    var tail: Node<Element>?\n    \n    // MARK: Protocol: Queue\n    \n    var isEmpty: Bool { return head == nil }\n    \n    var size: Int {\n        if isEmpty {\n            return 0\n        }\n        \n        var count = 1 // head 本身算一个\n        while head?.next != nil {\n            count += 1\n        }\n        \n        return count\n    }\n    \n    var peek: Element? { return head?.value }\n    \n    mutating func enqueue(newElement: Element) -> Bool {\n        if isEmpty {\n            // 空队列\n            let node = Node(value: newElement)\n            head = node\n            tail = node\n        } else {\n            tail!.next = Node(value: newElement)\n            tail = tail!.next\n        }\n        return true\n    }\n    \n    mutating func dequeue() -> Element? {\n        if isEmpty {\n            return nil\n        }\n        \n        let node = head\n        head = head!.next\n        return node?.value\n    }\n}\n"
  },
  {
    "path": "swift/11_sorts/Sorts.swift",
    "content": "import Foundation\n\n/// 冒泡排序\n///\n/// - Parameter elements: 数组\n/// - Returns: 返回值\npublic func bubbleSort<T>(_ elements: [T]) ->[T] where T: Comparable {\n    var array = elements\n    guard array.count > 1 else {\n        return array\n    }\n    for i in 0..<array.count {\n        // 提前退出标志位\n        var flag = false\n        for j in 0..<array.count - i - 1 {\n            if array[j] > array[j+1] {\n                array.swapAt(j+1, j)\n                // 此次冒泡有数据交换\n                flag = true\n            }\n        }\n        if (!flag) {\n            break\n        }\n    }\n    return array\n}\n\n\n/// 插入排序\n///\n/// - Parameter elements: 数组\n/// - Returns: 返回值\npublic func insertionSort<T>(_ elements: [T]) -> [T] where T: Comparable {\n    var array = elements\n    guard array.count > 1 else {\n        return array\n    }\n    for i in 1..<array.count {\n        let value = array[i]\n        var j = i - 1;\n        // 查找要插入的位置并移动数据\n        for p in (0...j).reversed() {\n            j = p\n            if array[p] > value {\n                array[p+1] = array[p]\n            } else {\n                break\n            }\n        }\n        array[j+1] = value\n    }\n    return array\n}\n\n\n/// 选择排序\n///\n/// - Parameter elements: 数组\n/// - Returns: 返回值\npublic func selectionSort<T>(_ elements: [T]) -> [T] where T: Comparable {\n    var array = elements\n    guard array.count > 1 else {\n        return array\n    }\n    for i in 0..<array.count {\n        // 查找最小值\n        var minIndex = i\n        var minValue = array[i]\n        for j in i..<array.count {\n            if array[j] < minValue {\n                minValue = array[j]\n                minIndex = j\n            }\n        }\n        // 交换\n        array.swapAt(i, minIndex)\n    }\n    return array\n}\n\n"
  },
  {
    "path": "swift/12_sorts/QuickSort.swift",
    "content": "//\n//  QuickSort.swift\n//  algo\n//\n//  Created by Wenru Dong on 2018/10/17.\n//  Copyright © 2018年 Wenru Dong. All rights reserved.\n//\n\nimport Foundation\n\npublic func quickSort<T: RandomAccessCollection & MutableCollection>(_ a: inout T) where T.Element: Comparable {\n    quickSort(&a, from: a.startIndex, to: a.index(before: a.endIndex))\n}\n\nfileprivate func quickSort<T: RandomAccessCollection & MutableCollection>(_ a: inout T, from low: T.Index, to high: T.Index) where T.Element: Comparable {\n    if low >= high { return }\n    \n    let m = partition(&a, from: low, to: high)\n    quickSort(&a, from: low, to: a.index(before: m))\n    quickSort(&a, from: a.index(after: m), to: high)\n}\n\nfileprivate func partition<T: RandomAccessCollection & MutableCollection>(_ a: inout T, from low: T.Index, to high: T.Index) -> T.Index where T.Element: Comparable {\n    let pivot = a[low]\n    var j = low\n    var i = a.index(after: low)\n    while i <= high {\n        if a[i] < pivot {\n            a.formIndex(after: &j)\n            a.swapAt(i, j)\n        }\n        a.formIndex(after: &i)\n    }\n    a.swapAt(low, j)\n    return j\n}\n"
  },
  {
    "path": "swift/12_sorts/SortsTests.swift",
    "content": "//\n//  SortsTests.swift\n//  SortsTests\n//\n//  Created by Wenru Dong on 2018/10/14.\n//  Copyright © 2018年 Wenru Dong. All rights reserved.\n//\n\nimport XCTest\n\nclass SortsTests: XCTestCase {\n    \n    override func setUp() {\n        super.setUp()\n        // Put setup code here. This method is called before the invocation of each test method in the class.\n    }\n    \n    override func tearDown() {\n        // Put teardown code here. This method is called after the invocation of each test method in the class.\n        super.tearDown()\n    }\n    \n    func testMergeSort() {\n        var a1 = [1, 1, 1, 1]\n        mergeSort(&a1)\n        XCTAssertEqual(a1, [1, 1, 1, 1])\n        \n        var a2 = [4, 3, 2, 1]\n        mergeSort(&a2)\n        XCTAssertEqual(a2, [1, 2, 3, 4])\n        \n        var a3 = [3, 6, 9, 7, 8, -1, 9, 3, -2, 0]\n        mergeSort(&a3)\n        XCTAssertEqual(a3, [-2, -1, 0, 3, 3, 6, 7, 8, 9, 9])\n    }\n    \n    func testQuickSort() {\n        var a1 = [1, 1, 1, 1]\n        quickSort(&a1)\n        XCTAssertEqual(a1, [1, 1, 1, 1])\n        \n        var a2 = [4, 3, 2, 1]\n        quickSort(&a2)\n        XCTAssertEqual(a2, [1, 2, 3, 4])\n        \n        var a3 = [3, 6, 9, 7, 8, -1, 9, 3, -2, 0]\n        quickSort(&a3)\n        XCTAssertEqual(a3, [-2, -1, 0, 3, 3, 6, 7, 8, 9, 9])\n    }\n    \n    func testPerformanceExample() {\n        // This is an example of a performance test case.\n        self.measure {\n            // Put the code you want to measure the time of here.\n        }\n    }\n    \n}\n"
  },
  {
    "path": "swift/12_sorts/mergeSort.swift",
    "content": "//\n//  sorts.swift\n//  algo\n//\n//  Created by Wenru Dong on 2018/10/14.\n//  Copyright © 2018年 Wenru Dong. All rights reserved.\n//\n\nimport Foundation\n\npublic func mergeSort<T>(_ a: inout T) where T: RandomAccessCollection, T: MutableCollection, T.Element: Comparable {\n    mergeSort(&a, from: a.startIndex, to: a.index(before: a.endIndex))\n}\n\nfileprivate func mergeSort<T>(_ a: inout T, from low: T.Index, to high: T.Index) where T: RandomAccessCollection, T: MutableCollection, T.Element: Comparable {\n    if low >= high { return }\n    let dist = a.distance(from: low, to: high)\n    let mid = a.index(low, offsetBy: dist/2)\n    mergeSort(&a, from: low, to: mid)\n    mergeSort(&a, from: a.index(mid, offsetBy: 1), to: high)\n    merge(&a, from: low, through: mid, to: high)\n}\n\nfileprivate func merge<T>(_ a: inout T, from low: T.Index, through mid: T.Index, to high: T.Index) where T: RandomAccessCollection, T: MutableCollection, T.Element: Comparable {\n    var i = low\n    var j = a.index(mid, offsetBy: 1)\n    var tmp = Array<T.Element>()\n    tmp.reserveCapacity(a.distance(from: low, to: high) + 1)\n    while i <= mid && j <= high {\n        if a[i] <= a[j] {\n            tmp.append(a[i])\n            a.formIndex(after: &i)\n        } else {\n            tmp.append(a[j])\n            a.formIndex(after: &j)\n        }\n    }\n    \n    var start = i\n    var end = mid\n    if j <= high {\n        start = j\n        end = high\n    }\n    tmp.append(contentsOf: a[start...end])\n    \n    var current = low\n    for element in tmp {\n        a[current] = element\n        a.formIndex(after: &current)\n    }\n}\n"
  },
  {
    "path": "swift/14_sorts/CountingSort.swift",
    "content": "//\n//  CountingSort.swift\n//  algo\n//\n//  Created by Wenru Dong on 2018/10/18.\n//  Copyright © 2018年 Wenru Dong. All rights reserved.\n//\n\nimport Foundation\n\n// 假设数组中储存的都是非负整数\npublic func countingSort(_ a: inout [Int]) {\n    if a.count <= 1 { return }\n    \n    var counts = Array(repeating: 0, count: a.max()! + 1)\n    for num in a {\n        counts[num] += 1\n    }\n    for i in 1..<counts.count {\n        counts[i] = counts[i-1] + counts[i]\n    }\n    \n    var aSorted = Array(repeating: 0, count: a.count)\n    for num in a.reversed() {\n        let index = counts[num] - 1\n        aSorted[index] = num\n        counts[num] -= 1\n    }\n    \n    for (i, num) in aSorted.enumerated() {\n        a[i] = num\n    }\n}\n"
  },
  {
    "path": "typescript/06_linkedlist/LRUCache.ts",
    "content": "/**\n * 基于Map和双向链表实现的LRU算法\n * 使用泛型可以存储多种类型的数据\n */\nclass LRUCache<K, V> {\n  private cacheMap: Map<K, LinkedListNode<K, V>>\n  private readonly limit: number\n  private head: LinkedListNode<K, V> | null = null\n  private end: LinkedListNode<K, V> | null = null\n\n  constructor(limit: number) {\n    if (limit <= 0) throw new Error('limit of cache must > 0')\n    this.cacheMap = new Map()\n    this.limit = limit\n  }\n\n  public get(key: K): V | null {\n    const node = this.cacheMap.get(key)\n    if (!node) return null\n    this.refreshNode(node)\n    return node.value\n  }\n\n  public put(key: K, value: V) {\n    const node = this.cacheMap.get(key)\n    // 原缓存不存在则加入到队尾\n    if (!node) {\n      // 大于规定的size则删除最不常用的\n      if (this.cacheMap.size >= this.limit) {\n        const oldKey = this.removeNode(this.head!)\n        this.cacheMap.delete(oldKey)\n      }\n      // 在队尾添加\n      const newNode = new LinkedListNode(key, value)\n      this.addNode(newNode)\n      this.cacheMap.set(key, newNode)\n    } else {\n      node.value = value\n      this.refreshNode(node)\n    }\n  }\n\n  private refreshNode(node: LinkedListNode<K, V>) {\n    if (node === this.end) return\n    this.removeNode(node)\n    this.addNode(node)\n  }\n\n  private removeNode(node: LinkedListNode<K, V>): K {\n    if (node === this.end) {\n      this.end = this.end.prev\n    } else if (node === this.head) {\n      this.head = this.head.next\n    } else {\n      // 这个由于排除了首尾节点\n      node.prev!.next = node.next\n      node.next!.prev = node.prev\n    }\n    return node.key\n  }\n\n  /**\n   * 这里向尾部追加节点\n   * @param node\n   */\n  private addNode(node: LinkedListNode<K, V>) {\n    if (this.end) {\n      this.end.next = node\n      node.prev = this.end\n    }\n    this.end = node\n    if (this.head === null) {\n      this.head = node\n    }\n    // 消除之前的节点的下一个引用对象,防止无限循环\n    node.next = null\n  }\n}\n\nclass LinkedListNode<K, V> {\n  key: K\n  value: V\n  next: LinkedListNode<K, V> | null\n  prev: LinkedListNode<K, V> | null\n\n  constructor(\n    key: K,\n    value: V,\n    next: LinkedListNode<K, V> | null = null,\n    prev: LinkedListNode<K, V> | null = null\n  ) {\n    this.key = key\n    this.value = value\n    this.next = next\n    this.prev = prev\n  }\n}\n\nconst cache = new LRUCache<string,string>(3)\ncache.put('lv','xzw')\ncache.put('lv2','xzw2')\ncache.put('lv3','xzw3')\ncache.put('lv4','xzw4')\ncache.put('lv5','xzw5')\n\nconsole.log(cache)\n"
  },
  {
    "path": "typescript/06_linkedlist/LinkedList.ts",
    "content": "/**\n * 双向链表，更加常用设计也更加复杂一些\n * 需要更多的存储空间和操作复杂度\n */\nimport List from './List'\n\nclass LinkedList<T> implements List<T> {\n  size: number = 0\n  private head: LinkedListNode<T> | null = null\n  private last: LinkedListNode<T> | null = null\n\n  findByIndex(index: number): LinkedListNode<T> | null {\n    let p = this.head\n    let pos = 0\n    while (p && pos !== index) {\n      p = p.next\n      pos++\n    }\n    return p\n  }\n\n  findByValue(value: T): LinkedListNode<T> | null {\n    let p = this.head\n    while (p && p.item !== value) {\n      p = p.next\n    }\n    return p\n  }\n\n  insertToHead(value: T): void {\n    let p = this.head\n    const newNode = new LinkedListNode(value)\n    // 没有元素的时候需要初始化头节点和尾节点\n    if (!p) {\n      this.last = this.head = newNode\n    } else {\n      p.prev = newNode\n      newNode.next = p\n      this.head = newNode\n    }\n    this.size++\n  }\n\n  /**\n   * 在指定的index后面插入节点\n   * @param value 节点的值\n   * @param index 指定的位置\n   */\n  insertToIndex(value: T, index: number): void {\n    let p = this.head\n    let pos = 0\n    const newNode = new LinkedListNode(value)\n    while (p !== null && pos !== index) {\n      p = p.next\n      pos++\n    }\n    if (p === null) return\n    newNode.next = p.next\n    p.next = newNode\n    newNode.prev = p\n    this.size++\n  }\n\n  insertToTail(value: T): void {\n    let p = this.last\n    const newNode = new LinkedListNode(value)\n    if (p === null) {\n      this.head = this.last = newNode\n    } else {\n      p.next = newNode\n      newNode.prev = p\n      this.last = newNode\n    }\n\n    this.size++\n  }\n\n  remove(value: T): boolean {\n    let p = this.head\n    while (p && p.item !== value) {\n      p = p.next\n    }\n    if (!p) return false\n    if (p.prev) {\n      p.prev.next = p.next\n    } else {\n      this.head = p.next\n    }\n    if (p.next) {\n      p.next.prev = p.prev\n    } else {\n      this.last = p.prev\n    }\n    this.size--\n    return true\n  }\n\n  toString(): string {\n    let ret: string = ''\n    let p = this.head\n    while (p) {\n      ret = `${ret} ${p.item} `\n      p = p.next\n    }\n    return ret\n  }\n}\n\nclass LinkedListNode<T> {\n  item: T\n  next: LinkedListNode<T> | null\n  prev: LinkedListNode<T> | null\n\n  constructor(\n    item: T,\n    next: LinkedListNode<T> | null = null,\n    prev: LinkedListNode<T> | null = null\n  ) {\n    this.item = item\n    this.next = next\n    this.prev = prev\n  }\n}\n\nconst linkedList = new LinkedList()\nlinkedList.insertToHead('12')\nlinkedList.insertToHead('haha')\nlinkedList.insertToHead('www')\nlinkedList.insertToTail('zxc')\nlinkedList.insertToIndex('12ooo', 0)\nlinkedList.remove('12oooo')\nconsole.log(linkedList.toString())\n"
  },
  {
    "path": "typescript/06_linkedlist/List.ts",
    "content": "interface List<T> {\n  insertToHead(value: T): void\n\n  findByValue(value: T): any\n\n  findByIndex(index: number): any\n\n  insertToIndex(value: T, index: number): void\n\n  remove(value: T): boolean\n\n  insertToHead(value: T): void\n\n  insertToTail(value: T): void\n\n  toString(): string\n}\n\nexport default List\n"
  },
  {
    "path": "typescript/06_linkedlist/SingleLinkedList.ts",
    "content": "/**\n * 1)单链表的插入、删除、查找操作；\n * 2)链表支持任意类型数据\n */\nimport List from './List'\n\nclass SingleLinkedList<T> implements List<T> {\n  // 哨兵头节点\n  private readonly head: SingleNode<T>\n\n  constructor() {\n    this.head = new SingleNode<any>(null)\n  }\n\n  public findByValue(value: T): SingleNode<T> | null {\n    let p = this.head\n    while (p.next != null) {\n      if (p.next.value === value) return p.next\n      p = p.next\n    }\n    return p.next\n  }\n\n  public findByIndex(index: number): SingleNode<T> | null {\n    let p = this.head\n    let pos = 0\n    while (p.next != null && pos !== index) {\n      p = p.next\n      pos++\n    }\n    return p.next\n  }\n\n  /**\n   * 向指定的位置插入节点\n   * @param value\n   * @param index\n   */\n  public insertToIndex(value: T, index: number): void {\n    const newNode = new SingleNode(value)\n    let p = this.head\n    let pos = 0\n    while (p.next != null && pos !== index) {\n      p = p.next\n      pos++\n    }\n    if (p.next == null) return\n    newNode.next = p.next.next\n    p.next.next = newNode\n  }\n\n  /**\n   * 根据值去删除节点\n   * @param value 1 表示删除成功，0 表示删除失败\n   */\n  public remove(value: T): boolean {\n    let p = this.head\n    while (p.next != null) {\n      if (p.next.value === value) break\n      p = p.next\n    }\n    if (p.next === null) return false\n    p.next = p.next.next\n    return true\n  }\n\n  public insertToHead(value: T): void {\n    const newNode = new SingleNode(value, null)\n    this.insertNodeToHead(newNode)\n  }\n\n  public insertToTail(value: T): void {\n    const newNode = new SingleNode(value, null)\n    this.insertNodeToTail(newNode)\n  }\n\n  private insertNodeToHead(node: SingleNode<T>): void {\n    node.next = this.head.next\n    this.head.next = node\n  }\n\n  public toString(): string {\n    let ret: string = ''\n    let p = this.head\n    while (p.next != null) {\n      ret = `${ret} ${p.next.value} `\n      p = p.next\n    }\n    return ret\n  }\n\n  /**\n   * 单链表的尾插入比较费时\n   * @param newNode 插入的新节点\n   */\n  private insertNodeToTail(newNode: SingleNode<T>): void {\n    let p = this.head\n    while (p.next != null) {\n      p = p.next\n    }\n    p.next = newNode\n  }\n}\n\nclass SingleNode<T> {\n  public value: T\n  public next: SingleNode<T> | null\n\n  constructor(value: T, next: SingleNode<T> | null = null) {\n    this.value = value\n    this.next = next\n  }\n}\n\nconst singleLinkedList = new SingleLinkedList<string>()\nsingleLinkedList.insertToTail('god')\nsingleLinkedList.insertToTail('my')\n// console.log(singleLinkedList.printLinkedList())\nsingleLinkedList.insertToIndex('haha', 1)\nsingleLinkedList.remove('ha1')\nsingleLinkedList.toString()\n"
  },
  {
    "path": "typescript/07_linkedlist/LinkedListAlog.ts",
    "content": "/**\n * 单链表的常见操作包括\n * 链表反转\n * 链表中环的检测\n * 有序链表的合并\n * 删除链表倒数第n个节点\n * 链表中间节点\n */\nclass LinkedListAlog {\n  /**\n   * 反转链表,依次将节点插入到头部\n   * @param list\n   */\n  public static reverse<T>(list: SingleNode<T>): SingleNode<T> | null {\n    let currentNode: SingleNode<T> | null = list\n    let prevNode = null\n    while (currentNode) {\n      const nextNode: SingleNode<T> | null = currentNode.next\n      currentNode.next = prevNode\n      prevNode = currentNode\n      currentNode = nextNode\n    }\n    return prevNode\n  }\n\n  /**\n   * 通过快慢指针来检测是否为一个环\n   * @param list\n   */\n  public static checkCircle<T>(list: SingleNode<T>): boolean {\n    if (!list) return false\n    let fast: SingleNode<T> | null = list.next\n    let slow: SingleNode<T> | null = list\n    while (fast && fast.next) {\n      fast = fast.next.next\n      slow = slow!.next\n      if (fast === slow) return true\n    }\n    return false\n  }\n\n  /**\n   * 倒序删除节点\n   * @param list\n   * @param index\n   */\n  public static removeFromEnd<T>(list: SingleNode<T>, index: number): SingleNode<T> | null {\n    // 如果是个环，就没必要找了\n    if (this.checkCircle(list)) return list\n    let newNode = this.reverse(list)\n    let preNode = null\n    let pos = 0\n    while (newNode && pos !== index) {\n      newNode = newNode.next\n      pos++\n      preNode = newNode\n    }\n    if (!newNode) return null\n    if (preNode) {\n      preNode.next = newNode.next\n    }\n    return this.reverse(newNode)\n  }\n\n  public static findMidNode<T>(list: SingleNode<T>): SingleNode<T> | null {\n    if (!list) return null\n    let fast = list.next\n    let slow = list\n    while (fast && fast.next) {\n      fast = fast.next.next\n      slow = slow.next!\n    }\n    return slow\n  }\n\n  /**\n   * 有序链表的合并，根据不同的值进行插入\n   * @param a\n   * @param b\n   */\n  public static mergeSortedLists<T>(a: SingleNode<T>, b: SingleNode<T>): SingleNode<T> | null {\n    if (!a || !b) return a ? a : b\n    let p: SingleNode<T> | null = a\n    let q: SingleNode<T> | null = b\n    // 新链表的头部指针\n    let newList: SingleNode<T> | null = null\n    if (p.value < q.value) {\n      newList = p\n      p = p.next\n    } else {\n      newList = q\n      q = q.next\n    }\n    let currNode = newList\n    while (p && q) {\n      if (p.value < q.value) {\n        currNode.next = p\n        p = p.next\n      } else {\n        currNode.next = q\n        q = q.next\n      }\n      currNode = currNode.next\n    }\n    if (p) {\n      currNode.next = p\n    } else {\n      currNode.next = q\n    }\n    return newList\n  }\n}\n\nclass SingleNode<T> {\n  public value: T\n  public next: SingleNode<T> | null\n\n  constructor(value: T, next: SingleNode<T> | null = null) {\n    this.value = value\n    this.next = next\n  }\n}\n\nconst node1 = new SingleNode(1)\nnode1.next = new SingleNode(3)\nnode1.next.next = new SingleNode(5)\n\nconst node2 = new SingleNode(2)\nnode2.next = new SingleNode(7)\nnode2.next.next = new SingleNode(8)\nnode2.next.next.next = new SingleNode(10)\n\nconsole.log(LinkedListAlog.findMidNode(node1))\n"
  },
  {
    "path": "typescript/08_stack/StackAndBrowser.ts",
    "content": "/**\n * 基于单向链表实现栈结构\n */\nexport class Stack<T> {\n  private node: LinkedNode<T> | null = null\n  size: number = 0\n\n  public push(value: T) {\n    if (!value) return\n    const newNode = new LinkedNode(value)\n    if (!this.node) {\n      this.node = newNode\n    } else {\n      newNode.next = this.node\n      this.node = newNode\n    }\n    this.size++\n  }\n\n  public pop(): T | null {\n    if (!this.node) {\n      return null\n    }\n    const value = this.node.value\n    this.node = this.node.next\n    this.size--\n    return value\n  }\n}\n\n/**\n * 单向链表\n */\nclass LinkedNode<T> {\n  value: T\n  next: LinkedNode<T> | null\n\n  constructor(value: T, next: LinkedNode<T> | null = null) {\n    this.value = value\n    this.next = next\n  }\n}\n\n/**\n * 使用双栈结构实现浏览器的前进后退\n */\nclass Browser<T> {\n  // 存放后退的所有历史url\n  private backStack: Stack<T>\n  // 存放前进的所有url\n  private forwardStack: Stack<T>\n  private current: T\n\n  constructor(current: T) {\n    this.backStack = new Stack<T>()\n    this.forwardStack = new Stack<T>()\n    this.current = current\n  }\n\n  public back(): T | null {\n    if (this.backStack.size > 0) {\n      this.forwardStack.push(this.current)\n      this.current = this.backStack.pop()!\n      return this.getCurrentPage()\n    }\n    return null\n  }\n\n  public forward(): T | null {\n    if (this.forwardStack.size > 0) {\n      this.backStack.push(this.current)\n      this.current = this.forwardStack.pop()!\n      return this.getCurrentPage()\n    }\n    return null\n  }\n\n  /**\n   * 在网页上点击一个链接\n   * @param value\n   */\n  public linkUrl(value: T) {\n    this.current && this.backStack.push(this.current)\n    this.current = value\n  }\n\n  public getCurrentPage(): T {\n    return this.current\n  }\n}\n\nconst browser = new Browser('www.baidu.com')\nbrowser.linkUrl('www.yuanzhoucehui.com')\nbrowser.linkUrl('www.github.com/jsrdxzw')\n// browser.back()\n// www.github.com/jsrdxzw\nconsole.log(browser.getCurrentPage())\nbrowser.back()\n// www.yuanzhucehui.com\nconsole.log(browser.getCurrentPage())\nbrowser.back()\n// www.baidu.com\nconsole.log(browser.getCurrentPage())\nbrowser.back()\n// www.baidu.com\nconsole.log(browser.getCurrentPage())\nbrowser.forward()\n// www.yuanzhucehui.com\nconsole.log(browser.getCurrentPage())\nbrowser.forward()\n// www.github.com/jsrdxzw\nconsole.log(browser.getCurrentPage())\n"
  },
  {
    "path": "typescript/09_queue/CircularQueue.ts",
    "content": "/**\n * 基于数组的循环队列\n * 为了方便判断队列为空的情况，这里最好引入元素个数\n */\nclass CircularQueue<T> {\n  private items: T[] = []\n  private readonly n: number = 0\n  private head: number = 0\n  private tail: number = 0\n  // 队列的实际元素大小\n  size: number = 0\n\n\n  constructor(capacity: number) {\n    this.n = capacity\n  }\n\n  public enqueue(item: T): boolean {\n    // 表示队列已经满了\n    if (this.size === this.n) return false\n    this.items[this.tail] = item\n    this.tail = (this.tail + 1) % this.n\n    this.size++\n    return true\n  }\n\n  public dequeue(): T | null {\n    if (!this.size) return null\n    const item = this.items[this.head]\n    this.head = (this.head + 1) % this.n\n    this.size--\n    return item\n  }\n}\n\nconst circularQueue = new CircularQueue<number>(3)\ncircularQueue.enqueue(1)\ncircularQueue.enqueue(2)\ncircularQueue.enqueue(3)\ncircularQueue.enqueue(4)\n\nconst value = circularQueue.dequeue()\nconst value1 = circularQueue.dequeue()\nconst value2 = circularQueue.dequeue()\nconst value3 = circularQueue.dequeue()\n\n// null\nconsole.log(value3)\n// 0\nconsole.log(circularQueue.size)\n\n"
  },
  {
    "path": "typescript/09_queue/README.md",
    "content": "### 队列\n\n由于js语言天生就可以使用array数组来实现栈和队列等结构:\n```javascript\n  // 模拟数组队列\n  const queue = []\n  queue.push(1) // 向数组尾部添加数据\n  queue.shift() //数组头部去除数据，并返回\n```\n这里我们使用链表来实现队列结构\n\n队列分为无限队列和循环队列\n"
  },
  {
    "path": "typescript/09_queue/SimpleQueue.ts",
    "content": "/**\n * 使用链表实现简单队列\n */\nclass SimpleQueue<T> {\n  private head: LinkedNode<T> | null = null\n  private tail: LinkedNode<T> | null = null\n\n  /**\n   * 入队,插入队尾\n   * @param value\n   */\n  public enqueue(value: T) {\n    if (!this.tail) {\n      this.head = this.tail = new LinkedNode<T>(value)\n    } else {\n      const newNode = new LinkedNode<T>(value)\n      this.tail.next = newNode\n      this.tail = newNode\n    }\n  }\n\n  /**\n   * 出队,在队头删除\n   */\n  public dequeue(): T | null {\n    if (!this.head) return null\n    const value = this.head.value\n    this.head = this.head.next\n    return value\n  }\n\n  public printAll(): string {\n    let p = this.head\n    let res = ''\n    while (p) {\n      res = `${res} ${p.value}`\n      p = p.next\n    }\n    return res\n  }\n}\n\n/**\n * 单向链表\n */\nclass LinkedNode<T> {\n  value: T\n  next: LinkedNode<T> | null\n\n  constructor(value: T, next: LinkedNode<T> | null = null) {\n    this.value = value\n    this.next = next\n  }\n}\n\nconst queue = new SimpleQueue()\nqueue.enqueue(1)\nqueue.enqueue(2)\nqueue.enqueue(3)\nqueue.dequeue()\nqueue.dequeue()\nconsole.log(queue.printAll())\n"
  },
  {
    "path": "typescript/10_recursive/climbStairs.ts",
    "content": "/**\n * 递归求解爬楼梯问题\n */\n\n/**\n * 最开始版本的递归算法\n * @param n\n */\nfunction fn(n: number): any {\n  if (n === 1) return 1\n  if (n === 2) return 2\n  return fn(n - 1) + fn(n - 2)\n}\n\nconst res1 = fn(10)\n// 89\nconsole.log(res1)\n\n/**\n * 使用depth结合js的闭包特性实现限制函数调用次数的功能\n * @param depth 递归的深度\n */\nfunction fnWithDepth(depth: number) {\n  return function fn(n: number): any {\n    depth++\n    if (depth > 1000) throw new Error('function stack is too deep!')\n    if (n === 1) return 1\n    if (n === 2) return 2\n    return fn(n - 1) + fn(n - 2)\n  }\n}\n\nconst res2 = fnWithDepth(3)(10)\n// 89\nconsole.log(res2)\n\n/**\n * 通过map来存储已经计算过的值，避免递归重复计算\n */\nfunction fnWithMap() {\n  const map = new Map<number, number>()\n  return function fn(n: number): any {\n    if (n === 1) return 1\n    if (n === 2) return 2\n    if (map.has(n)) {\n      return map.get(n)\n    }\n    const ret = fn(n - 1) + fn(n - 2)\n    map.set(n, ret)\n    return ret\n  }\n}\n\nconst res3 = fnWithMap()(10)\nconsole.log(res3)\n"
  },
  {
    "path": "typescript/11_sorts/simpleSort.ts",
    "content": "/**\n * 简单的排序，分为冒泡和插入排序\n * 注意他们都是稳定的排序,并且是原地排序\n * 一般情况下更推荐使用插入排序，因为它所需要的操作更少\n * 这里使用简单工厂创建我们的排序算法\n */\n\n/**\n * 排序的枚举类型\n */\nenum SortType {\n  BubbleSort,\n  InsertSort\n}\n\ninterface SortAlgo {\n  sort(array: number[]): void\n}\n\nclass BubbleSort implements SortAlgo {\n  sort(array: number[]) {\n    for (let i = 0; i < array.length; i++) {\n      let flag = false\n      for (let j = 0; j < array.length; j++) {\n        if (array[j] > array[j + 1]) {\n          const temp = array[j]\n          array[j] = array[j + 1]\n          array[j + 1] = temp\n          flag = true\n        }\n      }\n      if (!flag) {\n        break\n      }\n    }\n  }\n}\n\nclass InsertSort implements SortAlgo {\n  sort(array: number[]) {\n    for (let i = 1; i < array.length; i++) {\n      let j = i - 1\n      const temp = array[i]\n      for (; j >= 0; j--) {\n        if (array[j] > array[j + 1]) {\n          array[j + 1] = array[j]\n        } else {\n          // 这个说明之前的已经排好了，没必要继续比较\n          break\n        }\n      }\n      array[j + 1] = temp\n    }\n  }\n}\n\nclass SortFactory {\n  static getSortAlgo(type: SortType): SortAlgo {\n    switch (type) {\n      case SortType.BubbleSort:\n        return new BubbleSort()\n      case SortType.InsertSort:\n        return new InsertSort()\n      default:\n        throw new Error('unknown sort algorithm type')\n    }\n  }\n}\n\nconst insertSort = SortFactory.getSortAlgo(SortType.InsertSort)\nconst test1 = [1, 0, 2, 4, 8, 5, 10]\ninsertSort.sort(test1)\nconsole.log(test1)\n\n\n"
  },
  {
    "path": "typescript/12_sorts/KthNum.ts",
    "content": "/**\n * O(n)的时间复杂度内求无序数组的第K大元素\n * 如[4,2,5,12,3]的第3大元素就是4\n * 这里也是使用了分治和分区的思想\n */\nclass KthNum {\n  getKthNum(array: number[], k: number): number {\n    const length = array.length\n    if (k > length) return -1\n    // q+1对应的元素一定比q和之前的元素大,q+1就是第q+1大元素\n    // 注意返回的q是数组下标，所以我们要加1才能表示第k个元素\n    let q = this.partition(array, 0, array.length - 1)\n    while (q + 1 !== k) {\n      if (q + 1 > k) {\n        q = this.partition(array, 0, q - 1)\n      } else {\n        q = this.partition(array, q + 1, length - 1)\n      }\n    }\n    return array[q]\n  }\n\n  /**\n   * 这里和快速排序一样\n   * @param array 数组的一部分\n   * @param p 开始坐标\n   * @param r 结束坐标\n   */\n  private partition(array: number[], p: number, r: number) {\n    const pivot = array[p]\n    let index = p + 1\n    for (let i = index; i <= r; i++) {\n      if (array[i] < pivot) {\n        this.swap(array, index, i)\n        index++\n      }\n    }\n    this.swap(array, p, index - 1)\n    return index - 1\n  }\n\n  private swap(array: number[], p: number, q: number) {\n    const temp = array[p]\n    array[p] = array[q]\n    array[q] = temp\n  }\n}\n\nconst testFindSortNum = [4, 2, 5, 12, 3]\nconst kthNum = new KthNum()\nconst num = kthNum.getKthNum(testFindSortNum, 3)\nconsole.log(num)\n"
  },
  {
    "path": "typescript/12_sorts/MergeSort.ts",
    "content": "/**\n * 归并排序\n * 稳定排序，稳定的O(nlgn)时间复杂度\n * O(n)的空间复杂度\n * 在小规模数据排序中很常用\n */\nclass MergeSort {\n  public static mergeSort(array: number[]) {\n    if (!array || !array.length) return\n    const length = array.length\n    this.mergeSortInternally(array, 0, length - 1)\n  }\n\n  static mergeSortInternally(array: number[], p: number, r: number) {\n    if (p >= r) return\n    // 严格按照中间值作切分点\n    // js中除法需要做取整操作，不然结果有可能是小数\n    const q = Math.floor(p + (r - p) / 2)\n    this.mergeSortInternally(array, p, q)\n    this.mergeSortInternally(array, q + 1, r)\n    this.mergeArray(array, p, q, r)\n  }\n\n  private static mergeArray(a: number[], p: number, q: number, r: number) {\n    let i = p\n    let j = q + 1\n    let k = 0\n    // 定义一个临时数组来存放排序的值\n    const tmp: number[] = []\n    while (i <= q && j <= r) {\n      if (a[i] <= a[j]) {\n        tmp[k++] = a[i++]\n      } else {\n        tmp[k++] = a[j++]\n      }\n    }\n    // 判断哪个子数组中有剩余的数据\n    let start = i\n    let end = q\n    if (j <= r) {\n      start = j\n      end = r\n    }\n    // 将剩余的数据拷贝到临时数组tmp\n    while (start <= end) {\n      tmp[k++] = a[start++]\n    }\n\n    // 将tmp中的数组拷贝回a[p...r]\n    for (i = 0; i <= r - p; i++) {\n      a[p + i] = tmp[i]\n    }\n  }\n}\n\nconst test4 = [1, 3, 2, 3, 10, 9, 7, 6, 0, 12]\nMergeSort.mergeSort(test4)\nconsole.log(test4)\n"
  },
  {
    "path": "typescript/12_sorts/quickSort.ts",
    "content": "/**\n * 快速排序是不稳定的排序\n * 原地排序，空间复杂度O（1），比归并排序使用更广泛\n * 平均复杂度基本接近O(nlg(n))\n */\n\nexport class QuickSort {\n  static sort(array: number[]): void {\n    this.sortInternally(array, 0, array.length - 1)\n  }\n  private static sortInternally(array: number[], p: number, r: number) {\n    if (p >= r) return\n    // 获取分界点\n    const q: number = this.partition(array, p, r)\n    this.sortInternally(array, p, q - 1)\n    this.sortInternally(array, q + 1, r)\n  }\n  private static partition(array: number[], p: number, r: number): number {\n    /**\n     * 参考值pivot，小于pivot的放在左边，大于pivot的在右边，最后再把分界点的值和它做交换\n     * 这样返回的index一定是值在中间的下标\n     */\n    const pivot = array[p]\n    // 分界点\n    let index = p + 1\n    for (let i = index; i <= r; i++) {\n      if (array[i] < pivot) {\n        this.swap(array, index, i)\n        // 找到了比标记值小的元素就移动分界点\n        index++\n      }\n    }\n    this.swap(array, p, index - 1)\n    return index - 1\n  }\n\n  private static swap(array: number[], p: number, q: number) {\n    const temp = array[p]\n    array[p] = array[q]\n    array[q] = temp\n  }\n}\n\nconst testSort = [1, 3, 2, 3, 10, 9, 7, 6, 0, -12]\nQuickSort.sort(testSort)\nconsole.log(testSort)\n"
  },
  {
    "path": "typescript/13_sorts/BucketSort.ts",
    "content": "/**\n * 桶排序对数据的要求比较高\n * 首先要知道数据的范围\n * 然后根据范围将数据分到小范围的桶中\n * 每个桶采用快速排序\n * 当桶的数量接近数据量大小的时候，时间复杂度为O(n)\n */\nimport { QuickSort } from '../12_sorts/quickSort'\n\nclass BucketSort {\n  static sort(array: number[], bucketSize: number = 5) {\n    const length = array.length\n    if (length === 0) return array\n    // 首先要确定数据的范围\n    let min = array[0]\n    let max = array[0]\n    for (let i = 0; i < length; i++) {\n      if (array[i] < min) {\n        min = array[i]\n      } else if (array[i] > max) {\n        max = array[i]\n      }\n    }\n\n    // 初始化桶,确定桶的数量\n    // 因为不能保证正好被整除，需要+1 存放剩余的元素\n    const bucketCount = Math.floor((max - min) / bucketSize) + 1\n    // 桶是个二维数组\n    const buckets = new Array(bucketCount)\n    for (let i = 0; i < bucketCount; i++) {\n      buckets[i] = []\n    }\n\n    // 利用映射函数将数据分配到各个桶中\n    // 这个时间复杂度为O(n)\n    for (let i = 0; i < length; i++) {\n      buckets[Math.floor((array[i]-min) / bucketSize)].push(array[i])\n    }\n    array.length = 0 // 返回数组\n    for (let i = 0; i < bucketCount; i++) {\n      // 每个桶里根据具体情况排序，使用插入排序或者快速排序等等\n      QuickSort.sort(buckets[i])\n      for (let j = 0; j < buckets[i].length; j++) {\n        array.push(buckets[i][j]);\n      }\n    }\n  }\n}\n\nconst bucketTest = [1, 3, 2, 3, 10, 9, 7, 6, 0, -12]\nBucketSort.sort(bucketTest)\nconsole.log(bucketTest)\n"
  },
  {
    "path": "typescript/13_sorts/CountingSort.ts",
    "content": "/**\n * 计数排序\n * 也是线性时间复杂度，和桶排序非常类似\n * 适用于值范围较小的大数据排序\n * 注意值范围需要不小于0，不然需要将数据预处理\n * 并非原地排序\n */\nclass CountingSort {\n  static sort(array: number[]) {\n    const length = array.length\n\n    // 找到这个数组的最大值\n    let max = array[0]\n    array.forEach((item) => {\n      if (item > max) {\n        max = item\n      }\n    })\n\n    // 初始化值范围数组\n    const countArray = new Array(max + 1).fill(0, 0, max + 1)\n    // 先计算每个元素的出现个数\n    for (let i = 0; i < length; i++) {\n      countArray[array[i]] = countArray[array[i]] + 1\n    }\n    // 计算元素的累计出现个数\n    for (let i = 1; i <= max; i++) {\n      countArray[i] = countArray[i - 1] + countArray[i]\n    }\n\n    // 接下来开始计数排序了\n    // 空间还是要申请\n    const sortedArray = []\n    // 倒序遍历能够达到稳定排序的作用\n    for (let i = length - 1; i >= 0; i--) {\n      // -1是为了填补sortedArray在0的位置,因为countArray在0的位置中一定么有值\n      const index = countArray[array[i]] - 1\n      sortedArray[index] = array[i]\n      countArray[array[i]]--\n    }\n    for (let i = 0; i < length; i++) {\n      array[i] = sortedArray[i]\n    }\n  }\n}\n\n\nconst testSort2 = [1, 3, 2, 3, 10, 9, 7, 6, 0]\nCountingSort.sort(testSort2)\nconsole.log(testSort2)\n\n"
  },
  {
    "path": "typescript/14_binarysearch/BinarySearch.ts",
    "content": "/**\n * 二分查找适合于连续内存的数组查找\n * 并且是已经排好序的数组\n * 时间复杂度只有log(n)\n */\nclass BinarySearch {\n  static bSearch(array: number[], target: number) {\n    if (!array || array.length === 0) return -1\n    const length = array.length\n    let low = 0\n    let high = length - 1\n    while (low <= high) {\n      // 一定是整数,这边的移位运算优先级低于+，-运算符，需要加括号\n      const mid = low + ((high - low) >> 1)\n      if (array[mid] === target) {\n        return mid\n      } else if (array[mid] > target) {\n        high = mid - 1\n      } else {\n        low = mid + 1\n      }\n    }\n    return -1\n  }\n}\n\nconst testBinarySearch = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]\nconsole.log(BinarySearch.bSearch(testBinarySearch, 10))\n"
  },
  {
    "path": "typescript/15_binarysearch/BinaryFind.ts",
    "content": "/**\n * 二分查找法在实际的场合使用不多\n * 我们更多的是看到一些查找法的变种\n */\nclass BinaryFind {\n  /**\n   * 在数组中查找第一个等于给定值的位置\n   * @param array 给定的要查找的范围数组\n   * @param target 要查找的目标值\n   * @return 目标在数组中的位置\n   */\n  static findFirstElement(array: number[], target: number): number {\n    const length = array ? array.length : null\n    if (!length || length === 0) return -1\n    let low = 0\n    let high = length - 1\n    while (low <= high) {\n      const mid = low + ((high - low) >> 2)\n      if (array[mid] > target) {\n        high = mid - 1\n      } else if (array[mid] < target) {\n        low = mid + 1\n      } else {\n        if (mid === 0 || array[mid - 1] !== target) {\n          return mid\n        } else {\n          high = mid - 1\n        }\n      }\n    }\n    return -1\n  }\n\n  /**\n   * 在数组中查找最后一个等于给定值的位置\n   * @param array 给定的要查找的范围数组\n   * @param target 要查找的目标值\n   */\n  static findLastElement(array: number[], target: number): number {\n    const length = array ? array.length : null\n    if (!length || length === 0) return -1\n    let low = 0\n    let high = length - 1\n    while (low <= high) {\n      const mid = low + ((high - low) >> 2)\n      if (array[mid] > target) {\n        high = mid - 1\n      } else if (array[mid] < target) {\n        low = mid + 1\n        // 这里已经是找到相等的元素了\n      } else {\n        if (mid === length - 1 || array[mid + 1] !== target) {\n          return mid\n        } else {\n          low = mid + 1\n        }\n      }\n    }\n    return -1\n  }\n\n  /**\n   * 在数组中查找第一个大于等于给定值的位置\n   * @param array 给定的要查找的范围数组\n   * @param target 要查找的目标值\n   */\n  static findFirstElementGreaterThanTarget(array: number[], target: number): number {\n    const length = array ? array.length : null\n    if (!length || length === 0) return -1\n    let low = 0\n    let high = length - 1\n    while (low <= high) {\n      const mid = low + ((high - low) >> 2)\n      if (array[mid] < target) {\n        low = mid + 1\n      } else {\n        if (mid === 0 || array[mid - 1] < target) {\n          return mid\n        } else {\n          high = mid - 1\n        }\n      }\n    }\n    return -1\n  }\n\n  /**\n   * 在数组中查找最后一个小于等于给定值的位置\n   * @param array 给定的要查找的范围数组\n   * @param target 要查找的目标值\n   */\n  static findLastElementLessThanTarget(array: number[], target: number): number {\n    const length = array ? array.length : null\n    if (!length || length === 0) return -1\n    let low = 0\n    let high = length - 1\n    while (low <= high) {\n      const mid = low + ((high - low) >> 2)\n      if (array[mid] > target) {\n        high = mid - 1\n      } else {\n        if (mid === length - 1 || array[mid + 1] > target) {\n          return mid\n        } else {\n          low = mid + 1\n        }\n      }\n    }\n    return -1\n  }\n}\n\nconst binaryFindTest = [1, 3, 4, 4, 5, 6, 8, 8, 8, 11, 18]\nconst target = BinaryFind.findLastElementLessThanTarget(binaryFindTest, -1)\nconsole.log(target)\n"
  },
  {
    "path": "typescript/17_skiplist/SkipList.ts",
    "content": "/**\n * 跳跃表是Redis使用的底层算法\n * 在增删改查都有近似O（log n）的时间复杂度\n * 哈希表虽然在不产生冲突的情况下是O（1）的时间复杂度\n * 但是随着冲突的增多，所需要的扩容操作还是比较耗时的，综合起来不一定快于跳表\n * 这两种结构可以互相补充\n * 下面摘抄一段来自知乎的话 (https://juejin.im/post/57fa935b0e3dd90057c50fbc）\n * 比较跳表和哈希表，平衡树之间的区别\n * skiplist和各种平衡树（如AVL、红黑树等）的元素是有序排列的，而哈希表不是有序的。因此，在哈希表上只能做单个key的查找，不适宜做范围查找。所谓范围查找，指的是查找那些大小在指定的两个值之间的所有节点。\n * 在做范围查找的时候，平衡树比skiplist操作要复杂。在平衡树上，我们找到指定范围的小值之后，还需要以中序遍历的顺序继续寻找其它不超过大值的节点。\n * 如果不对平衡树进行一定的改造，这里的中序遍历并不容易实现。而在skiplist上进行范围查找就非常简单，只需要在找到小值之后，对第1层链表进行若干步的遍历就可以实现。\n * 平衡树的插入和删除操作可能引发子树的调整，逻辑复杂，而skiplist的插入和删除只需要修改相邻节点的指针，操作简单又快速。\n * 从内存占用上来说，skiplist比平衡树更灵活一些。一般来说，平衡树每个节点包含2个指针（分别指向左右子树），而skiplist每个节点包含的指针数目平均为1/(1-p)，具体取决于参数p的大小。\n * 如果像Redis里的实现一样，取p=1/4，那么平均每个节点包含1.33个指针，比平衡树更有优势。\n * 查找单个key，skiplist和平衡树的时间复杂度都为O(log n)，大体相当；而哈希表在保持较低的哈希值冲突概率的前提下，查找时间复杂度接近O(1)，性能更高一些。所以我们平常使用的各种Map或dictionary结构，大都是基于哈希表实现的。\n * 从算法实现难度上来比较，skiplist比平衡树要简单。\n */\nexport class SkipList<T> {\n  // head和tail始终指向最顶层的首位节点，通过链表能访问任何位置\n  private head: SkipListNode<T>\n  private tail: SkipListNode<T>\n\n  // 索引的层数，0表示最底层\n  private levelCount = 0\n\n  // 元素的个数\n  private size = 0\n\n  // private readonly MAX_LEVEL = 16\n\n\n  constructor() {\n    this.head = new SkipListNode<T>(SkipListNode.negInf, null)\n    this.tail = new SkipListNode<T>(SkipListNode.posInf, null)\n  }\n\n  public insert(key: number, value: T): void {\n    let p: SkipListNode<T>\n    let q: SkipListNode<T>\n\n    let i: number = 0\n\n    // 先查找位置\n    p = this.findNode(key)\n\n    // 如果跳跃表中的值已经存在了，直接赋值即可\n    if (p.key === key) {\n      p.value = value\n      return\n    }\n\n    // 没有该值，则进行插入操作,应该是插在p节点的右边. p -> q -> ?\n    q = new SkipListNode(key, value)\n    q.left = p\n    q.right = p.right\n    if (p.right) {\n      p.right.left = q\n    }\n    p.right = q\n\n    // 再使用随机数决定是否要向更高层攀升\n    while (Math.random() < 0.5) {\n      // 如果新元素的级别已经达到跳跃表的最大高度，则新建空白层\n      if (i >= this.levelCount) {\n        this.addEmptyLevel()\n      }\n\n      // 从p向左扫描含有高层节点的节点, 方便节点在每一层插入\n      while (!p.up) {\n        p = p.left!\n      }\n      p = p.up\n\n      // 新值对应的索引，这里不需要存value了，因为只需要最底层存value即可\n      const z = new SkipListNode<T>(key, null)\n\n      z.left = p\n      z.right = p.right\n      if (p.right) {\n        p.right.left = z\n      }\n      p.right = z\n\n      z.down = q\n      q.up = z\n\n      q = z\n      i = i + 1\n    }\n    this.size++\n  }\n\n  public get(key: number): T | null {\n    const p = this.findNode(key)\n    return p.key === key ? p.value : null\n  }\n\n  public remove(key: number) {\n    let p: SkipListNode<T> | undefined = this.findNode(key)\n    if (p.key !== key) return\n\n    while (p != null) {\n      p.left!.right = p.right\n      p.right!.left = p.left\n      p = p.up\n    }\n  }\n\n  private addEmptyLevel() {\n\n    const p1: SkipListNode<T> = new SkipListNode(SkipListNode.negInf, null)\n    const p2: SkipListNode<T> = new SkipListNode(SkipListNode.posInf, null)\n\n    p1.right = p2\n    p1.down = this.head\n\n    p2.left = p1\n    p2.down = this.tail\n\n    this.head.up = p1\n    this.tail.up = p2\n\n    this.head = p1\n    this.tail = p2\n\n    this.levelCount++\n  }\n\n  private findNode(key: number): SkipListNode<T> {\n    const { head } = this\n    let p = head\n    while (true) {\n      // 从左向右查找，直到右节点的key值大于要查找的key值\n      while (p.right && p.right.key !== SkipListNode.posInf && p.right.key <= key) {\n        p = p.right\n      }\n      // 如果有更低层的节点，则向低层移动\n      if (p.down) {\n        p = p.down\n      } else {\n        break\n      }\n    }\n    // 这里返回的p的key值，是小于等于要找的key值的\n    return p\n  }\n}\n\nexport class SkipListNode<T> {\n  key: number\n  value: T | null\n  up?: SkipListNode<T>\n  down?: SkipListNode<T>\n  left?: SkipListNode<T>\n  right?: SkipListNode<T>\n\n  constructor(key: number, value: T | null) {\n    this.key = key\n    this.value = value\n  }\n\n  // 最小的数，无限接近于0，用于表示左标兵\n  static negInf: number = Number.MIN_VALUE\n  // 最大的数，用于表示右标兵\n  static posInf: number = Number.MAX_VALUE\n}\n\nconst testSkipList = new SkipList()\ntestSkipList.insert(12, 'qwe')\ntestSkipList.insert(3, 'mmm')\nconsole.log(testSkipList.get(3))\n"
  },
  {
    "path": "typescript/24_treesearch/TreeSearch.ts",
    "content": "/**\n * 二叉树的增删查操作\n */\nexport class TreeSearch {\n  private root?: Node\n\n  public find(data: number) {\n    let p = this.root\n    while (p != null) {\n      if (data < p.data) {\n        p = p.left\n      } else if (data > p.data) {\n        p = p.right\n      } else {\n        return p\n      }\n    }\n    return p\n  }\n\n  public insert(data: number) {\n    if (this.root == null) {\n      this.root = new Node(data)\n      return\n    }\n    let p = this.root\n    while (p != null) {\n      if (data < p.data) {\n        if (p.left == null) {\n          p.left = new Node(data)\n          return\n        } else {\n          p = p.left\n        }\n      } else {\n        if (data > p.data) {\n          if (p.right == null) {\n            p.right = new Node(data)\n            return\n          } else {\n            p = p.right\n          }\n        }\n      }\n    }\n  }\n\n  /**\n   * 删除的时候分三种情况\n   * @param data\n   */\n  public delete(data: number) {\n    // 先把对应的元素找出来,并且为了删除，也得记录父节点的内存地址\n    // 包含了null或者undefined两种情况\n    if (this.root == null) return\n    let p: Node | undefined = this.root\n    let pp = this.root\n    while (p != null && p.data !== data) {\n      pp = p\n      data > p.data ? p = p.right : p = p.left\n    }\n    // 没有找到要删除的元素\n    if (p == null) return\n    // 要删除的元素有两个子节点,需要找到右子树的最小节点，然后赋值\n    if (p.left != null && p.right != null) {\n      // 右子树的最小节点\n      let minP = p.right\n      // 右子树的最小节点的父节点\n      let minPP = p\n      while (minP.left != null) {\n        minPP = minP\n        minP = minP.left\n      }\n      p.data = minP.data\n      p = minP\n      pp = minPP\n    }\n\n    // 有一个子节点或者没有子节点\n    let child\n    if (p.left != null) {\n      child = p.left\n    } else if (p.right != null) {\n      child = p.right\n    } else {\n      child = null\n    }\n    if (pp == null) {\n      this.root = undefined\n    } else if (pp.left === p) {\n      pp.left = child!\n    } else {\n      pp.right = child!\n    }\n  }\n\n  public printAllData(): void {\n    this.printAll(this.root)\n  }\n\n  private printAll(node?: Node): void {\n    if (node == null) return\n    console.log(node.data)\n    this.printAll(node.left)\n    this.printAll(node.right)\n  }\n}\n\nexport class Node {\n  data: number\n  left?: Node\n  right?: Node\n\n  constructor(data: number) {\n    this.data = data\n  }\n}\n\nconst treeSearch = new TreeSearch()\ntreeSearch.insert(1)\ntreeSearch.insert(4)\ntreeSearch.insert(3)\ntreeSearch.insert(2)\ntreeSearch.insert(5)\ntreeSearch.insert(0)\n\ntreeSearch.delete(4)\n\ntreeSearch.printAllData()\n"
  }
]