From 363f1f4b5f79148f86a72afe7f5dfb7205282c68 Mon Sep 17 00:00:00 2001 From: krahets Date: Tue, 18 Apr 2023 20:19:07 +0800 Subject: [PATCH] build --- chapter_appendix/installation.md | 7 +- chapter_array_and_linkedlist/array.md | 62 +++- chapter_array_and_linkedlist/linked_list.md | 61 +++- chapter_array_and_linkedlist/list.md | 113 ++++++- chapter_binary_search/binary_search.md | 41 ++- .../space_complexity.md | 134 ++++++++- .../time_complexity.md | 151 +++++++++- chapter_hashing/hash_map.md | 4 +- chapter_heap/build_heap.md | 13 +- chapter_heap/heap.md | 96 +++++- .../replace_linear_by_hashing.md | 33 ++- chapter_sorting/bubble_sort.md | 34 ++- chapter_sorting/counting_sort.md | 58 +++- chapter_sorting/insertion_sort.md | 16 +- chapter_sorting/merge_sort.md | 41 +++ chapter_sorting/quick_sort.md | 122 +++++++- chapter_sorting/radix_sort.md | 53 +++- chapter_stack_and_queue/deque.md | 279 +++++++++++++++++- chapter_stack_and_queue/queue.md | 161 +++++++++- chapter_stack_and_queue/stack.md | 126 +++++++- chapter_tree/array_representation_of_tree.md | 9 +- chapter_tree/avl_tree.md | 191 +++++++++++- chapter_tree/binary_search_tree.md | 100 ++++++- chapter_tree/binary_tree.md | 43 ++- chapter_tree/binary_tree_traversal.md | 69 ++++- 25 files changed, 1910 insertions(+), 107 deletions(-) diff --git a/chapter_appendix/installation.md b/chapter_appendix/installation.md index 13f107048..089390319 100644 --- a/chapter_appendix/installation.md +++ b/chapter_appendix/installation.md @@ -4,24 +4,26 @@ comments: true # 14.1.   编程环境安装 -## 14.1.1.   安装 VSCode +## 14.1.1.   VSCode 本书推荐使用开源轻量的 VSCode 作为本地 IDE ,下载并安装 [VSCode](https://code.visualstudio.com/) 。 ## 14.1.2.   Java 环境 1. 下载并安装 [OpenJDK](https://jdk.java.net/18/)(版本需满足 > JDK 9)。 -2. 在 VSCode 的插件市场中搜索 `java` ,安装 Java Extension Pack 。 +2. 在 VSCode 的插件市场中搜索 `java` ,安装 Extension Pack for Java 。 ## 14.1.3.   C/C++ 环境 1. Windows 系统需要安装 [MinGW](https://sourceforge.net/projects/mingw-w64/files/)([配置教程](https://blog.csdn.net/qq_33698226/article/details/129031241)),MacOS 自带 Clang 无需安装。 2. 在 VSCode 的插件市场中搜索 `c++` ,安装 C/C++ Extension Pack 。 +3. (可选)打开 Settings 页面,搜索 `Clang_format_fallback Style` 代码格式化选项,设置为 `{ BasedOnStyle: Microsoft, BreakBeforeBraces: Attach }` 。 ## 14.1.4.   Python 环境 1. 下载并安装 [Miniconda3](https://docs.conda.io/en/latest/miniconda.html) 。 2. 在 VSCode 的插件市场中搜索 `python` ,安装 Python Extension Pack 。 +3. (可选)在命令行输入 `pip install black` ,安装代码格式化工具。 ## 14.1.5.   Go 环境 @@ -33,6 +35,7 @@ comments: true 1. 下载并安装 [node.js](https://nodejs.org/en/) 。 2. 在 VSCode 的插件市场中搜索 `javascript` ,安装 JavaScript (ES6) code snippets 。 +3. (可选)在 VSCode 的插件市场中搜索 `Prettier` ,安装代码格式化工具。 ## 14.1.7.   C# 环境 diff --git a/chapter_array_and_linkedlist/array.md b/chapter_array_and_linkedlist/array.md index 57653cd72..848cbb787 100755 --- a/chapter_array_and_linkedlist/array.md +++ b/chapter_array_and_linkedlist/array.md @@ -199,7 +199,14 @@ elementAddr = firtstElementAddr + elementLength * elementIndex === "C" ```c title="array.c" - [class]{}-[func]{randomAccess} + /* 随机返回一个数组元素 */ + int randomAccess(int *nums, int size) { + // 在区间 [0, size) 中随机抽取一个数字 + int randomIndex = rand() % size; + // 获取并返回随机元素 + int randomNum = nums[randomIndex]; + return randomNum; + } ``` === "C#" @@ -350,7 +357,21 @@ elementAddr = firtstElementAddr + elementLength * elementIndex === "C" ```c title="array.c" - [class]{}-[func]{extend} + /* 扩展数组长度 */ + int *extend(int *nums, int size, int enlarge) { + // 初始化一个扩展长度后的数组 + int *res = (int *)malloc(sizeof(int) * (size + enlarge)); + // 将原数组中的所有元素复制到新数组 + for (int i = 0; i < size; i++) { + res[i] = nums[i]; + } + // 初始化扩展后的空间 + for (int i = size; i < size + enlarge; i++) { + res[i] = 0; + } + // 返回扩展后的新数组 + return res; + } ``` === "C#" @@ -493,7 +514,15 @@ elementAddr = firtstElementAddr + elementLength * elementIndex === "C" ```c title="array.c" - [class]{}-[func]{insert} + /* 在数组的索引 index 处插入元素 num */ + void insert(int *nums, int size, int num, int index) { + // 把索引 index 以及之后的所有元素向后移动一位 + for (int i = size - 1; i > index; i--) { + nums[i] = nums[i - 1]; + } + // 将 num 赋给 index 处元素 + nums[index] = num; + } ``` === "C#" @@ -605,7 +634,14 @@ elementAddr = firtstElementAddr + elementLength * elementIndex === "C" ```c title="array.c" - [class]{}-[func]{removeItem} + /* 删除索引 index 处元素 */ + // 注意:stdio.h 占用了 remove 关键词 + void removeItem(int *nums, int size, int index) { + // 把索引 index 之后的所有元素向前移动一位 + for (int i = index; i < size - 1; i++) { + nums[i] = nums[i + 1]; + } + } ``` === "C#" @@ -757,7 +793,14 @@ elementAddr = firtstElementAddr + elementLength * elementIndex === "C" ```c title="array.c" - [class]{}-[func]{traverse} + /* 遍历数组 */ + void traverse(int *nums, int size) { + int count = 0; + // 通过索引遍历数组 + for (int i = 0; i < size; i++) { + count++; + } + } ``` === "C#" @@ -900,7 +943,14 @@ elementAddr = firtstElementAddr + elementLength * elementIndex === "C" ```c title="array.c" - [class]{}-[func]{find} + /* 在数组中查找指定元素 */ + int find(int *nums, int size, int target) { + for (int i = 0; i < size; i++) { + if (nums[i] == target) + return i; + } + return -1; + } ``` === "C#" diff --git a/chapter_array_and_linkedlist/linked_list.md b/chapter_array_and_linkedlist/linked_list.md index 342a41f5a..001b0ad60 100755 --- a/chapter_array_and_linkedlist/linked_list.md +++ b/chapter_array_and_linkedlist/linked_list.md @@ -101,10 +101,9 @@ comments: true struct ListNode *next; // 指向下一节点的指针(引用) }; - // typedef 作用是为一种数据类型定义一个新名字 typedef struct ListNode ListNode; - /* 构造函数,初始化一个新节点 */ + /* 构造函数 */ ListNode *newListNode(int val) { ListNode *node, *next; node = (ListNode *) malloc(sizeof(ListNode)); @@ -416,7 +415,12 @@ comments: true === "C" ```c title="linked_list.c" - [class]{}-[func]{insertNode} + /* 在链表的节点 n0 之后插入节点 P */ + void insert(ListNode *n0, ListNode *P) { + ListNode *n1 = n0->next; + P->next = n1; + n0->next = P; + } ``` === "C#" @@ -548,7 +552,18 @@ comments: true === "C" ```c title="linked_list.c" - [class]{}-[func]{removeNode} + /* 删除链表的节点 n0 之后的首个节点 */ + // 注意:stdio.h 占用了 remove 关键词 + void removeNode(ListNode *n0) { + if (!n0->next) + return; + // n0 -> P -> n1 + ListNode *P = n0->next; + ListNode *n1 = P->next; + n0->next = n1; + // 释放内存 + free(P); + } ``` === "C#" @@ -687,7 +702,14 @@ comments: true === "C" ```c title="linked_list.c" - [class]{}-[func]{access} + /* 访问链表中索引为 index 的节点 */ + ListNode *access(ListNode *head, int index) { + while (head && head->next && index) { + head = head->next; + index--; + } + return head; + } ``` === "C#" @@ -843,7 +865,17 @@ comments: true === "C" ```c title="linked_list.c" - [class]{}-[func]{findNode} + /* 在链表中查找值为 target 的首个节点 */ + int find(ListNode *head, int target) { + int index = 0; + while (head) { + if (head->val == target) + return index; + head = head->next; + index++; + } + return -1; + } ``` === "C#" @@ -996,7 +1028,24 @@ comments: true === "C" ```c title="" + /* 双向链表节点结构体 */ + struct ListNode { + int val; // 节点值 + struct ListNode *next; // 指向后继节点的指针(引用) + struct ListNode *prev; // 指向前驱节点的指针(引用) + }; + typedef struct ListNode ListNode; + + /* 构造函数 */ + ListNode *newListNode(int val) { + ListNode *node, *next; + node = (ListNode *) malloc(sizeof(ListNode)); + node->val = val; + node->next = NULL; + node->prev = NULL; + return node; + } ``` === "C#" diff --git a/chapter_array_and_linkedlist/list.md b/chapter_array_and_linkedlist/list.md index 4b2c0b438..cf69fcd00 100755 --- a/chapter_array_and_linkedlist/list.md +++ b/chapter_array_and_linkedlist/list.md @@ -77,7 +77,7 @@ comments: true === "C" ```c title="list.c" - + // C 未提供内置动态数组 ``` === "C#" @@ -175,7 +175,7 @@ comments: true === "C" ```c title="list.c" - + // C 未提供内置动态数组 ``` === "C#" @@ -333,7 +333,7 @@ comments: true === "C" ```c title="list.c" - + // C 未提供内置动态数组 ``` === "C#" @@ -495,7 +495,7 @@ comments: true === "C" ```c title="list.c" - + // C 未提供内置动态数组 ``` === "C#" @@ -603,7 +603,7 @@ comments: true === "C" ```c title="list.c" - + // C 未提供内置动态数组 ``` === "C#" @@ -679,7 +679,7 @@ comments: true === "C" ```c title="list.c" - + // C 未提供内置动态数组 ``` === "C#" @@ -1320,7 +1320,106 @@ comments: true === "C" ```c title="my_list.c" - [class]{myList}-[func]{} + /* 列表类简易实现 */ + struct myList { + int *nums; // 数组(存储列表元素) + int capacity; // 列表容量 + int size; // 列表大小 + int extendRatio; // 列表每次扩容的倍数 + }; + + /* 构造函数 */ + myList *newMyList() { + myList *list = malloc(sizeof(myList)); + list->capacity = 10; + list->nums = malloc(sizeof(int) * list->capacity); + list->size = 0; + list->extendRatio = 2; + return list; + } + + /* 析构函数 */ + void delMyList(myList *list) { + free(list->nums); + free(list); + } + + /* 获取列表长度 */ + int size(myList *list) { + return list->size; + } + + /* 获取列表容量 */ + int capacity(myList *list) { + return list->capacity; + } + + /* 访问元素 */ + int get(myList *list, int index) { + assert(index >= 0 && index < list->size); + return list->nums[index]; + } + + /* 更新元素 */ + void set(myList *list, int index, int num) { + assert(index >= 0 && index < list->size); + list->nums[index] = num; + } + + /* 尾部添加元素 */ + void add(myList *list, int num) { + if (size(list) == capacity(list)) { + extendCapacity(list); // 扩容 + } + list->nums[size(list)] = num; + list->size++; + } + + /* 中间插入元素 */ + void insert(myList *list, int index, int num) { + assert(index >= 0 && index < size(list)); + for (int i = size(list); i > index; --i) { + list->nums[i] = list->nums[i - 1]; + } + list->nums[index] = num; + list->size++; + } + + /* 删除元素 */ + // 注意:stdio.h 占用了 remove 关键词 + int removeNum(myList *list, int index) { + assert(index >= 0 && index < size(list)); + int num = list->nums[index]; + for (int i = index; i < size(list) - 1; i++) { + list->nums[i] = list->nums[i + 1]; + } + list->size--; + return num; + } + + /* 列表扩容 */ + void extendCapacity(myList *list) { + // 先分配空间 + int newCapacity = capacity(list) * list->extendRatio; + int *extend = (int *)malloc(sizeof(int) * newCapacity); + int *temp = list->nums; + + // 拷贝旧数据到新数据 + for (int i = 0; i < size(list); i++) + extend[i] = list->nums[i]; + + // 释放旧数据 + free(temp); + + // 更新新数据 + list->nums = extend; + list->capacity = newCapacity; + } + + /* 将列表转换为 Array 用于打印 */ + int *toArray(myList *list) { + return list->nums; + } ``` === "C#" diff --git a/chapter_binary_search/binary_search.md b/chapter_binary_search/binary_search.md index 90e7b288c..2a7f5bc69 100755 --- a/chapter_binary_search/binary_search.md +++ b/chapter_binary_search/binary_search.md @@ -184,7 +184,23 @@ $$ === "C" ```c title="binary_search.c" - [class]{}-[func]{binarySearch} + /* 二分查找(双闭区间) */ + int binarySearch(int *nums, int len, int target) { + // 初始化双闭区间 [0, n-1] ,即 left, right 分别指向数组首元素、尾元素 + int left = 0, right = len - 1; + // 循环,当搜索区间为空时跳出(当 left > right 时为空) + while (left <= right) { + int mid = (left + right) / 2; // 计算中点索引 mid + if (nums[mid] < target) // 此情况说明 target 在区间 [mid+1, right] 中 + left = mid + 1; + else if (nums[mid] > target) // 此情况说明 target 在区间 [left, mid-1] 中 + right = mid - 1; + else // 找到目标元素,返回其索引 + return mid; + } + // 未找到目标元素,返回 -1 + return -1; + } ``` === "C#" @@ -316,7 +332,10 @@ $$ === "C" ```c title="" - + // (i + j) 有可能超出 int 的取值范围 + int m = (i + j) / 2; + // 更换为此写法则不会越界 + int m = i + (j - i) / 2; ``` === "C#" @@ -489,7 +508,23 @@ $$ === "C" ```c title="binary_search.c" - [class]{}-[func]{binarySearch1} + /* 二分查找(左闭右开) */ + int binarySearch1(int *nums, int len, int target) { + // 初始化左闭右开 [0, n) ,即 left, right 分别指向数组首元素、尾元素+1 + int left = 0, right = len; + // 循环,当搜索区间为空时跳出(当 left = right 时为空) + while (left < right) { + int mid = (left + right) / 2; // 计算中点索引 mid + if (nums[mid] < target) // 此情况说明 target 在区间 [mid+1, right) 中 + left = mid + 1; + else if (nums[mid] > target) // 此情况说明 target 在区间 [left, mid) 中 + right = mid; + else // 找到目标元素,返回其索引 + return mid; + } + // 未找到目标元素,返回 -1 + return -1; + } ``` === "C#" diff --git a/chapter_computational_complexity/space_complexity.md b/chapter_computational_complexity/space_complexity.md index 1fcb1a92c..9b949d871 100755 --- a/chapter_computational_complexity/space_complexity.md +++ b/chapter_computational_complexity/space_complexity.md @@ -188,7 +188,18 @@ comments: true === "C" ```c title="" + /* 函数 */ + int func() { + // do something... + return 0; + } + int algorithm(int n) { // 输入数据 + const int a = 0; // 暂存数据(常量) + int b = 0; // 暂存数据(变量) + int c = func(); // 栈帧空间(调用函数) + return a + b + c; // 输出数据 + } ``` === "C#" @@ -335,7 +346,12 @@ comments: true === "C" ```c title="" - + void algorithm(int n) { + int a = 0; // O(1) + int b[10000]; // O(1) + if (n > 10) + vector nums(n); // O(n) + } ``` === "C#" @@ -497,7 +513,21 @@ comments: true === "C" ```c title="" - + int func() { + // do something + return 0; + } + /* 循环 O(1) */ + void loop(int n) { + for (int i = 0; i < n; i++) { + func(); + } + } + /* 递归 O(n) */ + void recur(int n) { + if (n == 1) return; + return recur(n - 1); + } ``` === "C#" @@ -707,7 +737,23 @@ $$ === "C" ```c title="space_complexity.c" - [class]{}-[func]{spaceConstant} + /* 常数阶 */ + void constant(int n) { + // 常量、变量、对象占用 O(1) 空间 + const int a = 0; + int b = 0; + int nums[1000]; + ListNode *node = newListNode(0); + free(node); + // 循环中的变量占用 O(1) 空间 + for (int i = 0; i < n; i++) { + int c = 0; + } + // 循环中的函数占用 O(1) 空间 + for (int i = 0; i < n; i++) { + func(); + } + } ``` === "C#" @@ -903,7 +949,40 @@ $$ === "C" ```c title="space_complexity.c" - [class]{}-[func]{spaceLinear} + /* 线性阶 */ + void linear(int n) { + // 长度为 n 的数组占用 O(n) 空间 + int *nums = malloc(sizeof(int) * n); + free(nums); + + // 长度为 n 的列表占用 O(n) 空间 + ListNode **nodes = malloc(sizeof(ListNode *) * n); + for (int i = 0; i < n; i++) { + nodes[i] = newListNode(i); + } + // 内存释放 + for (int i = 0; i < n; i++) { + free(nodes[i]); + } + free(nodes); + + // 长度为 n 的哈希表占用 O(n) 空间 + hashTable *h = NULL; + + for (int i = 0; i < n; i++) { + hashTable *tmp = malloc(sizeof(hashTable)); + tmp->key = i; + tmp->val = i; + HASH_ADD_INT(h, key, tmp); + } + + // 内存释放 + hashTable *curr, *tmp; + HASH_ITER(hh, h, curr, tmp) { + HASH_DEL(h, curr); + free(curr); + } + } ``` === "C#" @@ -1045,7 +1124,13 @@ $$ === "C" ```c title="space_complexity.c" - [class]{}-[func]{spaceLinearRecur} + /* 线性阶(递归实现) */ + void linearRecur(int n) { + printf("递归 n = %d\r\n", n); + if (n == 1) + return; + linearRecur(n - 1); + } ``` === "C#" @@ -1195,7 +1280,24 @@ $$ === "C" ```c title="space_complexity.c" - [class]{}-[func]{spaceQuadratic} + /* 平方阶 */ + void quadratic(int n) { + // 二维列表占用 O(n^2) 空间 + int **numMatrix = malloc(sizeof(int *) * n); + for (int i = 0; i < n; i++) { + int *tmp = malloc(sizeof(int) * n); + for (int j = 0; j < n; j++) { + tmp[j] = 0; + } + numMatrix[i] = tmp; + } + + // 内存释放 + for (int i = 0; i < n; i++) { + free(numMatrix[i]); + } + free(numMatrix); + } ``` === "C#" @@ -1333,7 +1435,15 @@ $$ === "C" ```c title="space_complexity.c" - [class]{}-[func]{spaceQuadraticRecur} + /* 平方阶(递归实现) */ + int quadraticRecur(int n) { + if (n <= 0) + return 0; + int *nums = malloc(sizeof(int) * n); + printf("递归 n = %d 中的 nums 长度 = %d\r\n", n, n); + free(nums); + return quadraticRecur(n - 1); + } ``` === "C#" @@ -1470,7 +1580,15 @@ $$ === "C" ```c title="space_complexity.c" - [class]{}-[func]{buildTree} + /* 指数阶(建立满二叉树) */ + TreeNode *buildTree(int n) { + if (n == 0) + return NULL; + TreeNode *root = newTreeNode(0); + root->left = buildTree(n - 1); + root->right = buildTree(n - 1); + return root; + } ``` === "C#" diff --git a/chapter_computational_complexity/time_complexity.md b/chapter_computational_complexity/time_complexity.md index 2f3f021d6..bd4e45006 100755 --- a/chapter_computational_complexity/time_complexity.md +++ b/chapter_computational_complexity/time_complexity.md @@ -869,7 +869,16 @@ $$ === "C" ```c title="time_complexity.c" - [class]{}-[func]{constant} + /* 常数阶 */ + int constant(int n) { + int count = 0; + int size = 100000; + int i = 0; + for (int i = 0; i < size; i++) { + count++; + } + return count; + } ``` === "C#" @@ -993,7 +1002,14 @@ $$ === "C" ```c title="time_complexity.c" - [class]{}-[func]{linear} + /* 线性阶 */ + int linear(int n) { + int count = 0; + for (int i = 0; i < n; i++) { + count++; + } + return count; + } ``` === "C#" @@ -1127,7 +1143,15 @@ $$ === "C" ```c title="time_complexity.c" - [class]{}-[func]{arrayTraversal} + /* 线性阶(遍历数组) */ + int arrayTraversal(int *nums, int n) { + int count = 0; + // 循环次数与数组长度成正比 + for (int i = 0; i < n; i++) { + count++; + } + return count; + } ``` === "C#" @@ -1274,7 +1298,17 @@ $$ === "C" ```c title="time_complexity.c" - [class]{}-[func]{quadratic} + /* 平方阶 */ + int quadratic(int n) { + int count = 0; + // 循环次数与数组长度成平方关系 + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + count++; + } + } + return count; + } ``` === "C#" @@ -1477,7 +1511,24 @@ $$ === "C" ```c title="time_complexity.c" - [class]{}-[func]{bubbleSort} + /* 平方阶(冒泡排序) */ + int bubbleSort(int *nums, int n) { + int count = 0; // 计数器 + // 外循环:待排序元素数量为 n-1, n-2, ..., 1 + for (int i = n - 1; i > 0; i--) { + // 内循环:冒泡操作 + for (int j = 0; j < i; j++) { + if (nums[j] > nums[j + 1]) { + // 交换 nums[j] 与 nums[j + 1] + int tmp = nums[j]; + nums[j] = nums[j + 1]; + nums[j + 1] = tmp; + count += 3; // 元素交换包含 3 个单元操作 + } + } + } + return count; + } ``` === "C#" @@ -1674,7 +1725,20 @@ $$ === "C" ```c title="time_complexity.c" - [class]{}-[func]{exponential} + /* 指数阶(循环实现) */ + int exponential(int n) { + int count = 0; + int bas = 1; + // cell 每轮一分为二,形成数列 1, 2, 4, 8, ..., 2^(n-1) + for (int i = 0; i < n; i++) { + for (int j = 0; j < bas; j++) { + count++; + } + bas *= 2; + } + // count = 1 + 2 + 4 + 8 + .. + 2^(n-1) = 2^n - 1 + return count; + } ``` === "C#" @@ -1811,7 +1875,12 @@ $$ === "C" ```c title="time_complexity.c" - [class]{}-[func]{expRecur} + /* 指数阶(递归实现) */ + int expRecur(int n) { + if (n == 1) + return 1; + return expRecur(n - 1) + expRecur(n - 1) + 1; + } ``` === "C#" @@ -1940,7 +2009,15 @@ $$ === "C" ```c title="time_complexity.c" - [class]{}-[func]{logarithmic} + /* 对数阶(循环实现) */ + int logarithmic(float n) { + int count = 0; + while (n > 1) { + n = n / 2; + count++; + } + return count; + } ``` === "C#" @@ -2063,7 +2140,12 @@ $$ === "C" ```c title="time_complexity.c" - [class]{}-[func]{logRecur} + /* 对数阶(递归实现) */ + int logRecur(float n) { + if (n <= 1) + return 0; + return logRecur(n / 2) + 1; + } ``` === "C#" @@ -2197,7 +2279,16 @@ $$ === "C" ```c title="time_complexity.c" - [class]{}-[func]{linearLogRecur} + /* 线性对数阶 */ + int linearLogRecur(float n) { + if (n <= 1) + return 1; + int count = linearLogRecur(n / 2) + linearLogRecur(n / 2); + for (int i = 0; i < n; i++) { + count++; + } + return count; + } ``` === "C#" @@ -2359,7 +2450,16 @@ $$ === "C" ```c title="time_complexity.c" - [class]{}-[func]{factorialRecur} + /* 阶乘阶(递归实现) */ + int factorialRecur(int n) { + if (n == 0) + return 1; + int count = 0; + for (int i = 0; i < n; i++) { + count += factorialRecur(n - 1); + } + return count; + } ``` === "C#" @@ -2606,9 +2706,34 @@ $$ === "C" ```c title="worst_best_time_complexity.c" - [class]{}-[func]{randomNumbers} + /* 生成一个数组,元素为 { 1, 2, ..., n },顺序被打乱 */ + int *randomNumbers(int n) { + // 分配堆区内存(创建一维可变长数组:数组中元素数量为n,元素类型为int) + int *nums = (int *)malloc(n * sizeof(int)); + // 生成数组 nums = { 1, 2, 3, ..., n } + for (int i = 0; i < n; i++) { + nums[i] = i + 1; + } + // 随机打乱数组元素 + for (int i = n - 1; i > 0; i--) { + int j = rand() % (i + 1); + int temp = nums[i]; + nums[i] = nums[j]; + nums[j] = temp; + } + return nums; + } - [class]{}-[func]{findOne} + /* 查找数组 nums 中数字 1 所在索引 */ + int findOne(int *nums, int n) { + for (int i = 0; i < n; i++) { + // 当元素 1 在数组头部时,达到最佳时间复杂度 O(1) + // 当元素 1 在数组尾部时,达到最差时间复杂度 O(n) + if (nums[i] == 1) + return i; + } + return -1; + } ``` === "C#" diff --git a/chapter_hashing/hash_map.md b/chapter_hashing/hash_map.md index 3b59072b5..8b86ecef3 100755 --- a/chapter_hashing/hash_map.md +++ b/chapter_hashing/hash_map.md @@ -174,7 +174,7 @@ comments: true === "C" ```c title="hash_map.c" - + // C 未提供内置哈希表 ``` === "C#" @@ -339,7 +339,7 @@ comments: true === "C" ```c title="hash_map.c" - + // C 未提供内置哈希表 ``` === "C#" diff --git a/chapter_heap/build_heap.md b/chapter_heap/build_heap.md index 5e634a142..e98331fca 100644 --- a/chapter_heap/build_heap.md +++ b/chapter_heap/build_heap.md @@ -102,7 +102,18 @@ comments: true === "C" ```c title="my_heap.c" - [class]{maxHeap}-[func]{newMaxHeap} + /* 构造函数,根据切片建堆 */ + maxHeap *newMaxHeap(int nums[], int size) { + // 所有元素入堆 + maxHeap *h = (maxHeap *)malloc(sizeof(maxHeap)); + h->size = size; + memcpy(h->data, nums, size * sizeof(int)); + for (int i = size - 1; i >= 0; i--) { + // 堆化除叶节点以外的其他所有节点 + siftDown(h, i); + } + return h; + } ``` === "C#" diff --git a/chapter_heap/heap.md b/chapter_heap/heap.md index 11c96f659..b08710ba7 100644 --- a/chapter_heap/heap.md +++ b/chapter_heap/heap.md @@ -255,7 +255,7 @@ comments: true === "C" ```c title="heap.c" - + // C 未提供内置 Heap 类 ``` === "C#" @@ -440,11 +440,20 @@ comments: true === "C" ```c title="my_heap.c" - [class]{maxHeap}-[func]{left} + /* 获取左子节点索引 */ + int left(maxHeap *h, int i) { + return 2 * i + 1; + } - [class]{maxHeap}-[func]{right} + /* 获取右子节点索引 */ + int right(maxHeap *h, int i) { + return 2 * i + 2; + } - [class]{maxHeap}-[func]{parent} + /* 获取父节点索引 */ + int parent(maxHeap *h, int i) { + return (i - 1) / 2; + } ``` === "C#" @@ -568,7 +577,10 @@ comments: true === "C" ```c title="my_heap.c" - [class]{maxHeap}-[func]{peek} + /* 访问堆顶元素 */ + int peek(maxHeap *h) { + return h->data[0]; + } ``` === "C#" @@ -786,9 +798,36 @@ comments: true === "C" ```c title="my_heap.c" - [class]{maxHeap}-[func]{push} + /* 元素入堆 */ + void push(maxHeap *h, int val) { + // 默认情况下,不应该添加这么多节点 + if (h->size == MAX_SIZE) { + printf("heap is full!"); + return; + } + // 添加节点 + h->data[h->size] = val; + h->size++; - [class]{maxHeap}-[func]{siftUp} + // 从底至顶堆化 + siftUp(h, h->size - 1); + } + + /* 从节点 i 开始,从底至顶堆化 */ + void siftUp(maxHeap *h, int i) { + while (true) { + // 获取节点 i 的父节点 + int p = parent(h, i); + // 当“越过根节点”或“节点无需修复”时,结束堆化 + if (p < 0 || h->data[i] <= h->data[p]) { + break; + } + // 交换两节点 + swap(h, i, p); + // 循环向上堆化 + i = p; + } + } ``` === "C#" @@ -1148,9 +1187,48 @@ comments: true === "C" ```c title="my_heap.c" - [class]{maxHeap}-[func]{pop} + /* 元素出堆 */ + int pop(maxHeap *h) { + // 判空处理 + if (isEmpty(h)) { + printf("heap is empty!"); + return INT_MAX; + } + // 交换根节点与最右叶节点(即交换首元素与尾元素) + swap(h, 0, size(h) - 1); + // 删除节点 + int val = h->data[h->size - 1]; + h->size--; + // 从顶至底堆化 + siftDown(h, 0); - [class]{maxHeap}-[func]{siftDown} + // 返回堆顶元素 + return val; + } + + /* 从节点 i 开始,从顶至底堆化 */ + void siftDown(maxHeap *h, int i) { + while (true) { + // 判断节点 i, l, r 中值最大的节点,记为 max + int l = left(h, i); + int r = right(h, i); + int max = i; + if (l < size(h) && h->data[l] > h->data[max]) { + max = l; + } + if (r < size(h) && h->data[r] > h->data[max]) { + max = r; + } + // 若节点 i 最大或索引 l, r 越界,则无需继续堆化,跳出 + if (max == i) { + break; + } + // 交换两节点 + swap(h, i, max); + // 循环向下堆化 + i = max; + } + } ``` === "C#" diff --git a/chapter_searching/replace_linear_by_hashing.md b/chapter_searching/replace_linear_by_hashing.md index 2665ebde2..aabaf7f1e 100755 --- a/chapter_searching/replace_linear_by_hashing.md +++ b/chapter_searching/replace_linear_by_hashing.md @@ -124,7 +124,21 @@ comments: true === "C" ```c title="leetcode_two_sum.c" - [class]{}-[func]{twoSumBruteForce} + /* 方法一:暴力枚举 */ + int *twoSumBruteForce(int *nums, int numsSize, int target, int *returnSize) { + for (int i = 0; i < numsSize; ++i) { + for (int j = i + 1; j < numsSize; ++j) { + if (nums[i] + nums[j] == target) { + int *res = malloc(sizeof(int) * 2); + res[0] = i, res[1] = j; + *returnSize = 2; + return res; + } + } + } + *returnSize = 0; + return NULL; + } ``` === "C#" @@ -308,7 +322,22 @@ comments: true === "C" ```c title="leetcode_two_sum.c" - [class]{}-[func]{twoSumHashTable} + /* 方法二:辅助哈希表 */ + int *twoSumHashTable(int *nums, int numsSize, int target, int *returnSize) { + hashTable *hashtable = NULL; + for (int i = 0; i < numsSize; i++) { + hashTable *t = find(hashtable, target - nums[i]); + if (t != NULL) { + int *res = malloc(sizeof(int) * 2); + res[0] = t->val, res[1] = i; + *returnSize = 2; + return res; + } + insert(hashtable, nums[i], i); + } + *returnSize = 0; + return NULL; + } ``` === "C#" diff --git a/chapter_sorting/bubble_sort.md b/chapter_sorting/bubble_sort.md index 0b440627a..1e963c989 100755 --- a/chapter_sorting/bubble_sort.md +++ b/chapter_sorting/bubble_sort.md @@ -158,7 +158,20 @@ comments: true === "C" ```c title="bubble_sort.c" - [class]{}-[func]{bubbleSort} + /* 冒泡排序 */ + void bubbleSort(int nums[], int size) { + // 外循环:待排序元素数量为 n-1, n-2, ..., 1 + for (int i = 0; i < size - 1; i++) { + // 内循环:冒泡操作 + for (int j = 0; j < size - 1 - i; j++) { + if (nums[j] > nums[j + 1]) { + int temp = nums[j]; + nums[j] = nums[j + 1]; + nums[j + 1] = temp; + } + } + } + } ``` === "C#" @@ -379,7 +392,24 @@ comments: true === "C" ```c title="bubble_sort.c" - [class]{}-[func]{bubbleSortWithFlag} + /* 冒泡排序(标志优化)*/ + void bubbleSortWithFlag(int nums[], int size) { + // 外循环:待排序元素数量为 n-1, n-2, ..., 1 + for (int i = 0; i < size - 1; i++) { + bool flag = false; + // 内循环:冒泡操作 + for (int j = 0; j < size - 1 - i; j++) { + if (nums[j] > nums[j + 1]) { + int temp = nums[j]; + nums[j] = nums[j + 1]; + nums[j + 1] = temp; + flag = true; + } + } + if (!flag) + break; + } + } ``` === "C#" diff --git a/chapter_sorting/counting_sort.md b/chapter_sorting/counting_sort.md index 110a45d7b..4145baee4 100644 --- a/chapter_sorting/counting_sort.md +++ b/chapter_sorting/counting_sort.md @@ -181,7 +181,30 @@ comments: true === "C" ```c title="counting_sort.c" - [class]{}-[func]{countingSortNaive} + /* 计数排序 */ + // 简单实现,无法用于排序对象 + void countingSortNaive(int nums[], int size) { + // 1. 统计数组最大元素 m + int m = 0; + for (int i = 0; i < size; i++) { + if (nums[i] > m) { + m = nums[i]; + } + } + // 2. 统计各数字的出现次数 + // counter[num] 代表 num 的出现次数 + int *counter = malloc(sizeof(int) * size); + for (int i = 0; i < size; i++) { + counter[nums[i]]++; + } + // 3. 遍历 counter ,将各元素填入原数组 nums + int i = 0; + for (int num = 0; num < m + 1; num++) { + for (int j = 0; j < counter[num]; j++, i++) { + nums[i] = num; + } + } + } ``` === "C#" @@ -517,7 +540,38 @@ $$ === "C" ```c title="counting_sort.c" - [class]{}-[func]{countingSort} + /* 计数排序 */ + // 完整实现,可排序对象,并且是稳定排序 + void countingSort(int nums[], int size) { + // 1. 统计数组最大元素 m + int m = 0; + for (int i = 0; i < size; i++) { + if (nums[i] > m) { + m = nums[i]; + } + } + // 2. 统计各数字的出现次数 + // counter[num] 代表 num 的出现次数 + int *counter = malloc(sizeof(int) * size); + for (int i = 0; i < size; i++) { + counter[nums[i]]++; + } + // 3. 求 counter 的前缀和,将“出现次数”转换为“尾索引” + // 即 counter[num]-1 是 num 在 res 中最后一次出现的索引 + for (int i = 0; i < m; i++) { + counter[i + 1] += counter[i]; + } + // 4. 倒序遍历 nums ,将各元素填入结果数组 res + // 初始化数组 res 用于记录结果 + int *res = malloc(sizeof(int) * size); + for (int i = size - 1; i >= 0; i--) { + int num = nums[i]; + res[counter[num] - 1] = num; // 将 num 放置到对应索引处 + counter[num]--; // 令前缀和自减 1 ,得到下次放置 num 的索引 + } + // 使用结果数组 res 覆盖原数组 nums + memcpy(nums, res, size * sizeof(int)); + } ``` === "C#" diff --git a/chapter_sorting/insertion_sort.md b/chapter_sorting/insertion_sort.md index 1f05c0479..2060c030e 100755 --- a/chapter_sorting/insertion_sort.md +++ b/chapter_sorting/insertion_sort.md @@ -136,7 +136,21 @@ comments: true === "C" ```c title="insertion_sort.c" - [class]{}-[func]{insertionSort} + /* 插入排序 */ + void insertionSort(int nums[], int size) { + // 外循环:base = nums[1], nums[2], ..., nums[n-1] + for (int i = 1; i < size; i++) { + int base = nums[i], j = i - 1; + // 内循环:将 base 插入到左边的正确位置 + while (j >= 0 && nums[j] > base) { + // 1. 将 nums[j] 向右移动一位 + nums[j + 1] = nums[j]; + j--; + } + // 2. 将 base 赋值到正确位置 + nums[j + 1] = base; + } + } ``` === "C#" diff --git a/chapter_sorting/merge_sort.md b/chapter_sorting/merge_sort.md index 97544e309..34d1488f6 100755 --- a/chapter_sorting/merge_sort.md +++ b/chapter_sorting/merge_sort.md @@ -335,7 +335,48 @@ comments: true === "C" ```c title="merge_sort.c" + /* 合并左子数组和右子数组 */ + // 左子数组区间 [left, mid] + // 右子数组区间 [mid + 1, right] + void merge(int *nums, int left, int mid, int right) { + int index; + // 初始化辅助数组 + int tmp[right + 1 - left]; + for (index = left; index < right + 1; index++) { + tmp[index - left] = nums[index]; + } + // 左子数组的起始索引和结束索引 + int leftStart = left - left, leftEnd = mid - left; + // 右子数组的起始索引和结束索引 + int rightStart = mid + 1 - left, rightEnd = right - left; + // i, j 分别指向左子数组、右子数组的首元素 + int i = leftStart, j = rightStart; + // 通过覆盖原数组 nums 来合并左子数组和右子数组 + for (int k = left; k <= right; k++) { + // 若“左子数组已全部合并完”,则选取右子数组元素,并且 j++ + if (i > leftEnd) + nums[k] = tmp[j++]; + // 否则,若“右子数组已全部合并完”或“左子数组元素 <= 右子数组元素”,则选取左子数组元素,并且 i++ + else if (j > rightEnd || tmp[i] <= tmp[j]) + nums[k] = tmp[i++]; + // 否则,若“左右子数组都未全部合并完”且“左子数组元素 > 右子数组元素”,则选取右子数组元素,并且 j++ + else + nums[k] = tmp[j++]; + } + } + /* 归并排序 */ + void mergeSort(int *nums, int left, int right) { + // 终止条件 + if (left >= right) + return; // 当子数组长度为 1 时终止递归 + // 划分阶段 + int mid = (left + right) / 2; // 计算中点 + mergeSort(nums, left, mid); // 递归左子数组 + mergeSort(nums, mid + 1, right); // 递归右子数组 + // 合并阶段 + merge(nums, left, mid, right); + } ``` === "C#" diff --git a/chapter_sorting/quick_sort.md b/chapter_sorting/quick_sort.md index 3fa254342..d0942830b 100755 --- a/chapter_sorting/quick_sort.md +++ b/chapter_sorting/quick_sort.md @@ -202,7 +202,28 @@ comments: true === "C" ```c title="quick_sort.c" - [class]{quickSort}-[func]{partition} + /* 快速排序类 */ + // 快速排序类-哨兵划分 + int partition(int nums[], int left, int right) { + // 以 nums[left] 作为基准数 + int i = left, j = right; + while (i < j) { + while (i < j && nums[j] >= nums[left]) { + // 从右向左找首个小于基准数的元素 + j--; + } + while (i < j && nums[i] <= nums[left]) { + // 从左向右找首个大于基准数的元素 + i++; + } + // 交换这两个元素 + swap(nums, i, j); + } + // 将基准数交换至两子数组的分界线 + swap(nums, i, left); + // 返回基准数的索引 + return i; + } ``` === "C#" @@ -397,7 +418,41 @@ comments: true === "C" ```c title="quick_sort.c" - [class]{quickSort}-[func]{quickSort} + /* 快速排序类 */ + // 快速排序类-哨兵划分 + int partition(int nums[], int left, int right) { + // 以 nums[left] 作为基准数 + int i = left, j = right; + while (i < j) { + while (i < j && nums[j] >= nums[left]) { + // 从右向左找首个小于基准数的元素 + j--; + } + while (i < j && nums[i] <= nums[left]) { + // 从左向右找首个大于基准数的元素 + i++; + } + // 交换这两个元素 + swap(nums, i, j); + } + // 将基准数交换至两子数组的分界线 + swap(nums, i, left); + // 返回基准数的索引 + return i; + } + + // 快速排序类-快速排序 + void quickSort(int nums[], int left, int right) { + // 子数组长度为 1 时终止递归 + if (left >= right) { + return; + } + // 哨兵划分 + int pivot = partition(nums, left, right); + // 递归左子数组、右子数组 + quickSort(nums, left, pivot - 1); + quickSort(nums, pivot + 1, right); + } ``` === "C#" @@ -710,9 +765,50 @@ comments: true === "C" ```c title="quick_sort.c" - [class]{quickSortMedian}-[func]{medianThree} + /* 快速排序类(中位基准数优化) */ + // 选取三个元素的中位数 + int medianThree(int nums[], int left, int mid, int right) { + // 此处使用异或运算来简化代码 + // 异或规则为 0 ^ 0 = 1 ^ 1 = 0, 0 ^ 1 = 1 ^ 0 = 1 + if ((nums[left] < nums[mid]) ^ (nums[left] < nums[right])) + return left; + else if ((nums[mid] < nums[left]) ^ (nums[mid] < nums[right])) + return mid; + else + return right; + } - [class]{quickSortMedian}-[func]{partition} + /* 快速排序类(中位基准数优化) */ + // 选取三个元素的中位数 + int medianThree(int nums[], int left, int mid, int right) { + // 此处使用异或运算来简化代码 + // 异或规则为 0 ^ 0 = 1 ^ 1 = 0, 0 ^ 1 = 1 ^ 0 = 1 + if ((nums[left] < nums[mid]) ^ (nums[left] < nums[right])) + return left; + else if ((nums[mid] < nums[left]) ^ (nums[mid] < nums[right])) + return mid; + else + return right; + } + + // 哨兵划分(三数取中值) + int partitionMedian(int nums[], int left, int right) { + // 选取三个候选元素的中位数 + int med = medianThree(nums, left, (left + right) / 2, right); + // 将中位数交换至数组最左端 + swap(nums, left, med); + // 以 nums[left] 作为基准数 + int i = left, j = right; + while (i < j) { + while (i < j && nums[j] >= nums[left]) + j--; // 从右向左找首个小于基准数的元素 + while (i < j && nums[i] <= nums[left]) + i++; // 从左向右找首个大于基准数的元素 + swap(nums, i, j); // 交换这两个元素 + } + swap(nums, i, left); // 将基准数交换至两子数组的分界线 + return i; // 返回基准数的索引 + } ``` === "C#" @@ -944,7 +1040,23 @@ comments: true === "C" ```c title="quick_sort.c" - [class]{quickSortTailCall}-[func]{quickSort} + /* 快速排序类(尾递归优化) */ + // 快速排序(尾递归优化) + void quickSortTailCall(int nums[], int left, int right) { + // 子数组长度为 1 时终止 + while (left < right) { + // 哨兵划分操作 + int pivot = partition(nums, left, right); + // 对两个子数组中较短的那个执行快排 + if (pivot - left < right - pivot) { + quickSortTailCall(nums, left, pivot - 1); // 递归排序左子数组 + left = pivot + 1; // 剩余待排序区间为 [pivot + 1, right] + } else { + quickSortTailCall(nums, pivot + 1, right); // 递归排序右子数组 + right = pivot - 1; // 剩余待排序区间为 [left, pivot - 1] + } + } + } ``` === "C#" diff --git a/chapter_sorting/radix_sort.md b/chapter_sorting/radix_sort.md index 06351cbf0..a0b4dcd81 100644 --- a/chapter_sorting/radix_sort.md +++ b/chapter_sorting/radix_sort.md @@ -353,11 +353,58 @@ $$ === "C" ```c title="radix_sort.c" - [class]{}-[func]{digit} + /* 获取元素 num 的第 k 位,其中 exp = 10^(k-1) */ + int digit(int num, int exp) { + // 传入 exp 而非 k 可以避免在此重复执行昂贵的次方计算 + return (num / exp) % 10; + } - [class]{}-[func]{countingSortDigit} + /* 计数排序(根据 nums 第 k 位排序) */ + void countingSortDigit(int nums[], int size, int exp) { + // 十进制的位范围为 0~9 ,因此需要长度为 10 的桶 + int *counter = (int *)malloc((sizeof(int) * 10)); + // 统计 0~9 各数字的出现次数 + for (int i = 0; i < size; i++) { + // 获取 nums[i] 第 k 位,记为 d + int d = digit(nums[i], exp); + // 统计数字 d 的出现次数 + counter[d]++; + } + // 求前缀和,将“出现个数”转换为“数组索引” + for (int i = 1; i < 10; i++) { + counter[i] += counter[i - 1]; + } + // 倒序遍历,根据桶内统计结果,将各元素填入 res + int *res = (int *)malloc(sizeof(int) * size); + for (int i = size - 1; i >= 0; i--) { + int d = digit(nums[i], exp); + int j = counter[d] - 1; // 获取 d 在数组中的索引 j + res[j] = nums[i]; // 将当前元素填入索引 j + counter[d]--; // 将 d 的数量减 1 + } + // 使用结果覆盖原数组 nums + for (int i = 0; i < size; i++) { + nums[i] = res[i]; + } + } - [class]{}-[func]{radixSort} + /* 基数排序 */ + void radixSort(int nums[], int size) { + // 获取数组的最大元素,用于判断最大位数 + int max = INT32_MIN; + for (size_t i = 0; i < size - 1; i++) { + if (nums[i] > max) { + max = nums[i]; + } + } + // 按照从低位到高位的顺序遍历 + for (int exp = 1; max >= exp; exp *= 10) + // 对数组元素的第 k 位执行计数排序 + // k = 1 -> exp = 1 + // k = 2 -> exp = 10 + // 即 exp = 10^(k-1) + countingSortDigit(nums, size, exp); + } ``` === "C#" diff --git a/chapter_stack_and_queue/deque.md b/chapter_stack_and_queue/deque.md index 3be5efe6a..ece236f7b 100644 --- a/chapter_stack_and_queue/deque.md +++ b/chapter_stack_and_queue/deque.md @@ -221,7 +221,7 @@ comments: true === "C" ```c title="deque.c" - + // C 未提供内置双向队列 ``` === "C#" @@ -1036,9 +1036,163 @@ comments: true === "C" ```c title="linkedlist_deque.c" - [class]{ListNode}-[func]{} + /* 双向链表节点 */ + struct doublyListNode { + int val; // 节点值 + struct doublyListNode *next; // 后继节点 + struct doublyListNode *prev; // 前驱节点 + }; - [class]{LinkedListDeque}-[func]{} + /* 构造函数 */ + doublyListNode *newDoublyListNode(int num) { + doublyListNode *new = (doublyListNode *)malloc(sizeof(doublyListNode)); + new->val = num; + new->next = NULL; + new->prev = NULL; + return new; + } + + /* 析构函数 */ + void delDoublyListNode(doublyListNode *node) { + free(node); + } + + /* 基于双向链表实现的双向队列 */ + struct linkedListDeque { + doublyListNode *front, *rear; // 头节点 front ,尾节点 rear + int queSize; // 双向队列的长度 + }; + + /* 构造j */ + linkedListDeque *newLinkedListDeque() { + linkedListDeque *deque = (linkedListDeque *)malloc(sizeof(linkedListDeque)); + deque->front = NULL; + deque->rear = NULL; + deque->queSize = 0; + return deque; + } + + /* 析构函数 */ + void delLinkedListdeque(linkedListDeque *deque) { + // 释放所有节点 + for (int i = 0; i < deque->queSize && deque->front != NULL; i++) { + doublyListNode *tmp = deque->front; + deque->front = deque->front->next; + free(tmp); + } + // 释放 deque 结构体 + free(deque); + } + + /* 获取队列的长度 */ + int size(linkedListDeque *deque) { + return deque->queSize; + } + + /* 判断队列是否为空 */ + bool empty(linkedListDeque *deque) { + return (size(deque) == 0); + } + + /* 入队 */ + void push(linkedListDeque *deque, int num, bool isFront) { + doublyListNode *node = newDoublyListNode(num); + // 若链表为空,则令 front, rear 都指向node + if (empty(deque)) { + deque->front = deque->rear = node; + } + // 队首入队操作 + else if (isFront) { + // 将 node 添加至链表头部 + deque->front->prev = node; + node->next = deque->front; + deque->front = node; // 更新头节点 + } + // 对尾入队操作 + else { + // 将 node 添加至链表尾部 + deque->rear->next = node; + node->prev = deque->rear; + deque->rear = node; + } + deque->queSize++; // 更新队列长度 + } + + /* 队首入队 */ + void pushFirst(linkedListDeque *deque, int num) { + push(deque, num, true); + } + + /* 队尾入队 */ + void pushLast(linkedListDeque *deque, int num) { + push(deque, num, false); + } + + /* 访问队首元素 */ + int peekFirst(linkedListDeque *deque) { + assert(size(deque) && deque->front); + return deque->front->val; + } + + /* 访问队尾元素 */ + int peekLast(linkedListDeque *deque) { + assert(size(deque) && deque->rear); + return deque->rear->val; + } + + /* 出队 */ + int pop(linkedListDeque *deque, bool isFront) { + if (empty(deque)) + return -1; + int val; + // 队首出队操作 + if (isFront) { + val = peekFirst(deque); // 暂存头节点值 + doublyListNode *fNext = deque->front->next; + if (fNext) { + fNext->prev = NULL; + deque->front->next = NULL; + delDoublyListNode(deque->front); + } + deque->front = fNext; // 更新头节点 + } + // 队尾出队操作 + else { + val = peekLast(deque); // 暂存尾节点值 + doublyListNode *rPrev = deque->rear->prev; + if (rPrev) { + rPrev->next = NULL; + deque->rear->prev = NULL; + delDoublyListNode(deque->rear); + } + deque->rear = rPrev; // 更新尾节点 + } + deque->queSize--; // 更新队列长度 + return val; + } + + /* 队首出队 */ + int popFirst(linkedListDeque *deque) { + return pop(deque, true); + } + + /* 队尾出队 */ + int popLast(linkedListDeque *deque) { + return pop(deque, false); + } + + /* 打印队列 */ + void printLinkedListDeque(linkedListDeque *deque) { + int arr[deque->queSize]; + // 拷贝链表中的数据到数组 + int i; + doublyListNode *node; + for (i = 0, node = deque->front; i < deque->queSize; i++) { + arr[i] = node->val; + node = node->next; + } + printArray(arr, deque->queSize); + } ``` === "C#" @@ -2004,7 +2158,124 @@ comments: true === "C" ```c title="array_deque.c" - [class]{ArrayDeque}-[func]{} + /* 基于环形数组实现的双向队列 */ + struct arrayDeque { + int *nums; // 用于存储队列元素的数组 + int front; // 队首指针,指向队首元素 + int queSize; // 尾指针,指向队尾 + 1 + int queCapacity; // 队列容量 + }; + + /* 构造函数 */ + arrayDeque *newArrayDeque(int capacity) { + arrayDeque *deque = (arrayDeque *)malloc(sizeof(arrayDeque)); + // 初始化数组 + deque->queCapacity = capacity; + deque->nums = (int *)malloc(sizeof(int) * deque->queCapacity); + deque->front = deque->queSize = 0; + return deque; + } + + /* 析构函数 */ + void delArrayDeque(arrayDeque *deque) { + free(deque->nums); + deque->queCapacity = 0; + } + + /* 获取双向队列的容量 */ + int capacity(arrayDeque *deque) { + return deque->queCapacity; + } + + /* 获取双向队列的长度 */ + int size(arrayDeque *deque) { + return deque->queSize; + } + + /* 判断双向队列是否为空 */ + bool empty(arrayDeque *deque) { + return deque->queSize == 0; + } + + /* 判断双向队列是否为空 */ + bool empty(arrayDeque *deque) { + return deque->queSize == 0; + } + + int dequeIndex(arrayDeque *deque, int i) { + // 通过取余操作实现数组首尾相连 + // 当 i 越过数组尾部时,回到头部 + // 当 i 越过数组头部后,回到尾部 + return ((i + capacity(deque)) % capacity(deque)); + } + + /* 队首入队 */ + void pushFirst(arrayDeque *deque, int num) { + if (deque->queSize == capacity(deque)) { + printf("双向队列已满\r\n"); + return; + } + // 队首指针向左移动一位 + // 通过取余操作,实现 front 越过数组头部回到尾部 + deque->front = dequeIndex(deque, deque->front - 1); + // 将 num 添加到队首 + deque->nums[deque->front] = num; + deque->queSize++; + } + + /* 队尾入队 */ + void pushLast(arrayDeque *deque, int num) { + if (deque->queSize == capacity(deque)) { + printf("双向队列已满\r\n"); + return; + } + // 计算尾指针,指向队尾索引 + 1 + int rear = dequeIndex(deque, deque->front + deque->queSize); + // 将 num 添加至队尾 + deque->nums[rear] = num; + deque->queSize++; + } + + /* 访问队首元素 */ + int peekFirst(arrayDeque *deque) { + // 访问异常:双向队列为空 + assert(empty(deque) == 0); + return deque->nums[deque->front]; + } + + /* 访问队尾元素 */ + int peekLast(arrayDeque *deque) { + // 访问异常:双向队列为空 + assert(empty(deque) == 0); + int last = dequeIndex(deque, deque->front + deque->queSize - 1); + return deque->nums[last]; + } + + /* 队首出队 */ + int popFirst(arrayDeque *deque) { + int num = peekFirst(deque); + // 队首指针向后移动一位 + deque->front = dequeIndex(deque, deque->front + 1); + deque->queSize--; + return num; + } + + /* 队尾出队 */ + int popLast(arrayDeque *deque) { + int num = peekLast(deque); + deque->queSize--; + return num; + } + + /* 打印队列 */ + void printArrayDeque(arrayDeque *deque) { + int arr[deque->queSize]; + // 拷贝 + for (int i = 0, j = deque->front; i < deque->queSize; i++, j++) { + arr[i] = deque->nums[j % deque->queCapacity]; + } + printArray(arr, deque->queSize); + } ``` === "C#" diff --git a/chapter_stack_and_queue/queue.md b/chapter_stack_and_queue/queue.md index 167ef8e3a..c6d47f664 100755 --- a/chapter_stack_and_queue/queue.md +++ b/chapter_stack_and_queue/queue.md @@ -195,7 +195,7 @@ comments: true === "C" ```c title="queue.c" - + // C 未提供内置队列 ``` === "C#" @@ -679,7 +679,87 @@ comments: true === "C" ```c title="linkedlist_queue.c" - [class]{linkedListQueue}-[func]{} + /* 基于链表实现的队列 */ + struct linkedListQueue { + ListNode *front, *rear; + int queSize; + }; + + /* 构造函数 */ + linkedListQueue *newLinkedListQueue() { + linkedListQueue *queue = (linkedListQueue *)malloc(sizeof(linkedListQueue)); + queue->front = NULL; + queue->rear = NULL; + queue->queSize = 0; + return queue; + } + + /* 析构函数 */ + void delLinkedListQueue(linkedListQueue *queue) { + // 释放所有节点 + for (int i = 0; i < queue->queSize && queue->front != NULL; i++) { + ListNode *tmp = queue->front; + queue->front = queue->front->next; + free(tmp); + } + // 释放 queue 结构体 + free(queue); + } + + /* 获取队列的长度 */ + int size(linkedListQueue *queue) { + return queue->queSize; + } + + /* 判断队列是否为空 */ + bool empty(linkedListQueue *queue) { + return (size(queue) == 0); + } + + /* 入队 */ + void push(linkedListQueue *queue, int num) { + // 尾节点处添加 node + ListNode *node = newListNode(num); + // 如果队列为空,则令头、尾节点都指向该节点 + if (queue->front == NULL) { + queue->front = node; + queue->rear = node; + } + // 如果队列不为空,则将该节点添加到尾节点后 + else { + queue->rear->next = node; + queue->rear = node; + } + queue->queSize++; + } + + /* 访问队首元素 */ + int peek(linkedListQueue *queue) { + assert(size(queue) && queue->front); + return queue->front->val; + } + + /* 出队 */ + void pop(linkedListQueue *queue) { + int num = peek(queue); + ListNode *tmp = queue->front; + queue->front = queue->front->next; + free(tmp); + queue->queSize--; + } + + /* 打印队列 */ + void printLinkedListQueue(linkedListQueue *queue) { + int arr[queue->queSize]; + // 拷贝链表中的数据到数组 + int i; + ListNode *node; + for (i = 0, node = queue->front; i < queue->queSize && queue->front != queue->rear; i++) { + arr[i] = node->val; + node = node->next; + } + printArray(arr, queue->queSize); + } ``` === "C#" @@ -1371,7 +1451,82 @@ comments: true === "C" ```c title="array_queue.c" - [class]{arrayQueue}-[func]{} + /* 基于环形数组实现的队列 */ + struct arrayQueue { + int *nums; // 用于存储队列元素的数组 + int front; // 队首指针,指向队首元素 + int queSize; // 尾指针,指向队尾 + 1 + int queCapacity; // 队列容量 + }; + + /* 构造函数 */ + arrayQueue *newArrayQueue(int capacity) { + arrayQueue *queue = (arrayQueue *)malloc(sizeof(arrayQueue)); + // 初始化数组 + queue->queCapacity = capacity; + queue->nums = (int *)malloc(sizeof(int) * queue->queCapacity); + queue->front = queue->queSize = 0; + return queue; + } + + /* 析构函数 */ + void delArrayQueue(arrayQueue *queue) { + free(queue->nums); + queue->queCapacity = 0; + } + + /* 获取队列的容量 */ + int capacity(arrayQueue *queue) { + return queue->queCapacity; + } + + /* 获取队列的长度 */ + int size(arrayQueue *queue) { + return queue->queSize; + } + + /* 判断队列是否为空 */ + bool empty(arrayQueue *queue) { + return queue->queSize == 0; + } + + /* 访问队首元素 */ + int peek(arrayQueue *queue) { + assert(size(queue) != 0); + return queue->nums[queue->front]; + } + + /* 入队 */ + void push(arrayQueue *queue, int num) { + if (size(queue) == capacity(queue)) { + printf("队列已满\r\n"); + return; + } + // 计算队尾指针,指向队尾索引 + 1 + // 通过取余操作,实现 rear 越过数组尾部后回到头部 + int rear = (queue->front + queue->queSize) % queue->queCapacity; + // 将 num 添加至队尾 + queue->nums[rear] = num; + queue->queSize++; + } + + /* 出队 */ + void pop(arrayQueue *queue) { + int num = peek(queue); + // 队首指针向后移动一位,若越过尾部则返回到数组头部 + queue->front = (queue->front + 1) % queue->queCapacity; + queue->queSize--; + } + + /* 打印队列 */ + void printArrayQueue(arrayQueue *queue) { + int arr[queue->queSize]; + // 拷贝 + for (int i = 0, j = queue->front; i < queue->queSize; i++, j++) { + arr[i] = queue->nums[j % queue->queCapacity]; + } + printArray(arr, queue->queSize); + } ``` === "C#" diff --git a/chapter_stack_and_queue/stack.md b/chapter_stack_and_queue/stack.md index cb966d897..f745e8984 100755 --- a/chapter_stack_and_queue/stack.md +++ b/chapter_stack_and_queue/stack.md @@ -194,7 +194,7 @@ comments: true === "C" ```c title="stack.c" - + // C 未提供内置栈 ``` === "C#" @@ -630,7 +630,74 @@ comments: true === "C" ```c title="linkedlist_stack.c" - [class]{linkedListStack}-[func]{} + /* 基于链表实现的栈 */ + struct linkedListStack { + ListNode *top; // 将头节点作为栈顶 + int size; // 栈的长度 + }; + + /* 构造函数 */ + linkedListStack *newLinkedListStack() { + linkedListStack *s = malloc(sizeof(linkedListStack)); + s->top = NULL; + s->size = 0; + return s; + } + + /* 析构函数 */ + void delLinkedListStack(linkedListStack *s) { + while (s->top) { + ListNode *n = s->top->next; + free(s->top); + s->top = n; + } + free(s); + } + + /* 获取栈的长度 */ + int size(linkedListStack *s) { + assert(s); + return s->size; + } + + /* 判断栈是否为空 */ + bool isEmpty(linkedListStack *s) { + assert(s); + return size(s) == 0; + } + + /* 访问栈顶元素 */ + int peek(linkedListStack *s) { + assert(s); + assert(size(s) != 0); + return s->top->val; + } + + /* 入栈 */ + void push(linkedListStack *s, int num) { + assert(s); + ListNode *node = (ListNode *)malloc(sizeof(ListNode)); + node->next = s->top; // 更新新加节点指针域 + node->val = num; // 更新新加节点数据域 + s->top = node; // 更新栈顶 + s->size++; // 更新栈大小 + } + + /* 出栈 */ + int pop(linkedListStack *s) { + if (s->size == 0) { + printf("stack is empty.\n"); + return INT_MAX; + } + assert(s); + int val = peek(s); + ListNode *tmp = s->top; + s->top = s->top->next; + // 释放内存 + free(tmp); + s->size--; + return val; + } ``` === "C#" @@ -1131,7 +1198,60 @@ comments: true === "C" ```c title="array_stack.c" - [class]{arrayStack}-[func]{} + /* 基于数组实现的栈 */ + struct arrayStack { + int *data; + int size; + }; + + /* 构造函数 */ + arrayStack *newArrayStack() { + arrayStack *s = malloc(sizeof(arrayStack)); + // 初始化一个大容量,避免扩容 + s->data = malloc(sizeof(int) * MAX_SIZE); + s->size = 0; + return s; + } + + /* 获取栈的长度 */ + int size(arrayStack *s) { + return s->size; + } + + /* 判断栈是否为空 */ + bool isEmpty(arrayStack *s) { + return s->size == 0; + } + + /* 入栈 */ + void push(arrayStack *s, int num) { + if (s->size == MAX_SIZE) { + printf("stack is full.\n"); + return; + } + s->data[s->size] = num; + s->size++; + } + + /* 访问栈顶元素 */ + int peek(arrayStack *s) { + if (s->size == 0) { + printf("stack is empty.\n"); + return INT_MAX; + } + return s->data[s->size - 1]; + } + + /* 出栈 */ + int pop(arrayStack *s) { + if (s->size == 0) { + printf("stack is empty.\n"); + return INT_MAX; + } + int val = peek(s); + s->size--; + return val; + } ``` === "C#" diff --git a/chapter_tree/array_representation_of_tree.md b/chapter_tree/array_representation_of_tree.md index 8da83b59b..718265805 100644 --- a/chapter_tree/array_representation_of_tree.md +++ b/chapter_tree/array_representation_of_tree.md @@ -42,9 +42,8 @@ comments: true ```cpp title="" /* 二叉树的数组表示 */ - // 为了符合数据类型为 int ,使用 int 最大值标记空位 - // 该方法的使用前提是没有节点的值 = INT_MAX - vector tree = { 1, 2, 3, 4, INT_MAX, 6, 7, 8, 9, INT_MAX, INT_MAX, 12, INT_MAX, INT_MAX, 15 }; + // 使用 int 最大值标记空位,因此要求节点值不能为 INT_MAX + vector tree = {1, 2, 3, 4, INT_MAX, 6, 7, 8, 9, INT_MAX, INT_MAX, 12, INT_MAX, INT_MAX, 15}; ``` === "Python" @@ -82,7 +81,9 @@ comments: true === "C" ```c title="" - + /* 二叉树的数组表示 */ + // 使用 int 最大值标记空位,因此要求节点值不能为 INT_MAX + int tree[] = {1, 2, 3, 4, INT_MAX, 6, 7, 8, 9, INT_MAX, INT_MAX, 12, INT_MAX, INT_MAX, 15}; ``` === "C#" diff --git a/chapter_tree/avl_tree.md b/chapter_tree/avl_tree.md index d33c410ec..d872a063b 100644 --- a/chapter_tree/avl_tree.md +++ b/chapter_tree/avl_tree.md @@ -70,7 +70,7 @@ G. M. Adelson-Velsky 和 E. M. Landis 在其 1962 年发表的论文 "An algorit === "Go" ```go title="" - /* AVL 树节点类 */ + /* AVL 树节点结构体 */ type TreeNode struct { Val int // 节点值 Height int // 节点高度 @@ -82,6 +82,7 @@ G. M. Adelson-Velsky 和 E. M. Landis 在其 1962 年发表的论文 "An algorit === "JavaScript" ```javascript title="" + /* AVL 树节点类 */ class TreeNode { val; // 节点值 height; //节点高度 @@ -99,6 +100,7 @@ G. M. Adelson-Velsky 和 E. M. Landis 在其 1962 年发表的论文 "An algorit === "TypeScript" ```typescript title="" + /* AVL 树节点类 */ class TreeNode { val: number; // 节点值 height: number; // 节点高度 @@ -116,7 +118,27 @@ G. M. Adelson-Velsky 和 E. M. Landis 在其 1962 年发表的论文 "An algorit === "C" ```c title="" + /* AVL 树节点结构体 */ + struct TreeNode { + int val; + int height; + struct TreeNode *left; + struct TreeNode *right; + }; + typedef struct TreeNode TreeNode; + + /* 构造函数 */ + TreeNode *newTreeNode(int val) { + TreeNode *node; + + node = (TreeNode *)malloc(sizeof(TreeNode)); + node->val = val; + node->height = 0; + node->left = NULL; + node->right = NULL; + return node; + } ``` === "C#" @@ -267,9 +289,26 @@ G. M. Adelson-Velsky 和 E. M. Landis 在其 1962 年发表的论文 "An algorit === "C" ```c title="avl_tree.c" - [class]{aVLTree}-[func]{height} + /* 获取节点高度 */ + int height(TreeNode *node) { + // 空节点高度为 -1 ,叶节点高度为 0 + if (node != NULL) { + return node->height; + } + return -1; + } - [class]{aVLTree}-[func]{updateHeight} + /* 更新节点高度 */ + void updateHeight(TreeNode *node) { + int lh = height(node->left); + int rh = height(node->right); + // 节点高度等于最高子树高度 + 1 + if (lh > rh) { + node->height = lh + 1; + } else { + node->height = rh + 1; + } + } ``` === "C#" @@ -406,7 +445,15 @@ G. M. Adelson-Velsky 和 E. M. Landis 在其 1962 年发表的论文 "An algorit === "C" ```c title="avl_tree.c" - [class]{aVLTree}-[func]{balanceFactor} + /* 获取平衡因子 */ + int balanceFactor(TreeNode *node) { + // 空节点平衡因子为 0 + if (node == NULL) { + return 0; + } + // 节点平衡因子 = 左子树高度 - 右子树高度 + return height(node->left) - height(node->right); + } ``` === "C#" @@ -590,7 +637,20 @@ AVL 树的特点在于「旋转 Rotation」操作,它能够在不影响二叉 === "C" ```c title="avl_tree.c" - [class]{aVLTree}-[func]{rightRotate} + /* 右旋操作 */ + TreeNode *rightRotate(TreeNode *node) { + TreeNode *child, *grandChild; + child = node->left; + grandChild = child->right; + // 以 child 为原点,将 node 向右旋转 + child->right = node; + node->left = grandChild; + // 更新节点高度 + updateHeight(node); + updateHeight(child); + // 返回旋转后子树的根节点 + return child; + } ``` === "C#" @@ -774,7 +834,20 @@ AVL 树的特点在于「旋转 Rotation」操作,它能够在不影响二叉 === "C" ```c title="avl_tree.c" - [class]{aVLTree}-[func]{leftRotate} + /* 左旋操作 */ + TreeNode *leftRotate(TreeNode *node) { + TreeNode *child, *grandChild; + child = node->right; + grandChild = child->left; + // 以 child 为原点,将 node 向左旋转 + child->left = node; + node->right = grandChild; + // 更新节点高度 + updateHeight(node); + updateHeight(child); + // 返回旋转后子树的根节点 + return child; + } ``` === "C#" @@ -1074,7 +1147,35 @@ AVL 树的特点在于「旋转 Rotation」操作,它能够在不影响二叉 === "C" ```c title="avl_tree.c" - [class]{aVLTree}-[func]{rotate} + /* 执行旋转操作,使该子树重新恢复平衡 */ + TreeNode *rotate(TreeNode *node) { + // 获取节点 node 的平衡因子 + int bf = balanceFactor(node); + // 左偏树 + if (bf > 1) { + if (balanceFactor(node->left) >= 0) { + // 右旋 + return rightRotate(node); + } else { + // 先左旋后右旋 + node->left = leftRotate(node->left); + return rightRotate(node); + } + } + // 右偏树 + if (bf < -1) { + if (balanceFactor(node->right) <= 0) { + // 左旋 + return leftRotate(node); + } else { + // 先右旋后左旋 + node->right = rightRotate(node->right); + return leftRotate(node); + } + } + // 平衡树,无需旋转,直接返回 + return node; + } ``` === "C#" @@ -1358,9 +1459,32 @@ AVL 树的特点在于「旋转 Rotation」操作,它能够在不影响二叉 === "C" ```c title="avl_tree.c" - [class]{aVLTree}-[func]{insert} + /* 插入节点 */ + void insert(aVLTree *tree, int val) { + tree->root = insertHelper(tree->root, val); + } - [class]{aVLTree}-[func]{insertHelper} + /* 递归插入节点(辅助方法) */ + TreeNode *insertHelper(TreeNode *node, int val) { + if (node == NULL) { + return newTreeNode(val); + } + /* 1. 查找插入位置,并插入节点 */ + if (val < node->val) { + node->left = insertHelper(node->left, val); + } else if (val > node->val) { + node->right = insertHelper(node->right, val); + } else { + // 重复节点不插入,直接返回 + return node; + } + // 更新节点高度 + updateHeight(node); + /* 2. 执行旋转操作,使该子树重新恢复平衡 */ + node = rotate(node); + // 返回子树的根节点 + return node; + } ``` === "C#" @@ -1726,9 +1850,54 @@ AVL 树的特点在于「旋转 Rotation」操作,它能够在不影响二叉 === "C" ```c title="avl_tree.c" - [class]{aVLTree}-[func]{remove} + /* 删除节点 */ + // 由于引入了 stdio.h ,此处无法使用 remove 关键词 + void removeNode(aVLTree *tree, int val) { + TreeNode *root = removeHelper(tree->root, val); + } - [class]{aVLTree}-[func]{removeHelper} + /* 递归删除节点(辅助方法) */ + TreeNode *removeHelper(TreeNode *node, int val) { + TreeNode *child, *grandChild; + if (node == NULL) { + return NULL; + } + /* 1. 查找节点,并删除之 */ + if (val < node->val) { + node->left = removeHelper(node->left, val); + } else if (val > node->val) { + node->right = removeHelper(node->right, val); + } else { + if (node->left == NULL || node->right == NULL) { + child = node->left; + if (node->right != NULL) { + child = node->right; + } + // 子节点数量 = 0 ,直接删除 node 并返回 + if (child == NULL) { + return NULL; + } else { + // 子节点数量 = 1 ,直接删除 node + node = child; + } + } else { + // 子节点数量 = 2 ,则将中序遍历的下个节点删除,并用该节点替换当前节点 + TreeNode *temp = node->right; + while (temp->left != NULL) { + temp = temp->left; + } + int tempVal = temp->val; + node->right = removeHelper(node->right, temp->val); + node->val = tempVal; + } + } + // 更新节点高度 + updateHeight(node); + /* 2. 执行旋转操作,使该子树重新恢复平衡 */ + node = rotate(node); + // 返回子树的根节点 + return node; + } ``` === "C#" diff --git a/chapter_tree/binary_search_tree.md b/chapter_tree/binary_search_tree.md index 6c0e72128..8c708d8fe 100755 --- a/chapter_tree/binary_search_tree.md +++ b/chapter_tree/binary_search_tree.md @@ -171,7 +171,25 @@ comments: true === "C" ```c title="binary_search_tree.c" - [class]{binarySearchTree}-[func]{search} + /* 查找节点 */ + TreeNode *search(binarySearchTree *bst, int num) { + TreeNode *cur = bst->root; + // 循环查找,越过叶节点后跳出 + while (cur != NULL) { + if (cur->val < num) { + // 目标节点在 cur 的右子树中 + cur = cur->right; + } else if (cur->val > num) { + // 目标节点在 cur 的左子树中 + cur = cur->left; + } else { + // 找到目标节点,跳出循环 + break; + } + } + // 返回目标节点 + return cur; + } ``` === "C#" @@ -448,7 +466,35 @@ comments: true === "C" ```c title="binary_search_tree.c" - [class]{binarySearchTree}-[func]{insert} + /* 插入节点 */ + void insert(binarySearchTree *bst, int num) { + // 若树为空,直接提前返回 + if (bst->root == NULL) + return; + TreeNode *cur = bst->root, *pre = NULL; + // 循环查找,越过叶节点后跳出 + while (cur != NULL) { + // 找到重复节点,直接返回 + if (cur->val == num) { + return; + } + pre = cur; + if (cur->val < num) { + // 插入位置在 cur 的右子树中 + cur = cur->right; + } else { + // 插入位置在 cur 的左子树中 + cur = cur->left; + } + } + // 插入节点 val + TreeNode *node = newTreeNode(num); + if (pre->val < num) { + pre->right = node; + } else { + pre->left = node; + } + } ``` === "C#" @@ -903,7 +949,55 @@ comments: true === "C" ```c title="binary_search_tree.c" - [class]{binarySearchTree}-[func]{remove} + /* 删除节点 */ + // 由于引入了 stdio.h ,此处无法使用 remove 关键词 + void removeNode(binarySearchTree *bst, int num) { + // 若树为空,直接提前返回 + if (bst->root == NULL) + return; + TreeNode *cur = bst->root, *pre = NULL; + // 循环查找,越过叶节点后跳出 + while (cur != NULL) { + // 找到待删除节点,跳出循环 + if (cur->val == num) + break; + pre = cur; + if (cur->val < num) { + // 待删除节点在 root 的右子树中 + cur = cur->right; + } else { + // 待删除节点在 root 的左子树中 + cur = cur->left; + } + } + // 若无待删除节点,则直接返回 + if (cur == NULL) + return; + // 判断待删除节点是否存在子节点 + if (cur->left == NULL || cur->right == NULL) { + /* 子节点数量 = 0 or 1 */ + // 当子节点数量 = 0 / 1 时, child = nullptr / 该子节点 + TreeNode *child = cur->left != NULL ? cur->left : cur->right; + // 删除节点 cur + if (pre->left == cur) { + pre->left = child; + } else { + pre->right = child; + } + } else { + /* 子节点数量 = 2 */ + // 获取中序遍历中 cur 的下一个节点 + TreeNode *tmp = cur->right; + while (tmp->left != NULL) { + tmp = tmp->left; + } + int tmpVal = tmp->val; + // 递归删除节点 tmp + removeNode(bst, tmp->val); + // 用 tmp 覆盖 cur + cur->val = tmpVal; + } + } ``` === "C#" diff --git a/chapter_tree/binary_tree.md b/chapter_tree/binary_tree.md index a9cf15431..05651475a 100644 --- a/chapter_tree/binary_tree.md +++ b/chapter_tree/binary_tree.md @@ -91,7 +91,27 @@ comments: true === "C" ```c title="" - + /* 二叉树节点结构体 */ + struct TreeNode { + int val; // 节点值 + int height; // 节点高度 + struct TreeNode *left; // 左子节点指针 + struct TreeNode *right; // 右子节点指针 + }; + + typedef struct TreeNode TreeNode; + + /* 构造函数 */ + TreeNode *newTreeNode(int val) { + TreeNode *node; + + node = (TreeNode *)malloc(sizeof(TreeNode)); + node->val = val; + node->height = 0; + node->left = NULL; + node->right = NULL; + return node; + } ``` === "C#" @@ -264,7 +284,18 @@ comments: true === "C" ```c title="binary_tree.c" - + /* 初始化二叉树 */ + // 初始化节点 + TreeNode *n1 = newTreeNode(1); + TreeNode *n2 = newTreeNode(2); + TreeNode *n3 = newTreeNode(3); + TreeNode *n4 = newTreeNode(4); + TreeNode *n5 = newTreeNode(5); + // 构建引用指向(即指针) + n1->left = n2; + n1->right = n3; + n2->left = n4; + n2->right = n5; ``` === "C#" @@ -386,7 +417,13 @@ comments: true === "C" ```c title="binary_tree.c" - + /* 插入与删除节点 */ + TreeNode *P = newTreeNode(0); + // 在 n1 -> n2 中间插入节点 P + n1->left = P; + P->left = n2; + // 删除节点 P + n1->left = n2; ``` === "C#" diff --git a/chapter_tree/binary_tree_traversal.md b/chapter_tree/binary_tree_traversal.md index c37a118ad..2a7c9f584 100755 --- a/chapter_tree/binary_tree_traversal.md +++ b/chapter_tree/binary_tree_traversal.md @@ -159,7 +159,44 @@ comments: true === "C" ```c title="binary_tree_bfs.c" - [class]{}-[func]{levelOrder} + /* 层序遍历 */ + int *levelOrder(TreeNode *root, int *size) { + /* 辅助队列 */ + int front, rear; + int index, *arr; + TreeNode *node; + TreeNode **queue; + + /* 辅助队列 */ + queue = (TreeNode **)malloc(sizeof(TreeNode) * MAX_NODE_SIZE); + // 队列指针 + front = 0, rear = 0; + // 加入根节点 + queue[rear++] = root; + // 初始化一个列表,用于保存遍历序列 + /* 辅助数组 */ + arr = (int *)malloc(sizeof(int) * MAX_NODE_SIZE); + // 数组指针 + index = 0; + while (front < rear) { + // 队列出队 + node = queue[front++]; + // 保存节点值 + arr[index++] = node->val; + if (node->left != NULL) { + // 左子节点入队 + queue[rear++] = node->left; + } + if (node->right != NULL) { + // 右子节点入队 + queue[rear++] = node->right; + } + } + // 更新数组长度的值 + *size = index; + arr = realloc(arr, sizeof(int) * (*size)); + return arr; + } ``` === "C#" @@ -476,11 +513,35 @@ comments: true === "C" ```c title="binary_tree_dfs.c" - [class]{}-[func]{preOrder} + /* 前序遍历 */ + void preOrder(TreeNode *root, int *size) { + if (root == NULL) + return; + // 访问优先级:根节点 -> 左子树 -> 右子树 + arr[(*size)++] = root->val; + preOrder(root->left, size); + preOrder(root->right, size); + } - [class]{}-[func]{inOrder} + /* 中序遍历 */ + void inOrder(TreeNode *root, int *size) { + if (root == NULL) + return; + // 访问优先级:左子树 -> 根节点 -> 右子树 + inOrder(root->left, size); + arr[(*size)++] = root->val; + inOrder(root->right, size); + } - [class]{}-[func]{postOrder} + /* 后序遍历 */ + void postOrder(TreeNode *root, int *size) { + if (root == NULL) + return; + // 访问优先级:左子树 -> 右子树 -> 根节点 + postOrder(root->left, size); + postOrder(root->right, size); + arr[(*size)++] = root->val; + } ``` === "C#"