[
  {
    "path": "1_Fundamental/README.md",
    "content": "代码链接:\nxxxxxxxxx\n  \n# 1.binary search   \nurl get data练习  \nedu.princeton.cs.algs4 普林斯顿算法 jar包   \n  \n# 2.dataStruct   \nqueue stack bag  链表实现   \n  \n# 3.Union Find  \n连通集的查找   \nAPI: union find connected  \nkey: 辅助数组id   \n>Quick find   \nid[] 相同集合的元素,id[]相同   \n   \n>Quick union  \nid[] 当前元素的root节点序号  \n  \n>Weighted QuickUnion  \nunion操作加入size判断  使得较小的子树root指向较大的子树root\n小树加到大树上 避免origin quick union depth的过深   \n\n代码不在这里加了   一定要掌握   \n\n\n``` java\n//TODO iterator()方法返回一个实现hasnext() 和 next()方法的迭代器\n\n//first是自己定义的node类型 含有item和next结构\n\npublic Iterator<Item> iterator()  {\n    return new ListIterator<Item>(first);\n}\n\n// an iterator, doesn't implement remove() since it's optional\nprivate class ListIterator<Item> implements Iterator<Item> {\n    private Node<Item> current;\n\n    public ListIterator(Node<Item> first) {\n        current = first;\n    }\n\n    public boolean hasNext()  { return current != null;                     }\n    public void remove()      { throw new UnsupportedOperationException();  }\n\n    public Item next() {\n        if (!hasNext()) throw new NoSuchElementException();\n        Item item = current.item;\n        current = current.next;\n        return item;\n    }\n}\n```\n"
  },
  {
    "path": "1_Fundamental/UnionFind.h",
    "content": "#ifndef UNIONFIND_H_INCLUDED\n#define UNIONFIND_H_INCLUDED\n#include\"func.h\"\n/*\n实现weighted union find\n小树加入大树 从而减少树的高度\n*/\n\nclass UnionFind{\nprivate:\n    vector<int> parent;\n    vector<int> sz;\n    int unionCount;\npublic:\n    UnionFind(int vertexNum)\n    :unionCount(vertexNum){\n        parent.resize(vertexNum);\n        sz.resize(vertexNum,1);\n        for(int i=0;i<vertexNum;i++)\n            parent[i]=i;\n    }\n    int getUnionNum(){return unionCount;}\n\n    //API= find,union,connected\n    bool connected(int v,int w){\n        return findRoot(v) == findRoot(w);\n    }\n    int findRoot(int p){\n        while(p != parent[p])\n            p=parent[p];\n        return p;\n    }\n    void unionV(int v,int w){\n        int i=findRoot(v);\n        int j=findRoot(w);\n        if(i==j) return;\n        if(sz[i]<sz[j]){parent[i]=j; sz[j]+=sz[i];}\n        else{parent[j]=i; sz[i]+=sz[j];}\n        unionCount--;\n    }\n};\n\n#endif // UNIONFIND_H_INCLUDED\n"
  },
  {
    "path": "2_Sorting/HeapSort.h",
    "content": "#ifndef HEAPSORT_H_INCLUDED\n#define HEAPSORT_H_INCLUDED\n#include\"func.h\"\n\n/*\n堆排序\n1.构建有序堆(最大堆)\n2.不断取出maxVal,在重新有序化即可\n3.直到堆中不存在新元素\n\nnote:有序化API\nswim 上浮\nsink 下沉\n*/\n\n//e.g.   1....n序号的数组进行排序\nclass HeapSort{\npublic:\n    static void hsort(vector<int>& nums){\n        int n = nums.size();\n        for(int k=n/2;k>=1;k--)\n            sink(nums,k,n);\n        while(n>1){\n            swap(nums,1,n--);        //最大值放在数组后面保存\n            sink(nums,1,n);\n        }\n    }\nprivate:\n    //父节点k 不断下沉  直到k大于两个子节点\n    static void sink(vector<int>& nums,int k,int n){\n        while(2*k<=n){\n            //k的子节点2k 2k+1中val较大值为j\n            int j=2*k;\n            if(j<n && less(nums,j,j+1)) j++;\n\n            if(!less(nums,k,j))\n                break;\n            swap(nums,k,j);\n            k=j;\n        }\n    }\n\n    //helper function\n    //由于数组由0开始作为需要标记\n    static void swap(vector<int>&nums,int i,int j){\n        int tmp = nums[i-1];\n        nums[i-1]=nums[j-1];\n        nums[j-1]=tmp;\n    }\n    static bool less(vector<int>&nums,int i,int j){\n        return nums[i-1]<nums[j-1];\n    }\n};\n\n\n#endif // HEAPSORT_H_INCLUDED\n"
  },
  {
    "path": "2_Sorting/Heapsort_test.cpp",
    "content": "#include\"HeapSort.h\"\n/*\n堆排序\n1.构建有序堆(最大堆)\n2.不断取出maxVal,在重新有序化即可\n3.直到堆中不存在新元素\n\nnote:有序化API\nswim 上浮\nsink 下沉\n*/\nint main(){\n    vector<int> test={5,4,4,4,44,-1,2,7};\n    HeapSort::hsort(test);\n    for(auto i:test)\n        cout<<i<<\" \"<<endl;\n}\n"
  },
  {
    "path": "2_Sorting/Insertion.h",
    "content": "#ifndef INSERTION_H_INCLUDED\n#define INSERTION_H_INCLUDED\n#include\"func.h\"\nclass Insertion{\n    //class 默认private\npublic:\n    static void insort(vector<int>& nums){\n        int len = nums.size();\n        for(int i=1;i<len;i++){\n            for(int j=i; j>0&&nums[j]<nums[j-1]; j--)\n                std::swap(nums[j],nums[j-1]);\n        }\n    }\n};\n\n\n#endif // INSERTION_H_INCLUDED\n"
  },
  {
    "path": "2_Sorting/Insertion_test.cpp",
    "content": "#include\"Insertion.h\"\n\nint main(){\n    vector<int> test={5,4,-1,2,7};\n    Insertion::insort(test);\n    for(auto i:test)\n        cout<<i<<\" \"<<endl;\n}\n"
  },
  {
    "path": "2_Sorting/Quick3way.h",
    "content": "#ifndef QUICK3WAY_H_INCLUDED\n#define QUICK3WAY_H_INCLUDED\n#include\"func.h\"\n/*\n三向快速排序\n每次递归将 小数组首元素相同的元素放在相邻位置\n\n优点:\n对于大量重复的元素的数组排序 优于传统快排\n*/\n\nclass Quick3way{\npublic:\n    static void q3sort(vector<int>& nums){\n        q3sort(nums,0,(int)nums.size()-1);\n    }\n    static void q3sort(vector<int>& nums,int lo, int hi){\n        if(hi<lo) return;\n        int lt=lo,gt=hi,i=lo+1;\n        int v=nums[lo];\n\n        while(i<=gt){\n            if(nums[i]<v)   std::swap(nums[lt++],nums[i++]);\n            else if(nums[i]>v) std::swap(nums[i],nums[gt--]);\n            else i++;\n        }\n        //得到  nums[lo..lt-1] < v=nums[lt..gt] < nums[gt+1...hi]\n        q3sort(nums,lo,lt-1);\n        q3sort(nums,gt+1,hi);\n    }\n};\n\n\n#endif // QUICK3WAY_H_INCLUDED\n"
  },
  {
    "path": "2_Sorting/Quick3way_test.cpp",
    "content": "#include\"Quick3way.h\"\n\nint main(){\n    vector<int> test={5,4,4,4,44,-1,2,7};\n    Quick3way::q3sort(test);\n    for(auto i:test)\n        cout<<i<<\" \"<<endl;\n}\n"
  },
  {
    "path": "2_Sorting/README.md",
    "content": "代码链接:   \nhttps://github.com/ISCASTEAM/Algorithm   \n\n# 1.Elementray Sorts  \n> Select sort P156  \nkey:选择第i小的元素放入a[i]位置  \nnote:运行时间和输入顺序无关  \n   \n>Insertion sort P157  \nkey:当前索引左边的所有元素是有序的,但是最终位置尚不确定  \nnote:运行时间和输入顺序相关(1.部分有序 2.小数组)建议使用insert sort  \nnote2:有稳定性  \nres: time(insert) < time(select)  \n  \n>Shell sort P162  \nkey:数组中间隔h步长的元素是有序的, h从N/3递减为1  \nkey2:基于插入排序((只会交换相邻元素),shell sort是交换h步长的元素  \nnote:避免插入排序的极端情况--最后一位是最小元素--插入排序需要逐步移动到起始位置  \n  \n# 2.Merge Sort  \nkey:归并两个有序的数组;     \nkey2:递归到小数组;在进行归并(辅助数组N)    \nnote:有稳定性    \nimprovement:1.加快小数组排序(n < 8,则使用插入排序) 2.递归中交换参数避免重复辅助数组的赋值(MergeX file)      \n\n# 3.Quick Sort  \nkey: partition切分   \nimprovement:1.n < 8,则使用插入排序 2.中位数作为a[lo] 3.random shuffle   \n4.三向快排   \n```java\n// quicksort the subarray a[lo .. hi] using 3-way partitioning\nvoid sort(Comparable[] a, int lo, int hi) {\n    if (hi <= lo) return;\n    int lt = lo, gt = hi;\n    Comparable v = a[lo];\n    int i = lo + 1;\n    //TODO  小于v的值放在lt左侧 大于v的值放在gt右侧\n    //TODO 等于v的值在 (lt,i)之间\n    while (i <= gt) {\n        int cmp = a[i].compareTo(v);\n        if      (cmp < 0) exch(a, lt++, i++);       //a[i] < v  交换a[i]和a[lt]\n        else if (cmp > 0) exch(a, i, gt--);         //a[i] > v  交换a[i]和a[gt]\n        else              i++;\n    }\n\n    // a[lo..lt-1] < v = a[lt..gt] < a[gt+1..hi].\n    sort(a, lo, lt-1);\n    sort(a, gt+1, hi);\n    assert isSorted(a, lo, hi);\n}  \n```\n\n# 4.Priority Queue   \nkey: ordered array实现 或者 unordered array实现 或者 二叉树实现   \nkey2: 二叉树findMax+insert 都是是O(logN)  \nnote: 无法利用缓存 因为数组元素很少直接和相邻元素比较  \n> heap   \nkey: 父节点序号k,则子节点为2k,2k+.  子节点为k,父节点为k/2   \nkey:sink swim要掌握   \n```java\nvoid sort(Comparable[] pq) {\n    int n = pq.length;\n    //TODO 构建有序堆\n    //TODO 相当于从倒数第二层开始遍历到root  sink下沉构建\n    for (int k = n/2; k >= 1; k--)\n        sink(pq, k, n);\n    //TODO 当前最大值pq[1]和最后一位交换 + 重新sink剩余(n-1)个元素\n    while (n > 1) {\n        exch(pq, 1, n--);\n        sink(pq, 1, n);\n    }\n}\n\nvoid sink(Comparable[] pq, int k, int n) {\n    while (2*k <= n) {\n        //TODO k的子节点2k, 2k+1 中val较大的值 == j\n        int j = 2*k;\n        if (j < n && less(pq, j, j+1)) j++;\n        //TODO  不需要交换 则break\n        if (!less(pq, k, j)) break;\n        exch(pq, k, j);\n        k = j;\n    }\n}\n```\n"
  },
  {
    "path": "3_Searching/BinarySearchTree.h",
    "content": "#ifndef BINARYSEARCHTREE_H_INCLUDED\n#define BINARYSEARCHTREE_H_INCLUDED\n#include\"func.h\"\n/*\nkey: Int\nval: string\n*/\n\nclass BST{\nprivate:\n    class Node{\n    public:\n        int key;\n        string val;\n        int Size;\n        Node* left;\n        Node* right;\n        Node(int k,string v,int s)\n        :key(k),val(v),Size(s){\n            left=NULL;\n            right=NULL;\n        }\n    };\n    Node* root;\npublic:\n    BST(){root=NULL;}\n    int sizeTree(Node* root){if(root==NULL) return 0;else return root->Size;}\n    int sizeTree(){return sizeTree(root);}\n    bool isEmpty(){return sizeTree()==0;}\n    bool contains(int key){ return get(key)!=\"\";}\n    string get(int key){return get(root,key);}\n    string get(Node* root,int key){\n        if(root->key==key) return root->val;\n        else if(root->key > key)\n            return get(root->left,key);\n        else\n            return get(root->right,key);\n    }\n    //插入新元素 或者 更新val\n    void put(int key,string val){\n//        cout<<key<<\" \"<<val<<endl;\n        root = put(root,key,val);\n    }\n    Node* put(Node* root,int key,string val){\n        if(root==NULL)\n            return new Node(key,val,1);\n        if(root->key == key)\n            root->val = val;\n        else if(root->key > key)\n            root->left = put(root->left,key,val);\n        else\n            root->right = put(root->right,key,val);\n\n        root->Size = 1+sizeTree(root->left)+sizeTree(root->right);\n        return root;\n    }\n    //删除极小值\n    void deleteMin(){root=deleteMin(root);}\n    Node* deleteMin(Node* root){\n        if(root->left==NULL) return root->right;    //删除根节点\n        root->left = deleteMin(root->left);\n        root->Size = sizeTree(root->left) + sizeTree(root->right);\n        return root;\n    }\n    //删除\n    void deleteKey(int key){root = deleteKey(root,key);}\n    Node* deleteKey(Node* root, int key){\n        if(root->key > key)\n            root->left = deleteKey(root->left,key);\n        else if(root->key < key)\n            root->right = deleteKey(root->right,key);\n        else{\n            if(root->right==NULL) return root->left;\n            if(root->left==NULL) return root->right;\n            //待删除节点 左右子树均不为空\n            //右子树最小节点替换待删除节点\n            Node* tmp = root;\n            root = minLeaf(root->right);    //右子树最小节点\n            root->right = deleteMin(tmp->right);\n            root->left = tmp->left;\n        }\n    }\n    //寻找最小leaf\n    Node* minLeaf(Node* root){\n        if(root->left==NULL) return root;\n        else return minLeaf(root->left);\n    }\n    vector<int> print(){\n        vector<int> BFS;\n        queue<Node*> pq;\n        pq.push(root);\n        while(!pq.empty()){\n            Node* tmp = pq.front();\n            pq.pop();\n            BFS.push_back(tmp->key);\n            if(tmp->left!=NULL)\n                pq.push(tmp->left);\n            if(tmp->right!=NULL)\n                pq.push(tmp->right);\n        }\n        return BFS;\n    }\n\n};\n\n\n#endif // BINARYSEARCHTREE_H_INCLUDED\n"
  },
  {
    "path": "3_Searching/BinarySearchTree_test.cpp",
    "content": "#include\"BinarySearchTree.h\"\n\nint main(){\n    BST* bst = new BST();\n    std::ostringstream ss;\n    for(int i=0;i<10;i++){\n        ss << i;\n        bst->put(i,ss.str());\n    }\n\n    bst->deleteKey(5);\n    vector<int> BFS = bst->print();\n    for(auto i:BFS) //按照层级输出\n        cout<<i<<\" \";\n}\n"
  },
  {
    "path": "3_Searching/README.md",
    "content": "代码链接:   \nhttps://github.com/ISCASTEAM/Algorithm    \n\n# Elementray Tables  \n>无序链表   \nkey: 插入时间O(1)  查找时间O(N)  \nnote: 新元素直接插入链表头部  \n>有序数组  \nkey:查找使用二分查找  \nkey: 插入时间O(N)  查找时间O(lgN)  \nnote: 插入元素  \n\n# BinarySearchTree  \n>1.基本实现  \nget/put 时间复杂度O(1.39lgN)  \nnote: put操作 节点总是最后一层的leaf  \nnote: 随机的数据可能导致树的不平衡  复杂度为1.39lgN  \n\n>2.有序性相关API  \nflooring 向下取整    \n向下取整:  tree中的节点 不大于给定查找值key  \nceiling 向上取整    \nselect rank  \nmax最后一层最右边的节点  min最后一层最左边的节点  \n  \n>3.删除delete  \n删除的节点使用其右子树的最小值替换  \n```java\nprivate Node delete(Node x, Key key) {\n    if (x == null) return null;\n\n    int cmp = key.compareTo(x.key);\n    if      (cmp < 0) x.left  = delete(x.left,  key);\n    else if (cmp > 0) x.right = delete(x.right, key);\n    else {\n        if (x.right == null) return x.left;\n        if (x.left  == null) return x.right;\n        //TODO 待删除节点x 有左右子树\n        //TODO min(x.right) x右子树最小节点替换待删除节点x\n        Node t = x;\n        x = min(t.right);   //min也是寻找最小节点的函数\n        x.right = deleteMin(t.right);//删掉右子树最小值\n        x.left = t.left;\n    }\n    x.size = size(x.left) + size(x.right) + 1;\n    return x;\n}\n```\n>4. 范围查找    \nnote: 中序遍历的二叉搜索树是有序的;  queue;    \nnote: 掌握inorder的递归代码格式  P268    \n  \n# BalancedSearchTree    \n>1. 2-3树    \nkey数目是2, value数目是3(小于,中间,大于)    \nnote: 生长方式=自下而上    \n理解 不同情况的下的插入变换方法    \n\n>2. 红黑树    \nnote: 时间复杂度 1.00lgN  (对比二叉树),因为红黑树是平衡的    \nnote: 思考:新插入的节点都是红色的    \n\nC++重构 lite版本的  代码链接:https://github.com/ISCASTEAM/Algorithm/tree/master/Tree\n重点: 两种旋转 + 颜色转换 P280    \n\nP275   \n各种插入情况 汇总为三句话  \n*c++插入实现*  \n```cpp\nRedBlackTree::Node* RedBlackTree::put(Node* h,int key,string val){\n    if(h==NULL){\n        _size ++;\n        return new RedBlackTree::Node(key,val,_red);\n    }\n    if(h->_key == key) h->_val = val;\n    else if(h->_key > key) h->left = put(h->left,key,val);\n    else h->right = put(h->right,key,val);\n\n    //TODO fix-up links\n    if(isRed(h->right) && !isRed(h->left))  h = rotateLeft(h);\n    if(isRed(h->left) && isRed(h->left->left))h = rotateRight(h);\n    if(isRed(h->left) && isRed(h->right)) flipColors(h);\n\n    return h;\n}\n```\n\n*左右旋转 + 颜色转换*  \n```cpp\nNode* rotateRight(Node* h){\n    Node* tmp = h->left;\n    h->left = tmp->right;\n    tmp->right = h;\n    tmp->color = h->color;\n    h->color = _red;\n    return tmp;\n}\nNode* rotateLeft(Node* h){\n    Node* tmp = h->right;\n    h->right = tmp->left;\n    tmp->left = h;\n    tmp->color = h->color;\n    h->color = _red;\n    return tmp;\n}\nvoid flipColors(Node* h){\n    h->color = _red;\n    h->left->color = _black;\n    h->right->color = _black;\n}\n```\n*BST删除操作 P291*  \n\n\n# HashTable  \n*get put 都是常数时间*  \nkey:散列函数将key转化为数组索引; 处理碰撞冲突  \ne.g java return (key.hashcode() & 0x7fffffff) % M  \nnote: 缺点 =》 有序性的API无法支持  \n>1. 拉链法 P297  \n相同key,链表串起来val   \n  \n>2. 线性探测法  \n碰撞发生时,检测下一个位置是否为Null  \nnote: 删除操作,两个方法1.后面非空元素重新put一遍 2.置空NULL + 需要将后面元素前移一位    \n\n  \n# Application   \n思考:  \n稀疏矩阵 * vector如何在常数时间内完成  特别是当矩阵非常大时候  \nP323  \n"
  },
  {
    "path": "3_Searching/RedBlackTree.h",
    "content": "#include \"func.h\"\n\nclass RedBlackTree{\npublic:\n    RedBlackTree():_root(NULL),_size(0){};\n    string get(int key){ return get(_root,key);}\n    void put(int key,string val){\n        _root = put(_root,key,val);\n        _root->color = _black;\n    }\n\nprivate:\n    class Node{\n    public:\n        Node(int key,string val,bool color)\n        :_key(key),_val(val),color(color){\n            left = NULL;\n            right = NULL;\n        }\n    //private:\n        int _key;\n        string _val;\n        Node* left;\n        Node* right;\n        bool color;\n    };\n    Node* _root;\n    int _size;\n\n    string get(Node*,int);\n    Node* put(Node*,int,string);\n\n    Node* rotateRight(Node* h){\n        Node* tmp = h->left;\n        h->left = tmp->right;\n        tmp->right = h;\n        tmp->color = h->color;\n        h->color = _red;\n        return tmp;\n    }\n    Node* rotateLeft(Node* h){\n        Node* tmp = h->right;\n        h->right = tmp->left;\n        tmp->left = h;\n        tmp->color = h->color;\n        h->color = _red;\n        return tmp;\n    }\n    void flipColors(Node* h){\n        h->color = _red;\n        h->left->color = _black;\n        h->right->color = _black;\n    }\n    bool isRed(Node* x){\n        if(x == NULL) return false;\n        return x->color == _red;\n    }\n    int getmin(Node* root){\n        int key = -1;\n        while(root!=NULL){\n            key = root->_key;\n            root =  root->left;\n        }\n        return key;\n    }\n    int getmax(Node* root){\n        int key = -1;\n        while(root!=NULL){\n            key = root->_key;\n            root = root->right;\n        }\n        return key;\n    }\n    void getKeys(Node* root,queue<int>& res){\n        //inorder BFS\n        if(root==NULL) return;\n        getKeys(root->left,res);\n        res.push(root->_key);\n        getKeys(root->right,res);\n    }\npublic:\n    bool contains(int){ return _size;}\n    int getsize(){return this->_size;}\n    int getmin(){ return getmin(_root);}\n    int getmax(){return getmax(_root);}\n    void getKeys(queue<int>& res){ return getKeys(_root,res);}\n    int getRoot(){return _root->_key;}\n    //each path from root to leaf has the same black number\n    bool isBalanced(){\n        int black = 0;\n        Node* root = _root;\n        while(root!=NULL){\n            if(!isRed(root)) black++;\n            root = root->left;\n        }\n        return isBalanced(root,black);\n    }\n    bool isBalanced(Node* root, int numBlack){\n        if(root==NULL) return numBlack==0;\n        if(isRed(root)) numBlack--;\n        return isBalanced(root->left,numBlack) &&isBalanced(root,numBlack);\n    }\nprotected:\n    const bool _red = true;\n    const bool _black = false;\n\n};\n\nstring RedBlackTree::get(Node* x, int key){\n    while(x != NULL){\n        if(x->_key == key) return x->_val;\n        else if(x->_key > key) return get(x->left,key);\n        else return get(x->right,key);\n    }\n    return NULL;\n}\n\nRedBlackTree::Node* RedBlackTree::put(Node* h,int key,string val){\n    if(h==NULL){\n        _size ++;\n        return new RedBlackTree::Node(key,val,_red);\n    }\n    if(h->_key == key) h->_val = val;\n    else if(h->_key > key) h->left = put(h->left,key,val);\n    else h->right = put(h->right,key,val);\n\n    //TODO fix-up links\n    if(isRed(h->right) && !isRed(h->left))  h = rotateLeft(h);\n    if(isRed(h->left) && isRed(h->left->left))h = rotateRight(h);\n    if(isRed(h->left) && isRed(h->right)) flipColors(h);\n\n    return h;\n}\n\n\n"
  },
  {
    "path": "3_Searching/RedBlackTree_test.cpp",
    "content": "\n#include \"RedBlackTree.h\"\n\nint main(){\n    string test = \"ABCDEFG\";\n    RedBlackTree* rbt = new RedBlackTree();\n    for(int i=0;i<7;i++){\n//        cout<<string(1,test[i])<<endl;\n        rbt->put(i,string(1,test[i]));\n    }\n    cout<<\"root=\"<<rbt->getRoot()<<endl;\n    cout<<\"isBalanced=\"<<rbt->isBalanced()<<endl;\n    cout<<\"tree size=\"<<rbt->getsize()<<endl;\n    cout<<\"key min=\"<<rbt->getmin()<<endl;\n    cout<<\"key max=\"<<rbt->getmax()<<endl;\n\n    cout<<endl;\n    cout<<\"Testing:\"<<endl;\n    //inorder to get key\n    queue<int> res;\n    rbt->getKeys(res);\n    while(!res.empty()) {cout<<res.front()<<\" \"; res.pop();}\n    cout<<endl;\n    cout<<\"get key==3, val=\"<<rbt->get(3)<<endl;\n    return 0;\n}\n"
  },
  {
    "path": "4_Graphs/BFSPath.h",
    "content": "#ifndef BFSPATH_H_INCLUDED\n#define BFSPATH_H_INCLUDED\n\n#include\"Graph.h\"\nclass BFSPath{\nprivate:\n    vector<bool> marked;\n    vector<int> edgeto;     //父节点\npublic:\n    BFSPath(Graph* G){\n        marked.resize(G->getV(),false);\n        edgeto.reserve(G->getV());\n    }\n\n    void bfs(Graph* G, int s){\n        /*\n        input:  图结构Graph, 起始点S\n        */\n        queue<int> q;\n        marked[s] = true;\n        q.push(s);\n\n        while(!q.empty()){\n            int v = q.front();\n            q.pop();\n            for(int w:G->getadj(v)){\n                if(!marked[w]){\n                    edgeto[w] = v;          //区别于BFS代码\n                    marked[w]=true;\n                    q.push(w);\n                }\n            }\n        }\n    }\n    //从起始点S到节点V的路径\n    void pathTo(stack<int>& path,int s,int v){\n        for(int i=v;i!=s;i=edgeto[i])\n            path.push(i);\n        path.push(s);\n    }\n\n};\n\n#endif // BFSPATH_H_INCLUDED\n"
  },
  {
    "path": "4_Graphs/BellmanFordSP.h",
    "content": "#ifndef BELLMANFORDSP_H_INCLUDED\n#define BELLMANFORDSP_H_INCLUDED\n#include\"EdgeWeightedDigraph.h\"\n/*\n定理:\n1.distTo[S]=0,其他点无穷大。任意顺序relax所有边\n2.1 重复V(顶点个数)轮  则停止\n2.2 edgeTo[]存在负权重环  则停止\n\n3.note:优化:\nidea: 只有上一轮distTo[]发生变化的顶点指出的边才可能改变其他distTo[]的值,FIFO队列记录这样的顶点\n==》 一轮代表执行一次relax(G,v)  ？？\nqueue1 保存即将被放松的顶点\nbool[] 判断顶点是否已经存在于queue1\n*/\n\nclass BellmanSP{\nprivate:\n    vector<double> distTo;\n    vector<DiEdge> edgeTo;\n    vector<bool> onQ;\n    queue<int> fifo;        //待执行relax的顶点\n    int relaxCalls;\n    void relax(EdgeWeightedDigraph* G,int v){\n        for(DiEdge e:G->getadj(v)){\n            int w = e.to();\n            if(distTo[w] > distTo[v]+e.getWeight()){            //注意符号方向\n                distTo[w] = distTo[v]+e.getWeight();\n                edgeTo[w]=e;\n                //加入queue\n                if(onQ[w]==false){\n                    fifo.push(w);\n                    onQ[w]=true;\n                }\n            }\n            if(relaxCalls% G->getV()==0){\n//                findNegativeCycle();  //这里我就不实现了\n//                if(hasNCycle) return;\n            }\n        }\n    }\n\npublic:\n    BellmanSP(EdgeWeightedDigraph* G){\n        edgeTo.resize(G->getV());\n        distTo.resize(G->getV(),std::numeric_limits<double>::max());\n        onQ.resize(G->getV(),false);\n        relaxCalls=0;\n    }\n\n    void getBellmanSP(EdgeWeightedDigraph* G,int s){\n        distTo[s]=0.0;\n        onQ[s]=true;\n        fifo.push(s);\n//        while(!fifo.empty() && !hasNegativeCycle())\n        while(!fifo.empty()){\n            int v=fifo.front();\n            fifo.pop();\n            onQ[v]=false;\n            relax(G,v);\n        }\n    }\n\n    //其他API\n    double getdistTo(int v){return distTo[v];}\n    bool hasPathTo(int v){return distTo[v]<std::numeric_limits<double>::max();}\n    vector<DiEdge> pathTo(int v){\n        vector<DiEdge> pathRes;\n        stack<DiEdge> path;\n        if(!hasPathTo(v)) return vector<DiEdge>();\n        for(DiEdge e=edgeTo[v]; e!=DiEdge();e=edgeTo[e.from()])\n            path.push(e);\n        while(!path.empty()){\n            DiEdge e = path.top();\n            path.pop();\n            pathRes.push_back(e);\n        }\n        return pathRes;  //未包含起点\n    }\n};\n\n\n#endif // BELLMANFORDSP_H_INCLUDED\n"
  },
  {
    "path": "4_Graphs/DFSCC.h",
    "content": "#ifndef DFSCC_H_INCLUDED\n#define DFSCC_H_INCLUDED\n\n/*TODO   判断图存在多少个连通分量\n思路:\n所有未标记的节点 执行一遍完整DFS遍历\nDFS代表从某一个节点出发 能到达的所有节点都会被标记\n*/\n#include\"Graph.h\"\n\nclass DFSCC{\nprivate:\n    vector<bool> marked;\n    vector<int> id;     //id[v]表示所在连通集的编号\npublic:\n    int setCount = 0;\n    DFSCC(Graph* G){\n        marked.reserve(G->getV());\n        id.reserve(G->getV());\n    }\n\n    void dfs(Graph* G, int v){\n        marked[v] = true;\n        id[v]=setCount;             //在DFS代码上添加\n        for(int w : G->getadj(v)){\n            if(!marked[w])\n                dfs(G,w);\n        }\n\n    }\n    int getSetNum(Graph* G){\n        for(int v=0;v<G->getV();v++){\n            if(!marked[v]){\n                dfs(G,v);\n                setCount++;\n            }\n        }\n        return setCount;\n    }\n};\n\n#endif // DFSCC_H_INCLUDED\n"
  },
  {
    "path": "4_Graphs/DFSDirectedCycle.h",
    "content": "#ifndef DFSDIRECTEDCYCLE_H_INCLUDED\n#define DFSDIRECTEDCYCLE_H_INCLUDED\n#include\"Digraph.h\"\n/*\n有向图 判断是否存在有向环\n\n不存在环路 topological order算法的前提\n*/\n\nclass DFSDirectedCycle{\nprivate:\n    vector<bool> marked;\n    vector<int> edgeTo;\n    vector<bool> onstack;\n    stack<int> cycle;\n\npublic:\n    DFSDirectedCycle(Digraph* G){\n        marked.resize(G->getV(),false);\n        edgeTo.reserve(G->getV());\n        onstack.resize(G->getV(),false);\n    }\n    bool hasCycle(Digraph* G){\n        for(int v=0;v<G->getV();v++){\n           if(!marked[v] && cycle.empty())\n                dfs(G,v);\n        }\n        if(cycle.empty()) return false;\n        else return true;\n    }\n    void dfs(Digraph* G,int v){\n        marked[v]=true;\n        onstack[v]=true;\n\n        //正常的dfs流程\n        for(int w:G->getadj(v)){\n            if(!cycle.empty()) return;\n            else if(!marked[w]){\n                edgeTo[w]=v;\n                dfs(G,w);\n            }\n            //判断是否构成环路\n            else if(onstack[w]){\n                for(int tmp=v;tmp!=w;tmp=edgeTo[tmp])\n                    cycle.push(tmp);\n                cycle.push(w); //子节点w是环的终点\n            }\n        }\n\n        onstack[v]=false;\n    }\n};\n\n\n#endif // DFSDIRECTEDCYCLE_H_INCLUDED\n"
  },
  {
    "path": "4_Graphs/DFSTopo.h",
    "content": "#ifndef DFSTOPO_H_INCLUDED\n#define DFSTOPO_H_INCLUDED\n#include\"Digraph.h\"\n#include\"EdgeWeightedDigraph.h\"\n/*\n拓扑排序  DFS+一行代码\n\nBFS是将入度为零的先加入queue\n*/\n\nclass DFSTopo{\nprivate:\n    vector<bool> marked;\n    stack<int> reversePost;\n\npublic:\n    DFSTopo(Digraph* G){\n        marked.resize(G->getV(),false);\n    }\n\n    vector<int> getTopo(Digraph* G){\n        for(int v=0;v<G->getV();v++){     //可能是多连通分量图\n            if(!marked[v])\n                dfs(G,v);\n        }\n\n        vector<int> res;\n        while(!reversePost.empty()){\n            res.push_back(reversePost.top());\n            reversePost.pop();\n        }\n        return res;\n    }\n\n    void dfs(Digraph* G,int v){\n        marked[v] = true;\n        for(int w:G->getadj(v)){\n            if(!marked[w])\n                dfs(G,w);\n        }\n        reversePost.push(v);\n    }\n\n//****************************关于加权Weighted 有向图的重载\n    DFSTopo(EdgeWeightedDigraph* G){\n        marked.resize(G->getV(),false);\n    }\n\n    vector<int> getTopo(EdgeWeightedDigraph* G){\n        for(int v=0;v<G->getV();v++){     //可能是多连通分量图\n            if(!marked[v])\n                dfs(G,v);\n        }\n\n        vector<int> res;\n        while(!reversePost.empty()){\n            res.push_back(reversePost.top());\n            reversePost.pop();\n        }\n        return res;\n    }\n\n    void dfs(EdgeWeightedDigraph* G,int v){\n        marked[v] = true;\n        for(DiEdge e:G->getadj(v)){\n            if(!marked[e.to()])\n                dfs(G,e.to());\n        }\n        reversePost.push(v);\n    }\n};\n#endif // DFSTOPO_H_INCLUDED\n"
  },
  {
    "path": "4_Graphs/DiEdge.h",
    "content": "#ifndef DIEDGE_H_INCLUDED\n#define DIEDGE_H_INCLUDED\n#include \"func.h\"\n/*\n区别于无向边,v->w == v,w具有固定的含义\nAPI from v to w\n*/\nclass DiEdge{\nprivate:\n    int v;\n    int w;\n    double weight;\n\npublic:\n    DiEdge()\n    :v(0),w(0),weight(0.0){}\n    DiEdge(int v,int w,double weight)\n    :v(v),w(w),weight(weight){}\n    int from()const { return v;}\n    int to()const {return w;}\n    double getWeight()const {return weight;}        //const成员函数 不改变类对象的内部数据\n\n    friend bool operator<(const DiEdge& a,const DiEdge& b);\n    friend bool operator>(const DiEdge& a,const DiEdge& b);\n    friend bool operator==(const DiEdge& a,const DiEdge& b);\n    friend bool operator!=(const DiEdge& a,const DiEdge& b);\n\n};\n\nbool operator<(const DiEdge& a,const DiEdge& b){\n    return a.getWeight() < b.getWeight();\n}\nbool operator>(const DiEdge& a,const DiEdge& b){\n    return a.getWeight() > b.getWeight();\n}\nbool operator==(const DiEdge& a,const DiEdge& b){\n    return a.getWeight()==b.getWeight() && a.from()==b.from() && a.to()==b.to();\n}\n\nbool operator!=(const DiEdge& a,const DiEdge& b){\n    return a.getWeight()!=b.getWeight() || a.from()!=b.from() || a.to()!=b.to();\n}\n\n#endif // DIEDGE_H_INCLUDED\n"
  },
  {
    "path": "4_Graphs/Digraph.h",
    "content": "#ifndef DIGRAPH_H_INCLUDED\n#define DIGRAPH_H_INCLUDED\n#include \"func.h\"\n\nclass Digraph{\nprivate:\n    int V;\n    int E;\n    vector<vector<int>> adj;            //邻接链表\n    void addEdge(int v,int w){\n        adj[v].push_back(w);            //区别于无向图\n    }\npublic:\n    Digraph(int v):V(v),E(0){adj.resize(v,vector<int>());};\n    Digraph(string file){\n        std::ifstream infile(file);\n        string tmp;\n        int lineNum = 0;\n        while(std::getline(infile,tmp)){\n            std::istringstream iss(tmp);\n            int v1,v2;\n            if(lineNum==0) {iss>>V; adj.resize(V,vector<int>());}\n            else if(lineNum==1) iss>>E;\n            else {\n                iss>>v1>>v2;\n                addEdge(v1,v2);\n            }\n            lineNum++;\n        }\n    };\n    int getV(){return V;}\n    int getE(){return E;}\n    int degree(int v){return adj[v].size();}\n    vector<int> getadj(int v){return adj[v];}\n\n    //反转图\n    Digraph* reverseG(){\n        Digraph* revG = new Digraph(V);\n        for(int v=0;v<V;v++){\n            for(int w : getadj(v))\n                revG->addEdge(w,v);\n        }\n        return revG;\n    }\n\n};\n\n#endif // DIGRAPH_H_INCLUDED\n"
  },
  {
    "path": "4_Graphs/DijkstraSP.h",
    "content": "#ifndef DIJKSTRASP_H_INCLUDED\n#define DIJKSTRASP_H_INCLUDED\n#include\"DiEdge.h\"\n#include\"EdgeWeightedDigraph.h\"\n/*\n非负权重 有向图\n类似于即时的prim算法\n1.将dist[Start]=0,其他distTo[]初始化为无穷大\n2.重复将distTo[]中最小非树节点V =>relax(G,V)\n3.直到所有顶点都在树或所有非树顶点distTo无限大\n*/\n\nclass DijkstraSP{\nprivate:\n    vector<DiEdge> edgeTo;\n    vector<double> distTo;\n    map<int,double> minPQ;      //非树节点vertex和distTo\n    int getPQmin(){\n        double minval = std::numeric_limits<double>::max();\n        int key = 0;\n        for(map<int,double>::iterator iter=minPQ.begin();iter!=minPQ.end();iter++){\n            if(iter->second < minval){\n                key = iter->first;\n                minval = iter->second;\n            }\n        }\n        return key;\n    }\n    void erasePQ(int v){\n        map<int,double>::iterator it = minPQ.find(v);\n        minPQ.erase(it);\n    }\n\n    //relax 松弛API\n    void relax(EdgeWeightedDigraph* G,int v){\n        for(DiEdge e:G->getadj(v)){\n            int v=e.from();\n            int w=e.to();\n            //w到s的距离更新\n            if(distTo[v]+e.getWeight() < distTo[w]){\n                distTo[w]= distTo[v]+e.getWeight();\n                edgeTo[w]=e;\n                minPQ[w]=distTo[w];     //update or insert\n            }\n        }\n    }\n\npublic:\n    DijkstraSP(EdgeWeightedDigraph* G,int s){\n        edgeTo.resize(G->getV());\n        distTo.resize(G->getV(),std::numeric_limits<double>::max());\n    }\n    void getSP(EdgeWeightedDigraph* G,int s){\n        distTo[s]=0.0;\n        minPQ[s]=distTo[s];\n        while(!minPQ.empty()){\n            int v = getPQmin();\n            erasePQ(v);\n            relax(G,v);\n        }\n    }\n\n    //其他API\n    double getdistTo(int v){return distTo[v];}\n    bool hasPathTo(int v){return distTo[v]<std::numeric_limits<double>::max();}\n    vector<DiEdge> pathTo(int v){\n        vector<DiEdge> pathRes;\n        stack<DiEdge> path;\n        if(!hasPathTo(v)) return vector<DiEdge>();\n        for(DiEdge e=edgeTo[v]; e!=DiEdge();e=edgeTo[e.from()])\n            path.push(e);\n        while(!path.empty()){\n            DiEdge e = path.top();\n            path.pop();\n            pathRes.push_back(e);\n        }\n        return pathRes;  //未包含起点\n    }\n};\n#endif // DIJKSTRASP_H_INCLUDED\n"
  },
  {
    "path": "4_Graphs/DirectedGraph_test.cpp",
    "content": "#include\"Digraph.h\"\n#include\"DFSDirectedCycle.h\"\n#include\"DFSTopo.h\"\n\nint main(){\n    Digraph* G = new Digraph(\"./data/tinyDAG.txt\");      //tinyDAG无环有向图\n\n    //基础测试\n    cout<<G->getE()<<endl;\n    cout<<G->getV()<<endl;\n    cout<<G->degree(0)<<endl;\n\n    //单点路径问题(DFS判断存在性 BFS求最短)\n\n    //有向图是否存在环？\n    cout<<endl;\n    DFSDirectedCycle* dfsDC = new DFSDirectedCycle(G);\n    cout<<true<<endl;\n    cout<<dfsDC->hasCycle(G)<<endl;\n\n    //顶点的三种遍历顺序\n    //topological拓扑排序 解决优先级问题\n    //思路:  DFS+ revesePost顺序 证明P376  + P388 BFS\n    cout<<endl;\n    DFSTopo* dfsTopo = new DFSTopo(G);\n    for(auto i:dfsTopo->getTopo(G))\n        cout<<i<<\" \";\n\n\n    //思考题:\n    //P380 强连通分量 Kosaraju算法\n    //总结P385\n    return 0;\n}\n"
  },
  {
    "path": "4_Graphs/Edge.h",
    "content": "#ifndef EDGE_H_INCLUDED\n#define EDGE_H_INCLUDED\n\n#include\"func.h\"\n/*\n有权重的无向图\n边\n*/\n\nclass Edge{\nprivate:\n    int v;\n    int w;\n    double weight;\n\npublic:\n    Edge()\n    :v(0),w(0),weight(0.0){}\n    Edge(int v,int w,double weight)\n    :v(v),w(w),weight(weight){}\n    double getWeight()const {return weight;}\n\n    //either other获取边的两个顶点\n    int other(int vTmp){\n        if(vTmp==v) return w;\n        else return v;\n    }\n    int either(){return v;}\n\n    //compare重载\n    //const成员函数不允许修改类数据 + const对象只能使用const函数\n    friend bool operator<(const Edge& a,const Edge& b);\n    friend bool operator>(const Edge& a,const Edge& b);\n    friend bool operator==(const Edge& a,const Edge& b);\n\n};\n\nbool operator<(const Edge& a,const Edge& b){\n    return a.getWeight() < b.getWeight();\n}\nbool operator>(const Edge& a,const Edge& b){\n    return a.getWeight() > b.getWeight();\n}\nbool operator==(const Edge& a,const Edge& b){\n    return a.getWeight() == b.getWeight();\n}\n\n#endif // EDGE_H_INCLUDED\n"
  },
  {
    "path": "4_Graphs/EdgeWeightedDigraph.h",
    "content": "#ifndef EDGEWEIGHTEDDIGRAPH_H_INCLUDED\n#define EDGEWEIGHTEDDIGRAPH_H_INCLUDED\n#include\"DiEdge.h\"\n/*\n加权 有向图的定义\n*/\nclass EdgeWeightedDigraph{\nprivate:\n    int V;\n    int E;\n    vector<vector<DiEdge>> adj;\n    void addEdge(int v,int w,double weight){\n        DiEdge tmp(v,w,weight);\n        adj[v].push_back(tmp);\n    }\npublic:\n    EdgeWeightedDigraph(string file){\n        std::ifstream infile(file);\n        string tmp;\n        int lineNum = 0;\n        while(std::getline(infile,tmp)){\n            std::istringstream iss(tmp);\n            int v1,v2;\n            double weight;\n            if(lineNum==0) {iss>>V; adj.resize(V,vector<DiEdge>());}\n            else if(lineNum==1) iss>>E;\n            else {\n                iss>>v1>>v2>>weight;\n                addEdge(v1,v2,weight);\n            }\n            lineNum++;\n        }\n    }\n    int getV(){return V;}\n    int getE(){return E;}\n    int degree(int v){return adj[v].size();}\n    vector<DiEdge> getadj(int v){ return adj[v];}\n    set<DiEdge> getAllEdges(){\n        set<DiEdge> allEdges;\n        for(vector<DiEdge> tmp:adj){\n            for(DiEdge e:tmp){\n                allEdges.insert(e);\n            }\n        }\n        return allEdges;\n    }\n};\n\n\n#endif // EDGEWEIGHTEDDIGRAPH_H_INCLUDED\n"
  },
  {
    "path": "4_Graphs/EdgeWeightedGraph.h",
    "content": "#ifndef EDGEWEIGHTEDGRAPH_H_INCLUDED\n#define EDGEWEIGHTEDGRAPH_H_INCLUDED\n#include\"func.h\"\n#include\"Edge.h\"\n/*\n实现带带权重的无向图\n*/\nclass EdgeWeightedGraph{\nprivate:\n    int V;\n    int E;\n    vector<vector<Edge>> adj;\n    void addEdge(int v,int w,double weight){\n        Edge tmp(v,w,weight);\n        adj[v].push_back(tmp);\n        adj[w].push_back(tmp);\n    }\npublic:\n    EdgeWeightedGraph(string file){\n        std::ifstream infile(file);\n        string tmp;\n        int lineNum = 0;\n        while(std::getline(infile,tmp)){\n            std::istringstream iss(tmp);\n            int v1,v2;\n            double weight;\n            if(lineNum==0) {iss>>V; adj.resize(V,vector<Edge>());}\n            else if(lineNum==1) iss>>E;\n            else {\n                iss>>v1>>v2>>weight;\n                addEdge(v1,v2,weight);\n            }\n            lineNum++;\n        }\n    }\n    int getV(){return V;}\n    int getE(){return E;}\n    int degree(int v){return adj[v].size();}\n    vector<Edge> getadj(int v){ return adj[v];}\n    set<Edge> getAllEdges(){\n        set<Edge> allEdges;\n        for(vector<Edge> tmp:adj){\n            for(Edge e:tmp){\n                allEdges.insert(e);\n            }\n        }\n        return allEdges;\n    }\n};\n\n\n#endif // EDGEWEIGHTEDGRAPH_H_INCLUDED\n"
  },
  {
    "path": "4_Graphs/Graph.h",
    "content": "#ifndef GRAPH_H_INCLUDED\n#define GRAPH_H_INCLUDED\n\n#include \"func.h\"\nclass Graph{\nprivate:\n    int V;\n    int E;\n    vector<vector<int>> adj;            //邻接链表\n    void addEdge(int v,int w){\n        adj[v].push_back(w);\n        adj[w].push_back(v);\n    }\npublic:\n    Graph(int v):V(v),E(0){adj.resize(v,vector<int>());};\n    Graph(string file){\n        std::ifstream infile(file);\n        string tmp;\n        int lineNum = 0;\n        while(std::getline(infile,tmp)){\n            std::istringstream iss(tmp);\n            int v1,v2;\n            if(lineNum==0) {iss>>V; adj.resize(V,vector<int>());}\n            else if(lineNum==1) iss>>E;\n            else {\n                iss>>v1>>v2;\n                addEdge(v1,v2);\n            }\n            lineNum++;\n        }\n    };\n    int getV(){return V;}\n    int getE(){return E;}\n    int degree(int v){return adj[v].size();}\n    vector<int> getadj(int v){return adj[v];}\n\n};\n\n\n#endif // GRAPH_H_INCLUDED\n"
  },
  {
    "path": "4_Graphs/KruskalMST.h",
    "content": "#ifndef KRUSKALMST_H_INCLUDED\n#define KRUSKALMST_H_INCLUDED\n#include\"EdgeWeightedGraph.h\"\n#include\"UnionFind.h\"\n/*\n克鲁斯卡尔\nidea: 不断从所有边中寻找最小值,要求不能构成环路(不在同一个连通集)\nnote: 相同于不断合并不同的连通分量\nnote: unionFind数据结构\n*/\n\nclass KruskalMST{\nprivate:\n    queue<Edge> result;\n    double totalWeight;\n    priority_queue<Edge,vector<Edge>,std::greater<Edge>> minpq;     //初始加入所有边\n\npublic:\n    KruskalMST(EdgeWeightedGraph* G)\n    :totalWeight(0.0){\n        UnionFind* uf = new UnionFind(G->getV());\n        for(Edge e:G->getAllEdges())\n            minpq.push(e);\n\n        //生成MST\n        while(!minpq.empty() && result.size()<G->getV()-1){\n            Edge e = minpq.top();\n            minpq.pop();\n            int v=e.either(); int w=e.other(v);\n\n            //已经在同一个连通集合\n            if(uf->connected(v,w)) continue;\n            else{\n                uf->unionV(v,w);\n                result.push(e);\n                totalWeight += e.getWeight();\n            }\n        }\n    }\n    queue<Edge> getRes(){ return result;}\n    double getWeight(){return totalWeight;}\n\n};\n\n#endif // KRUSKALMST_H_INCLUDED\n"
  },
  {
    "path": "4_Graphs/LazyPrimMST.h",
    "content": "#ifndef LAZYPRIMMST_H_INCLUDED\n#define LAZYPRIMMST_H_INCLUDED\n\n#include \"EdgeWeightedGraph.h\"\n\n/*\n延时删除的prim最小生成树\npre: 图是连通\nidea: 随机起点 + 可到达边的最小值加入tree\nnote: 删除无效边(可达边的两个节点都已经标记)\n*/\n\nclass LazyPrimMST{\nprivate:\n    queue<Edge> mst;\n    double totalWeight;\n    vector<bool> marked;\n    priority_queue<Edge,vector<Edge>,std::greater<Edge>> minpq;\n\npublic:\n    LazyPrimMST(EdgeWeightedGraph* G):totalWeight(0.0){\n        marked.resize(G->getV(),false);\n    }\n\n    queue<Edge> getMST(EdgeWeightedGraph* G){\n        visit(G,0);\n        while(!minpq.empty()){\n            Edge e = minpq.top();\n            minpq.pop();\n            int v = e.either();\n            int w = e.other(v);\n            if(marked[v] && marked[w]) continue;\n            //加入最小生成树\n            mst.push(e);\n            totalWeight += e.getWeight();\n            //visit新顶点\n            if(!marked[v]) visit(G,v);\n            else visit(G,w);\n        }\n        return mst;\n    }\n\n    //节点v所有可达边\n    void visit(EdgeWeightedGraph* G,int v){\n        marked[v]=true;\n        for(Edge e : G->getadj(v)){\n            int w = e.other(v);\n            if(!marked[w])\n                minpq.push(e);\n        }\n    }\n\n    double getWeight(){ return totalWeight;}\n\n};\n\n#endif // LAZYPRIMMST_H_INCLUDED\n"
  },
  {
    "path": "4_Graphs/MinTree_test.cpp",
    "content": "#include\"EdgeWeightedGraph.h\"\n#include\"LazyPrimMST.h\"\n#include\"PrimMST.h\"\n#include\"KruskalMST.h\"\n\nint main(){\n    EdgeWeightedGraph* G = new EdgeWeightedGraph(\"./data/tinyEWG.txt\");      //tinyDAG无环有向图\n\n    //基础测试\n    cout<<G->getE()<<endl;\n    cout<<G->getV()<<endl;\n    cout<<G->degree(0)<<endl;\n\n    //最小生成树测试\n    //1.延时删除Prim算法\n    cout<<endl;\n    LazyPrimMST* mst = new LazyPrimMST(G);\n    queue<Edge> res = mst->getMST(G);\n    while(!res.empty()){\n        Edge e = res.front();\n        res.pop();\n        cout<<e.either()<<\"-\"<<e.other(e.either())<<\" \"<<e.getWeight()<<endl;\n    }\n    cout<<\"min total_weight = \"<<mst->getWeight()<<endl;\n    cout<<endl;\n\n\n    //2.即时删除Prim算法\n    PrimMST* pmst = new PrimMST(G);\n    vector<Edge> res2 = pmst->getMST(G);\n    for(Edge e:res2){\n        cout<<e.either()<<\"-\"<<e.other(e.either())<<\" \"<<e.getWeight()<<endl;\n    }\n    cout<<\"min total_weight = \"<<pmst->getWeight()<<endl;\n    cout<<endl;\n\n\n    //3.克鲁斯卡尔算法\n    KruskalMST* kmst = new KruskalMST(G);\n    queue<Edge> res3 = kmst->getRes();\n    while(!res3.empty()){\n        Edge e = res3.front();\n        res3.pop();\n        cout<<e.either()<<\"-\"<<e.other(e.either())<<\" \"<<e.getWeight()<<endl;\n    }\n    cout<<\"min total_weight = \"<<kmst->getWeight()<<endl;\n\n\n\n    return 0;\n}\n"
  },
  {
    "path": "4_Graphs/PrimMST.h",
    "content": "#ifndef PRIMMST_H_INCLUDED\n#define PRIMMST_H_INCLUDED\n\n#include \"EdgeWeightedGraph.h\"\n\n/*\n即时删除的prim最小生成树\nidea: 随机起点 + 可到达边的最小值加入tree\nnote:\n1.lazy模式 会将Edge都加入queue ==》 priority queue\n2.即时 在加入queue进行判断: 无效边+非最小边 被拒绝\n3.数据结构:\nedgeTo[w] w距离tree最近的edge 或者NULL\ndist[w]=edgeTo[w].weight\nindexQueue 用map<w,weight>实现,保存待遍历的横切边\n\n相比较于Lazy模式  indexPQ中的可达边数量是常数级别\n*/\n\nclass PrimMST{\nprivate:\n    vector<Edge> edgeTo;\n    vector<double> distTo;\n    vector<bool> marked;\n    map<int,double> indexPQ;\n    double totalWeight;\n\n    //indexPQ min_val对应的顶点\n    int getPQmin(){\n        double minval = std::numeric_limits<double>::max();\n        int key = 0;\n        for(map<int,double>::iterator iter=indexPQ.begin();iter!=indexPQ.end();iter++){\n            if(iter->second < minval){\n                key = iter->first;\n                minval = iter->second;\n            }\n        }\n        return key;\n    }\n    void erasePQ(int v){\n        map<int,double>::iterator it = indexPQ.find(v);\n        indexPQ.erase(it);\n    }\n\npublic:\n    PrimMST(EdgeWeightedGraph* G):totalWeight(0.0){\n        edgeTo.resize(G->getV());           //reserve并不会真实开辟空间\n        distTo.resize(G->getV(),std::numeric_limits<double>::max());\n        marked.resize(G->getV(),false);\n    }\n\n    vector<Edge> getMST(EdgeWeightedGraph* G){\n        distTo[0]=0.0;\n        indexPQ[0]=0.0;\n        while(!indexPQ.empty()){\n            int v = getPQmin();     //相比较于Lazy模式  indexPQ中的可达边数量是常数级别\n            totalWeight += indexPQ[v];\n            erasePQ(v);\n            visit(G,v);     //最近节点加入树中\n        }\n        return edgeTo;\n    }\n\n    //节点v所有可达边\n    void visit(EdgeWeightedGraph* G,int v){\n        marked[v]=true;\n        for(Edge e: G->getadj(v)){\n            int w = e.other(v);\n            if(marked[w]) continue;\n            else if(e.getWeight() >= distTo[w]) continue;\n            else{\n                edgeTo[w] = e;\n//                cout<<e.either()<<\"-\"<<e.other(e.either())<<\" \"<<e.getWeight()<<endl;\n                distTo[w] = e.getWeight();\n                indexPQ[w]=distTo[w];           //map中不存在w 则会添加\n            }\n        }\n    }\n\n    double getWeight(){ return totalWeight;}\n    vector<Edge> getEdgeTo(){ return edgeTo;}\n\n};\n\n#endif // PRIMMST_H_INCLUDED\n"
  },
  {
    "path": "4_Graphs/README.md",
    "content": "代码链接:\nxxxxxxxxxxxx\n  \n# 无向图  \nGraph.h 实现图的API 邻接链表保存边  \nDFS BFS的核心代码  \n>应用  \n连通性||单点路径 DFS or BFS  \n单点最短路径  BFS  \n其它:  \n1.连通分量个数  DFS         \n2.检测环 DFS  \n3.双色问题 DFS  \n   \n# 有向图  \nDigraph.h 实现图的API 邻接链表保存边  \n>应用  \n单点可达性||单点有向路径 DFS orBFS  \n单点最短路径 BFS  \n其它:  \n1.有向环检测  DFS  \n2.拓扑排序 DFS+stack  \n  \n# 最小生成树  \nEdge.h 实现有权重边的API  \nEdgeWeightedGraph.h 实现图的API  \n>prim 普里姆算法  \n所有可达边中选择最小边加入Tree(注意跳过失效边)  \n1.延时更新   \nmarked[] 记录顶点已在最小树上  \npriority_queue记录有效的可达边  \nlazyPrimMST.h  \n2.即时更新  \ndistTo[w]记录w到最小树的距离,跳过Edge.Weight > distTo[w]的顶点  \nmap_indexMin_queue 记录有效横切边 (常数空间大小)  \n  \n>kruskal 克鲁斯卡尔算法  \n所有边的最小值加入Tree(跳过v,w在同一个连通集的边,即新加入的边不构成环路)  \n终止条件:  \n所有边都遍历完 或者 MST的边数==G的顶点数目-1  \nUnionFind数据结构  \nUF判断Edge 的v,w顶点,从而跳过或者union(v,w)  \n  \n# 最短路径  \n加权 有向图  \nDiEdge.h 实现有权重,有方向边的API  \nEdgeWeightedDigraph.h 实现图的API  \n>Dijkstra 算法  \n前提:  边的权重必须为正  \n由起点开始,按照最小distTo[]顺序,relax松弛所有顶点  \n终止条件: map map_indexMin_queue为空  \n  \n> 拓扑排序  \n前提: 无环图  \n线性时间解决问题=> 按照topo顺序relax顶点  \n拓展: 如何计算最长路径？变换符号 或者取权重相反数  \n关键路径定义:  限制优先级问题得到有向图,计算最长路径  \n  \n> Bellman-Ford 算法  \n>基于队列  \n前提: 不存在负权重的环  \n任意顺序relax顶点,重复V轮  \n优化: 一轮意味着执行一次relax？ 只有relax才可能导致distTo的变化  \nqueue fifo保存即将被放松的顶点  \nbool[] 判断顶点是否已经存在于queue  \n  \n应用: 金融套汇问题  =》 换汇的乘积转换为ln求和 =》寻找负权重的环  ==》获利  \n"
  },
  {
    "path": "4_Graphs/ShortestPath_test.cpp",
    "content": "#include\"DijkstraSP.h\"\n#include\"TopoLongestPath.h\"\n#include\"BellmanFordSP.h\"\n\nint main(){\n    EdgeWeightedDigraph* G = new EdgeWeightedDigraph(\"./data/tinyEWD.txt\");      //tinyDAG无环有向图\n\n    //基础测试\n    cout<<G->getE()<<endl;\n    cout<<G->getV()<<endl;\n    cout<<G->degree(0)<<endl;\n\n    //Dijkstra shortest path测试\n    cout<<endl;\n    int startV=0;\n    int endV = 6;\n    DijkstraSP* dSP = new DijkstraSP(G,startV);\n    dSP->getSP(G,startV);\n    cout<<\"hasPathTo endVertex=\"<<endV<<\" is \"<<(bool)dSP->hasPathTo(endV)<<endl;\n    double totalW=0.0;\n    for(DiEdge e:dSP->pathTo(endV)){\n        cout<<e.from()<<\"-\"<<e.to()<<\" \"<<e.getWeight()<<endl;\n        totalW += e.getWeight();\n    }\n    cout<<\"from 0 to 6, shortestpath=\"<<totalW<<endl;\n    cout<<endl;\n\n\n    //无环图 LongestPath测试\n    startV=5;\n    endV = 2;\n    G = new EdgeWeightedDigraph(\"./data/tinyEWDAG.txt\");\n    TopoLP* topoLP = new TopoLP(G);\n    topoLP->getTopoLP(G,startV);\n    totalW=0.0;\n    for(DiEdge e:topoLP->pathTo(endV)){\n        cout<<e.from()<<\"-\"<<e.to()<<\" \"<<e.getWeight()<<endl;\n        totalW += e.getWeight();\n    }\n    cout<<\"from 5 to 2, longestpath=\"<<totalW<<endl;\n    //思考:\n    //应用: 限制优先级问题\n    //idea: 将限制条件转化为有向图 P431  => 加权有向图最长路径\n\n\n    //一般性问题  Bellman-Ford\n    //不存在权重值之和为负的还 有向图\n    cout<<endl;\n    startV=0;\n    endV = 3;\n    G = new EdgeWeightedDigraph(\"./data/tinyEWDnc.txt\");\n    BellmanSP* bSP = new BellmanSP(G);\n    bSP->getBellmanSP(G,startV);\n    totalW=0.0;\n    for(DiEdge e:bSP->pathTo(endV)){\n        cout<<e.from()<<\"-\"<<e.to()<<\" \"<<e.getWeight()<<endl;\n        totalW += e.getWeight();\n    }\n    cout<<\"from 0 to 1, longestpath=\"<<totalW<<endl;\n\n    //思考:\n    //金融套汇问题  ==》 转化为寻找负权重之和的环  P444\n\n}\n"
  },
  {
    "path": "4_Graphs/TopoLongestPath.h",
    "content": "#ifndef TOPOSHORTESTPATH_H_INCLUDED\n#define TOPOSHORTESTPATH_H_INCLUDED\n#include\"EdgeWeightedDigraph.h\"\n#include\"DFSTopo.h\"\n\n/*\nDijkstra 非负权重的有向图单源最短路径\n\nTopoSp 无环有向图\n1.线性时间找最短路径 + Weight可以为负 + 变换权重正负号寻找最长路径\nidea:\n按照topological顺序relax所有顶点\n证明 P426\n\nnote: 1.将weight全部取相反数  或者2.交换所有大小判断符号\n*/\n\nclass TopoLP{\nprivate:\n    vector<double> distTo;\n    vector<DiEdge> edgeTo;\n    void relax(EdgeWeightedDigraph* G,int v){\n        for(DiEdge e:G->getadj(v)){\n            int w = e.to();\n            if(distTo[w] < distTo[v]+e.getWeight()){            //注意符号方向\n                distTo[w] = distTo[v]+e.getWeight();\n                edgeTo[w]=e;\n            }\n        }\n    }\n\npublic:\n    TopoLP(EdgeWeightedDigraph* G){\n        edgeTo.resize(G->getV());\n        distTo.resize(G->getV(),std::numeric_limits<double>::min());        //double最小值\n    }\n    void getTopoLP(EdgeWeightedDigraph* G,int s){\n        distTo[s]=0.0;\n        DFSTopo* topo_order = new DFSTopo(G);\n        for(int v:topo_order->getTopo(G))\n            relax(G,v);\n    }\n\n    //其他API\n    double getdistTo(int v){return distTo[v];}\n    bool hasPathTo(int v){return distTo[v] > std::numeric_limits<double>::min();}\n    vector<DiEdge> pathTo(int v){\n        vector<DiEdge> pathRes;\n        stack<DiEdge> path;\n        if(!hasPathTo(v)) return vector<DiEdge>();\n        for(DiEdge e=edgeTo[v]; e!=DiEdge();e=edgeTo[e.from()])\n            path.push(e);\n        while(!path.empty()){\n            DiEdge e = path.top();\n            path.pop();\n            pathRes.push_back(e);\n        }\n        return pathRes;  //未包含起点\n    }\n};\n\n#endif // TOPOSHORTESTPATH_H_INCLUDED\n"
  },
  {
    "path": "4_Graphs/UndirectedGraph_test.cpp",
    "content": "#include\"Graph.h\"\n#include\"DFSCC.h\"\n#include\"BFSPath.h\"\n\nint initG(){\n    std::ifstream infile(\"./data/tinyG.txt\");\n    string tmp;\n    int E,V;\n    int lineNum = 0;\n    while(std::getline(infile,tmp)){\n        std::istringstream iss(tmp);\n        int v1,v2;\n        if(lineNum==0) iss>>E;\n        else if(lineNum==1) iss>>V;\n        else iss>>v1>>v2;\n        lineNum++;\n    }\n}\nint main(){\n    Graph* G = new Graph(\"./data/tinyG.txt\");\n\n    //基础测试\n    cout<<G->getE()<<endl;\n    cout<<G->getV()<<endl;\n    cout<<G->degree(0)<<endl;\n\n    //DFS得到图中连通集的数量\n    cout<<endl;\n    DFSCC* cc = new DFSCC(G);\n    cout<<cc->getSetNum(G)<<endl;   //3个连通分量\n    //DFS思考:\n    //判断是否存在从节点V到节点W的路径？\n    //判断图是否存在环？\n    //判断图是否可以只用两种颜色标记？\n    //P599 两点之间最长路径 DFS变型\n\n\n\n    //BFS得到最短单点路径问题\n    cout<<endl;\n    BFSPath* bfs = new BFSPath(G);\n    stack<int> path;\n    bfs->bfs(G,0);\n    bfs->pathTo(path,0,3);\n    while(!path.empty()){\n        cout<<path.top()<<\" \";\n        path.pop();\n    }\n\n    return 0;\n}\n"
  },
  {
    "path": "4_Graphs/UnionFind.h",
    "content": "#ifndef UNIONFIND_H_INCLUDED\n#define UNIONFIND_H_INCLUDED\n#include\"func.h\"\n/*\n实现weighted union find\n小树加入大树 从而减少树的高度\n*/\n\nclass UnionFind{\nprivate:\n    vector<int> parent;\n    vector<int> sz;\n    int unionCount;\npublic:\n    UnionFind(int vertexNum)\n    :unionCount(vertexNum){\n        parent.resize(vertexNum);\n        sz.resize(vertexNum,1);\n        for(int i=0;i<vertexNum;i++)\n            parent[i]=i;\n    }\n    int getUnionNum(){return unionCount;}\n\n    //API= find,union,connected\n    bool connected(int v,int w){\n        return findRoot(v) == findRoot(w);\n    }\n    int findRoot(int p){\n        while(p != parent[p])\n            p=parent[p];\n        return p;\n    }\n    void unionV(int v,int w){\n        int i=findRoot(v);\n        int j=findRoot(w);\n        if(i==j) return;\n        if(sz[i]<sz[j]){parent[i]=j; sz[j]+=sz[i];}\n        else{parent[j]=i; sz[i]+=sz[j];}\n        unionCount--;\n    }\n};\n\n#endif // UNIONFIND_H_INCLUDED\n"
  },
  {
    "path": "5_Strings/Huffman.h",
    "content": "#ifndef HUFFMAN_H_INCLUDED\n#define HUFFMAN_H_INCLUDED\n#include\"func.h\"\nclass Huffman{\nprivate:\n    const int R = 256;\n    class Node{\n//    private:\n    public:\n        char ch;        //非叶子节点是'\\0'\n        int freq;\n        Node* left;\n        Node* right;  //leaf.left and right is NULL\n\n        Node(char c,int f,Node* l,Node*r)\n        :ch(c),freq(f),left(l),right(r){}\n        char getVal(){return ch;}\n        bool isLeaf(){return left==NULL&&right==NULL;}\n        bool operator<(const Node&that){return freq <that.freq;}\n    };\n\n    Node* buildTrie(const vector<int>& freq){\n        priority_queue<Node*,vector<Node*>,std::greater<Node*>> minPQ;\n        for(int i=0;i<R;i++)\n            if(freq[i]>0)\n                minPQ.push(new Node(i,freq[i],NULL,NULL));\n        while(minPQ.size()>=2){\n            Node* left = minPQ.top();\n            minPQ.pop();\n            Node* right = minPQ.top();\n            minPQ.pop();\n            Node* parent = new Node('\\0',left->freq+right->freq,\n                                    left,right);\n            minPQ.push(parent);\n        }\n        Node* root = minPQ.top();\n        minPQ.pop();\n        return root;\n    }\n    void buildCode(vector<string>& st,Node* root,string s){\n        if(!root->isLeaf()){\n            buildCode(st,root->left,s+\"0\");\n            buildCode(st,root->right,s+\"1\");\n        }else{\n            st[(int)root->getVal()]=s;\n        }\n\n    }\n\npublic:\n    vector<string> st;\n    Huffman(){st.resize(R,\"\");}\n    int getR(){return R;}\n\n    void compress(string s){\n        //1.统计字符频率\n        vector<int> freq(R,0);\n        for(char c:s)\n            freq[c]++;\n\n        //2.构建huffman trie tree\n        Node* root = buildTrie(freq);\n\n        //3.构建编译表\n        buildCode(st,root,\"\");\n    }\n    /*  encode compress\n        前提:  已经得到Huffman编译表\n        这里简单的将\"|\"作为分隔符\n    */\n    string encodeStr(string str){\n        string ans=\"\";\n        for(char c:str)\n            ans += st[c]+\"|\";\n        return ans;\n    }\n\n};\n\n\n#endif // HUFFMAN_H_INCLUDED\n"
  },
  {
    "path": "5_Strings/Huffman_test.cpp",
    "content": "#include\"Huffman.h\"\n\nint main(){\n    string test = \"ABRACADABRA!\";\n    cout<<\"test string= \"<<test<<endl<<endl;\n\n    Huffman* hf = new Huffman();\n    hf->compress(test);\n    cout<<\"after compressing:\"<<endl;\n    cout<<hf->encodeStr(test)<<endl;\n    cout<<endl;\n\n    cout<<\"Trie Tree:\"<<endl;\n    for(int i=0;i<hf->getR();i++)\n        if(hf->st[i]!=\"\")\n        cout<<(char)i<<\" \"<<hf->st[i]<<endl;\n\n}\n"
  },
  {
    "path": "5_Strings/Quick3String.h",
    "content": "#ifndef QUICK3STRING_H_INCLUDED\n#define QUICK3STRING_H_INCLUDED\n\n#include\"func.h\"\n/*\n字符串数组排序\n\nLGD or MGD or quick3string 适用情况 P471\n当字符串具有公共前缀时 三向快速排序更合适\nquick3string 更加通用\n\nnote:\n剩余15个字符可以使用插入排序\n*/\nclass Quick3string{\nprivate:\n    int charAt(string s,int d){\n        if(d==(int)s.size()) return -1;\n        return s[d];\n    }\n    void swap(vector<string>& a,int i,int j){\n        string tmp = a[i];\n        a[i]=a[j];\n        a[j]=tmp;\n    }\n    //高位优先形式 对dth位置字符三向切分\n    void quick3string(vector<string>& a,int lo,int hi,int d){\n        if(lo>= hi) return;\n        int lt=lo;\n        int gt=hi;\n        int i=lo+1;\n        int v=charAt(a[lo],d);\n        while(i<=gt){\n            int tmp = charAt(a[i],d);\n            if(tmp < v) swap(a,lt++,i++);\n            else if(tmp > v) swap(a,i,gt--);\n            else i++;\n        }\n\n        //a[lo..lt-1] < v = a[lt..gt] < a[gt+1..hi].\n        quick3string(a,lo,lt-1,d);\n        if(v>=0) quick3string(a,lt,gt,d+1);\n        quick3string(a,gt+1,hi,d);\n    }\npublic:\n    void vectorStrSort(vector<string>& a){\n        quick3string(a,0,a.size()-1,0);\n    }\n};\n\n#endif // QUICK3STRING_H_INCLUDED\n"
  },
  {
    "path": "5_Strings/Quick3String_test.cpp",
    "content": "#include\"Quick3string.h\"\n/*\n字符串数组排序\n\nLGD or MGD or quick3string 适用情况 P471\n当字符串具有公共前缀时 三向快速排序更合适\nquick3string 更加通用\n\nnote:\n剩余15个字符可以使用插入排序\n*/\n\n\nint main(){\n    vector<string> a;\n    a.push_back(\"abb2\");\n    a.push_back(\"aab1\");\n    a.push_back(\"abb6\");\n    a.push_back(\"abc5\");\n    Quick3string* q3s = new Quick3string();\n    q3s->vectorStrSort(a);\n\n    for(auto i:a)\n        cout<<i<<endl;\n}\n"
  },
  {
    "path": "5_Strings/README.md",
    "content": "# 字符串数组排序  \n>低位优先  \n缺点:  \n字符串要求相同长度  \n>高位优先  \n> 三向快排排序  \n思路:高位优先的,递归排序。之间相等部分去掉首字符继续递归  \nnote:  \n大量具有公共前缀的字符串数组的排序  \n  \n//这两部分  我理解比较困难,实现复杂  \n//我就跳过了  \n# 单词查找树  \n# 子串搜索   \n   \n# 正则表达式   \n   \n# 数据压缩   \n只讨论无损压缩   \n>Huffman编码   \n已实现   \n>LZW压缩   \n我没看   \n"
  },
  {
    "path": "6_Context/FlowEdge.h",
    "content": "#ifndef FLOWEDGE_H_INCLUDED\n#define FLOWEDGE_H_INCLUDED\n\n#include\"func.h\"\n/*\nmaxFlow最大流问题\n相比较无向图Edge,添加两个变量和两个API\n*/\n\nclass FlowEdge{\nprivate:\n    int V;      //边的起点\n    int W;\n    double capacity;\n    double flow;        //实际流量\n\npublic:\n    FlowEdge():V(0),W(0),capacity(0.0){}\n    FlowEdge(int v,int w,double c)\n    :V(v),W(w),capacity(c),flow(0.0){}\n\n    //边的剩余容量 传入参数是边的enpoint\n    double residualCapacityTo(int vertex){\n        if(vertex==V) return flow;      //backward edge\n        else if(vertex==W) return capacity-flow;    //forward edge\n        return (double)-1;\n    }\n    //增加流量 传入参数是边的终点\n    void addResidualFlowTo(int vertex, double delta){\n        if(vertex==V) flow -= delta;    //反向边\n        else if(vertex==W) flow+=delta; //正向边\n    }\n\n    //其它API\n    int from()const {return V;}\n    int to()const {return W;}\n    double getCapacity()const {return capacity;}\n    double getFlow(){return flow;}\n    int other(int vertex){\n        if(vertex==V) return W;\n        if(vertex==W) return V;\n        return -1;\n    }\n    void setFlow(double delta){flow += delta;}\n    bool operator==(const FlowEdge& that){return V==that.from() && W==that.to() && capacity==that.getCapacity();}\n};\n\n#endif // FLOWEDGE_H_INCLUDED\n"
  },
  {
    "path": "6_Context/FlowFordFulkerson.h",
    "content": "#ifndef FLOWFORDFULKERSON_H_INCLUDED\n#define FLOWFORDFULKERSON_H_INCLUDED\n#include\"FlowNetwork.h\"\n/*\nFord-Fulkerson方法\n1.BFS判断剩余网络中是否存在\n2.直到增广路径不存在\n\n增广路径: 能从s到达t,路径上正向边不饱和而且反向边非零\n*/\n\nclass FlowFordFulkerson{\nprivate:\n    vector<bool> marked;\n    vector<FlowEdge> edgeTo;\n    double totalVal;\n    //BFS判断s到t是否存在增广路径\n    bool hasAugmentingPath(FlowNetwork*G,int s,int t){\n        edgeTo.assign(G->getV(),FlowEdge());\n        marked.assign(G->getV(),false);\n        queue<int> pq;\n        pq.push(s);\n        marked[s]=true;\n\n        while(!pq.empty() && !marked[t]){\n            int v = pq.front();\n            pq.pop();\n            for(FlowEdge e : G->getadj(v)){\n                int w = e.other(v);\n                //v->w边存在剩余容量则是非饱和边\n                if(e.residualCapacityTo(w)>0 && !marked[w]){\n                    edgeTo[w]=e;\n                    marked[w]=true;\n                    pq.push(w);\n                }\n            }\n        }\n        return marked[t];       //增广路径能否遍历到t\n    }\n\npublic:\n    FlowFordFulkerson(FlowNetwork* G){\n        marked.resize(G->getV(),false);\n        edgeTo.resize(G->getV());\n        totalVal=0.0;\n    }\n    void calMaxFlow(FlowNetwork* G,int s,int t){\n        totalVal = 0.0;\n        while(hasAugmentingPath(G,s,t)){\n            double delta = std::numeric_limits<double>::max();\n            //edgeTo增广路径中的最小可增加的流量\n            for(int v=t;v!=s;v=edgeTo[v].other(v)){\n                delta = std::min(delta,edgeTo[v].residualCapacityTo(v));\n            }\n            //对于路径上每条边增加流量\n            for(int v=t;v!=s;v=edgeTo[v].other(v)){\n//                edgeTo[v].addResidualFlowTo(v,delta);         //由于java里面传递引用,需要C++稍作变化\n                G->addResidualFlowTo(edgeTo[v],v,delta);\n            }\n\n            totalVal += delta;\n        }\n    }\n    double getMaxFlow(){return totalVal;}\n};\n\n#endif // FLOWFORDFULKERSON_H_INCLUDED\n"
  },
  {
    "path": "6_Context/FlowNetwork.h",
    "content": "#ifndef FLOWNETWORK_H_INCLUDED\n#define FLOWNETWORK_H_INCLUDED\n#include\"FlowEdge.h\"\n/*\n由FlowEdge构成的图 和无向图的结构相同\n*/\n\nclass FlowNetwork{\nprivate:\n    int V;      //顶点个数\n    int E;      //边的个数\n    vector<vector<FlowEdge>> adj;\n    void addEdge(int v,int w,double capacity){\n        adj[v].push_back(FlowEdge(v,w,capacity));\n        adj[w].push_back(FlowEdge(v,w,capacity));\n    }\n\npublic:\n    FlowNetwork(int v,int e)\n    :V(v),E(e){\n        adj.resize(v,vector<FlowEdge>());\n    }\n    FlowNetwork(string  file){\n        std::ifstream infile(file);\n        string tmp;\n        int lineNum = 0;\n        while(std::getline(infile,tmp)){\n            std::istringstream iss(tmp);\n            int v1,v2;\n            double capacity;\n            if(lineNum==0) {iss>>V; adj.resize(V,vector<FlowEdge>());}\n            else if(lineNum==1) iss>>E;\n            else {\n                iss>>v1>>v2>>capacity;\n//                cout<<v1<<\" \"<<v2<<\" \"<<capacity<<\" \"<<endl;\n                addEdge(v1,v2,capacity);\n            }\n            lineNum++;\n        }\n    }\n\n    //其他API\n    int getV(){return V;}\n    int getE(){return E;}\n    vector<FlowEdge> getadj(int v){return adj[v];}\n    void addResidualFlowTo(FlowEdge& e,int v,double delta){     //传入参数V是边的endPoint\n        for(vector<FlowEdge>& i:adj){\n            for(FlowEdge& j:i){\n                if(j==e){\n                    if(j.from()==v) j.setFlow(-delta);        //backward Edge\n                    else if(j.to()==v) j.setFlow(delta);     //forward Edge\n                }\n            }\n        }\n    }\n};\n\n\n#endif // FLOWNETWORK_H_INCLUDED\n"
  },
  {
    "path": "6_Context/README.md",
    "content": "# 事件驱动的粒子碰撞  \n  \n# B-树  \n查找成本很低  需要空间大  \n  \n# 后缀数组  \n字符串的子串中最长的公共前缀问题  \n思路:  \n排序的后缀数组,最长的公共前缀在相邻的位置出现  \n  \n# 最大流  \n给定有向图找出满足平衡的最大流  \n思路:  \n剩余网络中不存在从S到T的增广路径  \n(将增广路径所有边 add最小边的可增加容量)  \n\n跳过   \n# 归约问题  \n# 不可解性   \n"
  },
  {
    "path": "6_Context/SuffixArray.h",
    "content": "#ifndef SUFFIXARRAY_H_INCLUDED\n#define SUFFIXARRAY_H_INCLUDED\n#include\"func.h\"\n#include\"Quick3String.h\"\n//连续字符 子字符串\n//非连续字符 子序列\n\n/*\n问题: string至少重复两次的最长子串\n后缀数组\nidea: sorted 后缀数组最长重复子串在相邻位置\n\nAPI:\ni 已排序的后缀数组的元素序号\n1.select(i) 已排序的后缀数组第i元素\n2.index(i) select(i)在原有的后缀数组序号\n3.lcp(i) select(i)和select(i-1)的最长公共前缀长度\n4.rank(string key) 小于key的字符串的数量\n*/\n\nclass SuffixArray{\nprivate:\n    vector<string> suffixes;\n    int N;\n    int lcp(string str1,string str2){       //两个字符串的最长公共前缀\n        int len = std::min(str1.size(),str2.size());\n        for(int i=0;i<len;i++)\n            if(str1[i]!=str2[i])\n                return i;\n        return len;\n    }\npublic:\n    //sorted 后缀数组\n    SuffixArray(string str){\n        N = str.size();\n        suffixes.resize(N,\"\");\n        for(int i=0;i<N;i++)\n            suffixes[i]=str.substr(i);  //substr(i,N-i);\n        Quick3string* q3s = new Quick3string();\n        q3s->vectorStrSort(suffixes);\n    }\n    int lcp(int i){\n        return lcp(suffixes[i],suffixes[i-1]);\n    }\n    //不断比较相邻的两个字符串的公共前缀长度\n    string getLRS(){\n        string longReaptedStr=\"\";\n        for(int i=1;i<(int)suffixes.size();i++){\n            int len = lcp(i);\n            //更新 lrs\n            if((int)longReaptedStr.size() < len)\n                longReaptedStr = suffixes[i].substr(0,len);\n        }\n        return longReaptedStr;\n    }\n\n    int rankSuffix(string key){\n        //二分查找\n        int lo=0;\n        int hi=N-1;\n        while(lo <= hi){\n            int mid=lo+(hi-lo)/2;\n            if(key == suffixes[mid])\n                return mid;\n            else if(key < suffixes[mid])\n                hi = mid-1;\n            else\n                lo = mid+1;\n        }\n        return lo;\n    }\n\n    //其它API\n    vector<string> getSuffix(){ return suffixes;}\n    int length(){return N;}\n    string select(int i){return suffixes[i];}\n    int index(int i){return N-select(i).size();}\n\n};\n\n#endif // SUFFIXARRAY_H_INCLUDED\n"
  },
  {
    "path": "6_Context/SuffixArray_test.cpp",
    "content": "    #include\"SuffixArray.h\"\n\n    int main(){\n        string test = \"aacaagtttacaagc\";\n        string longReaptedStr = \"\";\n        SuffixArray* sfa = new SuffixArray(test);\n\n    //    for(auto i : sfa->getSuffix())\n    //        cout<<i<<endl;\n\n        //输出最长重复的子字符串\n        cout<<sfa->getLRS()<<endl;\n    }\n"
  },
  {
    "path": "README.md",
    "content": "# 使用说明  \n本项目C++实现基础算法库,重构于Algorithm4th java代码  \n\ne.g. 以插入排序为例  \n```java\n//1.添加算法所需头文件\n#include\"Insertion.h\"\n\nint main(){\n    vector<int> test={5,4,-1,2,7};\n//2. 调用算法\n    Insertion::insort(test);\n    for(auto i:test)\n        cout<<i<<\" \"<<endl;\n}\n//3.其他算法使用样例 请查阅对应*_test.cpp文件\n```\n  \n# 算法-头文件对应  \n*基础*    \n> 背包,队列,栈    \n> 连通集   UnionFind.h      \n    \n*排序*   \n> 初级排序  Insertion.h         \n> 归并排序   \n> 三向快速排序    Quick3way.h           \n> 优先队列  HeapSort.h        \n  \n*查找*  \n> 符号表    \n> 二叉树   BinarySearchTree.h   \n> 平衡二叉树 RedBlackTree.h     \n> 散列表  \n  \n*图*    \n> 无向图   Edge.h  Graph.h  \n1.图连通集数量    DFSCC.h  \n2.单点最短路径    BFSPath.h  \n> 有向图    DiEdge.h    DiGraph.h  \n1.环路判断  DFSDirectedCycle.h  \n2.拓扑排序  DFSTopo.h  \n> 最小生成树 DiEdge.h    EdgeWeightedGraph.h  \n1.延时Prim算法  LazyPrimMST.h  \n2.即时Prim算法  PrimMST.h    \n3.Kruskal算法 KruskalMST.h  \n>最短路径   DiEdge.h    EdgeWeightedDiGraph.h  \n 1.Dijkstra算法   DijkstraSP.h  \n 2.无环图Topo最长路径    TopoLongestPath.h  \n 3.Bellman-Ford算法   BellmanFordSP.h  \n  \n*字符串*  \n> 字符串数组排序    Quick3String.h  \n> 单词查找树  \n> 子串查找  \n> 正则表达式  \n> 数据压缩  Huffman.h  \n    \n*背景*    \n> 粒子碰撞      \n> B-树      \n> 后缀数组  SuffixArray.h    \n> 网络最大流问题 FlowEdge.h    FlowNetWork.h   FlowFordFulkerson.h    \n> 归约问题  \n> 不可解性  \n  \n# 引用  \n*Algorithm 4th Edition*    \nAuthor: Robert SedgeWick and Kevin Wayne  \n[Website:](https://algs4.cs.princeton.edu/home/) https://algs4.cs.princeton.edu/home/  \n"
  },
  {
    "path": "func.h",
    "content": "#ifndef __FUNC_H__\n#define __FUNC_H__\n\n#include<string.h>\n#include<stdio.h>\n#include<iostream>\n#include <fstream>\n#include<string>\n#include<set>\n#include<stack>\n#include<vector>\n#include<sstream>\n#include<queue>\n#include<limits>\n#include<map>\n#include<unordered_map>\n#include<hash_set>\n#include <utility>\n#include<algorithm>\nusing std::cout;\nusing std::endl;\nusing std::string;\nusing std::stack;\nusing std::vector;\nusing std::set;\nusing std::multiset;\nusing std::map;\nusing std::stringstream;\nusing std::unordered_map;\nusing std::queue;\nusing std::priority_queue;\n#endif // __FUNC__\n"
  }
]