├── README.md ├── Tree ├── BPlusTree.c ├── BTree.c ├── BalancedBinaryTree.c ├── BinarySearchTree.c ├── Makefile ├── README.md └── RedBlackTree.c ├── data_structure ├── DoubleCircularLinkedList.c ├── DoubleLinkedList.c ├── HashTable.c ├── Makefile ├── PriorityQueue.c ├── Queue.c ├── SingleCircularLinkedList.c ├── SingleLinkedList.c ├── StackByArr.c └── StackByDoubleLinkedList.c ├── include ├── algorithm.h ├── dll.h └── type.h ├── lib └── .gitkeep ├── search ├── BinarySearch.c └── Makefile ├── sort ├── BinInsertSort.c ├── BubbleSort.c ├── InsertSort.c ├── Makefile ├── MergeSort.c ├── QuickSort.c ├── SelectSort.c └── ShellSort.c └── src ├── algorithm.c └── dll.c /README.md: -------------------------------------------------------------------------------- 1 | # algorithm_in_c 2 | 3 | C语言的数据结构和算法实现 - 代码中几乎每一行都添加了注释,通过这种甚至有点啰嗦的方式,希望让人们能够容易的掌握数据结构和算法 4 | 5 | 一开始我们不太懂,所以需要了解这些数据结构和算法, 6 | 7 | 渐渐地我们可以实现这些数据结构和算法, 8 | 9 | 但是实现之后不要忘了,我们应该对我们的实现进行评估,并且对结构和算法本身有一定的认识才行 10 | 11 | 所欲数据结构和算法的出现,必然是为了解决特定的问题,如果能基于问题出发理解数据结构和算法,则会印象更加深刻 12 | 13 | 如果对数据结构和算法有热情或者其他疑问,欢迎加入QQ群: 530133597 14 | 15 | ### TODO - 有太多要做的事情,这里也只是罗列出一部分 16 | 17 | - 编写非递归的方案 18 | - 编写单元测试 19 | - 重写和封装链表,栈,堆,树等结构 20 | - 更直观的GUI来展示算法 21 | - 完善各个数据结构和算法的数学推导 22 | 23 | 24 | ### [排序](#sort---排序) 25 | - [x] [冒泡排序](#1.1) 26 | - [x] [直接插入排序](#1.2) 27 | - [x] [希尔排序](#1.3) 28 | - [x] [二分查找插入排序](#1.4) 29 | - [x] [选择排序](#1.5) 30 | - [x] [快速排序](#1.6) 31 | - [x] [归并排序](#1.7) 32 | - [ ] [堆排序](#1.8) 33 | 34 | ### [查找](#search---查找) 35 | - [x] [二分查找](#2.1) 36 | 37 | ### [数据结构](#data_structure---数据结构) 38 | - [x] [单向链表](#3.1) 39 | - [x] [双向链表](#3.2) 40 | - [x] [单向循环链表](#3.3) 41 | - [x] [双向循环链表](#3.4) 42 | - [x] [栈 - 通过数组实现](#3.5) 43 | - [x] [栈 - 通过双向链表实现](#3.6) 44 | - [x] [队列 - 使用双向链表实现](#3.7) 45 | - [x] [优先级队列 - 使用双向链表实现](#3.8) 46 | - [ ] [优先级队列 - 使用堆实现](#3.9) 47 | - [x] [哈希表 - 使用拉链法处理哈希冲突的实现](#3.10) 48 | - [ ] [堆](#3.11) 49 | 50 | ### [Tree - 树](#tree---树-1) 51 | - [x] [树 - 二叉查找树(BST)](#3.12) 52 | - [x] [树 - 平衡二叉树(AVL)](#3.13) 53 | - [x] [树 - 红黑树(RBT)](#3.14) 54 | - [ ] [树 - 哈希树](#3.17) 55 | - [x] [树 - B树](#3.15) 56 | - [x] [树 - B+树](#3.16) 57 | - [ ] [树 - B*树](#3.17) 58 | - [ ] [树 - Treap树](#3.19) 59 | - [ ] [树 - Trie树](#3.20) 60 | - [ ] [树 - R树](#3.18) 61 | - [ ] [树 - 伸展树](#3.18) 62 | - [ ] [树 - SB树 - Size Balanced Tree](#3.21) 63 | - [ ] [树 - Merkle Tree - Dynamo中用来同步数据一致性的算法](#3.22) 64 | - [ ] [树 - LSM Tree - Log-Structured Merge-Trees](#3.23) 65 | 66 | ### [Graph Theory - 图论](#graph_theory---图论) 67 | - [ ] [图 - 拓扑排序] 68 | - [ ] [图 - 最短路径算法] 69 | - [ ] [图 - 最小生成树] 70 | - [ ] [图 - 深度优先搜索] 71 | - [ ] [图 - NP完全性] 72 | 73 | ### [加密](#encryption---加密) 74 | - [ ] [AES] 75 | - [ ] [Blowfish] 76 | - [ ] [IDEA] 77 | - [ ] [RC4] 78 | 79 | ### [一致性算法](#consistency---一致性) 80 | - [ ] [Paxos] 81 | 82 | ## 使用方法: 83 | 84 | > 进入各个目录执行make即可编译文件,之后运行文件即可 85 | 86 | ## Sort - 排序 87 | 88 | ##### BubbleSort 冒泡排序 89 | ##### InsertSort - Direct Insert Sort 插入排序(直接插入排序) 90 | ##### ShellSort 希尔排序 91 | ##### BinInsertSort 二分查找插入排序 92 | 93 | - 直接插入排序对于大部分有序的序列排序时速度快。 94 | - 二分插入排序在直接插入的基本上改变了查找元素插入位置的方法,对于完全无序的序列来说,速度会变快,但是对于大部分有序的序列反而会更慢。 95 | - 希尔排序则利用直接插入排序对于大部分有序的序列速度快的特点,先让大部分序列有序,以此来提高排序效率。 96 | 97 | ##### SelectSort 选择排序 98 | ##### QuickSort 快速排序 99 | ##### MergeSort 归并排序 100 | 101 | ## Search - 查找 102 | 103 | ##### BinarySearch 二分查找 104 | 105 | ## data_structure - 数据结构 106 | 107 | ##### SingleLinkedList 单向链表 108 | ##### DoubleLinkedList 双向链表 109 | ##### SingleCircularLinkedList 单向循环链表 110 | ##### DoubleCircularLinkedList 双向循环链表 111 | ##### StackByArr 通过数组实现的栈 112 | ##### StackByDoubleLinkedList 通过双向链表实现的栈 113 | ##### Queue 通过双向链表实现的队列 114 | ##### PriorityQueue 通过双向链表实现的优先级队列 115 | ##### PriorityQueue 通过堆实现的优先级队列 116 | ##### HashTable 哈希表 117 | 118 | - 哈希表的主要问题在于设计好的哈希函数(可以得到均匀分布的key)和如何处理哈希冲突(hash collision) 119 | 120 | - 哈希表的长度应该是质数(也称为素数) 121 | 122 | - 哈希表一直在避免的就是过多的聚集 123 | 124 | - 哈希表处理冲突的主要三个方式是: 125 | 126 | 1. 线性再散列,就是如果冲突则按照步长一直找(步长与哈希表长度应该是互质的),找到一个空槽(slot)为止 127 | 128 | 2. 非线性再散列,如果冲突,则重新计算出一个哈希值 129 | 130 | 以上两种再散列法的特点为: 131 | 132 | - 再散列法都不能删除元素,否则会导致元素找不到(因为每一个元素都可能发生过冲突,而如果这个元素被删除,那么跟其发生过冲突并且再散列的元素就找不到了) 133 | - 当散列表负载因子(插入表中的元素个数/可用槽的总数)增大时,再散列花费的时间也会越长 134 | - 表的大小就是所能容纳的元素的大小 135 | 136 | 3. 拉链法(是解决哈希冲突的最常用方法),在发生冲突的槽中使用链表存放冲突元素,也就是使用链表数组来解决冲突 137 | 138 | ## Tree - 树 139 | 140 | ##### BinarySearchTree 二叉查找树 141 | ##### BalancedBinaryTree 平衡二叉树 - 又称为AVL树,因其作者(Adelson-Velskii and Landis)而得名 142 | ##### RedBlackTree 红黑树 - 通过为节点着色并且为色彩赋予一定的规则来实现平衡 143 | 144 | 145 | ##### B-Tree B树 - 又称为Balanced Tree,一棵多路搜索树,用途很广,几乎是所有数据库的默认索引结构 146 | ##### B+ Tree B+树 - 是一种读写代价更低,查询效率更稳定(因为非终端节点都是关键字的索引,所以每个关键字的查询长度相同)的B树的变形,适应于文件系统 147 | ##### B* Tree B*树 - B+树的变形,是一种对于子节点利用率更加高的B+树 148 | 149 | 150 | -------------------------------------------------------------------------------- /Tree/BPlusTree.c: -------------------------------------------------------------------------------- 1 | /** 2 | * B+树不但比B树简单,还比B树高效,并且使用更广泛 3 | * 4 | * m阶B+树的定义: 5 | * 6 | * 1. 每个节点至少有m棵子树 7 | * 2. 所有非叶节点的关键字数目等于他的孩子数量 8 | * 3. 除根节点以外的其他节点,必须有m/2棵子树 9 | * 4. 所有关键字信息都保存在叶子节点 10 | * 5. 所有叶子节点都在同一层,并且关键字数目大于ceil(m/2) 11 | * 6. 所有非叶子节点中的关键字都是子节点中的最大或者最小关键字 12 | * 13 | * B+树的特点简单点说,就是除了叶子节点,其他节点只是保存关键字的索引,而不是保存关键字,所有关键字都保存在叶子终端结点中 14 | * 15 | * 感觉最欠缺的还是调试方式和调试工具,靠printf打断点来调试真的效率低下 16 | * 三个星期的时间,磕磕绊绊的才写出B+树,而且还只有添加,节点删除还没写,另外这个B+树也不稳固 17 | * 那些写B+树觉得难的,应该心里会有些安慰吧,比我笨的应该很少了... 18 | * 19 | * 2016-07-04: 20 | * B+树的删除仍然比其他操作都复杂,但是竟然用了一个下午的时间就实现了......并且中间没有调试,一气呵成!然后几乎是一次调试就通过了。。。真的假的。。。连我自己都佩服我自己了...... 21 | * 因为删除操作中包括合并节点,借关键字,更新索引等等操作,东西还是蛮多的,没想到能一次通过。。。。。。佩服自己! 22 | * 23 | * 2016-07-05: 24 | * 我还是太乐观了,在我昨天就以为已经搞定删除的时候,没想到在删除合并到最后根节点的地方又有问题,然后才发现自己删除节点的时候,自始至终的节点合并都有问题, 25 | * 在合并完当前节点之后,应该一直递归遍历往祖先节点合并的,另外在合并到祖先节点的时候也有bug。。。 26 | * 就这样在经历了一整天无数次的痛苦调试之后,才终于调通删除逻辑。。。啊。。。真的好辛苦啊。。。 27 | * 每当觉得基本告一段落的时候,就发现其实自己高兴太早了。。。会有无数个bug不知道从哪里蹦出来。。。 28 | * 还是水平不够吧@_@ 29 | * 30 | * 31 | * 32 | * Q & A 33 | * Q1:能不能有二阶B+树? 34 | * A1:一般B+树种的阶M>2,所以二阶B+树没有意义 35 | * 36 | * 37 | * 38 | * @Description: Description 39 | * @Datetime: 2016-07-04 10:37:00 40 | * @Author: linjunjie 41 | */ 42 | 43 | #include "algorithm.h" 44 | 45 | #define BPLUSTREE_M 3 // B+树阶 46 | #define T (int)ceil((float)BPLUSTREE_M/2) //B+树的度 47 | 48 | typedef struct bptree_node{ 49 | int keynum; //关键字数量 50 | int isleaf; //是否叶节点 51 | int pos; //当前节点在父节点的位置 52 | int keys[BPLUSTREE_M]; //关键字域 53 | struct bptree_node * parent; //父指针 54 | struct bptree_node * left; //左兄弟指针,增加这个是为了删除时候合并的方便 55 | struct bptree_node * right; //右兄弟指针 56 | struct bptree_node * children[BPLUSTREE_M]; //保存的孩子指针 57 | } bpnode; 58 | 59 | bpnode * bptree_root; 60 | 61 | bpnode * bptree_create_node(); 62 | void bptree_init(); 63 | void bptree_print(bpnode * root); 64 | void bptree_insert(bpnode * root, int key); 65 | void bptree_split(bpnode * node); 66 | bpnode * find_insert_place(bpnode * node, int key); 67 | 68 | //删除 69 | bool bptree_remove(int key); 70 | bpnode * find_key(bpnode * node, int key); 71 | void update_index ( bpnode * node ); 72 | void bptree_merge( bpnode * current , bpnode * target ); 73 | void bptree_merge_recursive( bpnode * node ); 74 | 75 | /** 76 | * 创建一个新的节点 77 | */ 78 | bpnode * bptree_create_node() 79 | { 80 | bpnode * dummy_root = (bpnode *)malloc(sizeof(bpnode)); 81 | dummy_root -> parent = null; 82 | dummy_root -> left = null; 83 | dummy_root -> right = null; 84 | dummy_root -> keynum = 0; 85 | dummy_root -> isleaf = 1; //默认叶节点 86 | for (int i = 0; i < BPLUSTREE_M; ++i) 87 | { 88 | dummy_root -> keys[i] = 0; 89 | } 90 | for (int i = 0; i < BPLUSTREE_M; ++i) 91 | { 92 | dummy_root -> children[i] = null; 93 | } 94 | return dummy_root; 95 | } 96 | 97 | void bptree_init() 98 | { 99 | if( bptree_root == null ) 100 | { 101 | bptree_root = bptree_create_node(); 102 | } 103 | } 104 | 105 | void bptree_print(bpnode * root) 106 | { 107 | if( root == null ) 108 | { 109 | return; 110 | } 111 | 112 | if( root -> isleaf ) 113 | { 114 | printf("["); 115 | for (int i = 0; i < root -> keynum; ++i) 116 | { 117 | if( i == root -> keynum - 1 ) 118 | { 119 | printf("%d", root -> keys[i]); 120 | } 121 | else 122 | { 123 | printf("%d,", root -> keys[i]); 124 | } 125 | } 126 | printf("]"); 127 | printf("isleaf:%d,", root -> isleaf); 128 | printf("pos:%d\n", root -> pos); //就是因为缺少了这行打印出pos的调试信息,导致一直没有发现bug的问题所在,所以说调试过程中得到关键信息很重要! 129 | } 130 | else 131 | { 132 | if( root -> keynum > 0 ) 133 | { 134 | for (int i = 0; i < root -> keynum; ++i) 135 | { 136 | bptree_print( root -> children[i] ); 137 | } 138 | } 139 | } 140 | } 141 | 142 | /** 143 | * 其实节点的插入有两种 144 | * 一种是判断到满节点则先分裂再插入 145 | * 另外一种是判断到满节点,但是先不分裂,而是继续判断这个满节点的子节点中有没有插入的位置,并且这个插入的位置是否已满 146 | * 如果未满,那么我们完全可以不分裂第一个搜索到的满节点,而是直接插入这个位置,这样子可以避免过多的分裂(而过多的分裂会导致树的高度增加) 147 | */ 148 | void bptree_insert(bpnode * root, int key) 149 | { 150 | //如果插入节点为空 151 | if(root == null) 152 | { 153 | //则先开辟一个节点 154 | bptree_init(); 155 | 156 | //然后在这个新开辟的节点上插入关键字 157 | bptree_insert(bptree_root, key); 158 | } 159 | else 160 | { 161 | /** 162 | * 如果节点没满,那么是两种情况: 163 | * 1. 叶子节点的话,直接就可以插入了 164 | * 2. 非叶子节点的话,要继续向叶子节点搜索 165 | */ 166 | if(root -> keynum < BPLUSTREE_M) 167 | { 168 | //如果是叶节点 169 | if(root -> isleaf) 170 | { 171 | 172 | //所有关键字往后移动 173 | int i = root -> keynum; 174 | while( i > 0 ) 175 | { 176 | if ( key < root -> keys[ i - 1 ] ) 177 | { 178 | root -> keys[ i ] = root -> keys[ i - 1 ]; 179 | i--; 180 | } 181 | else 182 | { 183 | break; 184 | } 185 | } 186 | 187 | //关键字插入第一个位置 188 | root -> keys[ i ] = key; 189 | root -> keynum += 1; 190 | } 191 | //非叶子节点的话,则继续向下寻找插入 192 | else 193 | { 194 | //找到应该插入的位置 195 | int i = 0; 196 | while( i < root -> keynum ) 197 | { 198 | if ( key < root -> keys[i] ) 199 | { 200 | bptree_insert( root -> children[i], key ); 201 | return; 202 | } 203 | i++; 204 | } 205 | 206 | //如果都没找到,则说明此关键字是最大的,插入到最后一个孩子节点当中去 207 | bptree_insert( root -> children[ root -> keynum - 1 ], key ); 208 | update_index( root -> children[ root -> keynum - 1 ] ); 209 | } 210 | } 211 | //如果节点已满 212 | else 213 | { 214 | /** 215 | * 这里做一下优化,如果节点已满,如果不是叶节点的话,我们再判断一下其子节点的情况,如果子节点没满,也可以先不分裂此节点,先插入子节点 216 | * 这样做的好处就是,有效减少树的高度 217 | */ 218 | 219 | //如果是叶子节点则不用想了,分裂就好了 220 | if(root -> isleaf) 221 | { 222 | bptree_split(root); 223 | bptree_insert(bptree_root, key); //上面一行代码分裂了叶子节点,但是可能导致向父节点的连锁分裂,所以你不知道分裂到哪一个环节,所以还是从根节点插入关键字 224 | } 225 | //如果不是叶子节点的话,我们先看一下他应该插入的位置可不可以插入(也就是有没有满) 226 | else 227 | { 228 | //找到应该插入的位置 229 | bpnode * finder = find_insert_place(root, key); 230 | 231 | //如果找到应该插入的位置还没有满,则可以插入这里 232 | if(finder -> keynum < BPLUSTREE_M) 233 | { 234 | bptree_insert(finder, key); 235 | } 236 | //如果这个位置已经满了,则直接分裂当前节点然后再插入 237 | else 238 | { 239 | bptree_split(root); 240 | bptree_insert(bptree_root, key); //上面一行代码分裂了叶子节点,但是可能导致向父节点的连锁分裂,所以你不知道分裂到哪一个环节,所以还是从根节点插入关键字 241 | } 242 | } 243 | } 244 | } 245 | } 246 | 247 | /** 248 | * 找到适合插入的位置节点 249 | */ 250 | bpnode * find_insert_place(bpnode * node, int key) 251 | { 252 | if( node -> isleaf ) 253 | { 254 | return node; 255 | } 256 | 257 | int i; 258 | for (i = 0; i < node -> keynum; ++i) 259 | { 260 | if( key < node -> keys[i] ) 261 | { 262 | return find_insert_place( node -> children[i], key ); 263 | } 264 | } 265 | 266 | return find_insert_place( node -> children[i], key ); 267 | } 268 | 269 | /** 270 | * 节点分裂 271 | */ 272 | void bptree_split(bpnode * node) 273 | { 274 | // 1. 新开辟节点并移动数据 275 | 276 | //如果要分裂一个节点,则首先要开辟一个新节点 277 | bpnode * right = bptree_create_node(); 278 | 279 | //将左边节点的右边的数据全部转移到新开辟的节点上 280 | for (int i = T, j = 0; i < BPLUSTREE_M; ++i, ++j) 281 | { 282 | //赋值到右节点 283 | right -> keys[j] = node -> keys[i]; 284 | right -> keynum += 1; 285 | right -> children[j] = node -> children[i]; 286 | 287 | //赋值给right节点之后随即清空 288 | node -> keys[i] = 0; 289 | node -> keynum -= 1; 290 | node -> children[i] = null; 291 | 292 | //重置right子节点的父指针 293 | if(right -> children[j] != null) 294 | { 295 | right -> isleaf = 0; 296 | right -> children[j] -> parent = right; 297 | right -> children[j] -> pos = j; //子节点的pos也要更新,这里注释是因为忘记了设置子节点的pos导致最后插入节点18的时候导致了重大bug 298 | } 299 | } 300 | 301 | // 2. 分析各种情况 302 | 303 | //如果当前被分裂节点是根节点 304 | if(node == bptree_root) 305 | { 306 | //则新开辟一个根节点 307 | bpnode * new_root = bptree_create_node(); 308 | 309 | //此新的根节点后面会有孩子存在,所以为非叶子节点 310 | new_root -> isleaf = 0; 311 | 312 | //此节点左孩子为当前被分裂节点 313 | new_root -> children[0] = node; 314 | new_root -> children[1] = right; 315 | 316 | //给新的根节点赋予关键字 317 | new_root -> keys[0] = node -> keys[ node -> keynum - 1 ]; 318 | new_root -> keys[1] = right -> keys[ right -> keynum - 1 ]; 319 | new_root -> keynum += 2; 320 | 321 | //修改孩子的父指针 322 | node -> parent = new_root; 323 | right -> parent = new_root; 324 | 325 | //设定孩子的位置 326 | node -> pos = 0; 327 | right -> pos = 1; 328 | 329 | //将新开辟的节点作为新的根节点 330 | bptree_root = new_root; 331 | } 332 | else 333 | { 334 | //如果被分裂节点不是根节点,则必定存在父节点 335 | 336 | //将父节点的所有元素都后移 337 | int k = node -> parent -> keynum; 338 | while( k > node -> pos ) 339 | { 340 | node -> parent -> keys[ k ] = node -> parent -> keys[ k - 1 ]; 341 | node -> parent -> children[ k ] = node -> parent -> children[ k - 1 ]; 342 | node -> parent -> children[ k ] -> pos = k; 343 | k--; 344 | } 345 | 346 | //将新节点插入被让出来的位置 347 | 348 | //调整孩子节点的位置 349 | node -> parent -> children[ k ] = node; 350 | node -> parent -> children[ k + 1 ] = right; 351 | 352 | node -> pos = k; 353 | right -> pos = k + 1; 354 | 355 | node -> parent -> keys[ k ] = node -> keys[ node -> keynum - 1 ]; 356 | node -> parent -> keynum += 1; 357 | 358 | //新加入的右节点的父节点指向被分裂节点的父节点 359 | right -> parent = node -> parent; 360 | } 361 | 362 | //更新右兄弟指针 363 | right -> right = node -> right; 364 | node -> right = right; 365 | 366 | //更新左兄弟指针 367 | if( right -> right != null ) 368 | { 369 | right -> right -> left = right; 370 | } 371 | 372 | right -> left = node; 373 | } 374 | 375 | 376 | 377 | /** 378 | * 删除关键字 379 | * 380 | * 基本思路是这样的(主要取决于节点的丰满度): 381 | * 1. 如果删除节点之后此节点的丰满度仍然 >= T, 则无需合并操作(只需要更新节点索引即可) 382 | * 2. 如果删除之后 < T, 则查看兄弟节点能不能借给我们关键字: 383 | * 2.1 如果兄弟节点丰满度 > T, 则可以借 384 | * 2.2 如果兄弟节点丰满度 = T, 则合并 385 | */ 386 | bool bptree_remove(int key) 387 | { 388 | bpnode * node = find_key(bptree_root, key); 389 | 390 | if( node == null ) //如果没找到 391 | { 392 | return false; 393 | } 394 | else 395 | { 396 | //找到被删除元素的位置 397 | bool is_remove_the_max_key = false; 398 | int i; 399 | for (i = 0; i < node -> keynum; ++i) 400 | { 401 | if( key == node -> keys[i] ) 402 | { 403 | //删除的是否为最大元素,如果是最大元素,则需要更新其祖先节点们的关键字索引值 404 | if ( i == node -> keynum - 1 ) 405 | { 406 | is_remove_the_max_key = true; 407 | } 408 | 409 | //找到被删除元素则退出,此时的i即为被删除元素的实际位置 410 | break; 411 | } 412 | } 413 | 414 | //如果删除的不是最大元素,则被删除节点之后的元素往前移,覆盖上面被删除的元素 415 | if( is_remove_the_max_key == false ) 416 | { 417 | while( i < node -> keynum - 1 ) 418 | { 419 | node -> keys[ i ] = node -> keys[ i + 1 ]; 420 | i++; 421 | } 422 | } 423 | node -> keynum -= 1; 424 | 425 | //如果删除的是最大关键字,则更新所有祖先节点关键字索引 426 | if ( is_remove_the_max_key ) 427 | { 428 | update_index( node ); 429 | } 430 | 431 | bptree_merge_recursive( node ); 432 | } 433 | 434 | return true; 435 | } 436 | 437 | void bptree_merge_recursive( bpnode * node ) 438 | { 439 | if ( node == bptree_root || node -> parent == bptree_root ) 440 | { 441 | return; 442 | } 443 | 444 | //删除之后丰满度依然符合要求,则我们看他的兄弟节点是否有合并的需要 445 | if ( node -> keynum >= T ) 446 | { 447 | if ( node -> keynum == T ) 448 | { 449 | if ( node -> left != null && node -> left -> keynum < T ) 450 | { 451 | bptree_merge_recursive( node -> left ); 452 | } 453 | else if ( node -> right != null && node -> right -> keynum < T ) 454 | { 455 | bptree_merge_recursive( node -> right ); 456 | } 457 | } 458 | 459 | } 460 | /** 461 | * 如果删除之后不够丰满了,则有两条路走: 462 | * 1. 借:兄弟富裕的话,则向兄弟借孩子 463 | * 2. 合:兄弟不富裕的话,则更兄弟合并 464 | */ 465 | else 466 | { 467 | //如果左孩子存在 468 | if ( node -> left != null ) 469 | { 470 | //如果左孩子够丰满,则可以借左孩子关键字 471 | if( node -> left -> keynum > T ) 472 | { 473 | //所有关键字右移,腾出第一个位置 474 | int j = node -> keynum; 475 | while ( j > 0 ) 476 | { 477 | node -> keys[ j ] = node -> keys[ j - 1 ]; 478 | node -> children[ j ] = node -> children[ j - 1 ]; 479 | if( node -> children[ j ] != null ) 480 | { 481 | node -> children[ j ] -> pos = j; 482 | } 483 | j--; 484 | } 485 | 486 | //把左边最大的关键字赋值给当前节点第一个位置 487 | node -> keys[ 0 ] = node -> left -> keys[ node -> left -> keynum - 1 ]; 488 | node -> children[ 0 ] = node -> left -> children[ node -> left -> keynum - 1 ]; 489 | if ( node -> children[ 0 ] != null ) 490 | { 491 | node -> children[ 0 ] -> parent = node; 492 | node -> children[ 0 ] -> pos = 0; 493 | } 494 | 495 | //左边相应-1 496 | node -> left -> keynum -= 1; 497 | 498 | //当前相应+1 499 | node -> keynum += 1; 500 | 501 | //更新左节点索引 502 | update_index( node -> left ); 503 | } 504 | //如果左孩子不够丰满,则合并左孩子 505 | else if( node -> left -> keynum <= T ) 506 | { 507 | bptree_merge( node -> left, node ); 508 | } 509 | } 510 | //没有左孩子,则判断右孩子 511 | else 512 | { 513 | //借右孩子 514 | if( node -> right -> keynum > T ) 515 | { 516 | node -> keys[ node -> keynum ] = node -> right -> keys[ node -> right -> keynum - 1 ]; 517 | 518 | //右节点所有关键字往前移 519 | int j = 0; 520 | while ( j < node -> right -> keynum ) 521 | { 522 | node -> right -> keys[ j ] = node -> right -> keys[ j + 1 ]; 523 | node -> right -> children[ j ] = node -> right -> children[ j + 1 ]; 524 | if ( node -> right -> children[ j ] != null ) 525 | { 526 | node -> right -> children[ j ] -> pos = j; 527 | } 528 | j++; 529 | } 530 | 531 | node -> right -> keynum -= 1; 532 | 533 | node -> keynum += 1; 534 | 535 | update_index( node -> right ); 536 | } 537 | //合并右孩子 538 | else if( node -> right -> keynum <= T ) 539 | { 540 | bptree_merge( node , node -> right ); 541 | } 542 | } 543 | 544 | if( node -> parent != null ) 545 | { 546 | bptree_merge_recursive( node -> parent ); 547 | } 548 | } 549 | } 550 | 551 | /** 552 | * current 合并到 target 553 | * 554 | * current 在 target 的左边 555 | * 556 | */ 557 | void bptree_merge( bpnode * current , bpnode * target ) 558 | { 559 | //如果是同一个父节点下 560 | if ( current -> parent == target -> parent ) 561 | { 562 | //先把target的关键字往右移,给current要进来的关键字腾地方 563 | int i = target -> keynum - 1; 564 | while ( i >= 0 ) 565 | { 566 | target -> keys[ i + current -> keynum ] = target -> keys[ i ]; 567 | target -> children[ i + current -> keynum ] = target -> children[ i ]; 568 | if ( target -> children[ i + current -> keynum ] != null ) 569 | { 570 | target -> children[ i + current -> keynum ] -> pos = i + current -> keynum; 571 | } 572 | i--; 573 | } 574 | target -> keynum += current -> keynum; 575 | 576 | //把左边的关键字赋值给target(位置上面已经腾出来了) 577 | int j = 0; 578 | while( j < current -> keynum ) 579 | { 580 | target -> keys[ j ] = current -> keys[ j ]; 581 | target -> children[ j ] = current -> children[ j ]; 582 | if ( target -> children[ j ] != null ) 583 | { 584 | target -> children[ j ] -> pos = j; 585 | } 586 | j++; 587 | } 588 | 589 | //将parent的关键字和孩子都往左边移动 590 | j = current -> pos; 591 | int k = target -> parent -> keynum - 1; 592 | while ( j < k ) 593 | { 594 | target -> parent -> keys[ j ] = target -> parent -> keys[ j + 1 ]; 595 | target -> parent -> children[ j ] = target -> parent -> children[ j + 1 ]; 596 | if ( target -> parent -> children[ j ] != null ) 597 | { 598 | target -> parent -> children[ j ] -> pos = j; 599 | } 600 | target -> parent -> keynum -= 1; 601 | j++; 602 | } 603 | 604 | } 605 | //如果是不同父节点下 606 | else 607 | { 608 | //先把target的关键字(还有孩子)往右移,给current要进来的关键字(和孩子)腾地方 609 | int i = 0; 610 | while ( i < target -> keynum ) 611 | { 612 | target -> keys[ i + current -> keynum ] = target -> keys[ i ]; 613 | target -> children[ i + current -> keynum ] = target -> children[ i ]; 614 | if ( target -> children[ i + current -> keynum ] != null ) 615 | { 616 | target -> children[ i + current -> keynum ] -> pos = i + current -> keynum; 617 | } 618 | i++; 619 | } 620 | 621 | //把左边的关键字(还有孩子)赋值给target(位置上面已经腾出来了) 622 | i = 0; 623 | while( i < current -> keynum ) 624 | { 625 | target -> keys[ i ] = current -> keys[ i ]; 626 | target -> children[ i ] = current -> children[ i ]; 627 | if ( target -> children[ i ] != null ) 628 | { 629 | target -> children[ i ] -> parent = target; 630 | target -> children[ i ] -> pos = i; 631 | } 632 | i++; 633 | } 634 | 635 | //减掉左边节点对应的树的信息 636 | target -> keynum += current -> keynum; 637 | // current -> parent -> keys[ current -> parent -> keynum - 1 ] = 0; 638 | // current -> parent -> children[ current -> parent -> keynum - 1 ] = null; 639 | current -> parent -> keynum -= 1; 640 | update_index( current -> parent ); 641 | } 642 | 643 | //如果已经合并到根节点 644 | if ( target -> parent == bptree_root ) 645 | { 646 | //则将target置为根节点即可 647 | bptree_root = target; 648 | } 649 | else 650 | { 651 | //更新兄弟指针 652 | target -> left = current -> left; 653 | if( current -> left != null ) 654 | { 655 | current -> left -> right = target; 656 | } 657 | } 658 | 659 | //彻底删除current 660 | free(current); 661 | } 662 | 663 | /** 664 | * 更新节点的索引信息 665 | */ 666 | void update_index ( bpnode * node ) 667 | { 668 | if( node != bptree_root ) 669 | { 670 | node -> parent -> keys[ node -> pos ] = node -> keys[ node -> keynum - 1 ]; 671 | if( node -> pos == node -> parent -> keynum - 1 ) 672 | { 673 | return update_index( node -> parent ); 674 | } 675 | } 676 | else 677 | { 678 | return; 679 | } 680 | } 681 | 682 | /** 683 | * 找到关键字所在的节点 684 | */ 685 | bpnode * find_key(bpnode * node, int key) 686 | { 687 | if( node -> isleaf ) 688 | { 689 | return node; 690 | } 691 | 692 | int i; 693 | for (i = 0; i < node -> keynum; ++i) 694 | { 695 | if( key <= node -> keys[i] ) 696 | { 697 | return find_key( node -> children[i], key ); 698 | } 699 | } 700 | 701 | //否则就是没找到 702 | return null; 703 | } 704 | 705 | int main(int argc, char * argv[]){ 706 | // int data[] = {20,8,5,3,1,10,2,7,9,4,6,18,11,12,15}; 707 | int data[] = {20,8,5,3,1,10,2,7,9,4,6,18}; 708 | // int data[] = {20,8,5,3}; 709 | // int data[] = {1,2,3,6}; 710 | int len; 711 | GET_ARRAY_LEN(data, len); 712 | 713 | bptree_init(); 714 | 715 | //插入节点 716 | for (int i = 0; i < len; i++) 717 | { 718 | bptree_insert(bptree_root, data[i]); 719 | } 720 | 721 | /** 722 | * 测试节点右兄弟指针 723 | * test node -> right point for int data[] = {20,8,5,3,1,10,2,7,9,4,6,18}; 724 | * int data[] = {20,8,5,3,1,10,2,7,9,4,6,18}; 725 | */ 726 | // bpnode * node = bptree_root -> children[0] -> children[0]; 727 | // while( node != null ) 728 | // { 729 | // printf("%d\n", node -> keys[0]); 730 | // node = node -> right; 731 | // } 732 | 733 | /** 734 | * 测试节点左兄弟指针 735 | * test node -> left point for int data[] = {20,8,5,3,1,10,2,7,9,4,6,18}; 736 | * int data[] = {20,8,5,3,1,10,2,7,9,4,6,18}; 737 | */ 738 | // bpnode * node = bptree_root -> children[1] -> children[1]; 739 | // while( node != null ) 740 | // { 741 | // printf("%d\n", node -> keys[0]); 742 | // node = node -> left; 743 | // } 744 | 745 | //test remove 746 | bptree_remove(18); 747 | bptree_remove(20); 748 | bptree_remove(10); 749 | bptree_remove(7); 750 | bptree_remove(9); 751 | bptree_remove(5); 752 | bptree_remove(4); 753 | bptree_remove(8); 754 | bptree_remove(3); 755 | bptree_print(bptree_root); 756 | 757 | return 1; 758 | } -------------------------------------------------------------------------------- /Tree/BTree.c: -------------------------------------------------------------------------------- 1 | /** 2 | * B-Tree,B树,1970 年由 R. Bayer 和 E. McCreight 发明 3 | * 4 | * M阶B树的定义 - balanced tree of order m 5 | * 1.任意非叶子节点最多只有M个儿子,且M>2 6 | * 2.根节点的儿子数为[2,M] 7 | * 3.除根节点以外的非叶子节点的儿子数为[ceil(M/2),M](也就是除终端节点之外的节点的孩子数最少为ceil(M/2)) 8 | * 4.每个节点存放至少ceil(M/2)-1和至多M-1个关键字;(但是最少2个关键字) 9 | * 5.非叶子节点的关键字个数 = 指向儿子的指针个数 - 1 10 | * 6.非叶子节点的关键字k[1],k[2], ... k[M-1];并且k[i] < k[i+1] 11 | * 7.非叶节点的指针:P[1],P[2], ... , P[M];其中P[1]指向关键字小于K[1]的子树,P[M]指向关键字大于K[M-1]的子树,其他P[i]指向关键字属于(K[i-1],K[i])的子树 12 | * 8.所有叶子节点位于同一层 13 | * 14 | * 如果你能根据上面的定义明白B树到底是什么,那真是万幸!你很可能天赋秉异! 15 | * 但是如果你看完之后还是搞不懂B树到底是什么,那我更能理解你(因为我也看不懂),因为网上到处都是这种脱离人类语言,晦涩难懂又绕来绕去的学术定义,一般人根本搞不懂,所以我们一步一步来解释清楚: 16 | * 17 | * 首先我们要搞清楚,B树阶的定义是不统一的(看见了吧,不是我们糊涂,而是本身B树的定义都不同,所以你可能一会儿看到这种定义,一会儿看到另一种定义,不糊涂才怪呢),看下面: 18 | * Unfortunately, the literature on B-trees is not uniform in its terminology (Folk & Zoellick 1992, p. 362). 19 | * Bayer & McCreight (1972), Comer (1979), and others define the order of B-tree as the minimum number of keys in a non-root node. 20 | * Folk & Zoellick (1992) points out that terminology is ambiguous because the maximum number of keys is not clear. An order 3 B-tree might hold a maximum of 6 keys or a maximum of 7 keys. 21 | * Knuth (1998, p. 483) avoids the problem by defining the order to be maximum number of children (which is one more than the maximum number of keys). 22 | * 23 | * 综上所述,因为B树中阶的定义乱七八糟,所以最后Knuth看不下去了,为了避免混淆,把order(也就是阶)定义为最大的孩子数(也就是最大的子节点数,最大的指针域) 24 | * (其实我们应该明白一件事情,那就是,B树怎么被定义,阶被定义成什么都不重要,关键是我们要实现这么一种树,这种树就是这个样子的,嗯~~) 25 | * Ok,搞清楚B树中阶的定义之后,我们可以开始接着搞明白m阶B树的定义: 26 | * 27 | * 1.所有节点的数据域(也就是他们说的关键字)都比指针域(也就是他们说的子节点树,孩子数)少一个。 28 | * 2.根节点最少有两个子节点,至多有m个子节点。 29 | * 3.其他非叶节点最少有ceil(m/2)个子节点,最多有m个子节点;数据域最少有ceil(m/2)-1个,最多有m-1个 30 | * 31 | * 怎么样,上面三条就解释清楚的事情,他们非要用七八条定义把你搞糊涂才行...(虽然上面的解释不够严谨,但是指出了B树的几个关键特征,能让你大概了解B树到底是怎么一回儿事)。 32 | * 33 | * 我们再具体一点,我们来看一下四阶树的定义: 34 | * 1.所有节点的指针都比数据多一个,也就是这样的:[指针1,数据1,指针2,数据2,指针3] 35 | * 2.根节点最少有2个子节点,最多有4个。 36 | * 3.其他非叶节点最少有个2个子节点,最多有4个子节点;数据区最少有1个,最多有3个。 37 | * 38 | * 这么描述,是否能明白? 39 | * 40 | * 我们再用简短的语言描述一下M阶B树:B树是一棵多路搜索树,根据其可以最多叉的数目M称为M阶树 41 | * 42 | * 也看到网上有稍微简短清晰一点的定义: 43 | * 1.树中每个节点最多含有M棵子树 44 | * 2.若根节点不是叶子节点,则至少有2棵子树; 45 | * 3.除根节点之外的所有非叶子节点至少有ceil(M/2)棵子树 46 | * 4.每个非叶节点中包含信息keynum, ptr[0],key[0], ... ptr[keynum-1],key[keynum-1],ptr[keynum] 47 | * 48 | * 最后说下整个B树的编写经过: 49 | * 整个编写一个B树花费了两天半的时候,是的,从周三下午到周四,到今天周五下班,都在编写B树......你可以知道我有多笨了吧@_@ 50 | * 前一天半,我是参考B树的定义以及网上给出的例程在理解B树以及其如何实现,但是整个周四我都在调试网上给出的例程,当在插入最后一个节点6的时候,就出错了 51 | * 我始终找不到是哪里的问题,一直到周五上午 52 | * 周五上午我分析应该是在插入节点6的时候,这时候根节点是[2,5,8],是一个满节点,这时候根据B树的规则,根节点要进行分裂, 53 | * 所以这时候我已经大体知道问题是出在根节点进行分裂的时候,没有正确处理根节点中四个子节点,导致最后Segmentation fault: 11的错误 54 | * 但是我调试多次仍然是无法找到问题症结,因为这代码实在晦涩难懂,我心想如果继续在网上例程的沼泽里调试下去,还不知道要调试到什么时候,干脆不如重写算了! 55 | * 56 | * 于是周五整个下午,我都在实现B树,在这个过程中,我是根据节点插入的步骤一个一个来处理的,你会发现,如果你一步一步来实现B树,其实逻辑就渐渐地捋清楚了 57 | * 1.首先在插入根节点为空的时候,直接赋值,这个时候我们就有了根节点 58 | * 2.然后向根节点继续插入节点的时候,我们有了insert_nonfull函数来处理插入未满节点的函数 59 | * 3.根节点在插入[3,5,8]的时候,先满了,这时候在插入节点1的时候,根节点第一次需要使用到分裂,这个时候我们编写分裂函数 60 | * 4.分裂函数的主要过程是建立一个临时节点,然后将被分裂节点的右半部分成员key转移给这个临时节点(这个时候我还没有转移右半部分的孩子成员children,因为现在他们还是NULL,我还没察觉到他们) 61 | * 5.然后在插入9的时候,需要分裂右子节点,这个时候涉及到了提取右子节点的中间元素到父节点当中的问题,因为这个时候父节点并不是空的,需要对提升到父节点的中间元素进行排序 62 | * 6.然后在插入4的时候,跟上面第五条也是一样的道理 63 | * 7.最后重头戏在于插入节点6的时候,因为这个时候根节点是[2,5,8],判断到根节点已满之后,就需要先对根节点进行分裂操作,这个时候,设计到了分裂节点的孩子节点的处理,这个时候我才注意到了分裂节点也有孩子节点需要处理 64 | * 8.正确处理好(或者说分配好)被分裂节点的孩子节点之后,我们就可以插入最后一个元素6了。 65 | * 66 | * 当处理完最后根节点的分裂,并且插入节点6并调试成功之后,正好18:00整~~~,哇...呵呵,可以下班了~~ 67 | * 68 | * 69 | * ****************************************** 2016-06-22 ****************************************** 70 | * 编写B树的删除,猜猜花费了多长时间?一天?两天?一个周? 71 | * 都不对,实际是,花费了整整两个周!!! 72 | * 编写B树的删除逻辑,其中涉及到了节点的合并,与添加节点时的分裂相呼应,但是这个合并的逻辑,我整整花费了两个星期的时间...... 73 | * 而且中间非常吃力,对于调试C代码经验也非常不足(中间调试测试代码花费了非常多的时间)......所以又再一次证明了我不是一个聪明的人 74 | * 但是也想通过这个事情说明,哪怕像我这么笨的人,通过两个星期的时间,也可以实现B树的删除,所以,没有什么算法是不能实现的,只要你肯去做。 75 | * 这次合并的逻辑,并不完善,其中可能有一些地方可以使用分裂之后再合并的方法可能更好,但是我没有去实现; 76 | * 按层级打印B树的功能也没有实现好,目前只是通过加入了一个右兄弟指针的方法来实现的,属于投机取巧的方法(而且还有bug); 77 | * 另外这里为了实现一个B树,竟然用了七八百行代码,说明肯定有冗余的部分, 78 | * 所有这些问题以后有时间有机会一定要继续优化~~~ 79 | * 80 | * 81 | * 82 | * @Description: Description 83 | * @Datetime: 2016-04-20 17:33:30 84 | * @Author: linjunjie 85 | */ 86 | 87 | #include "algorithm.h" 88 | 89 | //4阶树,也就是2-3-4树,最简单的B树是2-3树,也即3阶树 90 | #define BTREE_M 4 //四阶树,也就是有四个子孩子,三个元素的B树 91 | 92 | //为方便编程,有的地方也称为出度,这里四阶B树的度即为2 93 | //这里关于B树的度和其高度H还有一个关系,也就是B树的高度H的上限为log以T为底(关键字个数N+1)/2的对数 94 | // ( N + 1 )/2 95 | // H <= log 96 | // T 97 | // 所以据此我们也得出了一个很直观的结论,也就是B树查找节点的渐进复杂度为 98 | // N 99 | // O ( log ) 100 | // T 101 | // 也就是以度T为底,结点个数N的对数 102 | // 可以看出B树是一个非常有效率的数据结构 103 | // 因为通常现实情况下出度T非常大,例如100甚至1000,那么得到的对数则会非常小,通常<=3 104 | // 当然查找到相应节点之后还要在节点关键字上进行二分查找等消耗 105 | #define T BTREE_M/2 106 | 107 | typedef struct btree_node{ 108 | int keynum; //顾名思义,保存的数据的数量,也就是key的数量 109 | int isleaf; 110 | int keys[2*T - 1]; //保存的数据域 111 | struct btree_node * children[2*T]; //保存的孩子节点 112 | struct btree_node * right_sibling; 113 | struct btree_node * parent; //父节点 114 | int p_pos; //当前节点在父节点的位置 115 | } bnode; 116 | 117 | bnode * bt_root; 118 | 119 | void init_btree(); 120 | void print_btree(bnode * root); 121 | void hierarchy_traversal(bnode * node); 122 | void hierarchy_traversal_recurse(bnode * root, int level); 123 | void insert_nonfull(bnode * node, int data); 124 | void split_node(bnode * split_node_parent, int index); 125 | void insert(bnode * * root, int key); 126 | void remove_key(bnode * * root, int key); 127 | bnode * find(bnode * root, int key, int * pos); 128 | bnode * find_max(bnode * root, int * pos, int * key); 129 | bnode * find_min(bnode * root, int * pos, int * key); 130 | void merge_sibling(bnode * theone); 131 | void merge_sibling_recursive(bnode * theone); 132 | 133 | void init_btree() 134 | { 135 | bt_root = NULL; 136 | } 137 | 138 | //插入到一个非空节点 139 | void insert_nonfull(bnode * node, int data) 140 | { 141 | //如果是叶子节点,则直接插入 142 | if(node -> isleaf == true) 143 | { 144 | //得到节点的key的数组的最大索引值 145 | int i = node -> keynum - 1; 146 | 147 | /** 148 | * 开始比较要插入的data 149 | * 如果data小于当前结点的key,则所有的key往后移:i+1 <= i 150 | * 最后吧data放到最终确定的位置,也就是把data放到node中最小的key的位置 151 | */ 152 | while( i >= 0 && data < node -> keys[i]) 153 | { 154 | node -> keys[i+1] = node -> keys[i]; 155 | i--; 156 | } 157 | node -> keynum++; 158 | node -> keys[i+1] = data; 159 | } 160 | //如果不是叶子节点,则肯定不是插在这个节点,而是需要比较大小,插入到他的子节点中去 161 | else 162 | { 163 | ///得到节点的key的数组的最大索引值 164 | int i = node -> keynum - 1; 165 | 166 | //寻找,通过比较大小,看看被插入节点应该落在哪个位置 167 | while( i >= 0 && data < node -> keys[i] ) 168 | { 169 | i--; 170 | } 171 | 172 | //往前进一步 173 | i++; 174 | 175 | //如果这个子节点已经满了,那么简单,先分裂此节点 176 | if(node -> children[i] -> keynum == 2*T - 1) 177 | { 178 | //分裂这个已满的i节点 179 | split_node(node, i); 180 | 181 | //注意,分裂之后的节点,肯定有一个节点被提上来了,并且这个被提上来的节点位置为i,而且这个被提上来的节点可能小于也可能大于之前确定好的i,所以我们这里要比较一下 182 | //如果被提上来的节点比我们要插入的节点要小的话,那么我们之前确定的位置i就要变更了,具体就是+1,往后挪一位 183 | if(data > node -> keys[i]) 184 | { 185 | i++; 186 | } 187 | } 188 | 189 | //插入到这个(如果满了就是被分裂)(如果没满就是未满节点)中去 190 | insert_nonfull(node -> children[i], data); 191 | } 192 | } 193 | 194 | //这里分裂的其实是待分裂节点的父节点 195 | void split_node(bnode * split_node_parent, int index) 196 | { 197 | //开辟一个新节点保存被实际分裂节点右边的节点 198 | bnode * right_node = (bnode *)malloc(sizeof(bnode)); 199 | right_node -> isleaf = split_node_parent -> children[index] -> isleaf; 200 | right_node -> keynum = 0; 201 | for(int i=0; i < 2*T - 1; i++) 202 | { 203 | right_node -> keys[i] = 0; 204 | } 205 | for(int i=0; i < 2*T; i++) 206 | { 207 | right_node -> children[i] = NULL; 208 | } 209 | 210 | //把当前节点右半边的节点赋值给right_node 211 | for(int i=T,j=0; i < 2*T - 1; i++, j++) 212 | { 213 | //把被分裂节点右边的值全部赋值给right_node 214 | right_node -> keys[j] = split_node_parent -> children[index] -> keys[i]; 215 | right_node -> keynum ++; 216 | 217 | //赋值之后右边值清空 218 | split_node_parent -> children[index] -> keys[i] = 0; 219 | split_node_parent -> children[index] -> keynum --; 220 | } 221 | 222 | //把右半边的孩子也赋值给right_node 223 | for(int i=T,j=0; i < 2*T; i++,j++) 224 | { 225 | right_node -> children[j] = split_node_parent -> children[index] -> children[i]; 226 | split_node_parent -> children[index] -> children[i] = NULL; 227 | } 228 | 229 | //把右半边的元素数赋给父节点的右孩子 230 | int m = split_node_parent -> keynum; 231 | 232 | //所有孩子也右移,是为了容纳分裂之后产生的新的右孩子 233 | while(m >= index + 1) 234 | { 235 | split_node_parent -> children[m + 1] = split_node_parent -> children[m]; 236 | m--; 237 | } 238 | split_node_parent -> children[index+1] = right_node; 239 | 240 | 241 | //把分裂节点的第T-1个key(也就是中间key)提到父节点上来 242 | //先拿到中间节点 243 | int mid_key = split_node_parent -> children[index] -> keys[T - 1]; 244 | 245 | //拿到父节点现在的数组最大索引值 246 | int i = split_node_parent -> keynum - 1; 247 | 248 | //如果比提上来的节点小,则全部往后移,目的是给移上来的节点腾出合适的地方 249 | while(i >= index){ 250 | split_node_parent -> keys[ i + 1 ] = split_node_parent -> keys[ i ]; 251 | i--; 252 | } 253 | i++; //找到腾出来的位置,要+1的 254 | 255 | split_node_parent -> keys[ i ] = mid_key; 256 | split_node_parent -> children[index] -> keynum --; 257 | split_node_parent -> keynum ++; 258 | 259 | //更新一些指针和节点信息 260 | for (int i = 0; i < split_node_parent -> keynum; i++) 261 | { 262 | //最后这个是建立起节点之间右兄弟节点的联系(用来日后层级遍历B树) 263 | split_node_parent -> children[i] -> right_sibling = split_node_parent -> children[i+1]; 264 | } 265 | 266 | //注意这里处理子节点的数目应该是keynum 267 | for (int i = 0; i <= split_node_parent -> keynum; i++) 268 | { 269 | //各个子节点指向父节点 270 | split_node_parent -> children[i] -> parent = split_node_parent; 271 | 272 | //子节点在父节点的位置 273 | split_node_parent -> children[i] -> p_pos = i; 274 | } 275 | 276 | } 277 | 278 | void insert(bnode * * root, int key) 279 | { 280 | //根节点为空,则直接放入根节点 281 | if(*root == NULL) 282 | { 283 | bnode * new_root = (bnode *)malloc(sizeof(bnode)); 284 | new_root -> parent = NULL; 285 | new_root -> isleaf = true; 286 | new_root -> keynum = 1; 287 | for(int i=0; i < 2*T - 1; i++) 288 | { 289 | new_root -> keys[i] = 0; 290 | } 291 | for(int i=0; i < 2*T; i++) 292 | { 293 | new_root -> children[i] = NULL; 294 | } 295 | new_root -> keys[0] = key; 296 | *root = new_root; 297 | return; 298 | } 299 | 300 | //根节点满了 301 | if((* root) -> keynum == 2*T - 1) 302 | { 303 | //新开辟一个节点,孩子节点指向根节点 304 | bnode * tmp = (bnode *)malloc(sizeof(bnode)); 305 | tmp -> keynum = 0; 306 | for(int i=0; i < 2*T - 1; i++) 307 | { 308 | tmp -> keys[i] = 0; 309 | } 310 | for(int i=0; i < 2*T; i++) 311 | { 312 | tmp -> children[i] = NULL; 313 | } 314 | tmp -> children[0] = *root; 315 | 316 | //分裂这个子节点所指向当前根节点的新节点 317 | split_node(tmp, 0); 318 | 319 | //将分裂后的节点设置为根节点 320 | *root = tmp; 321 | 322 | insert_nonfull(*root, key); 323 | } 324 | else 325 | { 326 | insert_nonfull(*root, key); 327 | } 328 | } 329 | 330 | /** 331 | * 合并兄弟节点 332 | * 333 | * 当对接点进行了变更之后,则调用此方法判断节点是否可以进行合并 334 | */ 335 | void merge_sibling(bnode * theone) 336 | { 337 | if( theone -> keynum <= T - 1) 338 | { 339 | bnode * parent = theone -> parent; 340 | 341 | int sibling_pos = abs(theone -> p_pos - 1); //需要待合并的节点位置 342 | 343 | if(sibling_pos > theone -> p_pos) //如果兄弟节点在当前节点右边,则直接使用当前节点合并 344 | { 345 | merge_sibling_recursive(theone); 346 | } 347 | else //如果兄弟节点在当前节点左边,则使用兄弟节点进行合并 348 | { 349 | merge_sibling_recursive(parent -> children[ sibling_pos ]); 350 | } 351 | } 352 | } 353 | 354 | /** 355 | * 递归合并节点 356 | * 357 | * 这里进来的元素,在merge_sibling方法已经被矫正过一次,所以进来的节点都是左子节点, 358 | * 哪怕本来这个节点是右边的节点需要合并,现在也变成了左边的节点, 359 | * 就像下面这样 360 | * 361 | * 父节点 362 | * 当前节点 <------------- 右子节点 363 | */ 364 | void merge_sibling_recursive(bnode * theone) 365 | { 366 | if( theone -> keynum <= T - 1) 367 | { 368 | bnode * parent = theone -> parent; 369 | 370 | int sibling_pos = theone -> p_pos + 1; 371 | 372 | if(parent -> children[ sibling_pos ] -> keynum <= T - 1) //如果待合并的节点的兄弟节点确实满足合并的条件 373 | { 374 | //父节点元素下移 375 | theone -> keys[ theone -> keynum ] = parent -> keys[ theone -> p_pos ]; 376 | theone -> keynum ++; 377 | 378 | //把兄弟节点的孩子也赋值给theone 379 | int j = 0; 380 | while( j < parent -> children[ sibling_pos ] -> keynum ) 381 | { 382 | theone -> keys[ theone -> keynum + j ] = parent -> children[ sibling_pos ] -> keys[ j ]; 383 | theone -> children[ theone -> keynum + j ] = parent -> children[ sibling_pos ] -> children[ j ]; 384 | 385 | if(theone -> children[ theone -> keynum + j ] != NULL) 386 | { 387 | theone -> children[ theone -> keynum + j ] -> parent = theone; 388 | theone -> children[ theone -> keynum + j ] -> p_pos = theone -> keynum + j; 389 | } 390 | j++; 391 | } 392 | //这里处理最后一个节点孩子 393 | theone -> children[ theone -> keynum + j ] = parent -> children[ sibling_pos ] -> children[ j ]; 394 | if(theone -> children[ theone -> keynum + j ] != NULL) 395 | { 396 | theone -> children[ theone -> keynum + j ] -> parent = theone; 397 | theone -> children[ theone -> keynum + j ] -> p_pos = theone -> keynum + j; 398 | } 399 | theone -> keynum += j; 400 | 401 | //父节点下移之后,也要调整父节点相应关键字和子节点的位置,也就是向左移动 402 | int k = theone -> p_pos; 403 | while( k < parent -> keynum ) 404 | { 405 | parent -> keys[ k ] = parent -> keys[ k + 1 ]; 406 | parent -> children[ k + 1 ] = parent -> children[ k + 2 ]; 407 | if(parent -> children[ k + 1 ] != NULL) 408 | { 409 | parent -> children[ k + 1 ] -> p_pos = k + 1; 410 | } 411 | k++; 412 | } 413 | parent -> children[ k + 1 ] = parent -> children[ k + 2 ]; 414 | if(parent -> children[ k + 1 ] != NULL) 415 | { 416 | parent -> children[ k + 1 ] -> p_pos = k + 1; 417 | } 418 | 419 | //调整完父节点之后,将父节点的关键字数目减1 420 | parent -> keynum --; 421 | 422 | if(parent -> keynum > 0) 423 | { 424 | //建立右兄弟的关系 425 | parent -> children[ theone -> p_pos ] -> right_sibling = parent -> children[ theone -> p_pos + 1 ]; 426 | } 427 | 428 | //如果不是父节点(父节点需要特殊处理) 429 | if(parent != bt_root) 430 | { 431 | int parent_sibling_pos = abs(parent -> p_pos - 1); //需要待合并的节点位置 432 | if(parent_sibling_pos > parent -> p_pos) //如果兄弟节点在当前节点右边,则直接使用当前节点合并 433 | { 434 | merge_sibling_recursive(parent); 435 | } 436 | else //如果兄弟节点在当前节点左边,则使用兄弟节点进行合并 437 | { 438 | merge_sibling_recursive(parent -> parent -> children[ parent_sibling_pos ]); 439 | } 440 | } 441 | else 442 | { 443 | //如果父节点元素已经为空,则销毁父节点,并重新建立父节点 444 | if(parent -> keynum == 0) 445 | { 446 | free(parent); 447 | bt_root = theone; 448 | bt_root -> right_sibling = null; 449 | } 450 | } 451 | } 452 | else 453 | { 454 | //如果兄弟节点为空的话 455 | if( theone -> keynum == 0 ) 456 | { 457 | 458 | //父节点下移到左子节点 459 | theone -> keys[0] = parent -> keys[ theone -> p_pos ]; 460 | theone -> keynum = 1; 461 | 462 | //右子节点最左边关键字上移到父节点 463 | parent -> keys[ theone -> p_pos ] = parent -> children[ sibling_pos ] -> keys[ 0 ]; 464 | int j = 0; 465 | while( j < parent -> children[ sibling_pos ] -> keynum - 1 ) 466 | { 467 | parent -> children[ sibling_pos ] -> keys[ j ] = parent -> children[ sibling_pos ] -> keys[ j + 1 ]; 468 | j ++; 469 | } 470 | 471 | //右子节点关键字数目减一 472 | parent -> children[ sibling_pos ] -> keynum --; 473 | 474 | //递归继续查看节点是否满足合并条件 475 | merge_sibling_recursive(theone); 476 | } 477 | } 478 | } 479 | //如果当前节点满足B树的特性,那么则判断其兄弟节点是否不满足B树特性,如果不满足则可以进行合并 480 | else 481 | { 482 | bnode * parent = theone -> parent; 483 | int sibling_pos = theone -> p_pos + 1; 484 | 485 | //如果兄弟节点为空的话 486 | if( parent -> children[ sibling_pos ] -> keynum <= T - 1 ) 487 | { 488 | //父节点下移到右子节点的最左边 489 | //首先所有孩子往右移 490 | int j = parent -> children[ sibling_pos ] -> keynum; 491 | while(j > 0) 492 | { 493 | parent -> children[ sibling_pos ] -> keys[ j ] = parent -> children[ sibling_pos ] -> keys[ j - 1 ]; 494 | j --; 495 | } 496 | parent -> children[ sibling_pos ] -> keys[ 0 ] = parent -> keys[ theone -> p_pos ]; 497 | parent -> children[ sibling_pos ] -> keynum += 1; 498 | 499 | //左子节点最右边关键字上移到父节点 500 | parent -> keys[ theone -> p_pos ] = theone -> keys[ theone -> keynum - 1 ]; 501 | 502 | //左子节点关键字数目减一 503 | theone -> keynum --; 504 | 505 | //继续递归判断 506 | merge_sibling_recursive(theone); 507 | 508 | } 509 | } 510 | } 511 | 512 | /** 513 | * 514 | * 删除B树种的关键字 515 | * 516 | * 517 | * B树的特性: 518 | * 519 | * 除根节点之外的节点必须满足: 520 | * ceil(m/2) - 1 <= n <= m-1 521 | * n表示关键字个数 522 | * 523 | * 对于四阶树来说,也就是关键字满足: 524 | * 1 <= n <= 3 525 | * 526 | */ 527 | void remove_key(bnode * * root, int key) 528 | { 529 | if(root == null) 530 | { 531 | return; 532 | } 533 | 534 | bnode * node = * root; 535 | int pos = -1; 536 | 537 | bnode * theone = find(node, key, &pos); 538 | 539 | if(theone == NULL) 540 | { 541 | return; 542 | } 543 | else 544 | { 545 | //如果节点是叶节点,则直接删除元素 546 | if(theone -> isleaf == true) 547 | { 548 | //则将此元素后面的元素向前移来删除此元素 549 | int i = pos; 550 | while(i < theone -> keynum) 551 | { 552 | theone -> keys[i] = theone -> keys[i + 1]; 553 | i++; 554 | } 555 | 556 | //节点元素数减1 557 | theone -> keynum --; 558 | 559 | //判断当前节点在删除关键字后还是否满足B树的节点关键字特性 560 | merge_sibling(theone); 561 | } 562 | /** 563 | * 如果被删除的不是叶子节点,则有三种情况: 564 | * 1.左边能借 565 | * 2.右边能借 566 | * 3.左右都不能借 567 | */ 568 | else 569 | { 570 | //如果节点左孩子满足富余节点的特性 571 | if( theone -> children[pos] -> keynum > T - 1 ) 572 | { 573 | // printf("%s\n", "left"); 574 | int pos_left_max, key_left_max= -1; 575 | bnode * findnode = find_max( theone -> children[pos], &pos_left_max, &key_left_max); 576 | if(findnode != null) 577 | { 578 | remove_key(&findnode, key_left_max); 579 | } 580 | theone -> keys[pos] = key_left_max; 581 | } 582 | //如果节点右孩子满足富余节点的特性 583 | else if(theone -> children[pos+1] -> keynum > T - 1 ) 584 | { 585 | // printf("%s\n", "right"); 586 | int pos_right_min,key_right_min= -1; 587 | bnode * findnode = find_min( theone -> children[pos+1], &pos_right_min, &key_right_min); 588 | if(findnode != null) 589 | { 590 | remove_key(&findnode, key_right_min); 591 | } 592 | theone -> keys[pos] = key_right_min; 593 | } 594 | //如果左右子节点关键字都不够用,那么需要先把左子节点,当前节点,右子节点进行合并 595 | else if( 596 | theone -> children[pos] -> keynum == T - 1 597 | && 598 | theone -> children[pos+1] -> keynum == T - 1) 599 | { 600 | // printf("%s\n", "mid"); 601 | //父节点下移到左子节点 602 | theone -> children[ pos ] -> keys[ theone -> children[ pos ] -> keynum ] = theone -> keys[ pos ]; 603 | theone -> children[ pos ] -> keynum ++; 604 | 605 | //右子节点关键字并入左子节点 606 | int i = 0; 607 | while( i < theone -> children[ pos + 1 ] -> keynum) 608 | { 609 | theone -> children[ pos ] -> keys[ theone -> children[ pos ] -> keynum + i ] = theone -> children[ pos + 1] -> keys[ i ]; 610 | theone -> children[ pos ] -> children[ theone -> children[ pos ] -> keynum + i ] = theone -> children[ pos + 1] -> children[ i ]; 611 | theone -> children[ pos ] -> children[ theone -> children[ pos ] -> keynum + i ] -> p_pos = theone -> children[ pos ] -> keynum + i; 612 | theone -> children[ pos ] -> keynum ++; 613 | i++; 614 | } 615 | //不要忘了处理最后一个孩子 616 | theone -> children[ pos ] -> children[ theone -> children[ pos ] -> keynum + i ] = theone -> children[ pos + 1] -> children[ i ]; 617 | theone -> children[ pos ] -> children[ theone -> children[ pos ] -> keynum + i ] -> p_pos = theone -> children[ pos ] -> keynum + i; 618 | theone -> children[ pos ] -> keynum ++; 619 | 620 | //父节点全部孩子左移 621 | int j = pos; 622 | while(j < theone -> keynum) 623 | { 624 | theone -> keys[j] = theone -> keys[j + 1]; 625 | theone -> children[j + 1] = theone -> children[j + 2]; 626 | j++; 627 | } 628 | theone -> keynum --; 629 | 630 | //不要忘记更新此节点指向的右兄弟节点(为了层级遍历) 631 | theone -> children[ pos ] -> right_sibling = theone -> children[ pos + 1 ]; 632 | 633 | //在子节点中继续删除元素 634 | remove_key( & (theone -> children[pos]), key ); 635 | } 636 | 637 | } 638 | } 639 | } 640 | 641 | /** 642 | * 查找的基本思路是这样的: 643 | * 644 | * 1.元素在当前节点中寻找 645 | * 2.如果元素大于节点中当前元素,则一直往后找 646 | * 3.否则查看元素和当前元素是否相等,若相等则说明已经找到 647 | * 4.否则查看当前元素中间是否存在子节点 648 | * 5.在子节点中重复以上1~4步骤 649 | */ 650 | bnode * find(bnode * root, int key, int * pos) 651 | { 652 | if(root == NULL) 653 | { 654 | return false; 655 | } 656 | 657 | bnode * tmp = root; 658 | int i = 0; 659 | 660 | //如果大于当前元素,则一直往后找 661 | while ( i < tmp -> keynum && key > tmp -> keys[i] ) 662 | { 663 | i++; 664 | } 665 | 666 | //若相等,则说明已经找到 667 | if( i < tmp -> keynum && key == tmp -> keys[i]) 668 | { 669 | * pos = i; 670 | return tmp; 671 | } 672 | 673 | //如果当前元素是叶子节点,说明没找到 674 | if( tmp -> isleaf ) 675 | { 676 | return NULL; 677 | } 678 | 679 | //否则继续向深处寻找 680 | return find( tmp -> children[i], key, pos); 681 | } 682 | 683 | //找到此节点所有子节点下的最大元素 684 | bnode * find_max(bnode * root, int * pos, int * key) 685 | { 686 | if(root == null) 687 | { 688 | return false; 689 | } 690 | 691 | bnode * tmp = root; 692 | 693 | if( tmp -> isleaf ) 694 | { 695 | * pos = tmp -> keynum - 1; 696 | * key = tmp -> keys[ tmp -> keynum - 1 ]; 697 | return tmp; 698 | } 699 | else 700 | { 701 | return find_max( tmp -> children[ tmp -> keynum ], pos , key); 702 | } 703 | 704 | } 705 | 706 | //找到此节点所有子节点下的最小元素 707 | bnode * find_min(bnode * root, int * pos, int * key) 708 | { 709 | if(root == null) 710 | { 711 | return false; 712 | } 713 | 714 | bnode * tmp = root; 715 | 716 | if( tmp -> isleaf ) 717 | { 718 | * pos = tmp -> keynum - 1; 719 | * key = tmp -> keys[ 0 ]; 720 | return tmp; 721 | } 722 | else 723 | { 724 | return find_min( tmp -> children[ 0 ], pos , key); 725 | } 726 | 727 | } 728 | 729 | //普通打印(打印包括节点信息) 730 | void print_btree(bnode * root) 731 | { 732 | if(root == NULL) 733 | { 734 | return; 735 | } 736 | 737 | printf("节点元素:"); 738 | printf("是否叶子节点:%d", root -> isleaf); 739 | 740 | //打印根节点 741 | printf(";成员:["); 742 | for(int i = 0; i < root -> keynum; i++) 743 | { 744 | printf("%d", root -> keys[i]); 745 | if(root -> keynum - 1 != i) 746 | { 747 | printf(","); 748 | } 749 | } 750 | printf("]"); 751 | printf(";在父节点的位置:%d", root -> p_pos); 752 | printf(";key数量:%d\n", root -> keynum); 753 | 754 | //打印子节点 755 | for(int i = 0; i <= root -> keynum; i++) 756 | { 757 | print_btree(root -> children[i]); 758 | } 759 | } 760 | 761 | //层级遍历打印B树 762 | void hierarchy_traversal(bnode * node) 763 | { 764 | if(node -> keynum > 0) 765 | { 766 | bnode * right = node; 767 | while(right != null) 768 | { 769 | printf("["); 770 | for (int i = 0; i < right -> keynum; i++) 771 | { 772 | printf("%d", right -> keys[i]); 773 | if(right -> keynum - 1 != i) 774 | { 775 | printf(","); 776 | } 777 | } 778 | printf("],"); 779 | 780 | right = right -> right_sibling; 781 | } 782 | } 783 | printf("\n"); 784 | } 785 | 786 | //递归层级遍历打印B树 787 | void hierarchy_traversal_recurse(bnode * root, int level) 788 | { 789 | printf("第%d层:", level); 790 | if(root -> keynum > 0) 791 | { 792 | hierarchy_traversal(root); 793 | } 794 | 795 | if(root -> isleaf == false) 796 | { 797 | hierarchy_traversal_recurse(root -> children[0], ++level); 798 | } 799 | } 800 | 801 | int main(int argc, char * argv[]){ 802 | // int data[] = {20,8,5,3,1,10,2,7,9,4,6,18,11,12,15}; 803 | // int data[] = {20,8,5,3,1,10,2,7,9,4,6,18,11}; 804 | int data[] = {20,8,5,3,1,10,2,7,9,4,6,18}; 805 | int len; 806 | GET_ARRAY_LEN(data, len); 807 | 808 | init_btree(); 809 | 810 | //插入节点 811 | for (int i = 0; i < len; i++) 812 | { 813 | insert(&bt_root, data[i]); 814 | } 815 | 816 | //测试查找函数 817 | // int pos = -1; 818 | // bnode * want = find(bt_root, 9, &pos); 819 | // if(pos != -1) 820 | // { 821 | // printf("%d\n", want -> parent -> keys[0]); 822 | // printf("pos :%d\n", pos); 823 | // printf("p_pos :%d\n", want -> p_pos); 824 | // } 825 | // else 826 | // { 827 | // printf("%s\n","没找到元素"); 828 | // } 829 | 830 | //层级打印B树 831 | printf("层级打印:\n"); 832 | hierarchy_traversal_recurse(bt_root, 1); 833 | 834 | printf("普通打印:\n"); 835 | print_btree(bt_root); 836 | 837 | 838 | //测试删除数据 示例 1 839 | // remove_key( &bt_root, 7 ); 840 | // remove_key( &bt_root, 5 ); 841 | // remove_key( &bt_root, 4 ); 842 | // remove_key( &bt_root, 1 ); 843 | // remove_key( &bt_root, 2 ); 844 | // remove_key( &bt_root, 3 ); 845 | // remove_key( &bt_root, 18 ); 846 | 847 | 848 | //测试删除数据 示例 2 849 | // remove_key( &bt_root, 20 ); 850 | // remove_key( &bt_root, 7 ); 851 | 852 | 853 | //测试find_max函数 854 | // int pos,key = -1; 855 | // bnode * want = find_max(bt_root -> children[2], &pos, &key); 856 | // if(pos != -1) 857 | // { 858 | // printf("%d,%d\n",pos,want -> keys[pos]); 859 | // } 860 | // else 861 | // { 862 | // printf("%s\n","没找到元素"); 863 | // } 864 | 865 | printf("层级打印:\n"); 866 | hierarchy_traversal_recurse(bt_root, 1); 867 | 868 | printf("普通打印:\n"); 869 | print_btree(bt_root); 870 | 871 | return 1; 872 | } 873 | -------------------------------------------------------------------------------- /Tree/BalancedBinaryTree.c: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Balanced Binary Tree - AVL树 - 平衡二叉树 (Adelson-Velskii and Landis @ 1962) 4 | * 5 | * 其实平衡二叉树很好理解,其实在最开始实现二叉查找树的时候,你会发现二叉查找树如果变成一个链表的话,也是有可能的 - -! 因为没有规则来限定它不能变为一个链表 6 | * 所以为了避免这种情况,相信你也想过,那就是通过旋转节点来实现树平衡,也就是将一个太长的链表变成一个长得更像是树的结构, 7 | * 并且这里还有一个规则:左右子树的高度只差不能超过1 8 | * 9 | * 所以你可以简单的将平衡二叉树理解为:对执行过操作之后的二叉查找树进行平衡操作之后的树。 10 | * 11 | * 所以平衡二叉树最主要的是增加了节点的旋转操作,这个旋转的操作主要是在插入或者删除之后再进行的 12 | * 13 | * @Description: Description 14 | * @Datetime: 2016-04-16 14:28:14 15 | * @Author: linjunjie 16 | * 17 | */ 18 | 19 | #include "algorithm.h" 20 | 21 | typedef struct tree_node{ 22 | int data; 23 | struct tree_node * parent; 24 | struct tree_node * left; 25 | struct tree_node * right; 26 | int height; //节点高度,可以根据节点高度计算平衡因子,平衡二叉树中平衡度因子的定义为左子树深度减去右子树的深度, 应该只有-1,0,1这三个值,也即是绝对值应不大于1 27 | } node; 28 | 29 | node * root; 30 | 31 | /* 32 | * 函数声明 33 | */ 34 | node * find_node_in_balanced_binary_tree(node * parent, node * n); 35 | int insert_node_to_balanced_binary_tree(node * * relative_root_node, node * insert_node); 36 | node * find_max_node(node * parent); 37 | int get_node_height(node * n); //得到节点的平衡度 38 | node * rotate_on_left(node * * n); //左边重了,要消除左边重的部分 39 | 40 | //基本上是比较两个整数的大小,这里其实是传入左右两个子树的高度,看谁的高度更高而已 41 | int get_max_height(int a, int b){ 42 | return a > b ? a : b; 43 | } 44 | 45 | //取得节点的高度,这里是直接拿到节点的高度值即可 46 | int get_node_height(node * n) 47 | { 48 | /** 49 | * 注意,这里的意思,不是说遍历到的节点如果是NULL,则返回失败-1。 50 | * 这里的意思是,如果被遍历到的节点是空,也就是NULL的话,这个节点的元素平衡度,就是-1,这样方便后面计算上一级节点的平衡度 51 | */ 52 | if(n == NULL) 53 | { 54 | return -1; 55 | } 56 | 57 | return n -> height; 58 | } 59 | 60 | void init_tree() 61 | { 62 | root = NULL; 63 | } 64 | 65 | // LL旋转 66 | node * rotate_single_left(node * n) 67 | { 68 | node * tmp = n -> left; 69 | n -> left = tmp -> right; 70 | tmp -> right = n; 71 | 72 | //维护变化节点的parent指针 73 | if(n -> parent != NULL) 74 | { 75 | if(n -> parent -> left == n) 76 | { 77 | n -> parent -> left = tmp; 78 | } 79 | else 80 | { 81 | n -> parent -> right = tmp; 82 | } 83 | } 84 | tmp -> parent = n -> parent; 85 | n -> parent = tmp; 86 | 87 | n -> height = get_max_height(get_node_height(n -> left), get_node_height(n -> right)) + 1; 88 | tmp -> height = get_max_height(get_node_height(tmp -> left), get_node_height(tmp -> right)) + 1; 89 | 90 | return tmp; 91 | } 92 | 93 | // RR旋转 94 | node * rotate_single_right(node * n) 95 | { 96 | node * tmp = n -> right; 97 | n -> right = tmp -> left; 98 | tmp -> left = n; 99 | 100 | //维护变化节点的parent指针 101 | if(n -> parent != NULL) 102 | { 103 | if(n -> parent -> left == n) 104 | { 105 | n -> parent -> left = tmp; 106 | } 107 | else 108 | { 109 | n -> parent -> right = tmp; 110 | } 111 | } 112 | tmp -> parent = n -> parent; 113 | n -> parent = tmp; 114 | 115 | n -> height = get_max_height(get_node_height(n -> left), get_node_height(n -> right)) + 1; 116 | tmp -> height = get_max_height(get_node_height(tmp -> left), get_node_height(tmp -> right)) + 1; 117 | 118 | return tmp; 119 | } 120 | 121 | // LR旋转 122 | node * rotate_double_left(node * n) 123 | { 124 | //先将左边变为LL型 125 | n -> left = rotate_single_right(n -> left); 126 | 127 | //进行LL型处理 128 | return rotate_single_left(n); 129 | } 130 | 131 | // RL旋转 132 | node * rotate_double_right(node * n) 133 | { 134 | //先将右边处理为RR型 135 | n -> right = rotate_single_left(n -> right); 136 | 137 | //然后进行RR型处理 138 | return rotate_single_right(n); 139 | } 140 | 141 | /** 142 | * @params pnode 被遍历到的节点位置 143 | * @params pparent 节点位置的父节点 144 | * @params insert_node 要插入的节点 145 | */ 146 | int insert(node * * pnode, node * pparent, node * pinsert) 147 | { 148 | 149 | if((*pnode) == NULL) 150 | { 151 | /** 152 | * 开辟一个节点 153 | * 其实节点都是在这里开辟和添加的 154 | */ 155 | node * n; 156 | n = (node *)malloc(sizeof(node)); 157 | n -> parent = n -> left = n -> right = NULL; 158 | n -> data = pinsert -> data; 159 | n -> parent = pparent; 160 | n -> height = 0; 161 | *pnode = n; 162 | 163 | return 1; 164 | } 165 | else 166 | { 167 | //小于则往左走 168 | if(pinsert -> data < (* pnode) -> data) 169 | { 170 | //先进行插入动作 171 | insert(&(* pnode) -> left, * pnode, pinsert); 172 | 173 | //如果这时候左子树 - 右子树 的平衡度大于1,那么我们就要通过旋转来维持平衡了 174 | if(get_node_height((* pnode) -> left) - get_node_height((* pnode) -> right) > 1) 175 | { 176 | //如果是插入了左子树的左边,则说明左边重了,需要进行LL旋转 177 | if(pinsert -> data < (* pnode) -> left -> data) 178 | { 179 | * pnode = rotate_single_left((* pnode)); 180 | } 181 | //如果是插入了左子树的右边,则左子树需要进行LR旋转 182 | else 183 | { 184 | * pnode = rotate_double_left((* pnode)); 185 | } 186 | } 187 | } 188 | //大于则往右走 189 | else if(pinsert -> data > (* pnode) -> data) 190 | { 191 | //先进行插入动作 192 | insert(&(* pnode) -> right, * pnode, pinsert); 193 | 194 | //失去平衡了 195 | if(get_node_height((* pnode) -> right) - get_node_height((* pnode) -> left) > 1) 196 | { 197 | //插入右子树的右边,右子树单次右旋转 198 | if(pinsert -> data > (* pnode) -> right -> data) 199 | { 200 | * pnode = rotate_single_right((* pnode)); 201 | } 202 | //插入了右子树的左边,右子树要进行RL旋转 203 | else 204 | { 205 | * pnode = rotate_double_right((* pnode)); 206 | } 207 | } 208 | } 209 | } 210 | 211 | //当前节点的高度等于其子树的最大高度+1 212 | (* pnode) -> height = get_max_height(get_node_height((* pnode) -> left), get_node_height((* pnode) -> right)) + 1; 213 | 214 | return 1; 215 | } 216 | 217 | // 对单个节点进行AVL调整的功能,在节点插入之后,对单个节点执行此功能即可 218 | // insert_with_rotate_node函数使用了此函数 219 | node * rotate_node(node * n) 220 | { 221 | if(get_node_height(n -> left) - get_node_height(n -> right) > 1) 222 | { 223 | if(get_node_height(n -> left -> left) >= get_node_height(n -> left -> right)) 224 | { 225 | n = rotate_single_left(n); 226 | } 227 | else 228 | { 229 | n = rotate_double_left(n); 230 | } 231 | } 232 | else if(get_node_height(n -> right) - get_node_height(n -> left) > 1) 233 | { 234 | if(get_node_height(n -> right -> right) >= get_node_height(n -> right -> left)) 235 | { 236 | n = rotate_single_right(n); 237 | } 238 | else 239 | { 240 | n = rotate_double_right(n); 241 | } 242 | } 243 | 244 | return n; 245 | } 246 | 247 | /** 248 | * 这个函数跟上面insert函数的区别只在于在插入节点之后进行的旋转,使用的是封装好的rotate_node函数 249 | * 这里保留了上面的insert函数只为了让读者可以看到更完整的删除节点逻辑 250 | * insert_with_rotate_node函数是通过rotate_node来进行平衡的函数,但是不如insert函数逻辑完整和利于阅读 251 | * 252 | * @params pnode 被遍历到的节点位置 253 | * @params pparent 节点位置的父节点 254 | * @params insert_node 要插入的节点 255 | */ 256 | int insert_with_rotate_node(node * * pnode, node * pparent, node * pinsert) 257 | { 258 | if((*pnode) == NULL) 259 | { 260 | /** 261 | * 开辟一个节点 262 | * 其实节点都是在这里开辟和添加的 263 | */ 264 | node * n; 265 | n = (node *)malloc(sizeof(node)); 266 | n -> parent = n -> left = n -> right = NULL; 267 | n -> data = pinsert -> data; 268 | n -> parent = pparent; 269 | n -> height = 0; 270 | *pnode = n; 271 | 272 | return 1; 273 | } 274 | else 275 | { 276 | //小于则往左走 277 | if(pinsert -> data < (* pnode) -> data) 278 | { 279 | //先进行插入动作 280 | insert_with_rotate_node(&((* pnode) -> left), * pnode, pinsert); 281 | } 282 | //大于则往右走 283 | else if(pinsert -> data > (* pnode) -> data) 284 | { 285 | //先进行插入动作 286 | insert_with_rotate_node(&((* pnode) -> right), * pnode, pinsert); 287 | } 288 | 289 | //对节点进行AVL调整 290 | *pnode = rotate_node((* pnode)); 291 | } 292 | 293 | //当前节点的高度等于其子树的最大高度+1 294 | (* pnode) -> height = get_max_height(get_node_height((* pnode) -> left), get_node_height((* pnode) -> right)) + 1; 295 | 296 | return 1; 297 | } 298 | 299 | /** 300 | * @params relative_root_node 相对根节点,所谓相对根节点也就是在某个局部树中的根节点 301 | * 不一定是真正的根节点,但是这个局部树可大可小,就算把整个树看做局部树也是可以的 302 | * 303 | * @params insert_node 需要插入的节点 304 | */ 305 | int insert_node_to_balanced_binary_tree(node * * relative_root_node, node * insert_node){ 306 | 307 | if((*relative_root_node) == NULL) 308 | { 309 | /** 310 | * 开辟一个节点 311 | * 其实节点都是在这里开辟和添加的 312 | */ 313 | node * n; 314 | n = (node *)malloc(sizeof(node)); 315 | n -> parent = n -> left = n -> right = NULL; 316 | n -> data = insert_node -> data; 317 | n -> height = 0; 318 | 319 | *relative_root_node = n; 320 | 321 | return 1; 322 | } 323 | 324 | // return insert(relative_root_node, NULL, insert_node ); //第一种插入函数 325 | return insert_with_rotate_node(relative_root_node, NULL, insert_node ); //第二种插入方式 326 | 327 | } 328 | 329 | /** 330 | * 找到指定节点作为根节点开始的最大节点 331 | * 332 | * 基本思路其实就是一直往右子树找就好了 333 | */ 334 | node * find_max_node(node * parent){ 335 | if(parent == NULL){ 336 | return NULL; 337 | } 338 | 339 | node * n = parent; 340 | 341 | while(n != NULL){ 342 | if(n -> right == NULL){ 343 | return n; 344 | }else{ 345 | n = n -> right; 346 | } 347 | } 348 | 349 | return NULL; 350 | } 351 | 352 | /** 353 | * 354 | * 这里使用的是二叉查找树里的删除节点方法 355 | * 356 | * 但是平衡二叉树和二叉查找树的删除节点规则不同,所以还需要修改 357 | * 358 | */ 359 | int delete_node_from_binary_search_tree_with_bst_tree(node * * parent, node * delete_node){ 360 | if(parent == NULL || delete_node == NULL){ 361 | return 0; 362 | } 363 | 364 | //函数内部临时代替parent 365 | node * tmp = *parent; 366 | 367 | //node_gonna_delete也就是将要被删除的节点 368 | node * node_gonna_delete = NULL; 369 | node_gonna_delete = find_node_in_balanced_binary_tree(* parent, delete_node); 370 | 371 | //如果没有找到被删除的节点,则返回 372 | if(node_gonna_delete == NULL){ 373 | return 0; 374 | } 375 | 376 | //如果删除的元素恰好是局部根节点 377 | if(tmp == node_gonna_delete){ 378 | 379 | //左右子树都没有 380 | if( tmp -> left == NULL && tmp -> right == NULL) 381 | { 382 | *parent = NULL; 383 | } 384 | //有左子树没有右子树 385 | else if(tmp -> left != NULL && tmp -> right == NULL) 386 | { 387 | tmp -> left -> parent = tmp -> parent; 388 | tmp = tmp -> left; 389 | } 390 | //有右子树没有左子树 391 | else if(tmp -> left == NULL && tmp -> right != NULL) 392 | { 393 | tmp -> right -> parent = tmp -> parent; 394 | tmp = tmp -> right; 395 | } 396 | /** 397 | * 如果左右子树都有 398 | * 399 | * 1.左子树的根节点就是左子树里最大的节点的时候,那么很简单,把左子树提到根节点就好了 400 | * 401 | * 2.左子树的根节点不是左子树里最大的节点,那么就把左子树中最大的节点提为根节点 402 | */ 403 | else 404 | { 405 | //得到左子树的最大节点 406 | node * pLeftMax = find_max_node(tmp -> left); 407 | 408 | //如果最大节点就是左子树根节点,那么很简单,只需要把左子树整个提到根节点就好了 409 | if(pLeftMax == tmp -> left) 410 | { 411 | tmp -> left -> parent = tmp -> parent; 412 | tmp -> left -> right = tmp -> right; 413 | tmp = tmp -> left; 414 | } 415 | //如果左节点不是最大节点,那么就找到左子树中的最大节点提为根节点 416 | else 417 | { 418 | //在这里发现树节点是需要一个父节点指针的,否则删除的时候没法把父节点的右子节点设置为NULL 419 | //另外这里只是赋值的话,就不会涉及到删除父节点的左右子节点的情况,所以就避免了复杂性 420 | tmp -> data = pLeftMax -> data; 421 | 422 | //这里用到了被删除的左子树最大节点的父节点,需要把父节点指向的右子节点指向NULL 423 | pLeftMax -> parent -> right = pLeftMax -> left; 424 | 425 | //如果这个时候最大节点的右子节点不是NULL,那么还需要把右子节点的父节点改为指向最大节点的父节点 426 | if(pLeftMax -> left != NULL){ 427 | pLeftMax -> left -> parent = pLeftMax -> parent; 428 | } 429 | 430 | //这个时候你删除的其实不是根节点,而是已经被找到的左子树中的最大节点 431 | node_gonna_delete = pLeftMax; 432 | } 433 | } 434 | 435 | // free(node_gonna_delete); 436 | // return 1; 437 | } 438 | //删除节点不是父节点时的情况 439 | else 440 | { 441 | //被删除的节点没有左子树也没有右子树 442 | if(node_gonna_delete -> left == NULL && node_gonna_delete -> right == NULL) 443 | { 444 | //如果是左节点,则将最后的左节点赋NULL 445 | if(node_gonna_delete -> parent -> left == node_gonna_delete) 446 | { 447 | node_gonna_delete -> parent -> left = NULL; 448 | } 449 | //如果是右节点,则将最后的左节点赋NULL 450 | else 451 | { 452 | node_gonna_delete -> parent -> right = NULL; 453 | } 454 | } 455 | //左子树 != NULL && 右子树 == NULL 456 | else if(node_gonna_delete -> left != NULL && node_gonna_delete -> right == NULL) 457 | { 458 | //如果在左侧,则直接将左子树的子节点上提 459 | if(node_gonna_delete -> parent -> left == node_gonna_delete) 460 | { 461 | node_gonna_delete -> parent -> left = node_gonna_delete -> left; 462 | } 463 | //如果在右侧,也是把右子树上的左子节点作为根节点的右子节点 464 | else 465 | { 466 | node_gonna_delete -> parent -> right = node_gonna_delete -> left; 467 | } 468 | } 469 | //左子树 == NULL && 右子树 != NULL 470 | else if(node_gonna_delete -> left == NULL && node_gonna_delete -> right != NULL) 471 | { 472 | //如果在左侧,则直接将左子树的右子节点上提 473 | if(node_gonna_delete -> parent -> left == node_gonna_delete) 474 | { 475 | node_gonna_delete -> parent -> left = node_gonna_delete -> right; 476 | } 477 | //如果在右侧,也是把右子树上的右子节点作为根节点的右子节点 478 | else 479 | { 480 | node_gonna_delete -> parent -> right = node_gonna_delete -> right; 481 | } 482 | } 483 | //左右子树均不为空 484 | else if(node_gonna_delete -> left != NULL && node_gonna_delete -> right != NULL) 485 | { 486 | //得到被删除节点左子树的最大节点 487 | node * pLeftMax = find_max_node(node_gonna_delete -> left); 488 | 489 | //如果左节点是被删除节点左侧做大的节点 490 | if(node_gonna_delete -> left == pLeftMax) 491 | { 492 | //如果被删除节点位于父节点的左侧,则修改父节点的左节点指向其左子树 493 | if(node_gonna_delete == node_gonna_delete -> parent -> left) 494 | { 495 | node_gonna_delete -> parent -> left = node_gonna_delete -> left; 496 | } 497 | //如果被删除节点位于父节点的右侧,则修改父节点的右节点指向其左子树 498 | else 499 | { 500 | node_gonna_delete -> parent -> right = node_gonna_delete -> left; 501 | } 502 | 503 | //将左节点父节点指向被删除节点的父节点 504 | node_gonna_delete -> left -> parent = node_gonna_delete -> parent; 505 | 506 | //将左节点的右节点指向被删除节点的右节点 507 | node_gonna_delete -> left -> right = node_gonna_delete -> right; 508 | 509 | //将被删除节点指向的整个左子树往上提到被删除节点的位置,所以这个时候被删除节点的左节点的父节点应该变为被提上来的被删除节点的左节点 510 | node_gonna_delete -> right -> parent = node_gonna_delete -> left; 511 | 512 | } 513 | //如果左节点不是被删除节点左侧的最大节点 514 | else 515 | { 516 | //将最大节点的数据赋值给被删除节点,因为后面被删除节点准备金蝉脱壳~~ 517 | node_gonna_delete -> data = pLeftMax -> data; 518 | 519 | //将最大节点的父节点的右节点指向最大节点的左子树 520 | pLeftMax -> parent -> right = pLeftMax -> left; 521 | 522 | //将最大节点的左子树的父节点赋值为最大节点的父节点 523 | //当然前提是你要先检查下最大节点有没有左子节点 524 | if(pLeftMax -> left != NULL) 525 | { 526 | pLeftMax -> left -> parent = pLeftMax -> parent; 527 | } 528 | 529 | //被删除节点金蝉脱壳,被删除的其实是最大节点~~ 530 | node_gonna_delete = pLeftMax; 531 | 532 | } 533 | } 534 | } 535 | 536 | free(node_gonna_delete); 537 | 538 | //重新计算根节点的高度 539 | tmp -> height = get_max_height(get_node_height(tmp -> left), get_node_height(tmp -> right)) + 1; 540 | 541 | //左子树如果不空则调整左子树平衡 542 | if(tmp -> left != NULL) 543 | { 544 | //确保左子树平衡 545 | tmp -> left = rotate_node(tmp -> left); 546 | } 547 | 548 | //右子树若非空则调整右子树平衡 549 | if(tmp -> right != NULL) 550 | { 551 | //确保右子树平衡 552 | tmp-> right = rotate_node(tmp -> right); 553 | } 554 | 555 | //根节点自己调节平衡 556 | if(tmp != NULL) 557 | { 558 | //确保根节点平衡 559 | tmp = rotate_node(tmp); 560 | } 561 | 562 | return 1; 563 | 564 | } 565 | 566 | /** 567 | * 改造了之前二叉查找树的删除逻辑 568 | * 这里的参数 node * parent,其实就是局部的根节点 569 | * 在代码的注释里统一称为根节点(并不是树的真正根节点),不要混淆 570 | * 571 | */ 572 | node * delete_node_from_balanced_binary_tree(node * parent, node * delete_node){ 573 | if(parent == NULL || delete_node == NULL){ 574 | return NULL; 575 | } 576 | 577 | //node_gonna_delete也就是将要被删除的节点 578 | node * node_gonna_delete = NULL; 579 | node_gonna_delete = find_node_in_balanced_binary_tree(parent, delete_node); 580 | 581 | //如果没有找到被删除的节点,则返回 582 | if(node_gonna_delete == NULL){ 583 | return NULL; 584 | } 585 | 586 | //如果被删除的是根节点 587 | if(parent == node_gonna_delete) 588 | { 589 | //如果右子树为空,则直接删除此节点 590 | if(parent -> right == NULL) 591 | { 592 | node * tmp = parent; 593 | parent = parent -> left; 594 | free(tmp); 595 | } 596 | //如果右子树不为空,则用右树种的最小元素代替根节点 597 | else 598 | { 599 | //找到右节点的最小元素 600 | node * tmp = parent -> right; 601 | while(tmp -> left != NULL) 602 | tmp = tmp -> left; 603 | 604 | //将此元素赋值给局部树的根节点 605 | parent -> data = tmp -> data; 606 | 607 | //从右树中删除节点 608 | parent -> right = delete_node_from_balanced_binary_tree(parent -> right, tmp); 609 | 610 | //重新计算现在根节点的高度值 611 | parent -> height = get_max_height(get_node_height(parent -> left), get_node_height(parent -> right)) + 1; 612 | } 613 | 614 | return parent; 615 | } 616 | //否则 617 | else 618 | { 619 | //被删除的节点小于根节点 620 | if(delete_node -> data < parent -> data) 621 | { 622 | //从左子树中查找删除此元素 623 | parent -> left = delete_node_from_balanced_binary_tree(parent -> left, delete_node); 624 | } 625 | //被删除的节点大于根节点 626 | else 627 | { 628 | //从右子树中查找删除此元素 629 | parent -> right = delete_node_from_balanced_binary_tree(parent -> right, delete_node); 630 | } 631 | } 632 | 633 | /*** 以上为删除节点操作,上面删除节点的操作结束之后,下面就要进行节点的高度计算和平衡操作 ***/ 634 | 635 | //重新计算根节点的高度 636 | parent -> height = get_max_height(get_node_height(parent -> left), get_node_height(parent -> right)) + 1; 637 | 638 | //左子树如果不空则调整左子树平衡 639 | if(parent -> left != NULL) 640 | { 641 | //确保左子树平衡 642 | parent -> left = rotate_node(parent -> left); 643 | } 644 | 645 | //右子树若非空则调整右子树平衡 646 | if(parent -> right != NULL) 647 | { 648 | //确保右子树平衡 649 | parent-> right = rotate_node(parent -> right); 650 | } 651 | 652 | //根节点自己调节平衡 653 | if(parent != NULL) 654 | { 655 | //确保根节点平衡 656 | parent = rotate_node(parent); 657 | } 658 | 659 | //返回一切操作尘埃落定之后的根节点 660 | return parent; 661 | } 662 | 663 | /** 664 | * 二叉树中寻找指定值的节点 665 | * 666 | * 函数返回的是二叉树中的节点指针 667 | */ 668 | 669 | node * find_node_in_balanced_binary_tree(node * parent, node * n){ 670 | if(parent == NULL){ 671 | return NULL; 672 | } 673 | 674 | if(n -> data < parent -> data){ 675 | return find_node_in_balanced_binary_tree(parent -> left, n); 676 | }else if(n -> data > parent -> data){ 677 | return find_node_in_balanced_binary_tree(parent -> right, n); 678 | }else if(n -> data == parent -> data) { 679 | return parent; 680 | } 681 | 682 | return NULL; 683 | } 684 | 685 | int print_tree_inorder(node * n); 686 | int print_tree_preorder(node * n); 687 | int print_tree_postorder(node * n); 688 | 689 | //打印二叉树 690 | int print_tree(node * n, char * order){ 691 | 692 | if(strcmp(order, "inorder") == 0) 693 | { 694 | printf("%s\n", "中序遍历打印二叉树 :"); 695 | return print_tree_inorder(n); 696 | } 697 | else if(strcmp(order, "preorder") == 0) 698 | { 699 | printf("%s\n", "前序遍历打印二叉树 :"); 700 | return print_tree_preorder(n); 701 | } 702 | else if(strcmp(order, "postorder") == 0) 703 | { 704 | printf("%s\n", "后序遍历打印二叉树 :"); 705 | return print_tree_postorder(n); 706 | } 707 | 708 | printf("%s\n", "中序遍历打印二叉树 :"); 709 | return print_tree_inorder(n); 710 | } 711 | 712 | 713 | // 中序遍历 714 | int print_tree_inorder(node * n){ 715 | if(n == NULL){ 716 | return 0; 717 | }else{ 718 | print_tree_inorder(n -> left); 719 | printf("%d\n", n -> data); 720 | print_tree_inorder(n -> right); 721 | } 722 | 723 | return 1; 724 | } 725 | 726 | //递归方式打印二叉树 727 | //前序遍历,父节点位于最前 728 | int print_tree_preorder(node * n){ 729 | 730 | if(n == NULL){ 731 | return 0; 732 | }else{ 733 | printf("%d\n", n -> data); 734 | print_tree_preorder(n -> left); 735 | print_tree_preorder(n -> right); 736 | } 737 | 738 | return 1; 739 | } 740 | 741 | //后序遍历 742 | int print_tree_postorder(node * n){ 743 | 744 | if(n == NULL){ 745 | return 0; 746 | }else{ 747 | print_tree_postorder(n -> left); 748 | print_tree_postorder(n -> right); 749 | printf("%d\n", n -> data); 750 | } 751 | 752 | return 1; 753 | } 754 | 755 | int main(int argc, char * argv[]){ 756 | /** 757 | * 平衡二叉树: 758 | * 759 | * 5 760 | * / \ 761 | * 2 8 762 | * / \ / \ 763 | * 1 3 7 10 764 | * \ / / 765 | * 4 6 9 766 | */ 767 | int data[] = {8,5,3,1,10,2,7,9,4,6}; 768 | 769 | //删除根节点的情况 770 | // int data[] = {8,5,3,1}; //只有左子树,测试节点旋转 771 | 772 | int len; 773 | GET_ARRAY_LEN(data, len); 774 | 775 | init_tree(); 776 | 777 | node n; 778 | for (int i = 0; i < len; i++) 779 | { 780 | n.data = data[i]; 781 | insert_node_to_balanced_binary_tree(&root, &n); 782 | } 783 | 784 | /** 785 | * 寻找元素为8的节点,并得到节点指针 786 | */ 787 | // n.data = 8; 788 | // node * node_to_find = find_node_in_balanced_binary_tree(root, &n); 789 | // printf("%p\n", node_to_find); 790 | // printf("%d\n", node_to_find -> data); 791 | // printf("%d\n", node_to_find -> parent -> data); 792 | // return 1; 793 | 794 | /** 795 | * 删除数据值为8的节点元素 796 | */ 797 | // n.data = 8; 798 | // root = delete_node_from_balanced_binary_tree(root, &n); 799 | // n.data = 5; 800 | // root = delete_node_from_balanced_binary_tree(root, &n); 801 | // n.data = 1; 802 | // root = delete_node_from_balanced_binary_tree(root, &n); 803 | // n.data = 10; 804 | // root = delete_node_from_balanced_binary_tree(root, &n); 805 | 806 | //用从BST删除节点改造过来的方法删除节点 807 | n.data = 8; 808 | delete_node_from_binary_search_tree_with_bst_tree(&root, &n); 809 | n.data = 5; 810 | delete_node_from_binary_search_tree_with_bst_tree(&root, &n); 811 | 812 | print_tree(root, "inorder"); 813 | 814 | return 1; 815 | } -------------------------------------------------------------------------------- /Tree/BinarySearchTree.c: -------------------------------------------------------------------------------- 1 | /** 2 | * 以下注释使用了这个Sublime插件生成(你可以根据自己的需要任意修改): 3 | * https://github.com/linjunjie/sublime_plugins/blob/master/addComments.py 4 | * 5 | * @Description: Binary - 二叉树 6 | * @Datetime: 2016-04-10 19:08:53 7 | * @Author: linjunjie 8 | * 9 | * 其实这是一个二叉查找树,所以应更名为 Binary Search Tree - 简称 BST 10 | */ 11 | 12 | #include "algorithm.h" 13 | 14 | typedef struct tree_node{ 15 | int data; 16 | struct tree_node * parent; //一个指向父节点的指针,这是因为如果你删除的是一个子节点的时候,需要把相应父节点的左或右节点设置为NULL 17 | struct tree_node * left; 18 | struct tree_node * right; 19 | } node; 20 | 21 | node * root; 22 | 23 | /* 24 | * 函数声明 25 | */ 26 | node * find_node_in_binary_search_tree(node * parent, node * n); 27 | // int insert(node * * pnode, node * pparent, node * pinsert); 28 | int insert_node_to_binary_search_tree(node * * relative_root_node, node * insert_node); 29 | node * find_max_node(node * parent); 30 | 31 | 32 | void init_tree(){ 33 | root = NULL; 34 | } 35 | 36 | 37 | /** 38 | * @params pnode 被遍历到的节点位置 39 | * @params pparent 节点位置的父节点 40 | * @params insert_node 要插入的节点 41 | */ 42 | int insert(node * * pnode, node * pparent, node * pinsert) 43 | { 44 | 45 | if((*pnode) == NULL) 46 | { 47 | /** 48 | * 开辟一个节点 49 | * 其实节点都是在这里开辟和添加的 50 | */ 51 | node * n; 52 | n = (node *)malloc(sizeof(node)); 53 | n -> parent = n -> left = n -> right = NULL; 54 | n -> data = pinsert -> data; 55 | n -> parent = pparent; 56 | *pnode = n; 57 | 58 | return 1; 59 | } 60 | else 61 | { 62 | if(pinsert -> data < (* pnode) -> data){ 63 | insert(&((* pnode) -> left), *pnode, pinsert); 64 | }else if(pinsert -> data > (* pnode) -> data){ 65 | insert(&((* pnode) -> right), *pnode, pinsert); 66 | }else{ 67 | return 0; 68 | } 69 | } 70 | 71 | return 1; 72 | } 73 | 74 | /** 75 | * @params relative_root_node 相对根节点,所谓相对根节点也就是在某个局部树中的根节点 76 | * 不一定是真正的根节点,但是这个局部树可大可小,就算把整个树看做局部树也是可以的 77 | * 78 | * @params insert_node 需要插入的节点 79 | */ 80 | int insert_node_to_binary_search_tree(node * * relative_root_node, node * insert_node){ 81 | 82 | if((*relative_root_node) == NULL) 83 | { 84 | /** 85 | * 开辟一个节点 86 | * 其实节点都是在这里开辟和添加的 87 | */ 88 | node * n; 89 | n = (node *)malloc(sizeof(node)); 90 | n -> parent = n -> left = n -> right = NULL; 91 | n -> data = insert_node -> data; 92 | 93 | *relative_root_node = n; 94 | 95 | return 1; 96 | } 97 | 98 | return insert(relative_root_node, NULL, insert_node ); 99 | 100 | } 101 | 102 | 103 | /** 104 | * 105 | * @params relative_root_node 相对根节点 106 | * @params insert_node 需要删除的节点 107 | * 108 | * 这个方法是不包含处理父节点的函数,这个方法再删除左节点不是左子树最大节点的时候是无法正确删除节点的 109 | * 这里暂不删除这个方法,留在这里让大家看看 110 | */ 111 | // int insert_node_to_binary_search_tree(node * * relative_root_node, node * insert_node){ 112 | 113 | // if((*relative_root_node) == NULL) 114 | // { 115 | // //开辟一个树节点 116 | // node * n; 117 | // n = (node *)malloc(sizeof(node)); 118 | // n -> parent = n -> left = n -> right = NULL; 119 | // n -> data = insert_node -> data; 120 | 121 | // *relative_root_node = n; 122 | // } 123 | // else 124 | // { 125 | // if(insert_node -> data < (* relative_root_node) -> data) 126 | // { 127 | // insert_node_to_binary_search_tree(&(*relative_root_node) -> left, insert_node); 128 | // } 129 | // else if(insert_node -> data > (* relative_root_node) -> data) 130 | // { 131 | // insert_node_to_binary_search_tree(&(*relative_root_node) -> right, insert_node); 132 | // }else 133 | // { 134 | // return 0; 135 | // } 136 | // } 137 | 138 | // return 1; 139 | 140 | // } 141 | 142 | 143 | /** 144 | * 从二叉树中根据值删除指定的节点 145 | * 146 | * 二叉树的删除比较复杂,但是我们可以一种情况一种情况的去分析 147 | * 148 | */ 149 | int delete_node_from_binary_search_tree(node * * parent, node * delete_node){ 150 | if(parent == NULL || delete_node == NULL){ 151 | return 0; 152 | } 153 | 154 | //函数内部临时代替parent 155 | node * tmp = *parent; 156 | 157 | //node_gonna_delete也就是将要被删除的节点 158 | node * node_gonna_delete = NULL; 159 | node_gonna_delete = find_node_in_binary_search_tree(* parent, delete_node); 160 | 161 | //如果没有找到被删除的节点,则返回 162 | if(node_gonna_delete == NULL){ 163 | return 0; 164 | } 165 | 166 | //如果删除的元素恰好是局部根节点 167 | if(tmp == node_gonna_delete){ 168 | 169 | //左右子树都没有 170 | if( tmp -> left == NULL && tmp -> right == NULL) 171 | { 172 | *parent = NULL; 173 | } 174 | //有左子树没有右子树 175 | else if(tmp -> left != NULL && tmp -> right == NULL) 176 | { 177 | tmp -> left -> parent = tmp -> parent; 178 | tmp = tmp -> left; 179 | } 180 | //有右子树没有左子树 181 | else if(tmp -> left == NULL && tmp -> right != NULL) 182 | { 183 | tmp -> right -> parent = tmp -> parent; 184 | tmp = tmp -> right; 185 | } 186 | /** 187 | * 如果左右子树都有 188 | * 189 | * 1.左子树的根节点就是左子树里最大的节点的时候,那么很简单,把左子树提到根节点就好了 190 | * 191 | * 2.左子树的根节点不是左子树里最大的节点,那么就把左子树中最大的节点提为根节点 192 | */ 193 | else 194 | { 195 | //得到左子树的最大节点 196 | node * pLeftMax = find_max_node(tmp -> left); 197 | 198 | //如果最大节点就是左子树根节点,那么很简单,只需要把左子树整个提到根节点就好了 199 | if(pLeftMax == tmp -> left) 200 | { 201 | tmp -> left -> parent = tmp -> parent; 202 | tmp -> left -> right = tmp -> right; 203 | tmp = tmp -> left; 204 | } 205 | //如果左节点不是最大节点,那么就找到左子树中的最大节点提为根节点 206 | else 207 | { 208 | //在这里发现树节点是需要一个父节点指针的,否则删除的时候没法把父节点的右子节点设置为NULL 209 | //另外这里只是赋值的话,就不会涉及到删除父节点的左右子节点的情况,所以就避免了复杂性 210 | tmp -> data = pLeftMax -> data; 211 | 212 | //这里用到了被删除的左子树最大节点的父节点,需要把父节点指向的右子节点指向NULL 213 | pLeftMax -> parent -> right = pLeftMax -> left; 214 | 215 | //如果这个时候最大节点的右子节点不是NULL,那么还需要把右子节点的父节点改为指向最大节点的父节点 216 | if(pLeftMax -> left != NULL){ 217 | pLeftMax -> left -> parent = pLeftMax -> parent; 218 | } 219 | 220 | //这个时候你删除的其实不是根节点,而是已经被找到的左子树中的最大节点 221 | node_gonna_delete = pLeftMax; 222 | } 223 | } 224 | 225 | free(node_gonna_delete); 226 | return 1; 227 | } 228 | //删除节点不是父节点时的情况 229 | else 230 | { 231 | //被删除的节点没有左子树也没有右子树 232 | if(node_gonna_delete -> left == NULL && node_gonna_delete -> right == NULL) 233 | { 234 | //如果是左节点,则将最后的左节点赋NULL 235 | if(node_gonna_delete -> parent -> left == node_gonna_delete) 236 | { 237 | node_gonna_delete -> parent -> left = NULL; 238 | } 239 | //如果是右节点,则将最后的左节点赋NULL 240 | else 241 | { 242 | node_gonna_delete -> parent -> right = NULL; 243 | } 244 | } 245 | //左子树 != NULL && 右子树 == NULL 246 | else if(node_gonna_delete -> left != NULL && node_gonna_delete -> right == NULL) 247 | { 248 | //如果在左侧,则直接将左子树的子节点上提 249 | if(node_gonna_delete -> parent -> left == node_gonna_delete) 250 | { 251 | node_gonna_delete -> parent -> left = node_gonna_delete -> left; 252 | } 253 | //如果在右侧,也是把右子树上的左子节点作为根节点的右子节点 254 | else 255 | { 256 | node_gonna_delete -> parent -> right = node_gonna_delete -> left; 257 | } 258 | } 259 | //左子树 == NULL && 右子树 != NULL 260 | else if(node_gonna_delete -> left == NULL && node_gonna_delete -> right != NULL) 261 | { 262 | //如果在左侧,则直接将左子树的右子节点上提 263 | if(node_gonna_delete -> parent -> left == node_gonna_delete) 264 | { 265 | node_gonna_delete -> parent -> left = node_gonna_delete -> right; 266 | } 267 | //如果在右侧,也是把右子树上的右子节点作为根节点的右子节点 268 | else 269 | { 270 | node_gonna_delete -> parent -> right = node_gonna_delete -> right; 271 | } 272 | } 273 | //左右子树均不为空 274 | else if(node_gonna_delete -> left != NULL && node_gonna_delete -> right != NULL) 275 | { 276 | //得到被删除节点左子树的最大节点 277 | node * pLeftMax = find_max_node(node_gonna_delete -> left); 278 | 279 | //如果左节点是被删除节点左侧做大的节点 280 | if(node_gonna_delete -> left == pLeftMax) 281 | { 282 | //如果被删除节点位于父节点的左侧,则修改父节点的左节点指向其左子树 283 | if(node_gonna_delete == node_gonna_delete -> parent -> left) 284 | { 285 | node_gonna_delete -> parent -> left = node_gonna_delete -> left; 286 | } 287 | //如果被删除节点位于父节点的右侧,则修改父节点的右节点指向其左子树 288 | else 289 | { 290 | node_gonna_delete -> parent -> right = node_gonna_delete -> left; 291 | } 292 | 293 | //将左节点父节点指向被删除节点的父节点 294 | node_gonna_delete -> left -> parent = node_gonna_delete -> parent; 295 | 296 | //将左节点的右节点指向被删除节点的右节点 297 | node_gonna_delete -> left -> right = node_gonna_delete -> right; 298 | 299 | //将被删除节点指向的整个左子树往上提到被删除节点的位置,所以这个时候被删除节点的左节点的父节点应该变为被提上来的被删除节点的左节点 300 | node_gonna_delete -> right -> parent = node_gonna_delete -> left; 301 | 302 | } 303 | //如果左节点不是被删除节点左侧的最大节点 304 | else 305 | { 306 | //将最大节点的数据赋值给被删除节点,因为后面被删除节点准备金蝉脱壳~~ 307 | node_gonna_delete -> data = pLeftMax -> data; 308 | 309 | //将最大节点的父节点的右节点指向最大节点的左子树 310 | pLeftMax -> parent -> right = pLeftMax -> left; 311 | 312 | //将最大节点的左子树的父节点赋值为最大节点的父节点 313 | //当然前提是你要先检查下最大节点有没有左子节点 314 | if(pLeftMax -> left != NULL) 315 | { 316 | pLeftMax -> left -> parent = pLeftMax -> parent; 317 | } 318 | 319 | //被删除节点金蝉脱壳,被删除的其实是最大节点~~ 320 | node_gonna_delete = pLeftMax; 321 | 322 | } 323 | } 324 | 325 | free(node_gonna_delete); 326 | return 1; 327 | } 328 | 329 | return 1; 330 | } 331 | 332 | /** 333 | * 找到指定节点作为根节点开始的最大节点 334 | * 335 | * 基本思路其实就是一直往右子树找就好了 336 | */ 337 | node * find_max_node(node * parent){ 338 | if(parent == NULL){ 339 | return NULL; 340 | } 341 | 342 | node * n = parent; 343 | 344 | while(n != NULL){ 345 | if(n -> right == NULL){ 346 | return n; 347 | }else{ 348 | n = n -> right; 349 | } 350 | } 351 | 352 | return NULL; 353 | } 354 | 355 | /** 356 | * 二叉树中寻找指定值的节点 357 | * 358 | * 函数返回的是二叉树中的节点指针 359 | */ 360 | 361 | node * find_node_in_binary_search_tree(node * parent, node * n){ 362 | if(parent == NULL){ 363 | return NULL; 364 | } 365 | 366 | if(n -> data < parent -> data){ 367 | return find_node_in_binary_search_tree(parent -> left, n); 368 | }else if(n -> data > parent -> data){ 369 | return find_node_in_binary_search_tree(parent -> right, n); 370 | }else if(n -> data == parent -> data) { 371 | return parent; 372 | } 373 | 374 | return NULL; 375 | } 376 | 377 | int print_tree_inorder(node * n); 378 | int print_tree_preorder(node * n); 379 | int print_tree_postorder(node * n); 380 | 381 | //打印二叉树 382 | int print_tree(node * n, char * order){ 383 | 384 | if(strcmp(order, "inorder") == 0) 385 | { 386 | printf("%s\n", "中序遍历打印二叉树 :"); 387 | return print_tree_inorder(n); 388 | } 389 | else if(strcmp(order, "preorder") == 0) 390 | { 391 | printf("%s\n", "前序遍历打印二叉树 :"); 392 | return print_tree_preorder(n); 393 | } 394 | else if(strcmp(order, "postorder") == 0) 395 | { 396 | printf("%s\n", "后序遍历打印二叉树 :"); 397 | return print_tree_postorder(n); 398 | } 399 | 400 | printf("%s\n", "中序遍历打印二叉树 :"); 401 | return print_tree_inorder(n); 402 | } 403 | 404 | 405 | // 中序遍历 406 | int print_tree_inorder(node * n){ 407 | if(n == NULL){ 408 | return 0; 409 | }else{ 410 | print_tree_inorder(n -> left); 411 | printf("%d\n", n -> data); 412 | print_tree_inorder(n -> right); 413 | } 414 | 415 | return 1; 416 | } 417 | 418 | //递归方式打印二叉树 419 | //前序遍历,父节点位于最前 420 | int print_tree_preorder(node * n){ 421 | 422 | if(n == NULL){ 423 | return 0; 424 | }else{ 425 | printf("%d\n", n -> data); 426 | print_tree_preorder(n -> left); 427 | print_tree_preorder(n -> right); 428 | } 429 | 430 | return 1; 431 | } 432 | 433 | //后序遍历 434 | int print_tree_postorder(node * n){ 435 | 436 | if(n == NULL){ 437 | return 0; 438 | }else{ 439 | print_tree_postorder(n -> left); 440 | print_tree_postorder(n -> right); 441 | printf("%d\n", n -> data); 442 | } 443 | 444 | return 1; 445 | } 446 | 447 | 448 | int main(int argc, char * argv[]){ 449 | /** 450 | * 451 | * 8 452 | * / \ 453 | * 5 10 454 | * / \ / 455 | * 3 7 9 456 | * / \ 457 | * 1 4 458 | * \ 459 | * 2 460 | */ 461 | int data[] = {8,5,3,1,10,2,7,9,4,6}; 462 | 463 | //删除根节点的情况 464 | // int data[] = {8,5,3}; //有左子树没有右子树 465 | // int data[] = {3,5,8}; //有右子树没有左子树 466 | // int data[] = {8,5,3,10}; //左右子树都有并且左节点就是左子树做大节点 467 | // int data[] = {8,5,7,10}; //左节点不是左子树最大节点 468 | 469 | int len; 470 | GET_ARRAY_LEN(data, len); 471 | 472 | init_tree(); 473 | 474 | node n; 475 | for (int i = 0; i < len; i++) 476 | { 477 | n.data = data[i]; 478 | insert_node_to_binary_search_tree(&root, &n); 479 | } 480 | 481 | /** 482 | * 寻找元素为5的节点,并得到节点指针 483 | */ 484 | // n.data = 8; 485 | // node * node_to_find = find_node_in_binary_search_tree(root, &n); 486 | // printf("%p\n", node_to_find); 487 | // printf("%d\n", node_to_find -> data); 488 | 489 | 490 | /** 491 | * 删除数据值为8的节点元素 492 | */ 493 | n.data = 3; 494 | delete_node_from_binary_search_tree(&root, &n); 495 | n.data = 8; 496 | delete_node_from_binary_search_tree(&root, &n); 497 | n.data = 10; 498 | delete_node_from_binary_search_tree(&root, &n); 499 | 500 | print_tree(root, "inorder"); 501 | 502 | return 1; 503 | } -------------------------------------------------------------------------------- /Tree/Makefile: -------------------------------------------------------------------------------- 1 | CC = cc 2 | 3 | LIB_DIR = ../lib 4 | 5 | #需要包含的头文件 6 | INCLUDES = -I../include 7 | 8 | #链接库目录 9 | LIBS = -L../lib 10 | 11 | #所有编译选项 12 | COMMON_FLAGS = -Wall $(INCLUDES) 13 | 14 | #所有需要生成的编译目标及其依赖 15 | all : BinarySearchTree \ 16 | BalancedBinaryTree \ 17 | RedBlackTree \ 18 | BTree \ 19 | BPlusTree 20 | 21 | #二叉查找树 22 | BinarySearchTree.o : BinarySearchTree.c 23 | $(CC) $(COMMON_FLAGS) -c BinarySearchTree.c 24 | 25 | #平衡二叉树 26 | BalancedBinaryTree.o : BalancedBinaryTree.c 27 | $(CC) $(COMMON_FLAGS) -c BalancedBinaryTree.c 28 | 29 | #红黑树 30 | RedBlackTree.o : RedBlackTree.c 31 | $(CC) $(COMMON_FLAGS) -c RedBlackTree.c 32 | 33 | #B树 34 | BTree.o : BTree.c 35 | $(CC) $(COMMON_FLAGS) -c BTree.c 36 | 37 | #B+树 38 | BPlusTree.o : BPlusTree.c 39 | $(CC) $(COMMON_FLAGS) -c BPlusTree.c 40 | 41 | # .PHONY是为了指定后面的元素,例如这里的clean并不是一个真正的文件,只是一个假想工作目标 42 | .PHONY : clean 43 | 44 | clean : 45 | rm -rf $(LIB_DIR)/*.a *.o \ 46 | BinarySearchTree \ 47 | BalancedBinaryTree \ 48 | RedBlackTree \ 49 | BTree \ 50 | BPlusTree 51 | 52 | -------------------------------------------------------------------------------- /Tree/README.md: -------------------------------------------------------------------------------- 1 | ### 树 - 想考验自己的编程功力,就把所有的树实现一遍吧:) - 2016-04-19 2 | 3 | 这里把树单独拿出来是因为树实在是太多种类了,也挺值得研究一下的 4 | 5 | 这里建议大家学习树的时候,能明确的知道一棵树在插入和删除节点之后的形态,最好能在纸上画一下,这样才能检查算法是否正确 6 | 7 | < 我们经常需要面对一些复杂性,这时我们需要细致的分析来理清头绪 > 8 | 9 | < 所有业务逻辑,都可以浓缩成算法,然后你会发现这个算法在很久以前就已经有人提出并实现了。所以,让我们好好研究下数据结构和算法吧 > 10 | 11 | < 数据结构和算法固然重要,但是明白它们解决问题的出发点更重要 > 12 | 13 | < 凡事,积累则快,应付则慢。想要快,则需要早早积累 > 14 | 15 | * 二叉查找树(BST) 16 | * 平衡二叉树(AVL) 17 | * 红黑树(RBT) 18 | * 哈希树 19 | * B树 20 | * B+树 21 | * B*树 22 | * Treap树 23 | * Trie树 24 | * R树 - 矩阵树 25 | * 伸展树(Splay Tree) 26 | * SB树 - Size Balanced Tree,由中国广东中山纪念中学的陈启峰发明 27 | * Merkle Tree - Dynamo中用来同步数据一致性的算法 28 | * LSM Tree - Log-Structured Merge-Trees,一种牺牲一定的读性能来大幅提高写性能的日志结构合并树 29 | 30 | #### 二叉查找树 31 | 32 | 也就是 左节点 < 根节点 < 右节点 的二叉树 33 | 34 | #### 平衡树 - 一棵二叉树的平衡性能越好,其效率越高! 35 | 36 | 其实除了二叉查找树之外,AVL,红黑树,Treap,伸展树等都属于平衡二叉树 37 | 38 | * AVL树 - 是最先发明的自平衡二叉查找树 39 | 40 | - 其平衡性最高 41 | 42 | - 应用场景:查询较多,插入和删除较少的情况 43 | 44 | * 红黑树 - 平衡性可能不如AVL树,但是其统计性能优异 45 | 46 | - 红黑树本质上是一棵二叉查找树,只是需要再满足下面这几个特点: 47 | 48 | 1. 每个节点非红即黑 49 | 2. 根节点是黑色 50 | 3. 每个叶节点都是黑色(包括NULL节点) 51 | 4. 所有红节点的子节点都是黑色 - 为了保证不会有连续的两个红色节点 52 | 5. 每个节点到其叶子节点的所有路径上都包含相同数目的黑色节点 53 | 54 | - 上面五条保证了最坏情况下的最长路径不会大于两倍的最短路径,这也就是红黑树的核心诉求:控制最坏情况下的最长路径 55 | 56 | - 红黑树实现平衡的核心在于为所有节点着色,并且通过以上五条来对节点颜色进行调整,并不是完全实现平衡,所以其平衡性是以颜色来驱动而非节点深度(所以其平衡性不如AVL树) 57 | 58 | - 应用场景:查询,插入和删除都较多的情况 59 | 60 | * 哈希树 - 一种附带哈希表的平衡二叉树 61 | 62 | #### B类树之所以在文件系统中应用广泛,是因为B类树可以有效减少磁盘的访问(或者更广泛的包括所有存在机械运动的操作),因为磁盘的I/O操作最费时 63 | 其实B树的出现还是因为成本的问题,目前内存的成本还是普遍高于磁盘,而因为大量数据不可能全部保存于内存中(例如数据库的索引),必然有一部分数据要转移到磁盘中,而这时对这些数据的操作,则需要考虑使用B类树来实现 64 | 所以也可以假设,如果有一天内存的成本已经足够低,生产内存已经和当前生成磁盘的成本一样的话,那么那个时候,可能B树就没有现在这样有用武之地了 65 | 所以说一切的一切,还是都在考虑成本和当前形势情况下做出的当下的最优选择(包括依赖于目前存储器的设计和计算机存取数据的方式,这些东西不是一成不变的,以后可能会发生变化),并不存在一个绝对完美的选择。选择是一直都在变化的。 66 | 67 | 因为B类树常常作为索引来使用,而衡量一个索引的最重要的指标就是在操作过程中I/O操作次数是否尽量少(比如为了尽量减少磁盘的读取次数,B树结构中的节点普遍设为页的大小,这样一个节点一次I/O就可以完全载入) 68 | 69 | BTree -> B+Tree -> B*Tree 的整个演化就是减少节点空间的分配,减少分配新节点的概率(也就是尽量减少分裂节点的操作) 70 | 71 | * B树 - 几乎是所有数据库的默认索引结构,一种磁盘存储器的树算法 72 | 73 | * B+树 - 是一种读写代价更低,查询效率更稳定(因为非终端节点都是关键字的索引,所以每个关键字的查询长度相同)的B树的变形,适应于文件系统 74 | 75 | * B* 树 - 是B+树的变体,最主要区别在于节点中关键字使用率为(2/3)*M(M为树的阶) 76 | 77 | * Treap树 - 一种二叉树与堆特征结合的树 78 | 79 | * Trie树 - 一种单词查找树,适合于匹配查找(效率高于哈希树) 80 | 81 | * R树 - Rectangle Tree - A dynamic index structure for spatial searching 82 | 83 | * 伸展树 84 | 85 | * SB树 86 | 87 | * Merkle Tree , 又称为 Merkle Hash Tree, 因为其所有节点都是哈希值 88 | 89 | - 是Amazon Dynamo中用来同步数据一致性的算法 90 | 91 | - Git在集群机器中的文件同步也是采用Merkle Tree 92 | 93 | - 在分布式环境下进行比对和校验时,Merkle Tree会大大减少数据的传输量和计算复杂度 94 | 95 | * LSM Tree , 一种redis和HBase中使用的树 -------------------------------------------------------------------------------- /Tree/RedBlackTree.c: -------------------------------------------------------------------------------- 1 | /** 2 | * 红黑树 3 | * 4 | * 因为红黑树不涉及到高度平衡度的旋转,所以插入效率肯定比AVL高,因为不涉及到复杂的旋转 5 | * 但是也就是因为如此,所以平衡度肯定不如AVL树 6 | * 7 | * 红黑树的平衡规则是根据红黑树自己的节点颜色定义来调整的,而不是AVL那样根据左右子树深度差不能超过1来调整的 8 | * 这就是红黑树和AVL树的平衡调整的不同 9 | * 10 | * @Description: Description 11 | * @Datetime: 2016-04-19 16:03:56 12 | * @Author: linjunjie 13 | */ 14 | 15 | #include "algorithm.h" 16 | 17 | typedef struct red_black_node{ 18 | int data; 19 | struct red_black_node * parent; 20 | struct red_black_node * left; 21 | struct red_black_node * right; 22 | int color; // 颜色标识 23 | } rbnode; 24 | 25 | #define BLACK 0 26 | #define RED 1 27 | 28 | rbnode * rbt_root; 29 | 30 | //打印类函数 31 | int print_tree_inorder(rbnode * n); 32 | int print_tree_preorder(rbnode * n); 33 | int print_tree_postorder(rbnode * n); 34 | int print_tree(rbnode * n, char * order); 35 | 36 | //树相关操作函数 37 | rbnode * get_grand_parent(rbnode * node); 38 | rbnode * get_uncle(rbnode * node); 39 | void rotate_left(rbnode * node); 40 | void rotate_right(rbnode * node); 41 | void modify_color(rbnode * * root, rbnode * node); 42 | rbnode * insert_node_to_red_black_tree(rbnode * * root, rbnode * node); 43 | 44 | 45 | void init_red_black_tree() 46 | { 47 | rbt_root = NULL; 48 | } 49 | 50 | //得到爷节点(祖父节点) 51 | rbnode * get_grand_parent(rbnode * node) 52 | { 53 | return node -> parent -> parent; 54 | } 55 | 56 | //得到叔节点(也就是父节点的平行节点) 57 | rbnode * get_uncle(rbnode * node) 58 | { 59 | if(node -> parent == get_grand_parent(node) -> left) 60 | { 61 | return get_grand_parent(node) -> right; 62 | } 63 | else 64 | { 65 | return get_grand_parent(node) -> left; 66 | } 67 | } 68 | 69 | /** 70 | * 左旋转,这种情况就是右边高度过高了 71 | * 72 | * 算法描述 73 | * rotate_left(t) 74 | * 1. k <- right(t) 75 | * 2. right(t) <- left(k) 76 | * 3. left(k) <- t 77 | * 4. parent(k) <- parent(t) 78 | */ 79 | void rotate_left(rbnode * node) 80 | { 81 | rbnode * tmp = node -> right; 82 | node -> right = tmp -> left; 83 | tmp -> left = node; 84 | tmp -> parent = node -> parent; 85 | 86 | //如果插入节点有父节点 87 | if(node -> parent != NULL) 88 | { 89 | //如果插入节点是父节点的左子节点 90 | if(node -> parent -> left == node) 91 | { 92 | node -> parent -> left = tmp; 93 | } 94 | else 95 | { 96 | node -> parent -> right = tmp; 97 | } 98 | } 99 | else 100 | { 101 | rbt_root = tmp; 102 | } 103 | 104 | node -> parent = tmp; 105 | } 106 | 107 | /** 108 | * 右旋转,这种情况就是左边过高了 109 | * 110 | * 看到有对右旋的算法描述很好,拿来这里看一下: 111 | * 112 | * rotate_right(t) 113 | * 1. k <- left(t) 114 | * 2. left(t) <- right(k) 115 | * 3. right(k) <- t 116 | * 4. parent(k) <- parent(t) 117 | */ 118 | void rotate_right(rbnode * node) 119 | { 120 | rbnode * tmp = node -> left; 121 | node -> left = tmp -> right; 122 | tmp -> right = node; 123 | tmp -> parent = node -> parent; 124 | 125 | //如果插入节点有父节点 126 | if(node -> parent != NULL) 127 | { 128 | //如果插入节点是父节点的左子节点 129 | if(node -> parent -> left == node) 130 | { 131 | node -> parent -> left = tmp; 132 | } 133 | else 134 | { 135 | node -> parent -> right = tmp; 136 | } 137 | } 138 | //如果父节点为空,那就是根节点啊兄弟!!!怎么这都能忘!!! 139 | else 140 | { 141 | rbt_root = tmp; 142 | } 143 | 144 | //上面的操作结束之后,终于可以把插入节点的父节点设置为左子节点 145 | node -> parent = tmp; 146 | } 147 | 148 | rbnode * find_node_in_red_black_tree(rbnode * root, rbnode * node){ 149 | if(root == NULL){ 150 | return NULL; 151 | } 152 | 153 | if(node -> data < root -> data){ 154 | return find_node_in_red_black_tree(root -> left, node); 155 | }else if(node -> data > root -> data){ 156 | return find_node_in_red_black_tree(root -> right, node); 157 | }else if(node -> data == root -> data) { 158 | return root; 159 | } 160 | 161 | return NULL; 162 | } 163 | 164 | //这里的插入就是BST的插入 165 | rbnode * insert_node_with_bst(rbnode * * current, rbnode * parent, rbnode * insert) 166 | { 167 | if((* current) == NULL) 168 | { 169 | rbnode * node; 170 | node = (rbnode *)malloc(sizeof(rbnode)); 171 | node -> color = RED; //默认插入红色 172 | node -> data = insert -> data; 173 | node -> parent = parent; 174 | node -> left = NULL; 175 | node -> right = NULL; 176 | *current = node; 177 | 178 | return *current; 179 | } 180 | else 181 | { 182 | if(insert -> data < (* current) -> data) 183 | { 184 | return insert_node_with_bst(&((* current) -> left), *current, insert); 185 | } 186 | else 187 | { 188 | return insert_node_with_bst(&((* current) -> right), *current, insert); 189 | } 190 | } 191 | 192 | return NULL; 193 | } 194 | 195 | /** 196 | * 红黑树中的节点插入 197 | * 198 | * 很多地方在介绍插入的时候,为了简化,默认是这么定义的: 199 | * 200 | * G节点:爷爷节点,祖父节点 201 | * P节点:父节点 202 | * U节点:叔父节点 203 | * N节点:插入节点 204 | * 205 | * 另外经过种种权衡,我们选择将插入的节点的默认颜色设置为红色(你可以去了解下为什么需要默认设置为红色而不是黑色) 206 | * 207 | * 在对红黑树颜色做调整的时候,我们需要注意比较常遇到的两点: 208 | * 1.父子节点不能同时为红色 209 | * 2.一个路径上的黑色高度是不允许改变的 210 | * 211 | * 212 | * 很多地方把红黑树的颜色调整分解为5种情况,这样挺便于理解的: 213 | * 1. 空树的情况,则直接设置为黑色 214 | * 2. 父节点黑色,则颜色不变更 215 | * 3. 父节点红色,叔节点红色 216 | * 4. 父节点红色,叔节点黑色 217 | * 5.1 插入节点是父节点的右孩子 218 | * 5.2 插入节点是父节点的左孩子 219 | */ 220 | rbnode * insert_node_to_red_black_tree(rbnode * * root, rbnode * insert) 221 | { 222 | //如果根节点为空 223 | if(root == NULL) 224 | { 225 | printf("%s\n", "case 1"); 226 | rbnode * n; 227 | n = (rbnode *)malloc(sizeof(rbnode)); 228 | n -> color = BLACK; //根节点为黑色 229 | n -> data = insert -> data; 230 | n -> parent = NULL; 231 | n -> left = NULL; 232 | n -> right = NULL; 233 | *root = n; 234 | 235 | return *root; 236 | } 237 | 238 | //节点插入 239 | rbnode * node = insert_node_with_bst(root, NULL, insert); 240 | 241 | modify_color(root, node); 242 | 243 | return node; 244 | } 245 | 246 | void modify_color(rbnode * * root, rbnode * node) 247 | { 248 | //如果根节点为空,也就是说是一棵空树的话,很简单,直接调整根节点颜色为黑色即可 249 | if(node -> parent == NULL) 250 | { 251 | printf("%s\n", "case 1"); 252 | node -> color = BLACK; 253 | } 254 | //如果插入节点的父节点是黑色,那么红黑树颜色是不需要调整的 255 | else if(node -> parent -> color == BLACK) 256 | { 257 | printf("%s\n", "case 2"); 258 | // return NULL; 259 | } 260 | /** 261 | * 否则父节点是红色,叔节点也存在并且为红色 262 | * 因为红黑树不能允许父子节点同时为红色,所以这里我们需要调整 263 | * 然后我们考虑一种特殊情况,那就是如果父节点和叔节点此时如果同时是红色,那么问题就变得简单了 264 | * 265 | * 黑 红 266 | * / \ / \ 267 | * 红 红 ======> 黑 黑 268 | * / / 269 | * 红 <-(insert node) 红 <-- (inserted) 270 | * 271 | * 如上图,我们调整的方法就是把爷节点变成红色,然后父节点和叔节点都变成黑色就可以了 272 | */ 273 | else if(get_uncle(node) != NULL && get_uncle(node) -> color == RED) 274 | { 275 | printf("%s\n", "case 3"); 276 | //叔父节点全部变为黑色 277 | node -> parent -> color = BLACK; 278 | get_uncle(node) -> color = BLACK; 279 | 280 | //爷节点调整为红色 281 | get_grand_parent(node) -> color = RED; 282 | 283 | /** 284 | * 因为我们调整了爷爷节点(也就是G节点)的颜色,那么让我们想一想......这样搞肯定会涉及破坏了G节点及其P节点的红黑性质 285 | * 所以此节点的G节点需要依次向上递归再次向上调整颜色 286 | * 287 | * 很多地方在介绍这里的时候用了一个词:尾递归,其实不要被这个词吓住哈哈,都是一样的意思 288 | */ 289 | modify_color(root, get_grand_parent(node)); 290 | } 291 | //那如果不是上面的情况,只有父节点是红色,叔节点不是红色(是黑色或者NULL) 292 | else 293 | { 294 | //插入节点是其父节点的右孩子,而父节点又是其父节点的左孩子 295 | if(node == node -> parent -> right && node -> parent == get_grand_parent(node) -> left) 296 | { 297 | printf("%s\n", "case 4.1"); 298 | rotate_left(node -> parent); 299 | node = node -> left; 300 | } 301 | //插入节点是其父节点的左孩子,而父节点又是其父节点的右孩子 302 | else if(node == node -> parent -> left && node -> parent == get_grand_parent(node) -> right) 303 | { 304 | printf("%s\n", "case 4.2"); 305 | rotate_right(node -> parent); 306 | node = node -> right; 307 | } 308 | 309 | node -> parent -> color = BLACK; 310 | get_grand_parent(node) -> color = RED; 311 | 312 | //插入节点是父节点的左节点,并且父节点是爷爷节点的左节点 313 | if(node == node -> parent -> left && node -> parent == get_grand_parent(node) -> left) 314 | { 315 | printf("%s\n", "case 5.1"); 316 | rotate_right(get_grand_parent(node)); 317 | } 318 | // (node == node -> parent -> left && node -> parent == get_grand_parent(node) -> right) 319 | else 320 | { 321 | printf("%s\n", "case 5.2"); 322 | rotate_left(get_grand_parent(node)); 323 | } 324 | } 325 | } 326 | 327 | int main(int argc, char * argv[]){ 328 | int data[] = {8,5,3,1,10,2,7,9,4,6}; 329 | // int data[] = {8,5,3,1,4}; //for test 330 | int len; 331 | GET_ARRAY_LEN(data, len); 332 | 333 | init_red_black_tree(); 334 | 335 | rbnode n; 336 | for (int i = 0; i < len; i++) 337 | { 338 | n.data = data[i]; 339 | insert_node_to_red_black_tree(&rbt_root, &n); 340 | } 341 | 342 | /** 343 | * 最终形态为: 344 | * 345 | * 5[黑] 346 | * / \ 347 | * 2[红] 8[红] 348 | * / \ / \ 349 | * 1[黑] 3[黑] 7[黑] 10[黑] 350 | * \ / / 351 | * 4[红] 6[红] 9[红] 352 | * 353 | */ 354 | print_tree(rbt_root, "preorder"); 355 | 356 | return 1; 357 | } 358 | 359 | //打印二叉树 360 | int print_tree(rbnode * n, char * order){ 361 | 362 | if(strcmp(order, "inorder") == 0) 363 | { 364 | printf("%s\n", "中序遍历打印红黑树 :"); 365 | return print_tree_inorder(n); 366 | } 367 | else if(strcmp(order, "preorder") == 0) 368 | { 369 | printf("%s\n", "前序遍历打印红黑树 :"); 370 | return print_tree_preorder(n); 371 | } 372 | else if(strcmp(order, "postorder") == 0) 373 | { 374 | printf("%s\n", "后序遍历打印红黑树 :"); 375 | return print_tree_postorder(n); 376 | } 377 | 378 | printf("%s\n", "中序遍历打印红黑树 :"); 379 | return print_tree_inorder(n); 380 | } 381 | 382 | // 中序遍历 383 | int print_tree_inorder(rbnode * n){ 384 | if(n == NULL){ 385 | return 0; 386 | }else{ 387 | print_tree_inorder(n -> left); 388 | printf("%d,%d\n", n -> data, n -> color); 389 | print_tree_inorder(n -> right); 390 | } 391 | 392 | return 1; 393 | } 394 | 395 | //递归方式打印二叉树 396 | //前序遍历,父节点位于最前 397 | int print_tree_preorder(rbnode * n){ 398 | 399 | if(n == NULL){ 400 | return 0; 401 | }else{ 402 | printf("%d,%d\n", n -> data, n -> color); 403 | print_tree_preorder(n -> left); 404 | print_tree_preorder(n -> right); 405 | } 406 | 407 | return 1; 408 | } 409 | 410 | //后序遍历 411 | int print_tree_postorder(rbnode * n){ 412 | 413 | if(n == NULL){ 414 | return 0; 415 | }else{ 416 | print_tree_postorder(n -> left); 417 | print_tree_postorder(n -> right); 418 | printf("%d,%d\n", n -> data, n -> color); 419 | } 420 | 421 | return 1; 422 | } -------------------------------------------------------------------------------- /data_structure/DoubleCircularLinkedList.c: -------------------------------------------------------------------------------- 1 | /** 2 | * 双向循环链表 - Doubly Circular Linked List 3 | * 4 | * 头尾相连的双向链表 5 | */ 6 | 7 | #include "algorithm.h" 8 | 9 | struct node{ 10 | int num; 11 | struct node * prev; /* 指向上一个节点 */ 12 | struct node * next; /* 指向下一个节点 */ 13 | }; 14 | 15 | typedef struct node * link; 16 | 17 | //定义一个头类型 18 | link head; 19 | 20 | //节点个数 21 | int node_count; 22 | 23 | /* 初始化一个空链表 */ 24 | void initList(){ 25 | head = NULL; 26 | node_count = 0; 27 | } 28 | 29 | /* 打印以head作为头开始的链表 */ 30 | void print(link head){ 31 | link node; 32 | node = head; 33 | printf("print the whole linkedlist:\n"); 34 | do{ 35 | printf("%d,", node -> num); 36 | node = node -> next; 37 | }while(node != head); 38 | printf("\n\n"); 39 | } 40 | 41 | /* 将一个节点加入到双向链表的末尾 */ 42 | int addNodeToTheEnd(link add_node){ 43 | link tmp; 44 | link current; 45 | 46 | tmp = (link) malloc (sizeof(struct node)); 47 | if(tmp == NULL){ 48 | return 0; 49 | } 50 | tmp -> num = add_node -> num; 51 | tmp -> prev = NULL; 52 | tmp -> next = NULL; 53 | 54 | if(head == NULL){ 55 | head = tmp; 56 | head -> next = head -> prev = tmp; 57 | }else{ 58 | current = head; 59 | for(;;current = current -> next){ 60 | if(current -> next == head){ /* 如果已经到了最后一个元素 */ 61 | current -> next = tmp; /* 当前节点的下一个节点应该是插入节点 */ 62 | tmp -> prev = current; /* 插入节点的上一级节点应该是当前节点 */ 63 | tmp -> next = head; /* 插入节点的下一个节点应该是头结点 */ 64 | head -> prev = tmp; 65 | break; 66 | } 67 | } 68 | } 69 | 70 | return 1; 71 | } 72 | 73 | /* 按照从小到大的顺序插入节点 */ 74 | int addNodeAscend(link add_node){ 75 | link prev,current,tail; 76 | link tmp; 77 | 78 | tmp = (link) malloc (sizeof(struct node)); 79 | if(tmp == NULL){ 80 | return 0; 81 | } 82 | memcpy(tmp, add_node, sizeof(struct node)); 83 | tmp -> prev = tmp -> next = NULL; 84 | 85 | if(head == NULL){ /* 如果是空链接 */ 86 | head = tmp; /* 直接将节点加入头结点 */ 87 | head -> next = head -> prev = head; 88 | }else{ 89 | current = head; /* 从头结点出发寻找 */ 90 | prev = current -> prev; 91 | for(;;prev = current, current = current -> next){ 92 | if(current -> next == head){ /* 如果是最后一个节点 */ 93 | if(tmp -> num < current -> num){ /* 判断最后一个节点与插入节点的大小关系 */ 94 | tmp -> next = current; 95 | current -> prev = tmp; 96 | prev -> next = tmp; 97 | tmp -> prev = prev; 98 | if(current == head){ /* 如果此时既是末节点又是头结点,则此时的头结点就应该变为插入节点 */ 99 | head = tmp; /* 头结点变为插入节点 */ 100 | } 101 | }else{ 102 | tmp -> prev = current; 103 | current -> next = tmp; 104 | tmp -> next = head; 105 | head -> prev = tmp; 106 | } 107 | break; 108 | }else if(tmp -> num < current -> num){ /* 如果找到了自己应该插入的位置 */ 109 | if(current == head){ /* 如果此时是头结点 */ 110 | tail = head -> prev; /* 首先拿到尾节点 */ 111 | current -> prev = tmp; /* 则此时当前节点不再作为头结点,并且当前节点的头结点变为了插入节点 */ 112 | tmp -> next = current; /* 插入节点的尾节点应该是当前节点 */ 113 | head = tmp; /* 将插入节点作为头结点 */ 114 | head -> prev = tail; 115 | tail -> next = head; 116 | }else{ /* 如果不是头结点 */ 117 | tmp -> next = current; /* 则插入节点的尾节点为当前节点 */ 118 | tmp -> prev = prev; /* 插入节点的头结点是当前节点的头结点 */ 119 | current -> prev = tmp; /* 则当前节点的头结点是插入节点 */ 120 | prev -> next = tmp; /* 头结点的尾节点应为插入节点 */ 121 | } 122 | break; 123 | } 124 | } 125 | } 126 | 127 | return 1; 128 | } 129 | 130 | /* 对节点的释放,如果这里包含有其他指针,需要统一在这里一起释放 */ 131 | void freeNode(link free_node){ 132 | free(free_node); 133 | } 134 | 135 | /* 双向链表删除节点 */ 136 | int deleteNode(link del_node){ 137 | link current; 138 | link prev; 139 | link next; 140 | 141 | if(head == NULL){ 142 | return 0; 143 | } 144 | 145 | current = head; 146 | for(;;current = current -> next){ 147 | if(current -> num == del_node -> num){ 148 | if(current == head){ 149 | head = current -> next; 150 | head -> prev = NULL; 151 | }else{ 152 | prev = current -> prev; 153 | next = current -> next; 154 | prev -> next = next; 155 | next -> prev = prev; 156 | } 157 | freeNode(current); 158 | printf("delete node: %d success!\n\n", current -> num); 159 | 160 | return 1; 161 | } 162 | } 163 | 164 | } 165 | 166 | int main(int argc, char *argv[]){ 167 | int data[] = {8,5,3,1,10,2,7,9,4,6}; 168 | int len; 169 | GET_ARRAY_LEN(data, len); 170 | 171 | initList(); 172 | struct node add_node; 173 | for(int i = 0; i < len; i++){ 174 | add_node.num = data[i]; 175 | // addNodeToTheEnd(&add_node); /* 插入到链表尾部 */ 176 | addNodeAscend(&add_node); 177 | } 178 | print(head); 179 | 180 | //删除一个链节点 181 | add_node.num = 5; 182 | deleteNode(&add_node); 183 | 184 | print(head); 185 | } -------------------------------------------------------------------------------- /data_structure/DoubleLinkedList.c: -------------------------------------------------------------------------------- 1 | /** 2 | * 双向链表 - Doubly-Linked List 3 | * 4 | * 每一个节点都包含一个或多个存储数据的data域和两个分别指向父节点(或称为头结点)和子节点(或称为尾节点)的指针域 5 | * 多个前后相连的双向链节点便组成了双向链表 6 | * 7 | */ 8 | 9 | #include "algorithm.h" 10 | 11 | struct node{ 12 | int num; 13 | struct node * prev; /* 指向上一个节点 */ 14 | struct node * next; /* 指向下一个节点 */ 15 | }; 16 | 17 | typedef struct node * link; 18 | 19 | //定义一个头类型 20 | link head; 21 | 22 | //节点个数 23 | int node_count; 24 | 25 | /* 初始化一个空链表 */ 26 | void initList(){ 27 | head = NULL; 28 | node_count = 0; 29 | } 30 | 31 | /* 打印以head作为头开始的链表 */ 32 | void print(link head){ 33 | link node; 34 | node = head; 35 | printf("print the whole linkedlist:\n"); 36 | while(node != NULL){ 37 | printf("%d,", node -> num); 38 | node = node -> next; 39 | } 40 | printf("\n\n"); 41 | } 42 | 43 | /* 将一个节点加入到双向链表的开始 */ 44 | int addNodeToHead(link add_node){ 45 | link tmp; 46 | link current; 47 | 48 | tmp = (link) malloc (sizeof(struct node)); 49 | if(tmp == NULL){ 50 | return 0; 51 | } 52 | tmp -> num = add_node -> num; 53 | tmp -> prev = NULL; 54 | tmp -> next = NULL; 55 | 56 | if(head == NULL){ 57 | head = tmp; 58 | }else{ 59 | current = head; 60 | tmp -> next = current; 61 | current -> prev = tmp; 62 | head = tmp; 63 | } 64 | 65 | return 1; 66 | } 67 | 68 | /* 将一个节点加入到双向链表的末尾 */ 69 | int addNodeToTail(link add_node){ 70 | link tmp; 71 | link current; 72 | 73 | tmp = (link) malloc (sizeof(struct node)); 74 | if(tmp == NULL){ 75 | return 0; 76 | } 77 | tmp -> num = add_node -> num; 78 | tmp -> prev = NULL; 79 | tmp -> next = NULL; 80 | 81 | if(head == NULL){ 82 | head = tmp; 83 | }else{ 84 | current = head; 85 | for(;;current = current -> next){ 86 | if(current -> next == NULL){ 87 | tmp -> prev = current; 88 | current -> next = tmp; 89 | break; 90 | } 91 | } 92 | } 93 | 94 | return 1; 95 | } 96 | 97 | /* 按照从小到大的顺序插入节点 */ 98 | int addNodeAscend(link add_node){ 99 | link prev,current; 100 | link tmp; 101 | 102 | tmp = (link) malloc (sizeof(struct node)); 103 | if(tmp == NULL){ 104 | return 0; 105 | } 106 | memcpy(tmp, add_node, sizeof(struct node)); 107 | tmp -> prev = tmp -> next = NULL; 108 | 109 | if(head == NULL){ /* 如果是空链接 */ 110 | head = tmp; /* 直接将节点加入头结点 */ 111 | }else{ 112 | current = head; /* 从头结点出发寻找 */ 113 | prev = current -> prev; 114 | for(;;prev = current, current = current -> next){ 115 | if(current == NULL){ /* 如果是最后一个节点,也就说明到了最后一个节点扔未找到合适的位置,则插入节点应插在链表尾部 (注意此时的current节点绝对不可能是head头结点) */ 116 | tmp -> prev = prev; /* 插入节点的头结点应该是上一个节点 */ 117 | prev -> next = tmp; /* 上一个节点的尾节点应该是插入节点 */ 118 | break; 119 | }else if(tmp -> num < current -> num){ /* 如果找到了自己应该插入的位置 */ 120 | if(current == head){ /* 如果此时是头结点 */ 121 | current -> prev = tmp; /* 则此时当前节点不再作为头结点,并且当前节点的头结点变为了插入节点 */ 122 | tmp -> next = current; /* 插入节点的尾节点应该是当前节点 */ 123 | head = tmp; /* 将插入节点作为头结点 */ 124 | }else{ /* 如果不是头结点 */ 125 | tmp -> next = current; /* 则插入节点的尾节点为当前节点 */ 126 | tmp -> prev = prev; /* 插入节点的头结点是当前节点的头结点 */ 127 | current -> prev = tmp; /* 则当前节点的头结点是插入节点 */ 128 | prev -> next = tmp; /* 头结点的尾节点应为插入节点 */ 129 | } 130 | break; 131 | } 132 | } 133 | } 134 | 135 | return 1; 136 | } 137 | 138 | /* 对节点的释放,如果这里包含有其他指针,需要统一在这里一起释放 */ 139 | void freeNode(link free_node){ 140 | free(free_node); 141 | } 142 | 143 | /* 双向链表删除节点 */ 144 | int deleteNode(link del_node){ 145 | link current; 146 | link prev; 147 | link next; 148 | 149 | if(head == NULL){ 150 | return 0; 151 | } 152 | 153 | current = head; 154 | for(;;current = current -> next){ 155 | if(current -> num == del_node -> num){ 156 | if(current == head){ 157 | head = current -> next; 158 | head -> prev = NULL; 159 | }else{ 160 | prev = current -> prev; 161 | next = current -> next; 162 | prev -> next = next; 163 | next -> prev = prev; 164 | } 165 | freeNode(current); 166 | printf("delete node: %d success!\n\n", current -> num); 167 | 168 | return 1; 169 | } 170 | } 171 | } 172 | 173 | /* 从链表开始处删除一个节点 */ 174 | int deleteNodeFromHead(){ 175 | link current; 176 | 177 | if(head == NULL){ 178 | return 0; 179 | } 180 | 181 | current = head -> next; 182 | current -> prev = NULL; 183 | head = current; 184 | 185 | return 1; 186 | } 187 | 188 | /* 根据位置获取链表中的特定元素(第一个元素的位置为1) */ 189 | int getNode(int node_index){ 190 | link current; 191 | if(head == NULL){ 192 | return -1; 193 | } 194 | 195 | int i = 1; 196 | current = head; 197 | for(;;current = current -> next, i++){ 198 | if(node_index == i){ 199 | return current -> num; 200 | } 201 | } 202 | } 203 | 204 | int main(int argc, char *argv[]){ 205 | int data[] = {8,5,3,1,10,2,7,9,4,6}; 206 | int len; 207 | GET_ARRAY_LEN(data, len); 208 | 209 | initList(); 210 | struct node add_node; 211 | for(int i = 0; i < len; i++){ 212 | add_node.num = data[i]; 213 | addNodeToHead(&add_node); 214 | // addNodeToTail(&add_node); /* 插入到链表尾部 */ 215 | // addNodeAscend(&add_node); 216 | } 217 | print(head); 218 | 219 | int num = getNode(3); 220 | printf("%d", num); 221 | return 1; 222 | 223 | //删除一个链节点 224 | add_node.num = 5; 225 | deleteNode(&add_node); 226 | 227 | //删除一个头节点 228 | // deleteNodeFromHead(); 229 | 230 | print(head); 231 | } -------------------------------------------------------------------------------- /data_structure/HashTable.c: -------------------------------------------------------------------------------- 1 | /** 2 | * hashtable,哈希表也称为散列表 3 | * 4 | * 这里使用拉链法来展示一个简单的哈希表 5 | * 所谓拉链法,也就是使用链表来处理哈希冲突 6 | * 7 | * 在目前php中( php5 )的哈希方法是:DJBX33A,其名称来源于此算法作者和方法: 8 | * DJBX33A (Daniel J. Bernstein, Times 33 with Addition) 9 | * 文件位于/php-src/Zend/zend_hash.h中,其中也有详细的介绍 10 | * php中对它的介绍是这样的: 11 | * This is one of the best known hash functions for strings. 12 | * Because it is both computed very fast and distributes very well. 13 | * 14 | */ 15 | 16 | #include "algorithm.h" 17 | 18 | #define HASH_TABLE_SIZE 7 //哈希表大小(哈希表的大小一般是一个质数) 19 | 20 | //哈希表元素 21 | typedef struct hashtable_element_struct { 22 | char * str; //一个用来保存字符串的字段 23 | struct hashtable_element_struct * next; 24 | } element; 25 | 26 | element * HashTable[HASH_TABLE_SIZE]; 27 | 28 | //unix中处理哈希的标准函数,这里直接拿来使用 29 | //当然你可以编写自己的哈希函数 30 | unsigned long hashELF(const char * name){ 31 | unsigned long hash = 0, i; 32 | 33 | while(*name){ 34 | hash = (hash << 4) + *name++; 35 | i = hash & 0xf0000000; 36 | if(i){ 37 | hash ^= i >> 24; 38 | } 39 | hash &= ~i; 40 | } 41 | return hash; 42 | } 43 | 44 | unsigned int myHash(const char * name){ 45 | unsigned long i = 0; 46 | i = hashELF(name); 47 | return i % HASH_TABLE_SIZE; 48 | } 49 | 50 | //初始化一个哈希表 51 | void initHashTable(){ 52 | for (int i = 0; i < HASH_TABLE_SIZE; i++){ 53 | HashTable[i] = NULL; 54 | } 55 | } 56 | 57 | void printHashTable(){ 58 | element * e; 59 | for (int i = 0; i < HASH_TABLE_SIZE; i++){ 60 | e = HashTable[i]; 61 | 62 | //按照槽打印 63 | printf("slot %d : ", i); 64 | while(e != NULL){ 65 | printf("%s,", e -> str); 66 | e = e -> next; 67 | } 68 | 69 | printf("\n"); 70 | } 71 | } 72 | 73 | int insertHashTable(char * str){ 74 | unsigned int hi; 75 | element * e; 76 | 77 | //计算出要插入的元素的哈希值 78 | hi = myHash(str); 79 | 80 | //为元素开辟内存 81 | e = (element *)malloc(sizeof(element)); 82 | if(e == NULL){ 83 | return 0; 84 | } 85 | 86 | //初始化 87 | // e -> str = (char *)malloc(sizeof(char) * (strlen(str) + 1)); 88 | // strcpy(e -> str, str); 89 | e -> str = str; 90 | e -> next = NULL; 91 | 92 | element * tmp = HashTable[hi]; 93 | if(tmp == NULL){ 94 | HashTable[hi] = e; 95 | }else{ 96 | while(tmp != NULL){ 97 | if(tmp -> next == NULL){ 98 | tmp -> next = e; 99 | break; 100 | }else{ 101 | tmp = tmp -> next; 102 | } 103 | } 104 | } 105 | 106 | return 1; 107 | 108 | } 109 | 110 | int main(int argc, char * argv[]){ 111 | char * data[] = {"my","name","is","hashtable","this","is","an","example","program","let","us","play"}; 112 | int len; 113 | GET_ARRAY_LEN(data, len); 114 | 115 | initHashTable(); 116 | 117 | for (int i = 0; i < len; i++) 118 | { 119 | insertHashTable(data[i]); 120 | } 121 | 122 | printHashTable(); 123 | 124 | return 1; 125 | } 126 | 127 | -------------------------------------------------------------------------------- /data_structure/Makefile: -------------------------------------------------------------------------------- 1 | CC = cc 2 | 3 | #定义本地目录 4 | #DIR = /usr/local/github/algorithm_in_c 5 | 6 | SRC_DIR = ../src 7 | LIB_DIR = ../lib 8 | 9 | #需要包含的头文件 10 | INCLUDES = -I../include 11 | 12 | #链接库目录 13 | LIBS = -L../lib 14 | 15 | #所有编译选项 16 | COMMON_FLAGS = -Wall $(INCLUDES) 17 | 18 | #所有要进行编译的文件,在这里也就是所有.c后缀的c文件 19 | SRC = $(wildcard *.c) 20 | 21 | #根据规则运算取得所有要生成的.o文件 22 | #OBJ = $(subst .c, .o, $(SRC)) 23 | 24 | #根据规则运算取得所有要编译生成的目标文件 25 | #TARGETS = $(subst .c, , $(SRC)) 26 | 27 | #需要的静态文件 28 | LIB_OBJ = libdll.a 29 | 30 | #所有需要生成的编译目标及其依赖 31 | all : $(LIB_OBJ) \ 32 | SingleLinkedList \ 33 | DoubleLinkedList \ 34 | SingleCircularLinkedList \ 35 | DoubleCircularLinkedList \ 36 | StackByArr \ 37 | StackByDoubleLinkedList \ 38 | Queue \ 39 | PriorityQueue \ 40 | HashTable 41 | 42 | #编译静态链接库 43 | libdll.a : dll.o 44 | #生成静态库libdll.a 45 | ar rs libdll.a dll.o 46 | #将生成的静态库文件转移到../lib目录下 47 | mv libdll.a ../lib 48 | 49 | dll.o : $(SRC_DIR)/dll.c 50 | $(CC) $(COMMON_FLAGS) -c $(SRC_DIR)/dll.c 51 | 52 | #单向链表 53 | SingleLinkedList.o : SingleLinkedList.c 54 | $(CC) $(COMMON_FLAGS) -c SingleLinkedList.c 55 | 56 | #双向链表 57 | DoubleLinkedList.o : DoubleLinkedList.c 58 | $(CC) $(COMMON_FLAGS) -c DoubleLinkedList.c 59 | 60 | #单向循环链表 61 | SingleCircularLinkedList.o : SingleCircularLinkedList.c 62 | $(CC) $(COMMON_FLAGS) -c SingleCircularLinkedList.c 63 | 64 | #双向循环链表 65 | DoubleCircularLinkedList.o : DoubleCircularLinkedList.c 66 | $(CC) $(COMMON_FLAGS) -c DoubleCircularLinkedList.c 67 | 68 | #栈 - 通过数组实现 69 | StackByArr.o : StackByArr.c 70 | $(CC) $(COMMON_FLAGS) -c StackByArr.c 71 | 72 | #栈 - 通过双向链表实现 73 | #这里用到了../lib/中的静态链接库libdll.a 74 | StackByDoubleLinkedList : StackByDoubleLinkedList.c 75 | $(CC) $(COMMON_FLAGS) $(LIBS) -ldll StackByDoubleLinkedList.c -o StackByDoubleLinkedList 76 | 77 | #队列 - 通过双向链表库libdll实现 78 | Queue : Queue.c 79 | $(CC) $(COMMON_FLAGS) $(LIBS) -ldll Queue.c -o Queue 80 | 81 | #优先级队列 - 通过双向链表库libdll实现的最大优先级队列 82 | PriorityQueue : PriorityQueue.c 83 | $(CC) $(COMMON_FLAGS) $(LIBS) -ldll PriorityQueue.c -o PriorityQueue 84 | 85 | #哈希表 86 | HashTable.o : HashTable.c 87 | $(CC) $(COMMON_FLAGS) -c HashTable.c 88 | 89 | # .PHONY是为了指定后面的元素,例如这里的clean并不是一个真正的文件,只是一个假想工作目标 90 | .PHONY : clean 91 | 92 | clean : 93 | rm -rf $(LIB_DIR)/*.a *.o \ 94 | SingleLinkedList \ 95 | DoubleLinkedList \ 96 | SingleCircularLinkedList \ 97 | DoubleCircularLinkedList \ 98 | StackByArr \ 99 | StackByDoubleLinkedList \ 100 | Queue \ 101 | PriorityQueue \ 102 | HashTable 103 | 104 | -------------------------------------------------------------------------------- /data_structure/PriorityQueue.c: -------------------------------------------------------------------------------- 1 | /** 2 | * 优先级队列 - 通过双向链表实现 3 | * 4 | * 这里实现的是最大优先级队列 5 | * 和普通队列的主要区别在于查找和出队列部分 6 | * 基本上,这里的实现是,在插入的时候将优先级越大的元素插入越靠近出队列的位置,这样最后优先级最大的就可以先出队列 7 | */ 8 | 9 | #include "dll.h" 10 | 11 | //队列元素 12 | typedef struct queue_element_struct { 13 | int num; 14 | int priority; //优先级 15 | } element; 16 | 17 | //队列 18 | typedef struct queue_struct { 19 | // element * base; 20 | int tail; 21 | int size; 22 | int max; 23 | int min; 24 | } queue; 25 | 26 | int myPrintPriorityQueueData(void * data); 27 | int myComparePriorityQueueData(void * left, void * right); 28 | 29 | queue * create_queue(int size){ 30 | queue * q; 31 | assert(size > 0); 32 | 33 | q = ( queue * ) malloc (sizeof( queue )); 34 | if(q == NULL){ 35 | printf("%s\n", "初始化队列失败"); 36 | return NULL; 37 | } 38 | 39 | // q -> base = (element * ) malloc ( size * sizeof( element )); 40 | // if(q -> base == NULL){ 41 | // printf("%s\n", "初始化队列元素失败"); 42 | // return NULL; 43 | // } 44 | 45 | q -> size = size; 46 | q -> tail = -1; 47 | q -> max = size - 1; 48 | q -> min = 0; 49 | 50 | return q; 51 | } 52 | 53 | int push_queue(queue * q, element * e){ 54 | if(q -> tail == q -> max){ 55 | return 0; 56 | } 57 | 58 | Snode node; 59 | node.data = (element *)malloc(sizeof(element)); 60 | memcpy(node.data, e, sizeof(element)); 61 | 62 | //优先级队列的插入是参照优先级大小的 63 | addNodeWithPriority(&node, myComparePriorityQueueData); 64 | 65 | q -> tail += 1; 66 | 67 | return 1; 68 | } 69 | 70 | int pop_queue(queue * q, element * e){ 71 | if(q -> tail == -1){ 72 | return 0; 73 | } 74 | 75 | element * tmp = deleteNodeFromTail(); 76 | memcpy(e, tmp, sizeof(element)); 77 | 78 | q -> tail -= 1; 79 | 80 | return 1; 81 | } 82 | 83 | //打印队列元素 84 | int myPrintPriorityQueueData(void * data){ 85 | element * e; 86 | e = data; 87 | printf("num : %d, priority : %d\n", e -> num, e -> priority); 88 | return 1; 89 | } 90 | 91 | //先判断优先级,在判断数值 92 | int myComparePriorityQueueData(void * left, void * right){ 93 | element * eleft, * eright; 94 | eleft = left; 95 | eright = right; 96 | if(eleft -> priority < eright -> priority){ 97 | return -1; 98 | }else if(eleft -> priority > eright -> priority){ 99 | return 1; 100 | }else if(eleft -> priority == eright -> priority){ 101 | if(eleft -> num < eright -> num){ 102 | return -1; 103 | }else if(eleft -> num == eright -> num){ 104 | return 1; 105 | }else if(eleft -> num == eright -> num){ 106 | return 0; 107 | } 108 | } 109 | 110 | return -2; 111 | } 112 | 113 | //打印整个队列 114 | int print_queue(){ 115 | printf("%s\n", "print the whole priority queue based on the linkedlist:"); 116 | printdll(myPrintPriorityQueueData); 117 | return 1; 118 | } 119 | 120 | //自定义比较函数 121 | int myCompare(void * data1, void * data2){ 122 | element * e1, * e2; 123 | e1 = data1; 124 | e2 = data2; 125 | if(e1 -> num < e2 -> num){ 126 | return 1; 127 | }else{ 128 | return 0; 129 | } 130 | } 131 | 132 | int main(int argc, char * argv[]){ 133 | int data[5][2] = {{8,5},{3,1},{10,2},{7,9},{4,6}}; 134 | int len; 135 | GET_ARRAY_LEN(data, len); 136 | 137 | initList(); 138 | 139 | queue * q; 140 | 141 | q = create_queue(len); 142 | if(q == NULL){ 143 | return 0; 144 | } 145 | 146 | //声明一个队列元素指针 147 | element e; 148 | 149 | /* 元素入队列 */ 150 | for(int i=0; i 0); 32 | 33 | q = ( queue * ) malloc (sizeof( queue )); 34 | if(q == NULL){ 35 | printf("%s\n", "初始化队列失败"); 36 | return NULL; 37 | } 38 | 39 | // q -> base = (element * ) malloc ( size * sizeof( element )); 40 | // if(q -> base == NULL){ 41 | // printf("%s\n", "初始化队列元素失败"); 42 | // return NULL; 43 | // } 44 | 45 | q -> size = size; 46 | q -> tail = -1; 47 | q -> max = size - 1; 48 | q -> min = 0; 49 | 50 | return q; 51 | } 52 | 53 | int push_queue(queue * q, element * e){ 54 | if(q -> tail == q -> max){ 55 | return 0; 56 | } 57 | 58 | Snode node; 59 | node.data = (element *)malloc(sizeof(element)); 60 | memcpy(node.data, e, sizeof(element)); 61 | 62 | addNodeToHead(&node, myPrintQueueData); 63 | 64 | if(head == NULL) 65 | printf("%d\n", 1); 66 | 67 | q -> tail += 1; 68 | 69 | return 1; 70 | } 71 | 72 | int pop_queue(queue * q, element * e){ 73 | if(q -> tail == -1){ 74 | return 0; 75 | } 76 | 77 | element * tmp = deleteNodeFromTail(); 78 | memcpy(e, tmp, sizeof(element)); 79 | 80 | q -> tail -= 1; 81 | 82 | return 1; 83 | } 84 | 85 | //打印队列元素 86 | int myPrintQueueData(void * data){ 87 | element * e = data; 88 | printf("%d,", e -> num); 89 | 90 | return 1; 91 | } 92 | 93 | //打印整个队列 94 | int print_queue(){ 95 | printf("%s\n", "print the whole queue based on the linkedlist:"); 96 | printdll(myPrintQueueData); 97 | return 1; 98 | } 99 | 100 | int main(int argc, char * argv[]){ 101 | int data[] = {8,5,3,1,10,2,7,9,4,6}; 102 | int len; 103 | GET_ARRAY_LEN(data, len); 104 | 105 | initQueue(); 106 | 107 | queue * q; 108 | 109 | q = create_queue(len); 110 | if(q == NULL){ 111 | return 0; 112 | } 113 | 114 | //声明一个队列元素结构 115 | element e; 116 | 117 | /* 元素入队列 */ 118 | for(int i=0; i next = head; 30 | node_count = 0; 31 | } 32 | 33 | /* 打印以head作为头开始的链表 */ 34 | void print(link head){ 35 | link node; 36 | node = head; 37 | printf("print the whole linkedlist:\n"); 38 | do{ 39 | printf("%d,", node->num); 40 | node = node -> next; 41 | }while(node != head); 42 | printf("\n\n"); 43 | } 44 | 45 | /* 在链表尾部添加一个节点 */ 46 | int addNodeToTheEnd(link add_node){ 47 | link tmp; //临时链,用于存储要添加的链,基本上就是要添加的节点add_node的拷贝 48 | link current; //当前链,用于遍历链表时存储当前遍历元素 49 | 50 | //申请一个链空间,若申请失败返回0,然后拷贝一份儿add_node,并且将尾部指向NULL 51 | tmp = (link)malloc(sizeof(struct node)); 52 | if(tmp == NULL){ return 0; } /* 申请内存空间失败则退出 */ 53 | memcpy(tmp, add_node, sizeof(struct node)); 54 | tmp -> next = NULL; 55 | 56 | if(head == NULL){ /* 如果还是空链表,则将申请的链节点直接赋予head头结点 */ 57 | head = tmp; 58 | head -> next = head; 59 | }else{ 60 | current = head; 61 | for(;;current = current -> next){ 62 | if(current -> next == head){ /* 循环到的node指向的下一个节点如果是head的话,则已经到达尾部 */ 63 | tmp -> next = head; 64 | current -> next = tmp; /* 将申请的链安装在尾部 */ 65 | break; 66 | } 67 | } 68 | } 69 | 70 | return 1; 71 | } 72 | 73 | /* 以降序添加节点 */ 74 | int addNodeAscend(link add_node){ 75 | link tmp; /* 用于保存被插入的临时节点 */ 76 | link current; /* 循环遍历时的当前节点 */ 77 | link prev; /* 前一个链节点 */ 78 | 79 | tmp = (link)malloc(sizeof(struct node)); 80 | if(tmp == NULL){ return 0; } /* 申请内存空间失败则退出 */ 81 | memcpy(tmp, add_node, sizeof(struct node)); 82 | tmp -> next = NULL; 83 | 84 | if(head == NULL){ /* 如果还是空链表,则将申请的链节点直接赋予head头结点 */ 85 | tail = head = tmp; 86 | }else{ 87 | prev = current = head; 88 | for(;;prev = current, current = current -> next){ /* 循环遍历链表,每次都保留上一个节点,并遍历到下一个节点 */ 89 | if(current == tail){ /* 如果遍历到链表最后一个节点都没有找到合适的位置 */ 90 | if(tmp -> num < current -> num){ /* 这个时候需要判断最后一个节点和插入节点的大小关系,如果小于 */ 91 | tmp -> next = current; /* 插入节点必然指向当前节点 */ 92 | prev -> next = tmp; 93 | if(current == head){ /* 如果当前节点是头结点(当前节点也有可能是头结点) */ 94 | head = tmp; /* 则插入节点变为头结点 */ 95 | } 96 | }else{ 97 | tmp -> next = head; /* 此时插入节点必然指向头结点 */ 98 | current -> next = tmp; /* 当前节点指向插入节点 */ 99 | tail = tmp; /* 插入节点作为尾部节点 */ 100 | } 101 | break; 102 | }else if(tmp -> num < current -> num){ /* 如果找到了自己的位置 */ 103 | if(current == head){ /* 如果这个位置是头结点 */ 104 | head = tmp; /* 则插入节点此时作为头结点 */ 105 | tmp -> next = current; /* 插入节点的尾部指向当前节点 */ 106 | tail -> next = head; /* 链表的尾部节点的指向也应该变为插入节点 */ 107 | }else{ /* 如果这个位置不是头结点 */ 108 | prev -> next = tmp; /* 则必然存在上一个节点,上一个节点必然指向插入节点 */ 109 | tmp -> next = current; /* 插入节点必然指向当前节点 */ 110 | } 111 | break; 112 | } 113 | } 114 | } 115 | 116 | return 1; 117 | } 118 | 119 | /* 对节点的释放,如果这里包含有其他指针,需要统一在这里一起释放 */ 120 | void freeNode(link free_node){ 121 | free(free_node); 122 | } 123 | 124 | int deleteNode(link del_node){ 125 | link current; /* 保存当前所在的链表 */ 126 | link prev; /* 保存上一个链 */ 127 | 128 | if(head == NULL){ 129 | return 0; 130 | } 131 | 132 | // 开辟一块儿新内存存放prev链节点,也就是所谓的“上一个链节点”,默认设置为指向head头结点 133 | prev = (link) malloc (sizeof(struct node)); 134 | if(prev == NULL){ return 0; } /* 申请内存空间失败则退出 */ 135 | prev -> next = current = head; 136 | for(;;prev = current, current = current -> next){ /* 将当前的链保存到prev,并遍历到下一个链 */ 137 | if(current == NULL){ 138 | break; 139 | }else if(current -> num == del_node -> num){ /* 如果找到了要删除的节点 */ 140 | if(current == head){ /* 如果要删除的节点是头节点 */ 141 | head = current -> next; /* 则将被删除的节点的子节点赋予头结点 */ 142 | }else{ 143 | prev -> next = current -> next; /* 如果要删除的节点不是头结点,则将父节点的子节点指向被删除节点的子节点 */ 144 | } 145 | freeNode(current); /* 经过上面处理了链节点的指向之后,current现在已经完全被孤立掉了,现在可以释放掉要删除链节点的内存了 */ 146 | printf("delete node: %d success!\n\n", current -> num); 147 | 148 | return 1; 149 | } 150 | } 151 | 152 | printf("delete node: %d fail!\n\n", current -> num); 153 | return -1; 154 | } 155 | 156 | int main(int argc, char *argv[]){ 157 | int data[] = {8,5,3,1,10,2,7,9,4,6}; 158 | int len; 159 | GET_ARRAY_LEN(data, len); 160 | 161 | initList(); 162 | struct node add_node; 163 | for(int i = 0; i < len; i++){ 164 | add_node.num = data[i]; 165 | // addNodeToTheEnd(&add_node); /* 插入到链表尾部 */ 166 | addNodeAscend(&add_node); /* 以降序插入链表 */ 167 | } 168 | print(head); 169 | 170 | //删除一个链节点 171 | add_node.num = 5; 172 | deleteNode(&add_node); 173 | 174 | print(head); 175 | } 176 | 177 | 178 | 179 | 180 | -------------------------------------------------------------------------------- /data_structure/SingleLinkedList.c: -------------------------------------------------------------------------------- 1 | /** 2 | * 单向链表 - Singly-Linked List 3 | * 4 | * 每一个节点都包含一个或多个存储数据的data域和一个指向子链节点的指针域 5 | * 多个前后相连的链节点便组成了链表 6 | * 7 | */ 8 | 9 | #include "algorithm.h" 10 | 11 | /* 一个存放正整数的链节点 */ 12 | struct node 13 | { 14 | int num; 15 | struct node * next; 16 | }; 17 | 18 | //定义一个链表节点指针类型link 19 | typedef struct node * link; 20 | 21 | //定义一个头类型 22 | link head; 23 | 24 | //节点个数 25 | int node_count; 26 | 27 | /* 初始化一个空链表 */ 28 | void initList(){ 29 | head = NULL; 30 | node_count = 0; 31 | } 32 | 33 | /* 打印以head作为头开始的链表 */ 34 | void print(link head){ 35 | link node; 36 | node = head; 37 | printf("print the whole linkedlist:\n"); 38 | while(node != NULL){ 39 | printf("%d,", node->num); 40 | node = node -> next; 41 | } 42 | printf("\n\n"); 43 | } 44 | 45 | /* 在链表尾部添加一个节点 */ 46 | int addNodeToTheEnd(link add_node){ 47 | link tmp; //临时链,用于存储要添加的链,基本上就是要添加的节点add_node的拷贝 48 | link current; //当前链,用于遍历链表时存储当前遍历元素 49 | 50 | //申请一个链空间,若申请失败返回0,然后拷贝一份儿add_node,并且将尾部指向NULL 51 | tmp = (link)malloc(sizeof(struct node)); 52 | if(tmp == NULL){ return 0; } /* 申请内存空间失败则退出 */ 53 | memcpy(tmp, add_node, sizeof(struct node)); 54 | tmp -> next = NULL; 55 | 56 | if(head == NULL){ /* 如果还是空链表,则将申请的链节点直接赋予head头结点 */ 57 | head = tmp; 58 | }else{ 59 | current = head; 60 | for(;;current = current -> next){ 61 | if(current -> next == NULL){ /* 循环到的node指向的下一个节点如果是NULL的话,则已经到达尾部 */ 62 | current -> next = tmp; /* 将申请的链安装在尾部 */ 63 | break; 64 | } 65 | } 66 | } 67 | 68 | return 1; 69 | } 70 | 71 | /* 以降序添加节点 */ 72 | int addNodeAscend(link add_node){ 73 | link tmp; /* 用于保存被插入的临时节点 */ 74 | link current; /* 循环遍历时的当前节点 */ 75 | link prev; /* 前一个链节点 */ 76 | 77 | tmp = (link)malloc(sizeof(struct node)); 78 | if(tmp == NULL){ return 0; } /* 申请内存空间失败则退出 */ 79 | memcpy(tmp, add_node, sizeof(struct node)); 80 | tmp -> next = NULL; 81 | 82 | if(head == NULL){ /* 如果还是空链表,则将申请的链节点直接赋予head头结点 */ 83 | head = tmp; 84 | }else{ 85 | prev = (link)malloc(sizeof(struct node)); 86 | if(prev == NULL){ return 0; } /* 申请内存空间失败则退出 */ 87 | prev -> next = current = head; 88 | for(;;prev = current, current = current -> next){ /* 循环遍历链表,每次都保留上一个节点,并遍历到下一个节点 */ 89 | if(current == NULL){ /* 如果已经遍历到链表的最后一个节点时仍没有找到比自己更大的节点的话,则将链节点插入到上一个节点所指向的下一个节点(也就是最后) */ 90 | prev -> next = tmp; 91 | break; 92 | }else if(tmp -> num < current -> num){ /* 如果找到了比自己大的节点位置 */ 93 | if(current == head){ 94 | head = tmp; /* 若此时是头结点,则直接将待插入节点插入到头结点 */ 95 | }else{ 96 | prev -> next = tmp; /* 否则插入上一个节点指向的下一个节点 */ 97 | } 98 | tmp -> next = current; /* 然后插入节点的下一个指向都是当前节点位置 */ 99 | break; 100 | } 101 | } 102 | } 103 | 104 | return 1; 105 | } 106 | 107 | /* 对节点的释放,如果这里包含有其他指针,需要统一在这里一起释放 */ 108 | void freeNode(link free_node){ 109 | free(free_node); 110 | } 111 | 112 | int deleteNode(link del_node){ 113 | link current; /* 保存当前所在的链表 */ 114 | link prev; /* 保存上一个链 */ 115 | 116 | if(head == NULL){ 117 | return 0; 118 | } 119 | 120 | // 开辟一块儿新内存存放prev链节点,也就是所谓的“上一个链节点”,默认设置为指向head头结点 121 | prev = (link) malloc (sizeof(struct node)); 122 | if(prev == NULL){ return 0; } /* 申请内存空间失败则退出 */ 123 | prev -> next = current = head; 124 | for(;;prev = current, current = current -> next){ /* 将当前的链保存到prev,并遍历到下一个链 */ 125 | if(current == NULL){ 126 | break; 127 | }else if(current -> num == del_node -> num){ /* 如果找到了要删除的节点 */ 128 | if(current == head){ /* 如果要删除的节点是头节点 */ 129 | head = current -> next; /* 则将被删除的节点的子节点赋予头结点 */ 130 | }else{ 131 | prev -> next = current -> next; /* 如果要删除的节点不是头结点,则将父节点的子节点指向被删除节点的子节点 */ 132 | } 133 | freeNode(current); /* 经过上面处理了链节点的指向之后,current现在已经完全被孤立掉了,现在可以释放掉要删除链节点的内存了 */ 134 | printf("delete node: %d success!\n\n", current -> num); 135 | 136 | return 1; 137 | } 138 | } 139 | 140 | printf("delete node: %d fail!\n\n", current -> num); 141 | return -1; 142 | } 143 | 144 | int main(int argc, char *argv[]){ 145 | int data[] = {8,5,3,1,10,2,7,9,4,6}; 146 | int len; 147 | GET_ARRAY_LEN(data, len); 148 | 149 | initList(); 150 | struct node add_node; 151 | for(int i = 0; i < len; i++){ 152 | add_node.num = data[i]; 153 | addNodeToTheEnd(&add_node); /* 插入到链表尾部 */ 154 | // addNodeAscend(&add_node); /* 以降序插入链表 */ 155 | } 156 | print(head); 157 | 158 | //删除一个链节点 159 | add_node.num = 5; 160 | deleteNode(&add_node); 161 | 162 | print(head); 163 | } 164 | 165 | 166 | 167 | 168 | -------------------------------------------------------------------------------- /data_structure/StackByArr.c: -------------------------------------------------------------------------------- 1 | /** 2 | * 栈结构 - 通过数组结构实现 3 | * 4 | * LIFO last in, first out 5 | */ 6 | 7 | #include "algorithm.h" 8 | 9 | struct _stack{ 10 | 11 | /* 栈内容纳的元素 */ 12 | struct _stack_element * base; 13 | 14 | /* 栈可以容纳元素的最大数 */ 15 | int max; 16 | 17 | /* 栈可以容纳元素的最小数 */ 18 | int min; 19 | 20 | /* 栈目前的大小 */ 21 | int size; 22 | 23 | /* 栈顶指针 */ 24 | int top; 25 | }; 26 | 27 | typedef struct _stack stack_struct; 28 | 29 | /* 一个最简单的栈元素,只保存一个整形变量 */ 30 | struct _stack_element 31 | { 32 | int num; 33 | }; 34 | 35 | typedef struct _stack_element stack_element_struct; 36 | 37 | stack_struct * create_stack(int size){ 38 | stack_struct * stack; 39 | assert(size > 0); 40 | 41 | stack = (stack_struct *) malloc (sizeof(stack_struct)); 42 | if(stack == NULL){ 43 | return NULL; 44 | } 45 | 46 | stack -> size = size; 47 | stack -> base = (stack_element_struct *) malloc ( size * sizeof( stack_element_struct )); 48 | if(stack -> base == NULL){ 49 | return NULL; 50 | } 51 | 52 | stack -> min = 0; 53 | stack -> max = size - 1; 54 | stack -> top = -1; 55 | 56 | return stack; 57 | } 58 | 59 | /* 销毁栈 */ 60 | void destroy_stack(stack_struct * stack){ 61 | if(stack == NULL){ 62 | return; 63 | } 64 | 65 | free(stack -> base); 66 | free(stack); 67 | 68 | return; 69 | } 70 | 71 | int push_stack(stack_struct * stack, stack_element_struct * element){ 72 | 73 | /* 栈满 */ 74 | if(stack -> top == stack -> max){ 75 | return 0; 76 | } 77 | 78 | /* 将压入栈的元素的内存拷贝到对应的栈数组中 */ 79 | memmove( & (( stack -> base )[ stack -> top]), element, sizeof(stack_element_struct)); 80 | 81 | /* 栈顶位置 +1 */ 82 | stack -> top += 1; 83 | 84 | return 1; 85 | } 86 | 87 | int pop_stack(stack_struct * stack, stack_element_struct * element){ 88 | 89 | /* 空栈 */ 90 | if(stack -> top == -1){ 91 | return 0; 92 | } 93 | 94 | /* 将栈顶元素的内存拷贝到弹出的栈元素中 */ 95 | memmove(element, & ( (stack -> base)[stack -> top]), sizeof(stack_element_struct)); 96 | 97 | /* 栈顶位置 -1 */ 98 | stack -> top -= 1; 99 | 100 | return 1; 101 | } 102 | 103 | stack_element_struct * view_element(stack_struct * stack, int element_num){ 104 | if(stack -> top == -1){ 105 | return NULL; 106 | } 107 | 108 | if(stack -> top - element_num < 0){ 109 | return NULL; 110 | } 111 | 112 | return ( & ( (stack -> base)[stack -> top - element_num] ) ); 113 | } 114 | 115 | int main(int argc, char * argv[]){ 116 | int data[] = {8,5,3,1,10,2,7,9,4,6}; 117 | int len; 118 | GET_ARRAY_LEN(data, len); 119 | 120 | /* 声明一个栈指针 */ 121 | stack_struct * stack; 122 | 123 | /* 构建一个栈,长度为上面的数组长度 */ 124 | stack = create_stack (len); 125 | if(stack == NULL){ 126 | return -1; 127 | } 128 | 129 | /* 声明一个栈元素 */ 130 | stack_element_struct * element; 131 | 132 | /* 为栈元素开辟内存空间 */ 133 | element = (stack_element_struct *) malloc (sizeof( stack_element_struct )); 134 | if(element == NULL){ 135 | return -1; 136 | } 137 | 138 | /* 将上面的数组元素全部压入栈 */ 139 | for(int i=0; i num = data[i]; 141 | push_stack(stack, element); 142 | } 143 | 144 | 145 | /* 打印栈内某一个元素 */ 146 | // element = view_element(stack, 2); 147 | // printf("%d\n", element -> num); 148 | 149 | 150 | /* 执行弹出操作,并打印被弹出的栈元素 */ 151 | while(pop_stack(stack, element) != 0){ 152 | printf("%d\n", element -> num); 153 | } 154 | 155 | return 1; 156 | } -------------------------------------------------------------------------------- /data_structure/StackByDoubleLinkedList.c: -------------------------------------------------------------------------------- 1 | #include "dll.h" 2 | struct stack_struct{ 3 | 4 | /* 栈内容纳的元素 */ 5 | // struct stack_element * base; 6 | 7 | /* 栈可以容纳元素的最大数 */ 8 | int max; 9 | 10 | /* 栈可以容纳元素的最小数 */ 11 | int min; 12 | 13 | /* 栈目前的大小 */ 14 | int size; 15 | 16 | /* 栈顶指针 */ 17 | int top; 18 | }; 19 | 20 | typedef struct stack_struct stack; 21 | 22 | /* 一个最简单的栈元素,只保存一个整形变量 */ 23 | struct stack_element 24 | { 25 | int num; 26 | }; 27 | 28 | typedef struct stack_element element; 29 | 30 | int myPrintStackData(void * data); 31 | 32 | stack * create_stack(int size){ 33 | stack * s; 34 | assert(size > 0); 35 | 36 | s = (stack *) malloc (sizeof(stack)); 37 | if(s == NULL){ 38 | return NULL; 39 | } 40 | 41 | s -> size = size; 42 | 43 | // s -> base = (element *) malloc ( size * sizeof( element )); 44 | // if(s -> base == NULL){ 45 | // return NULL; 46 | // } 47 | 48 | s -> min = 0; 49 | s -> max = size - 1; 50 | s -> top = -1; 51 | 52 | return s; 53 | } 54 | 55 | /* 销毁栈 */ 56 | void destroy_stack(stack * s){ 57 | if(s == NULL){ 58 | return; 59 | } 60 | 61 | // free(s -> base); 62 | free(s); 63 | 64 | return; 65 | } 66 | 67 | int push_stack(stack * s, element * e){ 68 | 69 | /* 栈满 */ 70 | if(s -> top == s -> max){ 71 | return 0; 72 | } 73 | 74 | /* 将压入栈中的元素添加到链表的开头 */ 75 | Snode n; 76 | n.data = (element *)malloc(sizeof(element)); 77 | memcpy(n.data, e, sizeof(element)); 78 | addNodeToHead(&n, myPrintStackData); 79 | 80 | /* 栈顶位置 +1 */ 81 | s -> top += 1; 82 | 83 | return 1; 84 | } 85 | 86 | int pop_stack(stack * s, element * e){ 87 | 88 | /* 空栈 */ 89 | if(s -> top == -1){ 90 | return 0; 91 | } 92 | 93 | //开辟一块儿内存空间 94 | element * tmp = (element *)malloc(sizeof(element)); 95 | 96 | /* 从链表的头部位置开始删除元素 */ 97 | tmp = deleteNodeFromHead(); 98 | 99 | //拷贝内存区到我们的参数指针element *中 100 | memcpy(e, tmp, sizeof(element)); 101 | 102 | /* 栈顶位置 -1 */ 103 | s -> top -= 1; 104 | 105 | return 1; 106 | } 107 | 108 | //打印队列元素 109 | int myPrintStackData(void * data){ 110 | element * e; 111 | e = data; 112 | printf("%d,", e -> num); 113 | return 1; 114 | } 115 | 116 | /* 打印整个栈 order : head to tail */ 117 | int print_stack(){ 118 | printf("%s\n", "print the whole stack based on the linkedlist:"); 119 | printdll(myPrintStackData); 120 | return 1; 121 | } 122 | 123 | /* 查看指定栈位置的栈元素 */ 124 | void * view_element(stack * s, int element_num){ 125 | if(s -> top == -1){ 126 | return NULL; 127 | } 128 | 129 | if(s -> top - element_num < 0){ 130 | return NULL; 131 | } 132 | 133 | return getNode(element_num); 134 | } 135 | 136 | int main(int argc, char * argv[]){ 137 | int data[] = {8,5,3,1,10,2,7,9,4,6}; 138 | // int data[] = {8,5}; 139 | int len; 140 | GET_ARRAY_LEN(data, len); 141 | 142 | initList(); 143 | 144 | /* 声明一个栈指针 */ 145 | stack * s; 146 | 147 | /* 构建一个栈,长度为上面的数组长度 */ 148 | s = create_stack (len); 149 | if(s == NULL){ 150 | return 0; 151 | } 152 | 153 | /* 声明一个栈元素 */ 154 | element * e; 155 | 156 | /* 为栈元素开辟内存空间 */ 157 | e = (element *) malloc (sizeof( element )); 158 | if(e == NULL){ 159 | return 0; 160 | } 161 | 162 | /* 将上面的数组元素全部压入栈 */ 163 | for(int i=0; i num = data[i]; 165 | push_stack(s, e); 166 | } 167 | 168 | //打印整个栈内元素 169 | print_stack(); 170 | // return 1; 171 | 172 | /* 打印栈内某一个元素 */ 173 | // int num = view_element(stack, 4); 174 | // printf("%d\n", num); 175 | // return 1; 176 | 177 | // 弹出一个栈元素 178 | // print_stack(); 179 | // pop_stack(stack, element); 180 | // printf("%d\n", element -> num); 181 | // print_stack(); //弹出一个之后再打印一下栈目前的情况 182 | // return 1; 183 | 184 | /* 执行弹出操作,并打印被弹出的栈元素 */ 185 | printf("%s\n", "pop the stack elements:"); 186 | while(pop_stack(s, e) != 0){ 187 | printf("%d,", e -> num); 188 | } 189 | printf("\n"); 190 | 191 | return 1; 192 | } -------------------------------------------------------------------------------- /include/algorithm.h: -------------------------------------------------------------------------------- 1 | #ifndef _ALGO_H_ 2 | #define _ALGO_H_ 1 3 | 4 | #include 5 | #include /* for malloc */ 6 | #include /* for memcpy */ 7 | #include /* for assert */ 8 | #include /* for B-tree */ 9 | 10 | #include "type.h" /* 自定义的一些类型 */ 11 | 12 | // 计算数组长度的宏 13 | #define GET_ARRAY_LEN(array,len){len = (sizeof(array) / sizeof(array[0]));} 14 | 15 | void printIntArray(int* arrData, int len); 16 | void swapInt (int *a, int *b); 17 | 18 | #endif -------------------------------------------------------------------------------- /include/dll.h: -------------------------------------------------------------------------------- 1 | /** 2 | * 带头尾指针的双向链表 3 | * 目前是用来实现栈和队列 4 | * dll.h 5 | * double linked list 6 | */ 7 | 8 | #ifndef _DLL_H_ 9 | #define _DLL_H_ 1 10 | 11 | #include "algorithm.h" 12 | 13 | struct node_struct{ 14 | void * data; /* 这个数据位应该是一个可以指向任意数据结构的指针 */ 15 | struct node_struct * prev; /* 指向上一个节点 */ 16 | struct node_struct * next; /* 指向下一个节点 */ 17 | }; 18 | 19 | //命名规则,大写S代表结构体 20 | typedef struct node_struct Snode; 21 | 22 | //命名规则,第一个小写p代表指针,第二个大写S代表结构体 23 | typedef Snode * pSnode; 24 | 25 | /** 26 | * 打印链表的回调函数接口, 调用链表的一方来自定义如何打印 27 | */ 28 | typedef int ( * dll_print_function )(void * data); 29 | 30 | /** 31 | * 比较函数 32 | * 这里的比较函数是为了支持任意两个类型的比较,所以类型定义为void *,比较的规则可以自定义 33 | * left < right -1 34 | * left = right 0 35 | * left > right 1 36 | */ 37 | typedef int ( * dll_compare_function )(void * left, void * right); 38 | 39 | //头尾指针 40 | pSnode head, tail; 41 | 42 | //节点个数 43 | int node_count; 44 | 45 | void initList(); 46 | void printdll(dll_print_function); 47 | int addNodeToHead(pSnode add_node, dll_print_function); 48 | int addNodeToTail(pSnode add_node); 49 | int addNodeWithPriority(pSnode add_node, dll_compare_function); 50 | void freeNode(pSnode free_node); 51 | int deleteNode(pSnode del_node, dll_compare_function); 52 | void * deleteNodeFromHead(); 53 | void * deleteNodeFromTail(); 54 | void * getNode(int node_index); 55 | 56 | #endif -------------------------------------------------------------------------------- /include/type.h: -------------------------------------------------------------------------------- 1 | #ifndef _TYPE_H_ 2 | 3 | #define true 1 4 | #define false 0 5 | #define null NULL 6 | 7 | typedef int bool; //我这边调试器没有boolean类型,所以自己定义bool类型 8 | 9 | #endif -------------------------------------------------------------------------------- /lib/.gitkeep: -------------------------------------------------------------------------------- 1 | #链接库目录 2 | -------------------------------------------------------------------------------- /search/BinarySearch.c: -------------------------------------------------------------------------------- 1 | /** 2 | * 二分查找 3 | */ 4 | 5 | #include "algorithm.h" 6 | 7 | /** 8 | * 二分查找算法 9 | * 10 | * pData 数组 11 | * len 数组长度 12 | * needle 待查询元素 13 | * start 查询数组起始位置 14 | * end 查询数组结束位置 15 | */ 16 | int BinarySearch (int* pData, int len, int needle, int start, int end) { 17 | int half = len / 2; 18 | int current = start + half; //当前索引 19 | if( needle < pData[current] ){ 20 | return BinarySearch(pData, half, needle, start, current); 21 | }else if( needle > pData[current] ){ 22 | return BinarySearch(pData, len - half - 1, needle, current + 1, end); 23 | }else{ 24 | return current; 25 | } 26 | } 27 | 28 | int main(){ 29 | int data[] = {1,2,4,7,10,13,15,20,22,25,30}; 30 | int len; 31 | GET_ARRAY_LEN(data, len); 32 | 33 | // 插入排序 34 | int index = BinarySearch(data, len, 10, 0, len - 1); 35 | printf("%d\n", index); 36 | } -------------------------------------------------------------------------------- /search/Makefile: -------------------------------------------------------------------------------- 1 | #需要包含的头文件 2 | INCLUS = -I../include 3 | 4 | #源文件 5 | SRC_DIR = ../src 6 | 7 | #链接库目录 8 | LIBS = -L../lib 9 | 10 | #所有编译选项 11 | COMMON_FLAGS = -Wall $(INCLUS) 12 | 13 | #需要的静态文件 14 | LIB_OBJ = libalgorithm.a 15 | 16 | #需要生成的文件 17 | all : $(LIB_OBJ) \ 18 | BinarySearch 19 | 20 | #编译静态链接库 21 | libalgorithm.a : algorithm.o 22 | #生成静态库libalgorithm.a 23 | ar rs libalgorithm.a algorithm.o 24 | #将生成的静态库文件转移到../lib目录下 25 | mv libalgorithm.a ../lib 26 | 27 | algorithm.o : $(SRC_DIR)/algorithm.c 28 | gcc $(COMMON_FLAGS) -c $(SRC_DIR)/algorithm.c 29 | 30 | BinarySearch : BinarySearch.o 31 | gcc -o BinarySearch -L../lib -lalgorithm BinarySearch.o 32 | 33 | BinarySearch.o : BinarySearch.c 34 | gcc $(COMMON_FLAGS) -c BinarySearch.c 35 | 36 | clean : 37 | rm -rf *o BinarySearch 38 | -------------------------------------------------------------------------------- /sort/BinInsertSort.c: -------------------------------------------------------------------------------- 1 | /** 2 | * 二分查找插入排序 - 一种添加了二分查找的直接插入排序 3 | * 4 | * 算法描述为: 5 | * 6 | * 设数组为a[0...n]。 7 | * 8 | * 1.将原序列分成有序区和无序区。a[0...i-1]为有序区,a[i...n] 为无序区。(i从1开始) 9 | * 2.从无序区中取出第一个元素,即a[i],使用二分查找算法在有序区中查找要插入的位置索引j 10 | * 3.将a[j]到a[i-1]的元素后移,并将a[i]赋值给a[j] 11 | * 4.重复步骤2~3,直到无序区元素为0 12 | */ 13 | 14 | #include "algorithm.h" 15 | 16 | // 二分搜索查找到需要插入的位置 17 | int BinSearch (int* pData, int needle, int start, int end) { 18 | int len = end - start + 1; 19 | int half = len / 2; 20 | int current = start + half; //当前索引 21 | if(needle < pData[current]){ 22 | if(current == start) return current; 23 | return BinSearch(pData, needle, start, current - 1); 24 | }else{ 25 | if(current == end) return current + 1; 26 | return BinSearch(pData, needle, current + 1, end); 27 | } 28 | } 29 | 30 | // Binary Insert Sort 二分查找插入排序 31 | void BinInsertSort(int* pData, int len){ 32 | int i,k; 33 | int tmp; 34 | int index; 35 | 36 | for(i = 1; i < len; i++){ 37 | //获取到待排序元素 38 | tmp = pData[i]; 39 | 40 | //通过对有序序列的二分搜索找到需要插入的位置 41 | index = BinSearch(pData, tmp, 0, i-1); 42 | 43 | //此位置开始的元素全部后移 44 | for(k = i; k > index; k--){ 45 | pData[k] = pData[k-1]; 46 | } 47 | 48 | //插入此位置 49 | pData[index] = tmp; 50 | 51 | // 打印排序过程 52 | printf("process %d: ",i); 53 | printIntArray(pData, len); 54 | } 55 | } 56 | 57 | int main(){ 58 | int data[] = {8,5,3,1,10,9,2,7,4,6}; 59 | int len; 60 | GET_ARRAY_LEN(data, len); 61 | 62 | // 打印原始需排序成员 63 | printf("original : "); 64 | printIntArray(data, len); 65 | 66 | // 插入排序 67 | BinInsertSort(data, len); 68 | 69 | // 打印最终结果 70 | printf("result : "); 71 | printIntArray(data, len); 72 | } 73 | -------------------------------------------------------------------------------- /sort/BubbleSort.c: -------------------------------------------------------------------------------- 1 | /** 2 | * 冒泡排序 3 | * 4 | * 此算法简单说就是总是将最大数往后移动到最后 5 | * 6 | * 编译之后运行, 将可以看到算法运行过程和排序结果 7 | * 8 | * 但是通过算法的步骤展示可以看出,此算法每次都会进行(len-1)次排序, 9 | * 此结果中在第四次排序之后已经排序成功,但是算法依然进行了四次没有必要的排序 10 | */ 11 | 12 | #include "algorithm.h" 13 | 14 | // Bubble Sort 冒泡排序 15 | // 2015年11月23日 星期一 11时38分59秒 CST 16 | void BubbleSort(int* pData, int len){ 17 | int iTemp; // 临时存储成员函数的变量 18 | for(int i=1;i pData[j+1]){ 22 | iTemp = pData[j+1]; 23 | pData[j+1] = pData[j]; 24 | pData[j] = iTemp; 25 | } 26 | } 27 | // 打印排序过程 28 | printf("process %d: ",i); 29 | printIntArray(pData, len); 30 | } 31 | } 32 | 33 | int main(){ 34 | int data[] = {8,5,3,1,9,2,7,4,6}; 35 | int len; 36 | GET_ARRAY_LEN(data, len); 37 | 38 | // 打印原始需排序成员 39 | printf("original : "); 40 | printIntArray(data, len); 41 | 42 | // 冒泡排序 43 | BubbleSort(data, len); 44 | 45 | // 打印最终结果 46 | printf("result : "); 47 | printIntArray(data, len); 48 | } 49 | -------------------------------------------------------------------------------- /sort/InsertSort.c: -------------------------------------------------------------------------------- 1 | /** 2 | * 插入排序 - 直接插入排序 3 | * 4 | * 插入排序又被我称为扑克牌排序法,就好像我们平时摸牌时的排序是一样的 5 | * 每次摸到新牌之后,都插入到前面已经排序好的牌中 6 | * 7 | * 在网上看到一种比较清晰的算法描述: 8 | * 9 | * 设数组为a[0...n-1]。 10 | * 11 | * 1. 初始时,a[0]自成1个有序区,无序区为a[1..n-1]。令i=1 12 | * 2. 将a[i]并入当前的有序区a[0…i-1]中形成a[0…i]的有序区间。 13 | * 3. i++并重复第二步直到i==n-1。排序完成。 14 | * 15 | */ 16 | 17 | #include "algorithm.h" 18 | 19 | // Insert Sort 插入排序 - 直接插入排序 20 | void InsertSort(int* pData, int len){ 21 | int i, j; 22 | int target; //target 用来保存被插入的元素 23 | 24 | //第一个元素默认已经排序过了,所以只对1-n的元素进行排序 25 | for(i=1;i0 && pData[j-1] > target){ 34 | pData[j] = pData[j-1]; 35 | j--; 36 | } 37 | 38 | //最终有两种情况: 39 | //1.找到了比target更小的元素,则target插入到此元素之后; 40 | //2.没有找到比target更小的元素,也就是说target本身就是最小的元素,则此时j=0,target放置到索引0的位置 41 | pData[j] = target; 42 | 43 | // 打印排序过程 44 | printf("process %d: ",i); 45 | printIntArray(pData, len); 46 | 47 | } 48 | } 49 | 50 | // 按照直接插入排序的算法描述标准自己编写的直接插入排序算法 51 | void MyInsertSort(int* pData, int len){ 52 | int i,j; 53 | int tmp; 54 | 55 | for(i = 1; i < len; i++){ //遍历无序区 56 | tmp = pData[i]; 57 | for(j = i; j > 0; j--){ //遍历有序区 58 | if(tmp < pData[j-1]){ //如果需排序元素 < 被比较元素 59 | pData[j] = pData[j-1]; //则被比较元素依次后移 60 | }else{ 61 | break; //如果 待排序元素 > 被比较元素,则此时的j就是正确的位置,应立即退出对有序区的遍历 62 | } 63 | } 64 | pData[j] = tmp; //将元素放置到自己的位置j 65 | 66 | // 打印排序过程 67 | printf("process %d: ",i); 68 | printIntArray(pData, len); 69 | } 70 | } 71 | 72 | int main(){ 73 | int data[] = {8,5,3,1,9,2,7,4,6}; 74 | int len; 75 | GET_ARRAY_LEN(data, len); 76 | 77 | // 打印原始需排序成员 78 | printf("original : "); 79 | printIntArray(data, len); 80 | 81 | // 插入排序 82 | InsertSort(data, len); 83 | // MyInsertSort(data, len); 84 | 85 | // 打印最终结果 86 | printf("result : "); 87 | printIntArray(data, len); 88 | } 89 | -------------------------------------------------------------------------------- /sort/Makefile: -------------------------------------------------------------------------------- 1 | #需要包含的头文件 2 | INCLUS = -I../include 3 | 4 | #源文件 5 | SRC_DIR = ../src 6 | 7 | #所有编译选项 8 | COMMON_FLAGS = -Wall $(INCLUS) -L../lib -lalgorithm 9 | 10 | #需要的静态文件 11 | LIB_OBJ = libalgorithm.a 12 | 13 | all : $(LIB_OBJ) \ 14 | BubbleSort InsertSort ShellSort BinInsertSort SelectSort QuickSort MergeSort 15 | 16 | #编译静态链接库 17 | libalgorithm.a : algorithm.o 18 | #生成静态库libalgorithm.a 19 | ar rs libalgorithm.a algorithm.o 20 | #将生成的静态库文件转移到../lib目录下 21 | mv libalgorithm.a ../lib 22 | 23 | algorithm.o : $(SRC_DIR)/algorithm.c 24 | gcc $(COMMON_FLAGS) -c $(SRC_DIR)/algorithm.c 25 | 26 | 27 | BubbleSort : BubbleSort.c 28 | gcc -o BubbleSort $(COMMON_FLAGS) BubbleSort.c 29 | 30 | InsertSort : InsertSort.c 31 | gcc -o InsertSort $(COMMON_FLAGS) InsertSort.c 32 | 33 | ShellSort : ShellSort.c 34 | gcc -o ShellSort $(COMMON_FLAGS) ShellSort.c 35 | 36 | BinInsertSort : BinInsertSort.c 37 | gcc -o BinInsertSort $(COMMON_FLAGS) BinInsertSort.c 38 | 39 | SelectSort : SelectSort.c 40 | gcc -o SelectSort $(COMMON_FLAGS) SelectSort.c 41 | 42 | QuickSort : QuickSort.c 43 | gcc -o QuickSort $(COMMON_FLAGS) QuickSort.c 44 | 45 | MergeSort : MergeSort.c 46 | gcc -o MergeSort $(COMMON_FLAGS) MergeSort.c 47 | 48 | clean : 49 | rm -rf *o BubbleSort InsertSort ShellSort BinInsertSort SelectSort QuickSort MergeSort 50 | -------------------------------------------------------------------------------- /sort/MergeSort.c: -------------------------------------------------------------------------------- 1 | /** 2 | * 归并排序 - 又称为合并排序,二路排序 3 | * 4 | * 算法描述: 5 | * 1.将一个待排序序列分成两个序列 6 | * 2.将两个序列中的项依次进行比较,将较小的项放入有序序列中 7 | * 3.递归遍历左右两个序列,直至排序完成 8 | * 9 | * 属于稳定排序 10 | * 11 | */ 12 | 13 | #include "algorithm.h" 14 | 15 | // 将两个有序序列数组合并成一个有序序列 16 | void MyMergeArr(int *pA, int *pB, int start, int mid, int end) { 17 | int i,j,k; 18 | int leftIndex = start; 19 | int rightIndex = mid + 1; 20 | int m; 21 | 22 | // 将左右两边的数组对应项进行比较,将较小的值放入pB中 23 | for(i = start; leftIndex <= mid && rightIndex <= end; i++){ 24 | if(pA[leftIndex] < pA[rightIndex]){ 25 | pB[i] = pA[leftIndex]; 26 | leftIndex++; 27 | }else{ 28 | pB[i] = pA[rightIndex]; 29 | rightIndex++; 30 | } 31 | } 32 | 33 | // 将left序列中剩余的成员接到最终序列后面 34 | for(j = leftIndex; j <= mid; i++, j++){ 35 | pB[i] = pA[j]; 36 | } 37 | 38 | // 将right序列中剩余的成员接到最终序列后面 39 | for(k = rightIndex; k <= end; i++, k++){ 40 | pB[i] = pA[k]; 41 | } 42 | 43 | /* 这一步至关重要,是将此次排序结果赋予pA,这样才能在下次排序时得到已排好序的序列 */ 44 | for(m = start; m <= end; m++){ 45 | pA[m] = pB[m]; 46 | } 47 | 48 | // 打印排序过程 49 | printf("mid is %d: ", mid); 50 | printIntArray(pB, end - start + 1); 51 | } 52 | 53 | void MyMergeSort(int *pData, int *pTmpData, int start, int end){ 54 | // 取得中间数,目的是将带排序序列分为左右两部分 55 | int mid = (start + end) / 2; 56 | 57 | // 只有序列中成员大于2的时候,才需要进行两个序列的合并,否则不需要再进行处理 58 | if(end > start){ 59 | MyMergeSort(pData, pTmpData, start, mid); 60 | MyMergeSort(pData, pTmpData, mid + 1, end); 61 | MyMergeArr(pData, pTmpData, start, mid, end); 62 | } 63 | } 64 | 65 | int main(){ 66 | int data[] = {8,5,3,1,10,2,7,9,4,6}; 67 | int len; 68 | GET_ARRAY_LEN(data, len); 69 | int *tmpData = (int *)malloc(sizeof(int) * len); /* 归并排序中用到的数组,用于保存排序好之后的成员,其长度应跟data一样大小 */ 70 | 71 | // 打印原始需排序成员 72 | printf("original : "); 73 | printIntArray(tmpData, len); 74 | 75 | // 快速排序 76 | MyMergeSort(data, tmpData, 0, len - 1); 77 | 78 | // 打印最终结果 79 | printf("result : "); 80 | printIntArray(tmpData, len); 81 | } -------------------------------------------------------------------------------- /sort/QuickSort.c: -------------------------------------------------------------------------------- 1 | /** 2 | * 快速排序 3 | * 4 | * - Charles Antony Richard Hoare @1960 5 | * - 东尼霍尔1962年在Computer Journal发表论文《Quicksort》以及《算法导论》的第七章 6 | * - 在 ALGOL 60 语言的基础上进行了改进,设计出了ALGOL X语言, 并发明了case语句 7 | * - 1980年获得了图灵奖 8 | * 9 | * 10 | * 一种通过一次排序将整个序列分为两个部分的排序算法,其中以中值为基准,一边序列的值一定比另一边序列的值大或小 11 | */ 12 | 13 | #include "algorithm.h" 14 | 15 | // 我自己实现的快速排序算法 16 | // 将序列中第一个数作为基准值,然后归位到中间,之后两边依次分治递归 17 | void MyQuickSort (int* pData, int start, int end){ 18 | int i,j,k; 19 | int mid; // 中值所在的元素索引 20 | int tmp; // 被比较的符合特征的元素 21 | int len; 22 | 23 | // 得到此次序列的长度 24 | len = end - start + 1; 25 | for(i = start; i < len; i++){ 26 | //中值起始位开始元素 27 | mid = i; 28 | for(j = i; j < len; j++){ 29 | 30 | //如果有比中值更小的元素 31 | if(pData[mid] > pData[j]){ 32 | 33 | //则将此更小的元素取出 34 | tmp = pData[j]; 35 | 36 | //将前面的元素依次后移 37 | for(k = j; k > i; k--){ 38 | pData[k] = pData[k-1]; 39 | } 40 | 41 | //将此元素安插在之前中值元素所在位置 42 | pData[i] = tmp; 43 | 44 | //中值元素本身的索引也后移一位 45 | mid++; 46 | } 47 | } 48 | } 49 | 50 | if(mid - start > 1){ 51 | MyQuickSort(pData, start, mid - 1); 52 | }else if(end - mid > 1){ 53 | MyQuickSort(pData, mid + 1, end); 54 | } 55 | } 56 | 57 | 58 | int main(){ 59 | int data[] = {8,5,3,1,10,2,7,9,4,6}; 60 | int len; 61 | GET_ARRAY_LEN(data, len); 62 | 63 | // 打印原始需排序成员 64 | printf("original : "); 65 | printIntArray(data, len); 66 | 67 | // 快速排序 68 | MyQuickSort(data, 0, len - 1); 69 | 70 | // 打印最终结果 71 | printf("result : "); 72 | printIntArray(data, len); 73 | } 74 | 75 | -------------------------------------------------------------------------------- /sort/SelectSort.c: -------------------------------------------------------------------------------- 1 | /** 2 | * 选择排序 3 | * 4 | * 仍然是将整个序列分为有序序列和无序序列 5 | * 但是是从无序序列中找到最小或最大的元素直接放置到有序序列中去 6 | */ 7 | 8 | #include 9 | 10 | // 选择排序 11 | void SelectSort(int* pData, int len){ 12 | int i,j; 13 | int tmp; 14 | int min_index; //无序序列中最小的元素索引 15 | 16 | for(i = 0; i < len; i++){ 17 | tmp = pData[i]; 18 | for(min_index = j = i; j < len; j++){ 19 | if(pData[min_index] > pData[j]){ 20 | min_index = j; 21 | } 22 | } 23 | 24 | //将最小的值和第i个比较元素位置互换 25 | swapInt(&pData[i], &pData[min_index]); 26 | 27 | // 打印排序过程 28 | printf("process %d: ",i); 29 | printIntArray(pData, len); 30 | } 31 | } 32 | 33 | int main(){ 34 | int data[] = {8,5,3,1,9,2,7,4,6}; 35 | int len; 36 | GET_ARRAY_LEN(data, len); 37 | 38 | // 打印原始需排序成员 39 | printf("original : "); 40 | printIntArray(data, len); 41 | 42 | // 冒泡排序 43 | SelectSort(data, len); 44 | 45 | // 打印最终结果 46 | printf("result : "); 47 | printIntArray(data, len); 48 | } -------------------------------------------------------------------------------- /sort/ShellSort.c: -------------------------------------------------------------------------------- 1 | /** 2 | * 希尔排序(shell sort): 一种增量序列插入排序, 又称缩小增量排序 3 | * 4 | * 是一种将整个序列根据增量进行分割,并在各子序列中分别进行插入排序的算法 5 | * 6 | * 之所以比直接插入排序的效率高的原因在于两点: 7 | * 1.一开始增量长度step_length较大时数据项每一趟排序需要的个数较少, 但数据项的间隔距离很长; 8 | * 2.当step_length减小时每一趟需要变动的数据项增多, 但此时已经基本接近于它们排序后的最终位置; 9 | * 10 | * 希尔排序的性能关键在于增量序列的选择 11 | * 12 | * 个人理解: 13 | * 因为直接插入排序在序列元素基本有序时是非常快的,所以希尔排序就是利用了这一点。 14 | * 希尔排序前期就是对整个序列中间距由大到小进行排序,直到最后序列已基本有序时再对全体元素进行一次插入排序。 15 | * 所以这样效率是很高的,如果直接对序列进行插入排序则效率会比较低。 16 | */ 17 | 18 | #include "algorithm.h" 19 | 20 | // Shell Sort 希尔排序 21 | void ShellSort(int* pData, int len){ 22 | int i,j; 23 | int tmp; 24 | int step_index = 0; 25 | int step_length = len / 2; //得到初次增量序列长度 26 | while(step_length >= 1){ 27 | for(i = step_length; i < len; i++){ 28 | //在各个增量子序列中进行插入排序 29 | tmp = pData[i]; 30 | for(j = i - step_length; j >= 0 && tmp < pData[j]; j = j - step_length){ 31 | pData[j+step_length] = pData[j]; 32 | } 33 | pData[j+step_length] = tmp; 34 | } 35 | 36 | // 打印排序过程 37 | printf("process %d: ", ++step_index); 38 | printIntArray(pData, len); 39 | 40 | step_length = step_length / 2; // 得到下一个增量序列长度 41 | } 42 | } 43 | 44 | int main(){ 45 | int data[] = {8,5,3,1,9,2,7,4,6}; 46 | int len; 47 | GET_ARRAY_LEN(data, len); 48 | 49 | // 打印原始需排序成员 50 | printf("original : "); 51 | printIntArray(data, len); 52 | 53 | // 希尔排序 54 | ShellSort(data, len); 55 | 56 | // 打印最终结果 57 | printf("result : "); 58 | printIntArray(data, len); 59 | } 60 | -------------------------------------------------------------------------------- /src/algorithm.c: -------------------------------------------------------------------------------- 1 | #include "algorithm.h" 2 | 3 | // 数组打印函数 4 | void printIntArray(int* arrData, int len){ 5 | for(int i=0;i data); 22 | node = node -> next; 23 | } 24 | printf("\n\n"); 25 | } 26 | 27 | /* 将一个节点加入到双向链表的开始 */ 28 | int addNodeToHead(pSnode add_node, dll_print_function self_defined_print){ 29 | pSnode tmp; 30 | pSnode current; 31 | 32 | tmp = (pSnode) malloc (sizeof(Snode)); 33 | if(tmp == NULL){ 34 | return 0; 35 | } 36 | 37 | memcpy(tmp, add_node, sizeof(Snode)); 38 | tmp -> prev = tmp -> next = NULL; 39 | 40 | if(head == NULL){ 41 | head = tail = tmp; 42 | }else{ 43 | if(head == tail){ 44 | tmp -> next = tail; 45 | tail -> prev = tmp; 46 | head = tmp; 47 | }else{ 48 | current = head; 49 | tmp -> next = current; 50 | current -> prev = tmp; 51 | head = tmp; 52 | } 53 | } 54 | 55 | return 1; 56 | } 57 | 58 | /* 将一个节点加入到双向链表的末尾 */ 59 | int addNodeToTail(pSnode add_node){ 60 | pSnode tmp; 61 | pSnode current; 62 | 63 | tmp = (pSnode) malloc (sizeof(Snode)); 64 | if(tmp == NULL){ 65 | return 0; 66 | } 67 | 68 | memcpy(tmp, add_node, sizeof(Snode)); 69 | tmp -> prev = tmp -> next = NULL; 70 | 71 | if(head == NULL){ 72 | tail = head = tmp; 73 | }else{ 74 | current = head; 75 | for(;;current = current -> next){ 76 | if(current -> next == NULL){ 77 | tmp -> prev = current; 78 | current -> next = tmp; 79 | tail = tmp; 80 | break; 81 | } 82 | } 83 | } 84 | 85 | return 1; 86 | } 87 | 88 | /* 按照从小到大的顺序插入节点 */ 89 | int addNodeWithPriority(pSnode add_node, dll_compare_function self_defined_compare){ 90 | pSnode prev,current; 91 | pSnode tmp; 92 | 93 | tmp = (pSnode) malloc (sizeof(Snode)); 94 | if(tmp == NULL){ 95 | return 0; 96 | } 97 | memcpy(tmp, add_node, sizeof(Snode)); 98 | tmp -> prev = tmp -> next = NULL; 99 | 100 | if(head == NULL){ /* 如果是空链接 */ 101 | head = tail = tmp; /* 直接将节点加入头结点 */ 102 | }else{ 103 | current = head; /* 从头结点出发寻找 */ 104 | prev = current -> prev; 105 | for(;;prev = current, current = current -> next){ 106 | if(current == NULL){ /* 如果是最后一个节点,也就说明到了最后一个节点扔未找到合适的位置,则插入节点应插在链表尾部 (注意此时的current节点绝对不可能是head头结点) */ 107 | tmp -> prev = prev; /* 插入节点的头结点应该是上一个节点 */ 108 | prev -> next = tmp; /* 上一个节点的尾节点应该是插入节点 */ 109 | tail = tmp; 110 | break; 111 | }else if(self_defined_compare(tmp -> data, current -> data) == -1){ /* 如果tmp < current, 则此时找到了自己应该插入的位置 */ 112 | if(current == head){ /* 如果此时是头结点 */ 113 | current -> prev = tmp; /* 则此时当前节点不再作为头结点,并且当前节点的头结点变为了插入节点 */ 114 | tmp -> next = current; /* 插入节点的尾节点应该是当前节点 */ 115 | head = tmp; /* 将插入节点作为头结点 */ 116 | }else{ /* 如果不是头结点 */ 117 | tmp -> next = current; /* 则插入节点的尾节点为当前节点 */ 118 | tmp -> prev = prev; /* 插入节点的头结点是当前节点的头结点 */ 119 | current -> prev = tmp; /* 则当前节点的头结点是插入节点 */ 120 | prev -> next = tmp; /* 头结点的尾节点应为插入节点 */ 121 | } 122 | break; 123 | } 124 | } 125 | } 126 | 127 | return 1; 128 | } 129 | 130 | /* 对节点的释放,如果这里包含有其他指针,需要统一在这里一起释放 */ 131 | void freeNode(pSnode free_node){ 132 | // free(free_node -> data); 133 | free(free_node); 134 | } 135 | 136 | /* 双向链表删除节点 */ 137 | int deleteNode(pSnode del_node, dll_compare_function self_defined_compare){ 138 | pSnode current; 139 | pSnode prev; 140 | pSnode next; 141 | 142 | if(head == NULL){ 143 | return 0; 144 | } 145 | 146 | current = head; 147 | for(;;current = current -> next){ 148 | if(self_defined_compare(current -> data, del_node -> data) == 0){ //如果current == del_node 149 | if(current == head){ 150 | head = current -> next; 151 | head -> prev = NULL; 152 | }else{ 153 | prev = current -> prev; 154 | next = current -> next; 155 | prev -> next = next; 156 | next -> prev = prev; 157 | } 158 | freeNode(current); 159 | // printf("delete node: %d success!\n\n", current -> num); 160 | 161 | return 1; 162 | } 163 | } 164 | } 165 | 166 | /* 删除一个头结点, 并返回删除的头结点指针 */ 167 | void * deleteNodeFromHead(){ 168 | void * data; 169 | pSnode current; 170 | pSnode next; 171 | 172 | if(head == NULL){ 173 | return NULL; 174 | } 175 | 176 | current = head; 177 | next = head -> next; 178 | data = current -> data; 179 | if(next != NULL){ 180 | next -> prev = NULL; 181 | head = next; 182 | } 183 | freeNode(current); 184 | 185 | return data; 186 | } 187 | 188 | //删除链表尾部元素 189 | //for 队列 190 | //@return 是被删除的链表元素 191 | void * deleteNodeFromTail(){ 192 | void * data; 193 | pSnode current; 194 | pSnode prev; 195 | 196 | if(tail == NULL){ 197 | return NULL; 198 | } 199 | 200 | current = tail; 201 | prev = tail -> prev; 202 | data = current -> data; 203 | if(prev != NULL){ 204 | prev -> next = NULL; 205 | tail = prev; 206 | } 207 | freeNode(current); 208 | 209 | return data; 210 | } 211 | 212 | /* 根据位置获取链表中的特定元素(第一个元素的位置为1) */ 213 | void * getNode(int node_index){ 214 | pSnode current; 215 | if(head == NULL){ 216 | return NULL; 217 | } 218 | 219 | int i = 1; 220 | current = head; 221 | for(;;current = current -> next, i++){ 222 | if(node_index == i){ 223 | return current -> data; 224 | } 225 | } 226 | } --------------------------------------------------------------------------------