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