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<Item> iterator() {
return new ListIterator<Item>(first);
}
// an iterator, doesn't implement remove() since it's optional
private class ListIterator<Item> implements Iterator<Item> {
private Node<Item> current;
public ListIterator(Node<Item> 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<int> parent;
vector<int> sz;
int unionCount;
public:
UnionFind(int vertexNum)
:unionCount(vertexNum){
parent.resize(vertexNum);
sz.resize(vertexNum,1);
for(int i=0;i<vertexNum;i++)
parent[i]=i;
}
int getUnionNum(){return unionCount;}
//API= find,union,connected
bool connected(int v,int w){
return findRoot(v) == findRoot(w);
}
int findRoot(int p){
while(p != parent[p])
p=parent[p];
return p;
}
void unionV(int v,int w){
int i=findRoot(v);
int j=findRoot(w);
if(i==j) return;
if(sz[i]<sz[j]){parent[i]=j; sz[j]+=sz[i];}
else{parent[j]=i; sz[i]+=sz[j];}
unionCount--;
}
};
#endif // UNIONFIND_H_INCLUDED
================================================
FILE: 2_Sorting/HeapSort.h
================================================
#ifndef HEAPSORT_H_INCLUDED
#define HEAPSORT_H_INCLUDED
#include"func.h"
/*
堆排序
1.构建有序堆(最大堆)
2.不断取出maxVal,在重新有序化即可
3.直到堆中不存在新元素
note:有序化API
swim 上浮
sink 下沉
*/
//e.g. 1....n序号的数组进行排序
class HeapSort{
public:
static void hsort(vector<int>& 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<int>& nums,int k,int n){
while(2*k<=n){
//k的子节点2k 2k+1中val较大值为j
int j=2*k;
if(j<n && less(nums,j,j+1)) j++;
if(!less(nums,k,j))
break;
swap(nums,k,j);
k=j;
}
}
//helper function
//由于数组由0开始作为需要标记
static void swap(vector<int>&nums,int i,int j){
int tmp = nums[i-1];
nums[i-1]=nums[j-1];
nums[j-1]=tmp;
}
static bool less(vector<int>&nums,int i,int j){
return nums[i-1]<nums[j-1];
}
};
#endif // HEAPSORT_H_INCLUDED
================================================
FILE: 2_Sorting/Heapsort_test.cpp
================================================
#include"HeapSort.h"
/*
堆排序
1.构建有序堆(最大堆)
2.不断取出maxVal,在重新有序化即可
3.直到堆中不存在新元素
note:有序化API
swim 上浮
sink 下沉
*/
int main(){
vector<int> test={5,4,4,4,44,-1,2,7};
HeapSort::hsort(test);
for(auto i:test)
cout<<i<<" "<<endl;
}
================================================
FILE: 2_Sorting/Insertion.h
================================================
#ifndef INSERTION_H_INCLUDED
#define INSERTION_H_INCLUDED
#include"func.h"
class Insertion{
//class 默认private
public:
static void insort(vector<int>& nums){
int len = nums.size();
for(int i=1;i<len;i++){
for(int j=i; j>0&&nums[j]<nums[j-1]; j--)
std::swap(nums[j],nums[j-1]);
}
}
};
#endif // INSERTION_H_INCLUDED
================================================
FILE: 2_Sorting/Insertion_test.cpp
================================================
#include"Insertion.h"
int main(){
vector<int> test={5,4,-1,2,7};
Insertion::insort(test);
for(auto i:test)
cout<<i<<" "<<endl;
}
================================================
FILE: 2_Sorting/Quick3way.h
================================================
#ifndef QUICK3WAY_H_INCLUDED
#define QUICK3WAY_H_INCLUDED
#include"func.h"
/*
三向快速排序
每次递归将 小数组首元素相同的元素放在相邻位置
优点:
对于大量重复的元素的数组排序 优于传统快排
*/
class Quick3way{
public:
static void q3sort(vector<int>& nums){
q3sort(nums,0,(int)nums.size()-1);
}
static void q3sort(vector<int>& nums,int lo, int hi){
if(hi<lo) return;
int lt=lo,gt=hi,i=lo+1;
int v=nums[lo];
while(i<=gt){
if(nums[i]<v) std::swap(nums[lt++],nums[i++]);
else if(nums[i]>v) 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<int> test={5,4,4,4,44,-1,2,7};
Quick3way::q3sort(test);
for(auto i:test)
cout<<i<<" "<<endl;
}
================================================
FILE: 2_Sorting/README.md
================================================
代码链接:
https://github.com/ISCASTEAM/Algorithm
# 1.Elementray Sorts
> 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<<" "<<val<<endl;
root = put(root,key,val);
}
Node* put(Node* root,int key,string val){
if(root==NULL)
return new Node(key,val,1);
if(root->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<int> print(){
vector<int> BFS;
queue<Node*> 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<int> BFS = bst->print();
for(auto i:BFS) //按照层级输出
cout<<i<<" ";
}
================================================
FILE: 3_Searching/README.md
================================================
代码链接:
https://github.com/ISCASTEAM/Algorithm
# Elementray Tables
>无序链表
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<int>& 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<int>& 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<<string(1,test[i])<<endl;
rbt->put(i,string(1,test[i]));
}
cout<<"root="<<rbt->getRoot()<<endl;
cout<<"isBalanced="<<rbt->isBalanced()<<endl;
cout<<"tree size="<<rbt->getsize()<<endl;
cout<<"key min="<<rbt->getmin()<<endl;
cout<<"key max="<<rbt->getmax()<<endl;
cout<<endl;
cout<<"Testing:"<<endl;
//inorder to get key
queue<int> res;
rbt->getKeys(res);
while(!res.empty()) {cout<<res.front()<<" "; res.pop();}
cout<<endl;
cout<<"get key==3, val="<<rbt->get(3)<<endl;
return 0;
}
================================================
FILE: 4_Graphs/BFSPath.h
================================================
#ifndef BFSPATH_H_INCLUDED
#define BFSPATH_H_INCLUDED
#include"Graph.h"
class BFSPath{
private:
vector<bool> marked;
vector<int> edgeto; //父节点
public:
BFSPath(Graph* G){
marked.resize(G->getV(),false);
edgeto.reserve(G->getV());
}
void bfs(Graph* G, int s){
/*
input: 图结构Graph, 起始点S
*/
queue<int> 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<int>& 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<double> distTo;
vector<DiEdge> edgeTo;
vector<bool> onQ;
queue<int> 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<double>::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]<std::numeric_limits<double>::max();}
vector<DiEdge> pathTo(int v){
vector<DiEdge> pathRes;
stack<DiEdge> path;
if(!hasPathTo(v)) return vector<DiEdge>();
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<bool> marked;
vector<int> 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;v<G->getV();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<bool> marked;
vector<int> edgeTo;
vector<bool> onstack;
stack<int> 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;v<G->getV();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<bool> marked;
stack<int> reversePost;
public:
DFSTopo(Digraph* G){
marked.resize(G->getV(),false);
}
vector<int> getTopo(Digraph* G){
for(int v=0;v<G->getV();v++){ //可能是多连通分量图
if(!marked[v])
dfs(G,v);
}
vector<int> 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<int> getTopo(EdgeWeightedDigraph* G){
for(int v=0;v<G->getV();v++){ //可能是多连通分量图
if(!marked[v])
dfs(G,v);
}
vector<int> 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<vector<int>> adj; //邻接链表
void addEdge(int v,int w){
adj[v].push_back(w); //区别于无向图
}
public:
Digraph(int v):V(v),E(0){adj.resize(v,vector<int>());};
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<int>());}
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<int> getadj(int v){return adj[v];}
//反转图
Digraph* reverseG(){
Digraph* revG = new Digraph(V);
for(int v=0;v<V;v++){
for(int w : getadj(v))
revG->addEdge(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<DiEdge> edgeTo;
vector<double> distTo;
map<int,double> minPQ; //非树节点vertex和distTo
int getPQmin(){
double minval = std::numeric_limits<double>::max();
int key = 0;
for(map<int,double>::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<int,double>::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<double>::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]<std::numeric_limits<double>::max();}
vector<DiEdge> pathTo(int v){
vector<DiEdge> pathRes;
stack<DiEdge> path;
if(!hasPathTo(v)) return vector<DiEdge>();
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<<G->getE()<<endl;
cout<<G->getV()<<endl;
cout<<G->degree(0)<<endl;
//单点路径问题(DFS判断存在性 BFS求最短)
//有向图是否存在环?
cout<<endl;
DFSDirectedCycle* dfsDC = new DFSDirectedCycle(G);
cout<<true<<endl;
cout<<dfsDC->hasCycle(G)<<endl;
//顶点的三种遍历顺序
//topological拓扑排序 解决优先级问题
//思路: DFS+ revesePost顺序 证明P376 + P388 BFS
cout<<endl;
DFSTopo* dfsTopo = new DFSTopo(G);
for(auto i:dfsTopo->getTopo(G))
cout<<i<<" ";
//思考题:
//P380 强连通分量 Kosaraju算法
//总结P385
return 0;
}
================================================
FILE: 4_Graphs/Edge.h
================================================
#ifndef EDGE_H_INCLUDED
#define EDGE_H_INCLUDED
#include"func.h"
/*
有权重的无向图
边
*/
class Edge{
private:
int v;
int w;
double weight;
public:
Edge()
:v(0),w(0),weight(0.0){}
Edge(int v,int w,double weight)
:v(v),w(w),weight(weight){}
double getWeight()const {return weight;}
//either other获取边的两个顶点
int other(int vTmp){
if(vTmp==v) return w;
else return v;
}
int either(){return v;}
//compare重载
//const成员函数不允许修改类数据 + const对象只能使用const函数
friend bool operator<(const Edge& a,const Edge& b);
friend bool operator>(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<vector<DiEdge>> 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<DiEdge>());}
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<DiEdge> getadj(int v){ return adj[v];}
set<DiEdge> getAllEdges(){
set<DiEdge> allEdges;
for(vector<DiEdge> 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<vector<Edge>> 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<Edge>());}
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<Edge> getadj(int v){ return adj[v];}
set<Edge> getAllEdges(){
set<Edge> allEdges;
for(vector<Edge> 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<vector<int>> 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<int>());};
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<int>());}
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<int> 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<Edge> result;
double totalWeight;
priority_queue<Edge,vector<Edge>,std::greater<Edge>> 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()<G->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<Edge> 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<Edge> mst;
double totalWeight;
vector<bool> marked;
priority_queue<Edge,vector<Edge>,std::greater<Edge>> minpq;
public:
LazyPrimMST(EdgeWeightedGraph* G):totalWeight(0.0){
marked.resize(G->getV(),false);
}
queue<Edge> 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<<G->getE()<<endl;
cout<<G->getV()<<endl;
cout<<G->degree(0)<<endl;
//最小生成树测试
//1.延时删除Prim算法
cout<<endl;
LazyPrimMST* mst = new LazyPrimMST(G);
queue<Edge> res = mst->getMST(G);
while(!res.empty()){
Edge e = res.front();
res.pop();
cout<<e.either()<<"-"<<e.other(e.either())<<" "<<e.getWeight()<<endl;
}
cout<<"min total_weight = "<<mst->getWeight()<<endl;
cout<<endl;
//2.即时删除Prim算法
PrimMST* pmst = new PrimMST(G);
vector<Edge> res2 = pmst->getMST(G);
for(Edge e:res2){
cout<<e.either()<<"-"<<e.other(e.either())<<" "<<e.getWeight()<<endl;
}
cout<<"min total_weight = "<<pmst->getWeight()<<endl;
cout<<endl;
//3.克鲁斯卡尔算法
KruskalMST* kmst = new KruskalMST(G);
queue<Edge> res3 = kmst->getRes();
while(!res3.empty()){
Edge e = res3.front();
res3.pop();
cout<<e.either()<<"-"<<e.other(e.either())<<" "<<e.getWeight()<<endl;
}
cout<<"min total_weight = "<<kmst->getWeight()<<endl;
return 0;
}
================================================
FILE: 4_Graphs/PrimMST.h
================================================
#ifndef PRIMMST_H_INCLUDED
#define PRIMMST_H_INCLUDED
#include "EdgeWeightedGraph.h"
/*
即时删除的prim最小生成树
idea: 随机起点 + 可到达边的最小值加入tree
note:
1.lazy模式 会将Edge都加入queue ==》 priority queue
2.即时 在加入queue进行判断: 无效边+非最小边 被拒绝
3.数据结构:
edgeTo[w] w距离tree最近的edge 或者NULL
dist[w]=edgeTo[w].weight
indexQueue 用map<w,weight>实现,保存待遍历的横切边
相比较于Lazy模式 indexPQ中的可达边数量是常数级别
*/
class PrimMST{
private:
vector<Edge> edgeTo;
vector<double> distTo;
vector<bool> marked;
map<int,double> indexPQ;
double totalWeight;
//indexPQ min_val对应的顶点
int getPQmin(){
double minval = std::numeric_limits<double>::max();
int key = 0;
for(map<int,double>::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<int,double>::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<double>::max());
marked.resize(G->getV(),false);
}
vector<Edge> 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<<e.either()<<"-"<<e.other(e.either())<<" "<<e.getWeight()<<endl;
distTo[w] = e.getWeight();
indexPQ[w]=distTo[w]; //map中不存在w 则会添加
}
}
}
double getWeight(){ return totalWeight;}
vector<Edge> 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<<G->getE()<<endl;
cout<<G->getV()<<endl;
cout<<G->degree(0)<<endl;
//Dijkstra shortest path测试
cout<<endl;
int startV=0;
int endV = 6;
DijkstraSP* dSP = new DijkstraSP(G,startV);
dSP->getSP(G,startV);
cout<<"hasPathTo endVertex="<<endV<<" is "<<(bool)dSP->hasPathTo(endV)<<endl;
double totalW=0.0;
for(DiEdge e:dSP->pathTo(endV)){
cout<<e.from()<<"-"<<e.to()<<" "<<e.getWeight()<<endl;
totalW += e.getWeight();
}
cout<<"from 0 to 6, shortestpath="<<totalW<<endl;
cout<<endl;
//无环图 LongestPath测试
startV=5;
endV = 2;
G = new EdgeWeightedDigraph("./data/tinyEWDAG.txt");
TopoLP* topoLP = new TopoLP(G);
topoLP->getTopoLP(G,startV);
totalW=0.0;
for(DiEdge e:topoLP->pathTo(endV)){
cout<<e.from()<<"-"<<e.to()<<" "<<e.getWeight()<<endl;
totalW += e.getWeight();
}
cout<<"from 5 to 2, longestpath="<<totalW<<endl;
//思考:
//应用: 限制优先级问题
//idea: 将限制条件转化为有向图 P431 => 加权有向图最长路径
//一般性问题 Bellman-Ford
//不存在权重值之和为负的还 有向图
cout<<endl;
startV=0;
endV = 3;
G = new EdgeWeightedDigraph("./data/tinyEWDnc.txt");
BellmanSP* bSP = new BellmanSP(G);
bSP->getBellmanSP(G,startV);
totalW=0.0;
for(DiEdge e:bSP->pathTo(endV)){
cout<<e.from()<<"-"<<e.to()<<" "<<e.getWeight()<<endl;
totalW += e.getWeight();
}
cout<<"from 0 to 1, longestpath="<<totalW<<endl;
//思考:
//金融套汇问题 ==》 转化为寻找负权重之和的环 P444
}
================================================
FILE: 4_Graphs/TopoLongestPath.h
================================================
#ifndef TOPOSHORTESTPATH_H_INCLUDED
#define TOPOSHORTESTPATH_H_INCLUDED
#include"EdgeWeightedDigraph.h"
#include"DFSTopo.h"
/*
Dijkstra 非负权重的有向图单源最短路径
TopoSp 无环有向图
1.线性时间找最短路径 + Weight可以为负 + 变换权重正负号寻找最长路径
idea:
按照topological顺序relax所有顶点
证明 P426
note: 1.将weight全部取相反数 或者2.交换所有大小判断符号
*/
class TopoLP{
private:
vector<double> distTo;
vector<DiEdge> 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<double>::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<double>::min();}
vector<DiEdge> pathTo(int v){
vector<DiEdge> pathRes;
stack<DiEdge> path;
if(!hasPathTo(v)) return vector<DiEdge>();
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<<G->getE()<<endl;
cout<<G->getV()<<endl;
cout<<G->degree(0)<<endl;
//DFS得到图中连通集的数量
cout<<endl;
DFSCC* cc = new DFSCC(G);
cout<<cc->getSetNum(G)<<endl; //3个连通分量
//DFS思考:
//判断是否存在从节点V到节点W的路径?
//判断图是否存在环?
//判断图是否可以只用两种颜色标记?
//P599 两点之间最长路径 DFS变型
//BFS得到最短单点路径问题
cout<<endl;
BFSPath* bfs = new BFSPath(G);
stack<int> path;
bfs->bfs(G,0);
bfs->pathTo(path,0,3);
while(!path.empty()){
cout<<path.top()<<" ";
path.pop();
}
return 0;
}
================================================
FILE: 4_Graphs/UnionFind.h
================================================
#ifndef UNIONFIND_H_INCLUDED
#define UNIONFIND_H_INCLUDED
#include"func.h"
/*
实现weighted union find
小树加入大树 从而减少树的高度
*/
class UnionFind{
private:
vector<int> parent;
vector<int> sz;
int unionCount;
public:
UnionFind(int vertexNum)
:unionCount(vertexNum){
parent.resize(vertexNum);
sz.resize(vertexNum,1);
for(int i=0;i<vertexNum;i++)
parent[i]=i;
}
int getUnionNum(){return unionCount;}
//API= find,union,connected
bool connected(int v,int w){
return findRoot(v) == findRoot(w);
}
int findRoot(int p){
while(p != parent[p])
p=parent[p];
return p;
}
void unionV(int v,int w){
int i=findRoot(v);
int j=findRoot(w);
if(i==j) return;
if(sz[i]<sz[j]){parent[i]=j; sz[j]+=sz[i];}
else{parent[j]=i; sz[i]+=sz[j];}
unionCount--;
}
};
#endif // UNIONFIND_H_INCLUDED
================================================
FILE: 5_Strings/Huffman.h
================================================
#ifndef HUFFMAN_H_INCLUDED
#define HUFFMAN_H_INCLUDED
#include"func.h"
class Huffman{
private:
const int R = 256;
class Node{
// private:
public:
char ch; //非叶子节点是'\0'
int freq;
Node* left;
Node* right; //leaf.left and right is NULL
Node(char c,int f,Node* l,Node*r)
:ch(c),freq(f),left(l),right(r){}
char getVal(){return ch;}
bool isLeaf(){return left==NULL&&right==NULL;}
bool operator<(const Node&that){return freq <that.freq;}
};
Node* buildTrie(const vector<int>& freq){
priority_queue<Node*,vector<Node*>,std::greater<Node*>> minPQ;
for(int i=0;i<R;i++)
if(freq[i]>0)
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<string>& 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<string> st;
Huffman(){st.resize(R,"");}
int getR(){return R;}
void compress(string s){
//1.统计字符频率
vector<int> 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= "<<test<<endl<<endl;
Huffman* hf = new Huffman();
hf->compress(test);
cout<<"after compressing:"<<endl;
cout<<hf->encodeStr(test)<<endl;
cout<<endl;
cout<<"Trie Tree:"<<endl;
for(int i=0;i<hf->getR();i++)
if(hf->st[i]!="")
cout<<(char)i<<" "<<hf->st[i]<<endl;
}
================================================
FILE: 5_Strings/Quick3String.h
================================================
#ifndef QUICK3STRING_H_INCLUDED
#define QUICK3STRING_H_INCLUDED
#include"func.h"
/*
字符串数组排序
LGD or MGD or quick3string 适用情况 P471
当字符串具有公共前缀时 三向快速排序更合适
quick3string 更加通用
note:
剩余15个字符可以使用插入排序
*/
class Quick3string{
private:
int charAt(string s,int d){
if(d==(int)s.size()) return -1;
return s[d];
}
void swap(vector<string>& a,int i,int j){
string tmp = a[i];
a[i]=a[j];
a[j]=tmp;
}
//高位优先形式 对dth位置字符三向切分
void quick3string(vector<string>& 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<string>& 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<string> 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<<i<<endl;
}
================================================
FILE: 5_Strings/README.md
================================================
# 字符串数组排序
>低位优先
缺点:
字符串要求相同长度
>高位优先
> 三向快排排序
思路:高位优先的,递归排序。之间相等部分去掉首字符继续递归
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<bool> marked;
vector<FlowEdge> 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<int> 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<double>::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<vector<FlowEdge>> 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<FlowEdge>());
}
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<FlowEdge>());}
else if(lineNum==1) iss>>E;
else {
iss>>v1>>v2>>capacity;
// cout<<v1<<" "<<v2<<" "<<capacity<<" "<<endl;
addEdge(v1,v2,capacity);
}
lineNum++;
}
}
//其他API
int getV(){return V;}
int getE(){return E;}
vector<FlowEdge> getadj(int v){return adj[v];}
void addResidualFlowTo(FlowEdge& e,int v,double delta){ //传入参数V是边的endPoint
for(vector<FlowEdge>& 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<string> suffixes;
int N;
int lcp(string str1,string str2){ //两个字符串的最长公共前缀
int len = std::min(str1.size(),str2.size());
for(int i=0;i<len;i++)
if(str1[i]!=str2[i])
return i;
return len;
}
public:
//sorted 后缀数组
SuffixArray(string str){
N = str.size();
suffixes.resize(N,"");
for(int i=0;i<N;i++)
suffixes[i]=str.substr(i); //substr(i,N-i);
Quick3string* q3s = new Quick3string();
q3s->vectorStrSort(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<string> 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<<i<<endl;
//输出最长重复的子字符串
cout<<sfa->getLRS()<<endl;
}
================================================
FILE: README.md
================================================
# 使用说明
本项目C++实现基础算法库,重构于Algorithm4th java代码
e.g. 以插入排序为例
```java
//1.添加算法所需头文件
#include"Insertion.h"
int main(){
vector<int> test={5,4,-1,2,7};
//2. 调用算法
Insertion::insort(test);
for(auto i:test)
cout<<i<<" "<<endl;
}
//3.其他算法使用样例 请查阅对应*_test.cpp文件
```
# 算法-头文件对应
*基础*
> 背包,队列,栈
> 连通集 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<string.h>
#include<stdio.h>
#include<iostream>
#include <fstream>
#include<string>
#include<set>
#include<stack>
#include<vector>
#include<sstream>
#include<queue>
#include<limits>
#include<map>
#include<unordered_map>
#include<hash_set>
#include <utility>
#include<algorithm>
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__
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
SYMBOL INDEX (95 symbols across 40 files)
FILE: 1_Fundamental/UnionFind.h
function class (line 9) | class UnionFind{
FILE: 2_Sorting/HeapSort.h
function class (line 17) | class HeapSort{
FILE: 2_Sorting/Heapsort_test.cpp
function main (line 12) | int main(){
FILE: 2_Sorting/Insertion.h
function class (line 4) | class Insertion{
FILE: 2_Sorting/Insertion_test.cpp
function main (line 3) | int main(){
FILE: 2_Sorting/Quick3way.h
function class (line 12) | class Quick3way{
FILE: 2_Sorting/Quick3way_test.cpp
function main (line 3) | int main(){
FILE: 3_Searching/BinarySearchTree.h
function class (line 9) | class BST{
function sizeTree (line 27) | int sizeTree(Node* root){if(root==NULL) return 0;else return root->Size;}
function sizeTree (line 28) | int sizeTree(){return sizeTree(root);}
function isEmpty (line 29) | bool isEmpty(){return sizeTree()==0;}
function contains (line 30) | bool contains(int key){ return get(key)!="";}
function string (line 31) | string get(int key){return get(root,key);}
function string (line 32) | string get(Node* root,int key){
function put (line 40) | void put(int key,string val){
function Node (line 44) | Node* put(Node* root,int key,string val){
function deleteMin (line 58) | void deleteMin(){root=deleteMin(root);}
function Node (line 59) | Node* deleteMin(Node* root){
function deleteKey (line 66) | void deleteKey(int key){root = deleteKey(root,key);}
function Node (line 67) | Node* deleteKey(Node* root, int key){
function Node (line 84) | Node* minLeaf(Node* root){
FILE: 3_Searching/BinarySearchTree_test.cpp
function main (line 3) | int main(){
FILE: 3_Searching/RedBlackTree.h
function class (line 3) | class RedBlackTree{
function Node (line 33) | Node* rotateRight(Node* h){
function Node (line 41) | Node* rotateLeft(Node* h){
function flipColors (line 49) | void flipColors(Node* h){
function isRed (line 54) | bool isRed(Node* x){
function getmin (line 58) | int getmin(Node* root){
function getmax (line 66) | int getmax(Node* root){
function getKeys (line 74) | void getKeys(Node* root,queue<int>& res){
function getsize (line 83) | int getsize(){return this->_size;}
function getmin (line 84) | int getmin(){ return getmin(_root);}
function getmax (line 85) | int getmax(){return getmax(_root);}
function getKeys (line 86) | void getKeys(queue<int>& res){ return getKeys(_root,res);}
function getRoot (line 87) | int getRoot(){return _root->_key;}
function isBalanced (line 89) | bool isBalanced(){
function isBalanced (line 98) | bool isBalanced(Node* root, int numBlack){
function string (line 109) | string RedBlackTree::get(Node* x, int key){
FILE: 3_Searching/RedBlackTree_test.cpp
function main (line 4) | int main(){
FILE: 4_Graphs/BFSPath.h
function class (line 5) | class BFSPath{
function pathTo (line 36) | void pathTo(stack<int>& path,int s,int v){
FILE: 4_Graphs/BellmanFordSP.h
function class (line 17) | class BellmanSP{
function getBellmanSP (line 51) | void getBellmanSP(EdgeWeightedDigraph* G,int s){
function getdistTo (line 65) | double getdistTo(int v){return distTo[v];}
function hasPathTo (line 66) | bool hasPathTo(int v){return distTo[v]<std::numeric_limits<double>::max();}
FILE: 4_Graphs/DFSCC.h
function class (line 11) | class DFSCC{
function getSetNum (line 31) | int getSetNum(Graph* G){
FILE: 4_Graphs/DFSDirectedCycle.h
function class (line 10) | class DFSDirectedCycle{
FILE: 4_Graphs/DFSTopo.h
function class (line 11) | class DFSTopo{
function dfs (line 63) | void dfs(EdgeWeightedDigraph* G,int v){
FILE: 4_Graphs/DiEdge.h
function class (line 8) | class DiEdge{
FILE: 4_Graphs/Digraph.h
function class (line 5) | class Digraph{
FILE: 4_Graphs/DijkstraSP.h
function class (line 13) | class DijkstraSP{
FILE: 4_Graphs/DirectedGraph_test.cpp
function main (line 5) | int main(){
FILE: 4_Graphs/Edge.h
function class (line 10) | class Edge{
function other (line 24) | int other(int vTmp){
function either (line 28) | int either(){return v;}
FILE: 4_Graphs/EdgeWeightedDigraph.h
function class (line 7) | class EdgeWeightedDigraph{
FILE: 4_Graphs/EdgeWeightedGraph.h
function class (line 8) | class EdgeWeightedGraph{
FILE: 4_Graphs/Graph.h
function class (line 5) | class Graph{
FILE: 4_Graphs/LazyPrimMST.h
function class (line 13) | class LazyPrimMST{
FILE: 4_Graphs/MinTree_test.cpp
function main (line 6) | int main(){
FILE: 4_Graphs/PrimMST.h
function getPQmin (line 29) | int getPQmin(){
function erasePQ (line 40) | void erasePQ(int v){
FILE: 4_Graphs/ShortestPath_test.cpp
function main (line 5) | int main(){
FILE: 4_Graphs/TopoLongestPath.h
function class (line 18) | class TopoLP{
FILE: 4_Graphs/UndirectedGraph_test.cpp
function initG (line 5) | int initG(){
function main (line 19) | int main(){
FILE: 4_Graphs/UnionFind.h
function class (line 9) | class UnionFind{
FILE: 5_Strings/Huffman.h
function class (line 4) | class Huffman{
function Node (line 22) | Node* buildTrie(const vector<int>& freq){
function buildCode (line 40) | void buildCode(vector<string>& st,Node* root,string s){
function getR (line 53) | int getR(){return R;}
function compress (line 55) | void compress(string s){
FILE: 5_Strings/Huffman_test.cpp
function main (line 3) | int main(){
FILE: 5_Strings/Quick3String.h
function class (line 15) | class Quick3string{
FILE: 5_Strings/Quick3String_test.cpp
function main (line 14) | int main(){
FILE: 6_Context/FlowEdge.h
function class (line 10) | class FlowEdge{
function addResidualFlowTo (line 29) | void addResidualFlowTo(int vertex, double delta){
function getFlow (line 38) | double getFlow(){return flow;}
function other (line 39) | int other(int vertex){
function setFlow (line 44) | void setFlow(double delta){flow += delta;}
FILE: 6_Context/FlowFordFulkerson.h
function hasAugmentingPath (line 18) | bool hasAugmentingPath(FlowNetwork*G,int s,int t){
FILE: 6_Context/FlowNetwork.h
function class (line 8) | class FlowNetwork{
function getV (line 43) | int getV(){return V;}
function getE (line 44) | int getE(){return E;}
function addResidualFlowTo (line 46) | void addResidualFlowTo(FlowEdge& e,int v,double delta){ //传入参数V是边的en...
FILE: 6_Context/SuffixArray.h
function class (line 21) | class SuffixArray{
function lcp (line 42) | int lcp(int i){
function string (line 46) | string getLRS(){
function rankSuffix (line 57) | int rankSuffix(string key){
function length (line 75) | int length(){return N;}
function string (line 76) | string select(int i){return suffixes[i];}
function index (line 77) | int index(int i){return N-select(i).size();}
FILE: 6_Context/SuffixArray_test.cpp
function main (line 3) | int main(){
Condensed preview — 49 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (72K chars).
[
{
"path": "1_Fundamental/README.md",
"chars": 1172,
"preview": "代码链接:\nxxxxxxxxx\n \n# 1.binary search \nurl get data练习 \nedu.princeton.cs.algs4 普林斯顿算法 jar包 \n \n# 2.dataStruct \nqueu"
},
{
"path": "1_Fundamental/UnionFind.h",
"chars": 935,
"preview": "#ifndef UNIONFIND_H_INCLUDED\n#define UNIONFIND_H_INCLUDED\n#include\"func.h\"\n/*\n实现weighted union find\n小树加入大树 从而减少树的高度\n*/\n\n"
},
{
"path": "2_Sorting/HeapSort.h",
"chars": 1103,
"preview": "#ifndef HEAPSORT_H_INCLUDED\n#define HEAPSORT_H_INCLUDED\n#include\"func.h\"\n\n/*\n堆排序\n1.构建有序堆(最大堆)\n2.不断取出maxVal,在重新有序化即可\n3.直到"
},
{
"path": "2_Sorting/Heapsort_test.cpp",
"chars": 240,
"preview": "#include\"HeapSort.h\"\n/*\n堆排序\n1.构建有序堆(最大堆)\n2.不断取出maxVal,在重新有序化即可\n3.直到堆中不存在新元素\n\nnote:有序化API\nswim 上浮\nsink 下沉\n*/\nint main(){\n"
},
{
"path": "2_Sorting/Insertion.h",
"chars": 380,
"preview": "#ifndef INSERTION_H_INCLUDED\n#define INSERTION_H_INCLUDED\n#include\"func.h\"\nclass Insertion{\n //class 默认private\npublic"
},
{
"path": "2_Sorting/Insertion_test.cpp",
"chars": 150,
"preview": "#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"
},
{
"path": "2_Sorting/Quick3way.h",
"chars": 741,
"preview": "#ifndef QUICK3WAY_H_INCLUDED\n#define QUICK3WAY_H_INCLUDED\n#include\"func.h\"\n/*\n三向快速排序\n每次递归将 小数组首元素相同的元素放在相邻位置\n\n优点:\n对于大量重复"
},
{
"path": "2_Sorting/Quick3way_test.cpp",
"chars": 157,
"preview": "#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"
},
{
"path": "2_Sorting/README.md",
"chars": 2186,
"preview": "代码链接: \nhttps://github.com/ISCASTEAM/Algorithm \n\n# 1.Elementray Sorts \n> Select sort P156 \nkey:选择第i小的元素放入a[i]位置 \nn"
},
{
"path": "3_Searching/BinarySearchTree.h",
"chars": 2953,
"preview": "#ifndef BINARYSEARCHTREE_H_INCLUDED\n#define BINARYSEARCHTREE_H_INCLUDED\n#include\"func.h\"\n/*\nkey: Int\nval: string\n*/\n\ncla"
},
{
"path": "3_Searching/BinarySearchTree_test.cpp",
"chars": 288,
"preview": "#include\"BinarySearchTree.h\"\n\nint main(){\n BST* bst = new BST();\n std::ostringstream ss;\n for(int i=0;i<10;i++)"
},
{
"path": "3_Searching/README.md",
"chars": 2868,
"preview": "代码链接: \nhttps://github.com/ISCASTEAM/Algorithm \n\n# Elementray Tables \n>无序链表 \nkey: 插入时间O(1) 查找时间O(N) \nnote: 新元素直"
},
{
"path": "3_Searching/RedBlackTree.h",
"chars": 3419,
"preview": "#include \"func.h\"\n\nclass RedBlackTree{\npublic:\n RedBlackTree():_root(NULL),_size(0){};\n string get(int key){ retur"
},
{
"path": "3_Searching/RedBlackTree_test.cpp",
"chars": 703,
"preview": "\n#include \"RedBlackTree.h\"\n\nint main(){\n string test = \"ABCDEFG\";\n RedBlackTree* rbt = new RedBlackTree();\n for"
},
{
"path": "4_Graphs/BFSPath.h",
"chars": 931,
"preview": "#ifndef BFSPATH_H_INCLUDED\n#define BFSPATH_H_INCLUDED\n\n#include\"Graph.h\"\nclass BFSPath{\nprivate:\n vector<bool> marked"
},
{
"path": "4_Graphs/BellmanFordSP.h",
"chars": 2212,
"preview": "#ifndef BELLMANFORDSP_H_INCLUDED\n#define BELLMANFORDSP_H_INCLUDED\n#include\"EdgeWeightedDigraph.h\"\n/*\n定理:\n1.distTo[S]=0,其"
},
{
"path": "4_Graphs/DFSCC.h",
"chars": 806,
"preview": "#ifndef DFSCC_H_INCLUDED\n#define DFSCC_H_INCLUDED\n\n/*TODO 判断图存在多少个连通分量\n思路:\n所有未标记的节点 执行一遍完整DFS遍历\nDFS代表从某一个节点出发 能到达的所有节点"
},
{
"path": "4_Graphs/DFSDirectedCycle.h",
"chars": 1223,
"preview": "#ifndef DFSDIRECTEDCYCLE_H_INCLUDED\n#define DFSDIRECTEDCYCLE_H_INCLUDED\n#include\"Digraph.h\"\n/*\n有向图 判断是否存在有向环\n\n不存在环路 topo"
},
{
"path": "4_Graphs/DFSTopo.h",
"chars": 1550,
"preview": "#ifndef DFSTOPO_H_INCLUDED\n#define DFSTOPO_H_INCLUDED\n#include\"Digraph.h\"\n#include\"EdgeWeightedDigraph.h\"\n/*\n拓扑排序 DFS+一"
},
{
"path": "4_Graphs/DiEdge.h",
"chars": 1172,
"preview": "#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*/\ncl"
},
{
"path": "4_Graphs/Digraph.h",
"chars": 1163,
"preview": "#ifndef DIGRAPH_H_INCLUDED\n#define DIGRAPH_H_INCLUDED\n#include \"func.h\"\n\nclass Digraph{\nprivate:\n int V;\n int E;\n "
},
{
"path": "4_Graphs/DijkstraSP.h",
"chars": 2200,
"preview": "#ifndef DIJKSTRASP_H_INCLUDED\n#define DIJKSTRASP_H_INCLUDED\n#include\"DiEdge.h\"\n#include\"EdgeWeightedDigraph.h\"\n/*\n非负权重 有"
},
{
"path": "4_Graphs/DirectedGraph_test.cpp",
"chars": 705,
"preview": "#include\"Digraph.h\"\n#include\"DFSDirectedCycle.h\"\n#include\"DFSTopo.h\"\n\nint main(){\n Digraph* G = new Digraph(\"./data/t"
},
{
"path": "4_Graphs/Edge.h",
"chars": 979,
"preview": "#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 i"
},
{
"path": "4_Graphs/EdgeWeightedDigraph.h",
"chars": 1246,
"preview": "#ifndef EDGEWEIGHTEDDIGRAPH_H_INCLUDED\n#define EDGEWEIGHTEDDIGRAPH_H_INCLUDED\n#include\"DiEdge.h\"\n/*\n加权 有向图的定义\n*/\nclass E"
},
{
"path": "4_Graphs/EdgeWeightedGraph.h",
"chars": 1267,
"preview": "#ifndef EDGEWEIGHTEDGRAPH_H_INCLUDED\n#define EDGEWEIGHTEDGRAPH_H_INCLUDED\n#include\"func.h\"\n#include\"Edge.h\"\n/*\n实现带带权重的无向"
},
{
"path": "4_Graphs/Graph.h",
"chars": 947,
"preview": "#ifndef GRAPH_H_INCLUDED\n#define GRAPH_H_INCLUDED\n\n#include \"func.h\"\nclass Graph{\nprivate:\n int V;\n int E;\n vec"
},
{
"path": "4_Graphs/KruskalMST.h",
"chars": 1077,
"preview": "#ifndef KRUSKALMST_H_INCLUDED\n#define KRUSKALMST_H_INCLUDED\n#include\"EdgeWeightedGraph.h\"\n#include\"UnionFind.h\"\n/*\n克鲁斯卡尔"
},
{
"path": "4_Graphs/LazyPrimMST.h",
"chars": 1251,
"preview": "#ifndef LAZYPRIMMST_H_INCLUDED\n#define LAZYPRIMMST_H_INCLUDED\n\n#include \"EdgeWeightedGraph.h\"\n\n/*\n延时删除的prim最小生成树\npre: 图是"
},
{
"path": "4_Graphs/MinTree_test.cpp",
"chars": 1275,
"preview": "#include\"EdgeWeightedGraph.h\"\n#include\"LazyPrimMST.h\"\n#include\"PrimMST.h\"\n#include\"KruskalMST.h\"\n\nint main(){\n EdgeWe"
},
{
"path": "4_Graphs/PrimMST.h",
"chars": 2225,
"preview": "#ifndef PRIMMST_H_INCLUDED\n#define PRIMMST_H_INCLUDED\n\n#include \"EdgeWeightedGraph.h\"\n\n/*\n即时删除的prim最小生成树\nidea: 随机起点 + 可到"
},
{
"path": "4_Graphs/README.md",
"chars": 1222,
"preview": "代码链接:\nxxxxxxxxxxxx\n \n# 无向图 \nGraph.h 实现图的API 邻接链表保存边 \nDFS BFS的核心代码 \n>应用 \n连通性||单点路径 DFS or BFS \n单点最短路径 BFS \n其它: \n"
},
{
"path": "4_Graphs/ShortestPath_test.cpp",
"chars": 1699,
"preview": "#include\"DijkstraSP.h\"\n#include\"TopoLongestPath.h\"\n#include\"BellmanFordSP.h\"\n\nint main(){\n EdgeWeightedDigraph* G = n"
},
{
"path": "4_Graphs/TopoLongestPath.h",
"chars": 1616,
"preview": "#ifndef TOPOSHORTESTPATH_H_INCLUDED\n#define TOPOSHORTESTPATH_H_INCLUDED\n#include\"EdgeWeightedDigraph.h\"\n#include\"DFSTopo"
},
{
"path": "4_Graphs/UndirectedGraph_test.cpp",
"chars": 989,
"preview": "#include\"Graph.h\"\n#include\"DFSCC.h\"\n#include\"BFSPath.h\"\n\nint initG(){\n std::ifstream infile(\"./data/tinyG.txt\");\n "
},
{
"path": "4_Graphs/UnionFind.h",
"chars": 935,
"preview": "#ifndef UNIONFIND_H_INCLUDED\n#define UNIONFIND_H_INCLUDED\n#include\"func.h\"\n/*\n实现weighted union find\n小树加入大树 从而减少树的高度\n*/\n\n"
},
{
"path": "5_Strings/Huffman.h",
"chars": 2008,
"preview": "#ifndef HUFFMAN_H_INCLUDED\n#define HUFFMAN_H_INCLUDED\n#include\"func.h\"\nclass Huffman{\nprivate:\n const int R = 256;\n "
},
{
"path": "5_Strings/Huffman_test.cpp",
"chars": 400,
"preview": "#include\"Huffman.h\"\n\nint main(){\n string test = \"ABRACADABRA!\";\n cout<<\"test string= \"<<test<<endl<<endl;\n\n Huf"
},
{
"path": "5_Strings/Quick3String.h",
"chars": 1134,
"preview": "#ifndef QUICK3STRING_H_INCLUDED\n#define QUICK3STRING_H_INCLUDED\n\n#include\"func.h\"\n/*\n字符串数组排序\n\nLGD or MGD or quick3string"
},
{
"path": "5_Strings/Quick3String_test.cpp",
"chars": 391,
"preview": "#include\"Quick3string.h\"\n/*\n字符串数组排序\n\nLGD or MGD or quick3string 适用情况 P471\n当字符串具有公共前缀时 三向快速排序更合适\nquick3string 更加通用\n\nnote:"
},
{
"path": "5_Strings/README.md",
"chars": 251,
"preview": "# 字符串数组排序 \n>低位优先 \n缺点: \n字符串要求相同长度 \n>高位优先 \n> 三向快排排序 \n思路:高位优先的,递归排序。之间相等部分去掉首字符继续递归 \nnote: \n大量具有公共前缀的字符串数组的排序 \n \n"
},
{
"path": "6_Context/FlowEdge.h",
"chars": 1224,
"preview": "#ifndef FLOWEDGE_H_INCLUDED\n#define FLOWEDGE_H_INCLUDED\n\n#include\"func.h\"\n/*\nmaxFlow最大流问题\n相比较无向图Edge,添加两个变量和两个API\n*/\n\ncl"
},
{
"path": "6_Context/FlowFordFulkerson.h",
"chars": 1862,
"preview": "#ifndef FLOWFORDFULKERSON_H_INCLUDED\n#define FLOWFORDFULKERSON_H_INCLUDED\n#include\"FlowNetwork.h\"\n/*\nFord-Fulkerson方法\n1."
},
{
"path": "6_Context/FlowNetwork.h",
"chars": 1584,
"preview": "#ifndef FLOWNETWORK_H_INCLUDED\n#define FLOWNETWORK_H_INCLUDED\n#include\"FlowEdge.h\"\n/*\n由FlowEdge构成的图 和无向图的结构相同\n*/\n\nclass "
},
{
"path": "6_Context/README.md",
"chars": 211,
"preview": "# 事件驱动的粒子碰撞 \n \n# B-树 \n查找成本很低 需要空间大 \n \n# 后缀数组 \n字符串的子串中最长的公共前缀问题 \n思路: \n排序的后缀数组,最长的公共前缀在相邻的位置出现 \n \n# 最大流 \n给定有向图"
},
{
"path": "6_Context/SuffixArray.h",
"chars": 1919,
"preview": "#ifndef SUFFIXARRAY_H_INCLUDED\n#define SUFFIXARRAY_H_INCLUDED\n#include\"func.h\"\n#include\"Quick3String.h\"\n//连续字符 子字符串\n//非连"
},
{
"path": "6_Context/SuffixArray_test.cpp",
"chars": 307,
"preview": " #include\"SuffixArray.h\"\n\n int main(){\n string test = \"aacaagtttacaagc\";\n string longReaptedStr = \"\""
},
{
"path": "README.md",
"chars": 1391,
"preview": "# 使用说明 \n本项目C++实现基础算法库,重构于Algorithm4th java代码 \n\ne.g. 以插入排序为例 \n```java\n//1.添加算法所需头文件\n#include\"Insertion.h\"\n\nint main(){"
},
{
"path": "func.h",
"chars": 583,
"preview": "#ifndef __FUNC_H__\n#define __FUNC_H__\n\n#include<string.h>\n#include<stdio.h>\n#include<iostream>\n#include <fstream>\n#inclu"
}
]
About this extraction
This page contains the full source code of the ISCASTEAM/Algorithm GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 49 files (57.9 KB), approximately 20.1k tokens, and a symbol index with 95 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.