├── AimToOffer ├── FindFirstCommonNode.cpp ├── JumpFloor2.cpp ├── MirrorTree.cpp ├── MultiplyArrays.cpp └── SumSolution.cpp ├── Algorithms ├── AlgorithmAndDatastructure │ ├── DynamicProgramming │ │ ├── zero_one_bag.cpp │ │ └── zero_one_bag.h │ ├── EdgeWeightedGraph │ │ ├── DijkstraSP.cpp │ │ ├── DijkstraSP.h │ │ ├── DirectedEdge.cpp │ │ ├── DirectedEdge.h │ │ ├── Edge.cpp │ │ ├── Edge.h │ │ ├── EdgeWeightDiGraph.cpp │ │ ├── EdgeWeightDiGraph.h │ │ ├── EdgeWeightedGraph.cpp │ │ ├── EdgeWeightedGraph.h │ │ ├── KruskalMST.cpp │ │ ├── KruskalMST.h │ │ ├── LazyPrim.cpp │ │ ├── LazyPrim.h │ │ ├── edgeweighteddfsorder.cpp │ │ ├── edgeweighteddfsorder.h │ │ ├── edgeweightedgrapdfs.cpp │ │ └── edgeweightedgrapdfs.h │ ├── Graph │ │ ├── BreadFirstDirectedPaths.cpp │ │ ├── BreadFirstDirectedPaths.h │ │ ├── BreadthFirstPaths.cpp │ │ ├── BreadthFirstPaths.h │ │ ├── ConnectedComponents.cpp │ │ ├── ConnectedComponents.h │ │ ├── Cycle.cpp │ │ ├── Cycle.h │ │ ├── DegreesOfSeparation.cpp │ │ ├── DegreesOfSeparation.h │ │ ├── DepthFirstDirectedPaths.cpp │ │ ├── DepthFirstDirectedPaths.h │ │ ├── DepthFirstOrder.cpp │ │ ├── DepthFirstOrder.h │ │ ├── DepthFirstPaths.cpp │ │ ├── DepthFirstPaths.h │ │ ├── DepthFirstSearch.cpp │ │ ├── DepthFirstSearch.h │ │ ├── Digraph.cpp │ │ ├── Digraph.h │ │ ├── DirectedCycle.cpp │ │ ├── DirectedCycle.h │ │ ├── DirectedDFS.cpp │ │ ├── DirectedDFS.h │ │ ├── Graph.cpp │ │ ├── Graph.h │ │ ├── KosarajuSCC.cpp │ │ ├── KosarajuSCC.h │ │ ├── SymbolDigraph.cpp │ │ ├── SymbolDigraph.h │ │ ├── SymbolGraph.cpp │ │ ├── SymbolGraph.h │ │ ├── Topological.cpp │ │ ├── Topological.h │ │ ├── TwoColorGraph.cpp │ │ └── TwoColorGraph.h │ ├── Priority_Queue │ │ ├── MaxPQ.cpp │ │ ├── MaxPQ.h │ │ ├── MinPQ.cpp │ │ └── MinPQ.h │ ├── Search │ │ ├── binary_searchST.h │ │ ├── sequential_searchST.h │ │ └── test_search.h │ ├── Sort │ │ ├── basesort.h │ │ ├── create_test_array.h │ │ ├── insert_sort.h │ │ ├── merge_sort.cpp │ │ ├── merge_sort.h │ │ ├── quick_short.h │ │ ├── select_sort.h │ │ ├── shell_sort.h │ │ └── sort_compare.h │ ├── Union │ │ ├── QuickUnion.cpp │ │ ├── QuickUnion.h │ │ ├── UnionFind.cpp │ │ ├── UnionFind.h │ │ ├── WeightedQuickUnion.cpp │ │ └── WeightedQuickUnion.h │ └── main.cpp └── couresa_union_find │ ├── main.cpp │ ├── quick_union.cpp │ ├── quick_union.h │ ├── union_find.cpp │ ├── union_find.h │ ├── weighted_quick_union.cpp │ └── weighted_quick_union.h ├── ClassicAlgorithm ├── Dijkstra │ └── main.cpp ├── dynamic_programming │ └── main.cpp └── simulated_annealing │ └── main.cpp ├── HeadFirstDesignPatterns ├── decorator │ ├── beverage.h │ ├── condiment_decorator.h │ ├── dark_roast.h │ ├── espresso.h │ ├── houseblend.h │ ├── mocha.h │ ├── soy.h │ └── whip.h ├── main.cpp └── strategy │ ├── Duck.h │ ├── fly_behavior.h │ ├── fly_no_way.h │ ├── fly_with_wings.h │ ├── mallard_duck.h │ ├── mute_quack.h │ ├── quack.h │ ├── quack_behavior.h │ ├── readme.md │ └── squeak.h ├── IPC ├── OneInstanceFileLock.cpp ├── POSIXSem_r.cpp ├── POSIXSem_w.cpp ├── ProducerConsumer.cpp └── makefile ├── InterviewProblem └── TestSo │ ├── TestSo.c │ ├── TestSo.h │ ├── TestSoMain.c │ ├── makefile │ └── readme.md ├── LICENSE ├── LibFunction ├── HeapSort.cpp ├── ListQuickSort.cpp ├── MyString.h ├── Singleton.h └── main.cpp ├── README.md ├── leetcode ├── 95leetcode生成不同的二叉搜索树.md ├── 98leetcode验证二叉树.md ├── APUE第八章进程控制读书笔记.md ├── Leetcode208 Trie 实现前缀树.md ├── blog │ ├── C++笔记1.md │ ├── cloneBranchFromOrigin.md │ ├── conclusionOfAWeek.md │ ├── learningSocket.md │ ├── leetcode142之寻找环形链表的环的起点.md │ ├── php_notes1.md │ ├── test.md │ ├── title.md │ ├── useHexoInTwoComputer.md │ ├── 学习git两天总结.md │ ├── 拜读UNP前两部分.md │ └── 插入排序.md ├── leetcode142之寻找环形链表的环的起点.md ├── leetcode169之寻找绝对众数.md ├── leetcode53之最大连续子数组和.md ├── leetcode_test │ ├── 137WordBread.h │ ├── 138WordBreak2.h │ ├── SortAlgorithm.h │ ├── TestConst.h │ └── main.cpp ├── leetcode_两整数之和.md ├── leetcode之两数相加.md ├── leetcode之两种方法反转链表.md ├── leetcode之删除倒数第N个节点.md ├── leetcode之删除重复的有序链表的节点.md ├── leetcode之删除链表中给定值的节点.md ├── leetcode之删除链表中重复的元素.md ├── leetcode之删除链表的节点.md ├── leetcode之合并有序链表.md ├── leetcode之回文链表.md ├── leetcode之找出相交链表的交点.md ├── leetcode之环形链表.md ├── source │ ├── 1.两数之和.cpp │ ├── 15.三数之和.cpp │ ├── 2.两链表表示的数相加.cpp │ ├── 20.有效的括号.cpp │ ├── 22.括号生成.cpp │ ├── 3.无重复字符的最长子串.cpp │ └── 32.最长有效括号.cpp └── 真题 │ ├── 编程笔试题之句子反转.md │ ├── 编程笔试题之求水仙花数.md │ └── 编程笔试题值求数列的和.md ├── linux ├── linux常见工具.md └── 软链接硬链接.png └── 基础知识整理 ├── C++内存布局.png ├── C++精选.md ├── POSIX信号量加共享内存实现两个进程简单通信(一收一发).md ├── c++面试题.md ├── 二维数组.png ├── 单实例进程.png ├── 浮点数.png ├── 用文件锁实现程序只能有一个进程实例.md └── 算法思路总结.md /AimToOffer/FindFirstCommonNode.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | struct ListNode { 3 | int val; 4 | struct ListNode *next; 5 | ListNode(int x) : 6 | val(x), next(NULL) { 7 | } 8 | };*/ 9 | class Solution { 10 | public: 11 | ListNode* FindFirstCommonNode( ListNode* pHead1, ListNode* pHead2) { 12 | /* if(pHead1 == NULL || pHead2 == NULL) 13 | return NULL; 14 | while(pHead1 != NULL && pHead2 != NULL){ 15 | if(pHead1->next == pHead2->next) 16 | return pHead1->next; 17 | pHead1 = pHead1->next; 18 | pHead2 = pHead2->next; 19 | } 20 | return NULL; */ 21 | //上述思路严重错误,因为链表不是一样长的,所以从头到尾遍历下去不一定能发现, 22 | //因为两个链表都是同步 23 | if(pHead1 == NULL || pHead2 == NULL) 24 | return NULL; 25 | //一种十分神奇的做法是将两个链表连起来走,每次都是走一步,总会走到共同节点或者都是空的那里。 26 | //关键的代码是当遍历到一个链表的结尾的时候,继续从另个一链表的头部开始遍历,所以要保存好链表头 27 | ListNode * pNode1 = pHead1; 28 | ListNode * pNode2 = pHead2; 29 | //只要两个结点指针不相等就向前遍历一次,知道链表尾,再从另一个链表开始遍历 30 | while(pNode1 != pNode2){ 31 | pNode1 = (pNode1 == NULL) ? (pNode1 = pHead2) : (pNode1 = pNode1->next); 32 | pNode2 = (pNode2 == NULL) ? (pNode2 = pHead1) : (pNode2 = pNode2->next); 33 | } 34 | return pNode1; 35 | } 36 | 37 | ListNode* FindFirstCommonNode2(ListNode* pHead1, ListNode* pHead2){ 38 | //如果有共同结点,之后的结点肯定相同,可以先让一个链表走长度差值的步数,之后共同遍历,有公共结点的 39 | //必然会找到公共结点 40 | if(pHead1 == NULL || pHead2 == NULL) 41 | return NULL; 42 | int len1 = 0, len2 = 0; 43 | ListNode* pNode1 = pHead1; 44 | ListNode* pNode2 = pHead2; 45 | while(pNode1 != NULL){ 46 | len1++; 47 | pNode1 = pNode1->next; 48 | } 49 | while(pNode2 != NULL){ 50 | len2++; 51 | pNode2 = pNode2->next; 52 | } 53 | int dif = len1 - len2; 54 | if(dif >= 0){ 55 | while(dif > 0){ 56 | pHead1 = pHead1->next; 57 | dif--; 58 | } 59 | } 60 | else{ 61 | while(dif < 0){ 62 | pHead2 = pHead2->next; 63 | dif++; 64 | } 65 | } 66 | while(pHead1 != NULL){ 67 | if(pHead1 == pHead2) 68 | return pHead1; 69 | pHead1 = pHead1->next; 70 | pHead2 = pHead2->next; 71 | } 72 | return pHead1; 73 | } 74 | }; -------------------------------------------------------------------------------- /AimToOffer/JumpFloor2.cpp: -------------------------------------------------------------------------------- 1 | //题目描述: 2 | /* 一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法。 3 | 思路1:开始时自己通过推导,一步一步就发现了规律,发f(n) = 2 ^ (n-1) ,此方法依靠推导和直觉推出来 4 | 思路2:排列组合的方法,想象成隔板问题,一个 n - 1 个隔板位置,都可以放或者不放隔板,所以一共有 5 | c(n-1,0)+c(n-1,1)+...+c(n-1,n-1)=2^(n-1) 种方法。 6 | 思路3:台阶都可以有两种状态,被跳上和不被跳上,最后一个台阶只有被跳上的状态,所以最后有 2 的 n - 1 次跳法 7 | 8 | 实现方法:尽量不用递归,考虑不同的输入情况,使用一个循环计算乘方即可。具有启发性的方法是利用移位操作。 9 | 实现方法2:不做优化,强行算出来结果,因为第 n 台阶的跳法跟前面有关,可以用数组依次保存前面计算的结果,一直 10 | 推导下去,需要用到循环嵌套。不推荐这种方法。 */ 11 | class Solution { 12 | public: 13 | int jumpFloorII(int number) { 14 | if(number < 0){ 15 | return -1; 16 | } 17 | return 1 << (number - 1); 18 | } 19 | int jumpFloorII2(int number){ 20 | int *a=new int[number+1]; 21 |         a[0]=1; 22 |         a[1]=1; 23 |         for(int i=2;i<=number;i++){ 24 |             a[i]=0; 25 |             for(int j=i-1;j>=0;j--){ 26 |                 a[i]+=a[j]; 27 |             } 28 |         } 29 |         return a[number]; 30 | } 31 | }; -------------------------------------------------------------------------------- /AimToOffer/MirrorTree.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | struct TreeNode { 3 | int val; 4 | struct TreeNode *left; 5 | struct TreeNode *right; 6 | TreeNode(int x) : 7 | val(x), left(NULL), right(NULL) { 8 | } 9 | };*/ 10 | class Solution { 11 | public: 12 | void Mirror(TreeNode *pRoot) { 13 | //思路是遍历树节点,非叶节点就将左右子节点交换 14 | if(pRoot == NULL){ 15 | return; 16 | } 17 | if(pRoot->left == NULL && pRoot->right == NULL){ 18 | return; 19 | } 20 | TreeNode * tempNode; 21 | tempNode = pRoot->left; 22 | pRoot->left = pRoot->right; 23 | pRoot->right = tempNode; 24 | //这里可以先判断是否有子树再查询 25 | Mirror(pRoot->left); 26 | Mirror(pRoot->right); 27 | } 28 | void Mirror2(TreeNode *root) { 29 | //思路2是遍历树节点,非叶节点就将左右子节点交换。 30 | //使用循环的方法进行遍历,需要用栈来保存数据,每次都将左子节点保存到栈中。取当前节点的左子节点作为当前节点。 31 | //在没有左子节点之后,出栈一个节点,取其右子节点作为当前节点,按照前序遍历的方式遍历。 32 | if(root == NULL) 33 | { 34 | return ; 35 | } 36 | 37 | stack nstack; 38 | TreeNode *node = root; 39 | 40 | while(node != NULL 41 | || nstack.empty( ) != true) 42 | { 43 | while(node != NULL) 44 | { 45 | if(node->left != NULL || node->right != NULL) 46 | { 47 | swap(node->left, node->right); 48 | } 49 | 50 | nstack.push(node); 51 | node = node->left; 52 | } 53 | 54 | if(nstack.empty( ) != true) 55 | { 56 | node = nstack.top( ); 57 | nstack.pop( ); 58 | node = node->right; 59 | } 60 | } 61 | } 62 | }; -------------------------------------------------------------------------------- /AimToOffer/MultiplyArrays.cpp: -------------------------------------------------------------------------------- 1 | class Solution { 2 | public: 3 | //整个代码的思路是将数组乘积分成两个部分,以中间元素为界。 4 | //只需要一次循环就可以分别求出两个乘数,而不必进行循环嵌套 5 | //第二次循环可以用一个中间变量优化 6 | vector multiply(const vector& A) { 7 | vector B(A.size()); 8 | B[0] = 1; 9 | int i = 0; 10 | for(i = 1; i < A.size(); i++){ 11 | B[i] = B[i-1] * A[i-1]; 12 | } 13 | vector C(A.size()); 14 | C[A.size()-1] = 1; 15 | for(i = A.size() - 2; i >= 0; i--){ 16 | C[i] = C[i+1] * A[i+1]; 17 | } 18 | for(i = 0; i < A.size(); i++){ 19 | B[i] = B[i] * C[i]; 20 | } 21 | return B; 22 | } 23 | 24 | vector multiply(const vector& A) { 25 | vector B(A.size()); 26 | B[0] = 1; 27 | int i = 0; 28 | for(i = 1; i < A.size(); i++){ 29 | B[i] = B[i-1] * A[i-1]; 30 | } 31 | int temp = A[A.size()-1]; 32 | for(i = A.size() - 2; i >= 0; i--){ 33 | B[i] = B[i] * temp; 34 | temp *= A[i]; 35 | } 36 | 37 | return B; 38 | } 39 | }; 40 | -------------------------------------------------------------------------------- /AimToOffer/SumSolution.cpp: -------------------------------------------------------------------------------- 1 | class Solution { 2 | public: 3 | int Sum_Solution(int n) { 4 | //短路求值,其实就是递归,不过不用if语句来结束递归 5 | int sum = n; 6 | sum > 0 && (sum += Sum_Solution(n-1)); 7 | return sum; 8 | } 9 | }; -------------------------------------------------------------------------------- /Algorithms/AlgorithmAndDatastructure/DynamicProgramming/zero_one_bag.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by windyear_office on 18-1-20. 3 | // 4 | #include "zero_one_bag.h" 5 | #include 6 | 7 | using namespace std; 8 | 9 | void ZeroOneBag::Dynamic_Zero_One_Bag(double *value_of_goods, int *weight_of_goods, int number_of_goods, int max_weight) { 10 | /* 11 | * 0-1背包问题,给定一个最多能装w重量的背包,每种物品都有一个重量以及价值,求可以带走最大价值的物品。 12 | * 分析:可以采用动态规划的方法,分解为两个子问题,即包含第i个物品的最大价值和不包含第i个物品的最大价值。 13 | * value[i][j]表示一个物品,重量为j的包能够带走的最大价值。从子问题中得到原问题的状态转移方程为: 14 | * value[i][j] = max{value[i-1][j] + value[i-1][j-wj] + vj} 15 | * 可以采取自底向上的方法通过迭代得到value[i][j] 16 | */ 17 | //声明一个存储最大价值的数组,maxvalue[i][j]表示包的最大容量为j,一共i个物品的可以带走的最大价值。 18 | int max_value[number_of_goods+1][max_weight+1]; 19 | for(int i = 0; i <= max_weight; i++){ 20 | max_value[0][i] = 0; 21 | } 22 | for(int i = 0; i <= number_of_goods; i++){ 23 | max_value[i][0] = 0; 24 | } 25 | 26 | for(int i = 1; i <= number_of_goods; i++){ 27 | for(int j = 1; j <= max_weight; j++){ 28 | //如果第i个物品的重量大于最大的重量,证明不能加入,取前i-1个物品的最大值即可 29 | if(weight_of_goods[i] > j){ 30 | max_value[i][j] = max_value[i-1][j]; 31 | continue; 32 | } 33 | //根据状态方程选出最大的价值 34 | if(max_value[i-1][j] > max_value[i-1][j-weight_of_goods[i]] + value_of_goods[i]){ 35 | max_value[i][j] = max_value[i-1][j]; 36 | }else{ 37 | max_value[i][j] = max_value[i-1][j-weight_of_goods[i]] + value_of_goods[i]; 38 | } 39 | } 40 | } 41 | cout << "The max value is: " << max_value[number_of_goods][max_weight] << endl; 42 | 43 | } 44 | -------------------------------------------------------------------------------- /Algorithms/AlgorithmAndDatastructure/DynamicProgramming/zero_one_bag.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by windyear_office on 18-1-20. 3 | // 4 | 5 | #ifndef SORTALGORITHM_ZERO_ONE_BAG_H 6 | #define SORTALGORITHM_ZERO_ONE_BAG_H 7 | class ZeroOneBag{ 8 | public: 9 | static void Dynamic_Zero_One_Bag(double value_of_goods[], int weight_of_goods[], int number_of_goods, int max_weight); 10 | }; 11 | #endif //SORTALGORITHM_ZERO_ONE_BAG_H 12 | -------------------------------------------------------------------------------- /Algorithms/AlgorithmAndDatastructure/EdgeWeightedGraph/DijkstraSP.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by windyear_office on 18-2-7. 3 | // 4 | 5 | #include "DijkstraSP.h" 6 | 7 | DijkstraSP::DijkstraSP(EdgeWeightDiGraph g, int start_point) { 8 | //初始化所有变量 9 | distance_to = new double[g.V()]; 10 | marked = new bool[g.V()]; 11 | for(int i = 0; i < g.V(); i++){ 12 | marked[i] = false; 13 | } 14 | //因为稍后要更新值,所以先初始化空间。 15 | edge_to.resize(g.V()); 16 | //所有值都为最大的距离 17 | for(int i = 0; i < g.V(); i++){ 18 | distance_to[i] = MAX_DISTANCE; 19 | } 20 | //到达自己时距离为0 21 | distance_to[start_point] = 0.0; 22 | marked[start_point] = true; 23 | //每次放松一个点,一共需要放松v-1次,可以使用一个for循环进行松弛 24 | //先放松起点。 25 | Relax(g, start_point); 26 | for(int i = 0; i < g.V() - 1; i++){ 27 | int shortest_index = ToRelax(start_point, g.V()); 28 | marked[shortest_index] = true; 29 | Relax(g, shortest_index); 30 | } 31 | } 32 | 33 | DijkstraSP::~DijkstraSP() { 34 | delete[] distance_to; 35 | } 36 | 37 | double DijkstraSP::DistanceTo(int v) { 38 | return distance_to[v]; 39 | } 40 | //这个函数的目的是选出一个距离开始点最近的一个点来进行松弛 41 | int DijkstraSP::ToRelax(int start_point, int num_of_point) { 42 | int shortest; 43 | //这里先设置一个shortest为第一个还没有被松弛的值 44 | for(int i = 0; i < num_of_point; i++){ 45 | if(marked[i] == true){ 46 | continue; 47 | }else{ 48 | shortest = i; 49 | break; 50 | } 51 | } 52 | for(int i = 0; i < num_of_point; i++){ 53 | if(marked[i] == true){ 54 | continue; 55 | } 56 | if(distance_to[shortest] >= distance_to[i]){ 57 | shortest = i; 58 | } 59 | } 60 | return shortest; 61 | } 62 | 63 | //放松节点的操作是找到节点为出发点的边,然后比较一下加上这条边之后是否满足能够把路径起点到该边终点的距离变短。变短就更新距离。并且添加路径。 64 | void DijkstraSP::Relax(EdgeWeightDiGraph &g, int to_relax) { 65 | for(auto e: g.Adj(to_relax)){ 66 | int to = e.To(); 67 | if(distance_to[to] > distance_to[to_relax] + e.Weight()){ 68 | distance_to[to] = distance_to[to_relax] + e.Weight(); 69 | edge_to[to] = e; 70 | } 71 | } 72 | } 73 | 74 | bool DijkstraSP::HasPathTo(int end_point) { 75 | return distance_to[end_point] < MAX_DISTANCE; 76 | } 77 | 78 | list DijkstraSP::PathTo(int point) { 79 | if(!HasPathTo(point)){ 80 | list no; 81 | return no; 82 | } 83 | list path; 84 | for(DirectedEdge e = edge_to[point]; e.Weight() != 0; e = edge_to[e.From()]){ 85 | path.push_front(e); 86 | } 87 | return path; 88 | } -------------------------------------------------------------------------------- /Algorithms/AlgorithmAndDatastructure/EdgeWeightedGraph/DijkstraSP.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by windyear_office on 18-2-7. 3 | // 4 | 5 | #ifndef SORTALGORITHM_DIJKSTRASP_H 6 | #define SORTALGORITHM_DIJKSTRASP_H 7 | 8 | #include 9 | #include 10 | #include 11 | #include "DirectedEdge.h" 12 | #include "EdgeWeightDiGraph.h" 13 | 14 | using namespace std; 15 | const double MAX_DISTANCE = 9999; 16 | //由于还没有实现优先队列,所以先实现一个用for循环找到最近顶点的点,而且该顶点要还没有被松弛。所以要增加一个数组来记录被松弛了的顶点。 17 | //算法的原理是利用松弛的方法,每次都松弛一个距离顶点最近的点。] 18 | //松弛就是通过被松弛的点可以使其他的一些路径变短。 19 | class DijkstraSP { 20 | private: 21 | double* distance_to;//用来保存每个节点与顶点的距离。 22 | bool* marked; 23 | vector edge_to; 24 | public: 25 | DijkstraSP(EdgeWeightDiGraph g, int start_point); 26 | ~DijkstraSP(); 27 | bool HasPathTo(int end_point); 28 | list PathTo(int point); 29 | double DistanceTo(int v); 30 | int ToRelax(int start_point, int num_of_point); 31 | void Relax(EdgeWeightDiGraph& g, int to_relax); 32 | }; 33 | 34 | 35 | #endif //SORTALGORITHM_DIJKSTRASP_H 36 | -------------------------------------------------------------------------------- /Algorithms/AlgorithmAndDatastructure/EdgeWeightedGraph/DirectedEdge.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by windyear_office on 18-2-7. 3 | // 4 | 5 | #include 6 | #include "DirectedEdge.h" 7 | 8 | DirectedEdge::DirectedEdge(int v, int w, double weight) { 9 | this->v = v; 10 | this->w = w; 11 | this->weight = weight; 12 | } 13 | 14 | int DirectedEdge::From() { 15 | return v; 16 | } 17 | 18 | int DirectedEdge::To() { 19 | return w; 20 | } 21 | 22 | double DirectedEdge::Weight() { 23 | return weight; 24 | } 25 | 26 | string DirectedEdge::ToString() { 27 | stringstream ss; 28 | ss << v << "->" << w << " " << weight; 29 | return ss.str(); 30 | } -------------------------------------------------------------------------------- /Algorithms/AlgorithmAndDatastructure/EdgeWeightedGraph/DirectedEdge.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by windyear_office on 18-2-7. 3 | // 4 | 5 | #ifndef SORTALGORITHM_DIRECTEDEDGE_H 6 | #define SORTALGORITHM_DIRECTEDEDGE_H 7 | 8 | #include 9 | using namespace std; 10 | //跟无向图的区别主要在于边是有方向的,所以可以指定两个函数分别返回边的起点和终点。 11 | class DirectedEdge { 12 | private: 13 | int v; 14 | int w; 15 | double weight; 16 | public: 17 | //默认构造函数在初始化STL容器的时候会用到。 18 | DirectedEdge(int v = 0, int w = 0, double weight = 0); 19 | int From(); 20 | int To(); 21 | string ToString(); 22 | double Weight(); 23 | }; 24 | 25 | #endif //SORTALGORITHM_DIRECTEDEDGE_H 26 | -------------------------------------------------------------------------------- /Algorithms/AlgorithmAndDatastructure/EdgeWeightedGraph/Edge.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by windyear_office on 18-2-3. 3 | // 4 | 5 | #include 6 | #include 7 | #include "Edge.h" 8 | 9 | Edge::Edge(int v, int w, double weight) : v(v), w(w), weight(weight){ 10 | 11 | }; 12 | 13 | int Edge::Either() const { 14 | return v; 15 | } 16 | 17 | int Edge::Other(int vertex) const { 18 | if(vertex == v){ 19 | return w; 20 | }else if(vertex == w){ 21 | return v; 22 | }else{ 23 | cout << "no exist!" << endl; 24 | return 0; 25 | } 26 | } 27 | 28 | double Edge::Weight() const { 29 | return weight; 30 | } 31 | 32 | int Edge::CompareTo(Edge that) const { 33 | if(weight > that.Weight()){ 34 | return 1; 35 | }else if(weight < that.Weight()){ 36 | return -1; 37 | }else{ 38 | return 0; 39 | } 40 | } 41 | 42 | string Edge::ToString() const { 43 | //格式化字符串输出,用stringstream流 44 | stringstream ss; 45 | ss << v << "-" << w << " " << weight; 46 | return ss.str(); 47 | } 48 | 49 | Edge::~Edge() { 50 | 51 | } -------------------------------------------------------------------------------- /Algorithms/AlgorithmAndDatastructure/EdgeWeightedGraph/Edge.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by windyear_office on 18-2-3. 3 | // 4 | 5 | #ifndef SORTALGORITHM_EDGE_H 6 | #define SORTALGORITHM_EDGE_H 7 | 8 | #include 9 | using namespace std; 10 | //这里的边要加入优先队列,所以应该实现一个比较的算法。 11 | class Edge { 12 | private: 13 | int v; 14 | int w;//两个点,不需要改变 15 | double weight;//权重 16 | public: 17 | //如果成员函数不用修改成员,应该声明为const 18 | //如果不适用默认参数的话,后面的vector调用resize函数的时候会不能初始化空间。 19 | Edge(int v = 0, int w = 0, double weight = 0); 20 | ~Edge(); 21 | double Weight() const; 22 | int Either() const; 23 | int Other(int vertex) const; 24 | int CompareTo(Edge that) const; 25 | string ToString() const; 26 | //重载一个比较操作符,进行比较 27 | //这里的小于要返回相反的情况,这样用在优先队列中才会有效。 28 | friend bool operator<(Edge e1, Edge e2){ 29 | if(e1.Weight() < e2.Weight()){ 30 | return false; 31 | } 32 | return true; 33 | } 34 | }; 35 | 36 | 37 | #endif //SORTALGORITHM_EDGE_H 38 | -------------------------------------------------------------------------------- /Algorithms/AlgorithmAndDatastructure/EdgeWeightedGraph/EdgeWeightDiGraph.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by windyear_office on 18-2-7. 3 | // 4 | 5 | #include 6 | #include "EdgeWeightDiGraph.h" 7 | 8 | EdgeWeightDiGraph::EdgeWeightDiGraph(int v): adj(v){ 9 | this->v = v; 10 | this->e = 0; 11 | } 12 | 13 | //从文件中读取图 14 | EdgeWeightDiGraph::EdgeWeightDiGraph(string filename){ 15 | ifstream input(filename); 16 | if(input){ 17 | int num_of_e; 18 | input >> v >> num_of_e; 19 | e = 0; 20 | adj.resize(v); 21 | for(int i = 0; i < num_of_e; i++){ 22 | int v, w; 23 | double weight; 24 | input >> v >> w >> weight; 25 | DirectedEdge e(v, w, weight); 26 | AddEdge(e); 27 | } 28 | } 29 | input.close(); 30 | } 31 | 32 | int EdgeWeightDiGraph::E() const { 33 | return e; 34 | } 35 | 36 | int EdgeWeightDiGraph::V() const { 37 | return v; 38 | } 39 | 40 | vector EdgeWeightDiGraph::Adj(int v) const { 41 | return adj[v]; 42 | } 43 | 44 | void EdgeWeightDiGraph::AddEdge(DirectedEdge edge) { 45 | int v = edge.From(); 46 | //为两个邻接表都增加一个元素,每条边有两个节点 47 | //这里只能添加一条边 48 | adj[v].push_back(edge); 49 | e++; 50 | } 51 | 52 | vector EdgeWeightDiGraph::Edges() { 53 | //返回所有边 54 | //由于每条边会存储两次,所以遍历的时候要排除相同的边。一个简单的方法是根据节点的大小判断。同一条边只会被加入一次 55 | vector all_edges; 56 | for(int i = 0; i < v; i++){ 57 | for(auto v: adj[i]){ 58 | //因为边不会重复,所以直接添加 59 | all_edges.push_back(v); 60 | } 61 | } 62 | return all_edges; 63 | } 64 | 65 | EdgeWeightDiGraph::~EdgeWeightDiGraph() { 66 | 67 | } -------------------------------------------------------------------------------- /Algorithms/AlgorithmAndDatastructure/EdgeWeightedGraph/EdgeWeightDiGraph.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by windyear_office on 18-2-7. 3 | // 4 | 5 | #ifndef SORTALGORITHM_EDGEWEIGHTDIGRAPH_H 6 | #define SORTALGORITHM_EDGEWEIGHTDIGRAPH_H 7 | 8 | 9 | #include 10 | #include "DirectedEdge.h" 11 | 12 | class EdgeWeightDiGraph { 13 | private: 14 | int v; //定点数目 15 | int e; //边数目 16 | vector > adj; //邻接表,这个指针有问题。之前用普通数组,类对象作为值传递的时候会调用复制构造函数,这时候会出问题。浅拷贝。 17 | public: 18 | EdgeWeightDiGraph(int v);//直接构造有v个顶点的图 19 | EdgeWeightDiGraph(string filename);//一个从文件中读取图的结构的构造函数 20 | ~EdgeWeightDiGraph();//删除new出来的内存空间 21 | int V() const;//返回点数 22 | int E() const;//返回边数 23 | void AddEdge(DirectedEdge edge);//添加一条边 24 | vector Adj(int v) const;//返回迭代器,list的迭代器。 25 | vector Edges();//返回所有边 26 | }; 27 | 28 | #endif //SORTALGORITHM_EDGEWEIGHTDIGRAPH_H 29 | -------------------------------------------------------------------------------- /Algorithms/AlgorithmAndDatastructure/EdgeWeightedGraph/EdgeWeightedGraph.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by windyear_office on 18-2-3. 3 | // 4 | 5 | #include 6 | #include "EdgeWeightedGraph.h" 7 | 8 | EdgeWeightedGraph::EdgeWeightedGraph(int v): adj(v){ 9 | this->v = v; 10 | this->e = 0; 11 | } 12 | 13 | //从文件中读取图 14 | EdgeWeightedGraph::EdgeWeightedGraph(string filename){ 15 | ifstream input(filename); 16 | if(input){ 17 | int num_of_e; 18 | input >> v >> num_of_e; 19 | e = 0; 20 | adj.resize(v); 21 | for(int i = 0; i < num_of_e; i++){ 22 | int v, w; 23 | double weight; 24 | input >> v >> w >> weight; 25 | Edge e(v, w, weight); 26 | AddEdge(e); 27 | } 28 | } 29 | input.close(); 30 | } 31 | 32 | int EdgeWeightedGraph::E() const { 33 | return e; 34 | } 35 | 36 | int EdgeWeightedGraph::V() const { 37 | return v; 38 | } 39 | 40 | vector EdgeWeightedGraph::Adj(int v) { 41 | return adj[v]; 42 | } 43 | 44 | void EdgeWeightedGraph::AddEdge(Edge edge) { 45 | int v = edge.Either(); 46 | int w = edge.Other(v); 47 | //为两个邻接表都增加一个元素,每条边有两个节点 48 | adj[v].push_back(edge); 49 | adj[w].push_back(edge); 50 | e++; 51 | } 52 | 53 | vector EdgeWeightedGraph::Edges() { 54 | //返回所有边 55 | //由于每条边会存储两次,所以遍历的时候要排除相同的边。一个简单的方法是根据节点的大小判断。同一条边只会被加入一次 56 | vector all_edges; 57 | for(int i = 0; i < v; i++){ 58 | for(auto v: adj[i]){ 59 | if(v.Other(i) > i){ 60 | all_edges.push_back(v); 61 | } 62 | } 63 | } 64 | return all_edges; 65 | } 66 | 67 | EdgeWeightedGraph::~EdgeWeightedGraph() { 68 | 69 | } -------------------------------------------------------------------------------- /Algorithms/AlgorithmAndDatastructure/EdgeWeightedGraph/EdgeWeightedGraph.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by windyear_office on 18-2-3. 3 | // 4 | 5 | #ifndef SORTALGORITHM_EDGEWEIGHTEDGRAPH_H 6 | #define SORTALGORITHM_EDGEWEIGHTEDGRAPH_H 7 | 8 | #include 9 | #include 10 | #include "Edge.h" 11 | 12 | using namespace std; 13 | 14 | class EdgeWeightedGraph { 15 | private: 16 | int v; //定点数目 17 | int e; //边数目 18 | vector > adj; //邻接表,这个指针有问题。之前用普通数组,类对象作为值传递的时候会调用复制构造函数,这时候会出问题。浅拷贝。 19 | public: 20 | EdgeWeightedGraph(int v);//直接构造有v个顶点的图 21 | EdgeWeightedGraph(string filename);//一个从文件中读取图的结构的构造函数 22 | ~EdgeWeightedGraph();//删除new出来的内存空间 23 | int V() const;//返回点数 24 | int E() const;//返回边数 25 | void AddEdge(Edge edge);//添加一条边 26 | vector Adj(int v);//返回迭代器,list的迭代器。 27 | vector Edges();//返回所有边 28 | }; 29 | 30 | 31 | #endif //SORTALGORITHM_EDGEWEIGHTEDGRAPH_H 32 | -------------------------------------------------------------------------------- /Algorithms/AlgorithmAndDatastructure/EdgeWeightedGraph/KruskalMST.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by windyear_office on 18-2-6. 3 | // 4 | 5 | #include 6 | #include "KruskalMST.h" 7 | #include "../Union/UnionFind.h" 8 | 9 | KruskalMST::KruskalMST(EdgeWeightedGraph g) { 10 | //初始化一个优先队列,一个union对象。 11 | priority_queue pq; 12 | UnionFind uf(g.V()); 13 | //mst.resize(g.V()); 14 | for(auto e: g.Edges()){ 15 | pq.push(e); 16 | } 17 | while(!pq.empty() && mst.size() < g.V() - 1){ 18 | //获取一条最短的边 19 | Edge e = pq.top(); 20 | pq.pop(); 21 | int v = e.Either(); 22 | int w = e.Other(v); 23 | //判断这条边是否有效,如果两个点能够连在一起,说明无效。 24 | if(uf.IsConnected(v, w)){ 25 | continue; 26 | } 27 | uf.Union(w, v); 28 | mst.push_back(e); 29 | } 30 | } 31 | 32 | vector KruskalMST::Edges() { 33 | return mst; 34 | } 35 | double KruskalMST::Weight() { 36 | double weight = 0; 37 | for(auto e: mst){ 38 | weight += e.Weight(); 39 | } 40 | } -------------------------------------------------------------------------------- /Algorithms/AlgorithmAndDatastructure/EdgeWeightedGraph/KruskalMST.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by windyear_office on 18-2-6. 3 | // 4 | 5 | #ifndef SORTALGORITHM_KRUSKALMST_H 6 | #define SORTALGORITHM_KRUSKALMST_H 7 | 8 | 9 | #include 10 | #include "Edge.h" 11 | #include "EdgeWeightedGraph.h" 12 | //思路就是以边为对象,每次选取不会构成一个环的最短边,直到选取了v-1条边。是否构成环可以用union_find结构来表示,每次加入边就相当于把边的两个点连到一起。 13 | //用一个优先队列保存所有边即可。 14 | class KruskalMST { 15 | private: 16 | vector mst; 17 | public: 18 | KruskalMST(EdgeWeightedGraph g); 19 | vector Edges(); 20 | double Weight(); 21 | }; 22 | 23 | 24 | #endif //SORTALGORITHM_KRUSKALMST_H 25 | -------------------------------------------------------------------------------- /Algorithms/AlgorithmAndDatastructure/EdgeWeightedGraph/LazyPrim.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by windyear_office on 18-2-3. 3 | // 4 | 5 | #include "LazyPrim.h" 6 | 7 | LazyPrim::LazyPrim(EdgeWeightedGraph g) { 8 | //初始化成员 9 | marked = new bool[g.V()]; 10 | for(int i = 0; i < g.V(); i++){ 11 | marked[i] = false; 12 | } 13 | //将第0个点加入最小生成树。 14 | Visit(g, 0); 15 | //从优先队列中取出最小值,然后判断边是否有效,有效则加入。 16 | while(!pq.empty()){ 17 | Edge e = pq.top(); 18 | pq.pop(); 19 | //拿出两个点 20 | int v = e.Either(); 21 | int w = e.Other(v); 22 | //判断两条边是否都已经被标记了 23 | if(marked[v] && marked[w]){ 24 | continue; 25 | } 26 | //加入到最小生成树 27 | mst.push_back(e); 28 | //看那个点还没有加入就加入 29 | if(!marked[w]){ 30 | Visit(g, w); 31 | } 32 | if(!marked[v]){ 33 | Visit(g, v); 34 | } 35 | } 36 | } 37 | 38 | LazyPrim::~LazyPrim() { 39 | delete[] marked; 40 | } 41 | 42 | void LazyPrim::Visit(EdgeWeightedGraph g, int v) { 43 | marked[v] = true; 44 | for(auto e: g.Adj(v)){ 45 | if(!marked[e.Other(v)]){ 46 | pq.push(e); 47 | } 48 | } 49 | } 50 | 51 | vector LazyPrim::Edges() { 52 | return mst; 53 | } 54 | 55 | double LazyPrim::Weight() { 56 | //遍历所有边 57 | double weight = 0; 58 | for(auto e: mst){ 59 | weight += e.Weight(); 60 | } 61 | } -------------------------------------------------------------------------------- /Algorithms/AlgorithmAndDatastructure/EdgeWeightedGraph/LazyPrim.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by windyear_office on 18-2-3. 3 | // 4 | 5 | #ifndef SORTALGORITHM_LAZYPRIM_H 6 | #define SORTALGORITHM_LAZYPRIM_H 7 | 8 | #include "Edge.h" 9 | #include "../Priority_Queue/MinPQ.h" 10 | #include "EdgeWeightedGraph.h" 11 | #include 12 | #include 13 | 14 | using namespace std; 15 | 16 | //最小生成树的prim算法,先随机选择一个点,划分为两个切分集,选择最短的一条边加入到最小生成树。 17 | //然后对应的点加入切分集,再次基于贪心算法的原理选择最短的一条横切边加入。知道定点全部加入。 18 | class LazyPrim { 19 | private: 20 | //需要一个数组标记访问了的节点,一个存储最小生成树,一个优先队列存放横切边集合。 21 | bool* marked; 22 | vector mst; 23 | //直接使用stl的优先队列 24 | priority_queue pq; //横切边 25 | //一个私有成员函数为了添加节点以及还没访问节点之间的横切边。 26 | void Visit(EdgeWeightedGraph g, int v); 27 | public: 28 | LazyPrim(EdgeWeightedGraph g); 29 | ~LazyPrim(); 30 | //返回最小生成树对应的所有边 31 | vector Edges(); 32 | double Weight(); 33 | }; 34 | 35 | 36 | #endif //SORTALGORITHM_LAZYPRIM_H 37 | -------------------------------------------------------------------------------- /Algorithms/AlgorithmAndDatastructure/EdgeWeightedGraph/edgeweighteddfsorder.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by windyear_office on 18-3-8. 3 | // 4 | 5 | #include 6 | #include "edgeweighteddfsorder.h" 7 | 8 | //使用初始化列表进行初始化 9 | EdgeWeightedDFSOrder::EdgeWeightedDFSOrder(const EdgeWeightDiGraph &g) : 10 | count(0), marked(g.V(), false), pre_order(), post_order(), reverse_order() 11 | { 12 | for(int i = 0; i < g.V(); i++){ 13 | if(marked[i] == false){ 14 | DFS(g, i); 15 | } 16 | } 17 | } 18 | 19 | void EdgeWeightedDFSOrder::DFS(const EdgeWeightDiGraph &g, int point) { 20 | //不同的顺序就是记录被访问点的时间不同 21 | marked[point] = true; 22 | pre_order.push(point); 23 | //cout << point << " "; 24 | for(DirectedEdge edge: g.Adj(point)){ 25 | int to = edge.To(); 26 | if(marked[to] == false){ 27 | DFS(g, to); 28 | } 29 | } 30 | post_order.push(point); 31 | reverse_order.push(point); 32 | } 33 | 34 | queue EdgeWeightedDFSOrder::PrePath() const { 35 | return pre_order; 36 | } 37 | 38 | queue EdgeWeightedDFSOrder::PostPath() const { 39 | return post_order; 40 | } 41 | 42 | stack EdgeWeightedDFSOrder::ReversePath() const { 43 | return reverse_order; 44 | } 45 | -------------------------------------------------------------------------------- /Algorithms/AlgorithmAndDatastructure/EdgeWeightedGraph/edgeweighteddfsorder.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by windyear_office on 18-3-8. 3 | // 4 | 5 | #ifndef SORTALGORITHM_EDGEWEIGHTEDDFSORDER_H 6 | #define SORTALGORITHM_EDGEWEIGHTEDDFSORDER_H 7 | 8 | #include 9 | #include 10 | #include 11 | #include "EdgeWeightDiGraph.h" 12 | 13 | using namespace std; 14 | //深度前序,后序,逆后序遍历 15 | class EdgeWeightedDFSOrder { 16 | private: 17 | int count; 18 | vector marked; 19 | queue pre_order; 20 | queue post_order; 21 | stack reverse_order; 22 | public: 23 | EdgeWeightedDFSOrder(const EdgeWeightDiGraph& g); 24 | void DFS(const EdgeWeightDiGraph& g, int point); 25 | //下面的函数返回遍历的顺序的容器 26 | queue PrePath() const; 27 | queue PostPath() const; 28 | stack ReversePath() const; 29 | }; 30 | 31 | 32 | #endif //SORTALGORITHM_EDGEWEIGHTEDDFSORDER_H 33 | -------------------------------------------------------------------------------- /Algorithms/AlgorithmAndDatastructure/EdgeWeightedGraph/edgeweightedgrapdfs.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by windyear_office on 18-3-8. 3 | // 4 | 5 | //之前用C++改写java程序的时候没有考虑如何写出比较好的代码,也没有考虑效率等因素。 6 | //后来学习了一段时间的effective C++,了解了一些编程应该遵循的准则,所以后面的代码尽量遵循准则进行开发 7 | #include "edgeweightedgrapdfs.h" 8 | 9 | EdgeWeightedGrapDFS::EdgeWeightedGrapDFS(const EdgeWeightDiGraph &g, int start_point): 10 | marked(g.V(), false), count(0) { 11 | DFS(g, start_point); 12 | } 13 | 14 | void EdgeWeightedGrapDFS::DFS(const EdgeWeightDiGraph &g, int point){ 15 | marked[point] = true; 16 | count++; 17 | //遍历该点的邻点 18 | for(DirectedEdge edge: g.Adj(point)){ 19 | //出现一个错误,因为传入的对象是const的,所以不能调用非const的函数 20 | int to = edge.To(); 21 | if(Marked(to) == false){ 22 | DFS(g, to); 23 | } 24 | } 25 | } 26 | 27 | int EdgeWeightedGrapDFS::Count() const { 28 | return count; 29 | } 30 | 31 | bool EdgeWeightedGrapDFS::Marked(int v) const { 32 | return marked[v]; 33 | } -------------------------------------------------------------------------------- /Algorithms/AlgorithmAndDatastructure/EdgeWeightedGraph/edgeweightedgrapdfs.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by windyear_office on 18-3-8. 3 | // 4 | 5 | #ifndef SORTALGORITHM_EDGEWEIGHTEDGRAPDFS_H 6 | #define SORTALGORITHM_EDGEWEIGHTEDGRAPDFS_H 7 | 8 | 9 | #include "EdgeWeightDiGraph.h" 10 | 11 | class EdgeWeightedGrapDFS { 12 | private: 13 | int count; 14 | vector marked; 15 | public: 16 | EdgeWeightedGrapDFS(const EdgeWeightDiGraph& g, int start_point);//构造函数,传入图的引用 17 | void DFS(const EdgeWeightDiGraph& g, int point); //这个函数需要修改成员,不能使用const修饰 18 | int Count() const; //不用修改成员变量,使用const修饰。 19 | bool Marked(int v) const; //不用修改成员变量,使用const修饰 20 | }; 21 | 22 | 23 | #endif //SORTALGORITHM_EDGEWEIGHTEDGRAPDFS_H 24 | -------------------------------------------------------------------------------- /Algorithms/AlgorithmAndDatastructure/Graph/BreadFirstDirectedPaths.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by windyear_office on 18-1-31. 3 | // 4 | 5 | #include 6 | #include "BreadFirstDirectedPaths.h" 7 | 8 | BreadFirstDirectedPaths::BreadFirstDirectedPaths(Digraph g, int v) { 9 | start_point = v; 10 | //初始化两个数组 11 | marked = new bool[g.V()]; 12 | for(int i = 0; i < g.V(); i++){ 13 | marked[i] = false; 14 | } 15 | edge_to = new int[g.V()]; 16 | BFS(g,start_point); 17 | } 18 | 19 | BreadFirstDirectedPaths::~BreadFirstDirectedPaths() { 20 | delete[] marked; 21 | delete[] edge_to; 22 | } 23 | 24 | bool BreadFirstDirectedPaths::HasPathTo(int v) { 25 | return marked[v]; 26 | } 27 | 28 | void BreadFirstDirectedPaths::BFS(Digraph g, int v) { 29 | //利用一个队列进行辅助存储还没有访问的节点。 30 | //首先处理开始的节点 31 | //加入队列的时候证明已经被访问了,标记为已访问,标记的顺序不能反。 32 | queue point_queue; 33 | marked[v] = true; 34 | edge_to[v] = v; 35 | //这里的for循环可以合并,将第一个点加到队列中就可以。 36 | for(int s: g.Adj(v)){ 37 | if(!marked[s]){ 38 | //加入队列,并且其父节点为v 39 | edge_to[s] = v; 40 | marked[s] = true; 41 | point_queue.push(s); 42 | } 43 | } 44 | //接着,一直从队列中取出节点,直至队列为空 45 | while(!point_queue.empty()){ 46 | int point = point_queue.front(); 47 | point_queue.pop(); 48 | for(int s: g.Adj(point)){ 49 | if(!marked[s]){ 50 | //加入队列,并且其父节点为v 51 | edge_to[s] = point; 52 | marked[s] = true; 53 | point_queue.push(s); 54 | } 55 | } 56 | } 57 | 58 | } 59 | 60 | list BreadFirstDirectedPaths::PathTo(int v) { 61 | if(!HasPathTo(v)){ 62 | //返回一个空的列表 63 | list no_path; 64 | return no_path; 65 | } 66 | list path; 67 | //头插法 68 | for(int x = v; x != start_point; x = edge_to[x]){ 69 | path.push_front(x); 70 | } 71 | //最后加上起点 72 | path.push_front(start_point); 73 | return path; 74 | } 75 | -------------------------------------------------------------------------------- /Algorithms/AlgorithmAndDatastructure/Graph/BreadFirstDirectedPaths.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by windyear_office on 18-1-31. 3 | // 4 | 5 | #ifndef SORTALGORITHM_BREADFIRSTDIRECTEDPATHS_H 6 | #define SORTALGORITHM_BREADFIRSTDIRECTEDPATHS_H 7 | 8 | 9 | #include "Digraph.h" 10 | 11 | class BreadFirstDirectedPaths { 12 | private: 13 | int* edge_to; 14 | bool* marked; 15 | int start_point;//起点 16 | public: 17 | BreadFirstDirectedPaths(Digraph g, int v); 18 | ~BreadFirstDirectedPaths(); 19 | void BFS(Digraph g, int v); 20 | list PathTo(int v); 21 | bool HasPathTo(int v); 22 | 23 | }; 24 | 25 | 26 | #endif //SORTALGORITHM_BREADFIRSTDIRECTEDPATHS_H 27 | -------------------------------------------------------------------------------- /Algorithms/AlgorithmAndDatastructure/Graph/BreadthFirstPaths.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by windyear_office on 18-1-28. 3 | // 4 | 5 | #include 6 | #include "BreadthFirstPaths.h" 7 | 8 | BreadthFirstPaths::BreadthFirstPaths(Graph g, int v) { 9 | start_point = v; 10 | //初始化两个数组 11 | marked = new bool[g.V()]; 12 | for(int i = 0; i < g.V(); i++){ 13 | marked[i] = false; 14 | } 15 | edge_to = new int[g.V()]; 16 | BFS(g,start_point); 17 | } 18 | 19 | BreadthFirstPaths::~BreadthFirstPaths() { 20 | delete[] marked; 21 | delete[] edge_to; 22 | } 23 | 24 | bool BreadthFirstPaths::HasPathTo(int v) { 25 | return marked[v]; 26 | } 27 | 28 | void BreadthFirstPaths::BFS(Graph g, int v) { 29 | //利用一个队列进行辅助存储还没有访问的节点。 30 | //首先处理开始的节点 31 | //加入队列的时候证明已经被访问了,标记为已访问,标记的顺序不能反。 32 | queue point_queue; 33 | marked[v] = true; 34 | edge_to[v] = v; 35 | //这里的for循环可以合并,将第一个点加到队列中就可以。 36 | for(int s: g.Adj(v)){ 37 | if(!marked[s]){ 38 | //加入队列,并且其父节点为v 39 | edge_to[s] = v; 40 | marked[s] = true; 41 | point_queue.push(s); 42 | } 43 | } 44 | //接着,一直从队列中取出节点,直至队列为空 45 | while(!point_queue.empty()){ 46 | int point = point_queue.front(); 47 | point_queue.pop(); 48 | for(int s: g.Adj(point)){ 49 | if(!marked[s]){ 50 | //加入队列,并且其父节点为v 51 | edge_to[s] = point; 52 | marked[s] = true; 53 | point_queue.push(s); 54 | } 55 | } 56 | } 57 | 58 | } 59 | 60 | list BreadthFirstPaths::PathTo(int v) { 61 | if(!HasPathTo(v)){ 62 | //返回一个空的列表 63 | list no_path; 64 | return no_path; 65 | } 66 | list path; 67 | //头插法 68 | for(int x = v; x != start_point; x = edge_to[x]){ 69 | path.push_front(x); 70 | } 71 | //最后加上起点 72 | path.push_front(start_point); 73 | return path; 74 | } 75 | -------------------------------------------------------------------------------- /Algorithms/AlgorithmAndDatastructure/Graph/BreadthFirstPaths.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by windyear_office on 18-1-28. 3 | // 4 | 5 | #ifndef SORTALGORITHM_BREADTHFIRSTPATHS_H 6 | #define SORTALGORITHM_BREADTHFIRSTPATHS_H 7 | 8 | #include "Graph.h" 9 | 10 | //广度优先搜索,思路是先进先出,利用一个辅助队列,访问一个队列的节点时,将还没有访问的邻接点加入队列。 11 | //直到所有节点都被访问到。从起点开始,每次都访问最近的节点。 12 | //队列中的节点表示已经被访问到了但是其邻接点还没有进行访问判断的节点。 13 | class BreadthFirstPaths { 14 | private: 15 | int* edge_to; 16 | bool* marked; 17 | int start_point;//起点 18 | public: 19 | BreadthFirstPaths(Graph g, int v); 20 | ~BreadthFirstPaths(); 21 | void BFS(Graph g, int v); 22 | list PathTo(int v); 23 | bool HasPathTo(int v); 24 | }; 25 | 26 | 27 | #endif //SORTALGORITHM_BREADTHFIRSTPATHS_H 28 | -------------------------------------------------------------------------------- /Algorithms/AlgorithmAndDatastructure/Graph/ConnectedComponents.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by windyear_office on 18-1-28. 3 | // 4 | 5 | #include "ConnectedComponents.h" 6 | 7 | ConnectedComponents::ConnectedComponents(Graph g) { 8 | //初始化数组 9 | marked = new bool[g.V()]; 10 | id = new int[g.V()]; 11 | count = 0; 12 | //遍历每个点,如果还没有被访问即标记,就对其使用深度优先搜索。 13 | for(int v = 0; v < g.V(); v++){ 14 | if(!marked[v]){ 15 | DFS(g, v); 16 | //一次深度优先搜索产生一个新的连通分量 17 | count++; 18 | } 19 | } 20 | } 21 | 22 | ConnectedComponents::~ConnectedComponents() { 23 | delete[] marked; 24 | delete[] id; 25 | } 26 | 27 | bool ConnectedComponents::Connected(int v, int w) { 28 | return id[v] == id[w]; 29 | } 30 | 31 | int ConnectedComponents::Count() { 32 | return count; 33 | } 34 | 35 | int ConnectedComponents::Id(int v) { 36 | return id[v]; 37 | } 38 | 39 | void ConnectedComponents::DFS(Graph g, int v) { 40 | marked[v] = true; 41 | //标记连通分量 42 | id[v] = count; 43 | for(auto V: g.Adj(v)){ 44 | if(!marked[V]){ 45 | DFS(g,V); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /Algorithms/AlgorithmAndDatastructure/Graph/ConnectedComponents.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by windyear_office on 18-1-28. 3 | // 4 | 5 | #ifndef SORTALGORITHM_CONNECTEDCOMPONENTS_H 6 | #define SORTALGORITHM_CONNECTEDCOMPONENTS_H 7 | 8 | #include "Graph.h" 9 | 10 | //查找连通分量,利用深度优先搜索,先从一个起点开始搜索,然后检查哪些节点还没有被查到,继续使用深度搜索。 11 | //用一个id数组以节点作为索引标记节点属于第几个连通分量。 12 | class ConnectedComponents { 13 | private: 14 | bool* marked; 15 | int* id;//标记连通分量 16 | int count;//标记是第几个连通分量 17 | public: 18 | ConnectedComponents(Graph g); 19 | ~ConnectedComponents(); 20 | void DFS(Graph g, int v); 21 | int Id(int v); 22 | int Count(); 23 | //判断两个节点是否在同一个分量,即是否连接在一起 24 | bool Connected(int v, int w); 25 | }; 26 | 27 | 28 | #endif //SORTALGORITHM_CONNECTEDCOMPONENTS_H 29 | -------------------------------------------------------------------------------- /Algorithms/AlgorithmAndDatastructure/Graph/Cycle.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by windyear_office on 18-1-28. 3 | // 4 | 5 | #include "Cycle.h" 6 | 7 | Cycle::Cycle(Graph g) { 8 | marked = new bool[g.V()]; 9 | has_cycle = false; 10 | //因为不知道图中有多少个连通分量,所以使用一个for循环来遍历递归。 11 | for(int v = 0; v < g.V(); v++){ 12 | //还没有访问的就遍历访问 13 | if(!marked[v]){ 14 | DFS(g, v, v); 15 | } 16 | } 17 | } 18 | 19 | Cycle::~Cycle() { 20 | delete[] marked; 21 | } 22 | 23 | bool Cycle::HasCycle() { 24 | return has_cycle; 25 | } 26 | 27 | void Cycle::DFS(Graph g, int v, int neighbor_point) { 28 | marked[v] = true; 29 | for(int s: g.Adj(v)){ 30 | if(!marked[s]){ 31 | //这里的第二个参数应该填v,之后递归遍历v的邻边的邻边,邻接点已经访问过的只能是v。 32 | DFS(g, s, v); 33 | }else if(s != neighbor_point){ 34 | has_cycle = true; 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /Algorithms/AlgorithmAndDatastructure/Graph/Cycle.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by windyear_office on 18-1-28. 3 | // 4 | 5 | #ifndef SORTALGORITHM_CYCLE_H 6 | #define SORTALGORITHM_CYCLE_H 7 | 8 | #include "Graph.h" 9 | 10 | //查看图中是否存在环:思路很简单,只需要做深度优先遍历,每次遍历邻点的时候查看是否被遍历过。 11 | //如果已经被遍历过而且不是父节点,证明有环。 12 | class Cycle { 13 | private: 14 | bool* marked; 15 | bool has_cycle; 16 | public: 17 | Cycle(Graph g); 18 | ~Cycle(); 19 | void DFS(Graph g, int v, int neighbor_point); 20 | bool HasCycle(); 21 | }; 22 | 23 | 24 | #endif //SORTALGORITHM_CYCLE_H 25 | -------------------------------------------------------------------------------- /Algorithms/AlgorithmAndDatastructure/Graph/DegreesOfSeparation.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by windyear_office on 18-1-30. 3 | // 4 | 5 | #include 6 | #include "DegreesOfSeparation.h" 7 | #include "SymbolGraph.h" 8 | #include "BreadthFirstPaths.h" 9 | 10 | void DegreesOfSeparation::TestDegrees(char* sp, string filename, string source) { 11 | SymbolGraph symbol_graph(filename, sp); 12 | Graph g = symbol_graph.G(); 13 | if(!symbol_graph.Contains(source)){ 14 | cout << "this location is not in the database." << endl; 15 | } 16 | int s = symbol_graph.Index(source); 17 | //一个广度优先搜索类 18 | BreadthFirstPaths breadth_first_paths(g, s); 19 | //读取输入 20 | string destination; 21 | while(getline(cin,destination)){ 22 | if(symbol_graph.Contains(destination)){ 23 | int v = symbol_graph.Index(destination); 24 | if(breadth_first_paths.HasPathTo(v)){ 25 | for(auto path: breadth_first_paths.PathTo(v)){ 26 | cout << " " << symbol_graph.Name(path) << endl; 27 | } 28 | }else{ 29 | cout << "Not connected!" << endl; 30 | } 31 | }else{ 32 | cout << "The location is not in the databases." << endl; 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Algorithms/AlgorithmAndDatastructure/Graph/DegreesOfSeparation.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by windyear_office on 18-1-30. 3 | // 4 | 5 | #ifndef SORTALGORITHM_DEGREESOFSEPARATION_H 6 | #define SORTALGORITHM_DEGREESOFSEPARATION_H 7 | 8 | #include 9 | using namespace std; 10 | 11 | class DegreesOfSeparation { 12 | public: 13 | void TestDegrees(char* sp, string filename, string source); 14 | }; 15 | 16 | 17 | #endif //SORTALGORITHM_DEGREESOFSEPARATION_H 18 | -------------------------------------------------------------------------------- /Algorithms/AlgorithmAndDatastructure/Graph/DepthFirstDirectedPaths.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by windyear_office on 18-1-31. 3 | // 4 | 5 | #include "DepthFirstDirectedPaths.h" 6 | 7 | DepthFirstDirectedPaths::DepthFirstDirectedPaths(Digraph g, int s) { 8 | marked = new bool[g.V()]; 9 | edge_to = new int[g.V()]; 10 | //因为后面用到了marked的内容,如果还没有初始化,使用里面的值,会有未知情况发生,如果是只有被赋值的操作就没有问题。 11 | for(int i = 0; i < g.V(); i++){ 12 | marked[i] = false; 13 | } 14 | start_point = s; 15 | DFS(g, s); 16 | } 17 | 18 | DepthFirstDirectedPaths::~DepthFirstDirectedPaths() { 19 | delete[] marked; 20 | delete[] edge_to; 21 | } 22 | 23 | void DepthFirstDirectedPaths::DFS(Digraph g, int v) { 24 | marked[v] = true; 25 | for(int s: g.Adj(v)){ 26 | if(!marked[s]){ 27 | edge_to[s] = v; 28 | DFS(g, s); 29 | } 30 | } 31 | } 32 | 33 | bool DepthFirstDirectedPaths::HasPathTo(int v) { 34 | return marked[v]; 35 | } 36 | 37 | list DepthFirstDirectedPaths::PathTo(int v) { 38 | if (!HasPathTo(v)) { 39 | //返回一个空的列表 40 | list no_path; 41 | return no_path; 42 | } 43 | list path; 44 | //头插法 45 | for (int x = v; x != start_point; x = edge_to[x]) { 46 | path.push_front(x); 47 | } 48 | //最后加上起点 49 | path.push_front(start_point); 50 | return path; 51 | } -------------------------------------------------------------------------------- /Algorithms/AlgorithmAndDatastructure/Graph/DepthFirstDirectedPaths.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by windyear_office on 18-1-31. 3 | // 4 | 5 | #ifndef SORTALGORITHM_DEPTHFIRSTDIRECTEDPATHS_H 6 | #define SORTALGORITHM_DEPTHFIRSTDIRECTEDPATHS_H 7 | 8 | 9 | #include "Digraph.h" 10 | 11 | class DepthFirstDirectedPaths { 12 | private: 13 | int* edge_to;//记录起点到一个顶点的路径的最后的一个节点 14 | bool* marked;//记录节点是否已经被访问 15 | int start_point;//起点 16 | public: 17 | DepthFirstDirectedPaths(Digraph g, int s); 18 | ~DepthFirstDirectedPaths(); 19 | void DFS(Digraph g, int v); 20 | bool HasPathTo(int v); 21 | list PathTo(int v); 22 | 23 | }; 24 | 25 | 26 | #endif //SORTALGORITHM_DEPTHFIRSTDIRECTEDPATHS_H 27 | -------------------------------------------------------------------------------- /Algorithms/AlgorithmAndDatastructure/Graph/DepthFirstOrder.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by windyear_office on 18-1-31. 3 | // 4 | 5 | #include "DepthFirstOrder.h" 6 | 7 | DepthFirstOrder::DepthFirstOrder(Digraph g) { 8 | marked = new bool[g.V()]; 9 | //必须进行下面这个初始化,不然会出现不确定的情况。因为还没有赋值就使用了。 10 | for(int v = 0; v < g.V(); v++){ 11 | marked[v] = false; 12 | } 13 | //遍历所有顶点。深度优先。 14 | for(int v = 0; v < g.V(); v++){ 15 | if(!marked[v]){ 16 | DFS(g, v); 17 | } 18 | } 19 | } 20 | 21 | DepthFirstOrder::~DepthFirstOrder() { 22 | delete[] marked; 23 | } 24 | 25 | void DepthFirstOrder::DFS(Digraph g, int v) { 26 | pre_path.push(v); 27 | marked[v] = true; 28 | for(auto s: g.Adj(v)){ 29 | if(!marked[s]){ 30 | DFS(g, s); 31 | } 32 | } 33 | post_path.push(v); 34 | reverse_post_path.push(v); 35 | } 36 | 37 | queue DepthFirstOrder::PostPath() { 38 | return post_path; 39 | } 40 | 41 | queue DepthFirstOrder::PrePath() { 42 | return pre_path; 43 | } 44 | 45 | stack DepthFirstOrder::ReversePostPath() { 46 | return reverse_post_path; 47 | } -------------------------------------------------------------------------------- /Algorithms/AlgorithmAndDatastructure/Graph/DepthFirstOrder.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by windyear_office on 18-1-31. 3 | // 4 | 5 | #ifndef SORTALGORITHM_DEPTHFIRSTORDER_H 6 | #define SORTALGORITHM_DEPTHFIRSTORDER_H 7 | #include 8 | #include 9 | #include "Digraph.h" 10 | 11 | using namespace std; 12 | //这个类的方法利用深度优先搜索获得遍历定点的顺序,包括前序,后序,逆后序。而逆后序正好就是拓扑排序。 13 | class DepthFirstOrder { 14 | private: 15 | bool* marked; 16 | queue pre_path; 17 | queue post_path; 18 | stack reverse_post_path; 19 | public: 20 | DepthFirstOrder(Digraph g); 21 | ~DepthFirstOrder(); 22 | void DFS(Digraph g, int V); 23 | queue PrePath(); 24 | queue PostPath(); 25 | stack ReversePostPath(); 26 | }; 27 | 28 | 29 | #endif //SORTALGORITHM_DEPTHFIRSTORDER_H 30 | -------------------------------------------------------------------------------- /Algorithms/AlgorithmAndDatastructure/Graph/DepthFirstPaths.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by windyear_office on 18-1-27. 3 | // 4 | 5 | #include "DepthFirstPaths.h" 6 | 7 | DepthFirstPaths::DepthFirstPaths(Graph g, int s) { 8 | marked = new bool[g.V()]; 9 | edge_to = new int[g.V()]; 10 | //因为后面用到了marked的内容,如果还没有初始化,使用里面的值,会有未知情况发生,如果是只有被赋值的操作就没有问题。 11 | for(int i = 0; i < g.V(); i++){ 12 | marked[i] = false; 13 | } 14 | start_point = s; 15 | DFS(g, s); 16 | } 17 | 18 | DepthFirstPaths::~DepthFirstPaths() { 19 | delete[] marked; 20 | delete[] edge_to; 21 | } 22 | 23 | void DepthFirstPaths::DFS(Graph g, int v) { 24 | marked[v] = true; 25 | for(int s: g.Adj(v)){ 26 | if(!marked[s]){ 27 | edge_to[s] = v; 28 | DFS(g, s); 29 | } 30 | } 31 | } 32 | 33 | bool DepthFirstPaths::HasPathTo(int v) { 34 | return marked[v]; 35 | } 36 | 37 | list DepthFirstPaths::PathTo(int v) { 38 | if(!HasPathTo(v)){ 39 | //返回一个空的列表 40 | list no_path; 41 | return no_path; 42 | } 43 | list path; 44 | //头插法 45 | for(int x = v; x != start_point; x = edge_to[x]){ 46 | path.push_front(x); 47 | } 48 | //最后加上起点 49 | path.push_front(start_point); 50 | return path; 51 | } -------------------------------------------------------------------------------- /Algorithms/AlgorithmAndDatastructure/Graph/DepthFirstPaths.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by windyear_office on 18-1-27. 3 | // 4 | 5 | #ifndef SORTALGORITHM_DEPTHFIRSTPATHS_H 6 | #define SORTALGORITHM_DEPTHFIRSTPATHS_H 7 | 8 | #include "Graph.h" 9 | 10 | //记录深度优先搜索过程中的路径,记录一棵父链接的路径 11 | //像quick_union算法一样 12 | class DepthFirstPaths { 13 | private: 14 | int* edge_to;//记录起点到一个顶点的路径的最后的一个节点 15 | bool* marked;//记录节点是否已经被访问 16 | int start_point;//起点 17 | public: 18 | DepthFirstPaths(Graph g, int s); 19 | ~DepthFirstPaths(); 20 | void DFS(Graph g, int v); 21 | bool HasPathTo(int v); 22 | list PathTo(int v); 23 | }; 24 | 25 | 26 | #endif //SORTALGORITHM_DEPTHFIRSTPATHS_H 27 | -------------------------------------------------------------------------------- /Algorithms/AlgorithmAndDatastructure/Graph/DepthFirstSearch.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by windyear_office on 18-1-27. 3 | // 4 | 5 | #include "DepthFirstSearch.h" 6 | DepthFirstSearch::DepthFirstSearch(Graph g, int s) { 7 | //创建标记数组 8 | marked = new bool[g.V()]; 9 | count = 0; 10 | DFS(g, s); 11 | } 12 | 13 | DepthFirstSearch::~DepthFirstSearch() { 14 | delete[] marked;//删除动态创建的数组 15 | } 16 | 17 | int DepthFirstSearch::Count() { 18 | return count; 19 | } 20 | 21 | bool DepthFirstSearch::Marked(int v) { 22 | return marked[v]; 23 | } 24 | 25 | void DepthFirstSearch::DFS(Graph g, int s) { 26 | //首先标记访问的节点为已经访问,然后遍历其相邻节点 27 | //如果邻节点还没有被访问, 就递归访问其邻节点 28 | marked[s] = true; 29 | count++; 30 | for(auto V: g.Adj(s)){ 31 | if(!Marked(V)){ 32 | DFS(g,V); 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /Algorithms/AlgorithmAndDatastructure/Graph/DepthFirstSearch.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by windyear_office on 18-1-27. 3 | // 4 | 5 | #ifndef SORTALGORITHM_DEPTHFIRSTSEARCH_H 6 | #define SORTALGORITHM_DEPTHFIRSTSEARCH_H 7 | 8 | #include "Graph.h" 9 | 10 | //深度优先搜索:访问一个顶点时首先标记其已经被访问,然后递归地访问它的没有被标记过的节点。 11 | class DepthFirstSearch { 12 | private: 13 | int count; //记录访问到的节点数目 14 | bool* marked;//标记节点是否被访问 15 | public: 16 | DepthFirstSearch(Graph g, int s);//构造函数,传入图结构和开始访问的节点 17 | ~DepthFirstSearch(); 18 | void DFS(Graph g, int s);//深度优先遍历的主程序 19 | int Count(); 20 | bool Marked(int v); 21 | }; 22 | 23 | 24 | #endif //SORTALGORITHM_DEPTHFIRSTSEARCH_H 25 | -------------------------------------------------------------------------------- /Algorithms/AlgorithmAndDatastructure/Graph/Digraph.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by windyear_office on 18-1-30. 3 | // 4 | 5 | #include "Digraph.h" 6 | #include 7 | 8 | Digraph::Digraph(int v): adj(v){ 9 | //创建空的邻接表,点的数目为v 10 | this->v = v; 11 | this->e = 0; 12 | //this->adj = new vector(v);//不要将stl和数组混合使用,比如这里指向list的指针,但是根本无法确定list的大小。可以改用vector。 13 | //后来发现原因应该是传入图对象是使用了默认的复制构造函数,而数组不能直接赋值。换成vector之后才可以。 14 | } 15 | Digraph::Digraph(string filename){ 16 | ifstream input(filename); 17 | //能够打开文件 18 | e = 0; 19 | if(input){ 20 | //直接当做流来进行处理 21 | int edge; 22 | input >> v >> edge; 23 | //依次读取每条边 24 | //可以用resize进行重新分配内存,stl的容器都是自动管理内存的,不用管他们。 25 | adj.resize(v); 26 | int point1, point2; 27 | for(int i = 0; i < edge; i++){ 28 | input >> point1 >> point2; 29 | AddEdge(point1, point2); 30 | } 31 | } 32 | input.close(); 33 | } 34 | 35 | 36 | Digraph::~Digraph() { 37 | //delete[] adj; 38 | 39 | } 40 | 41 | int Digraph::E() { 42 | return e; 43 | } 44 | 45 | int Digraph::V() { 46 | return v; 47 | } 48 | 49 | void Digraph::AddEdge(int v, int w) { 50 | //这里只加一条边 51 | adj[v].push_back(w); 52 | e++; 53 | } 54 | //直接返回一个列表,列表不大的话消耗不大。 55 | list Digraph::Adj(int v) { 56 | //翻转链表是为了满足书本的Bag类型对数据的操作,使得测试结果跟书中的一样。进一步验证了拓扑排序和遍历的顺序会不同,如果获取的邻边的顺序不同。 57 | list reverse_list; 58 | for(auto i: adj[v]){ 59 | reverse_list.push_front(i); 60 | } 61 | return reverse_list; 62 | } 63 | 64 | Digraph Digraph::Reverse() { 65 | Digraph digraph(v); 66 | for(int s = 0; s < v; s++){ 67 | for(auto w: Adj(s)){ 68 | digraph.AddEdge(w, s); 69 | } 70 | } 71 | return digraph; 72 | } -------------------------------------------------------------------------------- /Algorithms/AlgorithmAndDatastructure/Graph/Digraph.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by windyear_office on 18-1-30. 3 | // 4 | 5 | #ifndef SORTALGORITHM_DIGRAPH_H 6 | #define SORTALGORITHM_DIGRAPH_H 7 | #include 8 | #include 9 | #include 10 | 11 | using namespace std; 12 | //有向图的代码跟无向图差不多,只需要将添加边的操作变成只添加一条边。 13 | class Digraph { 14 | private: 15 | int v; //定点数目 16 | int e; //边数目 17 | vector > adj; //邻接表,这个指针有问题。之前用普通数组,类对象作为值传递的时候会调用复制构造函数,这时候会出问题。浅拷贝。 18 | public: 19 | Digraph(int v);//直接构造有v个顶点的图 20 | Digraph(string filename);//一个从文件中读取图的结构的构造函数 21 | ~Digraph();//删除new出来的内存空间 22 | int V();//返回点数 23 | int E();//返回边数 24 | void AddEdge(int v, int w);//添加一条边 25 | list Adj(int v);//返回迭代器,list的迭代器。 26 | Digraph Reverse();//反向图,可以找到所有指向一个节点的边。 27 | }; 28 | 29 | 30 | #endif //SORTALGORITHM_DIGRAPH_H 31 | -------------------------------------------------------------------------------- /Algorithms/AlgorithmAndDatastructure/Graph/DirectedCycle.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by windyear_office on 18-1-31. 3 | // 4 | 5 | #include "DirectedCycle.h" 6 | 7 | DirectedCycle::DirectedCycle(Digraph g) { 8 | //初始化几个数组 9 | marked = new bool[g.V()]; 10 | edge_to = new int[g.V()]; 11 | on_stack = new bool[g.V()]; 12 | for(int i = 0; i < g.V(); i++){ 13 | marked[i] = false; 14 | edge_to[i] = i; 15 | on_stack[i] = false; 16 | } 17 | for(int v = 0; v < g.V(); v++){ 18 | if(!marked[v]){ 19 | DFS(g, v); 20 | } 21 | } 22 | } 23 | 24 | DirectedCycle::~DirectedCycle() { 25 | delete[] marked; 26 | delete[] edge_to; 27 | delete[] on_stack; 28 | } 29 | 30 | bool DirectedCycle::HasCycle() { 31 | return !path.empty(); 32 | } 33 | 34 | vector DirectedCycle::CyclePath() { 35 | return path; 36 | } 37 | 38 | void DirectedCycle::DFS(Digraph g, int start_point) { 39 | //访问到的点入栈 40 | on_stack[start_point] = true; 41 | marked[start_point] = true; 42 | //递归访问相邻点,判断是否有环 43 | for(auto v: g.Adj(start_point)){ 44 | if(HasCycle()){ 45 | return; 46 | }else if(!marked[start_point]){ 47 | edge_to[v] = start_point; 48 | DFS(g, v); 49 | }else if(on_stack[v]){ 50 | //将路径存起来,从start_point开始返回 51 | for(int w = start_point; w != v; w = edge_to[w]){ 52 | path.push_back(w); 53 | } 54 | //补上路径。 55 | path.push_back(v); 56 | path.push_back(start_point); 57 | } 58 | } 59 | on_stack[start_point] = false; 60 | return; 61 | } -------------------------------------------------------------------------------- /Algorithms/AlgorithmAndDatastructure/Graph/DirectedCycle.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by windyear_office on 18-1-31. 3 | // 4 | 5 | #ifndef SORTALGORITHM_DIRECTEDCYCLE_H 6 | #define SORTALGORITHM_DIRECTEDCYCLE_H 7 | 8 | #include 9 | #include "Digraph.h" 10 | 11 | using namespace std; 12 | class DirectedCycle { 13 | private: 14 | bool* marked; 15 | int* edge_to;//记录路线 16 | bool* on_stack;//用来保存是否在栈上,不同于无向图,不用确认是否在同一次递归中。 17 | vector path; 18 | public: 19 | DirectedCycle(Digraph g); 20 | ~DirectedCycle(); 21 | void DFS(Digraph g, int start_point); 22 | bool HasCycle(); 23 | vector CyclePath(); 24 | 25 | }; 26 | 27 | 28 | #endif //SORTALGORITHM_DIRECTEDCYCLE_H 29 | -------------------------------------------------------------------------------- /Algorithms/AlgorithmAndDatastructure/Graph/DirectedDFS.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by windyear_office on 18-1-30. 3 | // 4 | 5 | #include "DirectedDFS.h" 6 | 7 | DirectedDFS::DirectedDFS(Digraph g, int s) { 8 | //创建标记数组 9 | marked = new bool[g.V()]; 10 | for(int i = 0; i < g.V(); i++){ 11 | marked[i] = false; 12 | } 13 | count = 0; 14 | DFS(g, s); 15 | } 16 | 17 | DirectedDFS::DirectedDFS(Digraph g, vector source){ 18 | marked = new bool[g.V()]; 19 | for(int i = 0; i < g.V(); i++){ 20 | marked[i] = false; 21 | } 22 | count = 0; 23 | for(auto v: source){ 24 | if(!marked[v]){ 25 | DFS(g, v); 26 | } 27 | } 28 | } 29 | DirectedDFS::~DirectedDFS() { 30 | delete[] marked;//删除动态创建的数组 31 | } 32 | 33 | int DirectedDFS::Count() { 34 | return count; 35 | } 36 | 37 | bool DirectedDFS::Marked(int v) { 38 | return marked[v]; 39 | } 40 | 41 | void DirectedDFS::DFS(Digraph g, int s) { 42 | //首先标记访问的节点为已经访问,然后遍历其相邻节点 43 | //如果邻节点还没有被访问, 就递归访问其邻节点 44 | marked[s] = true; 45 | count++; 46 | for(auto V: g.Adj(s)){ 47 | if(!Marked(V)){ 48 | DFS(g,V); 49 | } 50 | } 51 | } -------------------------------------------------------------------------------- /Algorithms/AlgorithmAndDatastructure/Graph/DirectedDFS.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by windyear_office on 18-1-30. 3 | // 4 | 5 | #ifndef SORTALGORITHM_DIRECTEDDFS_H 6 | #define SORTALGORITHM_DIRECTEDDFS_H 7 | 8 | 9 | #include "Digraph.h" 10 | 11 | class DirectedDFS { 12 | private: 13 | int count; //记录访问到的节点数目 14 | bool* marked;//标记节点是否被访问 15 | public: 16 | DirectedDFS(Digraph g, int s);//构造函数,传入图结构和开始访问的节点 17 | DirectedDFS(Digraph g, vector source); //找出集合source中的点可以遍历到的点 18 | ~DirectedDFS(); 19 | void DFS(Digraph g, int s);//深度优先遍历的主程序 20 | int Count(); 21 | bool Marked(int v); 22 | }; 23 | 24 | 25 | #endif //SORTALGORITHM_DIRECTEDDFS_H 26 | -------------------------------------------------------------------------------- /Algorithms/AlgorithmAndDatastructure/Graph/Graph.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by windyear_office on 18-1-25. 3 | // 4 | 5 | #include 6 | #include "Graph.h" 7 | 8 | Graph::Graph(int v): adj(v){ 9 | //创建空的邻接表,点的数目为v 10 | this->v = v; 11 | this->e = 0; 12 | //this->adj = new vector(v);//不要将stl和数组混合使用,比如这里指向list的指针,但是根本无法确定list的大小。可以改用vector。 13 | //后来发现原因应该是传入图对象是使用了默认的复制构造函数,而数组不能直接赋值。换成vector之后才可以。 14 | } 15 | Graph::Graph(string filename, int s){ 16 | ifstream input(filename); 17 | //能够打开文件 18 | e = 0; 19 | if(input){ 20 | //直接当做流来进行处理 21 | int edge; 22 | input >> v >> edge; 23 | //依次读取每条边 24 | //可以用resize进行重新分配内存,stl的容器都是自动管理内存的,不用管他们。 25 | //自动初始化 26 | adj.resize(v); 27 | int point1, point2; 28 | for(int i = 0; i < edge; i++){ 29 | input >> point1 >> point2; 30 | AddEdge(point1, point2); 31 | } 32 | } 33 | input.close(); 34 | } 35 | 36 | 37 | Graph::~Graph() { 38 | //delete[] adj; 39 | 40 | } 41 | 42 | int Graph::E() { 43 | return e; 44 | } 45 | 46 | int Graph::V() { 47 | return v; 48 | } 49 | 50 | void Graph::AddEdge(int v, int w) { 51 | adj[v].push_back(w); 52 | adj[w].push_back(v); 53 | e++; 54 | } 55 | //直接返回一个列表,列表不大的话消耗不大。 56 | list Graph::Adj(int v) { 57 | return adj[v]; 58 | } 59 | -------------------------------------------------------------------------------- /Algorithms/AlgorithmAndDatastructure/Graph/Graph.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by windyear_office on 18-1-25. 3 | // 4 | 5 | #ifndef SORTALGORITHM_GRAPH_H 6 | #define SORTALGORITHM_GRAPH_H 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | using namespace std; 13 | 14 | //采用邻接表来存储图。 15 | //graph作为参数会调用复制构造函数。 16 | class Graph { 17 | private: 18 | int v; //定点数目 19 | int e; //边数目 20 | vector > adj; //邻接表,这个指针有问题。之前用普通数组,类对象作为值传递的时候会调用复制构造函数,这时候会出问题。浅拷贝。 21 | public: 22 | Graph(int v);//直接构造有v个顶点的图 23 | Graph(string filename, int v);//一个从文件中读取图的结构的构造函数 24 | ~Graph();//删除new出来的内存空间 25 | int V();//返回点数 26 | int E();//返回边数 27 | void AddEdge(int v, int w);//添加一条边 28 | list Adj(int v);//返回迭代器,list的迭代器。 29 | }; 30 | 31 | 32 | #endif //SORTALGORITHM_GRAPH_H 33 | -------------------------------------------------------------------------------- /Algorithms/AlgorithmAndDatastructure/Graph/KosarajuSCC.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by windyear_office on 18-2-2. 3 | // 4 | 5 | #include 6 | #include "KosarajuSCC.h" 7 | #include "DepthFirstOrder.h" 8 | 9 | KosarajuSCC::KosarajuSCC(Digraph g) { 10 | //初始化数组 11 | marked = new bool[g.V()]; 12 | id = new int[g.V()]; 13 | count = 0; 14 | //遍历每个点,如果还没有被访问即标记,就对其使用深度优先搜索。 15 | //获取逆向图的逆后序 16 | DepthFirstOrder depth_first_order(g.Reverse()); 17 | stack reverse_path = depth_first_order.ReversePostPath(); 18 | int length = reverse_path.size(); 19 | for(int v = 0; v < length; v++){ 20 | int w = reverse_path.top(); 21 | if(!marked[w]){ 22 | DFS(g, w); 23 | //一次深度优先搜索产生一个新的连通分量 24 | count++; 25 | } 26 | reverse_path.pop(); 27 | } 28 | } 29 | 30 | KosarajuSCC::~KosarajuSCC() { 31 | delete[] marked; 32 | delete[] id; 33 | } 34 | 35 | bool KosarajuSCC::Connected(int v, int w) { 36 | return id[v] == id[w]; 37 | } 38 | 39 | int KosarajuSCC::Count() { 40 | return count; 41 | } 42 | 43 | int KosarajuSCC::Id(int v) { 44 | return id[v]; 45 | } 46 | 47 | void KosarajuSCC::DFS(Digraph g, int v) { 48 | marked[v] = true; 49 | //标记连通分量 50 | id[v] = count; 51 | for(auto V: g.Adj(v)){ 52 | if(!marked[V]){ 53 | DFS(g,V); 54 | } 55 | } 56 | } 57 | 58 | void KosarajuSCC::TestSCC(Digraph g) { 59 | vector > components(Count()); 60 | for(int i = 0; i < g.V(); i++){ 61 | components[Id(i)].push_back(i); 62 | } 63 | cout << "components: " << endl; 64 | for(int i = 0; i < Count(); i++){ 65 | for(auto v: components[i]){ 66 | cout << v << " "; 67 | } 68 | cout << endl; 69 | } 70 | } -------------------------------------------------------------------------------- /Algorithms/AlgorithmAndDatastructure/Graph/KosarajuSCC.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by windyear_office on 18-2-2. 3 | // 4 | 5 | #ifndef SORTALGORITHM_KOSARAJUSCC_H 6 | #define SORTALGORITHM_KOSARAJUSCC_H 7 | 8 | #include "Digraph.h" 9 | 10 | //该算法的核心在于用逆向图的逆后续进行深度优先遍历可以得到在同一个强连通分量的所有节点。 11 | //在图g中深度遍历,如果存在s到v,只需要再证明有v到s的存在即可。反过来假设逆向图逆后序v到s,存在两种情况,一种是 12 | //不在同一个连通分量,第二种是存在v到s的路径。如果g中存在s到v,证明g-1中存在v到s。得证。 13 | class KosarajuSCC { 14 | private: 15 | bool* marked; 16 | int* id;//标记连通分量 17 | int count;//标记是第几个连通分量 18 | public: 19 | KosarajuSCC(Digraph g); 20 | ~KosarajuSCC(); 21 | void DFS(Digraph g, int v); 22 | int Id(int v); 23 | int Count(); 24 | //判断两个节点是否在同一个分量,即是否连接在一起 25 | bool Connected(int v, int w); 26 | void TestSCC(Digraph g); 27 | }; 28 | 29 | 30 | #endif //SORTALGORITHM_KOSARAJUSCC_H 31 | -------------------------------------------------------------------------------- /Algorithms/AlgorithmAndDatastructure/Graph/SymbolDigraph.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by windyear_office on 18-1-31. 3 | // 4 | 5 | #include 6 | #include "SymbolDigraph.h" 7 | #include 8 | #include 9 | 10 | SymbolDigraph::SymbolDigraph(string filename, char* sp) { 11 | ifstream input(filename); 12 | if(input){ 13 | string s; 14 | vector ss; 15 | //以sp为分隔符读取字符串内容到s中 16 | while(getline(input, s)) { 17 | //map的插入方法,要熟悉 18 | boost::algorithm::split(ss, s, boost::algorithm::is_any_of(sp)); 19 | for(auto v: ss) { 20 | if (!index_table.count(v)) { 21 | index_table.insert(pair(v, index_table.size())); 22 | } 23 | } 24 | } 25 | } 26 | input.close(); 27 | //第二次遍历构造图 28 | g = new Digraph(index_table.size()); 29 | //重新分配大小 30 | index.resize(index_table.size()); 31 | //遍历map 32 | map::iterator it; 33 | for(it = index_table.begin(); it != index_table.end(); it++){ 34 | index[it->second] = it->first; 35 | } 36 | 37 | input.open(filename); 38 | if(input){ 39 | string s; 40 | vector ss; 41 | while(getline(input, s)) { 42 | //map的插入方法,要熟悉 43 | boost::algorithm::split(ss, s, boost::algorithm::is_any_of(sp)); 44 | int v = index_table[ss[0]]; 45 | for(int i = 1; i < ss.size(); i++) { 46 | g->AddEdge(v, index_table[ss[i]]); 47 | } 48 | } 49 | } 50 | input.close(); 51 | cout << "The size of the Digraph is: " << index_table.size() << endl; 52 | } 53 | 54 | SymbolDigraph::~SymbolDigraph() { 55 | delete g; 56 | } 57 | 58 | bool SymbolDigraph::Contains(string s) { 59 | return index_table.count(s); 60 | } 61 | 62 | Digraph SymbolDigraph::G() { 63 | return *g; 64 | } 65 | 66 | int SymbolDigraph::Index(string s) { 67 | return index_table[s]; 68 | } 69 | 70 | string SymbolDigraph::Name(int index_) { 71 | return index[index_]; 72 | } 73 | 74 | 75 | -------------------------------------------------------------------------------- /Algorithms/AlgorithmAndDatastructure/Graph/SymbolDigraph.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by windyear_office on 18-1-31. 3 | // 4 | 5 | #ifndef SORTALGORITHM_SYMBOLDIDigraph_H 6 | #define SORTALGORITHM_SYMBOLDIDigraph_H 7 | 8 | #include 9 | #include 10 | #include 11 | #include "Digraph.h" 12 | 13 | class SymbolDigraph { 14 | private: 15 | map index_table; 16 | vector index; 17 | Digraph* g; 18 | public: 19 | SymbolDigraph(string filename, char* sp); 20 | ~SymbolDigraph(); 21 | int Index(string s); 22 | string Name(int index_); 23 | bool Contains(string s); 24 | Digraph G(); 25 | }; 26 | 27 | 28 | #endif //SORTALGORITHM_SYMBOLDIDigraph_H 29 | -------------------------------------------------------------------------------- /Algorithms/AlgorithmAndDatastructure/Graph/SymbolGraph.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by windyear_office on 18-1-30. 3 | // 4 | 5 | #include 6 | #include "SymbolGraph.h" 7 | #include 8 | #include 9 | 10 | SymbolGraph::SymbolGraph(string filename, char* sp) { 11 | ifstream input(filename); 12 | if(input){ 13 | string s; 14 | vector ss; 15 | //以sp为分隔符读取字符串内容到s中 16 | while(getline(input, s)) { 17 | //map的插入方法,要熟悉 18 | boost::algorithm::split(ss, s, boost::algorithm::is_any_of(sp)); 19 | for(auto v: ss) { 20 | if (!index_table.count(v)) { 21 | index_table.insert(pair(v, index_table.size())); 22 | } 23 | } 24 | } 25 | } 26 | input.close(); 27 | //第二次遍历构造图 28 | g = new Graph(index_table.size()); 29 | //重新分配大小 30 | index.resize(index_table.size()); 31 | //遍历map 32 | map::iterator it; 33 | for(it = index_table.begin(); it != index_table.end(); it++){ 34 | index[it->second] = it->first; 35 | } 36 | 37 | input.open(filename); 38 | if(input){ 39 | string s; 40 | vector ss; 41 | while(getline(input, s)) { 42 | //map的插入方法,要熟悉 43 | boost::algorithm::split(ss, s, boost::algorithm::is_any_of(sp)); 44 | int v = index_table[ss[0]]; 45 | for(int i = 1; i < ss.size(); i++) { 46 | g->AddEdge(v, index_table[ss[i]]); 47 | } 48 | } 49 | } 50 | input.close(); 51 | cout << "The size of the graph is: " << index_table.size() << endl; 52 | } 53 | 54 | SymbolGraph::~SymbolGraph() { 55 | delete g; 56 | } 57 | 58 | bool SymbolGraph::Contains(string s) { 59 | return index_table.count(s); 60 | } 61 | 62 | Graph SymbolGraph::G() { 63 | return *g; 64 | } 65 | 66 | int SymbolGraph::Index(string s) { 67 | return index_table[s]; 68 | } 69 | 70 | string SymbolGraph::Name(int index_) { 71 | return index[index_]; 72 | } 73 | 74 | 75 | -------------------------------------------------------------------------------- /Algorithms/AlgorithmAndDatastructure/Graph/SymbolGraph.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by windyear_office on 18-1-30. 3 | // 4 | 5 | #ifndef SORTALGORITHM_SYMBOLGRAPH_H 6 | #define SORTALGORITHM_SYMBOLGRAPH_H 7 | 8 | #include 9 | #include 10 | #include 11 | #include "Graph.h" 12 | 13 | using namespace std; 14 | //符号图是表示实际应用中节点是由字符串或者其他形式表示的。思路是做一个映射和反向索引, 15 | //将字符串和索引对应起来 16 | 17 | class SymbolGraph { 18 | private: 19 | map index_table; 20 | vector index; 21 | Graph* g; 22 | public: 23 | SymbolGraph(string filename, char* sp); 24 | ~SymbolGraph(); 25 | int Index(string s); 26 | string Name(int index_); 27 | bool Contains(string s); 28 | Graph G(); 29 | 30 | }; 31 | 32 | 33 | #endif //SORTALGORITHM_SYMBOLGRAPH_H 34 | -------------------------------------------------------------------------------- /Algorithms/AlgorithmAndDatastructure/Graph/Topological.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by windyear_office on 18-1-31. 3 | // 4 | 5 | #include 6 | #include "Topological.h" 7 | #include "DirectedCycle.h" 8 | #include "DepthFirstOrder.h" 9 | 10 | Topological::Topological(Digraph g) { 11 | //首先判断是否有环 12 | DirectedCycle directed_cycle(g); 13 | if(!directed_cycle.HasCycle()){ 14 | DepthFirstOrder depth_first_order(g); 15 | order = depth_first_order.ReversePostPath(); 16 | }else{ 17 | cout << "输入的图有环!" << endl; 18 | } 19 | } 20 | 21 | bool Topological::IsDAG() { 22 | return order.empty(); 23 | } 24 | 25 | stack Topological::Order() { 26 | return order; 27 | } 28 | -------------------------------------------------------------------------------- /Algorithms/AlgorithmAndDatastructure/Graph/Topological.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by windyear_office on 18-1-31. 3 | // 4 | 5 | #ifndef SORTALGORITHM_TOPOLOGICAL_H 6 | #define SORTALGORITHM_TOPOLOGICAL_H 7 | 8 | #include 9 | #include "Digraph.h" 10 | 11 | using namespace std; 12 | 13 | class Topological { 14 | private: 15 | stack order;//存储一个拓扑排序 16 | public: 17 | Topological(Digraph g); 18 | bool IsDAG(); 19 | stack Order(); 20 | }; 21 | 22 | 23 | #endif //SORTALGORITHM_TOPOLOGICAL_H 24 | -------------------------------------------------------------------------------- /Algorithms/AlgorithmAndDatastructure/Graph/TwoColorGraph.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by windyear_office on 18-1-29. 3 | // 4 | 5 | #include "TwoColorGraph.h" 6 | 7 | TwoColorGraph::TwoColorGraph(Graph g) { 8 | marked = new bool[g.V()]; 9 | color = new bool[g.V()]; 10 | is_two_color = true; 11 | for(int v = 0; v < g.V(); v++){ 12 | if(!marked[v]){ 13 | DFS(g, v); 14 | } 15 | } 16 | } 17 | 18 | TwoColorGraph::~TwoColorGraph() { 19 | delete[] marked; 20 | delete[] color; 21 | } 22 | 23 | bool TwoColorGraph::IsTwoColor() { 24 | return is_two_color; 25 | } 26 | 27 | void TwoColorGraph::DFS(Graph g, int v) { 28 | marked[v] = true; 29 | for(int s: g.Adj(v)){ 30 | if(!marked[s]){ 31 | color[s] = !color[v]; 32 | DFS(g, s); 33 | }else if(color[s] == color[v]){ 34 | is_two_color = false; 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Algorithms/AlgorithmAndDatastructure/Graph/TwoColorGraph.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by windyear_office on 18-1-29. 3 | // 4 | 5 | #ifndef SORTALGORITHM_TWOCOLORGRAPH_H 6 | #define SORTALGORITHM_TWOCOLORGRAPH_H 7 | 8 | #include "Graph.h" 9 | 10 | //二分图问题,也是图的着色问题:思路是遍历的同时进行染色,遇到已经染色的节点,判断其与相邻的节点是否一样的颜色,是的话 11 | //就不是二分图。二分图的特性是没有奇数圈,颜色数目为2。 12 | class TwoColorGraph { 13 | private: 14 | bool* marked; 15 | bool* color;//用两种情况标识图节点的颜色 16 | bool is_two_color; 17 | public: 18 | TwoColorGraph(Graph g); 19 | ~TwoColorGraph(); 20 | void DFS(Graph g, int v); 21 | bool IsTwoColor(); 22 | }; 23 | 24 | 25 | #endif //SORTALGORITHM_TWOCOLORGRAPH_H 26 | -------------------------------------------------------------------------------- /Algorithms/AlgorithmAndDatastructure/Priority_Queue/MaxPQ.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by windyear_office on 18-2-3. 3 | // 4 | 5 | #include "MaxPQ.h" 6 | 7 | MaxPQ::MaxPQ(int maxN) : priority(maxN + 1){ 8 | length = maxN; 9 | } 10 | 11 | void MaxPQ::Exchange(int i, int j) { 12 | if(i > length || j > length){ 13 | return; 14 | } 15 | int temp = priority[i]; 16 | priority[i] = priority[j]; 17 | priority[j] = temp; 18 | } 19 | 20 | bool MaxPQ::less(int i, int j) { 21 | if(i > length || j > length){ 22 | return false; 23 | } 24 | if(priority[i] <= priority[j]){ 25 | return false; 26 | }else{ 27 | return true; 28 | } 29 | } 30 | 31 | void MaxPQ::Sink(int k) { 32 | //先找到两个子节点,找出大一点的子节点,再比较父节点与子节点来判断是否要交换。 33 | //while的条件保证了还有子节点。 34 | while(2*k <= length){ 35 | int son = 2*k; 36 | if(son < length && less(son, son + 1)){ 37 | son++; 38 | } 39 | if(less(son, k)){ 40 | break; 41 | } 42 | Exchange(son, k); 43 | k = son; 44 | } 45 | } 46 | 47 | void MaxPQ::Swim(int k) { 48 | //保证还有父节点 49 | while(k > 1){ 50 | if(less(k/2, k)){ 51 | Exchange(k/2, k); 52 | k = k / 2; 53 | }else{ 54 | break; 55 | } 56 | } 57 | } 58 | 59 | bool MaxPQ::IsEmpty() { 60 | return length == 0; 61 | } 62 | 63 | int MaxPQ::Size() { 64 | return length; 65 | } 66 | 67 | void MaxPQ::Insert(int v) { 68 | priority.push_back(v); 69 | length++; 70 | Swim(length); 71 | } 72 | 73 | int MaxPQ::DelteMax() { 74 | int max = priority[1]; 75 | Exchange(length, 1); 76 | length--; 77 | Sink(1); 78 | return max; 79 | } -------------------------------------------------------------------------------- /Algorithms/AlgorithmAndDatastructure/Priority_Queue/MaxPQ.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by windyear_office on 18-2-3. 3 | // 4 | 5 | #ifndef SORTALGORITHM_MAXPQ_H 6 | #define SORTALGORITHM_MAXPQ_H 7 | 8 | #include 9 | 10 | using namespace std; 11 | 12 | //最大优先队列,可以用基本的数组或者链表等实现,但是插入或者删除最大元素的操作总有一个需要线性时间。 13 | //采用完全二叉树实现的优先队列,即最大堆,可以实现对数时间的查找和插入。 14 | class MaxPQ { 15 | private: 16 | //基于数组的完全二叉树,直接使用vector 17 | vector priority; 18 | int length; 19 | //还有一些比如比较,交换等基础方法,不对外公开 20 | bool less(int i, int j); 21 | void Exchange(int i, int j); 22 | //堆调整 23 | void Swim(int k); 24 | void Sink(int k); 25 | public: 26 | MaxPQ(int maxN); 27 | void Insert(int v); 28 | int DelteMax(); 29 | int Size(); 30 | bool IsEmpty(); 31 | }; 32 | 33 | 34 | #endif //SORTALGORITHM_MAXPQ_H 35 | -------------------------------------------------------------------------------- /Algorithms/AlgorithmAndDatastructure/Priority_Queue/MinPQ.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by windyear_office on 18-2-3. 3 | // 4 | 5 | #include "MinPQ.h" 6 | 7 | MinPQ::MinPQ(int maxN) : priority(maxN + 1){ 8 | length = maxN; 9 | } 10 | 11 | void MinPQ::Exchange(int i, int j) { 12 | if(i > length || j > length){ 13 | return; 14 | } 15 | int temp = priority[i]; 16 | priority[i] = priority[j]; 17 | priority[j] = temp; 18 | return; 19 | } 20 | 21 | bool MinPQ::less(int i, int j) { 22 | if(i > length || j > length){ 23 | return false; 24 | } 25 | if(priority[i] <= priority[j]){ 26 | return true; 27 | }else{ 28 | return false; 29 | } 30 | } 31 | 32 | void MinPQ::Sink(int k) { 33 | //先找到两个子节点,找出大一点的子节点,再比较父节点与子节点来判断是否要交换。 34 | //while的条件保证了还有子节点。 35 | while(2*k <= length){ 36 | int son = 2*k; 37 | if(son < length && less(son, son + 1)){ 38 | son++; 39 | } 40 | if(less(son, k)){ 41 | break; 42 | } 43 | Exchange(son, k); 44 | k = son; 45 | } 46 | } 47 | 48 | void MinPQ::Swim(int k) { 49 | //保证还有父节点 50 | while(k > 1){ 51 | if(less(k/2, k)){ 52 | Exchange(k/2, k); 53 | k = k / 2; 54 | }else{ 55 | break; 56 | } 57 | } 58 | } 59 | 60 | bool MinPQ::IsEmpty() { 61 | return length == 0; 62 | } 63 | 64 | int MinPQ::Size() { 65 | return length; 66 | } 67 | 68 | int MinPQ::DelteMax() { 69 | int max = priority[1]; 70 | Exchange(length, 1); 71 | length--; 72 | Sink(1); 73 | return max; 74 | } 75 | 76 | void MinPQ::Insert(int v){ 77 | priority.push_back(v); 78 | length++; 79 | //这里一开始写错了,不应该是v,应该是最后的长度。 80 | Swim(length); 81 | } -------------------------------------------------------------------------------- /Algorithms/AlgorithmAndDatastructure/Priority_Queue/MinPQ.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by windyear_office on 18-2-3. 3 | // 4 | 5 | #ifndef SORTALGORITHM_MINPQ_H 6 | #define SORTALGORITHM_MINPQ_H 7 | 8 | #include 9 | using namespace std; 10 | 11 | //为了后面使用起来的方便,这里应该使用泛型编程,模板类。 12 | class MinPQ { 13 | private: 14 | //基于数组的完全二叉树,直接使用vector 15 | vector priority; 16 | int length; 17 | //还有一些比如比较,交换等基础方法,不对外公开 18 | bool less(int i, int j); 19 | void Exchange(int i, int j); 20 | //堆调整 21 | void Swim(int k); 22 | void Sink(int k); 23 | public: 24 | MinPQ(int maxN); 25 | void Insert(int v); 26 | int DelteMax(); 27 | int Size(); 28 | bool IsEmpty(); 29 | }; 30 | 31 | #endif //SORTALGORITHM_MINPQ_H 32 | -------------------------------------------------------------------------------- /Algorithms/AlgorithmAndDatastructure/Search/sequential_searchST.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by windyear_office on 18-6-21. 3 | // 4 | 5 | #ifndef SORTALGORITHM_SEQUENTIAL_SEARCHST_H 6 | #define SORTALGORITHM_SEQUENTIAL_SEARCHST_H 7 | #include 8 | #include 9 | 10 | using namespace std; 11 | 12 | // 使用链表实现的一个简单的符号表,使用泛型。每个节点都包含 key 和 value 13 | template 14 | class SequentialSearchST{ 15 | // 定义一个内部节点类 16 | private: 17 | class Node{ 18 | public: 19 | Key key; 20 | Value value; 21 | Node* next; 22 | public: 23 | Node(Key key, Value value, Node* next){ 24 | this->key = key; 25 | this->value = value; 26 | this->next = next; 27 | } 28 | }; 29 | // 链表的头指针 30 | Node* first; 31 | int n; 32 | 33 | public: 34 | // 为了表示没有查找到,只能返回一个值的指针。 35 | Value* get(Key key){ 36 | for(Node* node = first; node != nullptr; node = node->next){ 37 | if(node->key == key){ 38 | return &(node->value); 39 | } 40 | } 41 | return nullptr; 42 | }; 43 | 44 | void put(Key key, Value value){ 45 | // 遍历节点,如果存在key,则覆盖value,否则新增一个节点。 46 | for(Node* node = first; node != nullptr; node = node->next){ 47 | if(node->key == key){ 48 | node->value = value; 49 | return; 50 | } 51 | } 52 | // 如果找不到节点,更新头结点 53 | first = new Node(key, value, first); 54 | n++; 55 | return; 56 | }; 57 | 58 | int size(){ 59 | return n; 60 | }; 61 | // 从开始删除某个节点 62 | void delete_element(Key key){ 63 | if(first->key == key){ 64 | // 保存新的头指针。 65 | Node* head = first->next; 66 | delete first; 67 | first = head; 68 | n--; 69 | return; 70 | } 71 | // 遍历链表。 72 | Node* p = first; 73 | Node* q = first->next; 74 | while(q != nullptr){ 75 | if(q->key == key){ 76 | p->next = q->next; 77 | delete q; 78 | q = p->next; 79 | n--; 80 | return; 81 | } 82 | p = p->next; 83 | q = q->next; 84 | } 85 | std::cout << "The key isn't in the ST." << endl; 86 | return; 87 | }; 88 | // 利用查找函数 89 | bool contains(Key key){ 90 | return get(key) != nullptr; 91 | }; 92 | 93 | // 返回一个关键字的集合 94 | vector keys(){ 95 | vector all_key; 96 | for(Node* node = first; node != nullptr; node = node->next){ 97 | all_key.push_back(node->key); 98 | } 99 | return all_key; 100 | }; 101 | 102 | bool is_empty(){ 103 | return first == nullptr; 104 | }; 105 | 106 | // 构造函数 107 | SequentialSearchST():n(0), first(nullptr){ 108 | 109 | } 110 | // 析构函数需要delete整个链表 111 | ~SequentialSearchST(){ 112 | for(Node* node = first; node != nullptr;){ 113 | Node* to_delete = node; 114 | node = node->next; 115 | delete to_delete; 116 | } 117 | } 118 | }; 119 | #endif //SORTALGORITHM_SEQUENTIAL_SEARCHST_H 120 | -------------------------------------------------------------------------------- /Algorithms/AlgorithmAndDatastructure/Search/test_search.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by windyear_office on 18-9-12. 3 | // 专门用作测试查找表的头文件 4 | // 5 | 6 | #ifndef SORTALGORITHM_TEST_SEARCH_H 7 | #define SORTALGORITHM_TEST_SEARCH_H 8 | #include "sequential_searchST.h" 9 | #include "binary_searchST.h" 10 | #include 11 | #include 12 | #include 13 | 14 | // 简单测试一下基于无序链表的符号表 15 | void test_sqequential_search_ST(string file_name){ 16 | // 改用只能指针比较安全 17 | auto st = make_shared>(); 18 | // SequentialSearchST* st = new SequentialSearchST(); 19 | ifstream input(file_name); 20 | int count = 0; 21 | string input_char; 22 | while(input >> input_char){ 23 | st->put(input_char, count); 24 | count++; 25 | } 26 | cout << "The original ST:" << st->size() << endl; 27 | for(auto node: st->keys()){ 28 | cout << node << " " << *(st->get(node)) << endl; 29 | } 30 | // 测试删除功能 31 | st->delete_element("L"); 32 | st->delete_element("H"); 33 | cout << "Delete key \"L\" and \"H\": " << st->size() << endl; 34 | for(auto node: st->keys()){ 35 | cout << node << " " << *(st->get(node)) << endl; 36 | } 37 | cout << "Is St contains \"X\":" << st->contains("X") << endl; 38 | cout << "Is ST empty?" << st->is_empty() << endl; 39 | // delete st; 40 | } 41 | 42 | // 简单测试一下基于有序数组的符号表,核心是采用了二分查找法。 43 | void test_binary_searchST(string file_name){ 44 | auto st = make_shared >(); 45 | ifstream input(file_name); 46 | int count = 0; 47 | string input_string; 48 | while(input >> input_string){ 49 | st->put(input_string, count); 50 | count++; 51 | } 52 | 53 | // 遍历符号表,应该是有序的。 54 | for(string key: st->all_keys()){ 55 | cout << key << " " << *(st->get(key)) << endl; 56 | } 57 | } 58 | // 用符号表统计单词出现次数的测试程序 59 | void Frequency_Counter(string input_min_len, string filename){ 60 | // 文件读入流 61 | ifstream input(filename); 62 | int min_len = stoi(input_min_len); 63 | SequentialSearchST ST; 64 | // 从文件流中读取所有的单词,直到文件的末尾。 65 | string word; 66 | while(input >> word){ 67 | if(word.size() < min_len){ 68 | continue; 69 | } 70 | // 查找算法非常耗时,因为要遍历链表 71 | if(ST.contains(word)){ 72 | ST.put(word, (*ST.get(word)) + 1); 73 | } 74 | else{ 75 | ST.put(word, 1); 76 | } 77 | } 78 | // 找到出现频率最大的单词 79 | string max = " "; 80 | ST.put(max, 0); 81 | for(auto key: ST.keys()){ 82 | if(*(ST.get(key)) > *(ST.get(max))){ 83 | max = key; 84 | } 85 | 86 | } 87 | cout << "The most frequency word is " << max << " and it appends " << *(ST.get(max)) << " times!" << endl; 88 | } 89 | #endif //SORTALGORITHM_TEST_SEARCH_H 90 | -------------------------------------------------------------------------------- /Algorithms/AlgorithmAndDatastructure/Sort/basesort.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by windyear_office on 17-11-28. 3 | // 4 | /* 5 | * 定义一个所有排序类的基类,包含交换,比较等方法。 6 | * 这里只实现对整型数据的排序,不使用泛型编程。 7 | * 使用泛型编程可以实现处理不容数据类型的数组的排序。 8 | * 排序算法实现从小到大的排序 9 | */ 10 | #ifndef SORTALGORITHM_BASESORT_H 11 | #define SORTALGORITHM_BASESORT_H 12 | #include 13 | using namespace std; 14 | 15 | class Sort{ 16 | public: 17 | //定义两个成员分别表示数组的长度和指向数组的指针。 18 | //不能定义为私有,否则子类访问不到,只能通过公有成员函数访问。 19 | int N; 20 | int* array; 21 | 22 | explicit Sort(int length, int* p_array):N(length), array(p_array){} 23 | ~Sort(){} 24 | bool IsShorted(){ 25 | for(int i = 1; i < N; i++){ 26 | if( array[i] < array[i-1]){ 27 | return false; 28 | } 29 | } 30 | return true; 31 | } 32 | 33 | void Exchange(int p, int q){ 34 | if(p >= N || q >= N){ 35 | cout << "error, out of the array index!" << endl; 36 | } 37 | if(p == q){ 38 | return; 39 | } 40 | int temp; 41 | temp = array[p]; 42 | array[p] = array[q]; 43 | array[q] = temp; 44 | return; 45 | } 46 | 47 | void ShowArray(){ 48 | cout << "the array is :"; 49 | for(int i = 0; i < N; i++){ 50 | cout << array[i] << " "; 51 | } 52 | cout << endl; 53 | } 54 | }; 55 | #endif 56 | -------------------------------------------------------------------------------- /Algorithms/AlgorithmAndDatastructure/Sort/create_test_array.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by windyear_office on 17-11-29. 3 | // 4 | /* 5 | * 每次测试的时候自己输入数组很麻烦 6 | * 于是写一个生成测试数组的类模板,可以生成不同类型的数组 7 | */ 8 | #ifndef SORTALGORITHM_CREATE_TEST_ARRAY_H 9 | #define SORTALGORITHM_CREATE_TEST_ARRAY_H 10 | 11 | #include 12 | #include 13 | template 14 | class CreateTestArray{ 15 | public: 16 | explicit CreateTestArray(){ 17 | //生成的数组的最大值不超过数组长度,采用随机数的方式 18 | sleep(1); 19 | srand((unsigned)time(NULL)); 20 | //长度不能超过你n_mod 21 | int n_mod = length; 22 | if(length == 0){ 23 | return; 24 | } 25 | //生成随机数组 26 | for(int i = 0; i < length; i++){ 27 | test_array[i] = rand() % n_mod; 28 | } 29 | } 30 | //返回数组地址 31 | T* GetArrayAdress(){ 32 | return test_array; 33 | } 34 | //返回数组长度 35 | int GetSize(){ 36 | return length; 37 | } 38 | private: 39 | //一个私有的测试数组 40 | T test_array[length]; 41 | }; 42 | #endif //SORTALGORITHM_CREATE_TEST_ARRAY_H 43 | -------------------------------------------------------------------------------- /Algorithms/AlgorithmAndDatastructure/Sort/insert_sort.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by windyear_office on 17-11-29. 3 | // 4 | /* 5 | * 插入排序主要是遍历数组,然后每个元素都找到它对应应该插入的位置,在对已经排好序的程序进行插入排序的时候可以很快。 6 | * 但是数组的移动操作次数比较多。 7 | */ 8 | 9 | #include "basesort.h" 10 | 11 | class InsertSort: public Sort{ 12 | public: 13 | explicit InsertSort(int length, int* p_array): Sort(length, p_array){} 14 | 15 | //插入排序 16 | void Insert_Sort(){ 17 | for(int i = 0; i < N - 1; i++){ 18 | int insert_index = i + 1; 19 | int temp = array[insert_index]; 20 | for(int j = i; j >= 0; j--){ 21 | //如果还没找到合适位置,元素往后移动一个位置 22 | if(temp < array[j]){ 23 | //特殊处理一下j=0的情况 24 | array[j+1] = array[j]; 25 | if(j == 0){ 26 | array[j] = temp; 27 | } 28 | }else{ 29 | array[j+1] = temp; 30 | //error:这里一开始使用了continue导致某个元素找到应该插入的位置后没有进入 31 | // 到下一个位置的寻找,而是继续替换掉前面的元素。 32 | break; 33 | } 34 | } 35 | } 36 | } 37 | 38 | void Insert_Sort2(){ 39 | //每次都找到元素应该放置的位置 40 | for(int i = 1; i < N; i++){ 41 | for(int j = i; j > 0 && array[j] < array[j-1]; j--){ 42 | Exchange(j, j-1); 43 | } 44 | } 45 | } 46 | }; 47 | -------------------------------------------------------------------------------- /Algorithms/AlgorithmAndDatastructure/Sort/merge_sort.cpp: -------------------------------------------------------------------------------- 1 | // C++ program for Merge Sort 2 | #include 3 | using namespace std; 4 | 5 | // Merges two subarrays of arr[]. 6 | // First subarray is arr[l..m] 7 | // Second subarray is arr[m+1..r] 8 | void merge(int arr[], int l, int m, int r) 9 | { 10 | int n1 = m - l + 1; 11 | int n2 = r - m; 12 | 13 | // Create temp arrays 14 | int L[n1], R[n2]; 15 | 16 | // Copy data to temp arrays L[] and R[] 17 | for(int i = 0; i < n1; i++) 18 | L[i] = arr[l + i]; 19 | for(int j = 0; j < n2; j++) 20 | R[j] = arr[m + 1 + j]; 21 | 22 | // Merge the temp arrays back into arr[l..r] 23 | 24 | // Initial index of first subarray 25 | int i = 0; 26 | 27 | // Initial index of second subarray 28 | int j = 0; 29 | 30 | // Initial index of merged subarray 31 | int k = l; 32 | 33 | while (i < n1 && j < n2) 34 | { 35 | if (L[i] <= R[j]) 36 | { 37 | arr[k] = L[i]; 38 | i++; 39 | } 40 | else 41 | { 42 | arr[k] = R[j]; 43 | j++; 44 | } 45 | k++; 46 | } 47 | 48 | // Copy the remaining elements of 49 | // L[], if there are any 50 | while (i < n1) 51 | { 52 | arr[k] = L[i]; 53 | i++; 54 | k++; 55 | } 56 | 57 | // Copy the remaining elements of 58 | // R[], if there are any 59 | while (j < n2) 60 | { 61 | arr[k] = R[j]; 62 | j++; 63 | k++; 64 | } 65 | } 66 | 67 | // l is for left index and r is 68 | // right index of the sub-array 69 | // of arr to be sorted */ 70 | void mergeSort(int arr[], int l, int r) 71 | { 72 | if (l < r) 73 | { 74 | 75 | // Same as (l+r)/2, but avoids 76 | // overflow for large l and h 77 | int m = l + (r - l) / 2; 78 | 79 | // Sort first and second halves 80 | mergeSort(arr, l, m); 81 | mergeSort(arr, m + 1, r); 82 | 83 | merge(arr, l, m, r); 84 | } 85 | } 86 | 87 | // UTILITY FUNCTIONS 88 | // Function to print an array 89 | void printArray(int A[], int size) 90 | { 91 | for(int i = 0; i < size; i++) 92 | cout << A[i] << " "; 93 | } 94 | 95 | // Driver code 96 | int main() 97 | { 98 | int arr[] = { 12, 11, 13, 5, 6, 7 }; 99 | int arr_size = sizeof(arr) / sizeof(arr[0]); 100 | 101 | cout << "Given array is \n"; 102 | printArray(arr, arr_size); 103 | 104 | mergeSort(arr, 0, arr_size - 1); 105 | 106 | cout << "\nSorted array is \n"; 107 | printArray(arr, arr_size); 108 | return 0; 109 | } 110 | 111 | // This code is contributed by Mayank Tyagi 112 | -------------------------------------------------------------------------------- /Algorithms/AlgorithmAndDatastructure/Sort/merge_sort.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by windyear_office on 17-12-4. 3 | // 4 | 5 | #ifndef SORTALGORITHM_MERGE_SORT_H 6 | #define SORTALGORITHM_MERGE_SORT_H 7 | 8 | #include "basesort.h" 9 | 10 | class MergeSort: public Sort{ 11 | public: 12 | explicit MergeSort(int length, int* p_array):Sort(length, p_array){ 13 | //初始化了指针才能用,全部复制过去 14 | temp_array = new int[length]; 15 | for(int k = 0; k < length; k++){ 16 | temp_array[k] = array[k]; 17 | } 18 | } 19 | 20 | 21 | ~MergeSort(){ 22 | delete[] temp_array; 23 | temp_array = NULL; 24 | } 25 | //合并数组,需要额外使用一个数组空间临时保存。通过四个条件判断应该合并哪一个。 26 | void Merge(int low, int middle, int high){ 27 | int i = low; 28 | int j = middle + 1; 29 | //一个临时保存数组的变量,这里有问题,数组越界。很难察觉。 30 | //所以有一种做法是声明一个跟原来数组一样长的数组变量作为类的成员,这样每次访问临时数组的时候就不会错。 31 | //数组是类的成员,保证了数组不会越界1 32 | //这一步不能缺少,因为每次归并之后数组是局部排好序的,如果不复制过去,每次都会归并原来还没有排序的数组。 33 | for(int k = low; k <= high; k++){ 34 | temp_array[k] = array[k]; 35 | } 36 | //这里很容易数组越界访问。k不能从0开始访问。 37 | for(int k = low; k <= high; k++){ 38 | if(i > middle){ 39 | array[k] = temp_array[j++]; 40 | } 41 | else if(j > high){ 42 | array[k] = temp_array[i++]; 43 | } 44 | else if(temp_array[i] > temp_array[j]){ 45 | array[k] = temp_array[j++]; 46 | } 47 | else{ 48 | array[k] = temp_array[i++]; 49 | } 50 | } 51 | } 52 | 53 | void Merge_Sort(int low, int high){ 54 | //判断什么时候递归就结束,十分重要的递归条件 55 | if(low >= high){ 56 | return; 57 | } 58 | 59 | int middle = (low + high)/2; 60 | Merge_Sort(low, middle); 61 | Merge_Sort(middle + 1, high); 62 | Merge(low, middle, high); 63 | } 64 | //可以有一种自底向上的方法,不同于第一种自顶向下的分治法。 65 | //从两两合并开始,一直循环到最大的两个子数组合并,这里的关键是合并的连个子数组的大小sz,每次大小都翻倍。 66 | void Merge_Sort2(int low, int high){ 67 | //数组长度 68 | int length = N; 69 | for(int sz = 1; sz < N; sz = 2 * sz){ 70 | //这里的数组下标很容易就会乱,要认真对待 71 | //低索引值不能大于length - sz否则数组会越界。 72 | for(int low = 0 ; low < length -sz ; low = low + sz + sz){ 73 | Merge(low, low + sz - 1, ((low + 2 * sz - 1) < (length - 1))?(low + 2 * sz - 1):(length - 1)); 74 | } 75 | } 76 | } 77 | private: 78 | int* temp_array; 79 | }; 80 | #endif //SORTALGORITHM_MERGE_SORT_H 81 | -------------------------------------------------------------------------------- /Algorithms/AlgorithmAndDatastructure/Sort/quick_short.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by windyear_office on 17-12-14. 3 | // 4 | 5 | #ifndef SORTALGORITHM_QUICK_SHORT_H 6 | #define SORTALGORITHM_QUICK_SHORT_H 7 | 8 | #include "basesort.h" 9 | 10 | class QuickSort:public Sort{ 11 | public: 12 | explicit QuickSort(int length, int* p_array):Sort(length, p_array){} 13 | //第一种思路,选取最后一个元素作为参考值,每次都划分出两组元素,然后递归继续快速排序 14 | void Quick_Sort(int low, int high){ 15 | //判断递归结束的条件 16 | if(low >= high){ 17 | return; 18 | }else { 19 | //划分值 20 | int label = array[high]; 21 | int smaller_index = low; 22 | //开始循环遍历数组元素,遇到小于参考值的将其交换到前面 23 | for(int i = low; i < high; i++){ 24 | if(array[i] < label){ 25 | Exchange(i, smaller_index); 26 | smaller_index++; 27 | } 28 | } 29 | //最后交换一下最后的元素到中间 30 | Exchange(high, smaller_index); 31 | //开始递归 32 | //犯了一个大错误,此时smaller_index已经在正确的位置,不用进行排序,否则循环根本不会终止。 33 | Quick_Sort(low, smaller_index - 1); 34 | Quick_Sort(smaller_index + 1, high); 35 | } 36 | } 37 | 38 | //思路二:两边搜索,从两边分别向中间搜索,分别找到大于和小于的元素,交换,指导两个索引大小逆转 39 | void Quick_Sort2(int low, int high){ 40 | if(low >= high){ 41 | return; 42 | }else{ 43 | //先选一个标记 44 | int label = array[low]; 45 | int left = low + 1; 46 | int right = high; 47 | while(true){ 48 | //左边遍历 49 | while(array[left] < label){ 50 | if(left == high){ 51 | break; 52 | } 53 | left++; 54 | } 55 | while(array[right] >= label){ 56 | if(right == low){ 57 | break; 58 | } 59 | right--; 60 | } 61 | //刚开始的时候这里忘记了加一个等于号,导致循环不能终止。 62 | if(left >= right){ 63 | break; 64 | } 65 | Exchange(left, right); 66 | } 67 | Exchange(low, right); 68 | Quick_Sort2(low, right - 1); 69 | Quick_Sort2(right + 1, high); 70 | } 71 | } 72 | }; 73 | #endif //SORTALGORITHM_QUICK_SHORT_H 74 | -------------------------------------------------------------------------------- /Algorithms/AlgorithmAndDatastructure/Sort/select_sort.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by windyear_office on 17-11-28. 3 | // 4 | #ifndef SORTALGORITHM_SELECT_SORT_H 5 | #define SORTALGORITHM_SELECT_SORT_H 6 | #include "basesort.h" 7 | 8 | class SelectSort: public Sort{ 9 | public: 10 | explicit SelectSort(int length, int* p_array):Sort(length, p_array){} 11 | 12 | void Select_Sort(){ 13 | //每次选择一个最小值,然后交换到前面 14 | for(int i = 0; i < N; i++){ 15 | //用一个标记保留最小值的索引,这样就不用每次都操作数组 16 | int min = i; 17 | for(int j = i + 1; j < N; j++){ 18 | if(array[j] < array[min]){ 19 | min = j; 20 | } 21 | } 22 | Exchange(min, i); 23 | } 24 | } 25 | }; 26 | #endif -------------------------------------------------------------------------------- /Algorithms/AlgorithmAndDatastructure/Sort/shell_sort.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by windyear_office on 17-12-1. 3 | // 4 | 5 | #ifndef SORTALGORITHM_SHELL_SORT_H 6 | #define SORTALGORITHM_SHELL_SORT_H 7 | 8 | #include "basesort.h" 9 | 10 | class ShellSort:public Sort{ 11 | public: 12 | explicit ShellSort(int length, int* p_array):Sort(length, p_array){} 13 | void Shell_Sort(){ 14 | //希尔排序是插入排序的优化版本,每次移动的距离大一些,这样面对随机数组的排序时就不用一个一个位置慢慢移动 15 | //初始的间隔值寻求3h+1,然后每次取其三分之一 16 | int h = 1; 17 | while(h < N / 3){ 18 | h = 3 * h + 1; 19 | } 20 | //一直到h=1 21 | while(h > 0){ 22 | for(int i = h; i < N; i++){ 23 | //这里判断从j大于h开始,否则数组会越界。 24 | for(int j = i; j >= h && array[j] < array[j-h]; j = j - h){ 25 | Exchange(j, j - h); 26 | } 27 | } 28 | h = h / 3; 29 | } 30 | } 31 | }; 32 | #endif //SORTALGORITHM_SHELL_SORT_H 33 | -------------------------------------------------------------------------------- /Algorithms/AlgorithmAndDatastructure/Union/QuickUnion.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by windyear_office on 18-2-6. 3 | // 4 | 5 | #include "QuickUnion.h" 6 | 7 | QuickUnion::QuickUnion(int N):klength(N),count(N){ 8 | id = new int[klength]; 9 | for(int i = 0; i < klength; i++){ 10 | id[i] = i; 11 | } 12 | } 13 | 14 | QuickUnion::~QuickUnion(){ 15 | delete[] id; 16 | } 17 | 18 | int QuickUnion::Count(){ 19 | return count; 20 | } 21 | void QuickUnion::Union(int p, int q){ 22 | int proot = Root(p); 23 | int qroot = Root(q); 24 | if(proot == qroot){ 25 | return; 26 | } 27 | id[proot] = qroot; 28 | count--; 29 | return ; 30 | } 31 | 32 | bool QuickUnion::IsConnected(int p, int q){ 33 | return Root(p) == Root(q); 34 | } 35 | 36 | int QuickUnion::Root(int number){ 37 | //这里一开始用了if是错的,应该是不断循环 38 | while(id[number] != number){ 39 | number = id[number]; 40 | } 41 | return number; 42 | } -------------------------------------------------------------------------------- /Algorithms/AlgorithmAndDatastructure/Union/QuickUnion.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by windyear_office on 18-2-6. 3 | // 4 | 5 | #ifndef SORTALGORITHM_QUICKUNION_H 6 | #define SORTALGORITHM_QUICKUNION_H 7 | 8 | /* 9 | author: windyear 10 | time: 2017-11-26 11 | 思路: 12 | 快速连接,之前的方法需要遍历数组且改变很多元素的值,使用树的结构来表示元素的关系, 13 | 树连接时可以直接连接根节点即可。 14 | */ 15 | class QuickUnion { 16 | public: 17 | QuickUnion(int N); 18 | ~QuickUnion(); 19 | void Union(int p, int q); 20 | bool IsConnected(int p, int q); 21 | int Root(int number); 22 | int Count(); 23 | private: 24 | const int klength; 25 | int* id; 26 | int count; 27 | }; 28 | 29 | 30 | #endif //SORTALGORITHM_QUICKUNION_H 31 | -------------------------------------------------------------------------------- /Algorithms/AlgorithmAndDatastructure/Union/UnionFind.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by windyear_office on 18-2-6. 3 | // 4 | 5 | #include "UnionFind.h" 6 | 7 | UnionFind::UnionFind(int N):klength(N){ 8 | id = new int[N]; 9 | for(int i = 0; i < N; i++){ 10 | id[i] = i; 11 | } 12 | } 13 | 14 | UnionFind::~UnionFind(){ 15 | delete[] id; 16 | } 17 | 18 | void UnionFind::Union(int p, int q){ 19 | //必须先用两个变量保留原来的id对应的值,不然之后id[p]之后会变成id[q] 20 | int pid = id[p]; 21 | int qid = id[q]; 22 | for(int i = 0; i < klength; i++){ 23 | if(id[i] == pid){ 24 | id[i] = qid; 25 | } 26 | } 27 | } 28 | 29 | bool UnionFind::IsConnected(int p, int q){ 30 | return id[p] == id[q]; 31 | } 32 | -------------------------------------------------------------------------------- /Algorithms/AlgorithmAndDatastructure/Union/UnionFind.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by windyear_office on 18-2-6. 3 | // 4 | 5 | #ifndef SORTALGORITHM_UNIONFIND_H 6 | #define SORTALGORITHM_UNIONFIND_H 7 | 8 | //思路: 9 | //一个数组的索引当做是数字,数组的元素值相同代表连接在一起。 10 | //主要有两个操作,一个是连接,遍历数组,所有连接到一起的元素连到新元素上,即值等于新元素的值。 11 | //是否相等就判断元素值是否相等即可。 12 | //注意动态数组用指针表示,在析构函数进行delete,否则内存泄漏。 13 | class UnionFind { 14 | public: 15 | UnionFind(int N); 16 | ~UnionFind(); 17 | void Union(int p, int q); 18 | bool IsConnected(int p, int q); 19 | private: 20 | //代表数字id 21 | //要使用一个动态数组,所以可以定义一个指针变量,然后数组的长度使用常量表示 22 | int* id; 23 | const int klength; 24 | }; 25 | 26 | 27 | #endif //SORTALGORITHM_UNIONFIND_H 28 | -------------------------------------------------------------------------------- /Algorithms/AlgorithmAndDatastructure/Union/WeightedQuickUnion.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by windyear_office on 18-2-6. 3 | // 4 | 5 | #include "WeightedQuickUnion.h" 6 | 7 | WeightedQuickUnion::WeightedQuickUnion(int N):klength(N),count(N){ 8 | id = new int[klength]; 9 | sizeoftree = new int[klength]; 10 | for(int i = 0; i < klength; i++){ 11 | id[i] =i; 12 | sizeoftree[i] = 1; 13 | } 14 | } 15 | 16 | WeightedQuickUnion::~WeightedQuickUnion(){ 17 | delete[] id; 18 | delete[] sizeoftree; 19 | } 20 | 21 | void WeightedQuickUnion::Union(int p, int q){ 22 | int proot = Root(p); 23 | int qroot = Root(q); 24 | if( proot == qroot){ 25 | return; 26 | } 27 | //比较两棵树的节点数,节点少的连接到多的那边去,然后相应的树节点总数相加。 28 | if(sizeoftree[proot] <= sizeoftree[qroot]){ 29 | id[proot] = q; 30 | sizeoftree[q] += sizeoftree[p]; 31 | }else{ 32 | id[qroot] = p; 33 | sizeoftree[p] += sizeoftree[q]; 34 | } 35 | count--; 36 | } 37 | 38 | int WeightedQuickUnion::Root(int number){ 39 | //这里一开始用了if是错的,应该是不断循环 40 | while(id[number] != number){ 41 | number = id[number]; 42 | } 43 | return number; 44 | } 45 | 46 | bool WeightedQuickUnion::IsConnected(int p, int q){ 47 | return Root(p) == Root(q); 48 | } 49 | 50 | int WeightedQuickUnion::Count(){ 51 | return count; 52 | } 53 | -------------------------------------------------------------------------------- /Algorithms/AlgorithmAndDatastructure/Union/WeightedQuickUnion.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by windyear_office on 18-2-6. 3 | // 4 | 5 | #ifndef SORTALGORITHM_WEIGHTEDQUICKUNION_H 6 | #define SORTALGORITHM_WEIGHTEDQUICKUNION_H 7 | 8 | /* 9 | author: windyear 10 | time: 2017-11-26 11 | 思路描述: 12 | 优化思路是减少每次查找树的根节点的路径长度,之前的方法是固定某一棵树连接到另一棵树上。 13 | 而每次都将树的节点少的一方连接到多的一方,可以使树的高度限制在lgN,因为节点少的一棵树的节点的高度加一,节点数目至少翻倍,而一棵树至多翻倍lgN次。 14 | 这样就可以优化寻根的路径长度。变成对数级别。 15 | */ 16 | 17 | class WeightedQuickUnion { 18 | public: 19 | WeightedQuickUnion(int N); 20 | ~WeightedQuickUnion(); 21 | void Union(int p, int q); 22 | int Root(int number); 23 | bool IsConnected(int p, int q); 24 | int Count(); 25 | private: 26 | const int klength; 27 | int* id; 28 | int* sizeoftree; 29 | int count; 30 | }; 31 | 32 | 33 | #endif //SORTALGORITHM_WEIGHTEDQUICKUNION_H 34 | -------------------------------------------------------------------------------- /Algorithms/couresa_union_find/main.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | author: windyear 3 | time: 2017-11-26 4 | */ 5 | #include 6 | #include "union_find.h" 7 | #include "quick_union.h" 8 | #include "weighted_quick_union.h" 9 | using namespace std; 10 | 11 | int main() 12 | { 13 | /*cout << "Hello world!" << endl; 14 | cout << "Test UnionFind:" << endl; 15 | UnionFind test_unionfind(10); 16 | test_unionfind.Union(4, 3); 17 | test_unionfind.Union(3, 8); 18 | test_unionfind.Union(6, 5); 19 | test_unionfind.Union(9, 4); 20 | test_unionfind.Union(2, 1); 21 | cout << "Is 8 and 9 connected?" <> N; 44 | QuickUnion testquickunion(N); 45 | int p, q; 46 | while(cin >> p >> q){ 47 | if(testquickunion.IsConnected(p, q)){ 48 | continue; 49 | } 50 | testquickunion.Union(p, q); 51 | cout << p << " " << q << endl; 52 | } 53 | cout << "components: " << testquickunion.Count() << endl;*/ 54 |     //优化之后十几秒就可以跑出来 55 | int N; 56 | cin >> N; 57 | WeightedQuickUnion testweightedquickunion(N); 58 | int p, q; 59 | while(cin >> p >> q){ 60 | if(testweightedquickunion.IsConnected(p, q)){ 61 | continue; 62 | } 63 | testweightedquickunion.Union(p, q); 64 | cout << p << " " << q << endl; 65 | } 66 | cout << "components: " << testweightedquickunion.Count() << endl; 67 | return 0; 68 | } 69 | -------------------------------------------------------------------------------- /Algorithms/couresa_union_find/quick_union.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | author: windyear 3 | time: 2017-11-26 4 | */ 5 | #include "quick_union.h" 6 | 7 | QuickUnion::QuickUnion(int N):klength(N),count(N){ 8 | id = new int[klength]; 9 | for(int i = 0; i < klength; i++){ 10 | id[i] = i; 11 | } 12 | } 13 | 14 | QuickUnion::~QuickUnion(){ 15 | delete[] id; 16 | } 17 | 18 | int QuickUnion::Count(){ 19 | return count; 20 | } 21 | void QuickUnion::Union(int p, int q){ 22 | int proot = Root(p); 23 | int qroot = Root(q); 24 | if(proot == qroot){ 25 | return; 26 | } 27 | id[proot] = qroot; 28 | count--; 29 | return ; 30 | } 31 | 32 | bool QuickUnion::IsConnected(int p, int q){ 33 | return Root(p) == Root(q); 34 | } 35 | 36 | int QuickUnion::Root(int number){ 37 | //这里一开始用了if是错的,应该是不断循环 38 | while(id[number] != number){ 39 | number = id[number]; 40 | } 41 | return number; 42 | } 43 | -------------------------------------------------------------------------------- /Algorithms/couresa_union_find/quick_union.h: -------------------------------------------------------------------------------- 1 | /* 2 | author: windyear 3 | time: 2017-11-26 4 | 思路: 5 | 快速连接,之前的方法需要遍历数组且改变很多元素的值,使用树的结构来表示元素的关系, 6 | 树连接时可以直接连接根节点即可。 7 | */ 8 | 9 | #ifndef QUICK_UNION_H_INCLUDED 10 | #define QUICK_UNION_H_INCLUDED 11 | class QuickUnion{ 12 | public: 13 | QuickUnion(int N); 14 | ~QuickUnion(); 15 | void Union(int p, int q); 16 | bool IsConnected(int p, int q); 17 | int Root(int number); 18 | int Count(); 19 | private: 20 | const int klength; 21 | int* id; 22 | int count; 23 | }; 24 | 25 | 26 | #endif // QUICK_UNION_H_INCLUDED 27 | -------------------------------------------------------------------------------- /Algorithms/couresa_union_find/union_find.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | author: windyear 3 | time: 2017-11-26 4 | */ 5 | #include "union_find.h" 6 | 7 | UnionFind::UnionFind(int N):klength(N){ 8 | id = new int[N]; 9 | for(int i = 0; i < N; i++){ 10 | id[i] = i; 11 | } 12 | } 13 | 14 | UnionFind::~UnionFind(){ 15 | delete[] id; 16 | } 17 | 18 | void UnionFind::Union(int p, int q){ 19 | //必须先用两个变量保留原来的id对应的值,不然之后id[p]之后会变成id[q] 20 | int pid = id[p]; 21 | int qid = id[q]; 22 | for(int i = 0; i < klength; i++){ 23 | if(id[i] == pid){ 24 | id[i] = qid; 25 | } 26 | } 27 | } 28 | 29 | bool UnionFind::IsConnected(int p, int q){ 30 | return id[p] == id[q]; 31 | } 32 | 33 | -------------------------------------------------------------------------------- /Algorithms/couresa_union_find/union_find.h: -------------------------------------------------------------------------------- 1 | /* 2 | author: windyear 3 | time: 2017-11-26 4 | 思路: 5 | 一个数组的索引当做是数字,数组的元素值相同代表连接在一起。 6 | 主要有两个操作,一个是连接,遍历数组,所有连接到一起的元素连到新元素上,即值等于新元素的值。 7 | 是否相等就判断元素值是否相等即可。 8 | 注意动态数组用指针表示,在析构函数进行delete,否则内存泄漏。 9 | */ 10 | #ifndef UNION_FIND_H_INCLUDED 11 | #define UNION_FIND_H_INCLUDED 12 | 13 | class UnionFind{ 14 | public: 15 | UnionFind(int N); 16 | ~UnionFind(); 17 | void Union(int p, int q); 18 | bool IsConnected(int p, int q); 19 | private: 20 | //代表数字id 21 | //要使用一个动态数组,所以可以定义一个指针变量,然后数组的长度使用常量表示 22 | int* id; 23 | const int klength; 24 | }; 25 | 26 | 27 | #endif // UNION_FIND_H_INCLUDED 28 | -------------------------------------------------------------------------------- /Algorithms/couresa_union_find/weighted_quick_union.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | author: windyear 3 | time: 2017-11-26 4 | */ 5 | #include "weighted_quick_union.h" 6 | 7 | WeightedQuickUnion::WeightedQuickUnion(int N):klength(N),count(N){ 8 | id = new int[klength]; 9 | sizeoftree = new int[klength]; 10 | for(int i = 0; i < klength; i++){ 11 | id[i] =i; 12 | sizeoftree[i] = 1; 13 | } 14 | } 15 | 16 | WeightedQuickUnion::~WeightedQuickUnion(){ 17 | delete[] id; 18 | delete[] sizeoftree; 19 | } 20 | 21 | void WeightedQuickUnion::Union(int p, int q){ 22 | int proot = Root(p); 23 | int qroot = Root(q); 24 | if( proot == qroot){ 25 | return; 26 | } 27 | //比较两棵树的节点数,节点少的连接到多的那边去,然后相应的树节点总数相加。 28 | if(sizeoftree[proot] <= sizeoftree[qroot]){ 29 | id[proot] = q; 30 | sizeoftree[q] += sizeoftree[p]; 31 | }else{ 32 | id[qroot] = p; 33 | sizeoftree[p] += sizeoftree[q]; 34 | } 35 | count--; 36 | } 37 | 38 | int WeightedQuickUnion::Root(int number){ 39 | //这里一开始用了if是错的,应该是不断循环 40 | while(id[number] != number){ 41 | number = id[number]; 42 | } 43 | return number; 44 | } 45 | 46 | bool WeightedQuickUnion::IsConnected(int p, int q){ 47 | return Root(p) == Root(q); 48 | } 49 | 50 | int WeightedQuickUnion::Count(){ 51 | return count; 52 | } 53 | -------------------------------------------------------------------------------- /Algorithms/couresa_union_find/weighted_quick_union.h: -------------------------------------------------------------------------------- 1 | /* 2 | author: windyear 3 | time: 2017-11-26 4 | 思路描述: 5 | 优化思路是减少每次查找树的根节点的路径长度,之前的方法是固定某一棵树连接到另一棵树上。 6 | 而每次都将树的节点少的一方连接到多的一方,可以使树的高度限制在lgN,因为节点少的一棵树的节点的高度加一,节点数目至少翻倍,而一棵树至多翻倍lgN次。 7 | 这样就可以优化寻根的路径长度。变成对数级别。 8 | */ 9 | #ifndef WEIGHTED_QUICK_UNION_H_INCLUDED 10 | #define WEIGHTED_QUICK_UNION_H_INCLUDED 11 | 12 | class WeightedQuickUnion{ 13 | public: 14 | WeightedQuickUnion(int N); 15 | ~WeightedQuickUnion(); 16 | void Union(int p, int q); 17 | int Root(int number); 18 | bool IsConnected(int p, int q); 19 | int Count(); 20 | private: 21 | const int klength; 22 | int* id; 23 | int* sizeoftree; 24 | int count; 25 | }; 26 | 27 | 28 | #endif // WEIGHTED_QUICK_UNION_H_INCLUDED 29 | -------------------------------------------------------------------------------- /ClassicAlgorithm/Dijkstra/main.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windyear/AlgorithmsAndDataStructure/ebc6f4f3e055f7400a67fd578a3e8744b8fb2b40/ClassicAlgorithm/Dijkstra/main.cpp -------------------------------------------------------------------------------- /ClassicAlgorithm/dynamic_programming/main.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windyear/AlgorithmsAndDataStructure/ebc6f4f3e055f7400a67fd578a3e8744b8fb2b40/ClassicAlgorithm/dynamic_programming/main.cpp -------------------------------------------------------------------------------- /ClassicAlgorithm/simulated_annealing/main.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windyear/AlgorithmsAndDataStructure/ebc6f4f3e055f7400a67fd578a3e8744b8fb2b40/ClassicAlgorithm/simulated_annealing/main.cpp -------------------------------------------------------------------------------- /HeadFirstDesignPatterns/decorator/beverage.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by windyear_office on 18-5-7. 3 | // 4 | 5 | #ifndef HEADFIRSTDESIGNPATTERNS_BEVERAGE_H 6 | #define HEADFIRSTDESIGNPATTERNS_BEVERAGE_H 7 | 8 | #include 9 | 10 | class Beverage{ 11 | public: 12 | std::string description; 13 | explicit Beverage(std::string s1 = "Unknown Beverage"){ 14 | 15 | } 16 | // 不加virtual实现不了多态 17 | virtual std::string GetDescription(){ 18 | return description; 19 | } 20 | virtual double Cost() = 0; 21 | }; 22 | #endif //HEADFIRSTDESIGNPATTERNS_BEVERAGE_H 23 | -------------------------------------------------------------------------------- /HeadFirstDesignPatterns/decorator/condiment_decorator.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by windyear_office on 18-5-7. 3 | // 4 | 5 | #ifndef HEADFIRSTDESIGNPATTERNS_CONDIMENT_DECORATOR_H 6 | #define HEADFIRSTDESIGNPATTERNS_CONDIMENT_DECORATOR_H 7 | 8 | #include "beverage.h" 9 | 10 | class CondimentDecorator: public Beverage{ 11 | public: 12 | std::string GetDescription(){}; 13 | }; 14 | #endif //HEADFIRSTDESIGNPATTERNS_CONDIMENT_DECORATOR_H 15 | -------------------------------------------------------------------------------- /HeadFirstDesignPatterns/decorator/dark_roast.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by windyear_office on 18-5-7. 3 | // 4 | 5 | #ifndef HEADFIRSTDESIGNPATTERNS_DARK_ROAST_H 6 | #define HEADFIRSTDESIGNPATTERNS_DARK_ROAST_H 7 | 8 | #include "beverage.h" 9 | 10 | class DarkRoast: public Beverage{ 11 | public: 12 | DarkRoast(){ 13 | description = "Dark Roast"; 14 | } 15 | 16 | double Cost() override{ 17 | return 0.99; 18 | } 19 | }; 20 | #endif //HEADFIRSTDESIGNPATTERNS_DARK_ROAST_H 21 | -------------------------------------------------------------------------------- /HeadFirstDesignPatterns/decorator/espresso.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by windyear_office on 18-5-7. 3 | // 4 | 5 | #ifndef HEADFIRSTDESIGNPATTERNS_ESPRESSO_H 6 | #define HEADFIRSTDESIGNPATTERNS_ESPRESSO_H 7 | 8 | #include "beverage.h" 9 | 10 | class Espresso: public Beverage{ 11 | public: 12 | Espresso(){ 13 | description = "Espresso"; 14 | } 15 | 16 | double Cost() override { 17 | return 1.99; 18 | } 19 | }; 20 | #endif //HEADFIRSTDESIGNPATTERNS_ESPRESSO_H 21 | -------------------------------------------------------------------------------- /HeadFirstDesignPatterns/decorator/houseblend.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by windyear_office on 18-5-7. 3 | // 4 | 5 | #ifndef HEADFIRSTDESIGNPATTERNS_HOUSEBLEND_H 6 | #define HEADFIRSTDESIGNPATTERNS_HOUSEBLEND_H 7 | 8 | #include "beverage.h" 9 | 10 | class HouseBlend: public Beverage{ 11 | public: 12 | HouseBlend(){ 13 | description = "House Blend Coffee"; 14 | } 15 | 16 | double Cost() override { 17 | return 0.99; 18 | } 19 | }; 20 | #endif //HEADFIRSTDESIGNPATTERNS_HOUSEBLEND_H 21 | -------------------------------------------------------------------------------- /HeadFirstDesignPatterns/decorator/mocha.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by windyear_office on 18-5-7. 3 | // 4 | 5 | #ifndef HEADFIRSTDESIGNPATTERNS_MOCHA_H 6 | #define HEADFIRSTDESIGNPATTERNS_MOCHA_H 7 | 8 | #include "condiment_decorator.h" 9 | 10 | class Mocha: public CondimentDecorator{ 11 | public: 12 | Beverage* beverage; 13 | explicit Mocha(Beverage* b1): beverage(b1){ 14 | 15 | } 16 | std::string GetDescription(){ 17 | return beverage->GetDescription() + ", Mocha"; 18 | } 19 | double Cost() override { 20 | return 0.20 + beverage->Cost(); 21 | } 22 | }; 23 | #endif //HEADFIRSTDESIGNPATTERNS_MOCHA_H 24 | -------------------------------------------------------------------------------- /HeadFirstDesignPatterns/decorator/soy.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by windyear_office on 18-5-7. 3 | // 4 | 5 | #ifndef HEADFIRSTDESIGNPATTERNS_SOY_H 6 | #define HEADFIRSTDESIGNPATTERNS_SOY_H 7 | 8 | #include "condiment_decorator.h" 9 | 10 | class Soy: public CondimentDecorator{ 11 | public: 12 | Beverage* beverage; 13 | explicit Soy(Beverage* b1): beverage(b1){ 14 | 15 | } 16 | 17 | std::string GetDescription() override { 18 | return beverage->GetDescription() + ", soy"; 19 | } 20 | 21 | double Cost() override { 22 | return 0.15 + beverage->Cost(); 23 | } 24 | }; 25 | #endif //HEADFIRSTDESIGNPATTERNS_SOY_H 26 | -------------------------------------------------------------------------------- /HeadFirstDesignPatterns/decorator/whip.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by windyear_office on 18-5-7. 3 | // 4 | 5 | #ifndef HEADFIRSTDESIGNPATTERNS_WHIP_H 6 | #define HEADFIRSTDESIGNPATTERNS_WHIP_H 7 | 8 | #include "condiment_decorator.h" 9 | 10 | class Whip: public CondimentDecorator{ 11 | public: 12 | Beverage* beverage; 13 | explicit Whip(Beverage* b1): beverage(b1){ 14 | 15 | } 16 | 17 | std::string GetDescription() override { 18 | return beverage->GetDescription() + ", whip"; 19 | } 20 | 21 | double Cost() override { 22 | return 0.10 + beverage->Cost(); 23 | } 24 | }; 25 | #endif //HEADFIRSTDESIGNPATTERNS_WHIP_H 26 | -------------------------------------------------------------------------------- /HeadFirstDesignPatterns/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "decorator/beverage.h" 3 | #include "decorator/espresso.h" 4 | #include "decorator/dark_roast.h" 5 | #include "decorator/mocha.h" 6 | #include "decorator/soy.h" 7 | #include "strategy/Duck.h" 8 | #include "strategy/mallard_duck.h" 9 | 10 | // 测试装饰者模式 11 | void TestDecorator(){ 12 | std::cout << "Test decorator!" << std::endl; 13 | //先订一杯基础饮料,不带调料 14 | Beverage* espresso = new Espresso(); 15 | std::cout << espresso->GetDescription() << " $" << espresso->Cost() << std::endl; 16 | delete espresso; 17 | 18 | // 订一杯有加调料的饮料 19 | // 深焙咖啡 20 | Beverage* dark_roast = new DarkRoast(); 21 | std::cout << dark_roast->GetDescription() << " $" << dark_roast->Cost() << std::endl; 22 | // 加摩卡的深焙咖啡 23 | Beverage* mocha_dark_roast = new Mocha(dark_roast); 24 | std::cout << mocha_dark_roast->GetDescription() << " $" << mocha_dark_roast->Cost() << std::endl; 25 | // 再加豆浆 26 | Beverage* soy_mocha_dark_roast = new Soy(mocha_dark_roast); 27 | std::cout << soy_mocha_dark_roast->GetDescription() << " $" << soy_mocha_dark_roast->Cost() << std::endl; 28 | delete soy_mocha_dark_roast; 29 | delete mocha_dark_roast; 30 | delete dark_roast; 31 | } 32 | 33 | // 测试策略模式 34 | void TestStrategy(){ 35 | Duck* mallard = new MallardDuck(); 36 | mallard->PerformFly(); 37 | mallard->PerformQuack(); 38 | FlyBehavior* fb = new FlyWithWings(); 39 | mallard->SetFlyBehavior(fb); 40 | mallard->PerformFly(); 41 | delete mallard; 42 | } 43 | 44 | int main() { 45 | // TestDecorator(); 46 | TestStrategy(); 47 | return 0; 48 | } 49 | -------------------------------------------------------------------------------- /HeadFirstDesignPatterns/strategy/Duck.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by windyear_office on 18-4-5. 3 | // 4 | 5 | #ifndef HEADFIRSTDESIGNPATTERNS_DUCK_H 6 | #define HEADFIRSTDESIGNPATTERNS_DUCK_H 7 | 8 | #include 9 | #include "fly_behavior.h" 10 | #include "quack_behavior.h" 11 | 12 | class Duck{ 13 | public: 14 | // 指向行为类型的指针,用于实现多态。 15 | FlyBehavior* fly_behavior; 16 | QuackBehavior* quack_behavior; 17 | // 鸭子的行为 18 | void PerformQuack(){ 19 | quack_behavior->Quack_(); 20 | } 21 | void PerformFly(){ 22 | fly_behavior->Fly(); 23 | } 24 | void Swim(){ 25 | std::cout << "I can swim! All ducks float, even decoys." << std::endl; 26 | } 27 | void Display(){ 28 | std::cout << "I can display!" << std::endl; 29 | } 30 | void SetFlyBehavior(FlyBehavior* fb) { 31 | delete fly_behavior; 32 | fly_behavior = fb; 33 | } 34 | 35 | void SetQuackBehavior(QuackBehavior* qb){ 36 | delete quack_behavior; 37 | quack_behavior = qb; 38 | } 39 | virtual ~Duck(){ 40 | 41 | } 42 | }; 43 | #endif //HEADFIRSTDESIGNPATTERNS_DUCK_H 44 | -------------------------------------------------------------------------------- /HeadFirstDesignPatterns/strategy/fly_behavior.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by windyear_office on 18-4-5. 3 | // 4 | 5 | #ifndef HEADFIRSTDESIGNPATTERNS_FLYBEHAVIOR_H 6 | #define HEADFIRSTDESIGNPATTERNS_FLYBEHAVIOR_H 7 | 8 | //一个算法抽象类,把会变化的部分分离出来 9 | class FlyBehavior { 10 | public: 11 | virtual void Fly() = 0; 12 | }; 13 | 14 | 15 | #endif //HEADFIRSTDESIGNPATTERNS_FLYBEHAVIOR_H 16 | -------------------------------------------------------------------------------- /HeadFirstDesignPatterns/strategy/fly_no_way.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by windyear_office on 18-4-5. 3 | // 4 | 5 | #ifndef HEADFIRSTDESIGNPATTERNS_FLY_NO_WAY_H 6 | #define HEADFIRSTDESIGNPATTERNS_FLY_NO_WAY_H 7 | 8 | #include 9 | #include "fly_behavior.h" 10 | 11 | class FlyNoWay: public FlyBehavior{ 12 | public: 13 | void Fly(){ 14 | std::cout << "I can't fly! I am very foolish!" << std::endl; 15 | } 16 | }; 17 | 18 | #endif //HEADFIRSTDESIGNPATTERNS_FLY_NO_WAY_H 19 | -------------------------------------------------------------------------------- /HeadFirstDesignPatterns/strategy/fly_with_wings.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by windyear_office on 18-4-5. 3 | // 4 | 5 | #ifndef HEADFIRSTDESIGNPATTERNS_FLY_WITH_WINGS_H 6 | #define HEADFIRSTDESIGNPATTERNS_FLY_WITH_WINGS_H 7 | 8 | #include 9 | #include "fly_behavior.h" 10 | 11 | class FlyWithWings: public FlyBehavior{ 12 | public: 13 | void Fly(){ 14 | std::cout << "I can fly with wings! I am very happy!" << std::endl; 15 | } 16 | }; 17 | #endif //HEADFIRSTDESIGNPATTERNS_FLY_WITH_WINGS_H 18 | -------------------------------------------------------------------------------- /HeadFirstDesignPatterns/strategy/mallard_duck.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by windyear_office on 18-5-23. 3 | // 4 | 5 | #ifndef HEADFIRSTDESIGNPATTERNS_MALLARD_DUCK_H 6 | #define HEADFIRSTDESIGNPATTERNS_MALLARD_DUCK_H 7 | 8 | #include "Duck.h" 9 | #include "quack.h" 10 | #include "fly_with_wings.h" 11 | #include "fly_no_way.h" 12 | 13 | class MallardDuck: public Duck{ 14 | public: 15 | MallardDuck(){ 16 | quack_behavior = new Quack(); 17 | // 这里用于测试不同的飞行类型 18 | // fly_behavior = new FlyWithWings(); 19 | fly_behavior = new FlyNoWay(); 20 | } 21 | 22 | void Display(){ 23 | std::cout << "I am a real Mallard duck!" << std::endl; 24 | } 25 | 26 | ~MallardDuck(){ 27 | delete quack_behavior; 28 | delete fly_behavior; 29 | } 30 | }; 31 | #endif //HEADFIRSTDESIGNPATTERNS_MALLARD_DUCK_H 32 | -------------------------------------------------------------------------------- /HeadFirstDesignPatterns/strategy/mute_quack.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by windyear_office on 18-4-5. 3 | // 4 | 5 | #ifndef HEADFIRSTDESIGNPATTERNS_MUTE_QUACK_H 6 | #define HEADFIRSTDESIGNPATTERNS_MUTE_QUACK_H 7 | 8 | #include 9 | #include "quack_behavior.h" 10 | 11 | class MuteQuack: public QuackBehavior{ 12 | public: 13 | void Quack_(){ 14 | std::cout << "Mute...Mute...Mute..." << std::endl; 15 | } 16 | }; 17 | #endif //HEADFIRSTDESIGNPATTERNS_MUTE_QUACK_H 18 | -------------------------------------------------------------------------------- /HeadFirstDesignPatterns/strategy/quack.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by windyear_office on 18-4-5. 3 | // 4 | 5 | #ifndef HEADFIRSTDESIGNPATTERNS_QUACK_H 6 | #define HEADFIRSTDESIGNPATTERNS_QUACK_H 7 | 8 | #include 9 | #include "quack_behavior.h" 10 | 11 | class Quack: public QuackBehavior{ 12 | public: 13 | void Quack_(){ 14 | std::cout << "Quack...Quack...Quack..." << std::endl; 15 | } 16 | }; 17 | #endif //HEADFIRSTDESIGNPATTERNS_QUACK_H 18 | -------------------------------------------------------------------------------- /HeadFirstDesignPatterns/strategy/quack_behavior.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by windyear_office on 18-4-5. 3 | // 4 | 5 | #ifndef HEADFIRSTDESIGNPATTERNS_QUACK_BEHAVIOR_H 6 | #define HEADFIRSTDESIGNPATTERNS_QUACK_BEHAVIOR_H 7 | 8 | class QuackBehavior{ 9 | public: 10 | virtual void Quack_() = 0; 11 | }; 12 | #endif //HEADFIRSTDESIGNPATTERNS_QUACK_BEHAVIOR_H 13 | -------------------------------------------------------------------------------- /HeadFirstDesignPatterns/strategy/readme.md: -------------------------------------------------------------------------------- 1 | ### 策略模式 2 | 1. 设计原则:找出可变化支出,独立出来,不要将不需要变化和需要变化的代码混在一起。 3 | 2. 针对接口编程,而不是针对实现编程。 4 | 3. 策略模式通过将一系列行为抽出来,称为算法簇。算法簇之间可以互相替换,让算法的变化独立于使用算法的客户。 5 | 4. 设计原则: 多用组合,少用继承。 6 | -------------------------------------------------------------------------------- /HeadFirstDesignPatterns/strategy/squeak.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by windyear_office on 18-4-5. 3 | // 4 | 5 | #ifndef HEADFIRSTDESIGNPATTERNS_SQUEAK_H 6 | #define HEADFIRSTDESIGNPATTERNS_SQUEAK_H 7 | 8 | #include 9 | #include "quack_behavior.h" 10 | 11 | class Squeak: public QuackBehavior{ 12 | public: 13 | void Quack_(){ 14 | std::cout << "Squeak...Squeak...Squeak..." << std::endl; 15 | } 16 | }; 17 | #endif //HEADFIRSTDESIGNPATTERNS_SQUEAK_H 18 | -------------------------------------------------------------------------------- /IPC/OneInstanceFileLock.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by windyear_office on 19-2-28. 3 | // 一个用文件锁实现进程只能打开单个实例的程序。 4 | #include // fcntl sturct flock 5 | #include // exit 6 | #include // printf 7 | #include // close 8 | #include // errno 9 | #include // strlen 10 | #define lockfile "OneInstance.pid" 11 | int LockFile(int fd){ 12 | // flock结构体表明锁的一些设置 13 | struct flock f1; 14 | f1.l_type = F_WRLCK; // 设置为唯一写锁,如果是同一个进程不会失败,会替换原来的锁。 15 | f1.l_start = 0; 16 | f1.l_whence = SEEK_SET; 17 | f1.l_len = 0; // 设置锁的大小为为整个文件 18 | return fcntl(fd, F_SETLK, &f1); //出错会返回,errno为EACCES或者EAGAIN。 19 | } 20 | 21 | int main(){ 22 | int fd; 23 | fd = open(lockfile, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); 24 | if(fd < 0){ 25 | printf("can't open the lock file.\n"); 26 | exit(1); 27 | } 28 | // 进行加锁操作 29 | if(LockFile(fd) < 0){ 30 | if(errno == EACCES || errno == EAGAIN){ 31 | printf("The process only can have one instance.\n"); 32 | close(fd); 33 | return 1; 34 | } 35 | printf("can't lock.\n"); 36 | exit(-1); 37 | } 38 | 39 | // 截断文件 40 | ftruncate(fd, 0); 41 | char buf[32]; 42 | sprintf(buf, "%ld", (long)getpid()); 43 | write(fd, buf, strlen(buf)+1); 44 | while(1){ 45 | printf("It's the one instance process.\n"); 46 | sleep(1); 47 | } 48 | return 0; 49 | } 50 | -------------------------------------------------------------------------------- /IPC/POSIXSem_r.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by windyear_office on 19-2-26. 3 | // 4 | // 一个配合共享内存实现进程间同步的Posix信号量demo 5 | #define SHM_KEY 1111 6 | #define SEM_NAME "/TestSem" 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | int main(){ 15 | // 打开一段共享内存。 16 | int shm_id = shmget(SHM_KEY, sizeof(int), 0666 | IPC_CREAT); 17 | // 判断是否打开成功 18 | if( shm_id < 0){ 19 | printf("fail to create share memory.\n"); 20 | return -1; 21 | } 22 | // 连接共享内存到进程空间,注意第二个参数一般为NULL 23 | void* shmptr = shmat(shm_id, NULL, 0); 24 | // 判断是否创建成功 25 | if(shmptr == (void*)-1){ 26 | printf("fail to link the share memory.\n"); 27 | return -1; 28 | } 29 | // 打开一个命名信号量 30 | // 因为做的是同步,所以初始信号量的值为0,写入数据后POST一下 31 | sem_t* sem_id = sem_open(SEM_NAME, O_CREAT, S_IRWXU, 0); 32 | int* data = static_cast(shmptr); 33 | while(1){ 34 | // 等待信号量通知 35 | sem_wait(sem_id); 36 | // 从标准输入读入数据 37 | printf("%d\n", *data); 38 | // 通知读端取走,这里其实有一个同步的问题,如果输入很快的话,还来不及读取,但是因为会等待在标准输入,所以没问题 39 | // 输入-1退出进程 40 | if(*data == -1){ 41 | break; 42 | } 43 | } 44 | return 0; 45 | } 46 | -------------------------------------------------------------------------------- /IPC/POSIXSem_w.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by windyear_office on 19-2-26. 3 | // 一个配合共享内存实现进程间同步的Posix信号量demo 4 | #define SHM_KEY 1111 5 | #define SEM_NAME "/TestSem" 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | int main(){ 14 | // 打开一段共享内存。 15 | int shm_id = shmget(SHM_KEY, sizeof(int), 0666 | IPC_CREAT); 16 | // 判断是否打开成功 17 | if( shm_id < 0){ 18 | printf("fail to create share memory.\n"); 19 | return -1; 20 | } 21 | // 连接共享内存到进程空间,注意第二个参数一般为NULL 22 | void* shmptr = shmat(shm_id, NULL, 0); 23 | // 判断是否创建成功 24 | if(shmptr == (void*)-1){ 25 | printf("fail to link the share memory.\n"); 26 | return -1; 27 | } 28 | // 打开一个命名信号量 29 | // 因为做的是同步,所以初始信号量的值为0,写入数据后POST一下 30 | sem_t* sem_id = sem_open(SEM_NAME, O_CREAT, S_IRWXU, 0); 31 | int* data = static_cast(shmptr); 32 | while(1){ 33 | // 从标准输入读入数据 34 | scanf("%d", data); 35 | 36 | // 通知读端取走,这里其实有一个同步的问题,如果输入很快的话,还来不及读取,但是因为会等待在标准输入,所以没问题 37 | sem_post(sem_id); 38 | // 输入-1退出进程 39 | if(*data == -1){ 40 | break; 41 | } 42 | } 43 | return 0; 44 | } 45 | 46 | -------------------------------------------------------------------------------- /IPC/ProducerConsumer.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by windyear_office on 19-2-28. 3 | // 简单的用信号量做同步的生产者消费者 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | sem_t empty, full; 10 | 11 | int produce = 0, consum = 0; 12 | 13 | void* producer(void* data){ 14 | while(1){ 15 | sem_wait(&empty); 16 | printf("producer produce %d.\n", produce++); 17 | sem_post(&full); 18 | sleep(1); 19 | } 20 | } 21 | 22 | void* cosumer(void* data){ 23 | while(1){ 24 | sem_wait(&full); 25 | printf("consumer consum %d.\n", consum++); 26 | sem_post(&empty); 27 | sleep(1); 28 | } 29 | } 30 | 31 | int main(){ 32 | sem_init(&empty, 0, 1); 33 | sem_init(&full, 0, 0); 34 | pthread_t p1, p2; 35 | if(pthread_create(&p1, NULL, producer, NULL)){ 36 | printf("thread create fail.\n"); 37 | } 38 | if(pthread_create(&p2, NULL, cosumer, NULL)){ 39 | printf("thread create fail.\n"); 40 | } 41 | pthread_join(p1, NULL); 42 | pthread_join(p2, NULL); 43 | } -------------------------------------------------------------------------------- /IPC/makefile: -------------------------------------------------------------------------------- 1 | CFLAGS = -lpthread -Wall 2 | sem_w: POSIXSem_w.cpp 3 | gcc $^ ${CFLAGS} -o $@ 4 | sem_r: POSIXSem_r.cpp 5 | gcc $^ ${CFLAGS} -o $@ 6 | 7 | one_instance: OneInstanceFileLock.cpp 8 | gcc $^ ${CFLAGS} -o $@ 9 | clean: 10 | rm *.o 11 | -------------------------------------------------------------------------------- /InterviewProblem/TestSo/TestSo.c: -------------------------------------------------------------------------------- 1 | #include 2 | void TestSo(){ 3 | printf("Test the how to find the symbol of so file."); 4 | } 5 | -------------------------------------------------------------------------------- /InterviewProblem/TestSo/TestSo.h: -------------------------------------------------------------------------------- 1 | void TestSo(); 2 | -------------------------------------------------------------------------------- /InterviewProblem/TestSo/TestSoMain.c: -------------------------------------------------------------------------------- 1 | #include "TestSo.h" 2 | 3 | int main(){ 4 | TestSo(); 5 | return 0; 6 | } 7 | -------------------------------------------------------------------------------- /InterviewProblem/TestSo/makefile: -------------------------------------------------------------------------------- 1 | .PHONY: build test clean 2 | 3 | build: libTestSo.so 4 | 5 | libTestSo.so: TestSo.o 6 | gcc -o $@ -shared $^ 7 | 8 | TestSo.o: TestSo.c 9 | gcc -c -fPIC $^ 10 | 11 | test: a.out 12 | 13 | a.out: TestSoMain.c libTestSo.so 14 | gcc TestSoMain.c -L. -lTestSo 15 | 16 | clean: 17 | rm -f *.o *.so a.out 18 | -------------------------------------------------------------------------------- /InterviewProblem/TestSo/readme.md: -------------------------------------------------------------------------------- 1 | ### 测试生成动态链接库 2 | #### 重点笔记 3 | - 使用-fPIC -shared 编译选项生成动态链接库,第一个选项是以地址无关的模式生成。 4 | - makefile文件的编写注意$@ $^的用法。前者是目标集合,后者是依赖集合,就是不用写文件名。 5 | - 使用时可以用-L. -lname来指定链接动态库。 6 | #### 执行方法 7 | 1. make生成a.out和.so文件,./a.out执行测试程序。 8 | #### 补充说明如何查看动态库的符号 9 | 可以使用nm -D 或者objdump -tT 查看动态库符号。 10 | -------------------------------------------------------------------------------- /LibFunction/HeapSort.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by windyear_office on 19-3-11. 3 | // 4 | // 堆排序主要有堆调整和堆排序两个子程序构成。 5 | // 堆调整需要将root节点挪到合适的位置 6 | void HeapAdjust(int* arr, int head, int length){ 7 | int key = arr[head]; 8 | int index = 2*head+1; 9 | while(index < length){ 10 | if((index+1) arr[index]){ 11 | ++index; 12 | } 13 | if(arr[index] > key){ 14 | arr[head] = arr[index]; 15 | head = index; 16 | index = 2*index+1; 17 | } 18 | else{ 19 | break; 20 | } 21 | } 22 | // 找到位置放入key 23 | arr[head] = key; 24 | } 25 | 26 | void swap(int& a, int& b){ 27 | int tmp = a; 28 | a = b; 29 | b = tmp; 30 | } 31 | void HeapSort(int* arr, int length){ 32 | // 直接进行堆调整建堆 33 | // 从一半的位置开始,除去最后一行。 34 | for(int i = length/2-1; i >= 0; --i){ 35 | HeapAdjust(arr, i, length); 36 | } 37 | // 然后每次交换头节点到后面一个节点,再对头结点进行对调整 38 | for(int i = length -1; i >= 0; --i){ 39 | swap(arr[0], arr[i]); 40 | HeapAdjust(arr, 0, i); // 注意这里的位置是i; 41 | } 42 | } 43 | 44 | void TestHeapSort(){ 45 | int arr1[] = {2, 4, 5, 1, -1, 7}; 46 | HeapSort(arr1, 6); 47 | int arr2[] = {2, 3,3,1,1,7}; 48 | HeapSort(arr2, 6); 49 | void(1); 50 | } 51 | -------------------------------------------------------------------------------- /LibFunction/ListQuickSort.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by windyear_office on 19-3-11. 3 | // 4 | #include 5 | //Definition for singly-linked list. 6 | struct ListNode { 7 | int val; 8 | ListNode *next; 9 | ListNode(int x) : val(x), next(NULL) {} 10 | }; 11 | 12 | // 快速排序,前后指针。归并需要额外空间,快排不用。但是指针很难操作,需要返回指针和它前一个指针。 13 | // 此题太难先跳过。 14 | class Solution { 15 | public: 16 | ListNode* sortList(ListNode* head) { 17 | if(head == NULL){ 18 | return NULL; 19 | } 20 | quickSort(head, NULL); 21 | return head; 22 | } 23 | 24 | void swap(ListNode* a, ListNode* b){ 25 | int tmp = a->val; 26 | a->val = b->val; 27 | b->val = tmp; 28 | } 29 | // 不能返回索引,所以只能返回节点指针,右边的指针表示结尾,不能到达 30 | ListNode* partition(ListNode* left, ListNode* right){ 31 | if(left == NULL){ 32 | return NULL; 33 | } 34 | ListNode* p = left; 35 | ListNode* q = p->next; 36 | int key = left->val; 37 | while(q != right){ 38 | if(q->val <= key){ 39 | p=p->next; 40 | swap(p, q); 41 | } 42 | q = q->next; 43 | } 44 | swap(left, p); 45 | return p; 46 | } 47 | void quickSort(ListNode* left, ListNode* right){ 48 | if(left == right){ 49 | return; 50 | } 51 | ListNode* part = partition(left, right); 52 | quickSort(left, part); 53 | quickSort(part->next, right); 54 | //quickSort 55 | } 56 | 57 | }; 58 | -------------------------------------------------------------------------------- /LibFunction/MyString.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by windyear_office on 19-3-3. 3 | // 一个自行实现的简单的string类 4 | 5 | #ifndef LIBFUNCTION_MYSTRING_H 6 | #define LIBFUNCTION_MYSTRING_H 7 | #include 8 | 9 | class MyString{ 10 | public: 11 | MyString():data_(new char[1]){ 12 | *data_ = '\0'; 13 | } 14 | MyString(const MyString& s){ 15 | int len = strlen(s.data_); 16 | data_ = new char[len+1]; 17 | strcpy(data_, s.data_); 18 | } 19 | MyString(const char* c){ 20 | if( c == NULL){ 21 | data_ = new char[1]; 22 | *data_ = '\0'; 23 | } 24 | else{ 25 | int len = strlen(c); 26 | data_ = new char[len+1]; 27 | strcpy(data_, c); 28 | } 29 | } 30 | MyString& operator=(const MyString& s){ 31 | // 处理自复制 32 | if(this == &s){ 33 | return *this; 34 | } 35 | char* temp = data_; 36 | int len = strlen(s.data_); 37 | data_ = new char[len+1]; 38 | strcpy(data_, s.data_); 39 | delete [] temp; 40 | return *this; 41 | } 42 | ~MyString(){ 43 | if(data_ != NULL){ 44 | delete [] data_; 45 | } 46 | } 47 | 48 | const char* c_str() const{ 49 | return data_; 50 | } 51 | 52 | size_t size() const{ 53 | return strlen(data_); 54 | } 55 | private: 56 | char* data_; 57 | }; 58 | #endif //LIBFUNCTION_MYSTRING_H 59 | -------------------------------------------------------------------------------- /LibFunction/Singleton.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by windyear_office on 19-3-3. 3 | // 懒汉和饿汉模式的singleton 4 | 5 | #ifndef LIBFUNCTION_SINGLETON_H 6 | #define LIBFUNCTION_SINGLETON_H 7 | #include 8 | class Singleton{ 9 | private: 10 | Singleton(){} 11 | static Singleton* instance_; 12 | public: 13 | static Singleton* GetInstance(){ 14 | if(instance_ == NULL){ 15 | instance_ = new Singleton(); 16 | } 17 | return instance_; 18 | } 19 | }; 20 | 21 | class SingletonStatic{ 22 | private: 23 | 24 | SingletonStatic(){} 25 | static SingletonStatic* instance_; 26 | public: 27 | static SingletonStatic* GetInstance(){ 28 | return instance_; 29 | } 30 | }; 31 | 32 | SingletonStatic* SingletonStatic::instance_ = new SingletonStatic(); 33 | #endif //LIBFUNCTION_SINGLETON_H 34 | -------------------------------------------------------------------------------- /LibFunction/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "MyString.h" 5 | #include 6 | // 一些面试经常要手写的基础库函数的写法 7 | class TestVirtualCTR{ 8 | public: 9 | //报错 10 | //virtual TestVirtualCTR(); 11 | virtual void testVirtual()=0; 12 | virtual ~TestVirtualCTR(){} 13 | }; 14 | // 子类的析构函数会调用父类的,所以报错未定义的引用。 15 | class Derived: public TestVirtualCTR{ 16 | public: 17 | int data; 18 | void testVirtual(){ 19 | printf("Derived virtual function."); 20 | } 21 | }; 22 | /* 23 | * memcpy:拷贝内存,需要支持任意类型 24 | * 注意处理内存重叠,边界条件 25 | * 不能保证des有足够的长度。 26 | * src要使用const 27 | * 返回目标内存开始的位置 28 | */ 29 | void* mymemcpy(void* des,const void* src, int count){ 30 | if(src == NULL || des == NULL || count <= 0){ 31 | return NULL; 32 | } 33 | // 复制指针 34 | void* des_tmp = des; 35 | // 转化为char*进行操作 36 | char* sr = (char*)src; 37 | char* de = (char*)des; 38 | // 进一步优化,如果两个指针相等 39 | if(des == src){ 40 | return des; 41 | } 42 | if(de > sr && de < sr+count){ 43 | // 从后面开始拷贝。 44 | while(count > 0){ 45 | *(de+count-1) = *(sr+count-1); 46 | --count; 47 | } 48 | } 49 | else{ 50 | int i = 0; 51 | while(i < count){ 52 | *(de+i) = *(sr+i); 53 | ++i; 54 | } 55 | } 56 | (void*)des_tmp; 57 | return des_tmp; 58 | } 59 | 60 | // 字符复制strcpy 61 | // 注意考虑重叠情况 62 | // 返回char* 63 | char* mystrcpy(char* dest, const char* src){ 64 | assert(dest != NULL); 65 | assert(src != NULL); 66 | // 通过strlen求出长度 67 | if(dest == src){ 68 | return dest; 69 | } 70 | size_t len = strlen(src); 71 | // 根据内存是否重叠进行复制。 72 | if(dest > src && dest <(src+len)){ 73 | // 从后面开始复制 74 | size_t count = len - 1; 75 | while(count >= 0){ 76 | *(dest+count) = *(src+count); 77 | --count; 78 | } 79 | } 80 | else{ 81 | size_t count = 0; 82 | while(count < len){ 83 | *(dest+count) = *(src+count); 84 | ++count; 85 | } 86 | } 87 | *(dest+len) = '\0'; 88 | return dest; 89 | } 90 | 91 | void TestMyString(){ 92 | MyString s0; 93 | MyString s1("hello"); 94 | MyString s2(s0); 95 | printf("s0's size is:%d\n", s0.size()); 96 | printf("s1's size is:%d\n", s1.size()); 97 | std::vector s_vec; 98 | s_vec.push_back(s0); 99 | s_vec.push_back(s1); 100 | s_vec.push_back("nice!"); 101 | MyString test(s1); 102 | } 103 | int main() { 104 | //std::cout << "Hello, World!" << std::endl; 105 | //char testa[] = {'a', 'b', 'c', 'd', 'e', 'f'}; 106 | //memcpy(testa, testa+3, 3); 107 | //memcpy(testa, testa+2, 3); 108 | //char testb[] = {'h', 'h'}; 109 | //char* c = mystrcpy(testa, testb); 110 | //printf("%s\n", c); 111 | //Derived derived; 112 | TestMyString(); 113 | return 0; 114 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## 数据结构与算法 2 | ### 说明: 3 | - 本仓库的代码是关于数据结构与算法的,来自于牛客网、leetcode等网站的编程题目。 4 | - 一般情况下使用C/C++来实现,文件的名字概括了实现的算法内容。 5 | - 文件中一般带有中文说明算法思路以及记载自己的思考过程。 6 | - 可以为将来准备找实习找工作带来帮助,并且提高自己的编程基础能力。 7 | ### Algorithms文件夹里面的是算法第四版的习题或者是书本的例子,书本是用java实现的,重新用c++实现一遍。 8 | -------------------------------------------------------------------------------- /leetcode/95leetcode生成不同的二叉搜索树.md: -------------------------------------------------------------------------------- 1 | ## 95leetcode生成不同的二叉搜索树 2 | 3 | #### 题目 4 | 5 | 给定一个整数 *n*,生成所有由 1 ... *n* 为节点所组成的**二叉搜索树**。 6 | 7 | #### 分析 8 | 9 | 以1到n分别作为头结点递归生成二叉树,左右子树分别互相组合成不同的二叉树。 10 | 11 | #### 解答 12 | 13 | ```c++ 14 | /** 15 | * Definition for a binary tree node. 16 | * struct TreeNode { 17 | * int val; 18 | * TreeNode *left; 19 | * TreeNode *right; 20 | * TreeNode(int x) : val(x), left(NULL), right(NULL) {} 21 | * }; 22 | */ 23 | // 套用一个快速排序的方法,递归创建 24 | class Solution { 25 | public: 26 | vector generateTrees(int n) { 27 | vector result; 28 | if(n < 1){ 29 | return result; 30 | } 31 | return recurGener(1, n); 32 | } 33 | 34 | // 一个递归产生二叉搜索树的结构。 35 | vector recurGener(int start, int end){ 36 | vector result; 37 | if(start > end){ 38 | result.push_back(NULL); 39 | return result; 40 | } 41 | // 以start到end分别作为头结点。 42 | for(int i = start; i <= end; ++i){ 43 | vector left = recurGener(start, i-1); 44 | vector right = recurGener(i+1, end); 45 | // 合并子树。 46 | for(int j = 0; j < left.size(); ++j){ 47 | for(int k = 0; k < right.size(); ++k){ 48 | TreeNode* head = new TreeNode(i); 49 | head->left = left[j]; 50 | head->right = right[k]; 51 | result.push_back(head); 52 | } 53 | } 54 | } 55 | return result; 56 | } 57 | }; 58 | ``` 59 | 60 | #### 总结 61 | 62 | 生成树的算法用递归生成,此题的难点在于每个节点都可以作为根节点,且需要保存所有的树结构。 -------------------------------------------------------------------------------- /leetcode/98leetcode验证二叉树.md: -------------------------------------------------------------------------------- 1 | ### 98leetcode验证二叉树 2 | 3 | #### 题目 4 | 5 | 给定一个二叉树,判断其是否是一个有效的二叉搜索树。 6 | 7 | #### 分析 8 | 9 | 此题利用二叉搜索树的特性进行中序遍历即可。遍历过程可以判断,提前返回。 10 | 11 | #### 代码 12 | 13 | ```c++ 14 | /** 15 | * Definition for a binary tree node. 16 | * struct TreeNode { 17 | * int val; 18 | * TreeNode *left; 19 | * TreeNode *right; 20 | * TreeNode(int x) : val(x), left(NULL), right(NULL) {} 21 | * }; 22 | */ 23 | // 中序遍历过程判断是否是升序。 24 | class Solution { 25 | public: 26 | bool isValidBST(TreeNode* root) { 27 | if(root == NULL){ 28 | return true; 29 | } 30 | vector result; 31 | return inOrder(root,result); 32 | } 33 | 34 | // 中序遍历的过程进行比较 35 | bool inOrder(TreeNode* root, vector& result){ 36 | if(root == NULL){ 37 | return true; 38 | } 39 | if(!inOrder(root->left, result)){ 40 | return false; 41 | } 42 | if(result.empty()){ 43 | result.push_back(root->val); 44 | } 45 | else{ 46 | if(root->val <= result.back()){ 47 | return false; 48 | } 49 | else{ 50 | result.push_back(root->val); 51 | } 52 | } 53 | return inOrder(root->right, result); 54 | } 55 | }; 56 | ``` 57 | 58 | #### 总结 59 | 60 | 二叉搜索树注意利用其特点,中序遍历有序。 -------------------------------------------------------------------------------- /leetcode/APUE第八章进程控制读书笔记.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: APUE第八章进程控制读书笔记 3 | tags: 操作系统,进程,fork 4 | grammar_cjkRuby: true 5 | --- 6 | ## 总结 7 | 第八章主要讲了fork函数,exec系列函数,exit系列函数和wait系列函数的主要用途,这些函数可以对进程进行控制。后面还有一些对system函数,解释器文件,进程时间进程调度的简单介绍。重点内容在于对前面的几个系统调用的理解,还有孤儿进程,僵尸进程的概念。 8 | ## 重点笔记 9 | - fork函数调用之后,子进程和父进程共享正文段,其余的数据段,堆和栈被复制。且共享同一个文件偏移量。处理文件的时候要注意。不同的地方主要有进程ID, 文件锁不继承等。 10 | - fork返回0的代表子进程,大于零是子进程ID,即父进程。 11 | - 调用exit系列函数可以终止进程并且返回终止状态。包括异常终止也会返回终止状态,父进程可以用wait系列函数取得终止状态。 12 | - **若父进程早于子进程终止,子进程的父进程变为init进程,孤儿进程被收养。**内核的实现方式是在进程终止时检查其子进程,修改子进程的父进程ID。 13 | - **僵死进程,进程已经终止,但是父进程还没有处理内核保留的信息,即没有善后。**解决方式是对父进程处理,找到源头。 14 | - wait系列函数会导致父进程阻塞,但是不用wait系列会导致僵死进程。解决方法是调用fork两次。终止子进程,那么子进程的子进程的父进程就会变为init,init会妥善处理终止的子进程。 15 | - exec系列不改变进程ID,只是替换程序。 16 | - 很多系统实现中只有execve是系统调用。 -------------------------------------------------------------------------------- /leetcode/Leetcode208 Trie 实现前缀树.md: -------------------------------------------------------------------------------- 1 | # Leetcode208 Trie 实现前缀树 2 | 3 | ### 题目 4 | 5 | 实现一个 Trie (前缀树),包含 `insert`, `search`, 和 `startsWith` 这三个操作。 6 | 7 | ### 分析 8 | 9 | - 字典树关键在于节点的结构的设定。利用字符串的公共前缀节省空间。 10 | 11 | - 节点结构应该需要一个节点指针数组代表路径。一个变量记录是否是叶子。 12 | 13 | - **拓展**增加数量的记录,需要增加经过该节点的字符串数量和以其为结尾的数量。 14 | 15 | - 节点结构如下 16 | 17 | ```c++ 18 | struct TrieNode{ 19 | TrieNode* child[26]; 20 | bool is_end; 21 | TrieNode():is_end(false){ 22 | for(int i = 0; i < 26; ++i){ 23 | child[i] = 0; 24 | } 25 | } 26 | }; 27 | ``` 28 | 29 | - 主要需要实现插入,查找,查找前缀等功能。后续可以加上删除功能。 30 | 31 | 32 | 33 | ### 解题代码 34 | 35 | ```c++ 36 | // 关键是前缀树的结构体的设置。具有一个指针数组。可以使用map。包含一个变量代表是否是叶子节点。 37 | // 如果要记录数量的话,应该需要一个变量记录path。 38 | struct TrieNode{ 39 | TrieNode* child[26]; 40 | bool is_end; 41 | TrieNode():is_end(false){ 42 | for(int i = 0; i < 26; ++i){ 43 | child[i] = 0; 44 | } 45 | } 46 | }; 47 | class Trie { 48 | public: 49 | /** Initialize your data structure here. */ 50 | Trie(): root_(new TrieNode()) { 51 | 52 | } 53 | 54 | /** Inserts a word into the trie. */ 55 | void insert(string word) { 56 | TrieNode* node = root_; 57 | for(size_t i = 0; i < word.size(); ++i){ 58 | int pos = word[i] - 'a'; 59 | if(node->child[pos] == 0){ 60 | node->child[pos] = new TrieNode(); 61 | } 62 | node = node->child[pos]; 63 | } 64 | node->is_end = true; 65 | } 66 | 67 | /** Returns if the word is in the trie. */ 68 | // 要是最后一个才行。 69 | bool search(string word) { 70 | TrieNode* node = root_; 71 | for(size_t i = 0; i < word.size(); ++i){ 72 | int pos = word[i] - 'a'; 73 | if(node->child[pos] == 0){ 74 | return false; 75 | } 76 | node = node->child[pos]; 77 | } 78 | return node->is_end; 79 | } 80 | 81 | /** Returns if there is any word in the trie that starts with the given prefix. */ 82 | bool startsWith(string prefix) { 83 | TrieNode* node = root_; 84 | for(size_t i = 0; i < prefix.size(); ++i){ 85 | int pos = prefix[i] - 'a'; 86 | if(node->child[pos] == 0){ 87 | return false; 88 | } 89 | node = node->child[pos]; 90 | } 91 | return true; 92 | } 93 | 94 | private: 95 | TrieNode* root_; 96 | }; 97 | 98 | /** 99 | * Your Trie object will be instantiated and called as such: 100 | * Trie obj = new Trie(); 101 | * obj.insert(word); 102 | * bool param_2 = obj.search(word); 103 | * bool param_3 = obj.startsWith(prefix); 104 | */ 105 | ``` 106 | 107 | ### 字典树的应用 108 | 109 | 1. 字典树是一种用于快速检索的多叉树结构,如英文字母的字典树是一个26叉树,数字的字典树是一个10叉树。他的核心思想是空间换时间,空间消耗大但是插入和查询有着很优秀的时间复杂度。 110 | 111 | 2. 可以用字典树统计前缀出现次数。节点使用一个count记录出现次数。 112 | 113 | 3. 输入自动保存。以字符串结尾为root进行先根遍历。先根遍历时需要使用一个数据结构来记录前面出现了的字符。 114 | 115 | 4. 如果要查找的关键字可以分解成字符序列且不是很长,利用trie树查找速度优于二叉查找树。 116 | 117 | 5. [Trie树:应用于统计和排序](https://blog.csdn.net/hguisu/article/details/8131559) 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | -------------------------------------------------------------------------------- /leetcode/blog/C++笔记1.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: C++笔记1 3 | date: 2016-09-11 18:40:58 4 | tags: c++ 笔记 5 | --- 6 | ### C++学习笔记1 7 | #### 第二章开始学习C++ 8 | - 过程化编程思想是根据执行的操作来构思程序。使问题满足语言的过程性方法。 9 | - 面向对象让语言来满足问题的要求。 10 | - 名称空间可以区分不同版本的代码,比如不停公司开发的相同函数名称的函数。 11 | - 回车不能放在字符串中间,空格,制表和回车可以被忽略,但是不能放在名称或者单词之间。 12 | - cout可以实现智能的打印转换,比如打印一个整形变量,变量里面存储的是数值的二进制形式,但是cout可以识别它并且自动转换为字符串输出。其实是用了操作符重载的技术,重载的操作符是<<。 13 | #### 第四章复合类型 14 | 1. 数组的初始化可以在声明的时候进行,可以部分初始化,可以初始化为0。但是不能将数组整个赋值给另外一个数组。java可以,因为java的数组名字本身就是一个引用,可以赋值给另外一个引用。 15 | 2. c风格上的字符串是以\0来结尾的,字符串与char数组是有区别的。‘a’与“a”是不同的,后者是字符串常量,包括一个空字符。初始化的时候不用 显式写出字符串。 16 | 3. 可以使用库函数strlen()来计算存储在数组中的字符串长度。 17 | 4. **字符串输入的特殊性**cin采用空白换行还有制表来判断输入结束。可以采用getline()或者get()函数来实现面向行的输入。例如 18 | ```cpp 19 | char name[40]; 20 | cin.getline(name,20);//表示从流中读取20个字符或 21 | //者直到换行符。实际上是19个,最后会自动添加一个空字 22 | //符\0。 23 | ``` 24 | get()函数不会存储换行符,但是不会将换行符丢弃,还会将它保留在输入队列中。下一个cin读取时第一个看到就是换行符。可以在输入后面单独调用一个get()来处理掉换行符。cin.getJ()可以读取下一个字符包括换行符。**cin也会保留换行到队列中。但是下一个cin可以忽略掉换行符,但是getline却不行。** 25 | 5. c++可以采用string类来处理字符串的问题,方便。这时候就可以将一个string对象赋值给另外一个string对象了。 26 | 6. 共用体union可以存储不同的类型,但是不能同时存储。 27 | 7. 枚举类型主要是定义一些常量值,只能是枚举的值,不能是其他的值,具体的值是整数。 28 | 8. **指针:一定要在使用*解除地址引用之前将指针值确定下来,否则会出现很大的bug。未初始化之前不能使用,使用new以后就算是初始化了。** 29 | 9. new和delete要成对的使用,new提供了动态分配内存的能力,如果直接声明则静态分配内存,一直占用。new主要用来生成动态数组。 30 | 10. 一般来说,cout会直接打印指针的值,但是如果是指向字符的指针会打印整个字符串。 31 | ### 总结 32 | 重新学习C++,确实可以发现很多以前没有注意到的地方,有不同的收获。做笔记的时候只是随便写写,并没有较好的格式,希望以后能够有所改善。 -------------------------------------------------------------------------------- /leetcode/blog/cloneBranchFromOrigin.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: cloneBranchFromOrigin 3 | date: 2016-12-14 09:56:49 4 | tags: git 版本管理 5 | --- 6 | # 利用git从远程仓库中clone其他分支 7 | ## 使用需求说明 8 | - 使用git clone时会默认下载master分支并且实现跟踪,或者可以直接指定要下载的分支。 9 | - 其他人将其他分支推送到远程之后需要在本地创建一个跟踪分支进行开发。 10 | ## 使用方式 11 | 1. `git checkout -b branchname origin/branchname`本地创建一个对应的分支。 12 | 2. `git checkout -t origin/branchname`直接在本地创建 13 | 3. `git fetch origin branchname:branchname` 14 | 4. **错误方式:** 15 | `git branch branchname;git checkout branchname;git pull origin branchname:branchname 16 | 这样是在当前分支上创建新分支再与远程分支合并。 17 | -------------------------------------------------------------------------------- /leetcode/blog/conclusionOfAWeek.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: conclusionOfAWeek 3 | date: 2016-12-11 23:05:10 4 | tags: 5 | --- 6 | -------------------------------------------------------------------------------- /leetcode/blog/learningSocket.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: learningSocket 3 | date: 2016-12-12 22:31:16 4 | tags: 5 | - python 6 | - socket 7 | - 网络编程 8 | - 服务器 9 | --- 10 | # 简单的本地socket编程以及能够下载网页的socket客户端 11 | ## 程序说明 12 | 1. 使用python封装好的网络编程模块socket,可以快速实现socke编程开发。 13 | 2. 服务端的主要操作是首先有一个套接字实例,然后去监听某个地址和端口。客户端同样这样,使用connect()函数来连接服务器。此时,服务器的accept()方法一直阻塞到有连接并且返回一个套接字,地址的元组。通过套接字实例的send和recv方法可以实现发送和接受消息。 14 | 3. 使用完之后就可以关闭socket。 15 | ## 程序如下: 16 | 1. 服务器: 17 | ``` python 18 | `import socket 19 | 20 | s=socket.socket() 21 | 22 | host=socket.gethostname() 23 | port=1234 24 | 25 | s.bind((host,port)) 26 | 27 | s.listen(5) 28 | 29 | while(True): 30 | c,addr =s.accept() 31 | print('Got connection from',addr) 32 | c.send('Thank you for connecting'.encode()) 33 | c.close()` 34 | ``` 35 | 2. 客户端: 36 | #!/usr/bin/python 37 | # -*- coding:utf-8 -*- 38 | import socket 39 | 40 | s=socket.socket() 41 | 42 | host=socket.gethostname() 43 | port=1234 44 | 45 | s.connect((host,port)) 46 | print(s.recv(1024).decode()) 47 | 48 | #test=input("Just testing:") 49 | #raw_input()![图片不见了哟](http://i.imgur.com/W8jyeQL.png) 50 | 3. 运行结果 51 | ![图片不见了哟](http://i.imgur.com/W8jyeQL.png) 52 | ![](http://i.imgur.com/3rMIFqF.png) 53 | ## 各种坑 54 | - 首先是bind和connect函数的参数是一个元组而不是两个参数。 55 | - 第二个大坑是python3的str和byte类型明显的区分,所以通过send发送的字符串要先进行编码或者直接`b'Hello world'`这样表明是是byte类型的,同理接收时要解码。 56 | - 无法搞清楚端口号明明已经设置好了,但是连接时会换端口,不知道是什么原因。 57 | 58 | ## 下面是一个下载网页的程序 59 | ### 核心原理就是建立好socket连接之后发送GET请求,服务器会根据请求的内容返回内容,然后处理内容将其保存到文件中就可以 60 | ### 代码: 61 | #!/usr/bin/python 62 | # -*- coding: utf-8 -*- 63 | import socket 64 | s=socket.socket() 65 | 66 | s.connect(('www.sina.com.cn',80)) 67 | s.send(b'GET / HTTP/1.1\nHost: www.sina.com.cn\nConnection: keep-alive\n\n') 68 | 69 | buffer=[] 70 | while True: 71 | d=s.recv(1024) 72 | if d: 73 | buffer.append(d) 74 | else: 75 | break 76 | data=b''.join(buffer) 77 | s.close() 78 | 79 | #header,html = data.split(b'\n\n',1) 80 | #print(header.decode('utf-8')) 81 | 82 | with open(r'D:\windyear_files\testfiles\sina_test.html','wb') as f: 83 | f.write(data) 84 | **运行的结果是下载到一个网页保存为html文件** 85 | 86 | ### 走过的坑 87 | 1. 首先是windows系统和linux对于回车的使用是不同的,只需要\n而非\r\n。由此引发了一个关于split函数解包的问题,第二个参数指定返回多少个值,但是不能忽略不能分割的情况。 88 | 2. 其次是GET请求的参数问题,一开始的Connection参数是close,这时下载了一个错误的网页,后来使用chrome的开发者工具了解发哦正确的参数应该是keep-alive。 89 | 90 | ##总结 91 | 由于python封装好了socket模块,所以使用起来比较简单。但是很多细节问题必须自己实现一次才能发现。今天虽然做的不多,但是也算是有一些收获,希望自己可以坚持下去。 -------------------------------------------------------------------------------- /leetcode/blog/leetcode142之寻找环形链表的环的起点.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: leetcode142之寻找环形链表的环的起点 3 | tags: leetcode,算法, C++ 4 | grammar_cjkRuby: true 5 | --- 6 | ## 题目 7 | - 给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。 8 | - 说明:不允许修改给定的链表。 9 | - 尽量不用额外的存储空间 10 | ## 分析 11 | - 这个跟寻找环的题目相似,但是难度增加了。花费了较多时间去做。 12 | - 肯定是利用快慢指针找出是否存在环。画图之后发现,慢指针走的步数是环的长度。所以,只要另一个指针从开头出发,慢指针继续向前走,他们碰到的节点就是环的入口。 13 | - 第一版的代码还用了一个记录长度整型,多走了一些步骤。后面优化了。 14 | ## 代码 15 | ```c++?linenums 16 | /** 17 | * Definition for singly-linked list. 18 | * struct ListNode { 19 | * int val; 20 | * ListNode *next; 21 | * ListNode(int x) : val(x), next(NULL) {} 22 | * }; 23 | */ 24 | class Solution { 25 | public: 26 | ListNode *detectCycle(ListNode *head) { 27 | // 快慢指针,先求出环的长度,然后一个指针先走一个环的长度的步数,然后另一个指针从头开始,他们会在入口点遇见。 28 | if((head == nullptr) || (head->next == nullptr)){ 29 | return nullptr; 30 | } 31 | int count = 0; 32 | ListNode* normal = head; 33 | ListNode* fast = head; 34 | // 找到相等点或者到达空指针 35 | while(true){ 36 | if((fast->next == nullptr) || (fast->next->next == nullptr)){ 37 | return nullptr; 38 | } 39 | fast = (fast->next)->next; 40 | normal = normal->next; 41 | count += 1; 42 | if(fast == normal){ 43 | break; 44 | } 45 | } 46 | // count的次数就是环的长度 47 | fast = head; 48 | while(count > 0){ 49 | fast = fast->next; 50 | count--; 51 | } 52 | normal = head; 53 | while(true){ 54 | if(normal == fast){ 55 | return fast; 56 | } 57 | fast = fast->next; 58 | normal = normal->next; 59 | } 60 | } 61 | }; 62 | ``` 63 | **改良版本代码** 64 | ```c++?linenums 65 | /** 66 | * Definition for singly-linked list. 67 | * struct ListNode { 68 | * int val; 69 | * ListNode *next; 70 | * ListNode(int x) : val(x), next(NULL) {} 71 | * }; 72 | */ 73 | class Solution { 74 | public: 75 | ListNode *detectCycle(ListNode *head) { 76 | // 快慢指针,先求出环的长度,然后一个指针先走一个环的长度的步数,然后另一个指针从头开始,他们会在入口点遇见。 77 | if((head == nullptr) || (head->next == nullptr)){ 78 | return nullptr; 79 | } 80 | // int count = 0; 81 | ListNode* normal = head; 82 | ListNode* fast = head; 83 | // 找到相等点或者到达空指针 84 | while(true){ 85 | if((fast->next == nullptr) || (fast->next->next == nullptr)){ 86 | return nullptr; 87 | } 88 | fast = (fast->next)->next; 89 | normal = normal->next; 90 | // count += 1; 91 | if(fast == normal){ 92 | break; 93 | } 94 | } 95 | // count的次数就是环的长度 96 | fast = head; 97 | // while(count > 0){ 98 | // fast = fast->next; 99 | // count--; 100 | // } 101 | while(true){ 102 | if(normal == fast){ 103 | return fast; 104 | } 105 | fast = fast->next; 106 | normal = normal->next; 107 | } 108 | } 109 | }; 110 | ``` 111 | ## 总结 112 | 链表问题还是要注重快慢指针的应用和画图出来。 -------------------------------------------------------------------------------- /leetcode/blog/php_notes1.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: php_notes1 3 | date: 2016-07-17 00:50:29 4 | categories: Exercise 5 | toc: true 6 | --- 7 | # PHP基础知识学习笔记 8 | ## 前言 9 | 这几天开始学习php语言,因为之前也接触过不少高级语言,所以对于基础知识的学习是很快的,但是由于这样的做法有点走马观花,所以决定写下一个博客文章进行总结。(这是第一次写博客,可能写得不好) 10 | ## php究竟是什么,有什么用 11 | 经过了自己的学习经验,很显然要先懂得要学的东西究竟有什么用。我以前知道它是可以做网站开发的,现在了解到它主要可以用来当做服务端脚本语言,用来生成动态网页,收集表单数据等。说到这里,想起了计算机网络的知识。这大概说的就是当时讲的服务器根据客户端的请求动态生成html文件然后返回给客户端浏览器显示吧。总结起来,就是用来做web开发的。 12 | ## 基本知识 13 | **只列举一些重要笔记** 14 | ###基本语法 15 | - 1、php是嵌入到html语言当中的,需要特殊的标记符号进行标识。例如一句``,就是一句简单的输出语句。 16 | - 2、编码规范:用;结束语句,忽视空白,注释方法可以更C语言一样。 17 | - 3、常量用大写字母,变量以字母和下划线开头。这里有一个特殊的地方时,php的变量是弱类型的,不需要对每一个变量声明,使用$符号作为前缀。其他的诸如变量作用域,类型等大同小异于其他高级语言,不再赘述。值得一提的是数组的创建是通过函数实现的,比如`15,2=>10);?>`这样就算是创建了一个数组,下标只是一个标识,可以理解为存储方式是key-value对。 18 | #今天时间有点晚了,先完成到这里,以后有时间再更新。 -------------------------------------------------------------------------------- /leetcode/blog/test.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: test 3 | date: 2016-08-06 00:51:44 4 | tags: 5 | --- 6 | -------------------------------------------------------------------------------- /leetcode/blog/title.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 如何从远程仓库中clone不同的分支 3 | date: 2016-11-28 20:36:23 4 | tags: git 5 | --- 6 | -------------------------------------------------------------------------------- /leetcode/blog/useHexoInTwoComputer.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: useHexoInTwoComputer 3 | date: 2016-12-14 18:30:47 4 | tags: 5 | - git 版本管理 6 | --- 7 | ## 瞎折腾——在两台电脑上同时使用hexo发布博客 8 | ### 作死 9 | - 想要同时在宿舍和实验室写文章并且马上发布。 10 | - 之前的方式是写好文章,然后同步到github上的管理原始代码的分支,然后在宿舍pull下来并且使用hexo命令进行发布,这样太麻烦了。 11 | - 实验:写好文章后马上 `hexo deploy -g`发布,结果没有生成网站文件,并且将以前的commit覆盖了。 12 | ### 解决问题 13 | 上网查了很久,发现了一个.deploy_git的文件夹可能负责存储hexo发布指令的操作结果。于是把该文件夹也同步了。 14 | -------------------------------------------------------------------------------- /leetcode/blog/学习git两天总结.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 学习git两天总结 3 | date: 2016-11-22 23:25:51 4 | tags: 5 | - git 6 | - 总结 7 | - 编程 8 | - 版本管理 9 | --- 10 | ### 总结说明 11 | 教程花了接近两天的时间学习了git,基本上是顺着[廖雪峰老师的git教程](http://www.liaoxuefeng.com/wiki/0013739516305929606dd18361248578c67b8067c8c017b000/ "hello")走的,自己敲了大多数命令,总算是入门了。想起自己好久没有打理自己的博客了,于是乎写下这篇总结当做是一次温故而知新的过程,并且留下一些自己一步一步走过的学习轨迹。 12 | #### 概述 13 | git是一个分布式的版本管理软件,这句话有两重意思。第一:版本管理工具:本人的理解git是用来管理版本的,方便记录每次对代码的修改。实际上不单单是代码文件,任何文件的修改都可以被commit为版本,不过像图像,word文档这种不是纯文本格式的文件的具体修改git捕捉不到,但是仍然可以保存版本。 14 | 第二,分布式的:这是说每一个代码仓库都是等同性质的,都拥有所有的代码版本,只要及时同步其他仓库的对代码的修改。而现实开发中往往会用将一份代码托管到服务器上用于各个仓库进行代码同步。 15 | #### 基本用法 16 | 安装git十分简单,傻瓜式安装,很简单就不详细展开了。有一点比较特别的是,有三种打开方式的:一种是git gui,这种没有试过,不过本质上仍然是指令的封装,只不过更加直观一些。当然速度也会更慢一些。第二种是**git bash,这种形式可以用一些Llinux的指令**,暂时没有搞明白,估计是封装了linux的一些指令。最后是git cmd,就是cmd中使用git,只要环境变量设置好了就可以使用,想jaca一样。 17 | 1. `git init`是初始化仓库。 18 | 2. `git status`是十分有用的命令,用于查看当前的状态,会给出很多重要的信息比如说哪些文件被修改了但是还没有add进暂存区,哪些暂存区的文件还没有commit,它会提示操作,包括如何回退版本。 19 | 3. `git add filename`可以将修改过的文件添加到暂存区,相当于加入购物车,待后续的commit操作。 20 | 4. `git commit -m `在当前分支生成一个版本。 21 | 5. `git reset --hard HEAD`回退版本,可以使用id或者HEAD^。 22 | 6. 分支操作:`git branch; git checkout branchname;git branch -d `各种用于分支操作的指令。一般来说开发软件的时候按照功能来增加模块,每次都新建分支来开发。之后再`git merge branchname`合并分支,有时候需要进行冲突处理。 23 | 7. 总结:一般来说简单的操作会经常使用,只要多使用,多熟悉就行。如果忘记了用法的话可以查资料解决,不必太过于纠结于指令,值需要明白如何使用。 24 | #### 使用github作为远程库 25 | 由于之前已经拥有github账号,所以使用起来也很快。`git clone url`可以克隆仓库。一开始需要先关联仓库,如果先有本地仓库。使用`git push origin branch`可以推送本地分支到远程库,还要时不时拉取一下远程分支以获取同步。 26 | ### 结语 27 | 写着写着发现自己的中心不太明确,也不太会表达。写到后面有点敷衍了事,毕竟git一时半会也说不清楚,更依赖于实践过程中遇到问题,这样才会越来越熟悉。 28 | -------------------------------------------------------------------------------- /leetcode/blog/插入排序.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 小谈插入排序 3 | categories: Exercise 4 | toc: true 5 | --- 6 | # 小谈插入排序 # 7 | 1. 插入排序是常见的排序方式之一,时间复杂度为O(n^2)。它是一种原址性的排序方法,只需要额外的一点空间存储中间变量。比较节省空间,在输入规模不大的时候不失为一种好的排序方法。 8 | 2. 主要思路如下:从第二个元素开始,逐个与前面的元素比较以找到合适的插入位置,不符合大小关系的往后移动一位,直到找到正确的位置为止。 9 | 3. 代码如下:`#include 10 | int insertSort(int array[],int length){ 11 | for(int i=1;ikey){ 15 | array[j+1]=array[j]; 16 | j--; 17 | } 18 | array[j+1]=key; 19 | } 20 | return 0; 21 | } 22 | int main(void) { 23 | // your code goes here 24 | //初始化一个长度为5的数组 25 | int arr[5]={23,4,35,76,34}; 26 | int length=sizeof(arr)/sizeof(arr[0]); 27 | insertSort(arr,length); 28 | for(int i=0;i<5;i++){ 29 | printf("%d-",arr[i]); 30 | } 31 | printf("%d",sizeof(arr)/sizeof(arr[0])); 32 | return 0; 33 | } 34 | ` 35 | 4. 代码分析:写一个函数实现排序,这里面要注意元素个数的问题,还有边界值得问题。我在写代码的时候忽略了边界值检测。**这里说一个函数的数组参数传递为题,传入函数的数组名字已经退化成了指针,所以无法求出数组的长度,需要额外传入一个长度参数。如果对函数里面的数组名字求sizeof()只会得到指针的大小,32位机的指针大小是4个字节。** 36 | 5. 优化:看到有一种方法将寻找合适位置的查找采用二分查找,但是时间复杂度没有提高,因为不是查找大小相等的元素,还是要比较完所有的元素。 37 | -------------------------------------------------------------------------------- /leetcode/leetcode142之寻找环形链表的环的起点.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: leetcode142之寻找环形链表的环的起点 3 | tags: leetcode,算法, C++ 4 | grammar_cjkRuby: true 5 | --- 6 | ## 题目 7 | - 给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。 8 | - 说明:不允许修改给定的链表。 9 | - 尽量不用额外的存储空间 10 | ## 分析 11 | - 这个跟寻找环的题目相似,但是难度增加了。花费了较多时间去做。 12 | - 肯定是利用快慢指针找出是否存在环。画图之后发现,慢指针走的步数是环的长度。所以,只要另一个指针从开头出发,慢指针继续向前走,他们碰到的节点就是环的入口。 13 | - 第一版的代码还用了一个记录长度整型,多走了一些步骤。后面优化了。 14 | ## 代码 15 | ```c++?linenums 16 | /** 17 | * Definition for singly-linked list. 18 | * struct ListNode { 19 | * int val; 20 | * ListNode *next; 21 | * ListNode(int x) : val(x), next(NULL) {} 22 | * }; 23 | */ 24 | class Solution { 25 | public: 26 | ListNode *detectCycle(ListNode *head) { 27 | // 快慢指针,先求出环的长度,然后一个指针先走一个环的长度的步数,然后另一个指针从头开始,他们会在入口点遇见。 28 | if((head == nullptr) || (head->next == nullptr)){ 29 | return nullptr; 30 | } 31 | int count = 0; 32 | ListNode* normal = head; 33 | ListNode* fast = head; 34 | // 找到相等点或者到达空指针 35 | while(true){ 36 | if((fast->next == nullptr) || (fast->next->next == nullptr)){ 37 | return nullptr; 38 | } 39 | fast = (fast->next)->next; 40 | normal = normal->next; 41 | count += 1; 42 | if(fast == normal){ 43 | break; 44 | } 45 | } 46 | // count的次数就是环的长度 47 | fast = head; 48 | while(count > 0){ 49 | fast = fast->next; 50 | count--; 51 | } 52 | normal = head; 53 | while(true){ 54 | if(normal == fast){ 55 | return fast; 56 | } 57 | fast = fast->next; 58 | normal = normal->next; 59 | } 60 | } 61 | }; 62 | ``` 63 | **改良版本代码** 64 | ```c++?linenums 65 | /** 66 | * Definition for singly-linked list. 67 | * struct ListNode { 68 | * int val; 69 | * ListNode *next; 70 | * ListNode(int x) : val(x), next(NULL) {} 71 | * }; 72 | */ 73 | class Solution { 74 | public: 75 | ListNode *detectCycle(ListNode *head) { 76 | // 快慢指针,先求出环的长度,然后一个指针先走一个环的长度的步数,然后另一个指针从头开始,他们会在入口点遇见。 77 | if((head == nullptr) || (head->next == nullptr)){ 78 | return nullptr; 79 | } 80 | // int count = 0; 81 | ListNode* normal = head; 82 | ListNode* fast = head; 83 | // 找到相等点或者到达空指针 84 | while(true){ 85 | if((fast->next == nullptr) || (fast->next->next == nullptr)){ 86 | return nullptr; 87 | } 88 | fast = (fast->next)->next; 89 | normal = normal->next; 90 | // count += 1; 91 | if(fast == normal){ 92 | break; 93 | } 94 | } 95 | // count的次数就是环的长度 96 | fast = head; 97 | // while(count > 0){ 98 | // fast = fast->next; 99 | // count--; 100 | // } 101 | while(true){ 102 | if(normal == fast){ 103 | return fast; 104 | } 105 | fast = fast->next; 106 | normal = normal->next; 107 | } 108 | } 109 | }; 110 | ``` 111 | ## 总结 112 | 链表问题还是要注重快慢指针的应用和画图出来。 -------------------------------------------------------------------------------- /leetcode/leetcode169之寻找绝对众数.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: leetcode169之寻找绝对众数 3 | tags: leetcode,算法, C++ 4 | grammar_cjkRuby: true 5 | --- 6 | ## 题目 7 | - 给出一串数字,求出其中的众数的值,保证输入的数字数组拥有绝对众数。 8 | - 绝对众数是指超过总的数字个数的一半的数字。 9 | ## 分析 10 | - 这道题被归到分治算法当中去,应当使用分解为不同过得子问题的问题。 11 | - 想了很久无法拆出子问题来求解。无法利用超过一半数量的数字这个条件。后来直接使用了map去统计数字的个数的方法。直到找到超多一半数量的数字返回。 12 | - 还有一个方法是直接排序,然后找到其中中间的数字。 13 | - **后来查看资料得知可以使用摩尔投票法**,通过不断抵消两种不同的数字,最后剩下数字肯定是绝对众数。参考资料:[enter description here](https://blog.sengxian.com/algorithms/mode)。这篇文章说明了其中的动态规划思想。就是根据遍历的过程将数组分成两个部分,记录前面的数组是否有众数,入果有,就根据当前遍历的数字更新众数的情况。如果没有,就将剩下的数组当做一个子问题,继续从开头的一个数字当做数组求解。 14 | ## 代码 15 | - 第一个版本的使用了一个map,时间复杂读是nlgn,查找的时间复杂度是lgn。一旦找到超过一半的数量的数马上返回。 16 | ```c++?linenums 17 | #include 18 | using namespace std; 19 | 20 | class Solution { 21 | public: 22 | int majorityElement(vector& nums) { 23 | if(nums.size() == 1){ 24 | return nums[0]; 25 | } 26 | int length = nums.size() / 2; 27 | // 想不到分治的方法,使用暴力统计的方法,判断是否有数量超过一半的数字 28 | // 使用一个hashmap 29 | map statistics; 30 | 31 | // 遍历数字 32 | for(auto number: nums){ 33 | if(statistics.find(number) == statistics.end()){ 34 | statistics[number] = 1; 35 | } 36 | else{ 37 | statistics[number] += 1; 38 | } 39 | if(statistics[number] > length){ 40 | return number; 41 | } 42 | } 43 | } 44 | }; 45 | ``` 46 | - 第二个版本的代码参考了网上的资料,理解了动态规划分解出子问题的原理之后写出来的。时间复杂度为n。代码量更少。 47 | ```c++?linenums 48 | class Solution { 49 | public: 50 | int majorityElement(vector& nums) { 51 | if(nums.size() == 1){ 52 | return nums[0]; 53 | } 54 | // 采用摩尔投票法,按照数组遍历到的位置将问题分为两个部分,求绝对众数 55 | int num = nums[0]; 56 | int count = 1; 57 | for(vector::size_type i = 1; i < nums.size(); i++){ 58 | if(count == 0){ 59 | num = nums[i]; 60 | count = 1; 61 | } 62 | else if(num == nums[i]){ 63 | count += 1; 64 | } 65 | else{ 66 | count -= 1; 67 | } 68 | } 69 | return num; 70 | } 71 | }; 72 | ``` 73 | ## 总结 74 | - 涉及到分治方法的题目,关键是如何拆出子问题。 -------------------------------------------------------------------------------- /leetcode/leetcode53之最大连续子数组和.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: leetcode53之最大连续子数组和 3 | tags: leetcode,算法, C++ 4 | grammar_cjkRuby: true 5 | --- 6 | ## 题目 7 | - 给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。 8 | - 输入: [-2,1,-3,4,-1,2,1,-5,4];输出: 6;解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。 9 | - 尽量实现复杂度为O(n)的算法,还可以采用分治的方法。 10 | ## 分析 11 | - 这道题第一个想法是直接暴力求解。三重循环,但是时间复杂度太高了。 12 | - 分治的方法在算法导论上面做过,将数组分成两个部分,分别求出最大子数组和,然后还要处理一种横跨两个子数组的连续数组和,比较三者之间的最大值。分治的方法写起来比较麻烦。 13 | - 参考网上的解答,可以利用动态规划。关键是如何定义状态。这道题可以定义以第i个数字作为结尾的最大子数组和为dp[i],那么以i+1个数字作为结尾的最大子数组和就可以由状态转移方程获得。 14 | - 还有一种方法是记录当前的连续数组和,根据其大小即是否小于0。如果大于0,证明以后一个数字开头的子数组加上前面的连续子数组会更大,所以应该继续叠加。如果小于0,则重新开始计算连续子数组的值。始终保持一个记录扫描过的子数组的最大值的变量。最后返回变脸即可。 15 | ## 代码 16 | 采用的是第三种方法,时间复杂读为O(n)。 17 | ```c++?linenums 18 | class Solution { 19 | public: 20 | int maxSubArray(vector& nums) { 21 | // 分析遍历数组的情况。维持一个连续的数组元素和。 22 | // 如果该数组的和大于0,那么加上一个元素之后的值肯定大于这个元素的值。 23 | // 也就是说,从后一个元素开始的子数组和加上前面的数组会更大。 24 | // 但是,如果当前的数组和是负数,加上后肯定没有从后一个元素开始的子数组的和大。 25 | int max = nums[0]; 26 | int sum = nums[0]; 27 | for(int i = 1; i < nums.size(); i++){ 28 | if(sum < 0){ 29 | sum = nums[i]; 30 | } 31 | else{ 32 | 33 | sum += nums[i]; 34 | } 35 | if(sum > max){ 36 | max = sum; 37 | } 38 | } 39 | return max; 40 | } 41 | }; 42 | ``` 43 | ## 总结 44 | 动态规划需要拆分求解的问题,分阶段求解,然后保留中间值。 -------------------------------------------------------------------------------- /leetcode/leetcode_test/137WordBread.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by windyear_office on 19-2-22. 3 | // 4 | 5 | #ifndef LEETCODE_TEST_137WORDBREAD_H 6 | #define LEETCODE_TEST_137WORDBREAD_H 7 | #include 8 | #include 9 | #include 10 | using namespace std; 11 | //动态规划,首先找出第一个能拆分出来的字符串。 12 | // 接下来在前面能够拆分的情况下依次拆分后面的。 13 | class Solution137 { 14 | public: 15 | bool wordBreak(string s, vector& wordDict) { 16 | set wordSet(wordDict.begin(), wordDict.end()); 17 | vector dp(s.size()+1, false); 18 | //int start = dp.size()-1; 19 | // for(size_t i = 0; i < dp.size(); ++i){ 20 | // if(wordSet.find(s.substr(0, i+1)) != wordSet.end()){ 21 | // dp[i] = true; 22 | // //start = i; 23 | // break; 24 | // } 25 | // } 26 | dp[0] = true; 27 | for(int i=0; i <= dp.size(); ++i){ 28 | for(int j = i +1; j <= dp.size(); ++j){ 29 | if(dp[i] && wordSet.find(s.substr(i, j-i)) != wordSet.end()){ 30 | dp[j] = true; 31 | } 32 | } 33 | } 34 | return dp.back(); 35 | } 36 | }; 37 | #endif //LEETCODE_TEST_137WORDBREAD_H 38 | -------------------------------------------------------------------------------- /leetcode/leetcode_test/138WordBreak2.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by windyear_office on 19-2-22. 3 | // 4 | 5 | #ifndef LEETCODE_TEST_138WORDBREAK2_H 6 | #define LEETCODE_TEST_138WORDBREAK2_H 7 | #include 8 | #include 9 | #include 10 | using namespace std; 11 | class Solution138 { 12 | public: 13 | vector wordBreak(string s, vector& wordDict) { 14 | set wordSet(wordDict.begin(), wordDict.end()); 15 | vector dp(s.size()+1, false); 16 | //int start = dp.size()-1; 17 | // for(size_t i = 0; i < dp.size(); ++i){ 18 | // if(wordSet.find(s.substr(0, i+1)) != wordSet.end()){ 19 | // dp[i] = true; 20 | // //start = i; 21 | // break; 22 | // } 23 | // } 24 | dp[0] = true; 25 | //用来记录下一个可以匹配的路径。 26 | vector > path(s.size()+1, vector()); 27 | for(int i=0; i < dp.size(); ++i){ 28 | for(int j = i +1; j < dp.size(); ++j){ 29 | if(dp[i] && wordSet.find(s.substr(i, j-i)) != wordSet.end()){ 30 | dp[j] = true; 31 | path[i].push_back(j); 32 | } 33 | } 34 | } 35 | // 深度优先搜索获得答案 36 | vector result; 37 | string sentence; 38 | getSentence(result, sentence, dp, 0, s, path, 0); 39 | return result; 40 | } 41 | 42 | void getSentence(vector& result, string sentence, vector& dp, int index, string& s, vector >& path, int start){ 43 | // if(index == (dp.size()-2)){ 44 | // // 添加到result 45 | // result.push_back(sentence.substr(0, sentence.size()-1)); 46 | // return; 47 | // } 48 | if(dp[index] == true){ 49 | if(index >= s.size()){ 50 | string tmp = sentence + " "+ s.substr(start, index-start); 51 | result.push_back(tmp.substr(2)); 52 | return; 53 | } 54 | for(int i = 0; i < path[index].size(); ++i){ 55 | string tmp = sentence + " "+ s.substr(start, index-start); 56 | getSentence(result, tmp, dp,path[index][i], s, path, index); 57 | } 58 | } 59 | else{ 60 | return; 61 | } 62 | } 63 | }; 64 | #endif //LEETCODE_TEST_138WORDBREAK2_H 65 | -------------------------------------------------------------------------------- /leetcode/leetcode_test/SortAlgorithm.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by windyear_office on 19-2-22. 3 | // 4 | 5 | #ifndef LEETCODE_TEST_SORTALGORITHM_H 6 | #define LEETCODE_TEST_SORTALGORITHM_H 7 | #include 8 | class SortAlgorithm{ 9 | public: 10 | void QuickSort(int* arr, int start, int end){ 11 | if(start < end){ 12 | int index = Partition(arr, start, end); 13 | QuickSort(arr, start, index-1); 14 | QuickSort(arr, index+1, end); 15 | } 16 | } 17 | 18 | // 还好有写一写,写错了。 19 | int Partition(int* arr, int low, int high){ 20 | // 左右指针法,需要一个变量来保存最后一个元素 21 | int key = arr[high]; 22 | int right = high; 23 | while(low < high){ 24 | // 如果不写等于,会出不了循环 25 | while(low < high && arr[low] <= key){ 26 | ++low; 27 | } 28 | while(low < high && arr[high] >= key){ 29 | --high; 30 | } 31 | // 交换 32 | int tmp = arr[low]; 33 | arr[low] = arr[high]; 34 | arr[high] = tmp; 35 | } 36 | std::swap(arr[low], arr[right]); 37 | return low; 38 | } 39 | 40 | void TestQuickSort(){ 41 | int arr1[] = {3, 3, 3,3}; 42 | QuickSort(arr1, 0, 3); 43 | int arr2[] = {7, 4,2}; 44 | QuickSort(arr2, 0, 2); 45 | int arr3[] = {1, 2, 3}; 46 | QuickSort(arr3, 0, 2); 47 | int arr4[] = {2,1}; 48 | QuickSort(arr4, 0, 1); 49 | printf("haha"); 50 | } 51 | }; 52 | #endif //LEETCODE_TEST_SORTALGORITHM_H 53 | -------------------------------------------------------------------------------- /leetcode/leetcode_test/TestConst.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by windyear_office on 19-2-22. 3 | // 4 | 5 | #ifndef LEETCODE_TEST_TESTCONST_H 6 | #define LEETCODE_TEST_TESTCONST_H 7 | 8 | #endif //LEETCODE_TEST_TESTCONST_H 9 | -------------------------------------------------------------------------------- /leetcode/leetcode_test/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "137WordBread.h" 3 | #include "138WordBreak2.h" 4 | #include "SortAlgorithm.h" 5 | int main() { 6 | // std::cout << "Hello, World!" << std::endl; 7 | // string s("leetcode"); 8 | // vector wordDict; 9 | // wordDict.push_back("leet"); 10 | // wordDict.push_back("code"); 11 | // Solution137 solution137; 12 | // solution137.wordBreak(s, wordDict); 13 | // string s2("catsanddog"); 14 | // vector wordDict2 = {"cat","cats","and","sand","dog"}; 15 | // Solution138 solution138; 16 | // solution138.wordBreak(s2, wordDict2); 17 | // string s3("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); 18 | // vector wordDict3 = {"a","aa","aaa","aaaa","aaaaa","aaaaaa","aaaaaaa","aaaaaaaa","aaaaaaaaa","aaaaaaaaaa"}; 19 | //solution138.wordBreak(s3, wordDict3); 20 | SortAlgorithm sortAlgorithm; 21 | sortAlgorithm.TestQuickSort(); 22 | return 0; 23 | } -------------------------------------------------------------------------------- /leetcode/leetcode_两整数之和.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: leetcode_两整数之和 3 | tags: leetcode,算法,C++ 4 | grammar_cjkRuby: true 5 | --- 6 | ## 题目 7 | - 给定一个整数数组和一个目标值,找出数组中和为目标值的两个数。 8 | 可以假设每个输入只对应一种答案,且同样的元素不能被重复利用。 9 | - 示例:给定 nums = [2, 7, 11, 15], target = 9,因为 nums[0] + nums[1] = 2 + 7 = 9,所以返回 [0, 1] 10 | 11 | ## 思路分析 12 | - 一个马上想到的思路是遍历所有整数的两两组合,直到和为目标值。可用二重循环实现。由于1+2和2+1的等效性,内层循环可以从i+1开始。 13 | - 代码: 14 | ``` 15 | class Solution { 16 | public: 17 | vector twoSum(vector& nums, int target) { 18 | if(nums.size() < 2){ 19 | vector result; 20 | return result; 21 | } 22 | vector result = {0, 0}; 23 | for(vector::size_type i = 0; i < nums.size()-1; i++){ 24 | for(vector::size_type j = i+1; j < nums.size(); j++){ 25 | if(nums[i] + nums[j] == target){ 26 | result[0] = i; 27 | result[1] = j; 28 | break; 29 | } 30 | } 31 | } 32 | return result; 33 | } 34 | }; 35 | ``` 36 | ## 算法优化 37 | - 这道题可以只用一个遍历循环,只需要用一个hashmap保存前面遍历过的元素。值为key,索引为value。遍历到下一个整数时查看前面是否有该差值的索引即可。 38 | - 代码: 39 | ``` 40 | class Solution { 41 | public: 42 | vector twoSum(vector& nums, int target) { 43 | unordered_map hash; 44 | vector result; 45 | for(int i = 0; i < nums.size(); ++i) 46 | { 47 | int k = target - nums[i]; 48 | if(hash.find(k) != hash.end()) 49 | { 50 | result.push_back(hash[k]); 51 | result.push_back(i); 52 | break; 53 | } 54 | else 55 | hash[nums[i]] = i; 56 | } 57 | return result; 58 | } 59 | }; 60 | ``` 61 | ## 总结 62 | 题目较为简单,建立索引是一个保存元素位置的重要方法。牺牲存储空间换取运行时间。 -------------------------------------------------------------------------------- /leetcode/leetcode之两数相加.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: leetcode之两数相加 3 | tags: 算法,C++, leetcode 4 | grammar_cjkRuby: true 5 | --- 6 | 7 | ## 题目描述 8 | - 给定两个非空链表来表示两个非负整数。位数按照逆序方式存储,它们的每个节点只存储单个数字。将两数相加返回一个新的链表。 9 | 你可以假设除了数字 0 之外,这两个数字都不会以零开头。 10 | - 示例:输入:(2 -> 4 -> 3) + (5 -> 6 -> 4);输出:7 -> 0 -> 8;原因:342 + 465 = 807。 11 | ## 思路 12 | - 很容易想到按照位数从低位到高位开始遍历链表,进行相加,记录是否有进位。 13 | - 难点在于链表长度不一,不知道哪个链表先结束。想到了如果有一个链表已经指向空时可以将相加的值变为0,继续进行相加知道两个链表都指向空。 14 | - 一个容易忽略的地方就是如果最后还有进位,应该最后补上一个节点。 15 | ## 代码 16 | ``` 17 | /** 18 | * Definition for singly-linked list. 19 | * struct ListNode { 20 | * int val; 21 | * ListNode *next; 22 | * ListNode(int x) : val(x), next(NULL) {} 23 | * }; 24 | */ 25 | class Solution { 26 | public: 27 | ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) { 28 | ListNode* result = new ListNode(0); 29 | // 保留一个结果头指针 30 | ListNode* result_head = result; 31 | // 将个位数相加 32 | result->val = l1->val + l2->val; 33 | // 用于保存进位的数字 34 | int add = 0; 35 | if(result->val >= 10){ 36 | add = 1; 37 | result->val = result->val - 10; 38 | } 39 | // 指针指向下一位 40 | l1 = l1->next; 41 | l2 = l2->next; 42 | // 直到两个链表都指向空为止。 43 | while(!(l1==NULL && l2==NULL)){ 44 | // 如果有一个节点是空节点将待加的值设置为0 45 | int val1 = 0; 46 | int val2 = 0; 47 | if(l1 != NULL){ 48 | val1 = l1->val; 49 | } 50 | if(l2 != NULL){ 51 | val2 = l2->val; 52 | } 53 | ListNode* new_node = new ListNode(0); 54 | new_node->val = val1 + val2 + add; 55 | // 更新进位 56 | if(new_node->val >= 10){ 57 | add = 1; 58 | new_node->val = new_node->val - 10; 59 | } 60 | else{ 61 | add = 0; 62 | } 63 | // 更新三个链表指针 64 | result->next = new_node; 65 | result = new_node; 66 | if(l1 != NULL){ 67 | l1 = l1->next; 68 | } 69 | if(l2 != NULL){ 70 | l2 = l2->next; 71 | } 72 | } 73 | // 最后还要判断是否还有进位 74 | if(add == 1){ 75 | ListNode* new_node = new ListNode(1); 76 | result->next = new_node; 77 | } 78 | return result_head; 79 | 80 | } 81 | }; 82 | ``` 83 | ## 总结 84 | 题目有一定的难度,主要在于链表指向空的判断和处理。 -------------------------------------------------------------------------------- /leetcode/leetcode之两种方法反转链表.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: leetcode之两种方法反转链表 3 | tags: leetcode,算法, C++ 4 | grammar_cjkRuby: true 5 | --- 6 | ## 题目 7 | - 反转一个单链表。可以迭代或递归地反转链表。 8 | - 示例:输入: 1->2->3->4->5->NULL;输出: 5->4->3->2->1->NULL。 9 | ## 分析 10 | - 迭代的方法可以从头开始遍历,每次将一个指向反转。在草稿纸上画出草图,很明显需要三个临时指针。反转后所有指针向后移动一步。知道最后一个指针指向空。最后更新一下最后一个节点的指向。 11 | - 别忘记了头结点要指向空。 12 | - **递归的方法**:主要思想是调用反转函数反转子链表。将子链表和头结点合并。最后返回反转的链表的头结点。 13 | ## 代码 14 | ### 迭代版本 15 | ```c++?linenums 16 | /** 17 | * Definition for singly-linked list. 18 | * struct ListNode { 19 | * int val; 20 | * ListNode *next; 21 | * ListNode(int x) : val(x), next(NULL) {} 22 | * }; 23 | */ 24 | class Solution { 25 | public: 26 | ListNode* reverseList(ListNode* head) { 27 | if((head == NULL) || (head->next == NULL)){ 28 | return head; 29 | } 30 | // 使用三个额外的指针记录 31 | ListNode* p = head; 32 | ListNode* q = head->next; 33 | ListNode* r = (head->next)->next; 34 | // 头节点变为尾节点,先将头结点指向NULL 35 | p->next = NULL; 36 | while(r != NULL){ 37 | // 反向指针 38 | q->next = p; 39 | // 三个指针向后移动一步 40 | p = q; 41 | q = r; 42 | r = r->next; 43 | } 44 | // r为NULL时还要最后更改一个指向 45 | q->next = p; 46 | return q; 47 | } 48 | }; 49 | ``` 50 | ### 递归版本 51 | ```c++?linenums 52 | /** 53 | * Definition for singly-linked list. 54 | * struct ListNode { 55 | * int val; 56 | * ListNode *next; 57 | * ListNode(int x) : val(x), next(NULL) {} 58 | * }; 59 | */ 60 | class Solution { 61 | public: 62 | ListNode* reverseList(ListNode* head) { 63 | // 采用递归的方法,就是使用反转函数反转子链表,直接当做已经反转了。 64 | if((head == NULL) || (head->next == NULL)){ 65 | return head; 66 | } 67 | else{ 68 | ListNode* son_list = reverseList(head->next); 69 | // 子链表的尾节点指向前一个节点 70 | head->next->next = head; 71 | head->next = NULL; 72 | return son_list; 73 | } 74 | } 75 | }; 76 | ``` 77 | ## 总结 78 | 链表的问题注意画出草稿图,理清楚指针的操作步骤就可以了。写递归程序最重要是把调用函数当做已经执行完了返回了值,然后需要做的后续工作,不用管递归的函数是怎样做的。 -------------------------------------------------------------------------------- /leetcode/leetcode之删除倒数第N个节点.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: leetcode之删除倒数第N个节点 3 | tags: leetcode,算法, C++ 4 | grammar_cjkRuby: true 5 | --- 6 | ## 题目 7 | - 给定一个链表,删除链表的倒数第 n 个节点,并且返回链表的头结点。 8 | - 给定一个链表: 1->2->3->4->5, 和 n = 2。当删除了倒数第二个节点后,链表变为 1->2->3->5。 9 | - 你能尝试使用一趟扫描实现吗? 10 | ## 分析 11 | - 使用一遍扫描实现,需要计算倒数。很明显是使用快慢指针的问题。 12 | - 快指针先走N步,然后一起走,到达最后的时候就能找到慢指针需要删除的节点。 13 | - 添加一个头结点方便统一处理。 14 | ## 代码 15 | ```c++?linenums 16 | /** 17 | * Definition for singly-linked list. 18 | * struct ListNode { 19 | * int val; 20 | * ListNode *next; 21 | * ListNode(int x) : val(x), next(NULL) {} 22 | * }; 23 | */ 24 | class Solution { 25 | public: 26 | ListNode* removeNthFromEnd(ListNode* head, int n) { 27 | if(head == nullptr){ 28 | return head; 29 | } 30 | if(head->next == nullptr){ 31 | return head->next; 32 | } 33 | // 单向链表特性不可反向搜索,使用快慢指针即可。 34 | // 增加一个头结点 35 | ListNode* new_node = new ListNode(0); 36 | new_node->next = head; 37 | int over_step = n; 38 | ListNode* p = new_node; 39 | ListNode* q = new_node; 40 | while(over_step != 0){ 41 | p = p->next; 42 | over_step -= 1; 43 | } 44 | while(p->next != nullptr){ 45 | p = p->next; 46 | q = q->next; 47 | } 48 | (q->next) = (q->next)->next; 49 | return new_node->next; 50 | } 51 | }; 52 | ``` 53 | ## 总结 54 | 链表问题应用快慢指针和添加哨兵头结点非常有用。 -------------------------------------------------------------------------------- /leetcode/leetcode之删除重复的有序链表的节点.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: leetcode之删除重复的有序链表的节点 3 | tags: leetcode,算法, C++ 4 | grammar_cjkRuby: true 5 | --- 6 | ## 题目 7 | - 给定一个排序链表,删除所有含有重复数字的节点,只保留原始链表中 没有重复出现 的数字。 8 | - 输入: 1->2->3->3->4->4->5:输出: 1->2->5。 9 | ## 分析 10 | 1. 这个删除重复链表的题目跟之前的不同,要把重复的元素全部删除。遍历链表可以删除重复多余的链表,但是会剩下一个。问题的关键在于怎样把有重复的最后一个节点删除。 11 | 2. 可添加一个标记,如果为true,说明有重复,向后移动指针需要将重复的最后一个删除。 12 | 3. 增加一个头结点方便统一操作。 13 | ## 代码 14 | ```c++?linenums 15 | /** 16 | * Definition for singly-linked list. 17 | * struct ListNode { 18 | * int val; 19 | * ListNode *next; 20 | * ListNode(int x) : val(x), next(NULL) {} 21 | * }; 22 | */ 23 | class Solution { 24 | public: 25 | ListNode* deleteDuplicates(ListNode* head) { 26 | // 这道题跟之前删除链表的不同之处是相同的都要删除。 27 | // 注意链表是排好序的 28 | // 加一个头结点,然后如果出现重复的情况做一个标记 29 | if((head == nullptr) || (head->next == nullptr)){ 30 | return head; 31 | } 32 | ListNode* new_head = new ListNode(0); 33 | new_head->next = head; 34 | ListNode* p = new_head; 35 | ListNode* q = head; 36 | ListNode* r = head->next; 37 | bool is_repeat = false; 38 | while(r != nullptr){ 39 | if(r->val == q->val){ 40 | // 删除r节点 41 | is_repeat = true; 42 | q->next = r->next; 43 | r = r->next; 44 | } 45 | else{ 46 | // 先判断是否重复了 47 | if(is_repeat){ 48 | p->next = r; 49 | q = r; 50 | r = r->next; 51 | is_repeat = false; 52 | } 53 | else{ 54 | p = p->next; 55 | q = q->next; 56 | r = r->next; 57 | is_repeat = false; 58 | } 59 | } 60 | } 61 | // 最后还要判断q节点之前是否有重复 62 | if(is_repeat){ 63 | p->next = nullptr; 64 | } 65 | return new_head->next; 66 | } 67 | }; 68 | ``` 69 | ## 总结 70 | 仔细画好图之后处理特殊情况,题目不算太难。 -------------------------------------------------------------------------------- /leetcode/leetcode之删除链表中给定值的节点.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: leetcode之删除链表中给定值的节点 3 | tags: C++,leetcode,算法 4 | grammar_cjkRuby: true 5 | --- 6 | ## 题目 7 | - 删除链表中等于给定值 val 的所有节点。 8 | - 示例:输入: 1->2->6->3->4->5->6, val = 6;输出: 1->2->3->4->5 9 | ## 分析 10 | - 比较简单,直接遍历一遍链表,将值相等的删除即可。删除时要保留前一个节点的指针,直接指向下一个节点。 11 | - 重点注意的地方是,如果从头节点开始就是要删除的节点,会使得算法的操作边麻烦。 12 | - 可以采用添加一个头结点的方法。最后返回头结点指向的那个节点。 13 | ## 代码 14 | ```c++?linenums 15 | /** 16 | * Definition for singly-linked list. 17 | * struct ListNode { 18 | * int val; 19 | * ListNode *next; 20 | * ListNode(int x) : val(x), next(NULL) {} 21 | * }; 22 | */ 23 | class Solution { 24 | public: 25 | ListNode* removeElements(ListNode* head, int val) { 26 | if(head == NULL){ 27 | return head; 28 | } 29 | if((head->next == NULL) && (head->val == val)){ 30 | return NULL; 31 | } 32 | // 删除节点 33 | ListNode* result = head; 34 | // 排除头结点就是val的情况 35 | while(result != NULL){ 36 | if(result->val == val){ 37 | result = result->next; 38 | } 39 | else{ 40 | break; 41 | } 42 | } 43 | if(result == NULL){ 44 | return result; 45 | } 46 | ListNode* p = result; 47 | ListNode* q = result->next; 48 | while(q != NULL){ 49 | if(q->val == val){ 50 | p->next = q->next; 51 | q = q->next; 52 | } 53 | else{ 54 | p = p->next; 55 | q = q->next; 56 | } 57 | } 58 | return result; 59 | } 60 | }; 61 | ``` 62 | **增加头结点的版本** 63 | ```c++?linenums 64 | /** 65 | * Definition for singly-linked list. 66 | * struct ListNode { 67 | * int val; 68 | * ListNode *next; 69 | * ListNode(int x) : val(x), next(NULL) {} 70 | * }; 71 | */ 72 | class Solution { 73 | public: 74 | ListNode* removeElements(ListNode* head, int val) { 75 | if(head == NULL){ 76 | return head; 77 | } 78 | if((head->next == NULL) && (head->val == val)){ 79 | return NULL; 80 | } 81 | // 删除节 82 | // 排除头结点就是val的情况 83 | ListNode* new_head = new ListNode(0); 84 | new_head->next = head; 85 | ListNode* p = new_head; 86 | ListNode* q = new_head->next; 87 | while(q != NULL){ 88 | if(q->val == val){ 89 | p->next = q->next; 90 | q = q->next; 91 | } 92 | else{ 93 | p = p->next; 94 | q = q->next; 95 | } 96 | } 97 | return new_head->next; 98 | } 99 | }; 100 | ``` 101 | ## 总结 102 | 题目并不难,主要是特殊情况的处理要注意。在链表的头增加一个哨兵节点是非常重要的一个方法,这样就可以解放头结点的作用,方便进行处理。 -------------------------------------------------------------------------------- /leetcode/leetcode之删除链表中重复的元素.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: leetcode之删除链表中重复的元素 3 | tags: C++,leetcode,算法 4 | grammar_cjkRuby: true 5 | --- 6 | ## 题目 7 | - 给定一个排序链表,删除所有重复的元素,使得每个元素只出现一次。 8 | - 输入: 1->1->2;输出: 1->2 9 | 10 | ## 分析 11 | - 题目比较简单,直接遍历一次即可。使用两个指针,如果后一个节点的值和前面一个相等,删除后面的一个节点。 12 | - 注意传入的头结点为空或者只有一个元素的情况。 13 | ## 代码 14 | ```c++?linenums 15 | /** 16 | * Definition for singly-linked list. 17 | * struct ListNode { 18 | * int val; 19 | * ListNode *next; 20 | * ListNode(int x) : val(x), next(NULL) {} 21 | * }; 22 | */ 23 | class Solution { 24 | public: 25 | ListNode* deleteDuplicates(ListNode* head) { 26 | if((head == NULL) || (head->next == NULL)){ 27 | return head; 28 | } 29 | ListNode* p = head; 30 | ListNode* q = head->next; 31 | while(q != NULL){ 32 | if(p->val == q->val){ 33 | p->next = q->next; 34 | q = q->next; 35 | } 36 | else{ 37 | p = p->next; 38 | q = q->next; 39 | } 40 | } 41 | return head; 42 | } 43 | }; 44 | ``` 45 | ## 总结 46 | - 题目比较简单,也不用添加哨兵节点。 -------------------------------------------------------------------------------- /leetcode/leetcode之删除链表的节点.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: leetcode之删除链表的节点 3 | tags: leetcode,C++,算法 4 | grammar_cjkRuby: true 5 | --- 6 | ## 题目 7 | - 只给出一个节点信息,删除该节点。 8 | - 链表至少包含两个节点。 9 | 链表中所有节点的值都是唯一的。 10 | 给定的节点为非末尾节点并且一定是链表中的一个有效节点。 11 | 不要从你的函数中返回任何结果。 12 | ## 思路 13 | 这道题要比较巧妙的思路,一开始的时候没有想到。因为链表是不能向前走的,所以不能找到前面一个节点。那么应该如何删除当前的节点呢?其实可以理解链表的节点只是值不同,值替换了其实链表的节点的顺序也相当于替换了。所以可以删除下一个节点,然后保留其值到当前节点中即可。 14 | ## 代码 15 | ```c++?linenums 16 | /** 17 | * Definition for singly-linked list. 18 | * struct ListNode { 19 | * int val; 20 | * ListNode *next; 21 | * ListNode(int x) : val(x), next(NULL) {} 22 | * }; 23 | */ 24 | class Solution { 25 | public: 26 | void deleteNode(ListNode* node) { 27 | // 由于不能找到前面的指针,所以只能删除后一个节点,将值保留。 28 | node->val = node->next->val; 29 | node->next = node->next->next; 30 | } 31 | }; 32 | ``` 33 | ## 总结 34 | 这道题比较巧妙,如果想的到可以两行代码解决,如果想不到就没办法了。 -------------------------------------------------------------------------------- /leetcode/leetcode之合并有序链表.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: leetcode之合并有序链表 3 | tags: leetcode,算法,C++ 4 | grammar_cjkRuby: true 5 | --- 6 | 7 | ## 题目 8 | 1. 将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 9 | 2. 示例:输入:1->2->4, 1->3->4;输出:1->1->2->3->4->4 10 | 11 | ## 分析 12 | - 思路比较简单,直接遍历两个链表,知道两个到达结尾。每次合并一个节点。 13 | - 值得注意的是两个链表结束的时间不同,所以先判断是否有一个链表已经到达结尾,如果到达了直接合并另一个链表节点。 14 | - 二者都非空比较大小后合并。 15 | 16 | ## 代码 17 | - 速度较快,8ms。 18 | ```c++?linenums 19 | /** 20 | * Definition for singly-linked list. 21 | * struct ListNode { 22 | * int val; 23 | * ListNode *next; 24 | * ListNode(int x) : val(x), next(NULL) {} 25 | * }; 26 | */ 27 | class Solution { 28 | public: 29 | ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) { 30 | ListNode* result = new ListNode(0); 31 | // 保存返回的指针 32 | ListNode* result_point = result; 33 | if((l1 == NULL) && (l2 == NULL)){ 34 | return NULL; 35 | } 36 | if(l1 == NULL){ 37 | return l2; 38 | } 39 | if(l2 == NULL){ 40 | return l1; 41 | } 42 | while((l1 != NULL) or (l2 != NULL)){ 43 | if(l1 == NULL){ 44 | ListNode* new_node = new ListNode(l2->val); 45 | result->next = new_node; 46 | result = new_node; 47 | l2 = l2->next; 48 | continue; 49 | } 50 | if(l2 == NULL){ 51 | ListNode* new_node = new ListNode(l1->val); 52 | result->next = new_node; 53 | result = new_node; 54 | l1 = l1->next; 55 | continue; 56 | } 57 | if(l2->val < l1->val){ 58 | ListNode* new_node = new ListNode(l2->val); 59 | result->next = new_node; 60 | result = new_node; 61 | l2 = l2->next; 62 | } 63 | else{ 64 | ListNode* new_node = new ListNode(l1->val); 65 | result->next = new_node; 66 | result = new_node; 67 | l1 = l1->next; 68 | } 69 | } 70 | return result_point->next; 71 | } 72 | }; 73 | ``` 74 | ## 总结 75 | 题目较为简单,但是要主要保留第一个节点。还有每次合并到result链表后,result要从新指向最后一个节点。 -------------------------------------------------------------------------------- /leetcode/leetcode之回文链表.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: leetcode之回文链表 3 | tags: leetcode,算法, C++ 4 | grammar_cjkRuby: true 5 | --- 6 | ## 题目 7 | - 请判断一个链表是否为回文链表。 8 | - 输入: 1->2->2->1;输出: true 9 | ## 分析 10 | - 第一时间想到的是利用栈,额外的存储空间保存前面一半的节点值,然后跟后一半比较是否相等即可。 11 | - 还有一个方法是翻转一半的链表。 12 | - 由于单向链表不能反向搜索,所以经常应用快慢指针。比如用快慢指针找到一半的位置。 13 | ## 代码 14 | ```c++?linenums 15 | /** 16 | * Definition for singly-linked list. 17 | * struct ListNode { 18 | * int val; 19 | * ListNode *next; 20 | * ListNode(int x) : val(x), next(NULL) {} 21 | * }; 22 | */ 23 | #include 24 | 25 | static auto lo = [] { 26 | std::ios::sync_with_stdio(false); 27 | cin.tie(0); 28 | return 0; 29 | }(); 30 | class Solution { 31 | public: 32 | bool isPalindrome(ListNode* head) { 33 | //想不到不用额外空间的方法 34 | //使用栈来存储数据 35 | if((head == nullptr) ||(head->next == nullptr)){ 36 | return true; 37 | } 38 | stack val; 39 | // 统计长度 40 | int size_of_list = 0; 41 | ListNode* point = head; 42 | while(point != nullptr){ 43 | size_of_list += 1; 44 | point = point->next; 45 | } 46 | int a, b; 47 | a = size_of_list / 2; 48 | b = size_of_list % 2; 49 | // 分单数和双数的情况 50 | if(b == 0){ 51 | while(a > 0){ 52 | a--; 53 | val.push(head->val); 54 | head = head->next; 55 | } 56 | while(head != nullptr){ 57 | if(head->val != val.top()){ 58 | return false; 59 | } 60 | head = head->next; 61 | val.pop(); 62 | } 63 | return true; 64 | } 65 | else{ 66 | while(a > 0){ 67 | a--; 68 | val.push(head->val); 69 | head = head->next; 70 | } 71 | head = head->next; 72 | while(head != nullptr){ 73 | if(head->val != val.top()){ 74 | return false; 75 | } 76 | head = head->next; 77 | val.pop(); 78 | } 79 | return true; 80 | } 81 | } 82 | }; 83 | ``` 84 | ## 总结 85 | 反向顺序需要马上想到栈,链表问题少不了快慢指针。 -------------------------------------------------------------------------------- /leetcode/leetcode之找出相交链表的交点.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: leetcode之找出相交链表的交点 3 | tags: leetcode,算法, C++ 4 | grammar_cjkRuby: true 5 | --- 6 | ## 题目 7 | - 编写一个程序,找到两个单链表相交的起始节点。 8 | - 如果相交只会是y型相交,如果不想交就返回空指针。 9 | - **O(1)空间和O(n)时间** 10 | ## 分析 11 | - 直接采取暴力二重循环可以求解,但是超过时间限制。 12 | - 一个思路是先找出两个链表长度相差的值,然后一个链表先走相差的步数,如果相交,可以找到相交的节点。 13 | - 还有一个方法是建立hash表,但是超出存储的限制了。 14 | - 一种比较巧妙的方法是人工构建环,如果从A的尾部接到B的头部后构成了环,说明相交,但是找出相交的节点还比较麻烦。 15 | ## 代码 16 | ```c++?linenums 17 | /** 18 | * Definition for singly-linked list. 19 | * struct ListNode { 20 | * int val; 21 | * ListNode *next; 22 | * ListNode(int x) : val(x), next(NULL) {} 23 | * }; 24 | */ 25 | static int x = []() { 26 | ios::sync_with_stdio(false); // cin与stdin禁止同步 27 | cin.tie(NULL); //cin与cout解除绑定 28 | return 0; 29 | }(); 30 | class Solution { 31 | public: 32 | ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) { 33 | // 排除两个节点有一个为空的情况 34 | if((headA == NULL) || (headB == NULL)){ 35 | return NULL; 36 | } 37 | // 先遍历统计一个两个链表的长度,然后使指针同步。即更长的先走几步。 38 | int count_a = 0; 39 | int count_b = 0; 40 | ListNode* point_a = headA; 41 | ListNode* point_b = headB; 42 | while(point_a != NULL){ 43 | count_a += 1; 44 | point_a = point_a->next; 45 | } 46 | while(point_b != NULL){ 47 | count_b += 1; 48 | point_b = point_b->next; 49 | } 50 | if(count_a > count_b){ 51 | int c = count_a - count_b; 52 | while(c > 0){ 53 | headA = headA->next; 54 | c = c -1; 55 | } 56 | while(headA != NULL){ 57 | if(headA == headB){ 58 | return headA; 59 | }else{ 60 | headA = headA->next; 61 | headB = headB->next; 62 | } 63 | } 64 | }else if(count_a < count_b){ 65 | int c = count_b - count_a; 66 | while(c > 0){ 67 | headB = headB->next; 68 | c = c-1; 69 | } 70 | while(headA != NULL){ 71 | if(headA == headB){ 72 | return headA; 73 | }else{ 74 | headA = headA->next; 75 | headB = headB->next; 76 | } 77 | } 78 | } 79 | else{ 80 | while(headA != NULL){ 81 | if(headA == headB){ 82 | return headA; 83 | }else{ 84 | headA = headA->next; 85 | headB = headB->next; 86 | } 87 | } 88 | } 89 | return NULL; 90 | } 91 | }; 92 | ``` 93 | ## 总结 94 | 写的比较啰嗦,应该还可以简化。这道题不仅是判断相交,还要找出交点。还是通过画图得出相交后的节点都是相同的,所以将链表变为等长然后同时遍历即可。 -------------------------------------------------------------------------------- /leetcode/leetcode之环形链表.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: leetcode之环形链表 3 | tags: C++,leetcode,算法 4 | grammar_cjkRuby: true 5 | --- 6 | ## 题目描述 7 | - 给定一个链表,判断链表中是否有环。不用额外空间。 8 | ## 题目分析 9 | - 可以使用快慢指针,快指针比满指针快一步,如果有环就一定可以追上。如果到达了空指针,说明没有环。 10 | - 注意比较相等的时候要比较指针相等,不是节点的值相等。 11 | ## 代码 12 | ```c++?linenums 13 | /** 14 | * Definition for singly-linked list. 15 | * struct ListNode { 16 | * int val; 17 | * ListNode *next; 18 | * ListNode(int x) : val(x), next(NULL) {} 19 | * }; 20 | */ 21 | class Solution { 22 | public: 23 | bool hasCycle(ListNode *head) { 24 | // 快慢指针,一个走一步,一个走两步,如果有环肯定能碰到,如果没有就最后都指向空。 25 | bool has_cycle = false; 26 | if((head == NULL) || (head->next == NULL)){ 27 | return false; 28 | } 29 | ListNode* p1 = head; 30 | ListNode* p2 = head; 31 | while((p1 != NULL) || (p2 != NULL)){ 32 | // 往下走一步和两步 33 | // 如果走到结尾,证明是无环 34 | if(p1->next == NULL){ 35 | return false; 36 | } 37 | else{ 38 | p1 = p1->next; 39 | } 40 | if(p2->next == NULL){ 41 | return false; 42 | } 43 | else{ 44 | p2 = p2->next; 45 | } 46 | if(p2->next == NULL){ 47 | return false; 48 | } 49 | else{ 50 | p2 = p2->next; 51 | } 52 | if(p1 == p2 ){ 53 | return true; 54 | } 55 | } 56 | return false; 57 | } 58 | }; 59 | ``` 60 | **改进版代码:**因为快指针走得更快,所以如果没有环肯定先到达尾部,所以只需判断快指针是否指向空。 61 | ```c++?linenums 62 | /** 63 | * Definition for singly-linked list. 64 | * struct ListNode { 65 | * int val; 66 | * ListNode *next; 67 | * ListNode(int x) : val(x), next(NULL) {} 68 | * }; 69 | */ 70 | class Solution { 71 | public: 72 | bool hasCycle(ListNode *head) { 73 | // 快慢指针,一个走一步,一个走两步,如果有环肯定能碰到,如果没有就最后都指向空。 74 | bool has_cycle = false; 75 | if((head == NULL) || (head->next == NULL)){ 76 | return false; 77 | } 78 | ListNode* p1 = head; 79 | ListNode* p2 = head; 80 | // 快指针先到达,所以可以只检查快指针。 81 | // 不能先检查next 82 | while((p2 != NULL) && (p2->next != NULL)){ 83 | // 往下走一步和两步 84 | // 如果走到结尾,证明是无环 85 | p1 = p1->next; 86 | p2 = p2->next; 87 | p2 = p2->next; 88 | if(p1 == p2){ 89 | return true; 90 | } 91 | } 92 | return false; 93 | } 94 | }; 95 | ``` 96 | **一个坑:** 判断指针的是否不能先判断next,有可能快指针已经指向了空。 97 | ## 总结 98 | 一到快慢指针典型的题目。 -------------------------------------------------------------------------------- /leetcode/source/1.两数之和.cpp: -------------------------------------------------------------------------------- 1 | class Solution { 2 | public: 3 | vector twoSum(vector& nums, int target) { 4 | if(nums.size() < 2){ 5 | vector result; 6 | return result; 7 | } 8 | vector result = {0, 0}; 9 | for(vector::size_type i = 0; i < nums.size()-1; i++){ 10 | for(vector::size_type j = i+1; j < nums.size(); j++){ 11 | if(nums[i] + nums[j] == target){ 12 | result[0] = i; 13 | result[1] = j; 14 | break; 15 | } 16 | } 17 | } 18 | return result; 19 | } 20 | }; 21 | -------------------------------------------------------------------------------- /leetcode/source/15.三数之和.cpp: -------------------------------------------------------------------------------- 1 | // 固定一个数,剩下数找对应的和为差值的数。 2 | // 剩下的数可以根据指针从两边向中间寻找的过程。 3 | #include 4 | using namespace std; 5 | 6 | class Solution { 7 | public: 8 | vector> threeSum(vector& nums) { 9 | vector > result; 10 | if(nums.size() < 3){ 11 | return result; 12 | } 13 | // 先排序 14 | sort(nums.begin(), nums.end()); 15 | // 设定三个指针和一个存结果的变量 16 | vector group(3,0); 17 | int fixed = 0; 18 | int start = fixed+1; 19 | int end = nums.size()-1; 20 | int dif = 0 - nums[fixed]; 21 | // fixed需要跳过重复的三元组。 22 | for(;fixed < nums.size() - 2; ){ 23 | start = fixed + 1; 24 | end = nums.size() -1; 25 | dif = 0 - nums[fixed]; 26 | // bool用于加快速度提前退出。 27 | //bool is_found = false; 28 | // 两指针还没相遇时 29 | while(start < end){ 30 | // 如果存在 31 | if((nums[start]+nums[end]) == dif){ 32 | group[0] = nums[fixed]; 33 | group[1] = nums[start]; 34 | group[2] = nums[end]; 35 | result.push_back(group); 36 | --end; 37 | while(nums[end] == nums[end+1]){ 38 | --end; 39 | } 40 | ++start; 41 | while(nums[start] == nums[start-1]){ 42 | ++start; 43 | } 44 | // is_found = true; 45 | } 46 | else{ 47 | //if(is_found){ 48 | //break; 49 | //} 50 | if((nums[start]+nums[end]) > dif){ 51 | --end; 52 | while(nums[end] == nums[end+1]){ 53 | --end; 54 | } 55 | } 56 | else{ 57 | ++start; 58 | while(nums[start] == nums[start-1]){ 59 | ++start; 60 | } 61 | } 62 | } 63 | } 64 | ++fixed; 65 | while(nums[fixed] == nums[fixed-1]){ 66 | ++fixed; 67 | } 68 | } 69 | return result; 70 | } 71 | }; 72 | -------------------------------------------------------------------------------- /leetcode/source/2.两链表表示的数相加.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * Definition for singly-linked list. 3 | * struct ListNode { 4 | * int val; 5 | * ListNode *next; 6 | * ListNode(int x) : val(x), next(NULL) {} 7 | * }; 8 | */ 9 | class Solution { 10 | public: 11 | ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) { 12 | ListNode* result = new ListNode(0); 13 | // 保留一个结果头指针 14 | ListNode* result_head = result; 15 | // 将个位数相加 16 | result->val = l1->val + l2->val; 17 | // 用于保存进位的数字 18 | int add = 0; 19 | if(result->val >= 10){ 20 | add = 1; 21 | result->val = result->val - 10; 22 | } 23 | // 指针指向下一位 24 | l1 = l1->next; 25 | l2 = l2->next; 26 | // 直到两个链表都指向空为止。 27 | while(!(l1==NULL && l2==NULL)){ 28 | // 如果有一个节点是空节点将待加的值设置为0 29 | int val1 = 0; 30 | int val2 = 0; 31 | if(l1 != NULL){ 32 | val1 = l1->val; 33 | } 34 | if(l2 != NULL){ 35 | val2 = l2->val; 36 | } 37 | ListNode* new_node = new ListNode(0); 38 | new_node->val = val1 + val2 + add; 39 | // 更新进位 40 | if(new_node->val >= 10){ 41 | add = 1; 42 | new_node->val = new_node->val - 10; 43 | } 44 | else{ 45 | add = 0; 46 | } 47 | // 更新三个链表指针 48 | result->next = new_node; 49 | result = new_node; 50 | if(l1 != NULL){ 51 | l1 = l1->next; 52 | } 53 | if(l2 != NULL){ 54 | l2 = l2->next; 55 | } 56 | } 57 | // 最后还要判断是否还有进位 58 | if(add == 1){ 59 | ListNode* new_node = new ListNode(1); 60 | result->next = new_node; 61 | } 62 | return result_head; 63 | 64 | } 65 | }; 66 | -------------------------------------------------------------------------------- /leetcode/source/20.有效的括号.cpp: -------------------------------------------------------------------------------- 1 | // 使用一个栈辅助匹配,把匹配的括号对弹出,遇到不能匹配的即为不合法的括号串。 2 | #include 3 | 4 | class Solution { 5 | public: 6 | bool isValid(string s) { 7 | if(s.size() < 1){ 8 | return true; 9 | } 10 | else if(s.size() == 1){ 11 | return false; 12 | } 13 | else{ 14 | stack brackers; 15 | bool is_valid = true; 16 | for(size_t i = 0; i < s.size(); ++i){ 17 | if(s[i] == '(' || s[i] == '[' || s[i] == '{'){ 18 | brackers.push(s[i]); 19 | } 20 | else{ 21 | switch(s[i]){ 22 | case ')': 23 | if(!brackers.empty() && brackers.top() == '('){ 24 | brackers.pop(); 25 | } 26 | else{ 27 | is_valid = false; 28 | } 29 | break; 30 | case ']': 31 | { 32 | if(!brackers.empty() && brackers.top() == '['){ 33 | brackers.pop(); 34 | } 35 | else{ 36 | is_valid = false; 37 | } 38 | break; 39 | } 40 | case '}': 41 | { 42 | if(!brackers.empty() && brackers.top() == '{'){ 43 | brackers.pop(); 44 | } 45 | else{ 46 | is_valid = false; 47 | } 48 | break; 49 | } 50 | } 51 | } 52 | if(!is_valid){ 53 | break; 54 | } 55 | } 56 | if(is_valid && brackers.empty()){ 57 | return true; 58 | } 59 | else{ 60 | return false; 61 | } 62 | } 63 | } 64 | }; 65 | -------------------------------------------------------------------------------- /leetcode/source/22.括号生成.cpp: -------------------------------------------------------------------------------- 1 | // 深度优先搜索加剪枝,从左到右左括号数量要大于等于右括号。 2 | // 记录左右括号的数量。 3 | 4 | class Solution { 5 | public: 6 | vector generateParenthesis(int n) { 7 | vector result; 8 | if(n <= 0){ 9 | return result; 10 | } 11 | string s; 12 | getParenthesis(n, 0, s, 0, 0, result); 13 | return result; 14 | } 15 | 16 | void getParenthesis(int total, int cur, string& s, int open, int close, vector& result){ 17 | if(cur == 2 * total){ 18 | result.push_back(s); 19 | // 不用pop_back()出来。上层函数负责pop_back(); 20 | //s.pop_back(); 21 | return; 22 | } 23 | else{ 24 | if(open < total){ 25 | s.push_back('('); 26 | getParenthesis(total, cur+1, s, open+1, close, result); 27 | s.pop_back(); 28 | } 29 | if(close < open){ 30 | s.push_back(')'); 31 | getParenthesis(total, cur+1, s, open, close+1, result); 32 | s.pop_back(); 33 | } 34 | } 35 | 36 | } 37 | }; 38 | -------------------------------------------------------------------------------- /leetcode/source/3.无重复字符的最长子串.cpp: -------------------------------------------------------------------------------- 1 | // 用一个map保存前面的字符和位置。 2 | // 一个指针标记当前不重复子字符串的位置。 3 | #include 4 | class Solution { 5 | public: 6 | int lengthOfLongestSubstring(string s) { 7 | int len = 0; 8 | map index; 9 | int pre = 0; 10 | //int len = 0; 11 | for(size_t i = 0; i < s.size(); ++i){ 12 | // 判断之前是否出现过该字符。 13 | // 如果没有出现。则添加 14 | if(index.find(s[i]) == index.end()){ 15 | index[s[i]] = i; 16 | int cur_len = i - pre + 1; 17 | if(cur_len > len){ 18 | len = cur_len; 19 | } 20 | else{ 21 | continue; 22 | } 23 | } 24 | // 如果之前出现过 25 | else{ 26 | if(index[s[i]] >= pre){ 27 | pre = index[s[i]]+1; 28 | int cur_len = i - pre +1; 29 | // 更新位置 30 | index[s[i]] = i; 31 | if(cur_len > len){ 32 | len = cur_len; 33 | } 34 | else{ 35 | continue; 36 | } 37 | } 38 | else{ 39 | index[s[i]] = i; 40 | int cur_len = i - pre +1; 41 | if(cur_len > len){ 42 | len = cur_len; 43 | } 44 | else{ 45 | continue; 46 | } 47 | } 48 | } 49 | } 50 | return len; 51 | } 52 | }; 53 | -------------------------------------------------------------------------------- /leetcode/source/32.最长有效括号.cpp: -------------------------------------------------------------------------------- 1 | // 动态规划,d[i]表示以i结尾的最长有效子串长度 2 | class Solution { 3 | public: 4 | int longestValidParentheses(string s) { 5 | if(s.size() < 2){ 6 | return 0; 7 | }else{ 8 | vector length(s.size(),0); 9 | length[0] = 0; 10 | int max = 0; 11 | for(size_t i = 1; i < s.size(); ++i){ 12 | // 如果是左括号则不能跟前面匹配 13 | if(s[i] == '('){ 14 | length[i] = 0; 15 | } 16 | else{ 17 | if((i-length[i-1]-1)>=0 && s[i-length[i-1]-1] == '('){ 18 | length[i] = length[i-1]+2; 19 | if((i-length[i-1]-2) >=0){ 20 | length[i] += length[i-length[i-1]-2]; 21 | } 22 | if(length[i] > max){ 23 | max = length[i]; 24 | } 25 | } 26 | else{ 27 | length[i] = 0; 28 | } 29 | } 30 | } 31 | return max; 32 | } 33 | } 34 | }; 35 | -------------------------------------------------------------------------------- /leetcode/真题/编程笔试题之句子反转.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 编程笔试题之句子反转 3 | tags: 笔试,C++ 4 | grammar_cjkRuby: true 5 | --- 6 | ## 题目 7 | - 给定一个句子(只包含字母和空格), 将句子中的单词位置反转,单词用空格分割, 单词之间只有一个空格,前后没有空格。 比如: (1) “hello xiao mi”-> “mi xiao hello”。 8 | - 输入数据有多组,每组占一行,包含一个句子(句子长度小于1000个字符)。 9 | - 对于每个测试示例,要求输出句子中单词反转后形成的句子 10 | ## 分析 11 | 1. 反转想到使用栈,然后就是解决存储单词的问题。 12 | 2. 存储单词使用string,读入整句,然后用string的find和substr分割出单词. 13 | 3. 最后注意输出的格式问题,最后不要多带了空格。 14 | ## 具体代码 15 | 16 | ``` cpp?linenums 17 | #include 18 | #include 19 | #include 20 | using namespace std; 21 | 22 | int main(){ 23 | // 每一行保存为一个字符串 24 | string line; 25 | while(getline(cin, line)){ 26 | // 使用一个堆栈来存储字符串中的单词,通过寻找分隔符找出单词。 27 | string::size_type space_index, word_start=0; 28 | space_index = line.find(' ', 0); 29 | stack words; 30 | while(space_index != string::npos){ 31 | words.push(line.substr(word_start, space_index - word_start)); 32 | word_start = space_index + 1; 33 | space_index = line.find(' ', word_start); 34 | } 35 | // 添加最后一个单词 36 | words.push(line.substr(word_start)); 37 | // 逐个输出单词,注意最后一个没有空格 38 | while(true){a 39 | cout << words.top(); 40 | words.pop(); 41 | if(!words.empty()){ 42 | cout << ' '; 43 | } 44 | else{ 45 | break; 46 | } 47 | } 48 | cout << endl; 49 | } 50 | } 51 | ``` -------------------------------------------------------------------------------- /leetcode/真题/编程笔试题之求水仙花数.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 编程笔试题之求水仙花数 3 | tags: 笔试,算法, C++ 4 | grammar_cjkRuby: true 5 | --- 6 | ## 题目 7 | 春天是鲜花的季节,水仙花就是其中最迷人的代表,数学上有个水仙花数,他是这样定义的: “水仙花数”是指一个三位数,它的各位数字的立方和等于其本身,比如:153=1^3+5^3+3^3。 现在要求输出所有在m和n范围内的水仙花数。 8 | ## 分析 9 | 没有什么难度,暴力求解即可。注意格式的控制。本次采取一个vector记录数字,最后再调输出格式。 10 | ## 代码 11 | ```c++?linenums 12 | #include 13 | #include 14 | #include 15 | 16 | using namespace std; 17 | 18 | int main(){ 19 | int num1; 20 | int num2; 21 | while(cin >> num1 >> num2){ 22 | // 只需要求出一次三个数字,之后加上1就可以了。 23 | vector flower_num; 24 | int start = num1; 25 | while(start <= num2){ 26 | int a, b, c; 27 | c = num1 % 10; 28 | num1 = num1 / 10; 29 | b = num1 % 10; 30 | num1 = num1 / 10; 31 | a = num1 % 10; 32 | int sum = pow(a, 3) + pow(b, 3) + pow(c, 3); 33 | int num = a * 100 + b * 10 + c; 34 | if(sum == num){ 35 | flower_num.push_back(num); 36 | } 37 | start++; 38 | num1 = start; 39 | } 40 | if(flower_num.size() == 0){ 41 | cout << "no" << endl; 42 | } 43 | else{ 44 | int end_num = flower_num.back(); 45 | flower_num.pop_back(); 46 | for(int num: flower_num){ 47 | cout << num << ' '; 48 | } 49 | cout << end_num << endl; 50 | } 51 | } 52 | } 53 | ``` 54 | ## 总结 55 | 暴力求解,没有难度。 -------------------------------------------------------------------------------- /leetcode/真题/编程笔试题值求数列的和.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 编程笔试题值求数列的和 3 | tags: 笔试,算法, C++ 4 | grammar_cjkRuby: true 5 | --- 6 | ## 题目 7 | 数列的第一项为n,以后各项为前一项的平方根,求数列的前m项的和。 8 | ## 分析 9 | 题目没有难度,调用库函数sqrt求出平方根,然后控制输出的精度需要设置。 10 | ## 代码 11 | ```c++?linenums 12 | #include 13 | #include 14 | 15 | using namespace std; 16 | 17 | int main(){ 18 | double n, m; 19 | cout.flags(ios::fixed); 20 | cout.precision(2); 21 | while(cin >> n >> m){ 22 | double sum = 0.0; 23 | while(m > 0){ 24 | sum += n; 25 | n = sqrt(n); 26 | m -= 1; 27 | } 28 | 29 | cout << sum << endl; 30 | } 31 | } 32 | ``` 33 | ## 总结 34 | 题目不难,主要学习了如何设置输出的格式。 -------------------------------------------------------------------------------- /linux/软链接硬链接.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windyear/AlgorithmsAndDataStructure/ebc6f4f3e055f7400a67fd578a3e8744b8fb2b40/linux/软链接硬链接.png -------------------------------------------------------------------------------- /基础知识整理/C++内存布局.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windyear/AlgorithmsAndDataStructure/ebc6f4f3e055f7400a67fd578a3e8744b8fb2b40/基础知识整理/C++内存布局.png -------------------------------------------------------------------------------- /基础知识整理/C++精选.md: -------------------------------------------------------------------------------- 1 | 1. sizeof的原理:编译器求值关于sizeof的两个精巧的宏实现。 2 | 非数组的sizeof: #define _sizeof(T) ( (size_t)((T*)0 + 1)) 3 | 数组的sizeof: #define array_sizeof(T) ( (size_t)(&T+1) - (size_t)(&T) ) 4 | 5 | 对数组类型取地址,然后加1是跳过整个地址的操作。 6 | 7 | 2. 浮点数的表示,一个图说清楚,不能进行等于比较。 8 | 9 | ![](浮点数.png) 10 | 11 | 3. 数组的操作![](二维数组.png) 12 | 4. 13 | -------------------------------------------------------------------------------- /基础知识整理/POSIX信号量加共享内存实现两个进程简单通信(一收一发).md: -------------------------------------------------------------------------------- 1 | ### POSIX信号量加共享内存实现两个进程简单通信(一收一发) 2 | 3 | #### 要点 4 | 5 | - 用了POSIX具名信号量,可以在进程间共享 6 | - 用共享内存当做通信介质 7 | - 信号量初始值为0,实现同步的功能,需要发送端写入信息后通知接收方接收。 8 | - 其他细节在代码注释中 9 | - POSIXSem_w.cpp为发送端代码,POSIXSem_r.cpp为接收端代码。 10 | 11 | ### 代码 12 | 13 | - POSIXSem_w.cpp 14 | 15 | ```c++ 16 | // 一个配合共享内存实现进程间同步的Posix信号量demo 17 | #define SHM_KEY 1111 18 | #define SEM_NAME "/TestSem" 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | int main(){ 27 | // 打开一段共享内存。 28 | int shm_id = shmget(SHM_KEY, sizeof(int), 0666 | IPC_CREAT); 29 | // 判断是否打开成功 30 | if( shm_id < 0){ 31 | printf("fail to create share memory.\n"); 32 | return -1; 33 | } 34 | // 连接共享内存到进程空间,注意第二个参数一般为NULL 35 | void* shmptr = shmat(shm_id, NULL, 0); 36 | // 判断是否创建成功 37 | if(shmptr == (void*)-1){ 38 | printf("fail to link the share memory.\n"); 39 | return -1; 40 | } 41 | // 打开一个命名信号量 42 | // 因为做的是同步,所以初始信号量的值为0,写入数据后POST一下 43 | sem_t* sem_id = sem_open(SEM_NAME, O_CREAT, S_IRWXU, 0); 44 | int* data = static_cast(shmptr); 45 | while(1){ 46 | // 从标准输入读入数据 47 | scanf("%d", data); 48 | 49 | // 通知读端取走,这里其实有一个同步的问题,如果输入很快的话,还来不及读取,但是因为会等待在标准输入,所以没问题 50 | sem_post(sem_id); 51 | // 输入-1退出进程 52 | if(*data == -1){ 53 | break; 54 | } 55 | } 56 | return 0; 57 | } 58 | ``` 59 | 60 | - 接收端代码 61 | 62 | ```c++ 63 | // 一个配合共享内存实现进程间同步的Posix信号量demo 64 | #define SHM_KEY 1111 65 | #define SEM_NAME "/TestSem" 66 | 67 | #include 68 | #include 69 | #include 70 | #include 71 | #include 72 | 73 | int main(){ 74 | // 打开一段共享内存。 75 | int shm_id = shmget(SHM_KEY, sizeof(int), 0666 | IPC_CREAT); 76 | // 判断是否打开成功 77 | if( shm_id < 0){ 78 | printf("fail to create share memory.\n"); 79 | return -1; 80 | } 81 | // 连接共享内存到进程空间,注意第二个参数一般为NULL 82 | void* shmptr = shmat(shm_id, NULL, 0); 83 | // 判断是否创建成功 84 | if(shmptr == (void*)-1){ 85 | printf("fail to link the share memory.\n"); 86 | return -1; 87 | } 88 | // 打开一个命名信号量 89 | // 因为做的是同步,所以初始信号量的值为0,写入数据后POST一下 90 | sem_t* sem_id = sem_open(SEM_NAME, O_CREAT, S_IRWXU, 0); 91 | int* data = static_cast(shmptr); 92 | while(1){ 93 | // 等待信号量通知 94 | sem_wait(sem_id); 95 | // 从标准输入读入数据 96 | printf("%d\n", *data); 97 | // 通知读端取走,这里其实有一个同步的问题,如果输入很快的话,还来不及读取,但是因为会等待在标准输入,所以没问题 98 | // 输入-1退出进程 99 | if(*data == -1){ 100 | break; 101 | } 102 | } 103 | return 0; 104 | } 105 | ``` 106 | 107 | -------------------------------------------------------------------------------- /基础知识整理/c++面试题.md: -------------------------------------------------------------------------------- 1 | 1. 基类构造函数或析构函数中,将派生类对象看做基类。即调用虚函数时,运行的是构造函数自身类型的版本。因为此时对象还不完整。 2 | 2. C++的布局负担主要由虚函数和虚基类构成的。 3 | 3. 类型信息放在虚函数表。如果没有虚函数表放在?![](C++内存布局.png) -------------------------------------------------------------------------------- /基础知识整理/二维数组.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windyear/AlgorithmsAndDataStructure/ebc6f4f3e055f7400a67fd578a3e8744b8fb2b40/基础知识整理/二维数组.png -------------------------------------------------------------------------------- /基础知识整理/单实例进程.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windyear/AlgorithmsAndDataStructure/ebc6f4f3e055f7400a67fd578a3e8744b8fb2b40/基础知识整理/单实例进程.png -------------------------------------------------------------------------------- /基础知识整理/浮点数.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windyear/AlgorithmsAndDataStructure/ebc6f4f3e055f7400a67fd578a3e8744b8fb2b40/基础知识整理/浮点数.png -------------------------------------------------------------------------------- /基础知识整理/用文件锁实现程序只能有一个进程实例.md: -------------------------------------------------------------------------------- 1 | ### 用文件锁实现程序只能有一个进程实例 2 | 3 | #### 原理:利用文件锁,如果能加唯一写锁说明没有程序占用,否则已有进程,关闭该进程。 4 | 5 | #### 用途:用于需要单进程实例的程序比如守护进程。 6 | 7 | #### 代码示例: 8 | 9 | ```cpp 10 | // 11 | // Created by windyear_office on 19-2-28. 12 | // 一个用文件锁实现进程只能打开单个实例的程序。 13 | #include // fcntl sturct flock 14 | #include // exit 15 | #include // printf 16 | #include // close 17 | #include // errno 18 | #include // strlen 19 | #define lockfile "OneInstance.pid" 20 | int LockFile(int fd){ 21 | // flock结构体表明锁的一些设置 22 | struct flock f1; 23 | f1.l_type = F_WRLCK; // 设置为唯一写锁,如果是同一个进程不会失败,会替换原来的锁。 24 | f1.l_start = 0; 25 | f1.l_whence = SEEK_SET; 26 | f1.l_len = 0; // 设置锁的大小为为整个文件 27 | return fcntl(fd, F_SETLK, &f1); //出错会返回,errno为EACCES或者EAGAIN。 28 | } 29 | 30 | int main(){ 31 | int fd; 32 | fd = open(lockfile, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); 33 | if(fd < 0){ 34 | printf("can't open the lock file.\n"); 35 | exit(1); 36 | } 37 | // 进行加锁操作 38 | if(LockFile(fd) < 0){ 39 | if(errno == EACCES || errno == EAGAIN){ 40 | printf("The process only can have one instance.\n"); 41 | close(fd); 42 | return 1; 43 | } 44 | printf("can't lock.\n"); 45 | exit(-1); 46 | } 47 | 48 | // 截断文件 49 | ftruncate(fd, 0); 50 | char buf[32]; 51 | sprintf(buf, "%ld", (long)getpid()); 52 | write(fd, buf, strlen(buf)+1); 53 | while(1){ 54 | printf("It's the one instance process.\n"); 55 | sleep(1); 56 | } 57 | return 0; 58 | } 59 | 60 | ``` 61 | 62 | -------------------------------------------------------------------------------- /基础知识整理/算法思路总结.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windyear/AlgorithmsAndDataStructure/ebc6f4f3e055f7400a67fd578a3e8744b8fb2b40/基础知识整理/算法思路总结.md --------------------------------------------------------------------------------