diff --git "a/15\344\270\252\347\273\217\345\205\270\345\237\272\347\241\200\347\256\227\346\263\225/DP \347\256\227\346\263\225" "b/15\344\270\252\347\273\217\345\205\270\345\237\272\347\241\200\347\256\227\346\263\225/DP \347\256\227\346\263\225" new file mode 100644 index 0000000..ec84237 --- /dev/null +++ "b/15\344\270\252\347\273\217\345\205\270\345\237\272\347\241\200\347\256\227\346\263\225/DP \347\256\227\346\263\225" @@ -0,0 +1,94 @@ +三角形问题: +step 1 + +N = 5 +n\r n = 1,2,3...; r=1,2,3... +7 +3 8 +8 1 0 +2 7 4 4 +4 5 2 6 5 + +max value = D(r,j) + +new max value = max(D(r+1,j),D(r+1,j+1)) + D(r,j) + +vvv code vvv +#include +#include +#define MAX 101 +using namespace std; +int D[MAX][MAX]; +int n; +int MaxSum(int i, int j){ + if(i==n) + return D[i][j]; + int x = MaxSum(i+1,j); + int y = MaxSum(i+1,j+1); + return max(x,y)+D[i][j]; +} +int main(){ + int i,j; + cin >> n; + for(i=1;i<=n;i++) + for(j=1;j<=i;j++) + cin >> D[i][j]; + cout << MaxSum(1,1) << endl; +} + +递归转换为递推 +#include +#include +using namespace std; + +#define MAX 101 + +int D[MAX][MAX]; +int n; +int maxSum[MAX][MAX]; +int main(){ + int i,j; + cin >> n; + for(i=1;i<=n;i++) + for(j=1;j<=i;j++) + cin >> D[i][j]; + for( int i = 1;i <= n; ++ i ) + maxSum[n][i] = D[n][i]; + for( int i = n-1; i>= 1; --i ) + for( int j = 1; j <= i; ++j ) + maxSum[i][j] = max(maxSum[i+1][j],maxSum[i+1][j+1]) + D[i][j]; + cout << maxSum[1][1] << endl; +} + +空间优化后,final step +#include +#include +using namespace std; + +#define MAX 101 + +int D[MAX][MAX]; +int n; +int * maxSum; + +int main(){ + int i,j; + cin >> n; + for(i=1;i<=n;i++) + for(j=1;j<=i;j++) + cin >> D[i][j]; + maxSum = D[n]; //maxSum指向第n行 + for( int i = n-1; i>= 1; --i ) + for( int j = 1; j <= i; ++j ) + maxSum[j] = max(maxSum[j],maxSum[j+1]) + D[i][j]; + cout << maxSum[1] << endl; +} + +四个步骤: +划分主要问题为子问题 +确定问题状态,每一个状态,都是一个子问题得到的解 +确定初始状态,三角形问题中,初始化边的数量等 +确定状态转移方程 +定义出什么是“状态”,以及在该“状态”下的“值” +在此之后,就要找出不同的状态之间如何迁移,即如何从一个或多个“值”已知的 “状态”,求出另一个“状态”的“值”(递推型)。 +状态的迁移可以用递推公式表示,此递推公式也可被称作“状态转移方程”。 diff --git "a/15\344\270\252\347\273\217\345\205\270\345\237\272\347\241\200\347\256\227\346\263\225/README.md" "b/15\344\270\252\347\273\217\345\205\270\345\237\272\347\241\200\347\256\227\346\263\225/README.md" index 449803c..ae335d7 100644 --- "a/15\344\270\252\347\273\217\345\205\270\345\237\272\347\241\200\347\256\227\346\263\225/README.md" +++ "b/15\344\270\252\347\273\217\345\205\270\345\237\272\347\241\200\347\256\227\346\263\225/README.md" @@ -4,21 +4,61 @@ ## Dijkstra:最短路径算法 >Dijkstra是荷兰的计算机科学家,提出”信号量和PV原语“,"解决哲学家就餐问题",”死锁“也是它提出来的 -##DP (Dynamic Programming) -动态规划 +## DP (Dynamic Programming) +动态规划: +动态规划法所针对的问题有一个显著的特征,即它所对应的子问题树中的子问题呈现大量的重复。动态规划法的关键就在于,对于重复出现的子问题,只在第一次遇到时加以求解,并把答案保存起来,让以后再遇到时直接引用,不必重新求解 +经典案例: +1.丢硬币: + +2.背包: +一个最优解的子解,也是最优解。 + +3.最大子数组和问题: + +4.三角形问题 +https://github.com/chengmo03013106/Learn-Algorithms/blob/master/15%E4%B8%AA%E7%BB%8F%E5%85%B8%E5%9F%BA%E7%A1%80%E7%AE%97%E6%B3%95/DP%20%E7%AE%97%E6%B3%95 ##BFS/DFS (广度/深度优先遍历) -##红黑树 +## 红黑树 一种自平衡的`二叉查找树` +map和hash_map性能测试 +大家都知道在C++的STL中map是使用树来做查找算法,而hash_map使用hash表来排列配对,是使用关键字来计算表位置。那使用起来他们的差别主要是什么呢?对于性能差别是什么,适合什么情况下应用呢?于是我对它们进行了一些测试,并记录了测试数据供大家分享。 + 测试的内容主要是map和hash_map的添加、删除、查找和遍历操作,首先进行了几组测试,分别是10万次、30万次,时间单位均为毫秒,具体的性能对照如下: + + 1| hash_map(10万) | map(10万) | hash_map(20万) | map(20万) | hash_map(30万) | map(30万)
+ 2| ------| ------- | ------ | ------ | ------- | ------
+ 3|添加 | 93 | 47 | 156 | 94 | 203 | 172
+ 4|遍历 | 16 | 15 | 16 | 16 | 16 | 15
+ 5|查找 | 0 | 0 | 32 | 31 | 31 | 32
+ 6|删除 | 84 | 2 | 2 | 32 | 33765 | 63 | 76016 | 78
+ + +hash_map的添加和删除操作比map要慢,尤其是删除操作hash_map比map可能慢1000倍,从而得到结论是删除和插入操作较多的情况下,map比hash_map的性能更好,添加和删除的数据量越大越明显。 + + +时间单位仍为毫秒:map遍历性能较好,查找性能低于hash map + hash_map(100万) map(100万) hash_map(200万) map(200万) hash_map(300万) map(300万) + 遍历 94 31 203 32 297 47 + 查找 94 234 188 531 281 875 + +### 结论: +**两大组测试完毕,整体结论也可以得出:一般应用情况下,我们保存的数据不超过100万份,查找的频繁程度不高情况下使用map性能比较好;而保存的数据较多时(超过100万),查找频繁时使用hash_map的性能就高于map了。** + ##KMP 字符串匹配算法 ##遗传算法 -##启发式搜索 +## 启发式搜索 +启发式简单例子: +内存分配: +*** +if num == size + size *= 2 +*** ##图像特征提取之SIFT算法 diff --git "a/15\344\270\252\347\273\217\345\205\270\345\237\272\347\241\200\347\256\227\346\263\225/map_vs_unorder_map.cpp" "b/15\344\270\252\347\273\217\345\205\270\345\237\272\347\241\200\347\256\227\346\263\225/map_vs_unorder_map.cpp" new file mode 100644 index 0000000..17d90eb --- /dev/null +++ "b/15\344\270\252\347\273\217\345\205\270\345\237\272\347\241\200\347\256\227\346\263\225/map_vs_unorder_map.cpp" @@ -0,0 +1,107 @@ +#include +#include +#include +#include +#include +using namespace std; + +const int kRunTime1 = 1000*1000; // 循环次数 +const int kRunTime2 = 1000*10000; +int main() +{ + std::map mp; + std::unordered_map unordermp; + + timeval st, et; + + cout << "插入个数 = " << kRunTime1 << endl; + gettimeofday(&st, NULL); + for(int i = 0; i < kRunTime1; ++i) + { + mp.insert(make_pair(i, i)); + } + gettimeofday(&et, NULL); + cout << "1 有序map测试时间insert time:" << (et.tv_sec-st.tv_sec)*1000 + (et.tv_usec-st.tv_usec)/1000 << "ms" << endl; + + gettimeofday(&st, NULL); + for(int i = 0; i < kRunTime1; ++i) + { + unordermp.insert(make_pair(i, i)); + } + gettimeofday(&et, NULL); + cout << "1 无序map测试时间insert time:" << (et.tv_sec-st.tv_sec)*1000 + (et.tv_usec-st.tv_usec)/1000 << "ms" << endl; + + cout << "\n插入个数 = " << kRunTime2 << endl; + mp.clear(); + gettimeofday(&st, NULL); + for(int i = 0; i < kRunTime2; ++i) + { + mp.insert(make_pair(i, i)); + } + gettimeofday(&et, NULL); + cout << "2 有序map测试时间insert time:" << (et.tv_sec-st.tv_sec)*1000 + (et.tv_usec-st.tv_usec)/1000 << "ms" << endl; + + mp.clear(); + gettimeofday(&st, NULL); + for(int i = 0; i < kRunTime2; ++i) + { + mp.emplace(make_pair(i, i)); + } + gettimeofday(&et, NULL); + cout << "2 有序map测试时间emplace time:" << (et.tv_sec-st.tv_sec)*1000 + (et.tv_usec-st.tv_usec)/1000 << "ms" << endl; + + unordermp.clear(); + gettimeofday(&st, NULL); + for(int i = 0; i < kRunTime2; ++i) + { + unordermp.insert(make_pair(i, i)); + } + gettimeofday(&et, NULL); + cout << "2 无序map测试时间insert time:" << (et.tv_sec-st.tv_sec)*1000 + (et.tv_usec-st.tv_usec)/1000 << "ms" << endl; + + unordermp.clear(); + gettimeofday(&st, NULL); + for(int i = 0; i < kRunTime2; ++i) + { + unordermp.emplace(make_pair(i, i)); + } + gettimeofday(&et, NULL); + cout << "2 无序map测试时间emplace time:" << (et.tv_sec-st.tv_sec)*1000 + (et.tv_usec-st.tv_usec)/1000 << "ms" << endl; + + return 0; +} +--------------------- + +/* first round */ +插入个数 = 1000000 +1 有序map测试时间insert time:922ms +1 无序map测试时间insert time:360ms + +插入个数 = 10000000 +2 有序map测试时间insert time:10451ms +2 有序map测试时间emplace time:10531ms +2 无序map测试时间insert time:3854ms +2 无序map测试时间emplace time:2956ms +--------------------- +/* second round */ +插入个数 = 1000000 +1 有序map测试时间insert time:918ms +1 无序map测试时间insert time:344ms + +插入个数 = 10000000 +2 有序map测试时间insert time:10470ms +2 有序map测试时间emplace time:10597ms +2 无序map测试时间insert time:3826ms +2 无序map测试时间emplace time:2932ms +--------------------- +/* third round*/ +插入个数 = 1000000 +1 有序map测试时间insert time:909ms +1 无序map测试时间insert time:376ms + +插入个数 = 10000000 +2 有序map测试时间insert time:10395ms +2 有序map测试时间emplace time:10505ms +2 无序map测试时间insert time:4015ms +2 无序map测试时间emplace time:3102ms +--------------------- diff --git "a/\350\241\245\345\205\205:C\350\257\255\350\250\200\344\273\243\347\240\201\350\247\204\350\214\203.md" "b/C\350\257\255\350\250\200\344\273\243\347\240\201\350\247\204\350\214\203" similarity index 100% rename from "\350\241\245\345\205\205:C\350\257\255\350\250\200\344\273\243\347\240\201\350\247\204\350\214\203.md" rename to "C\350\257\255\350\250\200\344\273\243\347\240\201\350\247\204\350\214\203" diff --git a/README.md b/README.md index d2fdc4f..28c87d7 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ 这里的内容是我学习算法过程的一些记录,希望能一直坚持下去。 -##学习方法 +## 学习方法 * 把所有经典算法写一遍 * 看算法有关源码 @@ -12,7 +12,7 @@ * 刷题 -##基本数据结构和算法 +## 基本数据结构和算法 这些算法全部自己敲一遍: @@ -21,7 +21,7 @@ * 链表 * 双向链表 -### 二叉树 +### 二叉树, std set,map 就是使用平衡二叉树实现 * 二叉树 * 二叉查找树 @@ -39,12 +39,12 @@ * 斐波那契堆(Fibonacci Heap) -###哈希表/散列表 (Hash Table) +### 哈希表/散列表 (Hash Table) * 散列函数 * 碰撞解决 -###字符串算法 +### 字符串算法 * 排序 * 查找 @@ -55,7 +55,7 @@ * 数据压缩 -###图的算法 +### 图的算法 * 图的存储结构和基本操作(建立,遍历,删除节点,添加节点) * 最小生成树 @@ -91,14 +91,16 @@ * 哈希表: O(1) -###15个经典基础算法 +### 15个经典基础算法 * Hash * 快速排序 * 快递选择SELECT * BFS/DFS (广度/深度优先遍历) -* 红黑树 (一种自平衡的`二叉查找树`) +* 红黑树 (一种自平衡的`二叉查找树`): +通过每次插入,删除后旋转,位置红黑树性质,同时以保证最坏情况下时间复杂度仍然可以接受 * KMP 字符串匹配算法 +https://github.com/chengmo03013106/Learn-Algorithms/blob/master/%E5%AD%97%E7%AC%A6%E4%B8%B2/kmp%20%E7%AE%97%E6%B3%95 * DP (动态规划 dynamic programming) * A*寻路算法: 求解最短路径 * Dijkstra:最短路径算法 (八卦下:Dijkstra是荷兰的计算机科学家,提出”信号量和PV原语“,"解决哲学家就餐问题",”死锁“也是它提出来的) @@ -223,13 +225,13 @@ [程序员编程艺术](http://blog.csdn.net/v_JULY_v/article/details/6460494) -###基本算法演示 +### 基本算法演示 http://sjjg.js.zwu.edu.cn/SFXX/sf1/sfys.html http://www.cs.usfca.edu/~galles/visualization/Algorithms.html -###编程网站 +### 编程网站 http://leetcode.com/ http://openjudge.cn/ 开放在线程序评测平台,可以床架自己的OJ小组 diff --git "a/\344\272\214\345\217\211\346\240\221/3-\345\271\263\350\241\241\346\240\221AVL/README.md" "b/\344\272\214\345\217\211\346\240\221/3-\345\271\263\350\241\241\346\240\221AVL/README.md" index cacf95a..e613715 100644 --- "a/\344\272\214\345\217\211\346\240\221/3-\345\271\263\350\241\241\346\240\221AVL/README.md" +++ "b/\344\272\214\345\217\211\346\240\221/3-\345\271\263\350\241\241\346\240\221AVL/README.md" @@ -1,9 +1,14 @@ ### AVL 实现过程中的问题 +不平衡定义:左右子树深度>=2 +1. RR,root右旋90度 +2. LL,root左旋90度 +3. RL,先右旋,再左旋;右旋时,甩锅到小弟,小弟执行右旋90度;领导直接踩在有问题的同事(引起不平衡的节点)头上;然后执行左旋;之前如果有子树,先去掉;等旋转结束后,再回来按照大小处理子树位置。 +4. LR,先左旋,再右旋;左旋时,帅锅到小弟,小弟替领导左旋90度;同上,左右相反。 ### AVL 实际使用案例 * LLVM 的 ImmutableSet,其底层的实现选择为 AVL 树 - * 《一种基于二叉平衡树的P2P覆盖网络的研究》论文 \ No newline at end of file + * 《一种基于二叉平衡树的P2P覆盖网络的研究》论文 diff --git "a/\345\255\227\347\254\246\344\270\262/README.md" "b/\345\255\227\347\254\246\344\270\262/README.md" index 4bb1247..cfaa97f 100644 --- "a/\345\255\227\347\254\246\344\270\262/README.md" +++ "b/\345\255\227\347\254\246\344\270\262/README.md" @@ -8,6 +8,16 @@ * 正则表达式 * 数据压缩 +### 十进制数转换成R进制数 +思路:将目标十进制数不断除以R,并取余,直到结果为0为止 +需要注意的是,最后得到的结果不是数值,而是数值字符串 + +### R进制数转换成十进制数 +按权展开 +比如二进制 1001001 +1*pow(2,0) + 0*pow(2,1) + 0*pow(2,2) + 1*pow(2,3) + 0*pow(2,4) + 0*pow(2,5) + 1*pow(2,6) + +r进制数 改用pow(r,数值) ### 排序 diff --git "a/\345\255\227\347\254\246\344\270\262/kmp \347\256\227\346\263\225" "b/\345\255\227\347\254\246\344\270\262/kmp \347\256\227\346\263\225" new file mode 100644 index 0000000..078eb84 --- /dev/null +++ "b/\345\255\227\347\254\246\344\270\262/kmp \347\256\227\346\263\225" @@ -0,0 +1,21 @@ +KMP 算法 +两字符串n,m进行字符串匹配 +m字符串长度l +关键是求数组next的值 +next是m字符的部分匹配度 +部分匹配度:字符串前缀,后缀的最大公共部分的长度 +PS,前缀,后缀不同于平时使用的前缀,后缀 +例如: +ACDB前缀:包含第一个字符,而不包含最后一个字符的全部子串 +A,AC,ACD +ACDB后缀:不包含最后第一个字符,包含最后一个字符的所有子串 +B,DB,CDB + + A C D B A C + m 0 1 2 3 4 5 + next 0 0 0 0 1 2 + 即next数组[0,0,0,0,1,2] + + 而next的求解很关键,采用动态规划概念 + + 未完待续 diff --git "a/\346\216\222\345\272\217\347\256\227\346\263\225/README.md" "b/\346\216\222\345\272\217\347\256\227\346\263\225/README.md" index 2154964..2ebef2c 100644 --- "a/\346\216\222\345\272\217\347\256\227\346\263\225/README.md" +++ "b/\346\216\222\345\272\217\347\256\227\346\263\225/README.md" @@ -93,46 +93,7 @@ 扫描过程分2种: 1. 挖坑排序,2头向中间扫描,先从后向前找,再从前向后找。 2. 单向扫描 - - void quicksort(int *a, int left, int right){ - if (left=tmp){ - j--; - } - if (i=tmp){ + j--; + } + if (inext = (h)->next; + (x)->next->prev = x; + (x)->prev = h; + (h)->next = x; +} + +//尾部插入 +int insert_tail(h,x) { + (x)->prev = (h)->prev; + (x)->prev->next = x; + (x)->next = h; + (h)->prev = x; +} +``` + +用法: +``` +my_node_t x,y; +insert_head(&sentinel,&x); +insert_tail(&sentinel,&y); +``` +PS:保持哨兵(sentinel)节点的 prev 指向链表的尾数据节点,next 指向链表的头数据节点 + + + + + + +