├── .gitignore ├── Fibonacci ├── Fibonacci.cpp ├── Fibonacci.java └── Fibonacci.py ├── Huffman └── Huffman_expected_length.cpp ├── README.md ├── activity_selector └── activity_selector.cpp ├── backtrack ├── coloring.cpp ├── eight_queens.cpp ├── maze.cpp └── n_queens.cpp ├── billionsort ├── billionsort.cpp ├── counting_sort.cpp ├── counting_sort_example.cpp ├── insertion_sort.cpp ├── parallel_billionsort.cpp ├── parallel_gcc_billionsort.cpp ├── pstl_billionsort.cpp └── sort_forward_list.cpp ├── breadth_first_search └── BFS.cpp ├── bucket_sort ├── bucket_sort.cpp └── histogram_sort.cpp ├── combinatorics ├── permutation.cpp └── subset.cpp ├── cut_rod └── cut_rod.cpp ├── depth_first_search └── DFS.cpp ├── graph_diameter ├── sparse_graph_diameter.cpp └── tree_diameter.cpp ├── knapsack └── fractional_knapsack_greedy_algorithm.cpp ├── lcs └── lcs.cpp ├── matrix_chain_multiplication ├── matrix_chain_multiplication.cpp └── memoized_matrix_chain.cpp ├── merge ├── merge.cpp ├── merge.py └── multiway_merge.cpp ├── miscellaneous └── astrological_signs.cpp ├── sample.pdf ├── shortest_paths ├── Bellman_Ford_Moore.cpp ├── Dijkstra_heap.cpp ├── Dijkstra_priority_queue.cpp ├── Dijkstra_set.cpp └── Floyd_Warshall.cpp ├── sieve ├── Eratosthenes.cpp └── Pritchard.cpp ├── string_matching └── Rabin_Karp.cpp ├── strongly_connected_component └── SCC.cpp ├── topological_sorting └── Kahn.cpp └── unique ├── unique.cpp ├── unique.js └── unique.py /.gitignore: -------------------------------------------------------------------------------- 1 | # ignore vscode config 2 | .vscode/ 3 | .DS_Store 4 | -------------------------------------------------------------------------------- /Fibonacci/Fibonacci.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | std::pair Fib(size_t n) 4 | { 5 | // 返回F_{n}和F_{n + 1}, 注意是对UINT64_MAX取模的结果. 6 | if (n > 0) 7 | { 8 | auto PF = Fib(n / 2); // 基于递归求解. 9 | auto t0 = PF.first; 10 | auto t1 = PF.second; 11 | if (n % 2 == 1) // 奇数情况. 12 | return {t0 * t0 + t1 * t1, (2 * t0 + t1) * t1}; 13 | else 14 | return {(2 * t1 - t0) * t0, t0 * t0 + t1 * t1}; 15 | } 16 | return {0, 1}; // 基础情形: F_0和F1. 17 | } 18 | -------------------------------------------------------------------------------- /Fibonacci/Fibonacci.java: -------------------------------------------------------------------------------- 1 | import java.math.BigInteger; 2 | 3 | public class Fibonacci { 4 | //Fibonacci compute O(lg n) 5 | public static BigInteger[] Fib(BigInteger n) { 6 | if (n.equals(BigInteger.ZERO)) return new BigInteger[]{BigInteger.ZERO, BigInteger.ONE}; 7 | BigInteger[] tmp = Fib(n.divide(BigInteger.valueOf(2))); 8 | if (n.and(BigInteger.ONE).equals(BigInteger.ONE)) { 9 | return new BigInteger[]{tmp[0].multiply(tmp[0]).add(tmp[1].multiply(tmp[1])), 10 | tmp[0].multiply(BigInteger.valueOf(2)).add(tmp[1]).multiply(tmp[1])}; 11 | } 12 | else { 13 | return new BigInteger[]{tmp[1].multiply(BigInteger.valueOf(2)).subtract(tmp[0]).multiply(tmp[0]), 14 | tmp[0].multiply(tmp[0]).add(tmp[1].multiply(tmp[1]))}; 15 | } 16 | } 17 | 18 | public static void main(String[] args) { 19 | BigInteger n = BigInteger.valueOf(10); 20 | BigInteger result = Fib(n)[0]; 21 | System.out.println(result); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Fibonacci/Fibonacci.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | # -*- encoding:utf-8 -*- 3 | 4 | """ 5 | 使用 Python 计算 Fibonacci 数:0, 1, 1, 2, 3, 5, ... 6 | 7 | 1. fibonacci_simple, 最简单的写法,直接利用递归调用 8 | 2. fibonacci_list,使用列表缓存中间结果 9 | 3. fibonacci_formula,使用公式推导 10 | """ 11 | 12 | from __future__ import print_function 13 | import signal 14 | import time 15 | 16 | 17 | def signle_handler(signal_n, frame): 18 | """ 19 | 利用信号处理实现简单的定时器 20 | :param signal_n: 21 | :param frame: 22 | """ 23 | raise Exception('end of time') 24 | 25 | 26 | def timer(func): 27 | """ 28 | 计时器修饰器 29 | :param func: 30 | """ 31 | def wrapper(*args, **kwargs): 32 | _start = time.time() 33 | _result = func(*args, **kwargs) 34 | print("%s's result is %s, time cost: %s" % (func, _result, time.time() - _start)) 35 | return _result 36 | return wrapper 37 | 38 | 39 | def fibonacci_simple(n): 40 | """ 41 | 利用递归调用德方式计算,f_{n} = f_{n-1} + f_{n-2} 42 | :param n: n>0 43 | :return 44 | """ 45 | if n <= 0: 46 | return 0 47 | elif n == 1: 48 | return 1 49 | return fibonacci_simple(n - 1) + fibonacci_simple(n - 2) 50 | 51 | 52 | @timer 53 | def fibonacci_list(n): 54 | """ 55 | 用列表缓存中间的计算结果 56 | :param n: n>0 57 | :return 58 | """ 59 | lst = [0, 1, 1] 60 | while len(lst) < n + 1: 61 | ln = len(lst) 62 | lst.append(lst[ln - 1] + lst[ln - 2]) 63 | return lst[0] if n < 0 else lst[n] 64 | 65 | 66 | def fibonacci_formula(n): 67 | """ 68 | 利用公式推导,纯数学计算 69 | f_{2m + 1} = f_{m} ** 2 + f_{m+1} ** 2 70 | f_{2m} = (f_{m-1} + f_{m+1}) * f_{m} = (2 * f_{m + 1} - f_{m}) * f_{m} 71 | 奇数情况 n == 2m + 1: 72 | (f_{n}, f_{n+1}) 73 | = (f_{2m+1}, f_{2m+2}) 74 | = (f_{m} ** 2 + f_{m+1} ** 2, (f_{m} + f_{m+2}) * f_{m+1}) 75 | = (f_{m} ** 2 + f_{m+1} ** 2, (2 * f_{m} + f_{m+1}) * f_{m+1}) 76 | 偶数情况 n == 2m: 77 | (f_{n}, f_{n+1}) 78 | = (f_{2m}, f_{2m+1}) 79 | = ((2 * f_{m+1} - f_{m}) * f_{m}, f_{m} ** 2 + f_{m+1} ** 2) 80 | 比如,n = 1, m = 0: 81 | fibonacci_formula(1) 82 | = (f_{0} ** 2 + f_{1} ** 2, (2 * f_{0} + f_{1}) * f_{1}) 83 | = (0 + 1, (0 + 1) * 1) 84 | = (1, 1) 85 | 比如,n = 2, m = 1: 86 | fibonacci_formula(2) 87 | = ((2 * f_{2} - f_{1}) * f_{1}, f_{1} ** 2 + f_{2} ** 2) 88 | = ((2 * 1 - 1)* 1, 1 + 1) 89 | = (1, 2) 90 | :param n: n>0 91 | :return (f_{n}, f_{n+1}) 92 | """ 93 | if n <= 0: 94 | return (0, 1) 95 | # m = n / 2, fibonacci_formula(m) get (f_{m}, f_{m+1}) 96 | f_m, f_m1 = fibonacci_formula(n / 2) 97 | if n % 2: 98 | return f_m ** 2 + f_m1 ** 2, (2 * f_m + f_m1) * f_m1 99 | return (2 * f_m1 - f_m) * f_m, f_m ** 2 + f_m1 ** 2 100 | 101 | 102 | if __name__ == '__main__': 103 | signal.signal(signal.SIGALRM, signle_handler) 104 | signal.alarm(10) 105 | # 1. fibonacci_simple 计算第 30 个 Fibonacci 数,用时:0.852499961853 106 | # start = time.time() 107 | # result = fibonacci_simple(30) 108 | # print("%s's result is %s, time cost: %s" % ("fibonacci_simple", result, time.time() - start)) 109 | # 2. fibonacci_list 计算第 200 个 Fibonacci 数,用时:0.000149011611938 110 | # fibonacci_list(200) 111 | # 3. fibonacci_formula 计算第 200 个 Fibonacci 数,0.000126123428345 112 | start = time.time() 113 | result = fibonacci_formula(200) 114 | print("%s's result is %s, time cost: %s" % ("fibonacci_formula", result, time.time() - start)) 115 | -------------------------------------------------------------------------------- /Huffman/Huffman_expected_length.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | using namespace std; 6 | 7 | double wrong_expected_length(vector P) 8 | { 9 | // {0.1, 0.1, 0.2, 0.2, 0.15, 0.15, 0.1} 10 | if (P.size() < 2) 11 | return 0; 12 | sort(P.begin(), P.end(), greater()); 13 | queue F; 14 | double length = 0; 15 | size_t n = P.size(); 16 | for (size_t k = 1; k < n; ++k) 17 | { 18 | double x, y; 19 | x = P.empty() ? F.front() : P.back(); 20 | if (F.empty()) 21 | P.pop_back(); 22 | else if (F.front() < x) 23 | { 24 | x = F.front(); 25 | F.pop(); 26 | } 27 | else 28 | P.pop_back(); 29 | y = P.empty() ? F.front() : P.back(); 30 | if (F.empty()) 31 | P.pop_back(); 32 | else if (F.front() < y) 33 | { 34 | y = F.front(); 35 | F.pop(); 36 | } 37 | else 38 | P.pop_back(); 39 | length += x + y; 40 | F.push(x + y); 41 | } 42 | return length; 43 | } 44 | 45 | double correct_expected_length(vector P) 46 | { 47 | if (P.size() < 2) 48 | return 0; 49 | sort(P.begin(), P.end(), greater()); 50 | queue F; 51 | double length = 0; 52 | size_t n = P.size(); 53 | for (size_t k = 1; k < n; ++k) 54 | { 55 | double x, y; 56 | if (F.empty()) 57 | { 58 | x = P.back(); 59 | P.pop_back(); 60 | } 61 | else if (P.empty()) 62 | { 63 | x = F.front(); 64 | F.pop(); 65 | } 66 | else if (F.front() < P.back()) 67 | { 68 | x = F.front(); 69 | F.pop(); 70 | } 71 | else 72 | { 73 | x = P.back(); 74 | P.pop_back(); 75 | } 76 | if (F.empty()) 77 | { 78 | y = P.back(); 79 | P.pop_back(); 80 | } 81 | else if (P.empty()) 82 | { 83 | y = F.front(); 84 | F.pop(); 85 | } 86 | else if (F.front() < P.back()) 87 | { 88 | y = F.front(); 89 | F.pop(); 90 | } 91 | else 92 | { 93 | y = P.back(); 94 | P.pop_back(); 95 | } 96 | length += x + y; 97 | F.push(x + y); 98 | } 99 | return length; 100 | } 101 | 102 | double another_expected_length(vector P) 103 | { 104 | if (P.size() < 2) 105 | return 0; 106 | sort(P.begin(), P.end(), greater()); 107 | queue F; 108 | double length = 0; 109 | size_t n = P.size(); 110 | for (size_t k = 1; k < n; ++k) 111 | { 112 | double x, y; 113 | if (P.empty()) 114 | { 115 | x = F.front(); 116 | F.pop(); 117 | y = F.front(); 118 | F.pop(); 119 | } 120 | else if (F.empty()) 121 | { 122 | x = P.back(); 123 | P.pop_back(); 124 | y = P.back(); 125 | P.pop_back(); 126 | } 127 | else if (P.back() < F.front()) 128 | { 129 | x = P.back(); 130 | P.pop_back(); 131 | if (P.empty()) 132 | { 133 | y = F.front(); 134 | F.pop(); 135 | } 136 | else if (P.back() < F.front()) 137 | { 138 | y = P.back(); 139 | P.pop_back(); 140 | } 141 | else 142 | { 143 | y = F.front(); 144 | F.pop(); 145 | } 146 | } 147 | else 148 | { 149 | x = F.front(); 150 | F.pop(); 151 | if (F.empty()) 152 | { 153 | y = P.back(); 154 | P.pop_back(); 155 | } 156 | else if (P.back() < F.front()) 157 | { 158 | y = P.back(); 159 | P.pop_back(); 160 | } 161 | else 162 | { 163 | y = F.front(); 164 | F.pop(); 165 | } 166 | } 167 | length += x + y; 168 | F.push(x + y); 169 | } 170 | return length; 171 | } 172 | 173 | const double POSITIVE_INFINITY = numeric_limits::infinity(); 174 | 175 | double expected_length(vector P) 176 | { 177 | size_t n = P.size(); 178 | if (n < 2) 179 | return 0; 180 | P.reserve(n + 2); 181 | P.insert(P.begin(), POSITIVE_INFINITY); 182 | P.push_back(POSITIVE_INFINITY); 183 | sort(P.begin() + 1, P.begin() + 1 + n); 184 | size_t i = 1; 185 | size_t j = 0; 186 | size_t m = 1; 187 | double length = 0; 188 | for (size_t k = 1; k < n; ++k) 189 | { 190 | double d; 191 | if (P[i] < P[j]) 192 | d = P[i++]; 193 | else 194 | d = P[j++]; 195 | if (P[i] < P[j]) 196 | d += P[i++]; 197 | else 198 | d += P[j++]; 199 | length += d; 200 | P[m] = P[m - 1]; 201 | P[m - 1] = d; 202 | ++m; 203 | } 204 | return length; 205 | } 206 | 207 | int main() 208 | { 209 | vector P {0.1, 0.1, 0.2, 0.2, 0.15, 0.15, 0.1}; 210 | cout << wrong_expected_length(P) << endl; 211 | cout << correct_expected_length(P) << endl; 212 | cout << another_expected_length(P) << endl; 213 | cout << expected_length(P) << endl; 214 | return 0; 215 | } 216 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #### 知识星球 "算法时空" 2 | 一起进入我们的星球吧: 3 | 4 | https://t.xiaomiquan.com/QbmUfqF 5 | 6 | Algorithms and Data Structures 7 | 8 | :pencil2: 谢勰 (微博/微信公众号: 算法时空) 9 | 10 | --- 11 | 12 | 想学算法与数据结构又苦于无法坚持?我们来一起学! 13 | 14 | 欢迎加入“算法时空”这个知识的星球!星主是一位热爱文艺的高校副教授,可在微博 @算法时空 看到他的简介,作品有译著《算法设计指南》(The Algorithm Design Manual)和《面向算法设计的数据结构(C++语言版)》。 15 | 16 | 这里既有《算法导论》严谨扎实的理论基础,又有《算法设计指南》风趣幽默的实战技巧,还有《算法》平易近人的教学理念。我们为你提纲挈领总结算法要义,交融科技与人文,展现冷峻代码的缕缕温情。希望普通人也能不断提升算法与数据结构的内力,“每天看懂一点点”! 17 | 18 | 本星球还将不断传送算法学习的全手写视频(iPad Pro同步录制),前期部分课程可在B站“算法时空”预览。根据本人多年的高校教学经验,只有自己认真推导公式和构想算法,才能真正掌握,否则蜻蜓点水毫无效果。所以跟随手写视频来记笔记并且自己写代码是一种很好的学习方法。 19 | 20 | 我们鼓励大家用自己擅长的语言实现算法,并会挑选部分程序进行讲评和重构。 21 | 22 | One more thing,年费是关于星球人数以及万事万物终极答案42并且非线性逐渐增长的一个函数~~~ 23 | 24 | --- 25 | 26 | $$\Omega(n\log{n})$$ 27 | 28 | -------------------------------------------------------------------------------- /activity_selector/activity_selector.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | using namespace std; 6 | 7 | // 活动类型为pair. 8 | using activity = pair; 9 | 10 | void activity_selector(const vector& S, vector& A) 11 | { 12 | if (S.size() == 0) 13 | return; 14 | A.clear(); 15 | // 使用贪心算法求解, 当前相容集合中的结束时间为A.back().second. 16 | A.push_back(S[0]); 17 | for (size_t i = 1; i < S.size(); ++i) 18 | if (S[i].first >= A.back().second) 19 | A.push_back(S[i]); 20 | } 21 | 22 | int main() 23 | { 24 | vector S {{6, 10}, {8, 11}, {8, 12}, {3, 9}, {5, 9}, {2, 14}, 25 | {12, 16}, {1, 4}, {3, 5}, {0, 6}, {5, 7}}; 26 | vector A; 27 | // 按照结束时间排序. 28 | sort(S.begin(), S.end(), [](activity a, activity b) 29 | { return a.second < b.second; }); 30 | activity_selector(S, A); 31 | for (const auto& x : A) 32 | cout << x.first << ", " << x.second << endl; 33 | return 0; 34 | } 35 | -------------------------------------------------------------------------------- /backtrack/coloring.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | using namespace std; 6 | 7 | using graph = vector>; 8 | 9 | size_t k; 10 | 11 | bool solved; 12 | 13 | void coloring(const graph& G, vector& C, const vector& P, 14 | size_t v = 0) 15 | { 16 | if (v == G.size()) 17 | { 18 | solved = true; 19 | for (size_t i = 0; i < C.size(); ++i) 20 | cout << "顶点" << i << "着色为" << C[i] << endl; 21 | return; 22 | } 23 | vector R(k, true); 24 | for (const auto& x : G[P[v]]) 25 | if (C[x] != k) 26 | R[C[x]] = false; 27 | for (size_t i = 0; i < R.size(); ++i) 28 | if (R[i]) 29 | { 30 | C[P[v]] = i; 31 | coloring(G, C, P, v + 1); 32 | if (solved) 33 | return; 34 | } 35 | } 36 | 37 | bool two_colorable(const graph& G, vector& C) 38 | { 39 | if (G.size() == 0) 40 | return true; 41 | enum color : size_t {red, blue, blank}; 42 | for (auto& x : C) 43 | x = blank; 44 | queue Q; 45 | Q.push(0); 46 | C[0] = red; 47 | while (!Q.empty()) 48 | { 49 | size_t u = Q.front(); 50 | Q.pop(); 51 | for (const auto v : G[u]) 52 | if (C[v] == blank) 53 | { 54 | C[v] = (C[u] == red) ? blue : red; 55 | Q.push(v); 56 | } 57 | else if (C[u] == C[v]) 58 | return false; 59 | } 60 | return true; 61 | } 62 | 63 | int main() 64 | { 65 | graph G {{1, 3, 4, 5}, 66 | {0, 2, 3}, 67 | {1, 3, 4}, 68 | {0, 1, 2, 4}, 69 | {0, 2, 3, 5}, 70 | {0, 4}}; 71 | cin >> k; 72 | vector C(G.size(), k); 73 | vector P(G.size()); 74 | for (size_t i = 0; i < G.size(); ++i) 75 | P[i] = i; 76 | sort(P.begin(), P.end(), [G](size_t a, size_t b) 77 | { return G[a].size() > G[b].size(); }); 78 | solved = false; 79 | coloring(G, C, P); 80 | if (!solved) 81 | cout << "不能以" << k << "种颜色对该图着色!" << endl; 82 | if (!two_colorable(G, C)) 83 | cout << "不能以两种颜色对该图着色!" << endl; 84 | return 0; 85 | } 86 | -------------------------------------------------------------------------------- /backtrack/eight_queens.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace std; 5 | 6 | size_t m = 0; 7 | const int n = 8; 8 | void backtrack(vector& X, int k = 0) 9 | { 10 | if (k == n) 11 | { 12 | for (const auto& c : X) 13 | cout << c << ' '; 14 | cout << endl; 15 | ++m; 16 | } 17 | else 18 | for (int i = 0; i < n; ++i) 19 | { 20 | bool safe = true; 21 | for (int j = 0; j < k; ++j) 22 | if (X[j] == i || X[j] - i == j - k || X[j] - i == k - j) 23 | safe = false; 24 | if (safe) 25 | { 26 | X[k] = i; 27 | backtrack(X, k + 1); 28 | } 29 | } 30 | } 31 | 32 | int main() 33 | { 34 | vector Q(n); 35 | backtrack(Q); 36 | cout << "解法共有" << m << "种"<< endl; 37 | return 0; 38 | } 39 | -------------------------------------------------------------------------------- /backtrack/maze.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace std; 5 | 6 | // 为方便讲解, 我们不定义point的相等(==)和不相等(!=)运算符. 7 | struct point { 8 | int x; 9 | int y; 10 | }; 11 | 12 | const int m = 5; 13 | const int n = 7; 14 | const char unvisited = '0'; // 未访问过的标记. 15 | const char visited = 'V'; // 访问过的标记. 16 | // 迷宫字符数组, 周围一圈全是墙(以'*'标记). 17 | char maze[m][n] = { 18 | {'*', '*', '*', '*', '*', '*', '*'}, 19 | {'*', '0', '0', '0', '*', '0', '*'}, 20 | {'*', '0', '*', '*', '*', '0', '*'}, 21 | {'*', '0', '0', '0', '0', '0', '*'}, 22 | {'*', '*', '*', '*', '*', '*', '*'} 23 | }; 24 | const int d = 4; // 可行方向总数. 25 | // 以下标取值0, 1, 2, 3标记东南西北与当前位置的偏移量. 26 | const point delta[d] = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}}; 27 | // 起点与终点的坐标. 28 | point source = {1, 1}; 29 | point destination = {3, 5}; 30 | 31 | // 递归形式求解迷宫, 实际上不用递归更好, 避免了参数传递, 但要保存每个点的direction当前值. 32 | bool solved = false; 33 | void backtrack(vector& X) 34 | { 35 | if (X.back().x == destination.x && X.back().y == destination.y) 36 | { 37 | solved = true; 38 | for (const auto& c : X) 39 | cout << c.x << ' ' << c.y << endl; 40 | } 41 | else 42 | for (int direction = 0; direction < d; ++direction) 43 | { 44 | point next = {X.back().x + delta[direction].x, 45 | X.back().y + delta[direction].y}; 46 | if (maze[next.x][next.y] == unvisited) 47 | { 48 | maze[next.x][next.y] = visited; 49 | X.push_back(next); 50 | backtrack(X); 51 | X.pop_back(); 52 | if (solved) 53 | return; 54 | } 55 | } 56 | } 57 | 58 | int main() 59 | { 60 | vector P; 61 | P.reserve(m * n); 62 | P.push_back(source); // 初始点设为入口点. 63 | maze[source.x][source.y] = visited; 64 | backtrack(P); 65 | return 0; 66 | } 67 | -------------------------------------------------------------------------------- /backtrack/n_queens.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | using namespace std; 4 | 5 | size_t m; 6 | const int n = 16; 7 | size_t P[n]; 8 | bool C[n]; 9 | bool S[2 * n + 1]; 10 | bool D[2 * n + 1]; 11 | 12 | void backtrack(int x) 13 | { 14 | if (x == n) 15 | ++m; 16 | else 17 | for (int y = 0; y < n; ++y) 18 | { 19 | if (C[y] || S[x + y] || D[x - y + n]) 20 | continue; 21 | C[y] = true; 22 | S[x + y] = true; 23 | D[x - y + n] = true; 24 | backtrack(x + 1); 25 | C[y] = false; 26 | S[x + y] = false; 27 | D[x - y + n] = false; 28 | } 29 | } 30 | 31 | int main() 32 | { 33 | for (int i = 0; i < n; ++i) 34 | C[i] = false; 35 | for (int i = 0; i < 2 * n + 1; ++i) 36 | S[i] = D[i] = false; 37 | for (int y = 0; y < (n % 2 == 0 ? n / 2 : n / 2 + 1); ++y) 38 | { 39 | m = 0; 40 | C[y] = true; 41 | S[0 + y] = true; 42 | D[0 - y + n] = true; 43 | backtrack(1); 44 | C[y] = false; 45 | S[0 + y] = false; 46 | D[0 - y + n] = false; 47 | P[y] = m; 48 | } 49 | for (int i = 0; i < n / 2; ++i) 50 | P[n - 1 - i] = P[i]; 51 | m = 0; 52 | for (int i = 0; i < n; ++i) 53 | m += P[i]; 54 | cout << n << "皇后问题的解法共有" << m << "种"<< endl; 55 | return 0; 56 | } 57 | -------------------------------------------------------------------------------- /billionsort/billionsort.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | int main() 8 | { 9 | double start_t, end_t; // 计时器. 10 | 11 | // 内存分配计时开始. 12 | start_t = clock(); 13 | std::vector V(1000000000); // 10亿个数, 需要7.5GB内存. 14 | // 内存分配计时结束并输出时间. 15 | end_t = clock(); 16 | std::cout << (end_t - start_t) / (CLOCKS_PER_SEC * 60) 17 | << " minutes" << std::endl; 18 | 19 | // 数据赋值计时开始. 20 | start_t = clock(); 21 | // 利用随机数生成器生成0.0到1.0之间的实数. 22 | std::default_random_engine generator(time(NULL)); 23 | std::uniform_real_distribution distribution(0.0, 1.0); 24 | for (size_t i = 0; i < V.size(); ++i) 25 | V[i] = distribution(generator); 26 | // 数据赋值计时结束并输出时间. 27 | end_t = clock(); 28 | std::cout << (end_t - start_t) / (CLOCKS_PER_SEC * 60) 29 | << " minutes" << std::endl; 30 | 31 | // 排序计时开始. 32 | start_t = clock(); 33 | // 对10亿个随机数排序, 如果用数组时间也没什么太大差别. 34 | std::sort(V.begin(), V.end()); 35 | // 排序计时结束并输出时间. 36 | end_t = clock(); 37 | std::cout << (end_t - start_t) / (CLOCKS_PER_SEC * 60) 38 | << " minutes" << std::endl; 39 | 40 | return 0; 41 | } 42 | -------------------------------------------------------------------------------- /billionsort/counting_sort.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | int main() 8 | { 9 | double start_t, end_t; // 计时器. 10 | 11 | const size_t n = 1000000000; 12 | std::vector V(n); 13 | 14 | // 数据取值范围为[0, n * f), f越小计数排序效果越好, 若内存不够可将f改小一点. 15 | const double f = 2; 16 | const size_t m = n * f; 17 | 18 | // 利用随机数生成器生成[0, m)之间的自然数. 19 | std::default_random_engine generator(time(NULL)); 20 | std::uniform_int_distribution distribution(0, m - 1); 21 | for (size_t i = 0; i < V.size(); ++i) 22 | V[i] = distribution(generator); 23 | 24 | std::vector W(V.begin(), V.end()); 25 | 26 | // 常规sort函数排序. 27 | start_t = clock(); 28 | std::sort(V.begin(), V.end()); 29 | end_t = clock(); 30 | std::cout << (end_t - start_t) / (CLOCKS_PER_SEC * 60) 31 | << " minutes" << std::endl; 32 | 33 | // 计数排序. 34 | start_t = clock(); 35 | std::vector C(m, 0); 36 | for (const auto& z : W) 37 | ++C[z]; 38 | size_t x = 0; 39 | for (size_t i = 0; i < C.size(); ++i) 40 | while (C[i]-- > 0) 41 | W[x++] = i; 42 | end_t = clock(); 43 | std::cout << (end_t - start_t) / (CLOCKS_PER_SEC * 60) 44 | << " minutes" << std::endl; 45 | 46 | return 0; 47 | } 48 | -------------------------------------------------------------------------------- /billionsort/counting_sort_example.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | using namespace std; 6 | 7 | int main() 8 | { 9 | // 向量W之中的元素取自[l, r)区间, 且r - l不是很大. 10 | vector W {-4, 5, 6, 6, 6, 1}; 11 | auto p = minmax_element(W.begin(), W.end()); 12 | int l = *p.first; 13 | int r = *p.second + 1; 14 | size_t m = r - l; 15 | // 用于统计W中各种元素出现次数的向量C. 16 | vector C(m, 0); 17 | // 基于偏移和下标快速统计W中每个元素x的出现次数, 并存于C[z - l]中. 18 | for (const auto& z : W) 19 | ++C[z - l]; 20 | // 根据元素i出现的次数C[i]将其逐个放回W中. 21 | // 注意i从小到大增长, 可保证W有序. 22 | size_t x = 0; 23 | for (size_t i = 0; i < C.size(); ++i) 24 | while (C[i]-- > 0) 25 | W[x++] = i + l; 26 | for (const auto& z : W) 27 | cout << z << ' '; 28 | cout << endl; 29 | return 0; 30 | } 31 | -------------------------------------------------------------------------------- /billionsort/insertion_sort.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | template 8 | void insertion_sort(std::vector& V) 9 | { 10 | for (size_t i = 1; i < V.size(); ++i) 11 | { 12 | auto key = V[i]; 13 | auto position = std::lower_bound(V.begin(), V.begin() + i, key); 14 | for (auto iter = V.begin() + i; iter > position; --iter) 15 | *iter = *(iter - 1); 16 | *position = key; 17 | } 18 | } 19 | 20 | int main() 21 | { 22 | double start_t, end_t; // 计时器. 23 | 24 | // 内存分配计时开始. 25 | start_t = clock(); 26 | std::vector V(900000); // 90万个数, 需要6.9MB内存. 27 | // 内存分配计时结束并输出时间. 28 | end_t = clock(); 29 | std::cout << (end_t - start_t) / (CLOCKS_PER_SEC * 60) 30 | << " minutes" << std::endl; 31 | 32 | // 数据赋值计时开始. 33 | start_t = clock(); 34 | // 利用随机数生成器生成0.0到1.0之间的实数. 35 | std::default_random_engine generator(time(NULL)); 36 | std::uniform_real_distribution distribution(0.0, 1.0); 37 | for (size_t i = 0; i < V.size(); ++i) 38 | V[i] = distribution(generator); 39 | // 数据赋值计时结束并输出时间. 40 | end_t = clock(); 41 | std::cout << (end_t - start_t) / (CLOCKS_PER_SEC * 60) 42 | << " minutes" << std::endl; 43 | 44 | // 排序计时开始. 45 | start_t = clock(); 46 | // 插入排序相当慢. 47 | insertion_sort(V); 48 | // 排序计时结束并输出时间. 49 | end_t = clock(); 50 | std::cout << (end_t - start_t) / (CLOCKS_PER_SEC * 60) 51 | << " minutes" << std::endl; 52 | 53 | return 0; 54 | } 55 | -------------------------------------------------------------------------------- /billionsort/parallel_billionsort.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | // 需要支持C++17的execution, 运行环境为MSVC 2017. 9 | 10 | int main() 11 | { 12 | double start_t, end_t; // 计时器. 13 | 14 | // 内存分配计时开始. 15 | start_t = clock(); 16 | std::vector V(1000000000); // 10亿个数, 需要7.5GB内存. 17 | // 内存分配计时结束并输出时间. 18 | end_t = clock(); 19 | std::cout << (end_t - start_t) / (CLOCKS_PER_SEC * 60) 20 | << " minutes" << std::endl; 21 | 22 | // 数据赋值计时开始. 23 | start_t = clock(); 24 | // 利用随机数生成器生成0.0到1.0之间的实数. 25 | std::default_random_engine generator(time(NULL)); 26 | std::uniform_real_distribution distribution(0.0, 1.0); 27 | for (size_t i = 0; i < V.size(); ++i) 28 | V[i] = distribution(generator); 29 | // 数据赋值计时结束并输出时间. 30 | end_t = clock(); 31 | std::cout << (end_t - start_t) / (CLOCKS_PER_SEC * 60) 32 | << " minutes" << std::endl; 33 | 34 | // 排序计时开始. 35 | start_t = clock(); 36 | // 对10亿个随机数以并行算法排序, 如果用数组时间也没什么太大差别. 37 | std::sort(std::execution::par, V.begin(), V.end()); 38 | // 排序计时结束并输出时间. 39 | end_t = clock(); 40 | std::cout << (end_t - start_t) / (CLOCKS_PER_SEC * 60) 41 | << " minutes" << std::endl; 42 | 43 | return 0; 44 | } 45 | -------------------------------------------------------------------------------- /billionsort/parallel_gcc_billionsort.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | // 需要gcc 9以上版本并安装OpenMP. 9 | // 编译指令: g++-9 -std=c++17 -fopenmp -O3 10 | 11 | int main() 12 | { 13 | double start_t, end_t; // 计时器. 14 | 15 | // 内存分配计时开始. 16 | start_t = clock(); 17 | std::vector V(1000000000); // 10亿个数, 至少需要7.5GB内存. 18 | // 内存分配计时结束并输出时间. 19 | end_t = clock(); 20 | std::cout << (end_t - start_t) / (CLOCKS_PER_SEC * 60) 21 | << " minutes" << std::endl; 22 | 23 | // 数据赋值计时开始. 24 | start_t = clock(); 25 | // 利用随机数生成器生成0.0到1.0之间的实数. 26 | std::default_random_engine generator(time(NULL)); 27 | std::uniform_real_distribution distribution(0.0, 1.0); 28 | for (size_t i = 0; i < V.size(); ++i) 29 | V[i] = distribution(generator); 30 | // 数据赋值计时结束并输出时间. 31 | end_t = clock(); 32 | std::cout << (end_t - start_t) / (CLOCKS_PER_SEC * 60) 33 | << " minutes" << std::endl; 34 | 35 | // 排序计时开始, 多核并行计算如果用clock()会累计每个核的计算时间, 所以改用chrono. 36 | const auto p_start = std::chrono::system_clock::now(); 37 | // 对10亿个随机数以并行算法排序. 38 | __gnu_parallel::sort(V.begin(), V.end()); 39 | // 排序计时结束并输出时间. 40 | const auto p_end = std::chrono::system_clock::now(); 41 | std::cout << (std::chrono::duration(p_end - p_start).count() / 60) 42 | << " minutes" << std::endl; 43 | 44 | return 0; 45 | } 46 | -------------------------------------------------------------------------------- /billionsort/pstl_billionsort.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | // 使用Intel C++编译器以及pstl这个并行STL: 10 | // icpc -std=c++17 -qopenmp-simd -tbb -O1 11 | // O2以上会出问题, 主要是向量的size()返回不对, 而capacity()正确. 12 | // -m64 -fextend-arguments=64 13 | 14 | int main() 15 | { 16 | 17 | double start_t, end_t; // 计时器. 18 | 19 | // 内存分配计时开始. 20 | start_t = clock(); 21 | std::vector V(1000000000); // 10亿个数, 需要7.5GB内存. 22 | // 内存分配计时结束并输出时间. 23 | end_t = clock(); 24 | std::cout << (end_t - start_t) / (CLOCKS_PER_SEC * 60) 25 | << " minutes" << std::endl; 26 | 27 | // 数据赋值计时开始. 28 | start_t = clock(); 29 | // 利用随机数生成器生成0.0到1.0之间的实数. 30 | std::default_random_engine generator(time(NULL)); 31 | std::uniform_real_distribution distribution(0.0, 1.0); 32 | for (size_t i = 0; i < V.size(); ++i) 33 | V[i] = distribution(generator); 34 | // 数据赋值计时结束并输出时间. 35 | end_t = clock(); 36 | std::cout << (end_t - start_t) / (CLOCKS_PER_SEC * 60) 37 | << " minutes" << std::endl; 38 | 39 | // 排序计时开始. 40 | const auto p_start = std::chrono::system_clock::now(); 41 | // 对10亿个随机数以并行算法排序, 如果用数组时间也没什么太大差别. 42 | // 由于使用了并行排序, 实际占用内存量会更大且每次执行不同, 但不会超过原有向量两倍也即15GB. 43 | // 在这个场景下使用par比par_unseq略快一点点. 44 | std::sort(std::execution::par, V.begin(), V.end()); 45 | // 排序计时结束并输出时间. 46 | const auto p_end = std::chrono::system_clock::now(); 47 | std::cout << (std::chrono::duration(p_end - p_start).count() / 60) 48 | << " minutes" << std::endl; 49 | 50 | return 0; 51 | } 52 | -------------------------------------------------------------------------------- /billionsort/sort_forward_list.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | int main() 9 | { 10 | double start_t, end_t; // 计时器. 11 | 12 | // 内存分配计时开始. 13 | start_t = clock(); 14 | // 10亿个数, 需要15.1GB内存. 另外, 换成list内存会加倍, 但链表本身没用到这么大空间, 时间会长一点. 15 | std::forward_list L(1000000000); 16 | // 内存分配计时结束并输出时间. 17 | end_t = clock(); 18 | std::cout << (end_t - start_t) / (CLOCKS_PER_SEC * 60) 19 | << " minutes" << std::endl; 20 | 21 | // 数据赋值计时开始. 22 | start_t = clock(); 23 | // 利用随机数生成器生成0.0到1.0之间的实数. 24 | std::default_random_engine generator(time(NULL)); 25 | std::uniform_real_distribution distribution(0.0, 1.0); 26 | for (auto iter = L.begin(); iter != L.end(); ++iter) 27 | *iter = distribution(generator); 28 | // 数据赋值计时结束并输出时间. 29 | end_t = clock(); 30 | std::cout << (end_t - start_t) / (CLOCKS_PER_SEC * 60) 31 | << " minutes" << std::endl; 32 | 33 | // 排序计时开始. 34 | start_t = clock(); 35 | // 对10亿个随机数排序, 如果用数组时间也没什么太大差别. 36 | L.sort(); 37 | // 排序计时结束并输出时间. 38 | end_t = clock(); 39 | std::cout << (end_t - start_t) / (CLOCKS_PER_SEC * 60) 40 | << " minutes" << std::endl; 41 | 42 | return 0; 43 | } 44 | -------------------------------------------------------------------------------- /breadth_first_search/BFS.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | using namespace std; 6 | 7 | using graph = vector>; 8 | 9 | struct vertex { 10 | bool discovered; 11 | size_t level; 12 | size_t parent; 13 | }; 14 | 15 | void BFS(const graph& G, size_t s) 16 | { 17 | if (s > G.size()) 18 | return; 19 | vector A(G.size()); 20 | for (size_t i = 0; i < A.size(); ++i) 21 | A[i] = {false, (size_t) -1, i}; 22 | A[s].discovered = true; 23 | A[s].level = 0; 24 | queue Q; 25 | stack R; 26 | Q.push(s); 27 | while (!Q.empty()) 28 | { 29 | size_t u = Q.front(); 30 | cout << u << ' '; 31 | R.push(u); 32 | for (const auto& v : G[u]) 33 | if (!A[v].discovered) 34 | { 35 | A[v] = {true, A[u].level + 1, u}; 36 | Q.push(v); 37 | } 38 | Q.pop(); 39 | } 40 | cout << endl; 41 | for (auto& x : A) 42 | x.discovered = false; 43 | while (!R.empty()) 44 | { 45 | size_t v = R.top(); 46 | R.pop(); 47 | if (!A[v].discovered) 48 | { 49 | while (A[v].parent != v) 50 | { 51 | cout << v << ' '; 52 | A[v].discovered = true; 53 | v = A[v].parent; 54 | } 55 | cout << v << endl; 56 | A[v].discovered = true; 57 | } 58 | } 59 | } 60 | 61 | int main() 62 | { 63 | graph G {{}, {0, 2}, {0, 4}, {2}, {2, 3}}; 64 | BFS(G, 1); 65 | return 0; 66 | } 67 | -------------------------------------------------------------------------------- /bucket_sort/bucket_sort.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | int main() 8 | { 9 | double start_t, end_t; // 计时器. 10 | 11 | // 内存分配计时开始. 12 | start_t = clock(); 13 | std::vector V(1000000000); // 10亿个数. 14 | // 内存分配计时结束并输出时间. 15 | end_t = clock(); 16 | std::cout << (end_t - start_t) / (CLOCKS_PER_SEC * 60) 17 | << " minutes" << std::endl; 18 | 19 | // 数据赋值计时开始. 20 | start_t = clock(); 21 | // 利用随机数生成器生成0.0到1.0之间的实数. 22 | std::default_random_engine generator(time(NULL)); 23 | std::uniform_real_distribution distribution(0.0, 1.0); 24 | for (size_t i = 0; i < V.size(); ++i) 25 | V[i] = distribution(generator); 26 | // 数据赋值计时结束并输出时间. 27 | end_t = clock(); 28 | std::cout << (end_t - start_t) / (CLOCKS_PER_SEC * 60) 29 | << " minutes" << std::endl; 30 | 31 | // 排序计时开始. 32 | start_t = clock(); 33 | size_t E = 20; // 每个桶中期望元素个数. 34 | std::vector> B(V.size() / E); // 桶. 35 | for (auto& b : B) 36 | b.reserve(E); 37 | // 将V中元素分配到不同的桶中. 38 | for (const auto& x : V) 39 | B[x * B.size()].push_back(x); 40 | for (auto& b : B) 41 | std::sort(b.begin(), b.end()); 42 | size_t i = 0; 43 | for (const auto& b : B) 44 | for (const auto& x : b) 45 | V[i++] = x; 46 | // 排序计时结束并输出时间. 47 | end_t = clock(); 48 | std::cout << (end_t - start_t) / (CLOCKS_PER_SEC * 60) 49 | << " minutes" << std::endl; 50 | 51 | return 0; 52 | } 53 | -------------------------------------------------------------------------------- /bucket_sort/histogram_sort.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | int main() 8 | { 9 | double start_t, end_t; // 计时器. 10 | 11 | // 内存分配计时开始. 12 | start_t = clock(); 13 | std::vector V(1000000000); // 10亿个数. 14 | // 内存分配计时结束并输出时间. 15 | end_t = clock(); 16 | std::cout << (end_t - start_t) / (CLOCKS_PER_SEC * 60) 17 | << " minutes" << std::endl; 18 | 19 | // 数据赋值计时开始. 20 | start_t = clock(); 21 | // 利用随机数生成器生成0.0到1.0之间的实数. 22 | std::default_random_engine generator(time(NULL)); 23 | std::uniform_real_distribution distribution(0.0, 1.0); 24 | for (size_t i = 0; i < V.size(); ++i) 25 | V[i] = distribution(generator); 26 | // 数据赋值计时结束并输出时间. 27 | end_t = clock(); 28 | std::cout << (end_t - start_t) / (CLOCKS_PER_SEC * 60) 29 | << " minutes" << std::endl; 30 | 31 | // 排序计时开始. 32 | start_t = clock(); 33 | size_t E = 10; // 每个桶中期望元素个数. 34 | std::vector C(V.size() / E + 1, 0); // 使用计数方法. 35 | std::vector B(V.size()); // 另一种形式的桶. 36 | // 对每个桶的元素进行计数. 37 | for (const auto& x : V) 38 | ++C[x * (V.size() / E) + 1]; 39 | // 指示每个桶的起始位置. 40 | for (size_t i = 1; i < C.size(); ++i) 41 | C[i] += C[i - 1]; 42 | // 将V中元素分配到不同的桶中. 43 | for (const auto& x : V) 44 | B[C[x * (V.size() / E)]++] = x; 45 | // 对不同的桶进行排序, 区间为[left, right). 46 | auto left = B.begin(); 47 | for (size_t i = 1; i < C.size(); ++i) 48 | { 49 | auto right = B.begin() + C[i - 1]; 50 | std::sort(left, right); 51 | left = right; 52 | } 53 | // 利用交换将数据放回原向量中. 54 | V.swap(B); 55 | // 排序计时结束并输出时间. 56 | end_t = clock(); 57 | std::cout << (end_t - start_t) / (CLOCKS_PER_SEC * 60) 58 | << " minutes" << std::endl; 59 | 60 | return 0; 61 | } 62 | -------------------------------------------------------------------------------- /combinatorics/permutation.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | using namespace std; 7 | 8 | template 9 | void check(const vector& S) 10 | { 11 | // 可以加入最终解的测试, 这里为了方便仅输出整个向量. 12 | for (const auto& x : S) 13 | cout << x << ' '; 14 | cout << endl; 15 | } 16 | 17 | template 18 | void permute(vector& S, size_t k = 0) 19 | { 20 | if (k == S.size()) 21 | { 22 | check(S); 23 | return; 24 | } 25 | for (size_t i = k; i < S.size(); ++i) 26 | { 27 | swap(S[k], S[i]); 28 | permute(S, k + 1); 29 | swap(S[i], S[k]); 30 | } 31 | } 32 | 33 | int main() 34 | { 35 | vector V {"do", "care", "book", "apple"}; 36 | 37 | cout << "递归生成所有排列:" << endl; 38 | permute(V); 39 | 40 | cout << "从逆字典序开始生成所有排列:" << endl; 41 | do { 42 | check(V); 43 | } while ( prev_permutation(V.begin(), V.end()) ); 44 | next_permutation(V.begin(), V.end()); 45 | cout << "从字典序开始生成所有排列:" << endl; 46 | do { 47 | check(V); 48 | } while ( next_permutation(V.begin(), V.end()) ); 49 | 50 | return 0; 51 | } 52 | -------------------------------------------------------------------------------- /combinatorics/subset.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | using namespace std; 7 | 8 | template 9 | void check(const vector& S, const vector& AUX) 10 | { 11 | // 可以加入最终解的测试, 这里为了方便仅按照取值情况输出向量的当前子集. 12 | cout << "{ "; 13 | for (size_t i = 0; i < AUX.size(); ++i) 14 | if (AUX[i]) 15 | cout << S[i] << ' '; 16 | cout << "}" << endl; 17 | } 18 | 19 | // 假设向量S中无重复元素. 20 | template 21 | void subset(vector& S, vector& AUX, size_t k = 0) 22 | { 23 | if (k == S.size()) 24 | { 25 | check(S, AUX); 26 | return; 27 | } 28 | AUX[k] = false; 29 | subset(S, AUX, k + 1); 30 | AUX[k] = true; 31 | subset(S, AUX, k + 1); 32 | } 33 | 34 | int main() 35 | { 36 | vector V {"do", "care", "book", "apple"}; 37 | vector AUX(V.size()); 38 | cout << "递归生成所有子集:" << endl; 39 | subset(V, AUX); 40 | cout << "迭代生成所有子集:" << endl; 41 | size_t m = pow(2, V.size()); 42 | vector> P(m); 43 | for (size_t i = 0; i < V.size(); ++i) 44 | { 45 | size_t half = m / 2; 46 | for (size_t j = 0; j < P.size(); ++j) 47 | if (j % m >= half) 48 | P[j].push_back(i); 49 | m = half; 50 | } 51 | for (const auto& S : P) 52 | { 53 | cout << "{ "; 54 | for (const auto& x : S) 55 | cout << V[x] << ' '; 56 | cout << " }" << endl; 57 | } 58 | cout << "以二进制生成所有子集:" << endl; 59 | m = pow(2, V.size()); 60 | for (size_t i = 0; i < AUX.size(); ++i) 61 | AUX[i] = false; 62 | for (size_t i = 0; i < m; ++i) 63 | { 64 | check(V, AUX); 65 | for (size_t j = 0; j < AUX.size(); ++j) 66 | { 67 | if (!AUX[j]) 68 | { 69 | AUX[j] = true; 70 | break; 71 | } 72 | AUX[j] = false; 73 | } 74 | } 75 | return 0; 76 | } 77 | -------------------------------------------------------------------------------- /cut_rod/cut_rod.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace std; 5 | 6 | int recursive_cr(const vector& p, size_t n) 7 | { 8 | int opt = p[0]; 9 | for (size_t i = 1; i <= n; ++i) 10 | { 11 | int value = p[i] + recursive_cr(p, n - i); 12 | if (value > opt) 13 | opt = value; 14 | } 15 | return opt; 16 | } 17 | 18 | int memoized_cr_aux(const vector& p, vector& r, size_t k) 19 | { 20 | if (r[k] >= 0) 21 | return r[k]; 22 | int opt = 0; 23 | for (size_t i = 1; i <= k; ++i) 24 | { 25 | int value = p[i] + memoized_cr_aux(p, r, k - i); 26 | if (value > opt) 27 | opt = value; 28 | } 29 | r[k] = opt; 30 | return opt; 31 | } 32 | 33 | int memoized_cr(const vector& p, vector& r, size_t n) 34 | { 35 | for (size_t i = 0; i < r.size(); ++i) 36 | r[i] = -1; 37 | return memoized_cr_aux(p, r, n); 38 | } 39 | 40 | void bottom_up_cr(const vector& p, vector& r) 41 | { 42 | r[0] = 0; 43 | for (size_t j = 1; j < r.size(); ++j) 44 | { 45 | int opt = p[j]; 46 | for (size_t i = 1; i < j; ++i) 47 | if (p[i] + r[j - i] > opt) 48 | opt = p[i] + r[j - i]; 49 | r[j] = opt; 50 | } 51 | } 52 | 53 | void fast_bottom_up_cr(const vector& p, vector& r) 54 | { 55 | r[0] = 0; 56 | for (size_t j = 1; j < r.size(); ++j) 57 | r[j] = p[j]; 58 | for (size_t j = 1; j < r.size(); ++j) 59 | for (size_t i = 1; i < j / 2 + 1; ++i) 60 | if (r[i] + r[j - i] > r[j]) 61 | r[j] = r[i] + r[j - i]; 62 | } 63 | 64 | void extended_bottom_up_cr(const vector& p, vector& r, 65 | vector& s) 66 | { 67 | for (size_t i = 0; i < s.size(); ++i) 68 | s[i] = i; 69 | r[0] = 0; 70 | for (size_t j = 1; j < r.size(); ++j) 71 | { 72 | int opt = p[j]; 73 | for (size_t i = 1; i < j; ++i) 74 | if (p[i] + r[j - i] > opt) 75 | { 76 | opt = p[i] + r[j - i]; 77 | s[j] = i; 78 | } 79 | r[j] = opt; 80 | } 81 | } 82 | 83 | int main() 84 | { 85 | // 价格向量, 注意首项为0, 所有元素必须非负. 86 | vector p {0, 1, 5, 8, 9, 10, 17, 17, 20, 24, 30}; 87 | // 收入向量. 88 | vector r(p.size()); 89 | // 解向量. 90 | vector s(p.size()); 91 | 92 | // 递归方法计算结果并打印. 93 | cout << recursive_cr(p, p.size() - 1) << endl; 94 | // 备忘录方法计算结果和完整的收入向量并打印. 95 | cout << memoized_cr(p, r, p.size() - 1) << endl; 96 | for (size_t i = 0; i < r.size(); ++i) 97 | cout << r[i] << ' '; 98 | cout << endl; 99 | 100 | // 自底向上方法计算完整的收入向量并打印. 101 | bottom_up_cr(p, r); 102 | for (size_t i = 0; i < r.size(); ++i) 103 | cout << r[i] << ' '; 104 | cout << endl; 105 | 106 | // 使用较快的方案, 自底向上方法计算完整的收入向量并打印. 107 | fast_bottom_up_cr(p, r); 108 | for (size_t i = 0; i < r.size(); ++i) 109 | cout << r[i] << ' '; 110 | cout << endl; 111 | 112 | // 自底向上方法计算完整的解向量并打印. 113 | extended_bottom_up_cr(p, r, s); 114 | for (size_t i = 0; i < s.size(); ++i) 115 | { 116 | size_t j = i; 117 | while (j > 0) 118 | { 119 | cout << s[j] << ' '; 120 | j -= static_cast(s[j]); 121 | } 122 | cout << endl; 123 | } 124 | 125 | return 0; 126 | } 127 | -------------------------------------------------------------------------------- /depth_first_search/DFS.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | using namespace std; 6 | 7 | using graph = vector>; 8 | 9 | enum color { white, gray, black }; 10 | 11 | struct vertex { 12 | color status; 13 | pair time; 14 | size_t parent; 15 | }; 16 | 17 | void depth_first_search(const graph& G, vector& A, 18 | size_t u, size_t& counter) 19 | { 20 | A[u].time.first = counter++; 21 | A[u].status = gray; 22 | for (auto& v : G[u]) 23 | if (A[v].status == white) 24 | { 25 | A[v].parent = u; 26 | depth_first_search(G, A, v, counter); 27 | } 28 | A[u].time.second = counter++; 29 | A[u].status = black; 30 | } 31 | 32 | void DFS(const graph& G, vector& A) 33 | { 34 | size_t counter = 0; 35 | for (size_t i = 0; i < A.size(); ++i) 36 | A[i] = {white, {}, i}; 37 | for (size_t u = 0; u < G.size(); ++u) 38 | if (A[u].status == white) 39 | depth_first_search(G, A, u, counter); 40 | } 41 | 42 | int main() 43 | { 44 | graph G {{1, 3}, {2}, {3}, {1}, {2, 5}, {5}}; 45 | vector A(G.size()); 46 | DFS(G, A); 47 | for (const auto& x : A) 48 | cout << x.time.first << ' ' << x.time.second << endl; 49 | return 0; 50 | } 51 | -------------------------------------------------------------------------------- /graph_diameter/sparse_graph_diameter.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | using namespace std; 6 | 7 | using graph = vector>; 8 | 9 | size_t find_longest(const graph& G, size_t s) 10 | { 11 | vector A(G.size(), (size_t) -1); 12 | A[s] = 0; 13 | size_t level = 0; 14 | queue Q; 15 | Q.push(s); 16 | while (!Q.empty()) 17 | { 18 | size_t u = Q.front(); 19 | for (const auto& v : G[u]) 20 | if (A[v] == (size_t) -1) 21 | { 22 | A[v] = A[u] + 1; 23 | level = A[v]; 24 | Q.push(v); 25 | } 26 | Q.pop(); 27 | } 28 | return level; 29 | } 30 | 31 | int main() 32 | { 33 | graph G {{1, 3}, {0, 2}, {1, 3}, {0, 2}}; 34 | size_t diameter = 0; 35 | for (size_t s = 0; s < G.size(); ++s) 36 | { 37 | size_t level = find_longest(G, s); 38 | if (level > diameter) 39 | diameter = level; 40 | } 41 | cout << diameter << endl; 42 | return 0; 43 | } 44 | -------------------------------------------------------------------------------- /graph_diameter/tree_diameter.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | using namespace std; 6 | 7 | using graph = vector>; 8 | 9 | void find_last(const graph& G, size_t s, size_t& last, size_t& level) 10 | { 11 | if (s > G.size()) 12 | return; 13 | vector A(G.size(), (size_t) -1); 14 | A[s] = 0; 15 | queue Q; 16 | Q.push(s); 17 | while (!Q.empty()) 18 | { 19 | last = Q.front(); 20 | for (const auto& v : G[last]) 21 | if (A[v] == (size_t) -1) 22 | { 23 | A[v] = A[last] + 1; 24 | Q.push(v); 25 | } 26 | Q.pop(); 27 | } 28 | level = A[last]; 29 | } 30 | 31 | int main() 32 | { 33 | graph G {{1, 2, 3, 4}, {0}, {0}, {0}, {0}}; 34 | size_t s = 0; 35 | size_t t; 36 | size_t level; 37 | find_last(G, s, t, level); 38 | find_last(G, t, s, level); 39 | cout << level << endl; 40 | return 0; 41 | } 42 | -------------------------------------------------------------------------------- /knapsack/fractional_knapsack_greedy_algorithm.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | using namespace std; 6 | 7 | struct gold { 8 | int id; 9 | double value; 10 | double weight; 11 | }; 12 | 13 | int main() 14 | { 15 | vector G {{3, 120, 30}, {1, 60, 10}, {2, 100, 20}}; 16 | // 按照价值/重量从大到小排序. 17 | sort(G.begin(), G.end(), [](const gold& x, const gold& y) 18 | { return x.value / x.weight > y.value / y.weight; }); 19 | // 背包重量上限W. 20 | double W = 50; 21 | size_t m; 22 | double total = 0; 23 | for (m = 0; m < G.size() && total < W; ++m) 24 | total += G[m].weight; 25 | vector knapsack; 26 | if (m > 0) 27 | { 28 | knapsack.resize(m); 29 | for (size_t i = 0; i < m; ++i) 30 | knapsack[i] = G[i]; 31 | // 末尾修正比例. 32 | double f = 1 - (total - W) / knapsack.back().weight; 33 | knapsack.back().weight *= f; 34 | knapsack.back().value *= f; 35 | } 36 | for (const auto& x : knapsack) 37 | cout << x.id << " " << x.value << " " << x.weight << endl; 38 | return 0; 39 | } 40 | -------------------------------------------------------------------------------- /lcs/lcs.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | using namespace std; 6 | 7 | size_t LCS(const string& X, const string& Y) 8 | { 9 | if (X.size() == 0 || Y.size() == 0) 10 | return 0; 11 | vector> l; 12 | l.resize(X.size() + 1); 13 | for (size_t i = 0; i < l.size(); ++i) 14 | l[i].resize(Y.size() + 1, 0); 15 | // 动态规划求解. 16 | for (size_t i = 1; i <= X.size(); ++i) 17 | for (size_t j = 1; j <= Y.size(); ++j) 18 | if (X[i - 1] == Y[j - 1]) 19 | l[i][j] = l[i - 1][j - 1] + 1; 20 | else if (l[i][j - 1] < l[i - 1][j]) 21 | l[i][j] = l[i - 1][j]; 22 | else 23 | l[i][j] = l[i][j - 1]; 24 | size_t i = X.size() + 1; 25 | size_t j = Y.size() + 1; 26 | string Z; 27 | while (i > 0 && j > 0) 28 | if (X[i - 1] == Y[j - 1]) 29 | { 30 | Z.push_back(X[i - 1]); 31 | --i; 32 | --j; 33 | } 34 | else if (l[i][j - 1] < l[i - 1][j]) 35 | --i; 36 | else 37 | --j; 38 | // 逆向打印Z. 39 | for (auto iter = Z.crbegin(); iter != Z.crend(); ++iter) 40 | cout << *iter; 41 | cout << endl; 42 | return l.back().back(); 43 | } 44 | 45 | int main() 46 | { 47 | cout << LCS("Algorithm", "AlphaGo") << endl; 48 | cout << LCS("BDCABA", "ABCBDAB") << endl; 49 | return 0; 50 | } 51 | -------------------------------------------------------------------------------- /matrix_chain_multiplication/matrix_chain_multiplication.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace std; 5 | 6 | void print_optimal_solution(const vector>& s, size_t i, size_t j) 7 | { 8 | if (i == j) 9 | cout << "A" << i; 10 | else 11 | { 12 | cout << "("; 13 | print_optimal_solution(s, i, s[i][j]); 14 | print_optimal_solution(s, s[i][j] + 1, j); 15 | cout << ")"; 16 | } 17 | } 18 | 19 | int main() 20 | { 21 | // 矩阵尺寸数组. 22 | vector p {30, 35, 15, 5, 10, 20, 25}; 23 | if (p.size() <= 1) 24 | return 0; 25 | 26 | // 共有n个矩阵. 27 | size_t n = p.size() - 1; 28 | // 二维向量, 但是下标0位置不用. 29 | vector> m(n + 1); // 矩阵最优乘法次数. 30 | vector> s(n + 1); // 最优划分位置. 31 | // 二维向量每维的尺寸指定为n + 1并赋初值. 32 | for (size_t i = 1; i <= n; ++i) 33 | { 34 | m[i].resize(n + 1, 0); 35 | s[i].resize(n + 1, i); 36 | } 37 | 38 | //动态规划求解. 39 | for (size_t d = 1; d < n; ++d) 40 | for (size_t i = 1; i + d <= n; ++i) 41 | { 42 | m[i][i + d] = m[i][i] + m[i + 1][i + d] + p[i - 1] * p[i] * p[i + d]; 43 | // s[i][i + d]已经赋过初值i. 44 | for (size_t k = i + 1; k < i + d; ++k) 45 | { 46 | int cost = m[i][k] + m[k + 1][i + d] + p[i - 1] * p[k] * p[i + d]; 47 | if (cost < m[i][i + d]) 48 | { 49 | m[i][i + d] = cost; 50 | s[i][i + d] = k; 51 | } 52 | } 53 | } 54 | cout << "最少乘法次数: " << m[1][n] << endl; 55 | print_optimal_solution(s, 1, n); 56 | cout << endl; 57 | return 0; 58 | } 59 | -------------------------------------------------------------------------------- /matrix_chain_multiplication/memoized_matrix_chain.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace std; 5 | 6 | int look_up(vector>& m, const vector& p, size_t i, size_t j) 7 | { 8 | // 要求i <= j的前提条件. 9 | if (m[i][j] != -1) 10 | return m[i][j]; 11 | if (i == j) 12 | return 0; 13 | else 14 | { 15 | // 此处必然有i < j, 先赋m[i][j]的初值. 16 | m[i][j] = look_up(m, p, i, i) + look_up(m, p, i + 1, j) 17 | + p[i - 1] * p[i] * p[j]; 18 | for (size_t k = i + 1; k < j; ++k) 19 | { 20 | int cost = look_up(m, p, i, k) + look_up(m, p, k + 1, j) 21 | + p[i - 1] * p[k] * p[j]; 22 | if (cost < m[i][j]) 23 | m[i][j] = cost; 24 | } 25 | } 26 | return m[i][j]; 27 | } 28 | 29 | int memoized_matrix_chain(const vector& p) 30 | { 31 | if (p.size() <= 1) 32 | return 0; 33 | size_t n = p.size() - 1; 34 | vector> m(n + 1); 35 | for (size_t i = 1; i < m.size(); ++i) 36 | m[i].resize(n + 1, -1); 37 | return look_up(m, p, 1, n); 38 | } 39 | 40 | int main() 41 | { 42 | // 矩阵尺寸数组. 43 | vector p {30, 35, 15, 5, 10, 20, 25}; 44 | cout << "最少乘法次数: " << memoized_matrix_chain(p) << endl; 45 | return 0; 46 | } 47 | -------------------------------------------------------------------------------- /merge/merge.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | using namespace std; 5 | 6 | // [L, M), [M, R) ==> D 7 | template 8 | void merge(typename vector::const_iterator L, 9 | typename vector::const_iterator M, 10 | typename vector::const_iterator R, 11 | vector& D) 12 | { 13 | D.resize(R - L); 14 | auto iA = L; 15 | auto iB = M; 16 | size_t i = 0; 17 | while (iA != M && iB != R) 18 | if (*iA < *iB) 19 | D[i++] = *iA++; 20 | else 21 | D[i++] = *iB++; 22 | while (iA != M) 23 | D[i++] = *iA++; 24 | while (iB != R) 25 | D[i++] = *iB++; 26 | } 27 | 28 | int main() 29 | { 30 | vector A {3, 2, 1, 4, 5}; 31 | sort(A.begin(), A.begin() + A.size() / 2); 32 | sort(A.begin() + A.size() / 2, A.end()); 33 | vector B; 34 | merge(A.begin(), A.begin() + A.size() / 2, A.end(), B); 35 | for (const auto& x : B) 36 | cout << x << endl; 37 | return 0; 38 | } 39 | -------------------------------------------------------------------------------- /merge/merge.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | # -*- coding:utf8 -*- 3 | 4 | """ 5 | 给出一组数字,对数字进行递归归并排序 6 | """ 7 | 8 | from __future__ import print_function 9 | import random 10 | import time 11 | 12 | 13 | def gen_num_list(ln): 14 | """ 15 | 生成长度为 ln 的随机数数组 16 | """ 17 | return [int(random.random() * 33) for __ in xrange(ln)] 18 | 19 | 20 | def merge_simple(left_lst, right_lst): 21 | ln_left = len(left_lst) 22 | ln_right = len(right_lst) 23 | n_lst = [0, ] * (ln_left + ln_right) 24 | left_idx = right_idx = 0 25 | while (left_idx < ln_left) and (right_idx < ln_right): 26 | if left_lst[left_idx] < right_lst[right_idx]: 27 | n_lst[left_idx + right_idx] = left_lst[left_idx] 28 | left_idx += 1 29 | else: 30 | n_lst[left_idx + right_idx] = right_lst[right_idx] 31 | right_idx += 1 32 | if left_idx < ln_left: 33 | n_lst[left_idx + right_idx:] = left_lst[left_idx:] 34 | else: 35 | n_lst[left_idx + right_idx:] = right_lst[right_idx:] 36 | return n_lst 37 | 38 | def merge_sort_simple(lst): 39 | """ 40 | 使用分治法,归并排序 41 | 10000000,耗时:146.345419884,系统排序:2.12158203125 42 | 1000000,耗时:13.3469378948,系统排序:0.190033912659 43 | 分:从数组长度的中间分为两部分,递归获取排序结果 44 | 治:两个排好序的数组,取最左边元素进行比较放到新的数组里 45 | """ 46 | ln = len(lst) 47 | if ln > 1: 48 | mid = ln / 2 49 | left_lst = merge_sort_simple(lst[:mid]) 50 | right_lst = merge_sort_simple(lst[mid:]) 51 | return merge_simple(left_lst, right_lst) 52 | return lst 53 | 54 | 55 | def merge_dust(lst, left, mid, right): 56 | ln_left = mid - left + 1 57 | ln_right = right - mid 58 | n_lst = [0, ] * (right - left + 1) 59 | left_idx, right_idx = left, mid + 1 60 | while (left_idx <= mid) and (right_idx <= right): 61 | if lst[left_idx] < lst[right_idx]: 62 | n_lst[left_idx - left + right_idx - mid - 1] = lst[left_idx] 63 | left_idx += 1 64 | else: 65 | n_lst[left_idx - left + right_idx - mid - 1] = lst[right_idx] 66 | right_idx += 1 67 | if left_idx <= mid: 68 | n_lst[-(mid+1-left_idx):] = lst[left_idx:mid+1] 69 | else: 70 | n_lst[-(right+1-right_idx):] = lst[right_idx:right+1] 71 | # print(lst, left, mid, right, n_lst, 'before') 72 | lst[left:right+1] = n_lst 73 | # print(lst, left, mid, right, n_lst, 'after') 74 | 75 | 76 | def merge_sort_dust(lst, left, right): 77 | """ 78 | 使用下表方式 79 | 效率没有提升 80 | """ 81 | if left < right: 82 | mid = (left + right) / 2 83 | 84 | merge_sort_dust(lst, left, mid) 85 | merge_sort_dust(lst, mid + 1, right) 86 | merge_dust(lst, left, mid, right) 87 | 88 | 89 | if __name__ == "__main__": 90 | lst = gen_num_list(1000000) 91 | print('start') 92 | start = time.time() 93 | sorted(lst) 94 | print("%s's result, time cost: %s" % ("system_sort", time.time() - start)) 95 | 96 | start = time.time() 97 | merge_sort_simple(lst) 98 | print("%s's result, time cost: %s" % ("merge_sort_simple", time.time() - start)) 99 | 100 | # print(lst) 101 | start = time.time() 102 | merge_sort_dust(lst, 0, len(lst) - 1) 103 | # print(lst) 104 | print("%s's result, time cost: %s" % ("merge_sort_dust", time.time() - start)) 105 | 106 | # lst = [0, 4, 7, 26, 31, 3, 5, 27, 6, 6] 107 | # merge_dust(lst, 5, 7, 9) 108 | # print(lst) 109 | -------------------------------------------------------------------------------- /merge/multiway_merge.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | using namespace std; 6 | 7 | template 8 | void merge(const vector>& V, vector& S) 9 | { 10 | size_t L = 0; 11 | for (size_t i = 0; i < V.size(); ++i) 12 | L += V[i].size(); 13 | S.clear(); 14 | S.reserve(L); 15 | using range = pair::const_iterator, 16 | typename vector::const_iterator>; 17 | auto cmp = [](range a, range b) { return *(a.first) > *(b.first); }; 18 | priority_queue, decltype(cmp)> PQ(cmp); 19 | for (size_t i = 0; i < V.size(); ++i) 20 | if (V[i].begin() != V[i].end()) 21 | PQ.push({V[i].begin(), V[i].end()}); 22 | while (!PQ.empty()) 23 | { 24 | auto R = PQ.top(); 25 | PQ.pop(); 26 | S.push_back(*(R.first)); 27 | R.first = R.first + 1; 28 | if (R.first != R.second) 29 | PQ.push(R); 30 | } 31 | } 32 | 33 | int main() 34 | { 35 | vector> A { {1, 2, 4}, {}, {2, 3, 5}, {3, 4, 6, 8} }; 36 | // A[0], A[1], A[2], A[3] are sorted. 37 | vector B; 38 | 39 | merge(A, B); 40 | for (const auto& x : B) 41 | cout << x << endl; 42 | return 0; 43 | } 44 | -------------------------------------------------------------------------------- /miscellaneous/astrological_signs.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void Virgo() 4 | { 5 | while (true) 6 | std::cout << "作" << std::endl; 7 | } 8 | 9 | void Libra() 10 | { 11 | const int N = 42; 12 | double A[N] = {0}; 13 | for (int R = 0; R < N; R = (R + 1) % N) 14 | std::cout << "似乎A[" << R << "]不如其他元素~" << std::endl; 15 | } 16 | 17 | void Capricorn() 18 | { 19 | int black = 99; 20 | while (black) 21 | black++; 22 | } 23 | 24 | int main() 25 | { 26 | // 请谨慎调用某些函数! 27 | return 0; 28 | } 29 | -------------------------------------------------------------------------------- /sample.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiexiexx/Planet/33402b7536a72c000e2c7aedbaff18093e369708/sample.pdf -------------------------------------------------------------------------------- /shortest_paths/Bellman_Ford_Moore.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | using namespace std; 6 | 7 | const double POSITIVE_INFINITY = numeric_limits::infinity(); 8 | 9 | struct vertex_information { 10 | size_t vertex; 11 | double weight; 12 | }; 13 | 14 | bool Bellman_Ford_Moore(const vector>& WG, size_t s, 15 | vector& path) 16 | { 17 | size_t V = WG.size(); 18 | path.resize(V); 19 | for (auto& x : path) 20 | x = {s, POSITIVE_INFINITY}; 21 | path[s].weight = 0; 22 | vector L(V, 0); 23 | queue Q; 24 | // 初始存入顶点s, 其迭代层次为1. 25 | Q.push(s); 26 | L[s] = 1; 27 | // 逐步迭代求最短路径长度, 至多V - 1次. 28 | while (!Q.empty()) 29 | { 30 | // 队首元素对应顶点为u. 31 | size_t u = Q.front(); 32 | // 迭代层次超过V, 必然存在负环. 33 | if (L[u] > V) 34 | return false; 35 | Q.pop(); 36 | // 必须提前保留这个层次的边界值, 因为有可能存在自边. 减1的目的是为了区别于当前队列的层次. 37 | size_t bound = L[u]--; 38 | for (const auto& x : WG[u]) 39 | if (path[u].weight + x.weight < path[x.vertex].weight) 40 | { 41 | // 为方便表述, 将当前所处理顶点简记为v. 42 | size_t v = x.vertex; 43 | path[v] = {u, path[u].weight + x.weight}; 44 | // 若v不在队列中则入队, 注意迭代层次是bound的下一层次. 45 | if (L[v] < bound) 46 | { 47 | Q.push(v); 48 | L[v] = bound + 1; 49 | } 50 | } 51 | } 52 | return true; 53 | } 54 | 55 | int main() 56 | { 57 | vector> WG { 58 | {{1, 6}, {3, 7}}, 59 | {{2, 5}, {3, 8}, {4, -4}}, 60 | {{1, -2}}, 61 | {{2, -3}, {4, 9}}, 62 | {{0, 2}, {2, 7}} 63 | }; 64 | size_t s = 0; 65 | vector path(WG.size()); 66 | bool has_shortest_path = Bellman_Ford_Moore(WG, s, path); 67 | if (has_shortest_path) 68 | { 69 | for (const auto& x : path) 70 | cout << x.weight << ' '; 71 | cout << endl; 72 | } 73 | return 0; 74 | } 75 | -------------------------------------------------------------------------------- /shortest_paths/Dijkstra_heap.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace std; 5 | 6 | const double POSITIVE_INFINITY = numeric_limits::infinity(); 7 | 8 | struct vertex_information { 9 | size_t vertex; 10 | double weight; 11 | }; 12 | 13 | void sink(vector& H, size_t i, vector& vertex_index) 14 | { 15 | // 暂存H[i]数据, 注意后续操作是和H[0]比较. 16 | H[0] = H[i]; 17 | // 结点的右孩子编号. 18 | size_t rChildNo = 2 * i + 1; 19 | // 右孩子存在(即左右孩子均存在)时的下沉. 20 | while (rChildNo < H.size()) 21 | { 22 | // MIN为较小结点的编号, 注意其初值为0. 23 | size_t MIN = 0; 24 | // 右孩子较小则MIN换为右孩子编号. 25 | if (H[rChildNo].weight < H[MIN].weight) 26 | MIN = rChildNo; 27 | // 左孩子较小则MIN换为左孩子编号. 28 | if (H[rChildNo - 1].weight < H[MIN].weight) 29 | MIN = rChildNo - 1; 30 | // 断言: 不需要交换则下沉结束. 31 | if (MIN == 0) 32 | break; 33 | // 需要交换时先将较小的孩子存于i位置. 34 | H[i] = H[MIN]; 35 | vertex_index[H[i].vertex] = i; 36 | // 继续向下判断. 37 | i = MIN; 38 | rChildNo = 2 * i + 1; 39 | } 40 | // 处理特殊情况: 最后仅存在左孩子且需要继续下沉. 41 | if (rChildNo == H.size() && H[rChildNo - 1].weight < H[0].weight) 42 | { 43 | // 需要交换时先将左孩子存于i位置. 44 | H[i] = H[rChildNo - 1]; 45 | vertex_index[H[i].vertex] = i; 46 | // 获得最终停留位置. 47 | i = rChildNo - 1; 48 | } 49 | // 将初始结点处的数据存于下沉操作最终停留的位置. 50 | H[i] = H[0]; 51 | vertex_index[H[i].vertex] = i; 52 | } 53 | 54 | /* 55 | 其实路径长度不仅在path中存放, 堆里也存放里一份副本. 56 | 但是堆最终状态比较杂乱, 所以这种冗余还是有必要的. 57 | */ 58 | 59 | void swim(vector& H, size_t i, vector& vertex_index) 60 | { 61 | // 在H[0]处设置哨兵以终止循环, 并保存初始结点i处的数据. 62 | H[0] = H[i]; 63 | // 如果H[0]小于i位置的父亲结点值H[i / 2]则不断让i位置上浮, 64 | // 但初始结点处的数据暂不处理, 其值仍存于H[0]. 65 | while (H[0].weight < H[i / 2].weight) 66 | { 67 | H[i] = H[i / 2]; 68 | vertex_index[H[i].vertex] = i; 69 | i = i / 2; 70 | } 71 | // 将初始结点处的数据存于上浮操作最终停留的位置. 72 | H[i] = H[0]; 73 | vertex_index[H[i].vertex] = i; 74 | } 75 | 76 | void Dijkstra(vector> & WG, size_t s, 77 | vector& path) 78 | { 79 | size_t V = WG.size(); 80 | path.resize(V); 81 | for (auto& x : path) 82 | x = {s, POSITIVE_INFINITY}; 83 | path[s].weight = 0; 84 | // 堆H用于寻找最小权重的顶点, 注意首位置用于哨兵, 长度是V + 1. 85 | vector H(V + 1); 86 | vector vertex_index(V); 87 | for (size_t i = 1; i < H.size(); ++i) 88 | { 89 | H[i] = {i - 1, path[i - 1].weight}; 90 | vertex_index[i - 1] = i; 91 | } 92 | swim(H, vertex_index[s], vertex_index); 93 | for (size_t i = 0; i < V; ++i) 94 | { 95 | size_t u = H[1].vertex; 96 | H[1] = H.back(); 97 | sink(H, 1, vertex_index); 98 | H.pop_back(); 99 | for (const auto& neighbor : WG[u]) 100 | if (path[u].weight + neighbor.weight < path[neighbor.vertex].weight) 101 | { 102 | size_t v = neighbor.vertex; 103 | path[v] = {u, path[u].weight + neighbor.weight}; 104 | H[vertex_index[v]].weight = path[v].weight; 105 | swim(H, vertex_index[v], vertex_index); 106 | } 107 | } 108 | } 109 | 110 | int main() 111 | { 112 | vector> WG { 113 | {{1, 10}, {3, 5}}, 114 | {{2, 1}, {3, 2}}, 115 | {{4, 4}}, 116 | {{1, 3}, {2, 9}, {4, 2}}, 117 | {{0, 7}, {2, 4}}, 118 | {} 119 | }; 120 | size_t s = 0; 121 | // 更精简的写法, 将路径中的上一顶点和路径长度存在一起. 122 | vector path(WG.size()); 123 | Dijkstra(WG, s, path); 124 | vector real_path; 125 | real_path.reserve(WG.size()); 126 | for (size_t i = 0; i < path.size(); ++i) 127 | { 128 | cout << s << " to " << i << ", "; 129 | if (path[i].weight == POSITIVE_INFINITY) 130 | { 131 | cout << "Unreachable => Length: POSITIVE_INFINITY." << endl; 132 | continue; 133 | } 134 | real_path.resize(0); 135 | size_t x = i; 136 | while (x != s) 137 | { 138 | real_path.push_back(x); 139 | x = path[x].vertex; 140 | } 141 | if (real_path.size() != 0) 142 | { 143 | cout << "Path: " << s << ' '; 144 | for (auto riter = real_path.rbegin(); riter != real_path.rend(); ++riter) 145 | cout << *riter << ' '; 146 | } 147 | else 148 | cout << "Empty path "; 149 | cout << "=> Length: " << path[i].weight << '.'; 150 | cout << endl; 151 | } 152 | return 0; 153 | } 154 | -------------------------------------------------------------------------------- /shortest_paths/Dijkstra_priority_queue.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | using namespace std; 6 | 7 | const double POSITIVE_INFINITY = numeric_limits::infinity(); 8 | 9 | // 按照直观习惯, 次序是先写顶点后写权重. 当然直接用pair也可以, 但不够清晰. 10 | struct vertex_information { 11 | size_t vertex; 12 | double weight; 13 | }; 14 | 15 | // 必须定义比较准则才能使用vertex_information集合. 16 | struct cmp_vertex_weight_greater { 17 | bool operator()(const vertex_information& a, 18 | const vertex_information& b) const 19 | { 20 | // 利用pair的字典序, 这样会少写很多判断, 这里以weight为主, 注意pair定义略有不同. 21 | using P = pair; 22 | // 难点在于先要对weight分大于、小于和等于三种情况,等于情况下还得继续处理vertex. 23 | return P(a.weight, a.vertex) > P(b.weight, b.vertex); 24 | // 实际上由于这里可以使用==判断, 而vertex又是唯一的, 所以也可以这样写: 25 | /* 26 | return (a.weight == b.weight ? a.vertex > b.vertex : a.weight > b.weight); 27 | */ 28 | // 上述写法类似于字符串比较大小, 先找到不相等的位置. 29 | // 但是一般情况下如果只有<关系, 还是得分三种情况讨论. 30 | } 31 | }; 32 | 33 | void Dijkstra(vector>& WG, size_t s, 34 | vector& path, vector& length) 35 | { 36 | size_t V = WG.size(); 37 | // 顶点数为V, 我们需要保证path和length都是长为V的向量. 38 | path.resize(V); 39 | length.resize(V); 40 | // 初始化path向量, 其中所有元素初值均为s. 41 | for (auto& x : path) 42 | x = s; 43 | // 初始化length向量. 44 | // 暂时设所有length向量中的元素为正无穷大. 45 | for (auto& x : length) 46 | x = POSITIVE_INFINITY; 47 | // s位置路径长度初值为0, 这样还可让s最先被找到. 48 | length[s] = 0; 49 | // 尚未寻找到的顶点放到优先级队列PQ里. 50 | // 如果使用pair, 可用greater>构建PQ. 51 | priority_queue, 52 | cmp_vertex_weight_greater> PQ; 53 | // 初始化优先级队列. 54 | PQ.push({s, length[s]}); 55 | for (size_t i = 0; i < V; i++) 56 | { 57 | // 去除已处理过的顶点. 58 | while (length[PQ.top().vertex] < PQ.top().weight) 59 | PQ.pop(); 60 | // 每次在未找到的顶点集合中, 寻找以s为起点的路径长度最短的顶点. 61 | // 设本次所找到的顶点为u, 对应路径长度已经存于length[u]所以不必记录. 62 | size_t u = PQ.top().vertex; 63 | PQ.pop(); 64 | // 以u的邻接表为依据更新相关数据. 65 | for (const auto& neighbor : WG[u]) 66 | if (length[u] + neighbor.weight < length[neighbor.vertex]) 67 | { 68 | // 为方便表述, 将当前所处理顶点为neighbor.vertex简记为v. 69 | size_t v = neighbor.vertex; 70 | // 若lenght[u] + w(u, v) < length[v], 71 | // 则更新path[v]和length[v]. 72 | path[v] = u; 73 | length[v] = length[u] + neighbor.weight; 74 | // 放入优先级队列中. 75 | PQ.push({v, length[v]}); 76 | } 77 | } 78 | } 79 | 80 | int main() 81 | { 82 | vector> WG { 83 | {{1, 10}, {3, 5}}, 84 | {{2, 1}, {3, 2}}, 85 | {{4, 4}}, 86 | {{1, 3}, {2, 9}, {4, 2}}, 87 | {{0, 7}, {2, 4}} 88 | }; 89 | size_t s = 0; 90 | vector path(WG.size()); 91 | vector length(WG.size()); 92 | Dijkstra(WG, s, path, length); 93 | for (const auto& x : length) 94 | cout << x << ' '; 95 | cout << endl; 96 | return 0; 97 | } 98 | -------------------------------------------------------------------------------- /shortest_paths/Dijkstra_set.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | using namespace std; 6 | 7 | const double POSITIVE_INFINITY = numeric_limits::infinity(); 8 | 9 | // 按照直观习惯, 次序是先写顶点后写权重. 当然直接用pair也可以, 但不够清晰. 10 | struct vertex_information { 11 | size_t vertex; 12 | double weight; 13 | }; 14 | 15 | // 必须定义比较准则才能使用vertex_information集合. 16 | struct cmp_vertex_weight { 17 | bool operator()(const vertex_information& a, 18 | const vertex_information& b) const 19 | { 20 | // 利用pair的字典序, 这样会少写很多判断, 这里以weight为主, 注意pair定义略有不同. 21 | using P = pair; 22 | // 难点在于先要对weight分大于、小于和等于三种情况,等于情况下还得继续处理vertex. 23 | return P(a.weight, a.vertex) < P(b.weight, b.vertex); 24 | } 25 | }; 26 | 27 | /* 28 | 实际上可以使用vector直接存放路径长度, 将double*指针存入集合, 29 | 再用基于向量首元素的相对偏移了解其顶点编号, 集合里就不用存那么多信息了. 30 | */ 31 | 32 | void Dijkstra(vector>& WG, size_t s, 33 | vector& path, vector& length) 34 | { 35 | size_t V = WG.size(); 36 | // 顶点数为V, 我们需要保证path和length都是长为V的向量. 37 | path.resize(V); 38 | length.resize(V); 39 | // 初始化path向量, 其中所有元素初值均为s. 40 | for (auto& x : path) 41 | x = s; 42 | // 初始化length向量. 43 | // 暂时设所有length向量中的元素为正无穷大. 44 | for (auto& x : length) 45 | x = POSITIVE_INFINITY; 46 | // s位置路径长度初值为0, 这样还可让s最先被找到. 47 | length[s] = 0; 48 | // 尚未寻找到的顶点集合为NS. 49 | set NS; 50 | // 顶点在集合NS中的位置存于vertex_position中. 51 | vector::iterator> vertex_position(V); 52 | for (size_t u = 0; u < V; u++) 53 | vertex_position[u] = NS.insert({u, length[u]}).first; 54 | for (size_t i = 0; i < V; i++) 55 | { 56 | // 每次在未找到的顶点集合中, 寻找以s为起点的路径长度最短的顶点. 57 | // 设本次所找到的顶点为u, 对应路径长度已经存于length[u]所以不必记录. 58 | size_t u = NS.begin()->vertex; 59 | // u已找到, 将其从NS中删除. 60 | NS.erase(NS.begin()); 61 | // 以u的邻接表为依据更新相关数据. 62 | for (const auto& neighbor : WG[u]) 63 | if (length[u] + neighbor.weight < length[neighbor.vertex]) 64 | { 65 | // 为方便表述, 将当前所处理顶点为neighbor.vertex简记为v. 66 | size_t v = neighbor.vertex; 67 | // 若lenght[u] + w(u, v) < length[v], 68 | // 则更新path[v]和length[v]. 69 | path[v] = u; 70 | length[v] = length[u] + neighbor.weight; 71 | // 通过删除和重新插入改变NS中的相应取值并保存位置. 72 | NS.erase(vertex_position[v]); 73 | vertex_position[v] = NS.insert({v, length[v]}).first; 74 | } 75 | } 76 | } 77 | 78 | int main() 79 | { 80 | vector> WG { 81 | {{1, 10}, {3, 5}}, 82 | {{2, 1}, {3, 2}}, 83 | {{4, 4}}, 84 | {{1, 3}, {2, 9}, {4, 2}}, 85 | {{0, 7}, {2, 4}} 86 | }; 87 | size_t s = 0; 88 | vector path(WG.size()); 89 | vector length(WG.size()); 90 | Dijkstra(WG, s, path, length); 91 | for (const auto& x : length) 92 | cout << x << ' '; 93 | cout << endl; 94 | return 0; 95 | } 96 | -------------------------------------------------------------------------------- /shortest_paths/Floyd_Warshall.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace std; 5 | 6 | const double POSITIVE_INFINITY = numeric_limits::infinity(); 7 | 8 | struct vertex_information { 9 | size_t vertex; 10 | double weight; 11 | }; 12 | 13 | void print_all(const vector>& path) 14 | { 15 | for (const auto& x : path) 16 | { 17 | for (const auto& y : x) 18 | cout << y.vertex << ' '; 19 | cout << endl; 20 | } 21 | for (const auto& x : path) 22 | { 23 | for (const auto& y : x) 24 | cout << y.weight << ' '; 25 | cout << endl; 26 | } 27 | } 28 | 29 | void Floyd_Warshall(const vector>& WG, 30 | vector>& path) 31 | { 32 | size_t n = WG.size(); 33 | // 初始化path矩阵数据. 34 | path.resize(n); 35 | for (size_t u = 0; u < n; ++u) 36 | { 37 | path[u].resize(n); 38 | // 条件表达式无法使用{}初始化列表. 39 | for (size_t v = 0; v < n; ++v) 40 | if (WG[u][v] < POSITIVE_INFINITY && u != v) 41 | path[u][v] = {u, WG[u][v]}; 42 | else 43 | path[u][v] = {n, WG[u][v]}; 44 | } 45 | // 依次计算k-最短路径, 每次计算更新path矩阵, 46 | // 也就是根据需要将从u到v的路径后半段变为从k到v的路径. 47 | for (size_t k = 0; k < n; ++k) 48 | for (size_t u = 0; u < n; ++u) 49 | for (size_t v = 0; v < n; ++v) 50 | if (path[u][k].weight + path[k][v].weight < path[u][v].weight) 51 | path[u][v] = {path[k][v].vertex, 52 | path[u][k].weight + path[k][v].weight}; 53 | } 54 | 55 | void print_path(const vector>& path, 56 | size_t u, size_t v, vector& P) 57 | { 58 | if (path[u][v].vertex == path.size()) 59 | return; 60 | P.resize(0); 61 | while (v != u) 62 | { 63 | P.push_back(v); 64 | v = path[u][v].vertex; 65 | } 66 | P.push_back(v); 67 | for (auto riter = P.rbegin(); riter != P.rend(); ++riter) 68 | cout << *riter << ' '; 69 | cout << endl; 70 | } 71 | 72 | int main() 73 | { 74 | vector> WG { 75 | {0, 3, 8, POSITIVE_INFINITY, -4}, 76 | {POSITIVE_INFINITY, 0, POSITIVE_INFINITY, 1, 7}, 77 | {POSITIVE_INFINITY, 4, 0, POSITIVE_INFINITY, POSITIVE_INFINITY}, 78 | {2, POSITIVE_INFINITY, -5, 0, POSITIVE_INFINITY}, 79 | {POSITIVE_INFINITY, POSITIVE_INFINITY, POSITIVE_INFINITY, 6, 0} 80 | }; 81 | vector> path(WG.size()); 82 | Floyd_Warshall(WG, path); 83 | print_all(path); 84 | // 查询路径辅助向量P. 85 | vector P; 86 | P.reserve(WG.size()); 87 | print_path(path, 4, 2, P); 88 | return 0; 89 | } 90 | -------------------------------------------------------------------------------- /sieve/Eratosthenes.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | using namespace std; 6 | 7 | int main() 8 | { 9 | const size_t n = 1000000000; 10 | // 统一赋值更快, 不要一开始区分奇偶分开赋值. 11 | vector P(n + 1, true); 12 | P[0] = P[1] = false; 13 | // P[0]和P[1]后续不作处理, 也可以不赋值. 另外, P[2]为true是筛法起点. 14 | 15 | /* 16 | 这种涉及到取模运算(编译器已有位运算优化)分开赋值效果不好. 17 | for (size_t i = 3; i <= n; ++i) 18 | P[i] = i % 2 == 0 ? false : true; 19 | */ 20 | 21 | size_t s = sqrt(n); 22 | for (size_t i = 2; i <= s; ++i) 23 | if (P[i]) 24 | for (size_t j = i * i; j <= n; j += i) 25 | P[j] = false; 26 | // 有的解法根据P[j]从true到false的判断减去合数个数, 速度并不快. 27 | 28 | int m = 0; 29 | for (size_t i = 2; i <= n; ++i) 30 | if (P[i]) 31 | ++m; 32 | cout << m << endl; 33 | 34 | return 0; 35 | } 36 | -------------------------------------------------------------------------------- /sieve/Pritchard.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace std; 5 | 6 | int main() 7 | { 8 | const size_t n = 1000000000; 9 | // C代表合数判定. 10 | vector C(n + 1, false); 11 | C[0] = C[1] = true; 12 | // 对于int范围以内的素数, size_t全部换用int会快很多, 因为有乘法和取模运算. 13 | vector P(n / 2); 14 | size_t m = 0; 15 | 16 | for (size_t i = 2; i <= n / 2; ++i) 17 | { 18 | // 这种否定的写法有利于分支预测. 19 | if (!C[i]) 20 | P[m++] = i; 21 | /* 22 | 循环判断直接使用P[j] * i <= n有溢出的可能性, 23 | 但是和循环体的中的P[j] * i一致有利于编译器优化, 24 | 改为P[j] <= n / i速度会略慢, 不过会更安全. 25 | */ 26 | for (size_t j = 0; j < m && P[j] * i <= n; ++j) 27 | { 28 | C[P[j] * i] = true; 29 | if (i % P[j] == 0) 30 | break; 31 | } 32 | } 33 | 34 | for (size_t i = n / 2 + 1; i <= n; ++i) 35 | if (!C[i]) 36 | ++m; 37 | cout << m << endl; 38 | return 0; 39 | } 40 | -------------------------------------------------------------------------------- /string_matching/Rabin_Karp.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | using namespace std; 6 | 7 | const uint64_t L = 104729; 8 | const uint16_t A = 256; 9 | 10 | uint64_t Horner(const string& s, size_t m) 11 | { 12 | uint64_t value = 0; 13 | for (size_t i = 0; i < m; ++i) 14 | value = (value * A + (uint8_t)s[i]) % L; 15 | return value; 16 | } 17 | 18 | double initialize(const string& p, const string& t, size_t m, 19 | uint64_t& hp, uint64_t& ht) 20 | { 21 | vector power(m); 22 | auto iter = power.rbegin(); 23 | uint64_t value = 1; 24 | for (auto riter = power.rbegin(); riter != power.rend(); ++riter) 25 | { 26 | *riter = value; 27 | value = (value * A) % L; 28 | } 29 | hp = ht = 0; 30 | for (size_t i = 0; i < m; ++i) 31 | { 32 | hp = (hp + power[i] * (uint8_t)p[i]) % L; 33 | ht = (ht + power[i] * (uint8_t)t[i]) % L; 34 | } 35 | return power.front(); 36 | } 37 | 38 | size_t RK(const string& p, const string& t) 39 | { 40 | size_t m = p.size(); 41 | size_t n = t.size(); 42 | if (n < m || m == 0) 43 | return n; 44 | uint64_t c = 1; 45 | for (size_t i = 1; i < m; ++i) 46 | c = (c * A) % L; 47 | uint64_t hp = Horner(p, m); 48 | uint64_t ht = Horner(t, m); 49 | /* 50 | 下列语句也可使用, 但性能略差. 51 | c = initialize(p, t, m, hp, ht); 52 | */ 53 | for (size_t i = 0; i <= n - m; ++i) 54 | { 55 | if (hp == ht && t.compare(i, i + m, p, 0, m) == 0) 56 | return i; 57 | ht = (ht - (c * (uint8_t)t[i]) % L + L) % L; 58 | ht = (ht * A + (uint8_t)t[i + m]) % L; 59 | } 60 | return n; 61 | } 62 | 63 | int main() 64 | { 65 | cout << RK("Algorithm_", "Data Structures and Algorithms") << endl; 66 | cout << RK("Algorithms", "Data Structures and Algorithms") << endl; 67 | cout << RK("Data", "Data Structures and Algorithms") << endl; 68 | return 0; 69 | } 70 | -------------------------------------------------------------------------------- /strongly_connected_component/SCC.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | using namespace std; 4 | 5 | using graph = vector>; 6 | 7 | enum color { white, gray, black }; 8 | 9 | struct vertex { 10 | color status; 11 | pair time; 12 | size_t parent; 13 | }; 14 | 15 | void depth_first_search(const graph& G, vector& A, vector& F, 16 | size_t u, size_t& counter) 17 | { 18 | A[u].time.first = counter++; 19 | A[u].status = gray; 20 | for (auto& v : G[u]) 21 | if (A[v].status == white) 22 | { 23 | A[v].parent = u; 24 | depth_first_search(G, A, F, v, counter); 25 | } 26 | A[u].time.second = counter++; 27 | A[u].status = black; 28 | F.push_back(u); 29 | } 30 | 31 | int main() 32 | { 33 | graph G {{1}, {2, 3}, {0}, {5}, {3}, {4}}; 34 | vector A(G.size()); 35 | for (size_t i = 0; i < A.size(); ++i) 36 | A[i] = {white, {}, i}; 37 | vector R; 38 | R.reserve(G.size()); 39 | size_t counter = 0; 40 | for (size_t u = 0; u < G.size(); ++u) 41 | if (A[u].status == white) 42 | depth_first_search(G, A, R, u, counter); 43 | graph GT(G.size()); 44 | for (size_t u = 0; u < G.size(); ++u) 45 | for (const auto& v : G[u]) 46 | GT[v].push_back(u); 47 | for (size_t i = 0; i < A.size(); ++i) 48 | A[i] = {white, {}, i}; 49 | vector W; 50 | W.reserve(G.size()); 51 | counter = 0; 52 | for (auto iter = R.rbegin(); iter != R.rend(); ++iter) 53 | if (A[*iter].status == white) 54 | depth_first_search(GT, A, W, *iter, counter); 55 | for (auto& x : A) 56 | x.status = white; 57 | for (const auto& x : W) 58 | if (A[x].status == white) 59 | { 60 | size_t v = x; 61 | while (A[v].parent != v) 62 | { 63 | cout << v << ' '; 64 | A[v].status = black; 65 | v = A[v].parent; 66 | } 67 | cout << v << endl; 68 | A[v].status = black; 69 | } 70 | return 0; 71 | } 72 | -------------------------------------------------------------------------------- /topological_sorting/Kahn.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | using namespace std; 5 | 6 | using graph = vector>; 7 | 8 | void Kahn(const graph& G) 9 | { 10 | vector in_degree(G.size(), 0); 11 | for (size_t u = 0; u < G.size(); ++u) 12 | for (const auto& v : G[u]) 13 | ++in_degree[v]; 14 | queue Q; 15 | for (size_t v = 0; v < in_degree.size(); ++v) 16 | if (in_degree[v] == 0) 17 | Q.push(v); 18 | while (!Q.empty()) 19 | { 20 | size_t u = Q.front(); 21 | Q.pop(); 22 | cout << u << ' '; 23 | for (const auto& v : G[u]) 24 | if (--in_degree[v] == 0) 25 | Q.push(v); 26 | } 27 | } 28 | 29 | int main() 30 | { 31 | graph G {{1, 2}, {}, {4}, {2}, {}, {1}}; 32 | Kahn(G); 33 | return 0; 34 | } 35 | -------------------------------------------------------------------------------- /unique/unique.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | using namespace std; 8 | 9 | void simple_count(vector& V) 10 | { 11 | sort(V.begin(), V.end()); 12 | for (size_t left = 0, right; left < V.size(); left = right) 13 | { 14 | for (right = left + 1; right < V.size(); ++right) 15 | if (V[right] != V[left]) 16 | break; 17 | cout << V[left] << ": " << right - left << endl; 18 | } 19 | } 20 | 21 | void count(vector& V) 22 | { 23 | sort(V.begin(), V.end()); 24 | size_t left, right; 25 | for (left = 0, right = 1; right < V.size(); ++right) 26 | if (V[right] != V[left]) 27 | { 28 | cout << V[left] << ": " << right - left << endl; 29 | left = right; 30 | } 31 | if (V.size() > 0) 32 | cout << V[left] << ": " << right - left << endl; 33 | } 34 | 35 | // 主要基于相等关系判定, 也就是在序关系(<运算符)下的相等. 36 | void unique(vector& V) 37 | { 38 | // 因为必须判断V是否为空, 所以提前做可以节约时间. 39 | if (V.size() == 0) 40 | return; 41 | sort(V.begin(), V.end()); 42 | size_t left = 0; 43 | for (size_t right = left + 1; right < V.size(); ++right) 44 | if (V[right] != V[left]) 45 | V[++left] = V[right]; 46 | // 注意空向量情况不加判断直接执行下面这句会出错. 47 | V.resize(++left); 48 | } 49 | 50 | // 主要是基于等价关系判定, 也就是==运算符. 51 | template 52 | void unique_with_set(vector& V, U& S) 53 | { 54 | S.clear(); 55 | for (const auto& x : V) 56 | S.insert(x); 57 | V.resize(S.size()); 58 | copy(S.begin(), S.end(), V.begin()); 59 | S.clear(); 60 | } 61 | 62 | template 63 | void print_all(const T& S) 64 | { 65 | for (const auto& x : S) 66 | cout << x << ' '; 67 | cout << endl; 68 | } 69 | 70 | int main() 71 | { 72 | cout << "Example A: " << endl; 73 | vector A {7, 2, 3, 2, 5, 1, 1}; 74 | count(A); 75 | unique(A); 76 | print_all(A); 77 | cout << "Example B: " << endl; 78 | vector B {-9}; 79 | count(B); 80 | unique(B); 81 | print_all(B); 82 | cout << "Example C: " << endl; 83 | vector C; 84 | count(C); 85 | unique(C); 86 | print_all(C); 87 | cout << "Example D: " << endl; 88 | vector D {1, 2, 1, 2, 1, 1, 1}; 89 | vector E = D; 90 | set F; 91 | unordered_set G; 92 | unique_with_set(D, F); 93 | print_all(D); 94 | cout << "Example E: " << endl; 95 | unique_with_set(E, G); 96 | print_all(E); 97 | return 0; 98 | } 99 | -------------------------------------------------------------------------------- /unique/unique.js: -------------------------------------------------------------------------------- 1 | /* 2 | * 统计计数的JavaScript版本 3 | */ 4 | 5 | const unique = (list = [], callback) => { 6 | const length = list.sort((a, b) => a - b).length; 7 | 8 | if (length === 0) return; 9 | 10 | let left = right = 0; 11 | 12 | for (; right < length; right++) 13 | if (list[right] !== list[left]) { 14 | const message = `${list[left]}: ${right - left}`; 15 | callback(message); 16 | left = right; 17 | } 18 | 19 | // 最后一步 20 | callback(`${list[left]}:${right - left}`); 21 | } 22 | 23 | const exampleList = [1, 9, 0, 2, 12, 2, 4, 8, 9, 7, 3, 3, 3, -1, -2]; 24 | 25 | unique(exampleList, console.log) 26 | -------------------------------------------------------------------------------- /unique/unique.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | # -*- coding:utf8 -*- 3 | 4 | """ 5 | 给出一组数字,对数字进行排重和计数 6 | 复杂度 = 排序复杂度 + 排重计数复杂度 = O(nlogn) + O(n) = O(nlogn) 7 | """ 8 | 9 | from __future__ import print_function 10 | import random 11 | 12 | 13 | def gen_num_list(ln): 14 | """ 15 | 生成长度为 ln 的随机数数组 16 | """ 17 | return [random.randrange(32) for __ in xrange(ln)] 18 | 19 | 20 | def uniq_and_count(sorted_lst): 21 | """ 22 | 对已排好序的数组元素进行排重和计数输出 23 | 每次打印数组排重后的数字及它出现的次数 24 | """ 25 | # 不处理空数组 26 | if not sorted_lst: 27 | return 28 | 29 | left_value = sorted_lst[0] 30 | num = 1 31 | for right_value in sorted_lst[1:]: 32 | if right_value != left_value: 33 | print(left_value, num) 34 | left_value = right_value 35 | num = 0 36 | num += 1 37 | 38 | print(right_value, num) 39 | 40 | 41 | if __name__ == "__main__": 42 | # 得到随机数组 43 | lst = gen_num_list(20) 44 | 45 | # 用系统方法对数组排序 46 | lst.sort() 47 | print('Sorted list is: %s' % lst) 48 | # 对排序后的数组元素进行排重和计数 49 | uniq_and_count(lst) 50 | --------------------------------------------------------------------------------