diff --git a/codes/c/chapter_backtracking/n_queens.c b/codes/c/chapter_backtracking/n_queens.c index e1e3485cf..5dc51e4c3 100644 --- a/codes/c/chapter_backtracking/n_queens.c +++ b/codes/c/chapter_backtracking/n_queens.c @@ -23,10 +23,10 @@ void backtrack(int row, int n, char state[MAX_SIZE][MAX_SIZE], char ***res, int } // 遍历所有列 for (int col = 0; col < n; col++) { - // 计算该格子对应的主对角线和副对角线 + // 计算该格子对应的主对角线和次对角线 int diag1 = row - col + n - 1; int diag2 = row + col; - // 剪枝:不允许该格子所在列、主对角线、副对角线上存在皇后 + // 剪枝:不允许该格子所在列、主对角线、次对角线上存在皇后 if (!cols[col] && !diags1[diag1] && !diags2[diag2]) { // 尝试:将皇后放置在该格子 state[row][col] = 'Q'; @@ -52,7 +52,7 @@ char ***nQueens(int n, int *returnSize) { } bool cols[MAX_SIZE] = {false}; // 记录列是否有皇后 bool diags1[2 * MAX_SIZE - 1] = {false}; // 记录主对角线上是否有皇后 - bool diags2[2 * MAX_SIZE - 1] = {false}; // 记录副对角线上是否有皇后 + bool diags2[2 * MAX_SIZE - 1] = {false}; // 记录次对角线上是否有皇后 char ***res = (char ***)malloc(sizeof(char **) * MAX_SIZE); *returnSize = 0; diff --git a/codes/c/chapter_graph/graph_bfs.c b/codes/c/chapter_graph/graph_bfs.c index 6d13154de..719cd886b 100644 --- a/codes/c/chapter_graph/graph_bfs.c +++ b/codes/c/chapter_graph/graph_bfs.c @@ -52,7 +52,7 @@ int isVisited(Vertex **visited, int size, Vertex *vet) { return 0; } -/* 广度优先遍历 BFS */ +/* 广度优先遍历 */ // 使用邻接表来表示图,以便获取指定顶点的所有邻接顶点 void graphBFS(GraphAdjList *graph, Vertex *startVet, Vertex **res, int *resSize, Vertex **visited, int *visitedSize) { // 队列用于实现 BFS @@ -98,7 +98,7 @@ int main() { printf("\n初始化后,图为\n"); printGraph(graph); - // 广度优先遍历 BFS + // 广度优先遍历 // 顶点遍历序列 Vertex *res[MAX_SIZE]; int resSize = 0; diff --git a/codes/c/chapter_graph/graph_dfs.c b/codes/c/chapter_graph/graph_dfs.c index 2e53257d9..465f76b7a 100644 --- a/codes/c/chapter_graph/graph_dfs.c +++ b/codes/c/chapter_graph/graph_dfs.c @@ -20,7 +20,7 @@ int isVisited(Vertex **res, int size, Vertex *vet) { return 0; } -/* 深度优先遍历 DFS 辅助函数 */ +/* 深度优先遍历辅助函数 */ void dfs(GraphAdjList *graph, Vertex **res, int *resSize, Vertex *vet) { // 记录访问顶点 res[(*resSize)++] = vet; @@ -36,7 +36,7 @@ void dfs(GraphAdjList *graph, Vertex **res, int *resSize, Vertex *vet) { } } -/* 深度优先遍历 DFS */ +/* 深度优先遍历 */ // 使用邻接表来表示图,以便获取指定顶点的所有邻接顶点 void graphDFS(GraphAdjList *graph, Vertex *startVet, Vertex **res, int *resSize) { dfs(graph, res, resSize, startVet); @@ -61,7 +61,7 @@ int main() { printf("\n初始化后,图为\n"); printGraph(graph); - // 深度优先遍历 DFS + // 深度优先遍历 Vertex *res[MAX_SIZE]; int resSize = 0; graphDFS(graph, v[0], res, &resSize); diff --git a/codes/c/chapter_greedy/max_capacity.c b/codes/c/chapter_greedy/max_capacity.c index 6b13aea28..023819cff 100644 --- a/codes/c/chapter_greedy/max_capacity.c +++ b/codes/c/chapter_greedy/max_capacity.c @@ -17,7 +17,7 @@ int myMax(int a, int b) { /* 最大容量:贪心 */ int maxCapacity(int ht[], int htLength) { - // 初始化 i, j 分列数组两端 + // 初始化 i, j,使其分列数组两端 int i = 0; int j = htLength - 1; // 初始最大容量为 0 diff --git a/codes/c/chapter_hashing/array_hash_map.c b/codes/c/chapter_hashing/array_hash_map.c index 5263ee4ce..1150093c2 100644 --- a/codes/c/chapter_hashing/array_hash_map.c +++ b/codes/c/chapter_hashing/array_hash_map.c @@ -173,7 +173,7 @@ int main() { print(hmap); /* 查询操作 */ - // 向哈希表输入键 key ,得到值 value + // 向哈希表中输入键 key ,得到值 value const char *name = get(hmap, 15937); printf("\n输入学号 15937 ,查询到姓名 %s\n", name); diff --git a/codes/c/chapter_hashing/hash_map_chaining.c b/codes/c/chapter_hashing/hash_map_chaining.c index b68800b13..9d841f58f 100644 --- a/codes/c/chapter_hashing/hash_map_chaining.c +++ b/codes/c/chapter_hashing/hash_map_chaining.c @@ -74,7 +74,7 @@ double loadFactor(HashMapChaining *hashMap) { /* 查询操作 */ char *get(HashMapChaining *hashMap, int key) { int index = hashFunc(hashMap, key); - // 遍历桶,若找到 key 则返回对应 val + // 遍历桶,若找到 key ,则返回对应 val Node *cur = hashMap->buckets[index]; while (cur) { if (cur->pair->key == key) { @@ -82,7 +82,7 @@ char *get(HashMapChaining *hashMap, int key) { } cur = cur->next; } - return ""; // 若未找到 key 则返回空字符串 + return ""; // 若未找到 key ,则返回空字符串 } /* 添加操作 */ @@ -196,7 +196,7 @@ int main() { print(hashMap); /* 查询操作 */ - // 向哈希表输入键 key ,得到值 value + // 向哈希表中输入键 key ,得到值 value char *name = get(hashMap, 13276); printf("\n输入学号 13276 ,查询到姓名 %s\n", name); diff --git a/codes/c/chapter_hashing/hash_map_open_addressing.c b/codes/c/chapter_hashing/hash_map_open_addressing.c index ba12a10de..b0b29c30b 100644 --- a/codes/c/chapter_hashing/hash_map_open_addressing.c +++ b/codes/c/chapter_hashing/hash_map_open_addressing.c @@ -67,9 +67,9 @@ int findBucket(HashMapOpenAddressing *hashMap, int key) { int firstTombstone = -1; // 线性探测,当遇到空桶时跳出 while (hashMap->buckets[index] != NULL) { - // 若遇到 key ,返回对应桶索引 + // 若遇到 key ,返回对应的桶索引 if (hashMap->buckets[index]->key == key) { - // 若之前遇到了删除标记,则将键值对移动至该索引 + // 若之前遇到了删除标记,则将键值对移动至该索引处 if (firstTombstone != -1) { hashMap->buckets[firstTombstone] = hashMap->buckets[index]; hashMap->buckets[index] = hashMap->TOMBSTONE; @@ -81,7 +81,7 @@ int findBucket(HashMapOpenAddressing *hashMap, int key) { if (firstTombstone == -1 && hashMap->buckets[index] == hashMap->TOMBSTONE) { firstTombstone = index; } - // 计算桶索引,越过尾部返回头部 + // 计算桶索引,越过尾部则返回头部 index = (index + 1) % hashMap->capacity; } // 若 key 不存在,则返回添加点的索引 @@ -192,7 +192,7 @@ int main() { print(hashmap); // 查询操作 - // 向哈希表输入键 key ,得到值 val + // 向哈希表中输入键 key ,得到值 val char *name = get(hashmap, 13276); printf("\n输入学号 13276 ,查询到姓名 %s\n", name); diff --git a/codes/c/chapter_heap/my_heap.c b/codes/c/chapter_heap/my_heap.c index cd7762aac..a6f4e8342 100644 --- a/codes/c/chapter_heap/my_heap.c +++ b/codes/c/chapter_heap/my_heap.c @@ -40,17 +40,17 @@ void delMaxHeap(MaxHeap *maxHeap) { free(maxHeap); } -/* 获取左子节点索引 */ +/* 获取左子节点的索引 */ int left(MaxHeap *maxHeap, int i) { return 2 * i + 1; } -/* 获取右子节点索引 */ +/* 获取右子节点的索引 */ int right(MaxHeap *maxHeap, int i) { return 2 * i + 2; } -/* 获取父节点索引 */ +/* 获取父节点的索引 */ int parent(MaxHeap *maxHeap, int i) { return (i - 1) / 2; } diff --git a/codes/c/chapter_sorting/merge_sort.c b/codes/c/chapter_sorting/merge_sort.c index d6909907e..7ff28c804 100644 --- a/codes/c/chapter_sorting/merge_sort.c +++ b/codes/c/chapter_sorting/merge_sort.c @@ -8,13 +8,13 @@ /* 合并左子数组和右子数组 */ void merge(int *nums, int left, int mid, int right) { - // 左子数组区间 [left, mid], 右子数组区间 [mid+1, right] + // 左子数组区间为 [left, mid], 右子数组区间为 [mid+1, right] // 创建一个临时数组 tmp ,用于存放合并后的结果 int tmpSize = right - left + 1; int *tmp = (int *)malloc(tmpSize * sizeof(int)); // 初始化左子数组和右子数组的起始索引 int i = left, j = mid + 1, k = 0; - // 当左右子数组都还有元素时,比较并将较小的元素复制到临时数组中 + // 当左右子数组都还有元素时,进行比较并将较小的元素复制到临时数组中 while (i <= mid && j <= right) { if (nums[i] <= nums[j]) { tmp[k++] = nums[i++]; diff --git a/codes/c/chapter_sorting/quick_sort.c b/codes/c/chapter_sorting/quick_sort.c index 7fe861803..81202c7a0 100644 --- a/codes/c/chapter_sorting/quick_sort.c +++ b/codes/c/chapter_sorting/quick_sort.c @@ -50,7 +50,7 @@ void quickSort(int nums[], int left, int right) { } /* 快速排序类(中位基准数优化) */ -// 选取三个元素的中位数 +// 选取三个候选元素的中位数 int medianThree(int nums[], int left, int mid, int right) { // 此处使用异或运算来简化代码 // 异或规则为 0 ^ 0 = 1 ^ 1 = 0, 0 ^ 1 = 1 ^ 0 = 1 diff --git a/codes/c/chapter_stack_and_queue/array_deque.c b/codes/c/chapter_stack_and_queue/array_deque.c index 8c1a6a337..a17d1c755 100644 --- a/codes/c/chapter_stack_and_queue/array_deque.c +++ b/codes/c/chapter_stack_and_queue/array_deque.c @@ -60,7 +60,7 @@ void pushFirst(ArrayDeque *deque, int num) { return; } // 队首指针向左移动一位 - // 通过取余操作,实现 front 越过数组头部回到尾部 + // 通过取余操作实现 front 越过数组头部回到尾部 deque->front = dequeIndex(deque, deque->front - 1); // 将 num 添加到队首 deque->nums[deque->front] = num; @@ -73,7 +73,7 @@ void pushLast(ArrayDeque *deque, int num) { printf("双向队列已满\r\n"); return; } - // 计算尾指针,指向队尾索引 + 1 + // 计算队尾指针,指向队尾索引 + 1 int rear = dequeIndex(deque, deque->front + deque->queSize); // 将 num 添加至队尾 deque->nums[rear] = num; diff --git a/codes/c/chapter_stack_and_queue/array_queue.c b/codes/c/chapter_stack_and_queue/array_queue.c index 4a00dfdb6..84b3eae6d 100644 --- a/codes/c/chapter_stack_and_queue/array_queue.c +++ b/codes/c/chapter_stack_and_queue/array_queue.c @@ -58,7 +58,7 @@ void push(ArrayQueue *queue, int num) { return; } // 计算队尾指针,指向队尾索引 + 1 - // 通过取余操作,实现 rear 越过数组尾部后回到头部 + // 通过取余操作实现 rear 越过数组尾部后回到头部 int rear = (queue->front + queue->queSize) % queue->queCapacity; // 将 num 添加至队尾 queue->nums[rear] = num; @@ -68,7 +68,7 @@ void push(ArrayQueue *queue, int num) { /* 出队 */ int pop(ArrayQueue *queue) { int num = peek(queue); - // 队首指针向后移动一位,若越过尾部则返回到数组头部 + // 队首指针向后移动一位,若越过尾部,则返回到数组头部 queue->front = (queue->front + 1) % queue->queCapacity; queue->queSize--; return num; diff --git a/codes/c/chapter_tree/avl_tree.c b/codes/c/chapter_tree/avl_tree.c index 50c3877b0..5cbddbb7b 100644 --- a/codes/c/chapter_tree/avl_tree.c +++ b/codes/c/chapter_tree/avl_tree.c @@ -120,7 +120,7 @@ TreeNode *insertHelper(TreeNode *node, int val) { if (node == NULL) { return newTreeNode(val); } - /* 1. 查找插入位置,并插入节点 */ + /* 1. 查找插入位置并插入节点 */ if (val < node->val) { node->left = insertHelper(node->left, val); } else if (val > node->val) { @@ -148,7 +148,7 @@ TreeNode *removeHelper(TreeNode *node, int val) { if (node == NULL) { return NULL; } - /* 1. 查找节点,并删除之 */ + /* 1. 查找节点并删除 */ if (val < node->val) { node->left = removeHelper(node->left, val); } else if (val > node->val) { diff --git a/codes/cpp/chapter_array_and_linkedlist/my_list.cpp b/codes/cpp/chapter_array_and_linkedlist/my_list.cpp index 5fe40790a..25e820d39 100644 --- a/codes/cpp/chapter_array_and_linkedlist/my_list.cpp +++ b/codes/cpp/chapter_array_and_linkedlist/my_list.cpp @@ -37,7 +37,7 @@ class MyList { /* 访问元素 */ int get(int index) { - // 索引如果越界则抛出异常,下同 + // 索引如果越界,则抛出异常,下同 if (index < 0 || index >= size()) throw out_of_range("索引越界"); return arr[index]; @@ -87,7 +87,7 @@ class MyList { } // 更新元素数量 arrSize--; - // 返回被删除元素 + // 返回被删除的元素 return num; } diff --git a/codes/cpp/chapter_backtracking/n_queens.cpp b/codes/cpp/chapter_backtracking/n_queens.cpp index 8d4d7c87d..bced2cf7d 100644 --- a/codes/cpp/chapter_backtracking/n_queens.cpp +++ b/codes/cpp/chapter_backtracking/n_queens.cpp @@ -16,10 +16,10 @@ void backtrack(int row, int n, vector> &state, vector>> nQueens(int n) { vector> state(n, vector(n, "#")); vector cols(n, false); // 记录列是否有皇后 vector diags1(2 * n - 1, false); // 记录主对角线上是否有皇后 - vector diags2(2 * n - 1, false); // 记录副对角线上是否有皇后 + vector diags2(2 * n - 1, false); // 记录次对角线上是否有皇后 vector>> res; backtrack(0, n, state, res, cols, diags1, diags2); diff --git a/codes/cpp/chapter_graph/graph_bfs.cpp b/codes/cpp/chapter_graph/graph_bfs.cpp index 717579efd..12ea651e2 100644 --- a/codes/cpp/chapter_graph/graph_bfs.cpp +++ b/codes/cpp/chapter_graph/graph_bfs.cpp @@ -7,7 +7,7 @@ #include "../utils/common.hpp" #include "./graph_adjacency_list.cpp" -/* 广度优先遍历 BFS */ +/* 广度优先遍历 */ // 使用邻接表来表示图,以便获取指定顶点的所有邻接顶点 vector graphBFS(GraphAdjList &graph, Vertex *startVet) { // 顶点遍历序列 @@ -45,7 +45,7 @@ int main() { cout << "\n初始化后,图为\\n"; graph.print(); - /* 广度优先遍历 BFS */ + /* 广度优先遍历 */ vector res = graphBFS(graph, v[0]); cout << "\n广度优先遍历(BFS)顶点序列为" << endl; printVector(vetsToVals(res)); diff --git a/codes/cpp/chapter_graph/graph_dfs.cpp b/codes/cpp/chapter_graph/graph_dfs.cpp index 533897f54..4cb67d446 100644 --- a/codes/cpp/chapter_graph/graph_dfs.cpp +++ b/codes/cpp/chapter_graph/graph_dfs.cpp @@ -7,7 +7,7 @@ #include "../utils/common.hpp" #include "./graph_adjacency_list.cpp" -/* 深度优先遍历 DFS 辅助函数 */ +/* 深度优先遍历辅助函数 */ void dfs(GraphAdjList &graph, unordered_set &visited, vector &res, Vertex *vet) { res.push_back(vet); // 记录访问顶点 visited.emplace(vet); // 标记该顶点已被访问 @@ -20,7 +20,7 @@ void dfs(GraphAdjList &graph, unordered_set &visited, vector } } -/* 深度优先遍历 DFS */ +/* 深度优先遍历 */ // 使用邻接表来表示图,以便获取指定顶点的所有邻接顶点 vector graphDFS(GraphAdjList &graph, Vertex *startVet) { // 顶点遍历序列 @@ -41,7 +41,7 @@ int main() { cout << "\n初始化后,图为" << endl; graph.print(); - /* 深度优先遍历 DFS */ + /* 深度优先遍历 */ vector res = graphDFS(graph, v[0]); cout << "\n深度优先遍历(DFS)顶点序列为" << endl; printVector(vetsToVals(res)); diff --git a/codes/cpp/chapter_greedy/max_capacity.cpp b/codes/cpp/chapter_greedy/max_capacity.cpp index 99926c42a..008ea5366 100644 --- a/codes/cpp/chapter_greedy/max_capacity.cpp +++ b/codes/cpp/chapter_greedy/max_capacity.cpp @@ -8,7 +8,7 @@ /* 最大容量:贪心 */ int maxCapacity(vector &ht) { - // 初始化 i, j 分列数组两端 + // 初始化 i, j,使其分列数组两端 int i = 0, j = ht.size() - 1; // 初始最大容量为 0 int res = 0; diff --git a/codes/cpp/chapter_hashing/array_hash_map_test.cpp b/codes/cpp/chapter_hashing/array_hash_map_test.cpp index 87afdca99..4b3ccc8ec 100644 --- a/codes/cpp/chapter_hashing/array_hash_map_test.cpp +++ b/codes/cpp/chapter_hashing/array_hash_map_test.cpp @@ -22,7 +22,7 @@ int main() { map.print(); /* 查询操作 */ - // 向哈希表输入键 key ,得到值 value + // 向哈希表中输入键 key ,得到值 value string name = map.get(15937); cout << "\n输入学号 15937 ,查询到姓名 " << name << endl; diff --git a/codes/cpp/chapter_hashing/hash_map.cpp b/codes/cpp/chapter_hashing/hash_map.cpp index 22d64e991..887503228 100644 --- a/codes/cpp/chapter_hashing/hash_map.cpp +++ b/codes/cpp/chapter_hashing/hash_map.cpp @@ -22,7 +22,7 @@ int main() { printHashMap(map); /* 查询操作 */ - // 向哈希表输入键 key ,得到值 value + // 向哈希表中输入键 key ,得到值 value string name = map[15937]; cout << "\n输入学号 15937 ,查询到姓名 " << name << endl; diff --git a/codes/cpp/chapter_hashing/hash_map_chaining.cpp b/codes/cpp/chapter_hashing/hash_map_chaining.cpp index 1079be379..389323936 100644 --- a/codes/cpp/chapter_hashing/hash_map_chaining.cpp +++ b/codes/cpp/chapter_hashing/hash_map_chaining.cpp @@ -44,13 +44,13 @@ class HashMapChaining { /* 查询操作 */ string get(int key) { int index = hashFunc(key); - // 遍历桶,若找到 key 则返回对应 val + // 遍历桶,若找到 key ,则返回对应 val for (Pair *pair : buckets[index]) { if (pair->key == key) { return pair->val; } } - // 若未找到 key 则返回空字符串 + // 若未找到 key ,则返回空字符串 return ""; } @@ -136,7 +136,7 @@ int main() { map.print(); /* 查询操作 */ - // 向哈希表输入键 key ,得到值 value + // 向哈希表中输入键 key ,得到值 value string name = map.get(13276); cout << "\n输入学号 13276 ,查询到姓名 " << name << endl; diff --git a/codes/cpp/chapter_hashing/hash_map_open_addressing.cpp b/codes/cpp/chapter_hashing/hash_map_open_addressing.cpp index 4c411fb48..c7d13f627 100644 --- a/codes/cpp/chapter_hashing/hash_map_open_addressing.cpp +++ b/codes/cpp/chapter_hashing/hash_map_open_addressing.cpp @@ -47,9 +47,9 @@ class HashMapOpenAddressing { int firstTombstone = -1; // 线性探测,当遇到空桶时跳出 while (buckets[index] != nullptr) { - // 若遇到 key ,返回对应桶索引 + // 若遇到 key ,返回对应的桶索引 if (buckets[index]->key == key) { - // 若之前遇到了删除标记,则将键值对移动至该索引 + // 若之前遇到了删除标记,则将键值对移动至该索引处 if (firstTombstone != -1) { buckets[firstTombstone] = buckets[index]; buckets[index] = TOMBSTONE; @@ -61,7 +61,7 @@ class HashMapOpenAddressing { if (firstTombstone == -1 && buckets[index] == TOMBSTONE) { firstTombstone = index; } - // 计算桶索引,越过尾部返回头部 + // 计算桶索引,越过尾部则返回头部 index = (index + 1) % capacity; } // 若 key 不存在,则返回添加点的索引 @@ -157,7 +157,7 @@ int main() { hashmap.print(); // 查询操作 - // 向哈希表输入键 key ,得到值 val + // 向哈希表中输入键 key ,得到值 val string name = hashmap.get(13276); cout << "\n输入学号 13276 ,查询到姓名 " << name << endl; diff --git a/codes/cpp/chapter_heap/my_heap.cpp b/codes/cpp/chapter_heap/my_heap.cpp index b3b75fec4..5e6299ee8 100644 --- a/codes/cpp/chapter_heap/my_heap.cpp +++ b/codes/cpp/chapter_heap/my_heap.cpp @@ -12,17 +12,17 @@ class MaxHeap { // 使用动态数组,这样无须考虑扩容问题 vector maxHeap; - /* 获取左子节点索引 */ + /* 获取左子节点的索引 */ int left(int i) { return 2 * i + 1; } - /* 获取右子节点索引 */ + /* 获取右子节点的索引 */ int right(int i) { return 2 * i + 2; } - /* 获取父节点索引 */ + /* 获取父节点的索引 */ int parent(int i) { return (i - 1) / 2; // 向下整除 } diff --git a/codes/cpp/chapter_sorting/bubble_sort.cpp b/codes/cpp/chapter_sorting/bubble_sort.cpp index 23acfd1d1..3a4ad4a88 100644 --- a/codes/cpp/chapter_sorting/bubble_sort.cpp +++ b/codes/cpp/chapter_sorting/bubble_sort.cpp @@ -36,7 +36,7 @@ void bubbleSortWithFlag(vector &nums) { } } if (!flag) - break; // 此轮冒泡未交换任何元素,直接跳出 + break; // 此轮“冒泡”未交换任何元素,直接跳出 } } diff --git a/codes/cpp/chapter_sorting/merge_sort.cpp b/codes/cpp/chapter_sorting/merge_sort.cpp index 5fd984cd5..78e553443 100644 --- a/codes/cpp/chapter_sorting/merge_sort.cpp +++ b/codes/cpp/chapter_sorting/merge_sort.cpp @@ -8,12 +8,12 @@ /* 合并左子数组和右子数组 */ void merge(vector &nums, int left, int mid, int right) { - // 左子数组区间 [left, mid], 右子数组区间 [mid+1, right] + // 左子数组区间为 [left, mid], 右子数组区间为 [mid+1, right] // 创建一个临时数组 tmp ,用于存放合并后的结果 vector tmp(right - left + 1); // 初始化左子数组和右子数组的起始索引 int i = left, j = mid + 1, k = 0; - // 当左右子数组都还有元素时,比较并将较小的元素复制到临时数组中 + // 当左右子数组都还有元素时,进行比较并将较小的元素复制到临时数组中 while (i <= mid && j <= right) { if (nums[i] <= nums[j]) tmp[k++] = nums[i++]; diff --git a/codes/cpp/chapter_sorting/quick_sort.cpp b/codes/cpp/chapter_sorting/quick_sort.cpp index b6d1dc231..7ff8e086b 100644 --- a/codes/cpp/chapter_sorting/quick_sort.cpp +++ b/codes/cpp/chapter_sorting/quick_sort.cpp @@ -55,7 +55,7 @@ class QuickSortMedian { nums[j] = tmp; } - /* 选取三个元素的中位数 */ + /* 选取三个候选元素的中位数 */ static int medianThree(vector &nums, int left, int mid, int right) { // 此处使用异或运算来简化代码 // 异或规则为 0 ^ 0 = 1 ^ 1 = 0, 0 ^ 1 = 1 ^ 0 = 1 diff --git a/codes/cpp/chapter_stack_and_queue/array_deque.cpp b/codes/cpp/chapter_stack_and_queue/array_deque.cpp index 248e13a37..51cae696e 100644 --- a/codes/cpp/chapter_stack_and_queue/array_deque.cpp +++ b/codes/cpp/chapter_stack_and_queue/array_deque.cpp @@ -50,7 +50,7 @@ class ArrayDeque { return; } // 队首指针向左移动一位 - // 通过取余操作,实现 front 越过数组头部后回到尾部 + // 通过取余操作实现 front 越过数组头部后回到尾部 front = index(front - 1); // 将 num 添加至队首 nums[front] = num; @@ -63,7 +63,7 @@ class ArrayDeque { cout << "双向队列已满" << endl; return; } - // 计算尾指针,指向队尾索引 + 1 + // 计算队尾指针,指向队尾索引 + 1 int rear = index(front + queSize); // 将 num 添加至队尾 nums[rear] = num; diff --git a/codes/cpp/chapter_stack_and_queue/array_queue.cpp b/codes/cpp/chapter_stack_and_queue/array_queue.cpp index f12e64e5f..a072a3eef 100644 --- a/codes/cpp/chapter_stack_and_queue/array_queue.cpp +++ b/codes/cpp/chapter_stack_and_queue/array_queue.cpp @@ -48,7 +48,7 @@ class ArrayQueue { return; } // 计算队尾指针,指向队尾索引 + 1 - // 通过取余操作,实现 rear 越过数组尾部后回到头部 + // 通过取余操作实现 rear 越过数组尾部后回到头部 int rear = (front + queSize) % queCapacity; // 将 num 添加至队尾 nums[rear] = num; @@ -58,7 +58,7 @@ class ArrayQueue { /* 出队 */ int pop() { int num = peek(); - // 队首指针向后移动一位,若越过尾部则返回到数组头部 + // 队首指针向后移动一位,若越过尾部,则返回到数组头部 front = (front + 1) % queCapacity; queSize--; return num; diff --git a/codes/cpp/chapter_stack_and_queue/linkedlist_queue.cpp b/codes/cpp/chapter_stack_and_queue/linkedlist_queue.cpp index 0d2bd58e0..26d9a0b13 100644 --- a/codes/cpp/chapter_stack_and_queue/linkedlist_queue.cpp +++ b/codes/cpp/chapter_stack_and_queue/linkedlist_queue.cpp @@ -36,7 +36,7 @@ class LinkedListQueue { /* 入队 */ void push(int num) { - // 尾节点后添加 num + // 在尾节点后添加 num ListNode *node = new ListNode(num); // 如果队列为空,则令头、尾节点都指向该节点 if (front == nullptr) { diff --git a/codes/cpp/chapter_tree/avl_tree.cpp b/codes/cpp/chapter_tree/avl_tree.cpp index 04c08b66c..be5b40db0 100644 --- a/codes/cpp/chapter_tree/avl_tree.cpp +++ b/codes/cpp/chapter_tree/avl_tree.cpp @@ -79,7 +79,7 @@ class AVLTree { TreeNode *insertHelper(TreeNode *node, int val) { if (node == nullptr) return new TreeNode(val); - /* 1. 查找插入位置,并插入节点 */ + /* 1. 查找插入位置并插入节点 */ if (val < node->val) node->left = insertHelper(node->left, val); else if (val > node->val) @@ -97,7 +97,7 @@ class AVLTree { TreeNode *removeHelper(TreeNode *node, int val) { if (node == nullptr) return nullptr; - /* 1. 查找节点,并删除之 */ + /* 1. 查找节点并删除 */ if (val < node->val) node->left = removeHelper(node->left, val); else if (val > node->val) diff --git a/codes/csharp/chapter_array_and_linkedlist/my_list.cs b/codes/csharp/chapter_array_and_linkedlist/my_list.cs index 40df4f46e..61bb1147b 100644 --- a/codes/csharp/chapter_array_and_linkedlist/my_list.cs +++ b/codes/csharp/chapter_array_and_linkedlist/my_list.cs @@ -30,7 +30,7 @@ class MyList { /* 访问元素 */ public int Get(int index) { - // 索引如果越界则抛出异常,下同 + // 索引如果越界,则抛出异常,下同 if (index < 0 || index >= arrSize) throw new IndexOutOfRangeException("索引越界"); return arr[index]; @@ -80,13 +80,13 @@ class MyList { } // 更新元素数量 arrSize--; - // 返回被删除元素 + // 返回被删除的元素 return num; } /* 列表扩容 */ public void ExtendCapacity() { - // 新建一个长度为 arrCapacity * extendRatio 的数组,并将原数组拷贝到新数组 + // 新建一个长度为 arrCapacity * extendRatio 的数组,并将原数组复制到新数组 Array.Resize(ref arr, arrCapacity * extendRatio); // 更新列表容量 arrCapacity = arr.Length; diff --git a/codes/csharp/chapter_backtracking/n_queens.cs b/codes/csharp/chapter_backtracking/n_queens.cs index 80f15bd5f..fbd9a609a 100644 --- a/codes/csharp/chapter_backtracking/n_queens.cs +++ b/codes/csharp/chapter_backtracking/n_queens.cs @@ -21,10 +21,10 @@ public class n_queens { } // 遍历所有列 for (int col = 0; col < n; col++) { - // 计算该格子对应的主对角线和副对角线 + // 计算该格子对应的主对角线和次对角线 int diag1 = row - col + n - 1; int diag2 = row + col; - // 剪枝:不允许该格子所在列、主对角线、副对角线上存在皇后 + // 剪枝:不允许该格子所在列、主对角线、次对角线上存在皇后 if (!cols[col] && !diags1[diag1] && !diags2[diag2]) { // 尝试:将皇后放置在该格子 state[row][col] = "Q"; @@ -51,7 +51,7 @@ public class n_queens { } bool[] cols = new bool[n]; // 记录列是否有皇后 bool[] diags1 = new bool[2 * n - 1]; // 记录主对角线上是否有皇后 - bool[] diags2 = new bool[2 * n - 1]; // 记录副对角线上是否有皇后 + bool[] diags2 = new bool[2 * n - 1]; // 记录次对角线上是否有皇后 List>> res = []; Backtrack(0, n, state, res, cols, diags1, diags2); diff --git a/codes/csharp/chapter_graph/graph_bfs.cs b/codes/csharp/chapter_graph/graph_bfs.cs index 2f4166ef3..f5aca99d8 100644 --- a/codes/csharp/chapter_graph/graph_bfs.cs +++ b/codes/csharp/chapter_graph/graph_bfs.cs @@ -7,7 +7,7 @@ namespace hello_algo.chapter_graph; public class graph_bfs { - /* 广度优先遍历 BFS */ + /* 广度优先遍历 */ // 使用邻接表来表示图,以便获取指定顶点的所有邻接顶点 List GraphBFS(GraphAdjList graph, Vertex startVet) { // 顶点遍历序列 @@ -50,7 +50,7 @@ public class graph_bfs { Console.WriteLine("\n初始化后,图为"); graph.Print(); - /* 广度优先遍历 BFS */ + /* 广度优先遍历 */ List res = GraphBFS(graph, v[0]); Console.WriteLine("\n广度优先遍历(BFS)顶点序列为"); Console.WriteLine(string.Join(" ", Vertex.VetsToVals(res))); diff --git a/codes/csharp/chapter_graph/graph_dfs.cs b/codes/csharp/chapter_graph/graph_dfs.cs index 336f3a724..ccf6229a0 100644 --- a/codes/csharp/chapter_graph/graph_dfs.cs +++ b/codes/csharp/chapter_graph/graph_dfs.cs @@ -7,7 +7,7 @@ namespace hello_algo.chapter_graph; public class graph_dfs { - /* 深度优先遍历 DFS 辅助函数 */ + /* 深度优先遍历辅助函数 */ void DFS(GraphAdjList graph, HashSet visited, List res, Vertex vet) { res.Add(vet); // 记录访问顶点 visited.Add(vet); // 标记该顶点已被访问 @@ -21,7 +21,7 @@ public class graph_dfs { } } - /* 深度优先遍历 DFS */ + /* 深度优先遍历 */ // 使用邻接表来表示图,以便获取指定顶点的所有邻接顶点 List GraphDFS(GraphAdjList graph, Vertex startVet) { // 顶点遍历序列 @@ -46,7 +46,7 @@ public class graph_dfs { Console.WriteLine("\n初始化后,图为"); graph.Print(); - /* 深度优先遍历 DFS */ + /* 深度优先遍历 */ List res = GraphDFS(graph, v[0]); Console.WriteLine("\n深度优先遍历(DFS)顶点序列为"); Console.WriteLine(string.Join(" ", Vertex.VetsToVals(res))); diff --git a/codes/csharp/chapter_greedy/max_capacity.cs b/codes/csharp/chapter_greedy/max_capacity.cs index 004841d73..10d4e1c30 100644 --- a/codes/csharp/chapter_greedy/max_capacity.cs +++ b/codes/csharp/chapter_greedy/max_capacity.cs @@ -9,7 +9,7 @@ namespace hello_algo.chapter_greedy; public class max_capacity { /* 最大容量:贪心 */ int MaxCapacity(int[] ht) { - // 初始化 i, j 分列数组两端 + // 初始化 i, j,使其分列数组两端 int i = 0, j = ht.Length - 1; // 初始最大容量为 0 int res = 0; diff --git a/codes/csharp/chapter_hashing/array_hash_map.cs b/codes/csharp/chapter_hashing/array_hash_map.cs index eff06175e..7b7623728 100644 --- a/codes/csharp/chapter_hashing/array_hash_map.cs +++ b/codes/csharp/chapter_hashing/array_hash_map.cs @@ -107,7 +107,7 @@ public class array_hash_map { map.Print(); /* 查询操作 */ - // 向哈希表输入键 key ,得到值 value + // 向哈希表中输入键 key ,得到值 value string? name = map.Get(15937); Console.WriteLine("\n输入学号 15937 ,查询到姓名 " + name); diff --git a/codes/csharp/chapter_hashing/hash_map.cs b/codes/csharp/chapter_hashing/hash_map.cs index c269fd82c..93701d087 100644 --- a/codes/csharp/chapter_hashing/hash_map.cs +++ b/codes/csharp/chapter_hashing/hash_map.cs @@ -24,7 +24,7 @@ public class hash_map { PrintUtil.PrintHashMap(map); /* 查询操作 */ - // 向哈希表输入键 key ,得到值 value + // 向哈希表中输入键 key ,得到值 value string name = map[15937]; Console.WriteLine("\n输入学号 15937 ,查询到姓名 " + name); diff --git a/codes/csharp/chapter_hashing/hash_map_chaining.cs b/codes/csharp/chapter_hashing/hash_map_chaining.cs index 3a17e8010..3a7e25232 100644 --- a/codes/csharp/chapter_hashing/hash_map_chaining.cs +++ b/codes/csharp/chapter_hashing/hash_map_chaining.cs @@ -39,13 +39,13 @@ class HashMapChaining { /* 查询操作 */ public string? Get(int key) { int index = HashFunc(key); - // 遍历桶,若找到 key 则返回对应 val + // 遍历桶,若找到 key ,则返回对应 val foreach (Pair pair in buckets[index]) { if (pair.key == key) { return pair.val; } } - // 若未找到 key 则返回 null + // 若未找到 key ,则返回 null return null; } @@ -131,7 +131,7 @@ public class hash_map_chaining { map.Print(); /* 查询操作 */ - // 向哈希表输入键 key ,得到值 value + // 向哈希表中输入键 key ,得到值 value string? name = map.Get(13276); Console.WriteLine("\n输入学号 13276 ,查询到姓名 " + name); diff --git a/codes/csharp/chapter_hashing/hash_map_open_addressing.cs b/codes/csharp/chapter_hashing/hash_map_open_addressing.cs index 719a9b19a..ad6f2ee49 100644 --- a/codes/csharp/chapter_hashing/hash_map_open_addressing.cs +++ b/codes/csharp/chapter_hashing/hash_map_open_addressing.cs @@ -37,9 +37,9 @@ class HashMapOpenAddressing { int firstTombstone = -1; // 线性探测,当遇到空桶时跳出 while (buckets[index] != null) { - // 若遇到 key ,返回对应桶索引 + // 若遇到 key ,返回对应的桶索引 if (buckets[index].key == key) { - // 若之前遇到了删除标记,则将键值对移动至该索引 + // 若之前遇到了删除标记,则将键值对移动至该索引处 if (firstTombstone != -1) { buckets[firstTombstone] = buckets[index]; buckets[index] = TOMBSTONE; @@ -51,7 +51,7 @@ class HashMapOpenAddressing { if (firstTombstone == -1 && buckets[index] == TOMBSTONE) { firstTombstone = index; } - // 计算桶索引,越过尾部返回头部 + // 计算桶索引,越过尾部则返回头部 index = (index + 1) % capacity; } // 若 key 不存在,则返回添加点的索引 @@ -146,7 +146,7 @@ public class hash_map_open_addressing { map.Print(); /* 查询操作 */ - // 向哈希表输入键 key ,得到值 value + // 向哈希表中输入键 key ,得到值 value string? name = map.Get(13276); Console.WriteLine("\n输入学号 13276 ,查询到姓名 " + name); diff --git a/codes/csharp/chapter_heap/my_heap.cs b/codes/csharp/chapter_heap/my_heap.cs index c9369d951..f94721014 100644 --- a/codes/csharp/chapter_heap/my_heap.cs +++ b/codes/csharp/chapter_heap/my_heap.cs @@ -27,17 +27,17 @@ class MaxHeap { } } - /* 获取左子节点索引 */ + /* 获取左子节点的索引 */ int Left(int i) { return 2 * i + 1; } - /* 获取右子节点索引 */ + /* 获取右子节点的索引 */ int Right(int i) { return 2 * i + 2; } - /* 获取父节点索引 */ + /* 获取父节点的索引 */ int Parent(int i) { return (i - 1) / 2; // 向下整除 } diff --git a/codes/csharp/chapter_sorting/bubble_sort.cs b/codes/csharp/chapter_sorting/bubble_sort.cs index 67a154d15..265f74170 100644 --- a/codes/csharp/chapter_sorting/bubble_sort.cs +++ b/codes/csharp/chapter_sorting/bubble_sort.cs @@ -34,7 +34,7 @@ public class bubble_sort { flag = true; // 记录交换元素 } } - if (!flag) break; // 此轮冒泡未交换任何元素,直接跳出 + if (!flag) break; // 此轮“冒泡”未交换任何元素,直接跳出 } } diff --git a/codes/csharp/chapter_sorting/merge_sort.cs b/codes/csharp/chapter_sorting/merge_sort.cs index 0966e8907..86de13067 100644 --- a/codes/csharp/chapter_sorting/merge_sort.cs +++ b/codes/csharp/chapter_sorting/merge_sort.cs @@ -9,12 +9,12 @@ namespace hello_algo.chapter_sorting; public class merge_sort { /* 合并左子数组和右子数组 */ void Merge(int[] nums, int left, int mid, int right) { - // 左子数组区间 [left, mid], 右子数组区间 [mid+1, right] + // 左子数组区间为 [left, mid], 右子数组区间为 [mid+1, right] // 创建一个临时数组 tmp ,用于存放合并后的结果 int[] tmp = new int[right - left + 1]; // 初始化左子数组和右子数组的起始索引 int i = left, j = mid + 1, k = 0; - // 当左右子数组都还有元素时,比较并将较小的元素复制到临时数组中 + // 当左右子数组都还有元素时,进行比较并将较小的元素复制到临时数组中 while (i <= mid && j <= right) { if (nums[i] <= nums[j]) tmp[k++] = nums[i++]; diff --git a/codes/csharp/chapter_sorting/quick_sort.cs b/codes/csharp/chapter_sorting/quick_sort.cs index f0fb4f328..b9badb9f4 100644 --- a/codes/csharp/chapter_sorting/quick_sort.cs +++ b/codes/csharp/chapter_sorting/quick_sort.cs @@ -47,7 +47,7 @@ class QuickSortMedian { (nums[j], nums[i]) = (nums[i], nums[j]); } - /* 选取三个元素的中位数 */ + /* 选取三个候选元素的中位数 */ static int MedianThree(int[] nums, int left, int mid, int right) { // 此处使用异或运算来简化代码 // 异或规则为 0 ^ 0 = 1 ^ 1 = 0, 0 ^ 1 = 1 ^ 0 = 1 diff --git a/codes/csharp/chapter_stack_and_queue/array_deque.cs b/codes/csharp/chapter_stack_and_queue/array_deque.cs index 23060fc43..fbd87ef6e 100644 --- a/codes/csharp/chapter_stack_and_queue/array_deque.cs +++ b/codes/csharp/chapter_stack_and_queue/array_deque.cs @@ -48,7 +48,7 @@ public class ArrayDeque { return; } // 队首指针向左移动一位 - // 通过取余操作,实现 front 越过数组头部后回到尾部 + // 通过取余操作实现 front 越过数组头部后回到尾部 front = Index(front - 1); // 将 num 添加至队首 nums[front] = num; @@ -61,7 +61,7 @@ public class ArrayDeque { Console.WriteLine("双向队列已满"); return; } - // 计算尾指针,指向队尾索引 + 1 + // 计算队尾指针,指向队尾索引 + 1 int rear = Index(front + queSize); // 将 num 添加至队尾 nums[rear] = num; diff --git a/codes/csharp/chapter_stack_and_queue/array_queue.cs b/codes/csharp/chapter_stack_and_queue/array_queue.cs index b32622f51..7be3d835e 100644 --- a/codes/csharp/chapter_stack_and_queue/array_queue.cs +++ b/codes/csharp/chapter_stack_and_queue/array_queue.cs @@ -38,8 +38,8 @@ class ArrayQueue { Console.WriteLine("队列已满"); return; } - // 计算尾指针,指向队尾索引 + 1 - // 通过取余操作,实现 rear 越过数组尾部后回到头部 + // 计算队尾指针,指向队尾索引 + 1 + // 通过取余操作实现 rear 越过数组尾部后回到头部 int rear = (front + queSize) % Capacity(); // 将 num 添加至队尾 nums[rear] = num; @@ -49,7 +49,7 @@ class ArrayQueue { /* 出队 */ public int Pop() { int num = Peek(); - // 队首指针向后移动一位,若越过尾部则返回到数组头部 + // 队首指针向后移动一位,若越过尾部,则返回到数组头部 front = (front + 1) % Capacity(); queSize--; return num; diff --git a/codes/csharp/chapter_stack_and_queue/linkedlist_queue.cs b/codes/csharp/chapter_stack_and_queue/linkedlist_queue.cs index 59d98a53f..646c359e7 100644 --- a/codes/csharp/chapter_stack_and_queue/linkedlist_queue.cs +++ b/codes/csharp/chapter_stack_and_queue/linkedlist_queue.cs @@ -28,7 +28,7 @@ class LinkedListQueue { /* 入队 */ public void Push(int num) { - // 尾节点后添加 num + // 在尾节点后添加 num ListNode node = new(num); // 如果队列为空,则令头、尾节点都指向该节点 if (front == null) { diff --git a/codes/csharp/chapter_tree/avl_tree.cs b/codes/csharp/chapter_tree/avl_tree.cs index d40d9ebe0..6597889ca 100644 --- a/codes/csharp/chapter_tree/avl_tree.cs +++ b/codes/csharp/chapter_tree/avl_tree.cs @@ -96,7 +96,7 @@ class AVLTree { /* 递归插入节点(辅助方法) */ TreeNode? InsertHelper(TreeNode? node, int val) { if (node == null) return new TreeNode(val); - /* 1. 查找插入位置,并插入节点 */ + /* 1. 查找插入位置并插入节点 */ if (val < node.val) node.left = InsertHelper(node.left, val); else if (val > node.val) @@ -118,7 +118,7 @@ class AVLTree { /* 递归删除节点(辅助方法) */ TreeNode? RemoveHelper(TreeNode? node, int val) { if (node == null) return null; - /* 1. 查找节点,并删除之 */ + /* 1. 查找节点并删除 */ if (val < node.val) node.left = RemoveHelper(node.left, val); else if (val > node.val) diff --git a/codes/dart/chapter_array_and_linkedlist/my_list.dart b/codes/dart/chapter_array_and_linkedlist/my_list.dart index b3d102088..34f12242f 100644 --- a/codes/dart/chapter_array_and_linkedlist/my_list.dart +++ b/codes/dart/chapter_array_and_linkedlist/my_list.dart @@ -67,7 +67,7 @@ class MyList { } // 更新元素数量 _size--; - // 返回被删除元素 + // 返回被删除的元素 return _num; } @@ -75,7 +75,7 @@ class MyList { void extendCapacity() { // 新建一个长度为原数组 _extendRatio 倍的新数组 final _newNums = List.filled(_capacity * _extendRatio, 0); - // 将原数组拷贝到新数组 + // 将原数组复制到新数组 List.copyRange(_newNums, 0, _arr); // 更新 _arr 的引用 _arr = _newNums; diff --git a/codes/dart/chapter_backtracking/n_queens.dart b/codes/dart/chapter_backtracking/n_queens.dart index 0e865f056..acbe805d5 100644 --- a/codes/dart/chapter_backtracking/n_queens.dart +++ b/codes/dart/chapter_backtracking/n_queens.dart @@ -25,10 +25,10 @@ void backtrack( } // 遍历所有列 for (int col = 0; col < n; col++) { - // 计算该格子对应的主对角线和副对角线 + // 计算该格子对应的主对角线和次对角线 int diag1 = row - col + n - 1; int diag2 = row + col; - // 剪枝:不允许该格子所在列、主对角线、副对角线上存在皇后 + // 剪枝:不允许该格子所在列、主对角线、次对角线上存在皇后 if (!cols[col] && !diags1[diag1] && !diags2[diag2]) { // 尝试:将皇后放置在该格子 state[row][col] = "Q"; @@ -52,7 +52,7 @@ List>> nQueens(int n) { List> state = List.generate(n, (index) => List.filled(n, "#")); List cols = List.filled(n, false); // 记录列是否有皇后 List diags1 = List.filled(2 * n - 1, false); // 记录主对角线上是否有皇后 - List diags2 = List.filled(2 * n - 1, false); // 记录副对角线上是否有皇后 + List diags2 = List.filled(2 * n - 1, false); // 记录次对角线上是否有皇后 List>> res = []; backtrack(0, n, state, res, cols, diags1, diags2); diff --git a/codes/dart/chapter_graph/graph_bfs.dart b/codes/dart/chapter_graph/graph_bfs.dart index f2fea88c5..522ba2c0d 100644 --- a/codes/dart/chapter_graph/graph_bfs.dart +++ b/codes/dart/chapter_graph/graph_bfs.dart @@ -9,7 +9,7 @@ import 'dart:collection'; import '../utils/vertex.dart'; import 'graph_adjacency_list.dart'; -/* 广度优先遍历 BFS */ +/* 广度优先遍历 */ List graphBFS(GraphAdjList graph, Vertex startVet) { // 使用邻接表来表示图,以便获取指定顶点的所有邻接顶点 // 顶点遍历序列 @@ -59,7 +59,7 @@ void main() { print("\n初始化后,图为"); graph.printAdjList(); - /* 广度优先遍历 BFS */ + /* 广度优先遍历 */ List res = graphBFS(graph, v[0]); print("\n广度优先遍历(BFS)顶点序列为"); print(Vertex.vetsToVals(res)); diff --git a/codes/dart/chapter_graph/graph_dfs.dart b/codes/dart/chapter_graph/graph_dfs.dart index 6d64dca99..0565023fc 100644 --- a/codes/dart/chapter_graph/graph_dfs.dart +++ b/codes/dart/chapter_graph/graph_dfs.dart @@ -7,7 +7,7 @@ import '../utils/vertex.dart'; import 'graph_adjacency_list.dart'; -/* 深度优先遍历 DFS 辅助函数 */ +/* 深度优先遍历辅助函数 */ void dfs( GraphAdjList graph, Set visited, @@ -26,7 +26,7 @@ void dfs( } } -/* 深度优先遍历 DFS */ +/* 深度优先遍历 */ List graphDFS(GraphAdjList graph, Vertex startVet) { // 顶点遍历序列 List res = []; @@ -52,7 +52,7 @@ void main() { print("\n初始化后,图为"); graph.printAdjList(); - /* 深度优先遍历 DFS */ + /* 深度优先遍历 */ List res = graphDFS(graph, v[0]); print("\n深度优先遍历(DFS)顶点序列为"); print(Vertex.vetsToVals(res)); diff --git a/codes/dart/chapter_greedy/max_capacity.dart b/codes/dart/chapter_greedy/max_capacity.dart index ce5c0633a..1ed464a6e 100644 --- a/codes/dart/chapter_greedy/max_capacity.dart +++ b/codes/dart/chapter_greedy/max_capacity.dart @@ -8,7 +8,7 @@ import 'dart:math'; /* 最大容量:贪心 */ int maxCapacity(List ht) { - // 初始化 i, j 分列数组两端 + // 初始化 i, j,使其分列数组两端 int i = 0, j = ht.length - 1; // 初始最大容量为 0 int res = 0; diff --git a/codes/dart/chapter_hashing/array_hash_map.dart b/codes/dart/chapter_hashing/array_hash_map.dart index 8a8770aa9..45b46310f 100644 --- a/codes/dart/chapter_hashing/array_hash_map.dart +++ b/codes/dart/chapter_hashing/array_hash_map.dart @@ -106,7 +106,7 @@ void main() { map.printHashMap(); /* 查询操作 */ - // 向哈希表输入键 key ,得到值 value + // 向哈希表中输入键 key ,得到值 value String? name = map.get(15937); print("\n输入学号 15937 ,查询到姓名 $name"); diff --git a/codes/dart/chapter_hashing/hash_map.dart b/codes/dart/chapter_hashing/hash_map.dart index 455752320..811d3dac2 100644 --- a/codes/dart/chapter_hashing/hash_map.dart +++ b/codes/dart/chapter_hashing/hash_map.dart @@ -20,7 +20,7 @@ void main() { map.forEach((key, value) => print("$key -> $value")); /* 查询操作 */ - // 向哈希表输入键 key ,得到值 value + // 向哈希表中输入键 key ,得到值 value final String? name = map[15937]; print("\n输入学号 15937 ,查询到姓名 $name"); diff --git a/codes/dart/chapter_hashing/hash_map_chaining.dart b/codes/dart/chapter_hashing/hash_map_chaining.dart index 77221e3d3..a8ed9d5ab 100644 --- a/codes/dart/chapter_hashing/hash_map_chaining.dart +++ b/codes/dart/chapter_hashing/hash_map_chaining.dart @@ -37,13 +37,13 @@ class HashMapChaining { String? get(int key) { int index = hashFunc(key); List bucket = buckets[index]; - // 遍历桶,若找到 key 则返回对应 val + // 遍历桶,若找到 key ,则返回对应 val for (Pair pair in bucket) { if (pair.key == key) { return pair.val; } } - // 若未找到 key 则返回 null + // 若未找到 key ,则返回 null return null; } @@ -126,7 +126,7 @@ void main() { map.printHashMap(); /* 查询操作 */ - // 向哈希表输入键 key ,得到值 value + // 向哈希表中输入键 key ,得到值 value String? name = map.get(13276); print("\n输入学号 13276 ,查询到姓名 ${name}"); diff --git a/codes/dart/chapter_hashing/hash_map_open_addressing.dart b/codes/dart/chapter_hashing/hash_map_open_addressing.dart index 3dc831fea..618031234 100644 --- a/codes/dart/chapter_hashing/hash_map_open_addressing.dart +++ b/codes/dart/chapter_hashing/hash_map_open_addressing.dart @@ -37,9 +37,9 @@ class HashMapOpenAddressing { int firstTombstone = -1; // 线性探测,当遇到空桶时跳出 while (_buckets[index] != null) { - // 若遇到 key ,返回对应桶索引 + // 若遇到 key ,返回对应的桶索引 if (_buckets[index]!.key == key) { - // 若之前遇到了删除标记,则将键值对移动至该索引 + // 若之前遇到了删除标记,则将键值对移动至该索引处 if (firstTombstone != -1) { _buckets[firstTombstone] = _buckets[index]; _buckets[index] = _TOMBSTONE; @@ -51,7 +51,7 @@ class HashMapOpenAddressing { if (firstTombstone == -1 && _buckets[index] == _TOMBSTONE) { firstTombstone = index; } - // 计算桶索引,越过尾部返回头部 + // 计算桶索引,越过尾部则返回头部 index = (index + 1) % _capacity; } // 若 key 不存在,则返回添加点的索引 @@ -145,7 +145,7 @@ void main() { map.printHashMap(); /* 查询操作 */ - // 向哈希表输入键 key ,得到值 value + // 向哈希表中输入键 key ,得到值 value String? name = map.get(13276); print("\n输入学号 13276 ,查询到姓名 $name"); diff --git a/codes/dart/chapter_heap/my_heap.dart b/codes/dart/chapter_heap/my_heap.dart index e9e8535c9..7264ed78d 100644 --- a/codes/dart/chapter_heap/my_heap.dart +++ b/codes/dart/chapter_heap/my_heap.dart @@ -20,17 +20,17 @@ class MaxHeap { } } - /* 获取左子节点索引 */ + /* 获取左子节点的索引 */ int _left(int i) { return 2 * i + 1; } - /* 获取右子节点索引 */ + /* 获取右子节点的索引 */ int _right(int i) { return 2 * i + 2; } - /* 获取父节点索引 */ + /* 获取父节点的索引 */ int _parent(int i) { return (i - 1) ~/ 2; // 向下整除 } diff --git a/codes/dart/chapter_heap/top_k.dart b/codes/dart/chapter_heap/top_k.dart index 7053fd8b6..011d079b4 100644 --- a/codes/dart/chapter_heap/top_k.dart +++ b/codes/dart/chapter_heap/top_k.dart @@ -50,17 +50,17 @@ class MinHeap { return _minHeap; } - /* 获取左子节点索引 */ + /* 获取左子节点的索引 */ int _left(int i) { return 2 * i + 1; } - /* 获取右子节点索引 */ + /* 获取右子节点的索引 */ int _right(int i) { return 2 * i + 2; } - /* 获取父节点索引 */ + /* 获取父节点的索引 */ int _parent(int i) { return (i - 1) ~/ 2; // 向下整除 } diff --git a/codes/dart/chapter_sorting/bubble_sort.dart b/codes/dart/chapter_sorting/bubble_sort.dart index 9cec180fd..1d3cb2b4e 100644 --- a/codes/dart/chapter_sorting/bubble_sort.dart +++ b/codes/dart/chapter_sorting/bubble_sort.dart @@ -35,7 +35,7 @@ void bubbleSortWithFlag(List nums) { flag = true; // 记录交换元素 } } - if (!flag) break; // 此轮冒泡未交换任何元素,直接跳出 + if (!flag) break; // 此轮“冒泡”未交换任何元素,直接跳出 } } diff --git a/codes/dart/chapter_sorting/merge_sort.dart b/codes/dart/chapter_sorting/merge_sort.dart index b31ca68ca..03a0e1f68 100644 --- a/codes/dart/chapter_sorting/merge_sort.dart +++ b/codes/dart/chapter_sorting/merge_sort.dart @@ -6,12 +6,12 @@ /* 合并左子数组和右子数组 */ void merge(List nums, int left, int mid, int right) { - // 左子数组区间 [left, mid], 右子数组区间 [mid+1, right] + // 左子数组区间为 [left, mid], 右子数组区间为 [mid+1, right] // 创建一个临时数组 tmp ,用于存放合并后的结果 List tmp = List.filled(right - left + 1, 0); // 初始化左子数组和右子数组的起始索引 int i = left, j = mid + 1, k = 0; - // 当左右子数组都还有元素时,比较并将较小的元素复制到临时数组中 + // 当左右子数组都还有元素时,进行比较并将较小的元素复制到临时数组中 while (i <= mid && j <= right) { if (nums[i] <= nums[j]) tmp[k++] = nums[i++]; diff --git a/codes/dart/chapter_sorting/quick_sort.dart b/codes/dart/chapter_sorting/quick_sort.dart index 5d75f07e1..a3b49727d 100644 --- a/codes/dart/chapter_sorting/quick_sort.dart +++ b/codes/dart/chapter_sorting/quick_sort.dart @@ -47,7 +47,7 @@ class QuickSortMedian { nums[j] = tmp; } - /* 选取三个元素的中位数 */ + /* 选取三个候选元素的中位数 */ static int _medianThree(List nums, int left, int mid, int right) { // 此处使用异或运算来简化代码 // 异或规则为 0 ^ 0 = 1 ^ 1 = 0, 0 ^ 1 = 1 ^ 0 = 1 diff --git a/codes/dart/chapter_stack_and_queue/array_deque.dart b/codes/dart/chapter_stack_and_queue/array_deque.dart index 34eba9c8d..56de478f2 100644 --- a/codes/dart/chapter_stack_and_queue/array_deque.dart +++ b/codes/dart/chapter_stack_and_queue/array_deque.dart @@ -45,7 +45,7 @@ class ArrayDeque { throw Exception("双向队列已满"); } // 队首指针向左移动一位 - // 通过取余操作,实现 _front 越过数组头部后回到尾部 + // 通过取余操作实现 _front 越过数组头部后回到尾部 _front = index(_front - 1); // 将 _num 添加至队首 _nums[_front] = _num; @@ -57,7 +57,7 @@ class ArrayDeque { if (_queSize == capacity()) { throw Exception("双向队列已满"); } - // 计算尾指针,指向队尾索引 + 1 + // 计算队尾指针,指向队尾索引 + 1 int rear = index(_front + _queSize); // 将 _num 添加至队尾 _nums[rear] = _num; diff --git a/codes/dart/chapter_stack_and_queue/array_queue.dart b/codes/dart/chapter_stack_and_queue/array_queue.dart index 8c25e75e8..1d2aaf3e1 100644 --- a/codes/dart/chapter_stack_and_queue/array_queue.dart +++ b/codes/dart/chapter_stack_and_queue/array_queue.dart @@ -35,8 +35,8 @@ class ArrayQueue { if (_queSize == capaCity()) { throw Exception("队列已满"); } - // 计算尾指针,指向队尾索引 + 1 - // 通过取余操作,实现 rear 越过数组尾部后回到头部 + // 计算队尾指针,指向队尾索引 + 1 + // 通过取余操作实现 rear 越过数组尾部后回到头部 int rear = (_front + _queSize) % capaCity(); // 将 _num 添加至队尾 _nums[rear] = _num; @@ -46,7 +46,7 @@ class ArrayQueue { /* 出队 */ int pop() { int _num = peek(); - // 队首指针向后移动一位,若越过尾部则返回到数组头部 + // 队首指针向后移动一位,若越过尾部,则返回到数组头部 _front = (_front + 1) % capaCity(); _queSize--; return _num; diff --git a/codes/dart/chapter_stack_and_queue/linkedlist_queue.dart b/codes/dart/chapter_stack_and_queue/linkedlist_queue.dart index 567a64043..50ceb7a08 100644 --- a/codes/dart/chapter_stack_and_queue/linkedlist_queue.dart +++ b/codes/dart/chapter_stack_and_queue/linkedlist_queue.dart @@ -29,7 +29,7 @@ class LinkedListQueue { /* 入队 */ void push(int _num) { - // 尾节点后添加 _num + // 在尾节点后添加 _num final node = ListNode(_num); // 如果队列为空,则令头、尾节点都指向该节点 if (_front == null) { diff --git a/codes/dart/chapter_tree/avl_tree.dart b/codes/dart/chapter_tree/avl_tree.dart index a8729638a..0db64270b 100644 --- a/codes/dart/chapter_tree/avl_tree.dart +++ b/codes/dart/chapter_tree/avl_tree.dart @@ -102,7 +102,7 @@ class AVLTree { /* 递归插入节点(辅助方法) */ TreeNode? insertHelper(TreeNode? node, int val) { if (node == null) return TreeNode(val); - /* 1. 查找插入位置,并插入节点 */ + /* 1. 查找插入位置并插入节点 */ if (val < node.val) node.left = insertHelper(node.left, val); else if (val > node.val) @@ -124,7 +124,7 @@ class AVLTree { /* 递归删除节点(辅助方法) */ TreeNode? removeHelper(TreeNode? node, int val) { if (node == null) return null; - /* 1. 查找节点,并删除之 */ + /* 1. 查找节点并删除 */ if (val < node.val) node.left = removeHelper(node.left, val); else if (val > node.val) diff --git a/codes/go/chapter_array_and_linkedlist/my_list.go b/codes/go/chapter_array_and_linkedlist/my_list.go index 94406d5d9..aaefd2148 100644 --- a/codes/go/chapter_array_and_linkedlist/my_list.go +++ b/codes/go/chapter_array_and_linkedlist/my_list.go @@ -34,7 +34,7 @@ func (l *myList) capacity() int { /* 访问元素 */ func (l *myList) get(index int) int { - // 索引如果越界则抛出异常,下同 + // 索引如果越界,则抛出异常,下同 if index < 0 || index >= l.arrSize { panic("索引越界") } @@ -90,13 +90,13 @@ func (l *myList) remove(index int) int { } // 更新元素数量 l.arrSize-- - // 返回被删除元素 + // 返回被删除的元素 return num } /* 列表扩容 */ func (l *myList) extendCapacity() { - // 新建一个长度为原数组 extendRatio 倍的新数组,并将原数组拷贝到新数组 + // 新建一个长度为原数组 extendRatio 倍的新数组,并将原数组复制到新数组 l.arr = append(l.arr, make([]int, l.arrCapacity*(l.extendRatio-1))...) // 更新列表容量 l.arrCapacity = len(l.arr) diff --git a/codes/go/chapter_backtracking/n_queens.go b/codes/go/chapter_backtracking/n_queens.go index f0d3f2b7e..9097c5519 100644 --- a/codes/go/chapter_backtracking/n_queens.go +++ b/codes/go/chapter_backtracking/n_queens.go @@ -18,10 +18,10 @@ func backtrack(row, n int, state *[][]string, res *[][][]string, cols, diags1, d } // 遍历所有列 for col := 0; col < n; col++ { - // 计算该格子对应的主对角线和副对角线 + // 计算该格子对应的主对角线和次对角线 diag1 := row - col + n - 1 diag2 := row + col - // 剪枝:不允许该格子所在列、主对角线、副对角线上存在皇后 + // 剪枝:不允许该格子所在列、主对角线、次对角线上存在皇后 if !(*cols)[col] && !(*diags1)[diag1] && !(*diags2)[diag2] { // 尝试:将皇后放置在该格子 (*state)[row][col] = "Q" diff --git a/codes/go/chapter_graph/graph_bfs.go b/codes/go/chapter_graph/graph_bfs.go index 1b1ecd580..0f2098bd3 100644 --- a/codes/go/chapter_graph/graph_bfs.go +++ b/codes/go/chapter_graph/graph_bfs.go @@ -8,7 +8,7 @@ import ( . "github.com/krahets/hello-algo/pkg" ) -/* 广度优先遍历 BFS */ +/* 广度优先遍历 */ // 使用邻接表来表示图,以便获取指定顶点的所有邻接顶点 func graphBFS(g *graphAdjList, startVet Vertex) []Vertex { // 顶点遍历序列 diff --git a/codes/go/chapter_graph/graph_bfs_test.go b/codes/go/chapter_graph/graph_bfs_test.go index 318a52f24..fe81cb9d7 100644 --- a/codes/go/chapter_graph/graph_bfs_test.go +++ b/codes/go/chapter_graph/graph_bfs_test.go @@ -22,7 +22,7 @@ func TestGraphBFS(t *testing.T) { fmt.Println("初始化后,图为:") graph.print() - /* 广度优先遍历 BFS */ + /* 广度优先遍历 */ res := graphBFS(graph, vets[0]) fmt.Println("广度优先遍历(BFS)顶点序列为:") PrintSlice(VetsToVals(res)) diff --git a/codes/go/chapter_graph/graph_dfs.go b/codes/go/chapter_graph/graph_dfs.go index b1d26f7b8..b1fb9027d 100644 --- a/codes/go/chapter_graph/graph_dfs.go +++ b/codes/go/chapter_graph/graph_dfs.go @@ -8,7 +8,7 @@ import ( . "github.com/krahets/hello-algo/pkg" ) -/* 深度优先遍历 DFS 辅助函数 */ +/* 深度优先遍历辅助函数 */ func dfs(g *graphAdjList, visited map[Vertex]struct{}, res *[]Vertex, vet Vertex) { // append 操作会返回新的的引用,必须让原引用重新赋值为新slice的引用 *res = append(*res, vet) @@ -23,7 +23,7 @@ func dfs(g *graphAdjList, visited map[Vertex]struct{}, res *[]Vertex, vet Vertex } } -/* 深度优先遍历 DFS */ +/* 深度优先遍历 */ // 使用邻接表来表示图,以便获取指定顶点的所有邻接顶点 func graphDFS(g *graphAdjList, startVet Vertex) []Vertex { // 顶点遍历序列 diff --git a/codes/go/chapter_graph/graph_dfs_test.go b/codes/go/chapter_graph/graph_dfs_test.go index 903509fc7..80dbb979f 100644 --- a/codes/go/chapter_graph/graph_dfs_test.go +++ b/codes/go/chapter_graph/graph_dfs_test.go @@ -21,7 +21,7 @@ func TestGraphDFS(t *testing.T) { fmt.Println("初始化后,图为:") graph.print() - /* 深度优先遍历 DFS */ + /* 深度优先遍历 */ res := graphDFS(graph, vets[0]) fmt.Println("深度优先遍历(DFS)顶点序列为:") PrintSlice(VetsToVals(res)) diff --git a/codes/go/chapter_greedy/max_capacity.go b/codes/go/chapter_greedy/max_capacity.go index 6e4254b3c..178a4df12 100644 --- a/codes/go/chapter_greedy/max_capacity.go +++ b/codes/go/chapter_greedy/max_capacity.go @@ -8,7 +8,7 @@ import "math" /* 最大容量:贪心 */ func maxCapacity(ht []int) int { - // 初始化 i, j 分列数组两端 + // 初始化 i, j,使其分列数组两端 i, j := 0, len(ht)-1 // 初始最大容量为 0 res := 0 diff --git a/codes/go/chapter_hashing/array_hash_map_test.go b/codes/go/chapter_hashing/array_hash_map_test.go index 2fc83c524..ce976e5eb 100644 --- a/codes/go/chapter_hashing/array_hash_map_test.go +++ b/codes/go/chapter_hashing/array_hash_map_test.go @@ -24,7 +24,7 @@ func TestArrayHashMap(t *testing.T) { hmap.print() /* 查询操作 */ - // 向哈希表输入键 key ,得到值 value + // 向哈希表中输入键 key ,得到值 value name := hmap.get(15937) fmt.Println("\n输入学号 15937 ,查询到姓名 " + name) diff --git a/codes/go/chapter_hashing/hash_collision_test.go b/codes/go/chapter_hashing/hash_collision_test.go index 46ec26ebc..d0cc81af2 100644 --- a/codes/go/chapter_hashing/hash_collision_test.go +++ b/codes/go/chapter_hashing/hash_collision_test.go @@ -24,7 +24,7 @@ func TestHashMapChaining(t *testing.T) { hmap.print() /* 查询操作 */ - // 向哈希表输入键 key ,得到值 value + // 向哈希表中输入键 key ,得到值 value name := hmap.get(15937) fmt.Println("\n输入学号 15937 ,查询到姓名 ", name) @@ -50,7 +50,7 @@ func TestHashMapOpenAddressing(t *testing.T) { hmap.print() /* 查询操作 */ - // 向哈希表输入键 key ,得到值 value + // 向哈希表中输入键 key ,得到值 value name := hmap.get(13276) fmt.Println("\n输入学号 13276 ,查询到姓名 ", name) diff --git a/codes/go/chapter_hashing/hash_map_chaining.go b/codes/go/chapter_hashing/hash_map_chaining.go index e7f17416c..63874e1be 100644 --- a/codes/go/chapter_hashing/hash_map_chaining.go +++ b/codes/go/chapter_hashing/hash_map_chaining.go @@ -48,13 +48,13 @@ func (m *hashMapChaining) loadFactor() float64 { func (m *hashMapChaining) get(key int) string { idx := m.hashFunc(key) bucket := m.buckets[idx] - // 遍历桶,若找到 key 则返回对应 val + // 遍历桶,若找到 key ,则返回对应 val for _, p := range bucket { if p.key == key { return p.val } } - // 若未找到 key 则返回空字符串 + // 若未找到 key ,则返回空字符串 return "" } diff --git a/codes/go/chapter_hashing/hash_map_open_addressing.go b/codes/go/chapter_hashing/hash_map_open_addressing.go index c73be22ae..6fb61c3be 100644 --- a/codes/go/chapter_hashing/hash_map_open_addressing.go +++ b/codes/go/chapter_hashing/hash_map_open_addressing.go @@ -50,7 +50,7 @@ func (m *hashMapOpenAddressing) get(key int) string { idx := m.hashFunc(key) // 线性探测,从 index 开始向后遍历 for i := 0; i < m.capacity; i++ { - // 计算桶索引,越过尾部返回头部 + // 计算桶索引,越过尾部则返回头部 j := (idx + i) % m.capacity // 若遇到空桶,说明无此 key ,则返回 null if m.buckets[j] == (pair{}) { @@ -61,7 +61,7 @@ func (m *hashMapOpenAddressing) get(key int) string { return m.buckets[j].val } } - // 若未找到 key 则返回空字符串 + // 若未找到 key ,则返回空字符串 return "" } @@ -74,7 +74,7 @@ func (m *hashMapOpenAddressing) put(key int, val string) { idx := m.hashFunc(key) // 线性探测,从 index 开始向后遍历 for i := 0; i < m.capacity; i++ { - // 计算桶索引,越过尾部返回头部 + // 计算桶索引,越过尾部则返回头部 j := (idx + i) % m.capacity // 若遇到空桶、或带有删除标记的桶,则将键值对放入该桶 if m.buckets[j] == (pair{}) || m.buckets[j] == m.removed { @@ -99,7 +99,7 @@ func (m *hashMapOpenAddressing) remove(key int) { // 遍历桶,从中删除键值对 // 线性探测,从 index 开始向后遍历 for i := 0; i < m.capacity; i++ { - // 计算桶索引,越过尾部返回头部 + // 计算桶索引,越过尾部则返回头部 j := (idx + i) % m.capacity // 若遇到空桶,说明无此 key ,则直接返回 if m.buckets[j] == (pair{}) { diff --git a/codes/go/chapter_hashing/hash_map_test.go b/codes/go/chapter_hashing/hash_map_test.go index b4168fd0b..1809ca444 100644 --- a/codes/go/chapter_hashing/hash_map_test.go +++ b/codes/go/chapter_hashing/hash_map_test.go @@ -27,7 +27,7 @@ func TestHashMap(t *testing.T) { PrintMap(hmap) /* 查询操作 */ - // 向哈希表输入键 key ,得到值 value + // 向哈希表中输入键 key ,得到值 value name := hmap[15937] fmt.Println("\n输入学号 15937 ,查询到姓名 ", name) diff --git a/codes/go/chapter_heap/my_heap.go b/codes/go/chapter_heap/my_heap.go index cda948f19..1cc091b2f 100644 --- a/codes/go/chapter_heap/my_heap.go +++ b/codes/go/chapter_heap/my_heap.go @@ -33,17 +33,17 @@ func newMaxHeap(nums []any) *maxHeap { return h } -/* 获取左子节点索引 */ +/* 获取左子节点的索引 */ func (h *maxHeap) left(i int) int { return 2*i + 1 } -/* 获取右子节点索引 */ +/* 获取右子节点的索引 */ func (h *maxHeap) right(i int) int { return 2*i + 2 } -/* 获取父节点索引 */ +/* 获取父节点的索引 */ func (h *maxHeap) parent(i int) int { // 向下整除 return (i - 1) / 2 diff --git a/codes/go/chapter_sorting/bubble_sort.go b/codes/go/chapter_sorting/bubble_sort.go index 66167a4c4..773611e47 100644 --- a/codes/go/chapter_sorting/bubble_sort.go +++ b/codes/go/chapter_sorting/bubble_sort.go @@ -31,7 +31,7 @@ func bubbleSortWithFlag(nums []int) { flag = true // 记录交换元素 } } - if flag == false { // 此轮冒泡未交换任何元素,直接跳出 + if flag == false { // 此轮“冒泡”未交换任何元素,直接跳出 break } } diff --git a/codes/go/chapter_sorting/merge_sort.go b/codes/go/chapter_sorting/merge_sort.go index 6ebb57c30..54f2bbe32 100644 --- a/codes/go/chapter_sorting/merge_sort.go +++ b/codes/go/chapter_sorting/merge_sort.go @@ -6,12 +6,12 @@ package chapter_sorting /* 合并左子数组和右子数组 */ func merge(nums []int, left, mid, right int) { - // 左子数组区间 [left, mid], 右子数组区间 [mid+1, right] + // 左子数组区间为 [left, mid], 右子数组区间为 [mid+1, right] // 创建一个临时数组 tmp ,用于存放合并后的结果 tmp := make([]int, right-left+1) // 初始化左子数组和右子数组的起始索引 i, j, k := left, mid+1, 0 - // 当左右子数组都还有元素时,比较并将较小的元素复制到临时数组中 + // 当左右子数组都还有元素时,进行比较并将较小的元素复制到临时数组中 for i <= mid && j <= right { if nums[i] <= nums[j] { tmp[k] = nums[i] diff --git a/codes/go/chapter_sorting/quick_sort.go b/codes/go/chapter_sorting/quick_sort.go index dcfe74b74..c5eb95d3c 100644 --- a/codes/go/chapter_sorting/quick_sort.go +++ b/codes/go/chapter_sorting/quick_sort.go @@ -45,7 +45,7 @@ func (q *quickSort) quickSort(nums []int, left, right int) { q.quickSort(nums, pivot+1, right) } -/* 选取三个元素的中位数 */ +/* 选取三个候选元素的中位数 */ func (q *quickSortMedian) medianThree(nums []int, left, mid, right int) int { // 此处使用异或运算来简化代码(!= 在这里起到异或的作用) // 异或规则为 0 ^ 0 = 1 ^ 1 = 0, 0 ^ 1 = 1 ^ 0 = 1 diff --git a/codes/go/chapter_stack_and_queue/array_deque.go b/codes/go/chapter_stack_and_queue/array_deque.go index 6481811a6..acaa4095c 100644 --- a/codes/go/chapter_stack_and_queue/array_deque.go +++ b/codes/go/chapter_stack_and_queue/array_deque.go @@ -49,7 +49,7 @@ func (q *arrayDeque) pushFirst(num int) { return } // 队首指针向左移动一位 - // 通过取余操作,实现 front 越过数组头部后回到尾部 + // 通过取余操作实现 front 越过数组头部后回到尾部 q.front = q.index(q.front - 1) // 将 num 添加至队首 q.nums[q.front] = num @@ -62,7 +62,7 @@ func (q *arrayDeque) pushLast(num int) { fmt.Println("双向队列已满") return } - // 计算尾指针,指向队尾索引 + 1 + // 计算队尾指针,指向队尾索引 + 1 rear := q.index(q.front + q.queSize) // 将 num 添加至队首 q.nums[rear] = num diff --git a/codes/go/chapter_stack_and_queue/array_queue.go b/codes/go/chapter_stack_and_queue/array_queue.go index e4f692e50..9068996a1 100644 --- a/codes/go/chapter_stack_and_queue/array_queue.go +++ b/codes/go/chapter_stack_and_queue/array_queue.go @@ -38,8 +38,8 @@ func (q *arrayQueue) push(num int) { if q.queSize == q.queCapacity { return } - // 计算尾指针,指向队尾索引 + 1 - // 通过取余操作,实现 rear 越过数组尾部后回到头部 + // 计算队尾指针,指向队尾索引 + 1 + // 通过取余操作实现 rear 越过数组尾部后回到头部 rear := (q.front + q.queSize) % q.queCapacity // 将 num 添加至队尾 q.nums[rear] = num @@ -49,7 +49,7 @@ func (q *arrayQueue) push(num int) { /* 出队 */ func (q *arrayQueue) pop() any { num := q.peek() - // 队首指针向后移动一位,若越过尾部则返回到数组头部 + // 队首指针向后移动一位,若越过尾部,则返回到数组头部 q.front = (q.front + 1) % q.queCapacity q.queSize-- return num diff --git a/codes/go/chapter_tree/avl_tree.go b/codes/go/chapter_tree/avl_tree.go index 9f2cd9803..b54f6ba54 100644 --- a/codes/go/chapter_tree/avl_tree.go +++ b/codes/go/chapter_tree/avl_tree.go @@ -116,7 +116,7 @@ func (t *aVLTree) insertHelper(node *TreeNode, val int) *TreeNode { if node == nil { return NewTreeNode(val) } - /* 1. 查找插入位置,并插入节点 */ + /* 1. 查找插入位置并插入节点 */ if val < node.Val.(int) { node.Left = t.insertHelper(node.Left, val) } else if val > node.Val.(int) { @@ -143,7 +143,7 @@ func (t *aVLTree) removeHelper(node *TreeNode, val int) *TreeNode { if node == nil { return nil } - /* 1. 查找节点,并删除之 */ + /* 1. 查找节点并删除 */ if val < node.Val.(int) { node.Left = t.removeHelper(node.Left, val) } else if val > node.Val.(int) { diff --git a/codes/java/chapter_array_and_linkedlist/my_list.java b/codes/java/chapter_array_and_linkedlist/my_list.java index 0ec9ed286..c564d2901 100644 --- a/codes/java/chapter_array_and_linkedlist/my_list.java +++ b/codes/java/chapter_array_and_linkedlist/my_list.java @@ -32,7 +32,7 @@ class MyList { /* 访问元素 */ public int get(int index) { - // 索引如果越界则抛出异常,下同 + // 索引如果越界,则抛出异常,下同 if (index < 0 || index >= size) throw new IndexOutOfBoundsException("索引越界"); return arr[index]; @@ -82,13 +82,13 @@ class MyList { } // 更新元素数量 size--; - // 返回被删除元素 + // 返回被删除的元素 return num; } /* 列表扩容 */ public void extendCapacity() { - // 新建一个长度为原数组 extendRatio 倍的新数组,并将原数组拷贝到新数组 + // 新建一个长度为原数组 extendRatio 倍的新数组,并将原数组复制到新数组 arr = Arrays.copyOf(arr, capacity() * extendRatio); // 更新列表容量 capacity = arr.length; diff --git a/codes/java/chapter_backtracking/n_queens.java b/codes/java/chapter_backtracking/n_queens.java index a59912a5d..177ee0a4f 100644 --- a/codes/java/chapter_backtracking/n_queens.java +++ b/codes/java/chapter_backtracking/n_queens.java @@ -23,10 +23,10 @@ public class n_queens { } // 遍历所有列 for (int col = 0; col < n; col++) { - // 计算该格子对应的主对角线和副对角线 + // 计算该格子对应的主对角线和次对角线 int diag1 = row - col + n - 1; int diag2 = row + col; - // 剪枝:不允许该格子所在列、主对角线、副对角线上存在皇后 + // 剪枝:不允许该格子所在列、主对角线、次对角线上存在皇后 if (!cols[col] && !diags1[diag1] && !diags2[diag2]) { // 尝试:将皇后放置在该格子 state.get(row).set(col, "Q"); @@ -53,7 +53,7 @@ public class n_queens { } boolean[] cols = new boolean[n]; // 记录列是否有皇后 boolean[] diags1 = new boolean[2 * n - 1]; // 记录主对角线上是否有皇后 - boolean[] diags2 = new boolean[2 * n - 1]; // 记录副对角线上是否有皇后 + boolean[] diags2 = new boolean[2 * n - 1]; // 记录次对角线上是否有皇后 List>> res = new ArrayList<>(); backtrack(0, n, state, res, cols, diags1, diags2); diff --git a/codes/java/chapter_graph/graph_bfs.java b/codes/java/chapter_graph/graph_bfs.java index ec021d928..f3d1d2ad7 100644 --- a/codes/java/chapter_graph/graph_bfs.java +++ b/codes/java/chapter_graph/graph_bfs.java @@ -10,7 +10,7 @@ import java.util.*; import utils.*; public class graph_bfs { - /* 广度优先遍历 BFS */ + /* 广度优先遍历 */ // 使用邻接表来表示图,以便获取指定顶点的所有邻接顶点 static List graphBFS(GraphAdjList graph, Vertex startVet) { // 顶点遍历序列 @@ -47,7 +47,7 @@ public class graph_bfs { System.out.println("\n初始化后,图为"); graph.print(); - /* 广度优先遍历 BFS */ + /* 广度优先遍历 */ List res = graphBFS(graph, v[0]); System.out.println("\n广度优先遍历(BFS)顶点序列为"); System.out.println(Vertex.vetsToVals(res)); diff --git a/codes/java/chapter_graph/graph_dfs.java b/codes/java/chapter_graph/graph_dfs.java index c0b1d6d41..89dd0eb5f 100644 --- a/codes/java/chapter_graph/graph_dfs.java +++ b/codes/java/chapter_graph/graph_dfs.java @@ -10,7 +10,7 @@ import java.util.*; import utils.*; public class graph_dfs { - /* 深度优先遍历 DFS 辅助函数 */ + /* 深度优先遍历辅助函数 */ static void dfs(GraphAdjList graph, Set visited, List res, Vertex vet) { res.add(vet); // 记录访问顶点 visited.add(vet); // 标记该顶点已被访问 @@ -23,7 +23,7 @@ public class graph_dfs { } } - /* 深度优先遍历 DFS */ + /* 深度优先遍历 */ // 使用邻接表来表示图,以便获取指定顶点的所有邻接顶点 static List graphDFS(GraphAdjList graph, Vertex startVet) { // 顶点遍历序列 @@ -43,7 +43,7 @@ public class graph_dfs { System.out.println("\n初始化后,图为"); graph.print(); - /* 深度优先遍历 DFS */ + /* 深度优先遍历 */ List res = graphDFS(graph, v[0]); System.out.println("\n深度优先遍历(DFS)顶点序列为"); System.out.println(Vertex.vetsToVals(res)); diff --git a/codes/java/chapter_greedy/max_capacity.java b/codes/java/chapter_greedy/max_capacity.java index 85e743399..b0280a772 100644 --- a/codes/java/chapter_greedy/max_capacity.java +++ b/codes/java/chapter_greedy/max_capacity.java @@ -9,7 +9,7 @@ package chapter_greedy; public class max_capacity { /* 最大容量:贪心 */ static int maxCapacity(int[] ht) { - // 初始化 i, j 分列数组两端 + // 初始化 i, j,使其分列数组两端 int i = 0, j = ht.length - 1; // 初始最大容量为 0 int res = 0; diff --git a/codes/java/chapter_hashing/array_hash_map.java b/codes/java/chapter_hashing/array_hash_map.java index e0c875e77..4d650018b 100644 --- a/codes/java/chapter_hashing/array_hash_map.java +++ b/codes/java/chapter_hashing/array_hash_map.java @@ -114,7 +114,7 @@ public class array_hash_map { map.print(); /* 查询操作 */ - // 向哈希表输入键 key ,得到值 value + // 向哈希表中输入键 key ,得到值 value String name = map.get(15937); System.out.println("\n输入学号 15937 ,查询到姓名 " + name); diff --git a/codes/java/chapter_hashing/hash_map.java b/codes/java/chapter_hashing/hash_map.java index 469cf5a5d..876bb87f6 100644 --- a/codes/java/chapter_hashing/hash_map.java +++ b/codes/java/chapter_hashing/hash_map.java @@ -25,7 +25,7 @@ public class hash_map { PrintUtil.printHashMap(map); /* 查询操作 */ - // 向哈希表输入键 key ,得到值 value + // 向哈希表中输入键 key ,得到值 value String name = map.get(15937); System.out.println("\n输入学号 15937 ,查询到姓名 " + name); diff --git a/codes/java/chapter_hashing/hash_map_chaining.java b/codes/java/chapter_hashing/hash_map_chaining.java index 9133ef02b..fb2831376 100644 --- a/codes/java/chapter_hashing/hash_map_chaining.java +++ b/codes/java/chapter_hashing/hash_map_chaining.java @@ -43,13 +43,13 @@ class HashMapChaining { String get(int key) { int index = hashFunc(key); List bucket = buckets.get(index); - // 遍历桶,若找到 key 则返回对应 val + // 遍历桶,若找到 key ,则返回对应 val for (Pair pair : bucket) { if (pair.key == key) { return pair.val; } } - // 若未找到 key 则返回 null + // 若未找到 key ,则返回 null return null; } @@ -135,7 +135,7 @@ public class hash_map_chaining { map.print(); /* 查询操作 */ - // 向哈希表输入键 key ,得到值 value + // 向哈希表中输入键 key ,得到值 value String name = map.get(13276); System.out.println("\n输入学号 13276 ,查询到姓名 " + name); diff --git a/codes/java/chapter_hashing/hash_map_open_addressing.java b/codes/java/chapter_hashing/hash_map_open_addressing.java index 4e1534b58..d6d078c09 100644 --- a/codes/java/chapter_hashing/hash_map_open_addressing.java +++ b/codes/java/chapter_hashing/hash_map_open_addressing.java @@ -37,9 +37,9 @@ class HashMapOpenAddressing { int firstTombstone = -1; // 线性探测,当遇到空桶时跳出 while (buckets[index] != null) { - // 若遇到 key ,返回对应桶索引 + // 若遇到 key ,返回对应的桶索引 if (buckets[index].key == key) { - // 若之前遇到了删除标记,则将键值对移动至该索引 + // 若之前遇到了删除标记,则将键值对移动至该索引处 if (firstTombstone != -1) { buckets[firstTombstone] = buckets[index]; buckets[index] = TOMBSTONE; @@ -51,7 +51,7 @@ class HashMapOpenAddressing { if (firstTombstone == -1 && buckets[index] == TOMBSTONE) { firstTombstone = index; } - // 计算桶索引,越过尾部返回头部 + // 计算桶索引,越过尾部则返回头部 index = (index + 1) % capacity; } // 若 key 不存在,则返回添加点的索引 @@ -145,7 +145,7 @@ public class hash_map_open_addressing { hashmap.print(); // 查询操作 - // 向哈希表输入键 key ,得到值 val + // 向哈希表中输入键 key ,得到值 val String name = hashmap.get(13276); System.out.println("\n输入学号 13276 ,查询到姓名 " + name); diff --git a/codes/java/chapter_heap/my_heap.java b/codes/java/chapter_heap/my_heap.java index 3ddce1f8c..a61bfe0ce 100644 --- a/codes/java/chapter_heap/my_heap.java +++ b/codes/java/chapter_heap/my_heap.java @@ -24,17 +24,17 @@ class MaxHeap { } } - /* 获取左子节点索引 */ + /* 获取左子节点的索引 */ private int left(int i) { return 2 * i + 1; } - /* 获取右子节点索引 */ + /* 获取右子节点的索引 */ private int right(int i) { return 2 * i + 2; } - /* 获取父节点索引 */ + /* 获取父节点的索引 */ private int parent(int i) { return (i - 1) / 2; // 向下整除 } diff --git a/codes/java/chapter_sorting/bubble_sort.java b/codes/java/chapter_sorting/bubble_sort.java index 86b05de56..007686b09 100644 --- a/codes/java/chapter_sorting/bubble_sort.java +++ b/codes/java/chapter_sorting/bubble_sort.java @@ -41,7 +41,7 @@ public class bubble_sort { } } if (!flag) - break; // 此轮冒泡未交换任何元素,直接跳出 + break; // 此轮“冒泡”未交换任何元素,直接跳出 } } diff --git a/codes/java/chapter_sorting/merge_sort.java b/codes/java/chapter_sorting/merge_sort.java index 51ba482c3..12cc86ce5 100644 --- a/codes/java/chapter_sorting/merge_sort.java +++ b/codes/java/chapter_sorting/merge_sort.java @@ -11,12 +11,12 @@ import java.util.*; public class merge_sort { /* 合并左子数组和右子数组 */ static void merge(int[] nums, int left, int mid, int right) { - // 左子数组区间 [left, mid], 右子数组区间 [mid+1, right] + // 左子数组区间为 [left, mid], 右子数组区间为 [mid+1, right] // 创建一个临时数组 tmp ,用于存放合并后的结果 int[] tmp = new int[right - left + 1]; // 初始化左子数组和右子数组的起始索引 int i = left, j = mid + 1, k = 0; - // 当左右子数组都还有元素时,比较并将较小的元素复制到临时数组中 + // 当左右子数组都还有元素时,进行比较并将较小的元素复制到临时数组中 while (i <= mid && j <= right) { if (nums[i] <= nums[j]) tmp[k++] = nums[i++]; diff --git a/codes/java/chapter_sorting/quick_sort.java b/codes/java/chapter_sorting/quick_sort.java index f51973082..84ad76c1e 100644 --- a/codes/java/chapter_sorting/quick_sort.java +++ b/codes/java/chapter_sorting/quick_sort.java @@ -54,7 +54,7 @@ class QuickSortMedian { nums[j] = tmp; } - /* 选取三个元素的中位数 */ + /* 选取三个候选元素的中位数 */ static int medianThree(int[] nums, int left, int mid, int right) { // 此处使用异或运算来简化代码 // 异或规则为 0 ^ 0 = 1 ^ 1 = 0, 0 ^ 1 = 1 ^ 0 = 1 diff --git a/codes/java/chapter_stack_and_queue/array_deque.java b/codes/java/chapter_stack_and_queue/array_deque.java index a2621e845..00a0234f9 100644 --- a/codes/java/chapter_stack_and_queue/array_deque.java +++ b/codes/java/chapter_stack_and_queue/array_deque.java @@ -50,7 +50,7 @@ class ArrayDeque { return; } // 队首指针向左移动一位 - // 通过取余操作,实现 front 越过数组头部后回到尾部 + // 通过取余操作实现 front 越过数组头部后回到尾部 front = index(front - 1); // 将 num 添加至队首 nums[front] = num; @@ -63,7 +63,7 @@ class ArrayDeque { System.out.println("双向队列已满"); return; } - // 计算尾指针,指向队尾索引 + 1 + // 计算队尾指针,指向队尾索引 + 1 int rear = index(front + queSize); // 将 num 添加至队尾 nums[rear] = num; diff --git a/codes/java/chapter_stack_and_queue/array_queue.java b/codes/java/chapter_stack_and_queue/array_queue.java index aa9922ebd..97411f7ae 100644 --- a/codes/java/chapter_stack_and_queue/array_queue.java +++ b/codes/java/chapter_stack_and_queue/array_queue.java @@ -40,8 +40,8 @@ class ArrayQueue { System.out.println("队列已满"); return; } - // 计算尾指针,指向队尾索引 + 1 - // 通过取余操作,实现 rear 越过数组尾部后回到头部 + // 计算队尾指针,指向队尾索引 + 1 + // 通过取余操作实现 rear 越过数组尾部后回到头部 int rear = (front + queSize) % capacity(); // 将 num 添加至队尾 nums[rear] = num; @@ -51,7 +51,7 @@ class ArrayQueue { /* 出队 */ public int pop() { int num = peek(); - // 队首指针向后移动一位,若越过尾部则返回到数组头部 + // 队首指针向后移动一位,若越过尾部,则返回到数组头部 front = (front + 1) % capacity(); queSize--; return num; diff --git a/codes/java/chapter_stack_and_queue/linkedlist_queue.java b/codes/java/chapter_stack_and_queue/linkedlist_queue.java index fafde978a..94ffa984d 100644 --- a/codes/java/chapter_stack_and_queue/linkedlist_queue.java +++ b/codes/java/chapter_stack_and_queue/linkedlist_queue.java @@ -30,7 +30,7 @@ class LinkedListQueue { /* 入队 */ public void push(int num) { - // 尾节点后添加 num + // 在尾节点后添加 num ListNode node = new ListNode(num); // 如果队列为空,则令头、尾节点都指向该节点 if (front == null) { diff --git a/codes/java/chapter_tree/avl_tree.java b/codes/java/chapter_tree/avl_tree.java index f8bbb4248..b7e043ace 100644 --- a/codes/java/chapter_tree/avl_tree.java +++ b/codes/java/chapter_tree/avl_tree.java @@ -100,7 +100,7 @@ class AVLTree { private TreeNode insertHelper(TreeNode node, int val) { if (node == null) return new TreeNode(val); - /* 1. 查找插入位置,并插入节点 */ + /* 1. 查找插入位置并插入节点 */ if (val < node.val) node.left = insertHelper(node.left, val); else if (val > node.val) @@ -123,7 +123,7 @@ class AVLTree { private TreeNode removeHelper(TreeNode node, int val) { if (node == null) return null; - /* 1. 查找节点,并删除之 */ + /* 1. 查找节点并删除 */ if (val < node.val) node.left = removeHelper(node.left, val); else if (val > node.val) diff --git a/codes/javascript/chapter_array_and_linkedlist/my_list.js b/codes/javascript/chapter_array_and_linkedlist/my_list.js index 80c1029ba..fb1f264ba 100644 --- a/codes/javascript/chapter_array_and_linkedlist/my_list.js +++ b/codes/javascript/chapter_array_and_linkedlist/my_list.js @@ -28,7 +28,7 @@ class MyList { /* 访问元素 */ get(index) { - // 索引如果越界则抛出异常,下同 + // 索引如果越界,则抛出异常,下同 if (index < 0 || index >= this.#size) throw new Error('索引越界'); return this.#arr[index]; } @@ -76,13 +76,13 @@ class MyList { } // 更新元素数量 this.#size--; - // 返回被删除元素 + // 返回被删除的元素 return num; } /* 列表扩容 */ extendCapacity() { - // 新建一个长度为原数组 extendRatio 倍的新数组,并将原数组拷贝到新数组 + // 新建一个长度为原数组 extendRatio 倍的新数组,并将原数组复制到新数组 this.#arr = this.#arr.concat( new Array(this.capacity() * (this.#extendRatio - 1)) ); diff --git a/codes/javascript/chapter_backtracking/n_queens.js b/codes/javascript/chapter_backtracking/n_queens.js index a05a9ee63..0d22cccbd 100644 --- a/codes/javascript/chapter_backtracking/n_queens.js +++ b/codes/javascript/chapter_backtracking/n_queens.js @@ -13,10 +13,10 @@ function backtrack(row, n, state, res, cols, diags1, diags2) { } // 遍历所有列 for (let col = 0; col < n; col++) { - // 计算该格子对应的主对角线和副对角线 + // 计算该格子对应的主对角线和次对角线 const diag1 = row - col + n - 1; const diag2 = row + col; - // 剪枝:不允许该格子所在列、主对角线、副对角线上存在皇后 + // 剪枝:不允许该格子所在列、主对角线、次对角线上存在皇后 if (!cols[col] && !diags1[diag1] && !diags2[diag2]) { // 尝试:将皇后放置在该格子 state[row][col] = 'Q'; @@ -36,7 +36,7 @@ function nQueens(n) { const state = Array.from({ length: n }, () => Array(n).fill('#')); const cols = Array(n).fill(false); // 记录列是否有皇后 const diags1 = Array(2 * n - 1).fill(false); // 记录主对角线上是否有皇后 - const diags2 = Array(2 * n - 1).fill(false); // 记录副对角线上是否有皇后 + const diags2 = Array(2 * n - 1).fill(false); // 记录次对角线上是否有皇后 const res = []; backtrack(0, n, state, res, cols, diags1, diags2); diff --git a/codes/javascript/chapter_graph/graph_bfs.js b/codes/javascript/chapter_graph/graph_bfs.js index 1fa9b5210..83d14495d 100644 --- a/codes/javascript/chapter_graph/graph_bfs.js +++ b/codes/javascript/chapter_graph/graph_bfs.js @@ -7,7 +7,7 @@ const { GraphAdjList } = require('./graph_adjacency_list'); const { Vertex } = require('../modules/Vertex'); -/* 广度优先遍历 BFS */ +/* 广度优先遍历 */ // 使用邻接表来表示图,以便获取指定顶点的所有邻接顶点 function graphBFS(graph, startVet) { // 顶点遍历序列 @@ -55,7 +55,7 @@ const graph = new GraphAdjList(edges); console.log('\n初始化后,图为'); graph.print(); -/* 广度优先遍历 BFS */ +/* 广度优先遍历 */ const res = graphBFS(graph, v[0]); console.log('\n广度优先遍历(BFS)顶点序列为'); console.log(Vertex.vetsToVals(res)); diff --git a/codes/javascript/chapter_graph/graph_dfs.js b/codes/javascript/chapter_graph/graph_dfs.js index 6078819aa..0b8796aeb 100644 --- a/codes/javascript/chapter_graph/graph_dfs.js +++ b/codes/javascript/chapter_graph/graph_dfs.js @@ -7,7 +7,7 @@ const { Vertex } = require('../modules/Vertex'); const { GraphAdjList } = require('./graph_adjacency_list'); -/* 深度优先遍历 DFS */ +/* 深度优先遍历 */ // 使用邻接表来表示图,以便获取指定顶点的所有邻接顶点 function dfs(graph, visited, res, vet) { res.push(vet); // 记录访问顶点 @@ -22,7 +22,7 @@ function dfs(graph, visited, res, vet) { } } -/* 深度优先遍历 DFS */ +/* 深度优先遍历 */ // 使用邻接表来表示图,以便获取指定顶点的所有邻接顶点 function graphDFS(graph, startVet) { // 顶点遍历序列 @@ -48,7 +48,7 @@ const graph = new GraphAdjList(edges); console.log('\n初始化后,图为'); graph.print(); -/* 深度优先遍历 DFS */ +/* 深度优先遍历 */ const res = graphDFS(graph, v[0]); console.log('\n深度优先遍历(DFS)顶点序列为'); console.log(Vertex.vetsToVals(res)); diff --git a/codes/javascript/chapter_greedy/max_capacity.js b/codes/javascript/chapter_greedy/max_capacity.js index 95461bfe9..5f13b391c 100644 --- a/codes/javascript/chapter_greedy/max_capacity.js +++ b/codes/javascript/chapter_greedy/max_capacity.js @@ -6,7 +6,7 @@ /* 最大容量:贪心 */ function maxCapacity(ht) { - // 初始化 i, j 分列数组两端 + // 初始化 i, j,使其分列数组两端 let i = 0, j = ht.length - 1; // 初始最大容量为 0 diff --git a/codes/javascript/chapter_hashing/array_hash_map.js b/codes/javascript/chapter_hashing/array_hash_map.js index 324084789..9d0236b5b 100644 --- a/codes/javascript/chapter_hashing/array_hash_map.js +++ b/codes/javascript/chapter_hashing/array_hash_map.js @@ -102,7 +102,7 @@ console.info('\n添加完成后,哈希表为\nKey -> Value'); map.print(); /* 查询操作 */ -// 向哈希表输入键 key ,得到值 value +// 向哈希表中输入键 key ,得到值 value let name = map.get(15937); console.info('\n输入学号 15937 ,查询到姓名 ' + name); diff --git a/codes/javascript/chapter_hashing/hash_map.js b/codes/javascript/chapter_hashing/hash_map.js index 9157b3159..ed03b7bf9 100644 --- a/codes/javascript/chapter_hashing/hash_map.js +++ b/codes/javascript/chapter_hashing/hash_map.js @@ -19,7 +19,7 @@ console.info('\n添加完成后,哈希表为\nKey -> Value'); console.info(map); /* 查询操作 */ -// 向哈希表输入键 key ,得到值 value +// 向哈希表中输入键 key ,得到值 value let name = map.get(15937); console.info('\n输入学号 15937 ,查询到姓名 ' + name); diff --git a/codes/javascript/chapter_hashing/hash_map_chaining.js b/codes/javascript/chapter_hashing/hash_map_chaining.js index 6f1276f15..aa4a396c9 100644 --- a/codes/javascript/chapter_hashing/hash_map_chaining.js +++ b/codes/javascript/chapter_hashing/hash_map_chaining.js @@ -43,13 +43,13 @@ class HashMapChaining { get(key) { const index = this.#hashFunc(key); const bucket = this.#buckets[index]; - // 遍历桶,若找到 key 则返回对应 val + // 遍历桶,若找到 key ,则返回对应 val for (const pair of bucket) { if (pair.key === key) { return pair.val; } } - // 若未找到 key 则返回 null + // 若未找到 key ,则返回 null return null; } @@ -131,7 +131,7 @@ console.log('\n添加完成后,哈希表为\nKey -> Value'); map.print(); /* 查询操作 */ -// 向哈希表输入键 key ,得到值 value +// 向哈希表中输入键 key ,得到值 value const name = map.get(13276); console.log('\n输入学号 13276 ,查询到姓名 ' + name); diff --git a/codes/javascript/chapter_hashing/hash_map_open_addressing.js b/codes/javascript/chapter_hashing/hash_map_open_addressing.js index e9a0a3a57..f71ea0ae8 100644 --- a/codes/javascript/chapter_hashing/hash_map_open_addressing.js +++ b/codes/javascript/chapter_hashing/hash_map_open_addressing.js @@ -47,9 +47,9 @@ class HashMapOpenAddressing { let firstTombstone = -1; // 线性探测,当遇到空桶时跳出 while (this.#buckets[index] !== null) { - // 若遇到 key ,返回对应桶索引 + // 若遇到 key ,返回对应的桶索引 if (this.#buckets[index].key === key) { - // 若之前遇到了删除标记,则将键值对移动至该索引 + // 若之前遇到了删除标记,则将键值对移动至该索引处 if (firstTombstone !== -1) { this.#buckets[firstTombstone] = this.#buckets[index]; this.#buckets[index] = this.#TOMBSTONE; @@ -64,7 +64,7 @@ class HashMapOpenAddressing { ) { firstTombstone = index; } - // 计算桶索引,越过尾部返回头部 + // 计算桶索引,越过尾部则返回头部 index = (index + 1) % this.#capacity; } // 若 key 不存在,则返回添加点的索引 @@ -166,7 +166,7 @@ console.log('\n添加完成后,哈希表为\nKey -> Value'); hashmap.print(); // 查询操作 -// 向哈希表输入键 key ,得到值 val +// 向哈希表中输入键 key ,得到值 val const name = hashmap.get(13276); console.log('\n输入学号 13276 ,查询到姓名 ' + name); diff --git a/codes/javascript/chapter_heap/my_heap.js b/codes/javascript/chapter_heap/my_heap.js index 644646eab..42c6a0ccb 100644 --- a/codes/javascript/chapter_heap/my_heap.js +++ b/codes/javascript/chapter_heap/my_heap.js @@ -20,17 +20,17 @@ class MaxHeap { } } - /* 获取左子节点索引 */ + /* 获取左子节点的索引 */ #left(i) { return 2 * i + 1; } - /* 获取右子节点索引 */ + /* 获取右子节点的索引 */ #right(i) { return 2 * i + 2; } - /* 获取父节点索引 */ + /* 获取父节点的索引 */ #parent(i) { return Math.floor((i - 1) / 2); // 向下整除 } diff --git a/codes/javascript/chapter_sorting/bubble_sort.js b/codes/javascript/chapter_sorting/bubble_sort.js index 2c9526962..3d6871777 100644 --- a/codes/javascript/chapter_sorting/bubble_sort.js +++ b/codes/javascript/chapter_sorting/bubble_sort.js @@ -35,7 +35,7 @@ function bubbleSortWithFlag(nums) { flag = true; // 记录交换元素 } } - if (!flag) break; // 此轮冒泡未交换任何元素,直接跳出 + if (!flag) break; // 此轮“冒泡”未交换任何元素,直接跳出 } } diff --git a/codes/javascript/chapter_sorting/merge_sort.js b/codes/javascript/chapter_sorting/merge_sort.js index 55c4d8cc7..033df1b8b 100644 --- a/codes/javascript/chapter_sorting/merge_sort.js +++ b/codes/javascript/chapter_sorting/merge_sort.js @@ -6,14 +6,14 @@ /* 合并左子数组和右子数组 */ function merge(nums, left, mid, right) { - // 左子数组区间 [left, mid], 右子数组区间 [mid+1, right] + // 左子数组区间为 [left, mid], 右子数组区间为 [mid+1, right] // 创建一个临时数组 tmp ,用于存放合并后的结果 const tmp = new Array(right - left + 1); // 初始化左子数组和右子数组的起始索引 let i = left, j = mid + 1, k = 0; - // 当左右子数组都还有元素时,比较并将较小的元素复制到临时数组中 + // 当左右子数组都还有元素时,进行比较并将较小的元素复制到临时数组中 while (i <= mid && j <= right) { if (nums[i] <= nums[j]) { tmp[k++] = nums[i++]; diff --git a/codes/javascript/chapter_sorting/quick_sort.js b/codes/javascript/chapter_sorting/quick_sort.js index 6b383e6d6..8e1622053 100644 --- a/codes/javascript/chapter_sorting/quick_sort.js +++ b/codes/javascript/chapter_sorting/quick_sort.js @@ -53,7 +53,7 @@ class QuickSortMedian { nums[j] = tmp; } - /* 选取三个元素的中位数 */ + /* 选取三个候选元素的中位数 */ medianThree(nums, left, mid, right) { // 此处使用异或运算来简化代码 // 异或规则为 0 ^ 0 = 1 ^ 1 = 0, 0 ^ 1 = 1 ^ 0 = 1 diff --git a/codes/javascript/chapter_stack_and_queue/array_deque.js b/codes/javascript/chapter_stack_and_queue/array_deque.js index 13f2b13e5..0233c3a8f 100644 --- a/codes/javascript/chapter_stack_and_queue/array_deque.js +++ b/codes/javascript/chapter_stack_and_queue/array_deque.js @@ -47,7 +47,7 @@ class ArrayDeque { return; } // 队首指针向左移动一位 - // 通过取余操作,实现 front 越过数组头部后回到尾部 + // 通过取余操作实现 front 越过数组头部后回到尾部 this.#front = this.index(this.#front - 1); // 将 num 添加至队首 this.#nums[this.#front] = num; @@ -60,7 +60,7 @@ class ArrayDeque { console.log('双向队列已满'); return; } - // 计算尾指针,指向队尾索引 + 1 + // 计算队尾指针,指向队尾索引 + 1 const rear = this.index(this.#front + this.#queSize); // 将 num 添加至队尾 this.#nums[rear] = num; diff --git a/codes/javascript/chapter_stack_and_queue/array_queue.js b/codes/javascript/chapter_stack_and_queue/array_queue.js index 4e05de13a..85f2500b6 100644 --- a/codes/javascript/chapter_stack_and_queue/array_queue.js +++ b/codes/javascript/chapter_stack_and_queue/array_queue.js @@ -35,8 +35,8 @@ class ArrayQueue { console.log('队列已满'); return; } - // 计算尾指针,指向队尾索引 + 1 - // 通过取余操作,实现 rear 越过数组尾部后回到头部 + // 计算队尾指针,指向队尾索引 + 1 + // 通过取余操作实现 rear 越过数组尾部后回到头部 const rear = (this.#front + this.size) % this.capacity; // 将 num 添加至队尾 this.#nums[rear] = num; @@ -46,7 +46,7 @@ class ArrayQueue { /* 出队 */ pop() { const num = this.peek(); - // 队首指针向后移动一位,若越过尾部则返回到数组头部 + // 队首指针向后移动一位,若越过尾部,则返回到数组头部 this.#front = (this.#front + 1) % this.capacity; this.#queSize--; return num; diff --git a/codes/javascript/chapter_stack_and_queue/linkedlist_queue.js b/codes/javascript/chapter_stack_and_queue/linkedlist_queue.js index 5fc77bf72..75dcf65d3 100644 --- a/codes/javascript/chapter_stack_and_queue/linkedlist_queue.js +++ b/codes/javascript/chapter_stack_and_queue/linkedlist_queue.js @@ -29,7 +29,7 @@ class LinkedListQueue { /* 入队 */ push(num) { - // 尾节点后添加 num + // 在尾节点后添加 num const node = new ListNode(num); // 如果队列为空,则令头、尾节点都指向该节点 if (!this.#front) { diff --git a/codes/javascript/chapter_tree/avl_tree.js b/codes/javascript/chapter_tree/avl_tree.js index eb33933f1..33dc0766f 100644 --- a/codes/javascript/chapter_tree/avl_tree.js +++ b/codes/javascript/chapter_tree/avl_tree.js @@ -101,7 +101,7 @@ class AVLTree { /* 递归插入节点(辅助方法) */ #insertHelper(node, val) { if (node === null) return new TreeNode(val); - /* 1. 查找插入位置,并插入节点 */ + /* 1. 查找插入位置并插入节点 */ if (val < node.val) node.left = this.#insertHelper(node.left, val); else if (val > node.val) node.right = this.#insertHelper(node.right, val); @@ -121,7 +121,7 @@ class AVLTree { /* 递归删除节点(辅助方法) */ #removeHelper(node, val) { if (node === null) return null; - /* 1. 查找节点,并删除之 */ + /* 1. 查找节点并删除 */ if (val < node.val) node.left = this.#removeHelper(node.left, val); else if (val > node.val) node.right = this.#removeHelper(node.right, val); diff --git a/codes/python/chapter_array_and_linkedlist/my_list.py b/codes/python/chapter_array_and_linkedlist/my_list.py index b64dcd0e7..5dec9e9be 100644 --- a/codes/python/chapter_array_and_linkedlist/my_list.py +++ b/codes/python/chapter_array_and_linkedlist/my_list.py @@ -25,7 +25,7 @@ class MyList: def get(self, index: int) -> int: """访问元素""" - # 索引如果越界则抛出异常,下同 + # 索引如果越界,则抛出异常,下同 if index < 0 or index >= self._size: raise IndexError("索引越界") return self._arr[index] @@ -68,12 +68,12 @@ class MyList: self._arr[j] = self._arr[j + 1] # 更新元素数量 self._size -= 1 - # 返回被删除元素 + # 返回被删除的元素 return num def extend_capacity(self): """列表扩容""" - # 新建一个长度为原数组 __extend_ratio 倍的新数组,并将原数组拷贝到新数组 + # 新建一个长度为原数组 _extend_ratio 倍的新数组,并将原数组复制到新数组 self._arr = self._arr + [0] * self.capacity() * (self._extend_ratio - 1) # 更新列表容量 self._capacity = len(self._arr) diff --git a/codes/python/chapter_backtracking/n_queens.py b/codes/python/chapter_backtracking/n_queens.py index ab7fb3799..60fde644f 100644 --- a/codes/python/chapter_backtracking/n_queens.py +++ b/codes/python/chapter_backtracking/n_queens.py @@ -21,10 +21,10 @@ def backtrack( return # 遍历所有列 for col in range(n): - # 计算该格子对应的主对角线和副对角线 + # 计算该格子对应的主对角线和次对角线 diag1 = row - col + n - 1 diag2 = row + col - # 剪枝:不允许该格子所在列、主对角线、副对角线上存在皇后 + # 剪枝:不允许该格子所在列、主对角线、次对角线上存在皇后 if not cols[col] and not diags1[diag1] and not diags2[diag2]: # 尝试:将皇后放置在该格子 state[row][col] = "Q" @@ -42,7 +42,7 @@ def n_queens(n: int) -> list[list[list[str]]]: state = [["#" for _ in range(n)] for _ in range(n)] cols = [False] * n # 记录列是否有皇后 diags1 = [False] * (2 * n - 1) # 记录主对角线上是否有皇后 - diags2 = [False] * (2 * n - 1) # 记录副对角线上是否有皇后 + diags2 = [False] * (2 * n - 1) # 记录次对角线上是否有皇后 res = [] backtrack(0, n, state, res, cols, diags1, diags2) diff --git a/codes/python/chapter_graph/graph_bfs.py b/codes/python/chapter_graph/graph_bfs.py index 1109bc174..7ad6c8065 100644 --- a/codes/python/chapter_graph/graph_bfs.py +++ b/codes/python/chapter_graph/graph_bfs.py @@ -14,7 +14,7 @@ from graph_adjacency_list import GraphAdjList def graph_bfs(graph: GraphAdjList, start_vet: Vertex) -> list[Vertex]: - """广度优先遍历 BFS""" + """广度优先遍历""" # 使用邻接表来表示图,以便获取指定顶点的所有邻接顶点 # 顶点遍历序列 res = [] @@ -58,7 +58,7 @@ if __name__ == "__main__": print("\n初始化后,图为") graph.print() - # 广度优先遍历 BFS + # 广度优先遍历 res = graph_bfs(graph, v[0]) print("\n广度优先遍历(BFS)顶点序列为") print(vets_to_vals(res)) diff --git a/codes/python/chapter_graph/graph_dfs.py b/codes/python/chapter_graph/graph_dfs.py index a065bf8fb..920dcdad0 100644 --- a/codes/python/chapter_graph/graph_dfs.py +++ b/codes/python/chapter_graph/graph_dfs.py @@ -13,7 +13,7 @@ from graph_adjacency_list import GraphAdjList def dfs(graph: GraphAdjList, visited: set[Vertex], res: list[Vertex], vet: Vertex): - """深度优先遍历 DFS 辅助函数""" + """深度优先遍历辅助函数""" res.append(vet) # 记录访问顶点 visited.add(vet) # 标记该顶点已被访问 # 遍历该顶点的所有邻接顶点 @@ -25,7 +25,7 @@ def dfs(graph: GraphAdjList, visited: set[Vertex], res: list[Vertex], vet: Verte def graph_dfs(graph: GraphAdjList, start_vet: Vertex) -> list[Vertex]: - """深度优先遍历 DFS""" + """深度优先遍历""" # 使用邻接表来表示图,以便获取指定顶点的所有邻接顶点 # 顶点遍历序列 res = [] @@ -51,7 +51,7 @@ if __name__ == "__main__": print("\n初始化后,图为") graph.print() - # 深度优先遍历 DFS + # 深度优先遍历 res = graph_dfs(graph, v[0]) print("\n深度优先遍历(DFS)顶点序列为") print(vets_to_vals(res)) diff --git a/codes/python/chapter_greedy/max_capacity.py b/codes/python/chapter_greedy/max_capacity.py index 12377b408..22cbd8de4 100644 --- a/codes/python/chapter_greedy/max_capacity.py +++ b/codes/python/chapter_greedy/max_capacity.py @@ -7,7 +7,7 @@ Author: Krahets (krahets@163.com) def max_capacity(ht: list[int]) -> int: """最大容量:贪心""" - # 初始化 i, j 分列数组两端 + # 初始化 i, j,使其分列数组两端 i, j = 0, len(ht) - 1 # 初始最大容量为 0 res = 0 diff --git a/codes/python/chapter_hashing/array_hash_map.py b/codes/python/chapter_hashing/array_hash_map.py index d4217ee43..945f44bb0 100644 --- a/codes/python/chapter_hashing/array_hash_map.py +++ b/codes/python/chapter_hashing/array_hash_map.py @@ -93,7 +93,7 @@ if __name__ == "__main__": hmap.print() # 查询操作 - # 向哈希表输入键 key ,得到值 value + # 向哈希表中输入键 key ,得到值 value name = hmap.get(15937) print("\n输入学号 15937 ,查询到姓名 " + name) diff --git a/codes/python/chapter_hashing/hash_map.py b/codes/python/chapter_hashing/hash_map.py index c1b1472c0..7a9fe810d 100644 --- a/codes/python/chapter_hashing/hash_map.py +++ b/codes/python/chapter_hashing/hash_map.py @@ -26,7 +26,7 @@ if __name__ == "__main__": print_dict(hmap) # 查询操作 - # 向哈希表输入键 key ,得到值 value + # 向哈希表中输入键 key ,得到值 value name: str = hmap[15937] print("\n输入学号 15937 ,查询到姓名 " + name) diff --git a/codes/python/chapter_hashing/hash_map_chaining.py b/codes/python/chapter_hashing/hash_map_chaining.py index 964de79e9..89faa77ca 100644 --- a/codes/python/chapter_hashing/hash_map_chaining.py +++ b/codes/python/chapter_hashing/hash_map_chaining.py @@ -34,11 +34,11 @@ class HashMapChaining: """查询操作""" index = self.hash_func(key) bucket = self.buckets[index] - # 遍历桶,若找到 key 则返回对应 val + # 遍历桶,若找到 key ,则返回对应 val for pair in bucket: if pair.key == key: return pair.val - # 若未找到 key 则返回 None + # 若未找到 key ,则返回 None return None def put(self, key: int, val: str): @@ -107,7 +107,7 @@ if __name__ == "__main__": hashmap.print() # 查询操作 - # 向哈希表输入键 key ,得到值 value + # 向哈希表中输入键 key ,得到值 value name = hashmap.get(13276) print("\n输入学号 13276 ,查询到姓名 " + name) diff --git a/codes/python/chapter_hashing/hash_map_open_addressing.py b/codes/python/chapter_hashing/hash_map_open_addressing.py index 31d46e3d4..6cfb5b1f1 100644 --- a/codes/python/chapter_hashing/hash_map_open_addressing.py +++ b/codes/python/chapter_hashing/hash_map_open_addressing.py @@ -37,9 +37,9 @@ class HashMapOpenAddressing: first_tombstone = -1 # 线性探测,当遇到空桶时跳出 while self.buckets[index] is not None: - # 若遇到 key ,返回对应桶索引 + # 若遇到 key ,返回对应的桶索引 if self.buckets[index].key == key: - # 若之前遇到了删除标记,则将键值对移动至该索引 + # 若之前遇到了删除标记,则将键值对移动至该索引处 if first_tombstone != -1: self.buckets[first_tombstone] = self.buckets[index] self.buckets[index] = self.TOMBSTONE @@ -48,7 +48,7 @@ class HashMapOpenAddressing: # 记录遇到的首个删除标记 if first_tombstone == -1 and self.buckets[index] is self.TOMBSTONE: first_tombstone = index - # 计算桶索引,越过尾部返回头部 + # 计算桶索引,越过尾部则返回头部 index = (index + 1) % self.capacity # 若 key 不存在,则返回添加点的索引 return index if first_tombstone == -1 else first_tombstone @@ -127,7 +127,7 @@ if __name__ == "__main__": hashmap.print() # 查询操作 - # 向哈希表输入键 key ,得到值 val + # 向哈希表中输入键 key ,得到值 val name = hashmap.get(13276) print("\n输入学号 13276 ,查询到姓名 " + name) diff --git a/codes/python/chapter_heap/my_heap.py b/codes/python/chapter_heap/my_heap.py index 2d7e3d92d..e346b6f00 100644 --- a/codes/python/chapter_heap/my_heap.py +++ b/codes/python/chapter_heap/my_heap.py @@ -23,15 +23,15 @@ class MaxHeap: self.sift_down(i) def left(self, i: int) -> int: - """获取左子节点索引""" + """获取左子节点的索引""" return 2 * i + 1 def right(self, i: int) -> int: - """获取右子节点索引""" + """获取右子节点的索引""" return 2 * i + 2 def parent(self, i: int) -> int: - """获取父节点索引""" + """获取父节点的索引""" return (i - 1) // 2 # 向下整除 def swap(self, i: int, j: int): diff --git a/codes/python/chapter_sorting/bubble_sort.py b/codes/python/chapter_sorting/bubble_sort.py index 238444e2b..775298808 100644 --- a/codes/python/chapter_sorting/bubble_sort.py +++ b/codes/python/chapter_sorting/bubble_sort.py @@ -30,7 +30,7 @@ def bubble_sort_with_flag(nums: list[int]): nums[j], nums[j + 1] = nums[j + 1], nums[j] flag = True # 记录交换元素 if not flag: - break # 此轮冒泡未交换任何元素,直接跳出 + break # 此轮“冒泡”未交换任何元素,直接跳出 """Driver Code""" diff --git a/codes/python/chapter_sorting/merge_sort.py b/codes/python/chapter_sorting/merge_sort.py index 33dc93af3..9d2b430d7 100644 --- a/codes/python/chapter_sorting/merge_sort.py +++ b/codes/python/chapter_sorting/merge_sort.py @@ -7,12 +7,12 @@ Author: timi (xisunyy@163.com), Krahets (krahets@163.com) def merge(nums: list[int], left: int, mid: int, right: int): """合并左子数组和右子数组""" - # 左子数组区间 [left, mid], 右子数组区间 [mid+1, right] + # 左子数组区间为 [left, mid], 右子数组区间为 [mid+1, right] # 创建一个临时数组 tmp ,用于存放合并后的结果 tmp = [0] * (right - left + 1) # 初始化左子数组和右子数组的起始索引 i, j, k = left, mid + 1, 0 - # 当左右子数组都还有元素时,比较并将较小的元素复制到临时数组中 + # 当左右子数组都还有元素时,进行比较并将较小的元素复制到临时数组中 while i <= mid and j <= right: if nums[i] <= nums[j]: tmp[k] = nums[i] diff --git a/codes/python/chapter_sorting/quick_sort.py b/codes/python/chapter_sorting/quick_sort.py index 08be3936b..e2c73dc76 100644 --- a/codes/python/chapter_sorting/quick_sort.py +++ b/codes/python/chapter_sorting/quick_sort.py @@ -39,7 +39,7 @@ class QuickSortMedian: """快速排序类(中位基准数优化)""" def median_three(self, nums: list[int], left: int, mid: int, right: int) -> int: - """选取三个元素的中位数""" + """选取三个候选元素的中位数""" # 此处使用异或运算来简化代码 # 异或规则为 0 ^ 0 = 1 ^ 1 = 0, 0 ^ 1 = 1 ^ 0 = 1 if (nums[left] < nums[mid]) ^ (nums[left] < nums[right]): diff --git a/codes/python/chapter_stack_and_queue/array_deque.py b/codes/python/chapter_stack_and_queue/array_deque.py index 308f8e79e..570f3b5a5 100644 --- a/codes/python/chapter_stack_and_queue/array_deque.py +++ b/codes/python/chapter_stack_and_queue/array_deque.py @@ -39,7 +39,7 @@ class ArrayDeque: print("双向队列已满") return # 队首指针向左移动一位 - # 通过取余操作,实现 front 越过数组头部后回到尾部 + # 通过取余操作实现 front 越过数组头部后回到尾部 self._front = self.index(self._front - 1) # 将 num 添加至队首 self._nums[self._front] = num @@ -50,7 +50,7 @@ class ArrayDeque: if self._size == self.capacity(): print("双向队列已满") return - # 计算尾指针,指向队尾索引 + 1 + # 计算队尾指针,指向队尾索引 + 1 rear = self.index(self._front + self._size) # 将 num 添加至队尾 self._nums[rear] = num diff --git a/codes/python/chapter_stack_and_queue/array_queue.py b/codes/python/chapter_stack_and_queue/array_queue.py index 73b773b10..c1d884d30 100644 --- a/codes/python/chapter_stack_and_queue/array_queue.py +++ b/codes/python/chapter_stack_and_queue/array_queue.py @@ -30,8 +30,8 @@ class ArrayQueue: """入队""" if self._size == self.capacity(): raise IndexError("队列已满") - # 计算尾指针,指向队尾索引 + 1 - # 通过取余操作,实现 rear 越过数组尾部后回到头部 + # 计算队尾指针,指向队尾索引 + 1 + # 通过取余操作实现 rear 越过数组尾部后回到头部 rear: int = (self._front + self._size) % self.capacity() # 将 num 添加至队尾 self._nums[rear] = num @@ -40,7 +40,7 @@ class ArrayQueue: def pop(self) -> int: """出队""" num: int = self.peek() - # 队首指针向后移动一位,若越过尾部则返回到数组头部 + # 队首指针向后移动一位,若越过尾部,则返回到数组头部 self._front = (self._front + 1) % self.capacity() self._size -= 1 return num diff --git a/codes/python/chapter_stack_and_queue/linkedlist_queue.py b/codes/python/chapter_stack_and_queue/linkedlist_queue.py index 42f227c1e..86171918c 100644 --- a/codes/python/chapter_stack_and_queue/linkedlist_queue.py +++ b/codes/python/chapter_stack_and_queue/linkedlist_queue.py @@ -30,7 +30,7 @@ class LinkedListQueue: def push(self, num: int): """入队""" - # 尾节点后添加 num + # 在尾节点后添加 num node = ListNode(num) # 如果队列为空,则令头、尾节点都指向该节点 if self._front is None: diff --git a/codes/python/chapter_tree/avl_tree.py b/codes/python/chapter_tree/avl_tree.py index 9fb3477df..eee6e2b6f 100644 --- a/codes/python/chapter_tree/avl_tree.py +++ b/codes/python/chapter_tree/avl_tree.py @@ -101,7 +101,7 @@ class AVLTree: """递归插入节点(辅助方法)""" if node is None: return TreeNode(val) - # 1. 查找插入位置,并插入节点 + # 1. 查找插入位置并插入节点 if val < node.val: node.left = self.insert_helper(node.left, val) elif val > node.val: @@ -122,7 +122,7 @@ class AVLTree: """递归删除节点(辅助方法)""" if node is None: return None - # 1. 查找节点,并删除之 + # 1. 查找节点并删除 if val < node.val: node.left = self.remove_helper(node.left, val) elif val > node.val: diff --git a/codes/rust/chapter_array_and_linkedlist/my_list.rs b/codes/rust/chapter_array_and_linkedlist/my_list.rs index 12097e172..41a75e7c0 100644 --- a/codes/rust/chapter_array_and_linkedlist/my_list.rs +++ b/codes/rust/chapter_array_and_linkedlist/my_list.rs @@ -41,7 +41,7 @@ impl MyList { /* 访问元素 */ pub fn get(&self, index: usize) -> i32 { - // 索引如果越界则抛出异常,下同 + // 索引如果越界,则抛出异常,下同 if index >= self.size {panic!("索引越界")}; return self.arr[index]; } @@ -89,13 +89,13 @@ impl MyList { } // 更新元素数量 self.size -= 1; - // 返回被删除元素 + // 返回被删除的元素 return num; } /* 列表扩容 */ pub fn extend_capacity(&mut self) { - // 新建一个长度为原数组 extend_ratio 倍的新数组,并将原数组拷贝到新数组 + // 新建一个长度为原数组 extend_ratio 倍的新数组,并将原数组复制到新数组 let new_capacity = self.capacity * self.extend_ratio; self.arr.resize(new_capacity, 0); // 更新列表容量 diff --git a/codes/rust/chapter_backtracking/n_queens.rs b/codes/rust/chapter_backtracking/n_queens.rs index 7309838ab..2a3bc0f9f 100644 --- a/codes/rust/chapter_backtracking/n_queens.rs +++ b/codes/rust/chapter_backtracking/n_queens.rs @@ -18,10 +18,10 @@ fn backtrack(row: usize, n: usize, state: &mut Vec>, res: &mut Vec Vec>> { } let mut cols = vec![false; n]; // 记录列是否有皇后 let mut diags1 = vec![false; 2 * n - 1]; // 记录主对角线上是否有皇后 - let mut diags2 = vec![false; 2 * n - 1]; // 记录副对角线上是否有皇后 + let mut diags2 = vec![false; 2 * n - 1]; // 记录次对角线上是否有皇后 let mut res: Vec>> = Vec::new(); backtrack(0, n, &mut state, &mut res, &mut cols, &mut diags1, &mut diags2); diff --git a/codes/rust/chapter_graph/graph_bfs.rs b/codes/rust/chapter_graph/graph_bfs.rs index f9492c595..44e70df15 100644 --- a/codes/rust/chapter_graph/graph_bfs.rs +++ b/codes/rust/chapter_graph/graph_bfs.rs @@ -10,7 +10,7 @@ use std::collections::{HashSet, VecDeque}; use graph_adjacency_list::GraphAdjList; use graph_adjacency_list::{Vertex, vets_to_vals, vals_to_vets}; -/* 广度优先遍历 BFS */ +/* 广度优先遍历 */ // 使用邻接表来表示图,以便获取指定顶点的所有邻接顶点 fn graph_bfs(graph: GraphAdjList, start_vet: Vertex) -> Vec { // 顶点遍历序列 @@ -62,7 +62,7 @@ fn main() { println!("\n初始化后,图为"); graph.print(); - /* 广度优先遍历 BFS */ + /* 广度优先遍历 */ let res = graph_bfs(graph, v[0]); println!("\n广度优先遍历(BFS)顶点序列为"); println!("{:?}", vets_to_vals(res)); diff --git a/codes/rust/chapter_graph/graph_dfs.rs b/codes/rust/chapter_graph/graph_dfs.rs index bc8462b6a..6d76fb3c0 100644 --- a/codes/rust/chapter_graph/graph_dfs.rs +++ b/codes/rust/chapter_graph/graph_dfs.rs @@ -10,7 +10,7 @@ use std::collections::HashSet; use graph_adjacency_list::GraphAdjList; use graph_adjacency_list::{Vertex, vets_to_vals, vals_to_vets}; -/* 深度优先遍历 DFS 辅助函数 */ +/* 深度优先遍历辅助函数 */ fn dfs(graph: &GraphAdjList, visited: &mut HashSet, res: &mut Vec, vet: Vertex) { res.push(vet); // 记录访问顶点 visited.insert(vet); // 标记该顶点已被访问 @@ -26,7 +26,7 @@ fn dfs(graph: &GraphAdjList, visited: &mut HashSet, res: &mut Vec Vec { // 顶点遍历序列 @@ -54,7 +54,7 @@ fn main() { println!("\n初始化后,图为"); graph.print(); - /* 深度优先遍历 DFS */ + /* 深度优先遍历 */ let res = graph_dfs(graph, v[0]); println!("\n深度优先遍历(DFS)顶点序列为"); println!("{:?}", vets_to_vals(res)); diff --git a/codes/rust/chapter_greedy/max_capacity.rs b/codes/rust/chapter_greedy/max_capacity.rs index 0d0ab348c..631022179 100644 --- a/codes/rust/chapter_greedy/max_capacity.rs +++ b/codes/rust/chapter_greedy/max_capacity.rs @@ -6,7 +6,7 @@ /* 最大容量:贪心 */ fn max_capacity(ht: &[i32]) -> i32 { - // 初始化 i, j 分列数组两端 + // 初始化 i, j,使其分列数组两端 let mut i = 0; let mut j = ht.len() - 1; // 初始最大容量为 0 diff --git a/codes/rust/chapter_hashing/array_hash_map.rs b/codes/rust/chapter_hashing/array_hash_map.rs index 19def3aad..63cd517f1 100644 --- a/codes/rust/chapter_hashing/array_hash_map.rs +++ b/codes/rust/chapter_hashing/array_hash_map.rs @@ -85,7 +85,7 @@ fn main() { map.print(); /* 查询操作 */ - // 向哈希表输入键 key ,得到值 value + // 向哈希表中输入键 key ,得到值 value let name = map.get(15937).unwrap(); println!("\n输入学号 15937 ,查询到姓名 {}", name); diff --git a/codes/rust/chapter_hashing/hash_map.rs b/codes/rust/chapter_hashing/hash_map.rs index 3fa2dc5fe..b2dd46490 100644 --- a/codes/rust/chapter_hashing/hash_map.rs +++ b/codes/rust/chapter_hashing/hash_map.rs @@ -24,7 +24,7 @@ pub fn main() { print_util::print_hash_map(&map); // 查询操作 - // 向哈希表输入键 key ,得到值 value + // 向哈希表中输入键 key ,得到值 value let name = map.get(&15937).copied().unwrap(); println!("\n输入学号 15937 ,查询到姓名 {name}"); diff --git a/codes/rust/chapter_hashing/hash_map_chaining.rs b/codes/rust/chapter_hashing/hash_map_chaining.rs index 93e84c498..3f43e5eb0 100644 --- a/codes/rust/chapter_hashing/hash_map_chaining.rs +++ b/codes/rust/chapter_hashing/hash_map_chaining.rs @@ -56,7 +56,7 @@ impl HashMapChaining { } } - // 若未找到 key 则返回 None + // 若未找到 key ,则返回 None None } @@ -122,14 +122,14 @@ impl HashMapChaining { let index = self.hash_func(key); let bucket = &self.buckets[index]; - // 遍历桶,若找到 key 则返回对应 val + // 遍历桶,若找到 key ,则返回对应 val for pair in bucket { if pair.key == key { return Some(&pair.val); } } - // 若未找到 key 则返回 None + // 若未找到 key ,则返回 None None } } @@ -150,7 +150,7 @@ pub fn main() { map.print(); /* 查询操作 */ - // 向哈希表输入键 key ,得到值 value + // 向哈希表中输入键 key ,得到值 value println!("\n输入学号 13276,查询到姓名 {}", match map.get(13276) { Some(value) => value, None => "Not a valid Key" diff --git a/codes/rust/chapter_hashing/hash_map_open_addressing.rs b/codes/rust/chapter_hashing/hash_map_open_addressing.rs index e9b9f025b..61578ce1a 100644 --- a/codes/rust/chapter_hashing/hash_map_open_addressing.rs +++ b/codes/rust/chapter_hashing/hash_map_open_addressing.rs @@ -64,7 +64,7 @@ impl HashMapOpenAddressing { if first_tombstone == -1 && self.buckets[index] == self.TOMBSTONE { first_tombstone = index as i32; } - // 计算桶索引,越过尾部返回头部 + // 计算桶索引,越过尾部则返回头部 index = (index + 1) % self.capacity; } // 若 key 不存在,则返回添加点的索引 @@ -163,7 +163,7 @@ fn main() { hashmap.print(); /* 查询操作 */ - // 向哈希表输入键 key ,得到值 val + // 向哈希表中输入键 key ,得到值 val let name = hashmap.get(13276).unwrap(); println!("\n输入学号 13276 ,查询到姓名 {}", name); diff --git a/codes/rust/chapter_heap/my_heap.rs b/codes/rust/chapter_heap/my_heap.rs index 08d95fb4d..0d0a9769f 100644 --- a/codes/rust/chapter_heap/my_heap.rs +++ b/codes/rust/chapter_heap/my_heap.rs @@ -24,17 +24,17 @@ impl MaxHeap { heap } - /* 获取左子节点索引 */ + /* 获取左子节点的索引 */ fn left(i: usize) -> usize { 2 * i + 1 } - /* 获取右子节点索引 */ + /* 获取右子节点的索引 */ fn right(i: usize) -> usize { 2 * i + 2 } - /* 获取父节点索引 */ + /* 获取父节点的索引 */ fn parent(i: usize) -> usize { (i - 1) / 2 // 向下整除 } diff --git a/codes/rust/chapter_sorting/bubble_sort.rs b/codes/rust/chapter_sorting/bubble_sort.rs index 97fb6d520..074c85e97 100644 --- a/codes/rust/chapter_sorting/bubble_sort.rs +++ b/codes/rust/chapter_sorting/bubble_sort.rs @@ -37,7 +37,7 @@ fn bubble_sort_with_flag(nums: &mut [i32]) { flag = true; // 记录交换元素 } } - if !flag {break}; // 此轮冒泡未交换任何元素,直接跳出 + if !flag {break}; // 此轮“冒泡”未交换任何元素,直接跳出 } } diff --git a/codes/rust/chapter_sorting/merge_sort.rs b/codes/rust/chapter_sorting/merge_sort.rs index b22c15e22..fa3f348e6 100644 --- a/codes/rust/chapter_sorting/merge_sort.rs +++ b/codes/rust/chapter_sorting/merge_sort.rs @@ -6,13 +6,13 @@ /* 合并左子数组和右子数组 */ fn merge(nums: &mut [i32], left: usize, mid: usize, right: usize) { - // 左子数组区间 [left, mid], 右子数组区间 [mid+1, right] + // 左子数组区间为 [left, mid], 右子数组区间为 [mid+1, right] // 创建一个临时数组 tmp ,用于存放合并后的结果 let tmp_size = right - left + 1; let mut tmp = vec![0; tmp_size]; // 初始化左子数组和右子数组的起始索引 let (mut i, mut j, mut k) = (left, mid + 1, 0); - // 当左右子数组都还有元素时,比较并将较小的元素复制到临时数组中 + // 当左右子数组都还有元素时,进行比较并将较小的元素复制到临时数组中 while i <= mid && j <= right { if nums[i] <= nums[j] { tmp[k] = nums[j]; diff --git a/codes/rust/chapter_sorting/quick_sort.rs b/codes/rust/chapter_sorting/quick_sort.rs index 7989777ee..509812ea0 100644 --- a/codes/rust/chapter_sorting/quick_sort.rs +++ b/codes/rust/chapter_sorting/quick_sort.rs @@ -44,7 +44,7 @@ impl QuickSort { struct QuickSortMedian; impl QuickSortMedian { - /* 选取三个元素的中位数 */ + /* 选取三个候选元素的中位数 */ fn median_three(nums: &mut [i32], left: usize, mid: usize, right: usize) -> usize { // 此处使用异或运算来简化代码 // 异或规则为 0 ^ 0 = 1 ^ 1 = 0, 0 ^ 1 = 1 ^ 0 = 1 diff --git a/codes/rust/chapter_stack_and_queue/array_deque.rs b/codes/rust/chapter_stack_and_queue/array_deque.rs index 7b0a0f0e2..d4335d12b 100644 --- a/codes/rust/chapter_stack_and_queue/array_deque.rs +++ b/codes/rust/chapter_stack_and_queue/array_deque.rs @@ -53,7 +53,7 @@ impl ArrayDeque { return } // 队首指针向左移动一位 - // 通过取余操作,实现 front 越过数组头部后回到尾部 + // 通过取余操作实现 front 越过数组头部后回到尾部 self.front = self.index(self.front as i32 - 1); // 将 num 添加至队首 self.nums[self.front] = num; @@ -66,7 +66,7 @@ impl ArrayDeque { println!("双向队列已满"); return } - // 计算尾指针,指向队尾索引 + 1 + // 计算队尾指针,指向队尾索引 + 1 let rear = self.index(self.front as i32 + self.que_size as i32); // 将 num 添加至队尾 self.nums[rear] = num; diff --git a/codes/rust/chapter_stack_and_queue/array_queue.rs b/codes/rust/chapter_stack_and_queue/array_queue.rs index 5481b1866..374ec7c97 100644 --- a/codes/rust/chapter_stack_and_queue/array_queue.rs +++ b/codes/rust/chapter_stack_and_queue/array_queue.rs @@ -44,8 +44,8 @@ impl ArrayQueue { println!("队列已满"); return; } - // 计算尾指针,指向队尾索引 + 1 - // 通过取余操作,实现 rear 越过数组尾部后回到头部 + // 计算队尾指针,指向队尾索引 + 1 + // 通过取余操作实现 rear 越过数组尾部后回到头部 let rear = (self.front + self.que_size) % self.que_capacity; // 将 num 添加至队尾 self.nums[rear as usize] = num; @@ -55,7 +55,7 @@ impl ArrayQueue { /* 出队 */ fn pop(&mut self) -> i32 { let num = self.peek(); - // 队首指针向后移动一位,若越过尾部则返回到数组头部 + // 队首指针向后移动一位,若越过尾部,则返回到数组头部 self.front = (self.front + 1) % self.que_capacity; self.que_size -= 1; num diff --git a/codes/rust/chapter_stack_and_queue/linkedlist_queue.rs b/codes/rust/chapter_stack_and_queue/linkedlist_queue.rs index cb4cefca0..08ad12c9d 100644 --- a/codes/rust/chapter_stack_and_queue/linkedlist_queue.rs +++ b/codes/rust/chapter_stack_and_queue/linkedlist_queue.rs @@ -39,7 +39,7 @@ impl LinkedListQueue { /* 入队 */ pub fn push(&mut self, num: T) { - // 尾节点后添加 num + // 在尾节点后添加 num let new_rear = ListNode::new(num); match self.rear.take() { // 如果队列不为空,则将该节点添加到尾节点后 diff --git a/codes/rust/chapter_tree/avl_tree.rs b/codes/rust/chapter_tree/avl_tree.rs index 3d62ae6b9..a342350dd 100644 --- a/codes/rust/chapter_tree/avl_tree.rs +++ b/codes/rust/chapter_tree/avl_tree.rs @@ -137,7 +137,7 @@ impl AVLTree { fn insert_helper(node: OptionTreeNodeRc, val: i32) -> OptionTreeNodeRc { match node { Some(mut node) => { - /* 1. 查找插入位置,并插入节点 */ + /* 1. 查找插入位置并插入节点 */ match { let node_val = node.borrow().val; node_val @@ -175,7 +175,7 @@ impl AVLTree { fn remove_helper(node: OptionTreeNodeRc, val: i32) -> OptionTreeNodeRc { match node { Some(mut node) => { - /* 1. 查找节点,并删除之 */ + /* 1. 查找节点并删除 */ if val < node.borrow().val { let left = node.borrow().left.clone(); node.borrow_mut().left = Self::remove_helper(left, val); diff --git a/codes/swift/chapter_array_and_linkedlist/my_list.swift b/codes/swift/chapter_array_and_linkedlist/my_list.swift index 7d2700743..d3b7806dc 100644 --- a/codes/swift/chapter_array_and_linkedlist/my_list.swift +++ b/codes/swift/chapter_array_and_linkedlist/my_list.swift @@ -85,13 +85,13 @@ class MyList { } // 更新元素数量 _size -= 1 - // 返回被删除元素 + // 返回被删除的元素 return num } /* 列表扩容 */ func extendCapacity() { - // 新建一个长度为原数组 extendRatio 倍的新数组,并将原数组拷贝到新数组 + // 新建一个长度为原数组 extendRatio 倍的新数组,并将原数组复制到新数组 arr = arr + Array(repeating: 0, count: _capacity * (extendRatio - 1)) // 更新列表容量 _capacity = arr.count diff --git a/codes/swift/chapter_backtracking/n_queens.swift b/codes/swift/chapter_backtracking/n_queens.swift index 5c395a975..99a377dfc 100644 --- a/codes/swift/chapter_backtracking/n_queens.swift +++ b/codes/swift/chapter_backtracking/n_queens.swift @@ -13,10 +13,10 @@ func backtrack(row: Int, n: Int, state: inout [[String]], res: inout [[[String]] } // 遍历所有列 for col in 0 ..< n { - // 计算该格子对应的主对角线和副对角线 + // 计算该格子对应的主对角线和次对角线 let diag1 = row - col + n - 1 let diag2 = row + col - // 剪枝:不允许该格子所在列、主对角线、副对角线上存在皇后 + // 剪枝:不允许该格子所在列、主对角线、次对角线上存在皇后 if !cols[col] && !diags1[diag1] && !diags2[diag2] { // 尝试:将皇后放置在该格子 state[row][col] = "Q" @@ -40,7 +40,7 @@ func nQueens(n: Int) -> [[[String]]] { var state = Array(repeating: Array(repeating: "#", count: n), count: n) var cols = Array(repeating: false, count: n) // 记录列是否有皇后 var diags1 = Array(repeating: false, count: 2 * n - 1) // 记录主对角线上是否有皇后 - var diags2 = Array(repeating: false, count: 2 * n - 1) // 记录副对角线上是否有皇后 + var diags2 = Array(repeating: false, count: 2 * n - 1) // 记录次对角线上是否有皇后 var res: [[[String]]] = [] backtrack(row: 0, n: n, state: &state, res: &res, cols: &cols, diags1: &diags1, diags2: &diags2) diff --git a/codes/swift/chapter_graph/graph_bfs.swift b/codes/swift/chapter_graph/graph_bfs.swift index ceb3789a6..c1a800bdd 100644 --- a/codes/swift/chapter_graph/graph_bfs.swift +++ b/codes/swift/chapter_graph/graph_bfs.swift @@ -7,7 +7,7 @@ import graph_adjacency_list_target import utils -/* 广度优先遍历 BFS */ +/* 广度优先遍历 */ // 使用邻接表来表示图,以便获取指定顶点的所有邻接顶点 func graphBFS(graph: GraphAdjList, startVet: Vertex) -> [Vertex] { // 顶点遍历序列 @@ -48,7 +48,7 @@ enum GraphBFS { print("\n初始化后,图为") graph.print() - /* 广度优先遍历 BFS */ + /* 广度优先遍历 */ let res = graphBFS(graph: graph, startVet: v[0]) print("\n广度优先遍历(BFS)顶点序列为") print(Vertex.vetsToVals(vets: res)) diff --git a/codes/swift/chapter_graph/graph_dfs.swift b/codes/swift/chapter_graph/graph_dfs.swift index d617254c7..aa08815a0 100644 --- a/codes/swift/chapter_graph/graph_dfs.swift +++ b/codes/swift/chapter_graph/graph_dfs.swift @@ -7,7 +7,7 @@ import graph_adjacency_list_target import utils -/* 深度优先遍历 DFS 辅助函数 */ +/* 深度优先遍历辅助函数 */ func dfs(graph: GraphAdjList, visited: inout Set, res: inout [Vertex], vet: Vertex) { res.append(vet) // 记录访问顶点 visited.insert(vet) // 标记该顶点已被访问 @@ -21,7 +21,7 @@ func dfs(graph: GraphAdjList, visited: inout Set, res: inout [Vertex], v } } -/* 深度优先遍历 DFS */ +/* 深度优先遍历 */ // 使用邻接表来表示图,以便获取指定顶点的所有邻接顶点 func graphDFS(graph: GraphAdjList, startVet: Vertex) -> [Vertex] { // 顶点遍历序列 @@ -46,7 +46,7 @@ enum GraphDFS { print("\n初始化后,图为") graph.print() - /* 深度优先遍历 DFS */ + /* 深度优先遍历 */ let res = graphDFS(graph: graph, startVet: v[0]) print("\n深度优先遍历(DFS)顶点序列为") print(Vertex.vetsToVals(vets: res)) diff --git a/codes/swift/chapter_greedy/max_capacity.swift b/codes/swift/chapter_greedy/max_capacity.swift index 0d75c2771..feedd38fc 100644 --- a/codes/swift/chapter_greedy/max_capacity.swift +++ b/codes/swift/chapter_greedy/max_capacity.swift @@ -6,7 +6,7 @@ /* 最大容量:贪心 */ func maxCapacity(ht: [Int]) -> Int { - // 初始化 i, j 分列数组两端 + // 初始化 i, j,使其分列数组两端 var i = 0, j = ht.count - 1 // 初始最大容量为 0 var res = 0 diff --git a/codes/swift/chapter_hashing/array_hash_map.swift b/codes/swift/chapter_hashing/array_hash_map.swift index 735078933..882eb96bd 100644 --- a/codes/swift/chapter_hashing/array_hash_map.swift +++ b/codes/swift/chapter_hashing/array_hash_map.swift @@ -103,7 +103,7 @@ enum _ArrayHashMap { map.print() /* 查询操作 */ - // 向哈希表输入键 key ,得到值 value + // 向哈希表中输入键 key ,得到值 value let name = map.get(key: 15937)! print("\n输入学号 15937 ,查询到姓名 \(name)") diff --git a/codes/swift/chapter_hashing/hash_map.swift b/codes/swift/chapter_hashing/hash_map.swift index b74aed3fa..a0a584132 100644 --- a/codes/swift/chapter_hashing/hash_map.swift +++ b/codes/swift/chapter_hashing/hash_map.swift @@ -24,7 +24,7 @@ enum HashMap { PrintUtil.printHashMap(map: map) /* 查询操作 */ - // 向哈希表输入键 key ,得到值 value + // 向哈希表中输入键 key ,得到值 value let name = map[15937]! print("\n输入学号 15937 ,查询到姓名 \(name)") diff --git a/codes/swift/chapter_hashing/hash_map_chaining.swift b/codes/swift/chapter_hashing/hash_map_chaining.swift index 7d88f78be..745fca461 100644 --- a/codes/swift/chapter_hashing/hash_map_chaining.swift +++ b/codes/swift/chapter_hashing/hash_map_chaining.swift @@ -37,13 +37,13 @@ class HashMapChaining { func get(key: Int) -> String? { let index = hashFunc(key: key) let bucket = buckets[index] - // 遍历桶,若找到 key 则返回对应 val + // 遍历桶,若找到 key ,则返回对应 val for pair in bucket { if pair.key == key { return pair.val } } - // 若未找到 key 则返回 nil + // 若未找到 key ,则返回 nil return nil } @@ -124,7 +124,7 @@ enum _HashMapChaining { map.print() /* 查询操作 */ - // 向哈希表输入键 key ,得到值 value + // 向哈希表中输入键 key ,得到值 value let name = map.get(key: 13276) print("\n输入学号 13276 ,查询到姓名 \(name!)") diff --git a/codes/swift/chapter_hashing/hash_map_open_addressing.swift b/codes/swift/chapter_hashing/hash_map_open_addressing.swift index 61ef79120..d7515d57a 100644 --- a/codes/swift/chapter_hashing/hash_map_open_addressing.swift +++ b/codes/swift/chapter_hashing/hash_map_open_addressing.swift @@ -41,9 +41,9 @@ class HashMapOpenAddressing { var firstTombstone = -1 // 线性探测,当遇到空桶时跳出 while buckets[index] != nil { - // 若遇到 key ,返回对应桶索引 + // 若遇到 key ,返回对应的桶索引 if buckets[index]!.key == key { - // 若之前遇到了删除标记,则将键值对移动至该索引 + // 若之前遇到了删除标记,则将键值对移动至该索引处 if firstTombstone != -1 { buckets[firstTombstone] = buckets[index] buckets[index] = TOMBSTONE @@ -55,7 +55,7 @@ class HashMapOpenAddressing { if firstTombstone == -1 && buckets[index] == TOMBSTONE { firstTombstone = index } - // 计算桶索引,越过尾部返回头部 + // 计算桶索引,越过尾部则返回头部 index = (index + 1) % capacity } // 若 key 不存在,则返回添加点的索引 @@ -151,7 +151,7 @@ enum _HashMapOpenAddressing { map.print() /* 查询操作 */ - // 向哈希表输入键 key ,得到值 value + // 向哈希表中输入键 key ,得到值 value let name = map.get(key: 13276) print("\n输入学号 13276 ,查询到姓名 \(name!)") diff --git a/codes/swift/chapter_heap/my_heap.swift b/codes/swift/chapter_heap/my_heap.swift index d9e38f5c7..96152f520 100644 --- a/codes/swift/chapter_heap/my_heap.swift +++ b/codes/swift/chapter_heap/my_heap.swift @@ -20,17 +20,17 @@ class MaxHeap { } } - /* 获取左子节点索引 */ + /* 获取左子节点的索引 */ private func left(i: Int) -> Int { 2 * i + 1 } - /* 获取右子节点索引 */ + /* 获取右子节点的索引 */ private func right(i: Int) -> Int { 2 * i + 2 } - /* 获取父节点索引 */ + /* 获取父节点的索引 */ private func parent(i: Int) -> Int { (i - 1) / 2 // 向下整除 } diff --git a/codes/swift/chapter_sorting/bubble_sort.swift b/codes/swift/chapter_sorting/bubble_sort.swift index e065869f9..589c55fb8 100644 --- a/codes/swift/chapter_sorting/bubble_sort.swift +++ b/codes/swift/chapter_sorting/bubble_sort.swift @@ -34,7 +34,7 @@ func bubbleSortWithFlag(nums: inout [Int]) { flag = true // 记录交换元素 } } - if !flag { // 此轮冒泡未交换任何元素,直接跳出 + if !flag { // 此轮“冒泡”未交换任何元素,直接跳出 break } } diff --git a/codes/swift/chapter_sorting/merge_sort.swift b/codes/swift/chapter_sorting/merge_sort.swift index 75ddf71a0..1cf8b63da 100644 --- a/codes/swift/chapter_sorting/merge_sort.swift +++ b/codes/swift/chapter_sorting/merge_sort.swift @@ -6,12 +6,12 @@ /* 合并左子数组和右子数组 */ func merge(nums: inout [Int], left: Int, mid: Int, right: Int) { - // 左子数组区间 [left, mid], 右子数组区间 [mid+1, right] + // 左子数组区间为 [left, mid], 右子数组区间为 [mid+1, right] // 创建一个临时数组 tmp ,用于存放合并后的结果 var tmp = Array(repeating: 0, count: right - left + 1) // 初始化左子数组和右子数组的起始索引 var i = left, j = mid + 1, k = 0 - // 当左右子数组都还有元素时,比较并将较小的元素复制到临时数组中 + // 当左右子数组都还有元素时,进行比较并将较小的元素复制到临时数组中 while i <= mid, j <= right { if nums[i] <= nums[j] { tmp[k] = nums[i] diff --git a/codes/swift/chapter_sorting/quick_sort.swift b/codes/swift/chapter_sorting/quick_sort.swift index 1427a7d8c..13b0a8f2a 100644 --- a/codes/swift/chapter_sorting/quick_sort.swift +++ b/codes/swift/chapter_sorting/quick_sort.swift @@ -45,7 +45,7 @@ func quickSort(nums: inout [Int], left: Int, right: Int) { /* 快速排序类(中位基准数优化) */ -/* 选取三个元素的中位数 */ +/* 选取三个候选元素的中位数 */ func medianThree(nums: [Int], left: Int, mid: Int, right: Int) -> Int { if (nums[left] < nums[mid]) != (nums[left] < nums[right]) { return left diff --git a/codes/swift/chapter_stack_and_queue/array_deque.swift b/codes/swift/chapter_stack_and_queue/array_deque.swift index 20254b873..7051572cb 100644 --- a/codes/swift/chapter_stack_and_queue/array_deque.swift +++ b/codes/swift/chapter_stack_and_queue/array_deque.swift @@ -47,7 +47,7 @@ class ArrayDeque { return } // 队首指针向左移动一位 - // 通过取余操作,实现 front 越过数组头部后回到尾部 + // 通过取余操作实现 front 越过数组头部后回到尾部 front = index(i: front - 1) // 将 num 添加至队首 nums[front] = num @@ -60,7 +60,7 @@ class ArrayDeque { print("双向队列已满") return } - // 计算尾指针,指向队尾索引 + 1 + // 计算队尾指针,指向队尾索引 + 1 let rear = index(i: front + size()) // 将 num 添加至队尾 nums[rear] = num diff --git a/codes/swift/chapter_stack_and_queue/array_queue.swift b/codes/swift/chapter_stack_and_queue/array_queue.swift index a7c2b439d..2a6449956 100644 --- a/codes/swift/chapter_stack_and_queue/array_queue.swift +++ b/codes/swift/chapter_stack_and_queue/array_queue.swift @@ -36,8 +36,8 @@ class ArrayQueue { print("队列已满") return } - // 计算尾指针,指向队尾索引 + 1 - // 通过取余操作,实现 rear 越过数组尾部后回到头部 + // 计算队尾指针,指向队尾索引 + 1 + // 通过取余操作实现 rear 越过数组尾部后回到头部 let rear = (front + queSize) % capacity() // 将 num 添加至队尾 nums[rear] = num @@ -48,7 +48,7 @@ class ArrayQueue { @discardableResult func pop() -> Int { let num = peek() - // 队首指针向后移动一位,若越过尾部则返回到数组头部 + // 队首指针向后移动一位,若越过尾部,则返回到数组头部 front = (front + 1) % capacity() queSize -= 1 return num diff --git a/codes/swift/chapter_stack_and_queue/linkedlist_queue.swift b/codes/swift/chapter_stack_and_queue/linkedlist_queue.swift index 141de4348..623e95b12 100644 --- a/codes/swift/chapter_stack_and_queue/linkedlist_queue.swift +++ b/codes/swift/chapter_stack_and_queue/linkedlist_queue.swift @@ -26,7 +26,7 @@ class LinkedListQueue { /* 入队 */ func push(num: Int) { - // 尾节点后添加 num + // 在尾节点后添加 num let node = ListNode(x: num) // 如果队列为空,则令头、尾节点都指向该节点 if front == nil { diff --git a/codes/swift/chapter_tree/avl_tree.swift b/codes/swift/chapter_tree/avl_tree.swift index b83367965..a7f8dc865 100644 --- a/codes/swift/chapter_tree/avl_tree.swift +++ b/codes/swift/chapter_tree/avl_tree.swift @@ -99,7 +99,7 @@ class AVLTree { if node == nil { return TreeNode(x: val) } - /* 1. 查找插入位置,并插入节点 */ + /* 1. 查找插入位置并插入节点 */ if val < node!.val { node?.left = insertHelper(node: node?.left, val: val) } else if val > node!.val { @@ -125,7 +125,7 @@ class AVLTree { if node == nil { return nil } - /* 1. 查找节点,并删除之 */ + /* 1. 查找节点并删除 */ if val < node!.val { node?.left = removeHelper(node: node?.left, val: val) } else if val > node!.val { diff --git a/codes/typescript/chapter_array_and_linkedlist/my_list.ts b/codes/typescript/chapter_array_and_linkedlist/my_list.ts index a23d3c4cc..815b50fbe 100644 --- a/codes/typescript/chapter_array_and_linkedlist/my_list.ts +++ b/codes/typescript/chapter_array_and_linkedlist/my_list.ts @@ -28,7 +28,7 @@ class MyList { /* 访问元素 */ public get(index: number): number { - // 索引如果越界则抛出异常,下同 + // 索引如果越界,则抛出异常,下同 if (index < 0 || index >= this._size) throw new Error('索引越界'); return this.arr[index]; } @@ -74,13 +74,13 @@ class MyList { } // 更新元素数量 this._size--; - // 返回被删除元素 + // 返回被删除的元素 return num; } /* 列表扩容 */ public extendCapacity(): void { - // 新建一个长度为 size 的数组,并将原数组拷贝到新数组 + // 新建一个长度为 size 的数组,并将原数组复制到新数组 this.arr = this.arr.concat( new Array(this.capacity() * (this.extendRatio - 1)) ); diff --git a/codes/typescript/chapter_backtracking/n_queens.ts b/codes/typescript/chapter_backtracking/n_queens.ts index c6bb5ee02..741d5639b 100644 --- a/codes/typescript/chapter_backtracking/n_queens.ts +++ b/codes/typescript/chapter_backtracking/n_queens.ts @@ -21,10 +21,10 @@ function backtrack( } // 遍历所有列 for (let col = 0; col < n; col++) { - // 计算该格子对应的主对角线和副对角线 + // 计算该格子对应的主对角线和次对角线 const diag1 = row - col + n - 1; const diag2 = row + col; - // 剪枝:不允许该格子所在列、主对角线、副对角线上存在皇后 + // 剪枝:不允许该格子所在列、主对角线、次对角线上存在皇后 if (!cols[col] && !diags1[diag1] && !diags2[diag2]) { // 尝试:将皇后放置在该格子 state[row][col] = 'Q'; @@ -44,7 +44,7 @@ function nQueens(n: number): string[][][] { const state = Array.from({ length: n }, () => Array(n).fill('#')); const cols = Array(n).fill(false); // 记录列是否有皇后 const diags1 = Array(2 * n - 1).fill(false); // 记录主对角线上是否有皇后 - const diags2 = Array(2 * n - 1).fill(false); // 记录副对角线上是否有皇后 + const diags2 = Array(2 * n - 1).fill(false); // 记录次对角线上是否有皇后 const res: string[][][] = []; backtrack(0, n, state, res, cols, diags1, diags2); diff --git a/codes/typescript/chapter_graph/graph_bfs.ts b/codes/typescript/chapter_graph/graph_bfs.ts index cba409d8d..d9cf3eeef 100644 --- a/codes/typescript/chapter_graph/graph_bfs.ts +++ b/codes/typescript/chapter_graph/graph_bfs.ts @@ -7,7 +7,7 @@ import { GraphAdjList } from './graph_adjacency_list'; import { Vertex } from '../modules/Vertex'; -/* 广度优先遍历 BFS */ +/* 广度优先遍历 */ // 使用邻接表来表示图,以便获取指定顶点的所有邻接顶点 function graphBFS(graph: GraphAdjList, startVet: Vertex): Vertex[] { // 顶点遍历序列 @@ -55,7 +55,7 @@ const graph = new GraphAdjList(edges); console.log('\n初始化后,图为'); graph.print(); -/* 广度优先遍历 BFS */ +/* 广度优先遍历 */ const res = graphBFS(graph, v[0]); console.log('\n广度优先遍历(BFS)顶点序列为'); console.log(Vertex.vetsToVals(res)); diff --git a/codes/typescript/chapter_graph/graph_dfs.ts b/codes/typescript/chapter_graph/graph_dfs.ts index 8dbc872b7..3ca7984df 100644 --- a/codes/typescript/chapter_graph/graph_dfs.ts +++ b/codes/typescript/chapter_graph/graph_dfs.ts @@ -7,7 +7,7 @@ import { Vertex } from '../modules/Vertex'; import { GraphAdjList } from './graph_adjacency_list'; -/* 深度优先遍历 DFS 辅助函数 */ +/* 深度优先遍历辅助函数 */ function dfs( graph: GraphAdjList, visited: Set, @@ -26,7 +26,7 @@ function dfs( } } -/* 深度优先遍历 DFS */ +/* 深度优先遍历 */ // 使用邻接表来表示图,以便获取指定顶点的所有邻接顶点 function graphDFS(graph: GraphAdjList, startVet: Vertex): Vertex[] { // 顶点遍历序列 @@ -52,7 +52,7 @@ const graph = new GraphAdjList(edges); console.log('\n初始化后,图为'); graph.print(); -/* 深度优先遍历 DFS */ +/* 深度优先遍历 */ const res = graphDFS(graph, v[0]); console.log('\n深度优先遍历(DFS)顶点序列为'); console.log(Vertex.vetsToVals(res)); diff --git a/codes/typescript/chapter_greedy/max_capacity.ts b/codes/typescript/chapter_greedy/max_capacity.ts index 3bc62b130..edde65f64 100644 --- a/codes/typescript/chapter_greedy/max_capacity.ts +++ b/codes/typescript/chapter_greedy/max_capacity.ts @@ -6,7 +6,7 @@ /* 最大容量:贪心 */ function maxCapacity(ht: number[]): number { - // 初始化 i, j 分列数组两端 + // 初始化 i, j,使其分列数组两端 let i = 0, j = ht.length - 1; // 初始最大容量为 0 diff --git a/codes/typescript/chapter_hashing/array_hash_map.ts b/codes/typescript/chapter_hashing/array_hash_map.ts index 5276b028a..97534ccb7 100644 --- a/codes/typescript/chapter_hashing/array_hash_map.ts +++ b/codes/typescript/chapter_hashing/array_hash_map.ts @@ -106,7 +106,7 @@ console.info('\n添加完成后,哈希表为\nKey -> Value'); map.print(); /* 查询操作 */ -// 向哈希表输入键 key ,得到值 value +// 向哈希表中输入键 key ,得到值 value let name = map.get(15937); console.info('\n输入学号 15937 ,查询到姓名 ' + name); diff --git a/codes/typescript/chapter_hashing/hash_map.ts b/codes/typescript/chapter_hashing/hash_map.ts index 0dea03d89..54f52de90 100644 --- a/codes/typescript/chapter_hashing/hash_map.ts +++ b/codes/typescript/chapter_hashing/hash_map.ts @@ -19,7 +19,7 @@ console.info('\n添加完成后,哈希表为\nKey -> Value'); console.info(map); /* 查询操作 */ -// 向哈希表输入键 key ,得到值 value +// 向哈希表中输入键 key ,得到值 value let name = map.get(15937); console.info('\n输入学号 15937 ,查询到姓名 ' + name); diff --git a/codes/typescript/chapter_hashing/hash_map_chaining.ts b/codes/typescript/chapter_hashing/hash_map_chaining.ts index 51a59b636..9df6a3e16 100644 --- a/codes/typescript/chapter_hashing/hash_map_chaining.ts +++ b/codes/typescript/chapter_hashing/hash_map_chaining.ts @@ -45,13 +45,13 @@ class HashMapChaining { get(key: number): string | null { const index = this.#hashFunc(key); const bucket = this.#buckets[index]; - // 遍历桶,若找到 key 则返回对应 val + // 遍历桶,若找到 key ,则返回对应 val for (const pair of bucket) { if (pair.key === key) { return pair.val; } } - // 若未找到 key 则返回 null + // 若未找到 key ,则返回 null return null; } @@ -133,7 +133,7 @@ console.log('\n添加完成后,哈希表为\nKey -> Value'); map.print(); /* 查询操作 */ -// 向哈希表输入键 key ,得到值 value +// 向哈希表中输入键 key ,得到值 value const name = map.get(13276); console.log('\n输入学号 13276 ,查询到姓名 ' + name); diff --git a/codes/typescript/chapter_hashing/hash_map_open_addressing.ts b/codes/typescript/chapter_hashing/hash_map_open_addressing.ts index 876f99190..cf939ed16 100644 --- a/codes/typescript/chapter_hashing/hash_map_open_addressing.ts +++ b/codes/typescript/chapter_hashing/hash_map_open_addressing.ts @@ -50,9 +50,9 @@ class HashMapOpenAddressing { let firstTombstone = -1; // 线性探测,当遇到空桶时跳出 while (this.buckets[index] !== null) { - // 若遇到 key ,返回对应桶索引 + // 若遇到 key ,返回对应的桶索引 if (this.buckets[index]!.key === key) { - // 若之前遇到了删除标记,则将键值对移动至该索引 + // 若之前遇到了删除标记,则将键值对移动至该索引处 if (firstTombstone !== -1) { this.buckets[firstTombstone] = this.buckets[index]; this.buckets[index] = this.TOMBSTONE; @@ -67,7 +67,7 @@ class HashMapOpenAddressing { ) { firstTombstone = index; } - // 计算桶索引,越过尾部返回头部 + // 计算桶索引,越过尾部则返回头部 index = (index + 1) % this.capacity; } // 若 key 不存在,则返回添加点的索引 @@ -169,7 +169,7 @@ console.log('\n添加完成后,哈希表为\nKey -> Value'); hashmap.print(); // 查询操作 -// 向哈希表输入键 key ,得到值 val +// 向哈希表中输入键 key ,得到值 val const name = hashmap.get(13276); console.log('\n输入学号 13276 ,查询到姓名 ' + name); diff --git a/codes/typescript/chapter_heap/my_heap.ts b/codes/typescript/chapter_heap/my_heap.ts index 4cdb3fad2..188bd7f08 100644 --- a/codes/typescript/chapter_heap/my_heap.ts +++ b/codes/typescript/chapter_heap/my_heap.ts @@ -19,17 +19,17 @@ class MaxHeap { } } - /* 获取左子节点索引 */ + /* 获取左子节点的索引 */ private left(i: number): number { return 2 * i + 1; } - /* 获取右子节点索引 */ + /* 获取右子节点的索引 */ private right(i: number): number { return 2 * i + 2; } - /* 获取父节点索引 */ + /* 获取父节点的索引 */ private parent(i: number): number { return Math.floor((i - 1) / 2); // 向下整除 } diff --git a/codes/typescript/chapter_sorting/bubble_sort.ts b/codes/typescript/chapter_sorting/bubble_sort.ts index c07ab7902..936b8e9a9 100644 --- a/codes/typescript/chapter_sorting/bubble_sort.ts +++ b/codes/typescript/chapter_sorting/bubble_sort.ts @@ -35,7 +35,7 @@ function bubbleSortWithFlag(nums: number[]): void { flag = true; // 记录交换元素 } } - if (!flag) break; // 此轮冒泡未交换任何元素,直接跳出 + if (!flag) break; // 此轮“冒泡”未交换任何元素,直接跳出 } } diff --git a/codes/typescript/chapter_sorting/merge_sort.ts b/codes/typescript/chapter_sorting/merge_sort.ts index a2aada84e..9fc0c4adb 100644 --- a/codes/typescript/chapter_sorting/merge_sort.ts +++ b/codes/typescript/chapter_sorting/merge_sort.ts @@ -6,14 +6,14 @@ /* 合并左子数组和右子数组 */ function merge(nums: number[], left: number, mid: number, right: number): void { - // 左子数组区间 [left, mid], 右子数组区间 [mid+1, right] + // 左子数组区间为 [left, mid], 右子数组区间为 [mid+1, right] // 创建一个临时数组 tmp ,用于存放合并后的结果 const tmp = new Array(right - left + 1); // 初始化左子数组和右子数组的起始索引 let i = left, j = mid + 1, k = 0; - // 当左右子数组都还有元素时,比较并将较小的元素复制到临时数组中 + // 当左右子数组都还有元素时,进行比较并将较小的元素复制到临时数组中 while (i <= mid && j <= right) { if (nums[i] <= nums[j]) { tmp[k++] = nums[i++]; diff --git a/codes/typescript/chapter_sorting/quick_sort.ts b/codes/typescript/chapter_sorting/quick_sort.ts index 9352787d0..5f4e7378c 100644 --- a/codes/typescript/chapter_sorting/quick_sort.ts +++ b/codes/typescript/chapter_sorting/quick_sort.ts @@ -55,7 +55,7 @@ class QuickSortMedian { nums[j] = tmp; } - /* 选取三个元素的中位数 */ + /* 选取三个候选元素的中位数 */ medianThree( nums: number[], left: number, diff --git a/codes/typescript/chapter_stack_and_queue/array_deque.ts b/codes/typescript/chapter_stack_and_queue/array_deque.ts index f3d429d83..554731f21 100644 --- a/codes/typescript/chapter_stack_and_queue/array_deque.ts +++ b/codes/typescript/chapter_stack_and_queue/array_deque.ts @@ -47,7 +47,7 @@ class ArrayDeque { return; } // 队首指针向左移动一位 - // 通过取余操作,实现 front 越过数组头部后回到尾部 + // 通过取余操作实现 front 越过数组头部后回到尾部 this.front = this.index(this.front - 1); // 将 num 添加至队首 this.nums[this.front] = num; @@ -60,7 +60,7 @@ class ArrayDeque { console.log('双向队列已满'); return; } - // 计算尾指针,指向队尾索引 + 1 + // 计算队尾指针,指向队尾索引 + 1 const rear: number = this.index(this.front + this.queSize); // 将 num 添加至队尾 this.nums[rear] = num; diff --git a/codes/typescript/chapter_stack_and_queue/array_queue.ts b/codes/typescript/chapter_stack_and_queue/array_queue.ts index 71ab27c8d..daf31fa4b 100644 --- a/codes/typescript/chapter_stack_and_queue/array_queue.ts +++ b/codes/typescript/chapter_stack_and_queue/array_queue.ts @@ -36,8 +36,8 @@ class ArrayQueue { console.log('队列已满'); return; } - // 计算尾指针,指向队尾索引 + 1 - // 通过取余操作,实现 rear 越过数组尾部后回到头部 + // 计算队尾指针,指向队尾索引 + 1 + // 通过取余操作实现 rear 越过数组尾部后回到头部 const rear = (this.front + this.queSize) % this.capacity; // 将 num 添加至队尾 this.nums[rear] = num; @@ -47,7 +47,7 @@ class ArrayQueue { /* 出队 */ pop(): number { const num = this.peek(); - // 队首指针向后移动一位,若越过尾部则返回到数组头部 + // 队首指针向后移动一位,若越过尾部,则返回到数组头部 this.front = (this.front + 1) % this.capacity; this.queSize--; return num; diff --git a/codes/typescript/chapter_stack_and_queue/linkedlist_queue.ts b/codes/typescript/chapter_stack_and_queue/linkedlist_queue.ts index e403c012c..44cf99808 100644 --- a/codes/typescript/chapter_stack_and_queue/linkedlist_queue.ts +++ b/codes/typescript/chapter_stack_and_queue/linkedlist_queue.ts @@ -29,7 +29,7 @@ class LinkedListQueue { /* 入队 */ push(num: number): void { - // 尾节点后添加 num + // 在尾节点后添加 num const node = new ListNode(num); // 如果队列为空,则令头、尾节点都指向该节点 if (!this.front) { diff --git a/codes/typescript/chapter_tree/avl_tree.ts b/codes/typescript/chapter_tree/avl_tree.ts index 812fe4a03..ba4ea8489 100644 --- a/codes/typescript/chapter_tree/avl_tree.ts +++ b/codes/typescript/chapter_tree/avl_tree.ts @@ -102,7 +102,7 @@ class AVLTree { /* 递归插入节点(辅助方法) */ private insertHelper(node: TreeNode, val: number): TreeNode { if (node === null) return new TreeNode(val); - /* 1. 查找插入位置,并插入节点 */ + /* 1. 查找插入位置并插入节点 */ if (val < node.val) { node.left = this.insertHelper(node.left, val); } else if (val > node.val) { @@ -125,7 +125,7 @@ class AVLTree { /* 递归删除节点(辅助方法) */ private removeHelper(node: TreeNode, val: number): TreeNode { if (node === null) return null; - /* 1. 查找节点,并删除之 */ + /* 1. 查找节点并删除 */ if (val < node.val) { node.left = this.removeHelper(node.left, val); } else if (val > node.val) { diff --git a/codes/zig/chapter_array_and_linkedlist/my_list.zig b/codes/zig/chapter_array_and_linkedlist/my_list.zig index 41066fd65..dd8fb52ee 100644 --- a/codes/zig/chapter_array_and_linkedlist/my_list.zig +++ b/codes/zig/chapter_array_and_linkedlist/my_list.zig @@ -45,14 +45,14 @@ pub fn MyList(comptime T: type) type { // 访问元素 pub fn get(self: *Self, index: usize) T { - // 索引如果越界则抛出异常,下同 + // 索引如果越界,则抛出异常,下同 if (index < 0 or index >= self.size()) @panic("索引越界"); return self.arr[index]; } // 更新元素 pub fn set(self: *Self, index: usize, num: T) void { - // 索引如果越界则抛出异常,下同 + // 索引如果越界,则抛出异常,下同 if (index < 0 or index >= self.size()) @panic("索引越界"); self.arr[index] = num; } @@ -92,13 +92,13 @@ pub fn MyList(comptime T: type) type { } // 更新元素数量 self.numSize -= 1; - // 返回被删除元素 + // 返回被删除的元素 return num; } // 列表扩容 pub fn extendCapacity(self: *Self) !void { - // 新建一个长度为 size * extendRatio 的数组,并将原数组拷贝到新数组 + // 新建一个长度为 size * extendRatio 的数组,并将原数组复制到新数组 var newCapacity = self.capacity() * self.extendRatio; var extend = try self.mem_allocator.alloc(T, newCapacity); @memset(extend, @as(T, 0)); diff --git a/codes/zig/chapter_hashing/array_hash_map.zig b/codes/zig/chapter_hashing/array_hash_map.zig index 62f746389..67088305c 100644 --- a/codes/zig/chapter_hashing/array_hash_map.zig +++ b/codes/zig/chapter_hashing/array_hash_map.zig @@ -128,7 +128,7 @@ pub fn main() !void { try map.print(); // 查询操作 - // 向哈希表输入键 key ,得到值 value + // 向哈希表中输入键 key ,得到值 value var name = map.get(15937); std.debug.print("\n输入学号 15937 ,查询到姓名 {s}\n", .{name}); diff --git a/codes/zig/chapter_hashing/hash_map.zig b/codes/zig/chapter_hashing/hash_map.zig index d9dee1b81..08ee5d19a 100644 --- a/codes/zig/chapter_hashing/hash_map.zig +++ b/codes/zig/chapter_hashing/hash_map.zig @@ -23,7 +23,7 @@ pub fn main() !void { inc.PrintUtil.printHashMap(i32, []const u8, map); // 查询操作 - // 向哈希表输入键 key ,得到值 value + // 向哈希表中输入键 key ,得到值 value var name = map.get(15937).?; std.debug.print("\n输入学号 15937 ,查询到姓名 {s}\n", .{name}); diff --git a/codes/zig/chapter_heap/my_heap.zig b/codes/zig/chapter_heap/my_heap.zig index 8732e106f..4b421ef1d 100644 --- a/codes/zig/chapter_heap/my_heap.zig +++ b/codes/zig/chapter_heap/my_heap.zig @@ -30,17 +30,17 @@ pub fn MaxHeap(comptime T: type) type { if (self.max_heap != null) self.max_heap.?.deinit(); } - // 获取左子节点索引 + // 获取左子节点的索引 fn left(i: usize) usize { return 2 * i + 1; } - // 获取右子节点索引 + // 获取右子节点的索引 fn right(i: usize) usize { return 2 * i + 2; } - // 获取父节点索引 + // 获取父节点的索引 fn parent(i: usize) usize { // return (i - 1) / 2; // 向下整除 return @divFloor(i - 1, 2); diff --git a/codes/zig/chapter_sorting/bubble_sort.zig b/codes/zig/chapter_sorting/bubble_sort.zig index ef4bf0661..4782b02d8 100644 --- a/codes/zig/chapter_sorting/bubble_sort.zig +++ b/codes/zig/chapter_sorting/bubble_sort.zig @@ -40,7 +40,7 @@ fn bubbleSortWithFlag(nums: []i32) void { flag = true; } } - if (!flag) break; // 此轮冒泡未交换任何元素,直接跳出 + if (!flag) break; // 此轮“冒泡”未交换任何元素,直接跳出 } } diff --git a/codes/zig/chapter_sorting/quick_sort.zig b/codes/zig/chapter_sorting/quick_sort.zig index 7a0dd2add..914fb7654 100644 --- a/codes/zig/chapter_sorting/quick_sort.zig +++ b/codes/zig/chapter_sorting/quick_sort.zig @@ -51,7 +51,7 @@ const QuickSortMedian = struct { nums[j] = tmp; } - // 选取三个元素的中位数 + // 选取三个候选元素的中位数 pub fn medianThree(nums: []i32, left: usize, mid: usize, right: usize) usize { // 此处使用异或运算来简化代码 // 异或规则为 0 ^ 0 = 1 ^ 1 = 0, 0 ^ 1 = 1 ^ 0 = 1 diff --git a/codes/zig/chapter_stack_and_queue/array_queue.zig b/codes/zig/chapter_stack_and_queue/array_queue.zig index 9432a558d..709079de9 100644 --- a/codes/zig/chapter_stack_and_queue/array_queue.zig +++ b/codes/zig/chapter_stack_and_queue/array_queue.zig @@ -55,10 +55,10 @@ pub fn ArrayQueue(comptime T: type) type { std.debug.print("队列已满\n", .{}); return; } - // 计算尾指针,指向队尾索引 + 1 - // 通过取余操作,实现 rear 越过数组尾部后回到头部 + // 计算队尾指针,指向队尾索引 + 1 + // 通过取余操作实现 rear 越过数组尾部后回到头部 var rear = (self.front + self.queSize) % self.capacity(); - // 尾节点后添加 num + // 在尾节点后添加 num self.nums[rear] = num; self.queSize += 1; } @@ -66,7 +66,7 @@ pub fn ArrayQueue(comptime T: type) type { // 出队 pub fn pop(self: *Self) T { var num = self.peek(); - // 队首指针向后移动一位,若越过尾部则返回到数组头部 + // 队首指针向后移动一位,若越过尾部,则返回到数组头部 self.front = (self.front + 1) % self.capacity(); self.queSize -= 1; return num; diff --git a/codes/zig/chapter_stack_and_queue/linkedlist_queue.zig b/codes/zig/chapter_stack_and_queue/linkedlist_queue.zig index 3746afbac..bf341677f 100644 --- a/codes/zig/chapter_stack_and_queue/linkedlist_queue.zig +++ b/codes/zig/chapter_stack_and_queue/linkedlist_queue.zig @@ -51,7 +51,7 @@ pub fn LinkedListQueue(comptime T: type) type { // 入队 pub fn push(self: *Self, num: T) !void { - // 尾节点后添加 num + // 在尾节点后添加 num var node = try self.mem_allocator.create(inc.ListNode(T)); node.init(num); // 如果队列为空,则令头、尾节点都指向该节点 diff --git a/codes/zig/chapter_tree/avl_tree.zig b/codes/zig/chapter_tree/avl_tree.zig index 767fc80ee..daf76a18e 100644 --- a/codes/zig/chapter_tree/avl_tree.zig +++ b/codes/zig/chapter_tree/avl_tree.zig @@ -120,7 +120,7 @@ pub fn AVLTree(comptime T: type) type { tmp_node.init(val); return tmp_node; } - // 1. 查找插入位置,并插入节点 + // 1. 查找插入位置并插入节点 if (val < node.?.val) { node.?.left = try self.insertHelper(node.?.left, val); } else if (val > node.?.val) { @@ -144,7 +144,7 @@ pub fn AVLTree(comptime T: type) type { fn removeHelper(self: *Self, node_: ?*inc.TreeNode(T), val: T) ?*inc.TreeNode(T) { var node = node_; if (node == null) return null; - // 1. 查找节点,并删除之 + // 1. 查找节点并删除 if (val < node.?.val) { node.?.left = self.removeHelper(node.?.left, val); } else if (val > node.?.val) { diff --git a/docs-en/chapter_array_and_linkedlist/linked_list.md b/docs-en/chapter_array_and_linkedlist/linked_list.md index 6f4435b49..c60a0fe30 100755 --- a/docs-en/chapter_array_and_linkedlist/linked_list.md +++ b/docs-en/chapter_array_and_linkedlist/linked_list.md @@ -11,7 +11,7 @@ The design of a linked list allows its nodes to be scattered throughout memory, Observing the image above, the fundamental unit of a linked list is the "node" object. Each node contains two pieces of data: the "value" of the node and the "reference" to the next node. - The first node of a linked list is known as the "head node", and the last one is called the "tail node". -- The tail node points to "null", which is represented as $\text{null}$ in Java, $\text{nullptr}$ in C++, and $\text{None}$ in Python. +- The tail node points to "null", which is represented as `null` in Java, `nullptr` in C++, and `None` in Python. - In languages that support pointers, like C, C++, Go, and Rust, the aforementioned "reference" should be replaced with a "pointer". As shown in the following code, a linked list node `ListNode`, apart from containing a value, also needs to store a reference (pointer). Therefore, **a linked list consumes more memory space than an array for the same amount of data**. diff --git a/docs-en/chapter_preface/suggestions.md b/docs-en/chapter_preface/suggestions.md index bfba2aa85..67a9e25f3 100644 --- a/docs-en/chapter_preface/suggestions.md +++ b/docs-en/chapter_preface/suggestions.md @@ -10,7 +10,7 @@ - Proper nouns and words and phrases with specific meanings are marked with `"double quotes"` to avoid ambiguity. - Important proper nouns and their English translations are marked with `" "` in parentheses, e.g. `"array array"` . It is recommended to memorize them for reading the literature. - **Bolded text** Indicates key content or summary statements, which deserve special attention. -- When it comes to terms that are inconsistent between programming languages, this book follows Python, for example using $\text{None}$ to mean "empty". +- When it comes to terms that are inconsistent between programming languages, this book follows Python, for example using `None` to mean "empty". - This book partially abandons the specification of annotations in programming languages in exchange for a more compact layout of the content. There are three main types of annotations: title annotations, content annotations, and multi-line annotations. === "Python" diff --git a/docs/chapter_array_and_linkedlist/array.md b/docs/chapter_array_and_linkedlist/array.md index 0fdd099cb..be902d5a6 100755 --- a/docs/chapter_array_and_linkedlist/array.md +++ b/docs/chapter_array_and_linkedlist/array.md @@ -1,6 +1,6 @@ # 数组 -「数组 array」是一种线性数据结构,其将相同类型的元素存储在连续的内存空间中。我们将元素在数组中的位置称为该元素的「索引 index」。下图展示了数组的主要术语和概念。 +「数组 array」是一种线性数据结构,其将相同类型的元素存储在连续的内存空间中。我们将元素在数组中的位置称为该元素的「索引 index」。下图展示了数组的主要概念和存储方式。 ![数组定义与存储方式](array.assets/array_definition.png) diff --git a/docs/chapter_array_and_linkedlist/linked_list.md b/docs/chapter_array_and_linkedlist/linked_list.md index 546f58512..34a025c98 100755 --- a/docs/chapter_array_and_linkedlist/linked_list.md +++ b/docs/chapter_array_and_linkedlist/linked_list.md @@ -11,8 +11,8 @@ 观察上图,链表的组成单位是「节点 node」对象。每个节点都包含两项数据:节点的“值”和指向下一节点的“引用”。 - 链表的首个节点被称为“头节点”,最后一个节点被称为“尾节点”。 -- 尾节点指向的是“空”,它在 Java、C++ 和 Python 中分别被记为 $\text{null}$、$\text{nullptr}$ 和 $\text{None}$ 。 -- 在 C、C++、Go 和 Rust 等支持指针的语言中,上述的“引用”应被替换为“指针”。 +- 尾节点指向的是“空”,它在 Java、C++ 和 Python 中分别被记为 `null`、`nullptr` 和 `None` 。 +- 在 C、C++、Go 和 Rust 等支持指针的语言中,上述“引用”应被替换为“指针”。 如以下代码所示,链表节点 `ListNode` 除了包含值,还需额外保存一个引用(指针)。因此在相同数据量下,**链表比数组占用更多的内存空间**。 @@ -451,7 +451,7 @@ 如下图所示,常见的链表类型包括三种。 -- **单向链表**:即前面介绍的普通链表。单向链表的节点包含值和指向下一节点的引用两项数据。我们将首个节点称为头节点,将最后一个节点称为尾节点,尾节点指向空 $\text{None}$ 。 +- **单向链表**:即前面介绍的普通链表。单向链表的节点包含值和指向下一节点的引用两项数据。我们将首个节点称为头节点,将最后一个节点称为尾节点,尾节点指向空 `None` 。 - **环形链表**:如果我们令单向链表的尾节点指向头节点(首尾相接),则得到一个环形链表。在环形链表中,任意节点都可以视作头节点。 - **双向链表**:与单向链表相比,双向链表记录了两个方向的引用。双向链表的节点定义同时包含指向后继节点(下一个节点)和前驱节点(上一个节点)的引用(指针)。相较于单向链表,双向链表更具灵活性,可以朝两个方向遍历链表,但相应地也需要占用更多的内存空间。 diff --git a/docs/chapter_array_and_linkedlist/summary.md b/docs/chapter_array_and_linkedlist/summary.md index 552b70365..c1588ba09 100644 --- a/docs/chapter_array_and_linkedlist/summary.md +++ b/docs/chapter_array_and_linkedlist/summary.md @@ -4,9 +4,8 @@ - 数组和链表是两种基本的数据结构,分别代表数据在计算机内存中的两种存储方式:连续空间存储和分散空间存储。两者的特点呈现出互补的特性。 - 数组支持随机访问、占用内存较少;但插入和删除元素效率低,且初始化后长度不可变。 -- 链表通过更改引用(指针)实现高效的节点插入与删除,且可以灵活调整长度;但节点访问效率低、占用内存较多。 -- 常见的链表类型包括单向链表、环形链表、双向链表,它们分别具有各自的应用场景。 -- 列表是一种支持增删查改的元素有序集合,通常基于动态数组实现,其保留了数组的优势,同时可以灵活调整长度。 +- 链表通过更改引用(指针)实现高效的节点插入与删除,且可以灵活调整长度;但节点访问效率低、占用内存较多。常见的链表类型包括单向链表、环形链表、双向链表。 +- 列表是一种支持增删查改的元素有序集合,通常基于动态数组实现,它保留了数组的优势,同时可以灵活调整长度。 - 列表的出现大幅地提高了数组的实用性,但可能导致部分内存空间浪费。 - 程序运行时,数据主要存储在内存中。数组可提供更高的内存空间效率,而链表则在内存使用上更加灵活。 - 缓存通过缓存行、预取机制以及空间局部性和时间局部性等数据加载机制,为 CPU 提供快速数据访问,显著提升程序的执行效率。 @@ -24,15 +23,15 @@ !!! question "为什么数组要求相同类型的元素,而在链表中却没有强调同类型呢?" - 链表由节点组成,节点之间通过引用(指针)连接,各个节点可以存储不同类型的数据,例如 int、double、string、object 等。 + 链表由节点组成,节点之间通过引用(指针)连接,各个节点可以存储不同类型的数据,例如 `int`、`double`、`string`、`object` 等。 - 相对地,数组元素则必须是相同类型的,这样才能通过计算偏移量来获取对应元素位置。例如,数组同时包含 int 和 long 两种类型,单个元素分别占用 4 bytes 和 8 bytes ,此时就不能用以下公式计算偏移量了,因为数组中包含了两种长度的元素。 + 相对地,数组元素则必须是相同类型的,这样才能通过计算偏移量来获取对应元素位置。例如,数组同时包含 `int` 和 `long` 两种类型,单个元素分别占用 4 字节 和 8 字节 ,此时就不能用以下公式计算偏移量了,因为数组中包含了两种“元素长度”。 ```shell # 元素内存地址 = 数组内存地址 + 元素长度 * 元素索引 ``` -!!! question "删除节点后,是否需要把 `P.next` 设为 $\text{None}$ 呢?" +!!! question "删除节点后,是否需要把 `P.next` 设为 `None` 呢?" 不修改 `P.next` 也可以。从该链表的角度看,从头节点遍历到尾节点已经不会遇到 `P` 了。这意味着节点 `P` 已经从链表中删除了,此时节点 `P` 指向哪里都不会对该链表产生影响。 @@ -46,7 +45,7 @@ 该示意图只是定性表示,定量表示需要根据具体情况进行分析。 - - 不同类型的节点值占用的空间是不同的,比如 int、long、double 和实例对象等。 + - 不同类型的节点值占用的空间是不同的,比如 `int`、`long`、`double` 和实例对象等。 - 指针变量占用的内存空间大小根据所使用的操作系统及编译环境而定,大多为 8 字节或 4 字节。 !!! question "在列表末尾添加元素是否时时刻刻都为 $O(1)$ ?" diff --git a/docs/chapter_backtracking/n_queens_problem.md b/docs/chapter_backtracking/n_queens_problem.md index 755ce8cb2..cf8e24593 100644 --- a/docs/chapter_backtracking/n_queens_problem.md +++ b/docs/chapter_backtracking/n_queens_problem.md @@ -8,7 +8,7 @@ ![4 皇后问题的解](n_queens_problem.assets/solution_4_queens.png) -下图展示了本题的三个约束条件:**多个皇后不能在同一行、同一列、同一对角线**。值得注意的是,对角线分为主对角线 `\` 和次对角线 `/` 两种。 +下图展示了本题的三个约束条件:**多个皇后不能在同一行、同一列、同一条对角线上**。值得注意的是,对角线分为主对角线 `\` 和次对角线 `/` 两种。 ![n 皇后问题的约束条件](n_queens_problem.assets/n_queens_constraints.png) diff --git a/docs/chapter_backtracking/permutations_problem.md b/docs/chapter_backtracking/permutations_problem.md index 1de22f155..4b7d71fd3 100644 --- a/docs/chapter_backtracking/permutations_problem.md +++ b/docs/chapter_backtracking/permutations_problem.md @@ -87,8 +87,8 @@ 请注意,虽然 `selected` 和 `duplicated` 都用于剪枝,但两者的目标不同。 -- **重复选择剪枝**:整个搜索过程中只有一个 `selected` 。它记录的是当前状态中包含哪些元素,其作用是防止 `choices` 中的任一元素在 `state` 中重复出现。 -- **相等元素剪枝**:每轮选择(每个调用的 `backtrack` 函数)都包含一个 `duplicated` 。它记录的是在本轮遍历(`for` 循环)中哪些元素已被选择过,其作用是保证相等的元素只被选择一次。 +- **重复选择剪枝**:整个搜索过程中只有一个 `selected` 。它记录的是当前状态中包含哪些元素,其作用是避免某个元素在 `state` 中重复出现。 +- **相等元素剪枝**:每轮选择(每个调用的 `backtrack` 函数)都包含一个 `duplicated` 。它记录的是在本轮遍历(`for` 循环)中哪些元素已被选择过,其作用是保证相等元素只被选择一次。 下图展示了两个剪枝条件的生效范围。注意,树中的每个节点代表一个选择,从根节点到叶节点的路径上的各个节点构成一个排列。 diff --git a/docs/chapter_backtracking/summary.md b/docs/chapter_backtracking/summary.md index 07eb55364..00d0ead07 100644 --- a/docs/chapter_backtracking/summary.md +++ b/docs/chapter_backtracking/summary.md @@ -10,8 +10,8 @@ - 在全排列问题中,如果集合中存在重复元素,则最终结果会出现重复排列。我们需要约束相等元素在每轮中只能被选择一次,这通常借助一个哈希表来实现。 - 子集和问题的目标是在给定集合中找到和为目标值的所有子集。集合不区分元素顺序,而搜索过程会输出所有顺序的结果,产生重复子集。我们在回溯前将数据进行排序,并设置一个变量来指示每一轮的遍历起始点,从而将生成重复子集的搜索分支进行剪枝。 - 对于子集和问题,数组中的相等元素会产生重复集合。我们利用数组已排序的前置条件,通过判断相邻元素是否相等实现剪枝,从而确保相等元素在每轮中只能被选中一次。 -- $n$ 皇后问题旨在寻找将 $n$ 个皇后放置到 $n \times n$ 尺寸棋盘上的方案,要求所有皇后两两之间无法攻击对方。该问题的约束条件有行约束、列约束、主对角线和副对角线约束。为满足行约束,我们采用按行放置的策略,保证每一行放置一个皇后。 -- 列约束和对角线约束的处理方式类似。对于列约束,我们利用一个数组来记录每一列是否有皇后,从而指示选中的格子是否合法。对于对角线约束,我们借助两个数组来分别记录该主、副对角线上是否存在皇后;难点在于找处在到同一主(副)对角线上格子满足的行列索引规律。 +- $n$ 皇后问题旨在寻找将 $n$ 个皇后放置到 $n \times n$ 尺寸棋盘上的方案,要求所有皇后两两之间无法攻击对方。该问题的约束条件有行约束、列约束、主对角线和次对角线约束。为满足行约束,我们采用按行放置的策略,保证每一行放置一个皇后。 +- 列约束和对角线约束的处理方式类似。对于列约束,我们利用一个数组来记录每一列是否有皇后,从而指示选中的格子是否合法。对于对角线约束,我们借助两个数组来分别记录该主、次对角线上是否存在皇后;难点在于找处在到同一主(副)对角线上格子满足的行列索引规律。 ### Q & A diff --git a/docs/chapter_computational_complexity/space_complexity.md b/docs/chapter_computational_complexity/space_complexity.md index 39d2371b0..8d914a55a 100755 --- a/docs/chapter_computational_complexity/space_complexity.md +++ b/docs/chapter_computational_complexity/space_complexity.md @@ -471,12 +471,12 @@ return 0 def loop(n: int): - """循环 O(1)""" + """循环的空间复杂度为 O(1)""" for _ in range(n): function() def recur(n: int) -> int: - """递归 O(n)""" + """递归的空间复杂度为 O(n)""" if n == 1: return return recur(n - 1) ``` diff --git a/docs/chapter_computational_complexity/time_complexity.md b/docs/chapter_computational_complexity/time_complexity.md index f0ed19887..b7ee15386 100755 --- a/docs/chapter_computational_complexity/time_complexity.md +++ b/docs/chapter_computational_complexity/time_complexity.md @@ -988,7 +988,7 @@ $$ 生物学的“细胞分裂”是指数阶增长的典型例子:初始状态为 $1$ 个细胞,分裂一轮后变为 $2$ 个,分裂两轮后变为 $4$ 个,以此类推,分裂 $n$ 轮后有 $2^n$ 个细胞。 -下图和以下代码模拟了细胞分裂的过程,时间复杂度为 $O(2^n)$ 。 +下图和以下代码模拟了细胞分裂的过程,时间复杂度为 $O(2^n)$ : ```src [file]{time_complexity}-[class]{}-[func]{exponential} diff --git a/docs/chapter_data_structure/basic_data_types.md b/docs/chapter_data_structure/basic_data_types.md index 71dca5876..31c8d46e3 100644 --- a/docs/chapter_data_structure/basic_data_types.md +++ b/docs/chapter_data_structure/basic_data_types.md @@ -13,8 +13,8 @@ 基本数据类型的取值范围取决于其占用的空间大小。下面以 Java 为例。 -- 整数类型 `byte` 占用 $1$ byte = $8$ bits ,可以表示 $2^{8}$ 个数字。 -- 整数类型 `int` 占用 $4$ bytes = $32$ bits ,可以表示 $2^{32}$ 个数字。 +- 整数类型 `byte` 占用 $1$ 字节 = $8$ 比特 ,可以表示 $2^{8}$ 个数字。 +- 整数类型 `int` 占用 $4$ 字节 = $32$ 比特 ,可以表示 $2^{32}$ 个数字。 下表列举了 Java 中各种基本数据类型的占用空间、取值范围和默认值。此表格无须死记硬背,大致理解即可,需要时可以通过查表来回忆。 @@ -22,25 +22,25 @@ | 类型 | 符号 | 占用空间 | 最小值 | 最大值 | 默认值 | | ------ | -------- | -------- | ------------------------ | ----------------------- | -------------- | -| 整数 | `byte` | 1 byte | $-2^7$ ($-128$) | $2^7 - 1$ ($127$) | $0$ | -| | `short` | 2 bytes | $-2^{15}$ | $2^{15} - 1$ | $0$ | -| | `int` | 4 bytes | $-2^{31}$ | $2^{31} - 1$ | $0$ | -| | `long` | 8 bytes | $-2^{63}$ | $2^{63} - 1$ | $0$ | -| 浮点数 | `float` | 4 bytes | $1.175 \times 10^{-38}$ | $3.403 \times 10^{38}$ | $0.0\text{f}$ | -| | `double` | 8 bytes | $2.225 \times 10^{-308}$ | $1.798 \times 10^{308}$ | $0.0$ | -| 字符 | `char` | 2 bytes | $0$ | $2^{16} - 1$ | $0$ | -| 布尔 | `bool` | 1 byte | $\text{false}$ | $\text{true}$ | $\text{false}$ | +| 整数 | `byte` | 1 字节 | $-2^7$ ($-128$) | $2^7 - 1$ ($127$) | $0$ | +| | `short` | 2 字节 | $-2^{15}$ | $2^{15} - 1$ | $0$ | +| | `int` | 4 字节 | $-2^{31}$ | $2^{31} - 1$ | $0$ | +| | `long` | 8 字节 | $-2^{63}$ | $2^{63} - 1$ | $0$ | +| 浮点数 | `float` | 4 字节 | $1.175 \times 10^{-38}$ | $3.403 \times 10^{38}$ | $0.0\text{f}$ | +| | `double` | 8 字节 | $2.225 \times 10^{-308}$ | $1.798 \times 10^{308}$ | $0.0$ | +| 字符 | `char` | 2 字节 | $0$ | $2^{16} - 1$ | $0$ | +| 布尔 | `bool` | 1 字节 | $\text{false}$ | $\text{true}$ | $\text{false}$ | 请注意,上表针对的是 Java 的基本数据类型的情况。每种编程语言都有各自的数据类型定义,它们的占用空间、取值范围和默认值可能会有所不同。 - 在 Python 中,整数类型 `int` 可以是任意大小,只受限于可用内存;浮点数 `float` 是双精度 64 位;没有 `char` 类型,单个字符实际上是长度为 1 的字符串 `str` 。 -- C 和 C++ 未明确规定基本数据类型大小,而因实现和平台各异。上表遵循 LP64 [数据模型](https://en.cppreference.com/w/cpp/language/types#Properties),其用于包括 Linux 和 macOS 在内的 Unix 64 位操作系统。 +- C 和 C++ 未明确规定基本数据类型的大小,而因实现和平台各异。上表遵循 LP64 [数据模型](https://en.cppreference.com/w/cpp/language/types#Properties),其用于包括 Linux 和 macOS 在内的 Unix 64 位操作系统。 - 字符 `char` 的大小在 C 和 C++ 中为 1 字节,在大多数编程语言中取决于特定的字符编码方法,详见“字符编码”章节。 -- 即使表示布尔量仅需 1 位($0$ 或 $1$),它在内存中通常存储为 1 字节。这是因为现代计算机 CPU 通常将 1 字节作为最小寻址内存单元。 +- 即使表示布尔量仅需 1 位($0$ 或 $1$),它在内存中通常也存储为 1 字节。这是因为现代计算机 CPU 通常将 1 字节作为最小寻址内存单元。 那么,基本数据类型与数据结构之间有什么联系呢?我们知道,数据结构是在计算机中组织与存储数据的方式。这句话的主语是“结构”而非“数据”。 -如果想表示“一排数字”,我们自然会想到使用数组。这是因为数组的线性结构可以表示数字的相邻关系和顺序关系,但至于存储的内容是整数 `int`、小数 `float` 或是字符 `char` ,则与“数据结构”无关。 +如果想表示“一排数字”,我们自然会想到使用数组。这是因为数组的线性结构可以表示数字的相邻关系和顺序关系,但至于存储的内容是整数 `int`、小数 `float` 还是字符 `char` ,则与“数据结构”无关。 换句话说,**基本数据类型提供了数据的“内容类型”,而数据结构提供了数据的“组织方式”**。例如以下代码,我们用相同的数据结构(数组)来存储与表示不同的基本数据类型,包括 `int`、`float`、`char`、`bool` 等。 diff --git a/docs/chapter_data_structure/character_encoding.md b/docs/chapter_data_structure/character_encoding.md index 2bbdbbd78..221ec47c5 100644 --- a/docs/chapter_data_structure/character_encoding.md +++ b/docs/chapter_data_structure/character_encoding.md @@ -8,7 +8,7 @@ ![ASCII 码](character_encoding.assets/ascii_table.png) -然而,**ASCII 码仅能够表示英文**。随着计算机的全球化,诞生了一种能够表示更多语言的字符集「EASCII」。它在 ASCII 的 7 位基础上扩展到 8 位,能够表示 256 个不同的字符。 +然而,**ASCII 码仅能够表示英文**。随着计算机的全球化,诞生了一种能够表示更多语言的「EASCII」字符集。它在 ASCII 的 7 位基础上扩展到 8 位,能够表示 256 个不同的字符。 在世界范围内,陆续出现了一批适用于不同地区的 EASCII 字符集。这些字符集的前 128 个字符统一为 ASCII 码,后 128 个字符定义不同,以适应不同语言的需求。 @@ -64,7 +64,7 @@ UTF-8 的编码规则并不复杂,分为以下两种情况。 ## 编程语言的字符编码 -对于以往的大多数编程语言,程序运行中的字符串都采用 UTF-16 或 UTF-32 这类等长的编码。在等长编码下,我们可以将字符串看作数组来处理,这种做法具有以下优点。 +对于以往的大多数编程语言,程序运行中的字符串都采用 UTF-16 或 UTF-32 这类等长编码。在等长编码下,我们可以将字符串看作数组来处理,这种做法具有以下优点。 - **随机访问**:UTF-16 编码的字符串可以很容易地进行随机访问。UTF-8 是一种变长编码,要想找到第 $i$ 个字符,我们需要从字符串的开始处遍历到第 $i$ 个字符,这需要 $O(n)$ 的时间。 - **字符计数**:与随机访问类似,计算 UTF-16 编码的字符串的长度也是 $O(1)$ 的操作。但是,计算 UTF-8 编码的字符串的长度需要遍历整个字符串。 diff --git a/docs/chapter_data_structure/classification_of_data_structure.md b/docs/chapter_data_structure/classification_of_data_structure.md index b0938d1c2..42bb7230f 100644 --- a/docs/chapter_data_structure/classification_of_data_structure.md +++ b/docs/chapter_data_structure/classification_of_data_structure.md @@ -42,7 +42,7 @@ - **基于数组可实现**:栈、队列、哈希表、树、堆、图、矩阵、张量(维度 $\geq 3$ 的数组)等。 - **基于链表可实现**:栈、队列、哈希表、树、堆、图等。 -基于数组实现的数据结构也称“静态数据结构”,这意味着此类数据结构在初始化后长度不可变。相对应地,基于链表实现的数据结构称“动态数据结构”,这类数据结构在初始化后,仍可以在程序运行过程中对其长度进行调整。 +基于数组实现的数据结构也称“静态数据结构”,这意味着此类数据结构在初始化后长度不可变。相对应地,基于链表实现的数据结构也称“动态数据结构”,这类数据结构在初始化后,仍可以在程序运行过程中对其长度进行调整。 !!! tip diff --git a/docs/chapter_data_structure/number_encoding.md b/docs/chapter_data_structure/number_encoding.md index e32f05f45..aecbf238d 100644 --- a/docs/chapter_data_structure/number_encoding.md +++ b/docs/chapter_data_structure/number_encoding.md @@ -4,7 +4,7 @@ 在本书中,标题带有 * 符号的是选读章节。如果你时间有限或感到理解困难,可以先跳过,等学完必读章节后再单独攻克。 -## 整数编码 +## 原码、反码和补码 在上一节的表格中我们发现,所有整数类型能够表示的负数都比正数多一个,例如 `byte` 的取值范围是 $[-128, 127]$ 。这个现象比较反直觉,它的内在原因涉及原码、反码、补码的相关知识。 @@ -88,9 +88,9 @@ $$ ## 浮点数编码 -细心的你可能会发现:`int` 和 `float` 长度相同,都是 4 bytes ,但为什么 `float` 的取值范围远大于 `int` ?这非常反直觉,因为按理说 `float` 需要表示小数,取值范围应该变小才对。 +细心的你可能会发现:`int` 和 `float` 长度相同,都是 4 字节 ,但为什么 `float` 的取值范围远大于 `int` ?这非常反直觉,因为按理说 `float` 需要表示小数,取值范围应该变小才对。 -实际上,**这是因为浮点数 `float` 采用了不同的表示方式**。记一个 32-bit 长度的二进制数为: +实际上,**这是因为浮点数 `float` 采用了不同的表示方式**。记一个 32 位长度的二进制数为: $$ b_{31} b_{30} b_{29} \ldots b_2 b_1 b_0 @@ -98,9 +98,9 @@ $$ 根据 IEEE 754 标准,32-bit 长度的 `float` 由以下三个部分构成。 -- 符号位 $\mathrm{S}$ :占 1 bit ,对应 $b_{31}$ 。 -- 指数位 $\mathrm{E}$ :占 8 bits ,对应 $b_{30} b_{29} \ldots b_{23}$ 。 -- 分数位 $\mathrm{N}$ :占 23 bits ,对应 $b_{22} b_{21} \ldots b_0$ 。 +- 符号位 $\mathrm{S}$ :占 1 位 ,对应 $b_{31}$ 。 +- 指数位 $\mathrm{E}$ :占 8 位 ,对应 $b_{30} b_{29} \ldots b_{23}$ 。 +- 分数位 $\mathrm{N}$ :占 23 位 ,对应 $b_{22} b_{21} \ldots b_0$ 。 二进制数 `float` 对应值的计算方法为: diff --git a/docs/chapter_data_structure/summary.md b/docs/chapter_data_structure/summary.md index 9add8e3cd..cd72deaa0 100644 --- a/docs/chapter_data_structure/summary.md +++ b/docs/chapter_data_structure/summary.md @@ -10,8 +10,8 @@ - 原码、反码和补码是在计算机中编码数字的三种方法,它们之间可以相互转换。整数的原码的最高位是符号位,其余位是数字的值。 - 整数在计算机中是以补码的形式存储的。在补码表示下,计算机可以对正数和负数的加法一视同仁,不需要为减法操作单独设计特殊的硬件电路,并且不存在正负零歧义的问题。 - 浮点数的编码由 1 位符号位、8 位指数位和 23 位分数位构成。由于存在指数位,因此浮点数的取值范围远大于整数,代价是牺牲了精度。 -- ASCII 码是最早出现的英文字符集,长度为 1 字节,共收录 127 个字符。GBK 字符集是常用的中文字符集,共收录两万多个汉字。Unicode 致力于提供一个完整的字符集标准,收录世界内各种语言的字符,从而解决由于字符编码方法不一致而导致的乱码问题。 -- UTF-8 是最受欢迎的 Unicode 编码方法,通用性非常好。它是一种变长的编码方法,具有很好的扩展性,有效提升了存储空间的使用效率。UTF-16 和 UTF-32 是等长的编码方法。在编码中文时,UTF-16 比 UTF-8 的占用空间更小。Java 和 C# 等编程语言默认使用 UTF-16 编码。 +- ASCII 码是最早出现的英文字符集,长度为 1 字节,共收录 127 个字符。GBK 字符集是常用的中文字符集,共收录两万多个汉字。Unicode 致力于提供一个完整的字符集标准,收录世界上各种语言的字符,从而解决由于字符编码方法不一致而导致的乱码问题。 +- UTF-8 是最受欢迎的 Unicode 编码方法,通用性非常好。它是一种变长的编码方法,具有很好的扩展性,有效提升了存储空间的使用效率。UTF-16 和 UTF-32 是等长的编码方法。在编码中文时,UTF-16 占用的空间比 UTF-8 更小。Java 和 C# 等编程语言默认使用 UTF-16 编码。 ### Q & A @@ -20,14 +20,14 @@ 哈希表底层是数组,而为了解决哈希冲突,我们可能会使用“链式地址”(后续“哈希冲突”章节会讲):数组中每个桶指向一个链表,当链表长度超过一定阈值时,又可能被转化为树(通常为红黑树)。 从存储的角度来看,哈希表的底层是数组,其中每一个桶槽位可能包含一个值,也可能包含一个链表或一棵树。因此,哈希表可能同时包含线性数据结构(数组、链表)和非线性数据结构(树)。 -!!! question "`char` 类型的长度是 1 byte 吗?" +!!! question "`char` 类型的长度是 1 字节吗?" - `char` 类型的长度由编程语言采用的编码方法决定。例如,Java、JavaScript、TypeScript、C# 都采用 UTF-16 编码(保存 Unicode 码点),因此 char 类型的长度为 2 bytes。 + `char` 类型的长度由编程语言采用的编码方法决定。例如,Java、JavaScript、TypeScript、C# 都采用 UTF-16 编码(保存 Unicode 码点),因此 `char` 类型的长度为 2 字节。 -!!! question "基于数组实现的数据结构也称“静态数据结构” 是否有歧义?因为栈也可以进行出栈和入栈等操作,这些操作都是“动态”的。" +!!! question "基于数组实现的数据结构也称“静态数据结构” 是否有歧义?栈也可以进行出栈和入栈等操作,这些操作都是“动态”的。" 栈确实可以实现动态的数据操作,但数据结构仍然是“静态”(长度不可变)的。尽管基于数组的数据结构可以动态地添加或删除元素,但它们的容量是固定的。如果数据量超出了预分配的大小,就需要创建一个新的更大的数组,并将旧数组的内容复制到新数组中。 !!! question "在构建栈(队列)的时候,未指定它的大小,为什么它们是“静态数据结构”呢?" - 在高级编程语言中,我们无须人工指定栈(队列)的初始容量,这个工作由类内部自动完成。例如,Java 的 ArrayList 的初始容量通常为 10。另外,扩容操作也是自动实现的。详见后续的“列表”章节。 + 在高级编程语言中,我们无须人工指定栈(队列)的初始容量,这个工作由类内部自动完成。例如,Java 的 `ArrayList` 的初始容量通常为 10。另外,扩容操作也是自动实现的。详见后续的“列表”章节。 diff --git a/docs/chapter_divide_and_conquer/build_binary_tree_problem.md b/docs/chapter_divide_and_conquer/build_binary_tree_problem.md index 69e6aafad..69c68c53e 100644 --- a/docs/chapter_divide_and_conquer/build_binary_tree_problem.md +++ b/docs/chapter_divide_and_conquer/build_binary_tree_problem.md @@ -27,7 +27,7 @@ 1. 前序遍历的首元素 3 是根节点的值。 2. 查找根节点 3 在 `inorder` 中的索引,利用该索引可将 `inorder` 划分为 `[ 9 | 3 | 1 2 7 ]` 。 -3. 根据 `inorder` 划分结果,易得左子树和右子树的节点数量分别为 1 和 3 ,从而可将 `preorder` 划分为 `[ 3 | 9 | 2 1 7 ]` 。 +3. 根据 `inorder` 的划分结果,易得左子树和右子树的节点数量分别为 1 和 3 ,从而可将 `preorder` 划分为 `[ 3 | 9 | 2 1 7 ]` 。 ![在前序遍历和中序遍历中划分子树](build_binary_tree_problem.assets/build_tree_preorder_inorder_division.png) @@ -49,7 +49,7 @@ | 左子树 | $i + 1$ | $[l, m-1]$ | | 右子树 | $i + 1 + (m - l)$ | $[m+1, r]$ | -请注意,右子树根节点索引中的 $(m-l)$ 的含义是“左子树的节点数量”,建议配合下图理解。 +请注意,右子树根节点索引中的 $(m-l)$ 的含义是“左子树的节点数量”,建议结合下图理解。 ![根节点和左右子树的索引区间表示](build_binary_tree_problem.assets/build_tree_division_pointers.png) diff --git a/docs/chapter_divide_and_conquer/divide_and_conquer.md b/docs/chapter_divide_and_conquer/divide_and_conquer.md index 176ff8b0b..28757da77 100644 --- a/docs/chapter_divide_and_conquer/divide_and_conquer.md +++ b/docs/chapter_divide_and_conquer/divide_and_conquer.md @@ -20,7 +20,7 @@ 2. **子问题是独立的**:子问题之间没有重叠,互不依赖,可以独立解决。 3. **子问题的解可以合并**:原问题的解通过合并子问题的解得来。 -显然,归并排序满足以上三条判断依据。 +显然,归并排序满足以上三个判断依据。 1. **问题可以分解**:递归地将数组(原问题)划分为两个子数组(子问题)。 2. **子问题是独立的**:每个子数组都可以独立地进行排序(子问题可以独立进行求解)。 @@ -78,7 +78,7 @@ $$ - **汉诺塔问题**:汉诺塔问题可以通过递归解决,这是典型的分治策略应用。 - **求解逆序对**:在一个序列中,如果前面的数字大于后面的数字,那么这两个数字构成一个逆序对。求解逆序对问题可以利用分治的思想,借助归并排序进行求解。 -另一方面,分治在算法和数据结构的设计中应用非常广泛。 +另一方面,分治在算法和数据结构的设计中应用得非常广泛。 - **二分查找**:二分查找是将有序数组从中点索引处分为两部分,然后根据目标值与中间元素值比较结果,决定排除哪一半区间,并在剩余区间执行相同的二分操作。 - **归并排序**:本节开头已介绍,不再赘述。 @@ -86,6 +86,6 @@ $$ - **桶排序**:桶排序的基本思想是将数据分散到多个桶,然后对每个桶内的元素进行排序,最后将各个桶的元素依次取出,从而得到一个有序数组。 - **树**:例如二叉搜索树、AVL 树、红黑树、B 树、B+ 树等,它们的查找、插入和删除等操作都可以视为分治策略的应用。 - **堆**:堆是一种特殊的完全二叉树,其各种操作,如插入、删除和堆化,实际上都隐含了分治的思想。 -- **哈希表**:虽然哈希表来并不直接应用分治,但某些哈希冲突解决方案间接应用了分治策略,例如,链式地址中的长链表会被转化为红黑树,以提升查询效率。 +- **哈希表**:虽然哈希表并不直接应用分治,但某些哈希冲突解决方案间接应用了分治策略,例如,链式地址中的长链表会被转化为红黑树,以提升查询效率。 可以看出,**分治是一种“润物细无声”的算法思想**,隐含在各种算法与数据结构之中。 diff --git a/docs/chapter_divide_and_conquer/hanota_problem.md b/docs/chapter_divide_and_conquer/hanota_problem.md index 22f8813b3..f892dd770 100644 --- a/docs/chapter_divide_and_conquer/hanota_problem.md +++ b/docs/chapter_divide_and_conquer/hanota_problem.md @@ -19,7 +19,7 @@ 如下图所示,对于问题 $f(1)$ ,即当只有一个圆盘时,我们将它直接从 `A` 移动至 `C` 即可。 === "<1>" - ![规模为 1 问题的解](hanota_problem.assets/hanota_f1_step1.png) + ![规模为 1 的问题的解](hanota_problem.assets/hanota_f1_step1.png) === "<2>" ![hanota_f1_step2](hanota_problem.assets/hanota_f1_step2.png) @@ -31,7 +31,7 @@ 3. 最后将小圆盘从 `B` 移至 `C` 。 === "<1>" - ![规模为 2 问题的解](hanota_problem.assets/hanota_f2_step1.png) + ![规模为 2 的问题的解](hanota_problem.assets/hanota_f2_step1.png) === "<2>" ![hanota_f2_step2](hanota_problem.assets/hanota_f2_step2.png) @@ -55,7 +55,7 @@ 3. 令 `C` 为目标柱、`A` 为缓冲柱,将两个圆盘从 `B` 移至 `C` 。 === "<1>" - ![规模为 3 问题的解](hanota_problem.assets/hanota_f3_step1.png) + ![规模为 3 的问题的解](hanota_problem.assets/hanota_f3_step1.png) === "<2>" ![hanota_f3_step2](hanota_problem.assets/hanota_f3_step2.png) @@ -66,7 +66,7 @@ === "<4>" ![hanota_f3_step4](hanota_problem.assets/hanota_f3_step4.png) -从本质上看,**我们将问题 $f(3)$ 划分为两个子问题 $f(2)$ 和子问题 $f(1)$** 。按顺序解决这三个子问题之后,原问题随之得到解决。这说明子问题是独立的,而且解可以合并。 +从本质上看,**我们将问题 $f(3)$ 划分为两个子问题 $f(2)$ 和一个子问题 $f(1)$** 。按顺序解决这三个子问题之后,原问题随之得到解决。这说明子问题是独立的,而且解可以合并。 至此,我们可总结出下图所示的解决汉诺塔问题的分治策略:将原问题 $f(n)$ 划分为两个子问题 $f(n-1)$ 和一个子问题 $f(1)$ ,并按照以下顺序解决这三个子问题。 diff --git a/docs/chapter_dynamic_programming/dp_problem_features.md b/docs/chapter_dynamic_programming/dp_problem_features.md index 830dfa226..e75f2f7f6 100644 --- a/docs/chapter_dynamic_programming/dp_problem_features.md +++ b/docs/chapter_dynamic_programming/dp_problem_features.md @@ -64,7 +64,7 @@ $$ ![带约束爬到第 3 阶的方案数量](dp_problem_features.assets/climbing_stairs_constraint_example.png) -在该问题中,如果上一轮是跳 $1$ 阶上来的,那么下一轮就必须跳 $2$ 阶。这意味着,**下一步选择不能由当前状态(当前所在楼梯阶数)独立决定,还和前一个状态(上轮所在楼梯阶数)有关**。 +在该问题中,如果上一轮是跳 $1$ 阶上来的,那么下一轮就必须跳 $2$ 阶。这意味着,**下一步选择不能由当前状态(当前所在楼梯阶数)独立决定,还和前一个状态(上一轮所在楼梯阶数)有关**。 不难发现,此问题已不满足无后效性,状态转移方程 $dp[i] = dp[i-1] + dp[i-2]$ 也失效了,因为 $dp[i-1]$ 代表本轮跳 $1$ 阶,但其中包含了许多“上一轮是跳 $1$ 阶上来的”方案,而为了满足约束,我们就不能将 $dp[i-1]$ 直接计入 $dp[i]$ 中。 diff --git a/docs/chapter_dynamic_programming/dp_solution_pipeline.md b/docs/chapter_dynamic_programming/dp_solution_pipeline.md index 7d77563a5..aff5d7dad 100644 --- a/docs/chapter_dynamic_programming/dp_solution_pipeline.md +++ b/docs/chapter_dynamic_programming/dp_solution_pipeline.md @@ -23,7 +23,7 @@ - 问题的目标是找出所有可能的解决方案,而不是找出最优解。 - 问题描述中有明显的排列组合的特征,需要返回具体的多个方案。 -如果一个问题满足决策树模型,并具有较为明显的“加分项“,我们就可以假设它是一个动态规划问题,并在求解过程中验证它。 +如果一个问题满足决策树模型,并具有较为明显的“加分项”,我们就可以假设它是一个动态规划问题,并在求解过程中验证它。 ## 问题求解步骤 diff --git a/docs/chapter_dynamic_programming/intro_to_dynamic_programming.md b/docs/chapter_dynamic_programming/intro_to_dynamic_programming.md index c2a3682fc..0e5f0d34f 100644 --- a/docs/chapter_dynamic_programming/intro_to_dynamic_programming.md +++ b/docs/chapter_dynamic_programming/intro_to_dynamic_programming.md @@ -54,7 +54,7 @@ $$ 观察上图,**指数阶的时间复杂度是“重叠子问题”导致的**。例如 $dp[9]$ 被分解为 $dp[8]$ 和 $dp[7]$ ,$dp[8]$ 被分解为 $dp[7]$ 和 $dp[6]$ ,两者都包含子问题 $dp[7]$ 。 -以此类推,子问题中包含更小的重叠子问题,子子孙孙无穷尽也。绝大部分计算资源都浪费在这些重叠的问题上。 +以此类推,子问题中包含更小的重叠子问题,子子孙孙无穷尽也。绝大部分计算资源都浪费在这些重叠的子问题上。 ## 方法二:记忆化搜索 diff --git a/docs/chapter_dynamic_programming/summary.md b/docs/chapter_dynamic_programming/summary.md index 697847c29..d6fd3c9a4 100644 --- a/docs/chapter_dynamic_programming/summary.md +++ b/docs/chapter_dynamic_programming/summary.md @@ -1,8 +1,8 @@ # 小结 -- 动态规划对问题进行分解,并通过存储子问题的解来规避重复计算,提高 计算效率。 +- 动态规划对问题进行分解,并通过存储子问题的解来规避重复计算,提高计算效率。 - 不考虑时间的前提下,所有动态规划问题都可以用回溯(暴力搜索)进行求解,但递归树中存在大量的重叠子问题,效率极低。通过引入记忆化列表,可以存储所有计算过的子问题的解,从而保证重叠子问题只被计算一次。 -- 记忆化递归是一种从顶至底的递归式解法,而与之对应的动态规划是一种从底至顶的递推式解法,其如同“填写表格”一样。由于当前状态仅依赖某些局部状态,因此我们可以消除 $dp$ 表的一个维度,从而降低空间复杂度。 +- 记忆化搜索是一种从顶至底的递归式解法,而与之对应的动态规划是一种从底至顶的递推式解法,其如同“填写表格”一样。由于当前状态仅依赖某些局部状态,因此我们可以消除 $dp$ 表的一个维度,从而降低空间复杂度。 - 子问题分解是一种通用的算法思路,在分治、动态规划、回溯中具有不同的性质。 - 动态规划问题有三大特性:重叠子问题、最优子结构、无后效性。 - 如果原问题的最优解可以从子问题的最优解构建得来,则它就具有最优子结构。 diff --git a/docs/chapter_graph/graph.md b/docs/chapter_graph/graph.md index d216780d5..e3e2cf0b2 100644 --- a/docs/chapter_graph/graph.md +++ b/docs/chapter_graph/graph.md @@ -30,7 +30,7 @@ $$ ![连通图与非连通图](graph.assets/connected_graph.png) -我们还可以为边添加“权重”变量,从而得到如下图所示的「有权图 weighted graph」。例如在“王者荣耀”等手游中,系统会根据共同游戏时间来计算玩家之间的“亲密度”,这种亲密度网络就可以用有权图来表示。 +我们还可以为边添加“权重”变量,从而得到如下图所示的「有权图 weighted graph」。例如在《王者荣耀》等手游中,系统会根据共同游戏时间来计算玩家之间的“亲密度”,这种亲密度网络就可以用有权图来表示。 ![有权图与无权图](graph.assets/weighted_graph.png) @@ -70,7 +70,7 @@ $$ 观察上图,**邻接表结构与哈希表中的“链式地址”非常相似,因此我们也可以采用类似的方法来优化效率**。比如当链表较长时,可以将链表转化为 AVL 树或红黑树,从而将时间效率从 $O(n)$ 优化至 $O(\log n)$ ;还可以把链表转换为哈希表,从而将时间复杂度降至 $O(1)$ 。 -## 图常见应用 +## 图的常见应用 如下表所示,许多现实系统可以用图来建模,相应的问题也可以约化为图计算问题。 diff --git a/docs/chapter_graph/graph_traversal.md b/docs/chapter_graph/graph_traversal.md index 276d5db7e..940cad159 100644 --- a/docs/chapter_graph/graph_traversal.md +++ b/docs/chapter_graph/graph_traversal.md @@ -2,7 +2,7 @@ 树代表的是“一对多”的关系,而图则具有更高的自由度,可以表示任意的“多对多”关系。因此,我们可以把树看作图的一种特例。显然,**树的遍历操作也是图的遍历操作的一种特例**。 -图和树都需要应用搜索算法来实现遍历操作。图的遍历方式可分为两种:「广度优先遍历 breadth-first traversal」和「深度优先遍历 depth-first traversal」。它们也常被称为「广度优先搜索 breadth-first search」和「深度优先搜索 depth-first search」,简称 BFS 和 DFS 。 +图和树都需要应用搜索算法来实现遍历操作。图的遍历方式也可分为两种:「广度优先遍历」和「深度优先遍历」。 ## 广度优先遍历 diff --git a/docs/chapter_greedy/fractional_knapsack_problem.md b/docs/chapter_greedy/fractional_knapsack_problem.md index 3c41c1491..22860ff4e 100644 --- a/docs/chapter_greedy/fractional_knapsack_problem.md +++ b/docs/chapter_greedy/fractional_knapsack_problem.md @@ -33,7 +33,7 @@ [file]{fractional_knapsack}-[class]{}-[func]{fractional_knapsack} ``` -在最差情况下,需要遍历整个物品列表,**因此时间复杂度为 $O(n)$** ,其中 $n$ 为物品数量。 +除排序之外,在最差情况下,需要遍历整个物品列表,**因此时间复杂度为 $O(n)$** ,其中 $n$ 为物品数量。 由于初始化了一个 `Item` 对象列表,**因此空间复杂度为 $O(n)$** 。 @@ -45,6 +45,6 @@ 对于该解中的其他物品,我们也可以构建出上述矛盾。总而言之,**单位价值更大的物品总是更优选择**,这说明贪心策略是有效的。 -如下图所示,如果将物品重量和物品单位价值分别看作一张二维图表的横轴和纵轴,则分数背包问题可转化为“求在有限横轴区间下的最大围成面积”。这个类比可以帮助我们从几何角度理解贪心策略的有效性。 +如下图所示,如果将物品重量和物品单位价值分别看作一张二维图表的横轴和纵轴,则分数背包问题可转化为“求在有限横轴区间下围成的最大面积”。这个类比可以帮助我们从几何角度理解贪心策略的有效性。 ![分数背包问题的几何表示](fractional_knapsack_problem.assets/fractional_knapsack_area_chart.png) diff --git a/docs/chapter_greedy/greedy_algorithm.md b/docs/chapter_greedy/greedy_algorithm.md index 55f9f3239..5f5ffc9d0 100644 --- a/docs/chapter_greedy/greedy_algorithm.md +++ b/docs/chapter_greedy/greedy_algorithm.md @@ -11,19 +11,21 @@ !!! question - 给定 $n$ 种硬币,第 $i$ 种硬币的面值为 $coins[i - 1]$ ,目标金额为 $amt$ ,每种硬币可以重复选取,问能够凑出目标金额的最少硬币数量。如果无法凑出目标金额则返回 $-1$ 。 + 给定 $n$ 种硬币,第 $i$ 种硬币的面值为 $coins[i - 1]$ ,目标金额为 $amt$ ,每种硬币可以重复选取,问能够凑出目标金额的最少硬币数量。如果无法凑出目标金额,则返回 $-1$ 。 本题采取的贪心策略如下图所示。给定目标金额,**我们贪心地选择不大于且最接近它的硬币**,不断循环该步骤,直至凑出目标金额为止。 ![零钱兑换的贪心策略](greedy_algorithm.assets/coin_change_greedy_strategy.png) -实现代码如下所示。你可能会不由地发出感叹:So clean !贪心算法仅用约十行代码就解决了零钱兑换问题: +实现代码如下所示: ```src [file]{coin_change_greedy}-[class]{}-[func]{coin_change_greedy} ``` -## 贪心的优点与局限性 +你可能会不由地发出感叹:So clean !贪心算法仅用约十行代码就解决了零钱兑换问题。 + +## 贪心算法的优点与局限性 **贪心算法不仅操作直接、实现简单,而且通常效率也很高**。在以上代码中,记硬币最小面值为 $\min(coins)$ ,则贪心选择最多循环 $amt / \min(coins)$ 次,时间复杂度为 $O(amt / \min(coins))$ 。这比动态规划解法的时间复杂度 $O(n \times amt)$ 提升了一个数量级。 @@ -33,7 +35,7 @@ - **反例 $coins = [1, 20, 50]$**:假设 $amt = 60$ ,贪心算法只能找到 $50 + 1 \times 10$ 的兑换组合,共计 $11$ 枚硬币,但动态规划可以找到最优解 $20 + 20 + 20$ ,仅需 $3$ 枚硬币。 - **反例 $coins = [1, 49, 50]$**:假设 $amt = 98$ ,贪心算法只能找到 $50 + 1 \times 48$ 的兑换组合,共计 $49$ 枚硬币,但动态规划可以找到最优解 $49 + 49$ ,仅需 $2$ 枚硬币。 -![贪心无法找出最优解的示例](greedy_algorithm.assets/coin_change_greedy_vs_dp.png) +![贪心算法无法找出最优解的示例](greedy_algorithm.assets/coin_change_greedy_vs_dp.png) 也就是说,对于零钱兑换问题,贪心算法无法保证找到全局最优解,并且有可能找到非常差的解。它更适合用动态规划解决。 @@ -61,9 +63,9 @@ 有一篇论文给出了一个 $O(n^3)$ 时间复杂度的算法,用于判断一个硬币组合能否使用贪心算法找出任意金额的最优解。 - Pearson, David. A polynomial-time algorithm for the change-making problem. Operations Research Letters 33.3 (2005): 231-234. + Pearson, D. A polynomial-time algorithm for the change-making problem[J]. Operations Research Letters, 2005, 33(3): 231-234. -## 贪心解题步骤 +## 贪心算法解题步骤 贪心问题的解决流程大体可分为以下三步。 @@ -80,7 +82,7 @@ 然而,正确性证明也很可能不是一件易事。如若没有头绪,我们通常会选择面向测试用例进行代码调试,一步步修改与验证贪心策略。 -## 贪心典型例题 +## 贪心算法典型例题 贪心算法常常应用在满足贪心选择性质和最优子结构的优化问题中,以下列举了一些典型的贪心算法问题。 diff --git a/docs/chapter_greedy/max_capacity_problem.md b/docs/chapter_greedy/max_capacity_problem.md index 75c2e9e9c..16d1ec6a8 100644 --- a/docs/chapter_greedy/max_capacity_problem.md +++ b/docs/chapter_greedy/max_capacity_problem.md @@ -36,11 +36,11 @@ $$ ![向内移动短板后的状态](max_capacity_problem.assets/max_capacity_moving_short_board.png) -由此便可推出本题的贪心策略:初始化两指针分列容器两端,每轮向内收缩短板对应的指针,直至两指针相遇。 +由此便可推出本题的贪心策略:初始化两指针,使其分列容器两端,每轮向内收缩短板对应的指针,直至两指针相遇。 下图展示了贪心策略的执行过程。 -1. 初始状态下,指针 $i$ 和 $j$ 分列与数组两端。 +1. 初始状态下,指针 $i$ 和 $j$ 分列数组两端。 2. 计算当前状态的容量 $cap[i, j]$ ,并更新最大容量。 3. 比较板 $i$ 和 板 $j$ 的高度,并将短板向内移动一格。 4. 循环执行第 `2.` 步和第 `3.` 步,直至 $i$ 和 $j$ 相遇时结束。 diff --git a/docs/chapter_greedy/max_product_cutting_problem.md b/docs/chapter_greedy/max_product_cutting_problem.md index 190f65b27..e282a9293 100644 --- a/docs/chapter_greedy/max_product_cutting_problem.md +++ b/docs/chapter_greedy/max_product_cutting_problem.md @@ -50,7 +50,7 @@ $$ 1. 输入整数 $n$ ,从其不断地切分出因子 $3$ ,直至余数为 $0$、$1$、$2$ 。 2. 当余数为 $0$ 时,代表 $n$ 是 $3$ 的倍数,因此不做任何处理。 -3. 当余数为 $2$ 时,不继续划分,保留之。 +3. 当余数为 $2$ 时,不继续划分,保留。 4. 当余数为 $1$ 时,由于 $2 \times 2 > 1 \times 3$ ,因此应将最后一个 $3$ 替换为 $2$ 。 ### 代码实现 diff --git a/docs/chapter_greedy/summary.md b/docs/chapter_greedy/summary.md index 28d9feef8..caefed243 100644 --- a/docs/chapter_greedy/summary.md +++ b/docs/chapter_greedy/summary.md @@ -9,4 +9,4 @@ - 求解贪心问题主要分为三步:问题分析、确定贪心策略、正确性证明。其中,确定贪心策略是核心步骤,正确性证明往往是难点。 - 分数背包问题在 0-1 背包的基础上,允许选择物品的一部分,因此可使用贪心算法求解。贪心策略的正确性可以使用反证法来证明。 - 最大容量问题可使用穷举法求解,时间复杂度为 $O(n^2)$ 。通过设计贪心策略,每轮向内移动短板,可将时间复杂度优化至 $O(n)$ 。 -- 在最大切分乘积问题中,我们先后推理出两个贪心策略:$\geq 4$ 的整数都应该继续切分、最优切分因子为 $3$ 。代码中包含幂运算,时间复杂度取决于幂运算实现方法,通常为 $O(1)$ 或 $O(\log n)$ 。 +- 在最大切分乘积问题中,我们先后推理出两个贪心策略:$\geq 4$ 的整数都应该继续切分,最优切分因子为 $3$ 。代码中包含幂运算,时间复杂度取决于幂运算实现方法,通常为 $O(1)$ 或 $O(\log n)$ 。 diff --git a/docs/chapter_hashing/hash_algorithm.md b/docs/chapter_hashing/hash_algorithm.md index 802f46824..55fcd5bb0 100644 --- a/docs/chapter_hashing/hash_algorithm.md +++ b/docs/chapter_hashing/hash_algorithm.md @@ -82,7 +82,7 @@ $$ 不难发现,以上介绍的简单哈希算法都比较“脆弱”,远远没有达到哈希算法的设计目标。例如,由于加法和异或满足交换律,因此加法哈希和异或哈希无法区分内容相同但顺序不同的字符串,这可能会加剧哈希冲突,并引起一些安全问题。 -在实际中,我们通常会用一些标准哈希算法,例如 MD5、SHA-1、SHA-2、SHA-3 等。它们可以将任意长度的输入数据映射到恒定长度的哈希值。 +在实际中,我们通常会用一些标准哈希算法,例如 MD5、SHA-1、SHA-2 和 SHA-3 等。它们可以将任意长度的输入数据映射到恒定长度的哈希值。 近一个世纪以来,哈希算法处在不断升级与优化的过程中。一部分研究人员努力提升哈希算法的性能,另一部分研究人员和黑客则致力于寻找哈希算法的安全性问题。下表展示了在实际应用中常见的哈希算法。 @@ -92,13 +92,13 @@ $$

  常见的哈希算法

-| | MD5 | SHA-1 | SHA-2 | SHA-3 | -| -------- | ------------------------------ | ---------------- | ---------------------------- | -------------------- | -| 推出时间 | 1992 | 1995 | 2002 | 2008 | -| 输出长度 | 128 bits | 160 bits | 256/512 bits | 224/256/384/512 bits | -| 哈希冲突 | 较多 | 较多 | 很少 | 很少 | -| 安全等级 | 低,已被成功攻击 | 低,已被成功攻击 | 高 | 高 | -| 应用 | 已被弃用,仍用于数据完整性检查 | 已被弃用 | 加密货币交易验证、数字签名等 | 可用于替代 SHA-2 | +| | MD5 | SHA-1 | SHA-2 | SHA-3 | +| -------- | ------------------------------ | ---------------- | ---------------------------- | ------------------- | +| 推出时间 | 1992 | 1995 | 2002 | 2008 | +| 输出长度 | 128 bit | 160 bit | 256/512 bit | 224/256/384/512 bit | +| 哈希冲突 | 较多 | 较多 | 很少 | 很少 | +| 安全等级 | 低,已被成功攻击 | 低,已被成功攻击 | 高 | 高 | +| 应用 | 已被弃用,仍用于数据完整性检查 | 已被弃用 | 加密货币交易验证、数字签名等 | 可用于替代 SHA-2 | ## 数据结构的哈希值 @@ -354,4 +354,4 @@ $$ 虽然自定义对象(比如链表节点)的成员变量是可变的,但它是可哈希的。**这是因为对象的哈希值通常是基于内存地址生成的**,即使对象的内容发生了变化,但它的内存地址不变,哈希值仍然是不变的。 -细心的你可能发现在不同控制台中运行程序时,输出的哈希值是不同的。**这是因为 Python 解释器在每次启动时,都会为字符串哈希函数加入一个随机的盐(Salt)值**。这种做法可以有效防止 HashDoS 攻击,提升哈希算法的安全性。 +细心的你可能发现在不同控制台中运行程序时,输出的哈希值是不同的。**这是因为 Python 解释器在每次启动时,都会为字符串哈希函数加入一个随机的盐(salt)值**。这种做法可以有效防止 HashDoS 攻击,提升哈希算法的安全性。 diff --git a/docs/chapter_hashing/hash_collision.md b/docs/chapter_hashing/hash_collision.md index 787d2e5b9..d28e13878 100644 --- a/docs/chapter_hashing/hash_collision.md +++ b/docs/chapter_hashing/hash_collision.md @@ -2,7 +2,7 @@ 上一节提到,**通常情况下哈希函数的输入空间远大于输出空间**,因此理论上哈希冲突是不可避免的。比如,输入空间为全体整数,输出空间为数组容量大小,则必然有多个整数映射至同一桶索引。 -哈希冲突会导致查询结果错误,严重影响哈希表的可用性。为解决该问题,我们可以每当遇到哈希冲突就进行哈希表扩容,直至冲突消失为止。此方法简单粗暴且有效,但效率太低,因为哈希表扩容需要进行大量的数据搬运与哈希值计算。为了提升效率,我们可以采用以下策略。 +哈希冲突会导致查询结果错误,严重影响哈希表的可用性。为了解决该问题,每当遇到哈希冲突时,我们就进行哈希表扩容,直至冲突消失为止。此方法简单粗暴且有效,但效率太低,因为哈希表扩容需要进行大量的数据搬运与哈希值计算。为了提升效率,我们可以采用以下策略。 1. 改良哈希表数据结构,**使得哈希表可以在出现哈希冲突时正常工作**。 2. 仅在必要时,即当哈希冲突比较严重时,才执行扩容操作。 @@ -23,8 +23,8 @@ 链式地址存在以下局限性。 -- **占用空间增大**,链表包含节点指针,它相比数组更加耗费内存空间。 -- **查询效率降低**,因为需要线性遍历链表来查找对应元素。 +- **占用空间增大**:链表包含节点指针,它相比数组更加耗费内存空间。 +- **查询效率降低**:因为需要线性遍历链表来查找对应元素。 以下代码给出了链式地址哈希表的简单实现,需要注意两点。 @@ -39,7 +39,7 @@ ## 开放寻址 -「开放寻址 open addressing」不引入额外的数据结构,而是通过“多次探测”来处理哈希冲突,探测方式主要包括线性探测、平方探测、多次哈希等。 +「开放寻址 open addressing」不引入额外的数据结构,而是通过“多次探测”来处理哈希冲突,探测方式主要包括线性探测、平方探测和多次哈希等。 下面以线性探测为例,介绍开放寻址哈希表的工作机制。 @@ -48,19 +48,19 @@ 线性探测采用固定步长的线性搜索来进行探测,其操作方法与普通哈希表有所不同。 - **插入元素**:通过哈希函数计算桶索引,若发现桶内已有元素,则从冲突位置向后线性遍历(步长通常为 $1$ ),直至找到空桶,将元素插入其中。 -- **查找元素**:若发现哈希冲突,则使用相同步长向后线性遍历,直到找到对应元素,返回 `value` 即可;如果遇到空桶,说明目标元素不在哈希表中,返回 $\text{None}$ 。 +- **查找元素**:若发现哈希冲突,则使用相同步长向后进行线性遍历,直到找到对应元素,返回 `value` 即可;如果遇到空桶,说明目标元素不在哈希表中,返回 `None` 。 下图展示了开放寻址(线性探测)哈希表的键值对分布。根据此哈希函数,最后两位相同的 `key` 都会被映射到相同的桶。而通过线性探测,它们被依次存储在该桶以及之下的桶中。 -![开放寻址和线性探测](hash_collision.assets/hash_table_linear_probing.png) +![开放寻址(线性探测)哈希表的键值对分布](hash_collision.assets/hash_table_linear_probing.png) 然而,**线性探测容易产生“聚集现象”**。具体来说,数组中连续被占用的位置越长,这些连续位置发生哈希冲突的可能性越大,从而进一步促使该位置的聚堆生长,形成恶性循环,最终导致增删查改操作效率劣化。 -值得注意的是,**我们不能在开放寻址哈希表中直接删除元素**。这是因为删除元素会在数组内产生一个空桶 $\text{None}$ ,而当查询元素时,线性探测到该空桶就会返回,因此在该空桶之下的元素都无法再被访问到,程序可能误判这些元素不存在。 +值得注意的是,**我们不能在开放寻址哈希表中直接删除元素**。这是因为删除元素会在数组内产生一个空桶 `None` ,而当查询元素时,线性探测到该空桶就会返回,因此在该空桶之下的元素都无法再被访问到,程序可能误判这些元素不存在。 ![在开放寻址中删除元素导致的查询问题](hash_collision.assets/hash_table_open_addressing_deletion.png) -为了解决该问题,我们可以采用「懒删除 lazy deletion」机制:它不直接从哈希表中移除元素,**而是利用一个常量 `TOMBSTONE` 来标记这个桶**。在该机制下,$\text{None}$ 和 `TOMBSTONE` 都代表空桶,都可以放置键值对。但不同的是,线性探测到 `TOMBSTONE` 时应该继续遍历,因为其之下可能还存在键值对。 +为了解决该问题,我们可以采用「懒删除 lazy deletion」机制:它不直接从哈希表中移除元素,**而是利用一个常量 `TOMBSTONE` 来标记这个桶**。在该机制下,`None` 和 `TOMBSTONE` 都代表空桶,都可以放置键值对。但不同的是,线性探测到 `TOMBSTONE` 时应该继续遍历,因为其之下可能还存在键值对。 然而,**懒删除可能会加速哈希表的性能退化**。这是因为每次删除操作都会产生一个删除标记,随着 `TOMBSTONE` 的增加,搜索时间也会增加,因为线性探测可能需要跳过多个 `TOMBSTONE` 才能找到目标元素。 @@ -90,8 +90,8 @@ 顾名思义,多次哈希方法使用多个哈希函数 $f_1(x)$、$f_2(x)$、$f_3(x)$、$\dots$ 进行探测。 -- **插入元素**:若哈希函数 $f_1(x)$ 出现冲突,则尝试 $f_2(x)$ ,以此类推,直到找到空桶后插入元素。 -- **查找元素**:在相同的哈希函数顺序下进行查找,直到找到目标元素时返回;若遇到空桶或已尝试所有哈希函数,说明哈希表中不存在该元素,则返回 $\text{None}$ 。 +- **插入元素**:若哈希函数 $f_1(x)$ 出现冲突,则尝试 $f_2(x)$ ,以此类推,直到找到空位后插入元素。 +- **查找元素**:在相同的哈希函数顺序下进行查找,直到找到目标元素时返回;若遇到空位或已尝试所有哈希函数,说明哈希表中不存在该元素,则返回 `None` 。 与线性探测相比,多次哈希方法不易产生聚集,但多个哈希函数会带来额外的计算量。 @@ -103,6 +103,6 @@ 各种编程语言采取了不同的哈希表实现策略,下面举几个例子。 -- Python 采用开放寻址。字典 dict 使用伪随机数进行探测。 -- Java 采用链式地址。自 JDK 1.8 以来,当 HashMap 内数组长度达到 64 且链表长度达到 8 时,链表会转换为红黑树以提升查找性能。 -- Go 采用链式地址。Go 规定每个桶最多存储 8 个键值对,超出容量则连接一个溢出桶。当溢出桶过多时,会执行一次特殊的等量扩容操作,以确保性能。 +- Python 采用开放寻址。字典 `dict` 使用伪随机数进行探测。 +- Java 采用链式地址。自 JDK 1.8 以来,当 `HashMap` 内数组长度达到 64 且链表长度达到 8 时,链表会转换为红黑树以提升查找性能。 +- Go 采用链式地址。Go 规定每个桶最多存储 8 个键值对,超出容量则连接一个溢出桶;当溢出桶过多时,会执行一次特殊的等量扩容操作,以确保性能。 diff --git a/docs/chapter_hashing/hash_map.md b/docs/chapter_hashing/hash_map.md index b45f3d548..8e452ba5d 100755 --- a/docs/chapter_hashing/hash_map.md +++ b/docs/chapter_hashing/hash_map.md @@ -1,6 +1,6 @@ # 哈希表 -「哈希表 hash table」,又称「散列表」,其通过建立键 `key` 与值 `value` 之间的映射,实现高效的元素查询。具体而言,我们向哈希表输入一个键 `key` ,则可以在 $O(1)$ 时间内获取对应的值 `value` 。 +「哈希表 hash table」,又称「散列表」,它通过建立键 `key` 与值 `value` 之间的映射,实现高效的元素查询。具体而言,我们向哈希表中输入一个键 `key` ,则可以在 $O(1)$ 时间内获取对应的值 `value` 。 如下图所示,给定 $n$ 个学生,每个学生都有“姓名”和“学号”两项数据。假如我们希望实现“输入一个学号,返回对应的姓名”的查询功能,则可以采用下图所示的哈希表来实现。 @@ -41,7 +41,7 @@ hmap[10583] = "小鸭" # 查询操作 - # 向哈希表输入键 key ,得到值 value + # 向哈希表中输入键 key ,得到值 value name: str = hmap[15937] # 删除操作 @@ -64,7 +64,7 @@ map[10583] = "小鸭"; /* 查询操作 */ - // 向哈希表输入键 key ,得到值 value + // 向哈希表中输入键 key ,得到值 value string name = map[15937]; /* 删除操作 */ @@ -87,7 +87,7 @@ map.put(10583, "小鸭"); /* 查询操作 */ - // 向哈希表输入键 key ,得到值 value + // 向哈希表中输入键 key ,得到值 value String name = map.get(15937); /* 删除操作 */ @@ -110,7 +110,7 @@ }; /* 查询操作 */ - // 向哈希表输入键 key ,得到值 value + // 向哈希表中输入键 key ,得到值 value string name = map[15937]; /* 删除操作 */ @@ -133,7 +133,7 @@ hmap[10583] = "小鸭" /* 查询操作 */ - // 向哈希表输入键 key ,得到值 value + // 向哈希表中输入键 key ,得到值 value name := hmap[15937] /* 删除操作 */ @@ -156,7 +156,7 @@ map[10583] = "小鸭" /* 查询操作 */ - // 向哈希表输入键 key ,得到值 value + // 向哈希表中输入键 key ,得到值 value let name = map[15937]! /* 删除操作 */ @@ -178,7 +178,7 @@ map.set(10583, '小鸭'); /* 查询操作 */ - // 向哈希表输入键 key ,得到值 value + // 向哈希表中输入键 key ,得到值 value let name = map.get(15937); /* 删除操作 */ @@ -202,7 +202,7 @@ console.info(map); /* 查询操作 */ - // 向哈希表输入键 key ,得到值 value + // 向哈希表中输入键 key ,得到值 value let name = map.get(15937); console.info('\n输入学号 15937 ,查询到姓名 ' + name); @@ -228,7 +228,7 @@ map[10583] = "小鸭"; /* 查询操作 */ - // 向哈希表输入键 key ,得到值 value + // 向哈希表中输入键 key ,得到值 value String name = map[15937]; /* 删除操作 */ @@ -512,6 +512,6 @@ index = hash(key) % capacity ![哈希表扩容](hash_map.assets/hash_table_reshash.png) -类似于数组扩容,哈希表扩容需将所有键值对从原哈希表迁移至新哈希表,非常耗时;并且由于哈希表容量 `capacity` 改变,我们需要通过哈希函数来重新计算所有键值对的存储位置,这进一步提高了扩容过程的计算开销。为此,编程语言通常会预留足够大的哈希表容量,防止频繁扩容。 +类似于数组扩容,哈希表扩容需将所有键值对从原哈希表迁移至新哈希表,非常耗时;并且由于哈希表容量 `capacity` 改变,我们需要通过哈希函数来重新计算所有键值对的存储位置,这进一步增加了扩容过程的计算开销。为此,编程语言通常会预留足够大的哈希表容量,防止频繁扩容。 「负载因子 load factor」是哈希表的一个重要概念,其定义为哈希表的元素数量除以桶数量,用于衡量哈希冲突的严重程度,**也常作为哈希表扩容的触发条件**。例如在 Java 中,当负载因子超过 $0.75$ 时,系统会将哈希表扩容至原先的 $2$ 倍。 diff --git a/docs/chapter_hashing/summary.md b/docs/chapter_hashing/summary.md index 0a7fb60fe..9c5a9cceb 100644 --- a/docs/chapter_hashing/summary.md +++ b/docs/chapter_hashing/summary.md @@ -44,4 +44,4 @@ !!! question "为什么哈希表扩容能够缓解哈希冲突?" - 哈希函数的最后一步往往是对数组长度 $n$ 取余,让输出值落在数组索引范围内;在扩容后,数组长度 $n$ 发生变化,而 `key` 对应的索引也可能发生变化。原先落在同一个桶的多个 `key` ,在扩容后可能会被分配到多个桶中,从而实现哈希冲突的缓解。 + 哈希函数的最后一步往往是对数组长度 $n$ 取模(取余),让输出值落在数组索引范围内;在扩容后,数组长度 $n$ 发生变化,而 `key` 对应的索引也可能发生变化。原先落在同一个桶的多个 `key` ,在扩容后可能会被分配到多个桶中,从而实现哈希冲突的缓解。 diff --git a/docs/chapter_heap/build_heap.md b/docs/chapter_heap/build_heap.md index 26a006722..fa3d6845a 100644 --- a/docs/chapter_heap/build_heap.md +++ b/docs/chapter_heap/build_heap.md @@ -46,7 +46,7 @@ $$ T(h) = 2^0h + 2^1(h-1) + 2^2(h-2) + \dots + 2^{(h-1)}\times1 $$ -化简上式需要借助中学的数列知识,先对 $T(h)$ 乘以 $2$ ,得到: +化简上式需要借助中学的数列知识,将对 $T(h)$ 乘以 $2$ ,得到: $$ \begin{aligned} @@ -71,4 +71,4 @@ T(h) & = 2 \frac{1 - 2^h}{1 - 2} - h \newline \end{aligned} $$ -进一步地,高度为 $h$ 的完美二叉树的节点数量为 $n = 2^{h+1} - 1$ ,易得复杂度为 $O(2^h) = O(n)$ 。以上推算表明,**输入列表并建堆的时间复杂度为 $O(n)$ ,非常高效**。 +进一步,高度为 $h$ 的完美二叉树的节点数量为 $n = 2^{h+1} - 1$ ,易得复杂度为 $O(2^h) = O(n)$ 。以上推算表明,**输入列表并建堆的时间复杂度为 $O(n)$ ,非常高效**。 diff --git a/docs/chapter_heap/heap.md b/docs/chapter_heap/heap.md index daa8790a0..4401a1e89 100644 --- a/docs/chapter_heap/heap.md +++ b/docs/chapter_heap/heap.md @@ -2,8 +2,8 @@ 「堆 heap」是一种满足特定条件的完全二叉树,主要可分为两种类型,如下图所示。 -- 「大顶堆 max heap」:任意节点的值 $\geq$ 其子节点的值。 - 「小顶堆 min heap」:任意节点的值 $\leq$ 其子节点的值。 +- 「大顶堆 max heap」:任意节点的值 $\geq$ 其子节点的值。 ![小顶堆与大顶堆](heap.assets/min_heap_and_max_heap.png) @@ -11,11 +11,11 @@ - 最底层节点靠左填充,其他层的节点都被填满。 - 我们将二叉树的根节点称为“堆顶”,将底层最靠右的节点称为“堆底”。 -- 对于大顶堆(小顶堆),堆顶元素(根节点)的值分别是最大(最小)的。 +- 对于大顶堆(小顶堆),堆顶元素(根节点)的值是最大(最小)的。 -## 堆常用操作 +## 堆的常用操作 -需要指出的是,许多编程语言提供的是「优先队列 priority queue」,这是一种抽象数据结构,定义为具有优先级排序的队列。 +需要指出的是,许多编程语言提供的是「优先队列 priority queue」,这是一种抽象的数据结构,定义为具有优先级排序的队列。 实际上,**堆通常用于实现优先队列,大顶堆相当于元素按从大到小的顺序出队的优先队列**。从使用角度来看,我们可以将“优先队列”和“堆”看作等价的数据结构。因此,本书对两者不做特别区分,统一称作“堆”。 @@ -23,13 +23,13 @@

  堆的操作效率

-| 方法名 | 描述 | 时间复杂度 | -| --------- | -------------------------------------------- | ----------- | -| push() | 元素入堆 | $O(\log n)$ | -| pop() | 堆顶元素出堆 | $O(\log n)$ | -| peek() | 访问堆顶元素(大 / 小顶堆分别为最大 / 小值) | $O(1)$ | -| size() | 获取堆的元素数量 | $O(1)$ | -| isEmpty() | 判断堆是否为空 | $O(1)$ | +| 方法名 | 描述 | 时间复杂度 | +| ----------- | ------------------------------------------------ | ----------- | +| `push()` | 元素入堆 | $O(\log n)$ | +| `pop()` | 堆顶元素出堆 | $O(\log n)$ | +| `peek()` | 访问堆顶元素(对于大 / 小顶堆分别为最大 / 小值) | $O(1)$ | +| `size()` | 获取堆的元素数量 | $O(1)$ | +| `isEmpty()` | 判断堆是否为空 | $O(1)$ | 在实际应用中,我们可以直接使用编程语言提供的堆类(或优先队列类)。 @@ -353,7 +353,7 @@ 当使用数组表示二叉树时,元素代表节点值,索引代表节点在二叉树中的位置。**节点指针通过索引映射公式来实现**。 -如下图所示,给定索引 $i$ ,其左子节点索引为 $2i + 1$ ,右子节点索引为 $2i + 2$ ,父节点索引为 $(i - 1) / 2$(向下整除)。当索引越界时,表示空节点或节点不存在。 +如下图所示,给定索引 $i$ ,其左子节点的索引为 $2i + 1$ ,右子节点的索引为 $2i + 2$ ,父节点的索引为 $(i - 1) / 2$(向下整除)。当索引越界时,表示空节点或节点不存在。 ![堆的表示与存储](heap.assets/representation_of_heap.png) @@ -373,7 +373,7 @@ ### 元素入堆 -给定元素 `val` ,我们首先将其添加到堆底。添加之后,由于 val 可能大于堆中其他元素,堆的成立条件可能已被破坏,**因此需要修复从插入节点到根节点的路径上的各个节点**,这个操作被称为「堆化 heapify」。 +给定元素 `val` ,我们首先将其添加到堆底。添加之后,由于 `val` 可能大于堆中其他元素,堆的成立条件可能已被破坏,**因此需要修复从插入节点到根节点的路径上的各个节点**,这个操作被称为「堆化 heapify」。 考虑从入堆节点开始,**从底至顶执行堆化**。如下图所示,我们比较插入节点与其父节点的值,如果插入节点更大,则将它们交换。然后继续执行此操作,从底至顶修复堆中的各个节点,直至越过根节点或遇到无须交换的节点时结束。 @@ -456,7 +456,7 @@ [file]{my_heap}-[class]{max_heap}-[func]{sift_down} ``` -## 堆常见应用 +## 堆的常见应用 - **优先队列**:堆通常作为实现优先队列的首选数据结构,其入队和出队操作的时间复杂度均为 $O(\log n)$ ,而建队操作为 $O(n)$ ,这些操作都非常高效。 - **堆排序**:给定一组数据,我们可以用它们建立一个堆,然后不断地执行元素出堆操作,从而得到有序数据。然而,我们通常会使用一种更优雅的方式实现堆排序,详见“堆排序”章节。 diff --git a/docs/chapter_heap/summary.md b/docs/chapter_heap/summary.md index bb2d9e348..270a3e33f 100644 --- a/docs/chapter_heap/summary.md +++ b/docs/chapter_heap/summary.md @@ -8,10 +8,10 @@ - 完全二叉树非常适合用数组表示,因此我们通常使用数组来存储堆。 - 堆化操作用于维护堆的性质,在入堆和出堆操作中都会用到。 - 输入 $n$ 个元素并建堆的时间复杂度可以优化至 $O(n)$ ,非常高效。 -- Top-K 是一个经典算法问题,可以使用堆数据结构高效解决,时间复杂度为 $O(n \log k)$ 。 +- Top-k 是一个经典算法问题,可以使用堆数据结构高效解决,时间复杂度为 $O(n \log k)$ 。 ### Q & A !!! question "数据结构的“堆”与内存管理的“堆”是同一个概念吗?" - 两者不是同一个概念,只是碰巧都叫堆。计算机系统内存中的堆是动态内存分配的一部分,程序在运行时可以使用它来存储数据。程序可以请求一定量的堆内存,用于存储如对象和数组等复杂结构。当这些数据不再需要时,程序需要释放这些内存,以防止内存泄漏。相较于栈内存,堆内存的管理和使用需要更谨慎,使用不当可能会导致内存泄漏和野指针等问题。 + 两者不是同一个概念,只是碰巧都叫“堆”。计算机系统内存中的堆是动态内存分配的一部分,程序在运行时可以使用它来存储数据。程序可以请求一定量的堆内存,用于存储如对象和数组等复杂结构。当这些数据不再需要时,程序需要释放这些内存,以防止内存泄漏。相较于栈内存,堆内存的管理和使用需要更谨慎,使用不当可能会导致内存泄漏和野指针等问题。 diff --git a/docs/chapter_heap/top_k.md b/docs/chapter_heap/top_k.md index 8e263a142..710ef423c 100644 --- a/docs/chapter_heap/top_k.md +++ b/docs/chapter_heap/top_k.md @@ -1,8 +1,8 @@ -# Top-K 问题 +# Top-k 问题 !!! question - 给定一个长度为 $n$ 的无序数组 `nums` ,请返回数组中前 $k$ 大的元素。 + 给定一个长度为 $n$ 的无序数组 `nums` ,请返回数组中最大的 $k$ 个元素。 对于该问题,我们先介绍两种思路比较直接的解法,再介绍效率更高的堆解法。 @@ -28,7 +28,7 @@ ## 方法三:堆 -我们可以基于堆更加高效地解决 Top-K 问题,流程如下图所示。 +我们可以基于堆更加高效地解决 Top-k 问题,流程如下图所示。 1. 初始化一个小顶堆,其堆顶元素最小。 2. 先将数组的前 $k$ 个元素依次入堆。 @@ -70,4 +70,4 @@ 总共执行了 $n$ 轮入堆和出堆,堆的最大长度为 $k$ ,因此时间复杂度为 $O(n \log k)$ 。该方法的效率很高,当 $k$ 较小时,时间复杂度趋向 $O(n)$ ;当 $k$ 较大时,时间复杂度不会超过 $O(n \log n)$ 。 -另外,该方法适用于动态数据流的使用场景。在不断加入数据时,我们可以持续维护堆内的元素,从而实现最大 $k$ 个元素的动态更新。 +另外,该方法适用于动态数据流的使用场景。在不断加入数据时,我们可以持续维护堆内的元素,从而实现最大的 $k$ 个元素的动态更新。 diff --git a/docs/chapter_introduction/algorithms_are_everywhere.md b/docs/chapter_introduction/algorithms_are_everywhere.md index 4e8a27e1a..a1903c61a 100644 --- a/docs/chapter_introduction/algorithms_are_everywhere.md +++ b/docs/chapter_introduction/algorithms_are_everywhere.md @@ -4,7 +4,7 @@ 在正式探讨算法之前,有一个有趣的事实值得分享:**你已经在不知不觉中学会了许多算法,并习惯将它们应用到日常生活中了**。下面我将举几个具体的例子来证实这一点。 -**例一:查阅字典**。在字典里,每个汉字都对应一个拼音,而字典是按照拼音字母顺序排列的。假设我们需要查找一个拼音首字母为 $r$ 的字,通常会按照下图所示的方式实现。 +**例一:查字典**。在字典里,每个汉字都对应一个拼音,而字典是按照拼音字母顺序排列的。假设我们需要查找一个拼音首字母为 $r$ 的字,通常会按照下图所示的方式实现。 1. 翻开字典约一半的页数,查看该页的首字母是什么,假设首字母为 $m$ 。 2. 由于在拼音字母表中 $r$ 位于 $m$ 之后,所以排除字典前半部分,查找范围缩小到后半部分。 diff --git a/docs/chapter_introduction/summary.md b/docs/chapter_introduction/summary.md index 74ed938f1..ad7c42e34 100644 --- a/docs/chapter_introduction/summary.md +++ b/docs/chapter_introduction/summary.md @@ -1,7 +1,7 @@ # 小结 - 算法在日常生活中无处不在,并不是遥不可及的高深知识。实际上,我们已经在不知不觉中学会了许多算法,用以解决生活中的大小问题。 -- 查阅字典的原理与二分查找算法相一致。二分查找算法体现了分而治之的重要算法思想。 +- 查字典的原理与二分查找算法相一致。二分查找算法体现了分而治之的重要算法思想。 - 整理扑克的过程与插入排序算法非常类似。插入排序算法适合排序小型数据集。 - 货币找零的步骤本质上是贪心算法,每一步都采取当前看来最好的选择。 - 算法是在有限时间内解决特定问题的一组指令或操作步骤,而数据结构是计算机中组织和存储数据的方式。 diff --git a/docs/chapter_preface/about_the_book.md b/docs/chapter_preface/about_the_book.md index f79e13b45..2783ec42c 100644 --- a/docs/chapter_preface/about_the_book.md +++ b/docs/chapter_preface/about_the_book.md @@ -1,18 +1,18 @@ # 关于本书 -本项目旨在创建一本开源、免费、新手友好的数据结构与算法入门教程。 +本项目旨在创建一本开源、免费、对新手友好的数据结构与算法入门教程。 -- 全书采用动画图解,结构化地讲解数据结构与算法知识,内容清晰易懂、学习曲线平滑。 -- 算法源代码皆可一键运行,支持 Python、C++、Java、C#、Go、Swift、JavaScript、TypeScript、Dart、Rust、C、Zig 等语言。 -- 鼓励读者在章节讨论区互帮互助、共同进步,提问与评论通常可在两日内得到回复。 +- 全书采用动画图解,结构化地讲解数据结构与算法知识,内容清晰易懂,学习曲线平滑。 +- 算法源代码皆可一键运行,支持 Python、C++、Java、C#、Go、Swift、JavaScript、TypeScript、Dart、Rust、C 和 Zig 等语言。 +- 鼓励读者在线上章节评论区互帮互助、共同进步,提问与评论通常可在两日内得到回复。 ## 读者对象 若你是算法初学者,从未接触过算法,或者已经有一些刷题经验,对数据结构与算法有模糊的认识,在会与不会之间反复横跳,那么本书正是为你量身定制的! -如果你已经积累一定刷题量,熟悉大部分题型,那么本书可助你回顾与梳理算法知识体系,仓库源代码可以当作“刷题工具库”或“算法字典”来使用。 +如果你已经积累一定的刷题量,熟悉大部分题型,那么本书可助你回顾与梳理算法知识体系,仓库源代码可以当作“刷题工具库”或“算法字典”来使用。 -若你是算法大神,我们期待收到你的宝贵建议,或者[一起参与创作](https://www.hello-algo.com/chapter_appendix/contribution/)。 +若你是算法“大神”,我们期待收到你的宝贵建议,或者[一起参与创作](https://www.hello-algo.com/chapter_appendix/contribution/)。 !!! success "前置条件" @@ -22,25 +22,25 @@ 本书的主要内容如下图所示。 -- **复杂度分析**:数据结构和算法的评价维度与方法。时间复杂度、空间复杂度的推算方法、常见类型、示例等。 -- **数据结构**:基本数据类型,数据结构的分类方法。数组、链表、栈、队列、哈希表、树、堆、图等数据结构的定义、优缺点、常用操作、常见类型、典型应用、实现方法等。 +- **复杂度分析**:数据结构和算法的评价维度与方法。时间复杂度和空间复杂度的推算方法、常见类型、示例等。 +- **数据结构**:基本数据类型和数据结构的分类方法。数组、链表、栈、队列、哈希表、树、堆、图等数据结构的定义、优缺点、常用操作、常见类型、典型应用、实现方法等。 - **算法**:搜索、排序、分治、回溯、动态规划、贪心等算法的定义、优缺点、效率、应用场景、解题步骤和示例问题等。 ![本书主要内容](about_the_book.assets/hello_algo_mindmap.jpg) ## 致谢 -在本书的创作过程中,我得到了许多人的帮助,包括但不限于: +在本书的创作过程中,我得到了许多人的帮助。 - 感谢我在公司的导师李汐博士,在一次畅谈中你鼓励我“快行动起来”,坚定了我写这本书的决心; - 感谢我的女朋友泡泡作为本书的首位读者,从算法小白的角度提出许多宝贵建议,使得本书更适合新手阅读; - 感谢腾宝、琦宝、飞宝为本书起了一个富有创意的名字,唤起大家写下第一行代码 "Hello World!" 的美好回忆; -- 感谢校铨在知识产权方面提供的专业帮助,这对本开源书的发展起到了重要的作用; -- 感谢苏潼为本书设计了精美的封面和 logo ,并在我的强迫症下多次耐心修改; -- 感谢 @squidfunk 提供的写作排版建议,以及他开发的开源文档主题 [Material-for-MkDocs](https://github.com/squidfunk/mkdocs-material/tree/master) 。 +- 感谢校铨在知识产权方面提供的专业帮助,这对本开源书的完善起到了重要作用; +- 感谢苏潼为本书设计了精美的封面和 logo ,并在我的强迫症的驱使下多次耐心修改; +- 感谢 @squidfunk 提供的排版建议,以及他开发的开源文档主题 [Material-for-MkDocs](https://github.com/squidfunk/mkdocs-material/tree/master) 。 在写作过程中,我阅读了许多关于数据结构与算法的教材和文章。这些作品为本书提供了优秀的范本,确保了本书内容的准确性与品质。在此感谢所有老师和前辈们的杰出贡献! -本书倡导手脑并用的学习方式,在这一点上深受[《动手学深度学习》](https://github.com/d2l-ai/d2l-zh)的启发。在此向各位读者强烈推荐这本优秀的著作。 +本书倡导手脑并用的学习方式,在这一点上我深受[《动手学深度学习》](https://github.com/d2l-ai/d2l-zh)的启发。在此向各位读者强烈推荐这本优秀的著作。 **衷心感谢我的父母,正是你们一直以来的支持与鼓励,让我有机会做这件富有趣味的事**。 diff --git a/docs/chapter_preface/suggestions.md b/docs/chapter_preface/suggestions.md index 0b23312d2..6d38a44c4 100644 --- a/docs/chapter_preface/suggestions.md +++ b/docs/chapter_preface/suggestions.md @@ -9,8 +9,8 @@ - 标题后标注 `*` 的是选读章节,内容相对困难。如果你的时间有限,可以先跳过。 - 重要专有名词及其英文翻译会用 `「 」` 括号标注,例如 `「数组 array」` 。建议记住它们,以便阅读文献。 - 专有名词和有特指含义的词句会使用 `“引号”` 标注,以避免歧义。 -- 重要名词、重点内容和总结性语句会被 **加粗** ,这类文字值得特别关注。 -- 当涉及编程语言之间不一致的名词时,本书均以 Python 为准,例如使用 $\text{None}$ 来表示“空”。 +- 重要名词、重点内容和总结性语句会 **加粗** ,这类文字值得特别关注。 +- 当涉及编程语言之间不一致的名词时,本书均以 Python 为准,例如使用 `None` 来表示“空”。 - 本书部分放弃了编程语言的注释规范,以换取更加紧凑的内容排版。注释主要分为三种类型:标题注释、内容注释、多行注释。 === "Python" @@ -169,7 +169,7 @@ ## 在动画图解中高效学习 -相较于文字,视频和图片具有更高的信息密度和结构化程度,更易于理解。在本书中,**重点和难点知识将主要通过动画和图解形式展示**,而文字则作为动画和图片的解释与补充。 +相较于文字,视频和图片具有更高的信息密度和结构化程度,更易于理解。在本书中,**重点和难点知识将主要通过动画以图解形式展示**,而文字则作为解释与补充。 如果你在阅读本书时,发现某段内容提供了如下图所示的动画或图解,**请以图为主、以文字为辅**,综合两者来理解内容。 @@ -209,7 +209,7 @@ git clone https://github.com/krahets/hello-algo.git 在阅读本书时,请不要轻易跳过那些没学明白的知识点。**欢迎在评论区提出你的问题**,我和小伙伴们将竭诚为你解答,一般情况下可在两天内回复。 -如下图所示,每个章节的底部都配有评论区。希望你能多关注评论区的内容。一方面,你可以了解大家遇到的问题,从而查漏补缺,激发更深入的思考。另一方面,期待你能慷慨地回答其他小伙伴的问题,分享你的见解,帮助他人进步。 +如下图所示,网页版每个章节的底部都配有评论区。希望你能多关注评论区的内容。一方面,你可以了解大家遇到的问题,从而查漏补缺,激发更深入的思考。另一方面,期待你能慷慨地回答其他小伙伴的问题,分享你的见解,帮助他人进步。 ![评论区示例](../index.assets/comment.gif) diff --git a/docs/chapter_preface/summary.md b/docs/chapter_preface/summary.md index 22cb13633..0ee5a588d 100644 --- a/docs/chapter_preface/summary.md +++ b/docs/chapter_preface/summary.md @@ -1,8 +1,8 @@ # 小结 -- 本书的主要受众是算法初学者。如果已有一定基础,本书能帮助你系统回顾算法知识,书中源代码也可作为“刷题工具库”使用。 -- 书中内容主要包括复杂度分析、数据结构、算法三部分,涵盖了该领域的大部分主题。 +- 本书的主要受众是算法初学者。如果你已有一定基础,本书能帮助你系统回顾算法知识,书中源代码也可作为“刷题工具库”使用。 +- 书中内容主要包括复杂度分析、数据结构和算法三部分,涵盖了该领域的大部分主题。 - 对于算法新手,在初学阶段阅读一本入门书至关重要,可以少走许多弯路。 -- 书中的动画和图解通常用于介绍重点和难点知识。阅读本书时,应给予这些内容更多关注。 +- 书中的动画图解通常用于介绍重点和难点知识。阅读本书时,应给予这些内容更多关注。 - 实践乃学习编程之最佳途径。强烈建议运行源代码并亲自敲代码。 -- 本书网页版的每个章节都设有讨论区,欢迎随时分享你的疑惑与见解。 +- 本书网页版的每个章节都设有评论区,欢迎随时分享你的疑惑与见解。 diff --git a/docs/chapter_searching/binary_search.md b/docs/chapter_searching/binary_search.md index becc4db79..0c1d510ca 100755 --- a/docs/chapter_searching/binary_search.md +++ b/docs/chapter_searching/binary_search.md @@ -49,9 +49,9 @@ [file]{binary_search}-[class]{}-[func]{binary_search} ``` -**时间复杂度 $O(\log n)$** :在二分循环中,区间每轮缩小一半,循环次数为 $\log_2 n$ 。 +**时间复杂度为 $O(\log n)$** :在二分循环中,区间每轮缩小一半,循环次数为 $\log_2 n$ 。 -**空间复杂度 $O(1)$** :指针 $i$ 和 $j$ 使用常数大小空间。 +**空间复杂度为 $O(1)$** :指针 $i$ 和 $j$ 使用常数大小空间。 ## 区间表示方法 diff --git a/docs/chapter_searching/binary_search_edge.md b/docs/chapter_searching/binary_search_edge.md index 906149ecf..bccf0f50e 100644 --- a/docs/chapter_searching/binary_search_edge.md +++ b/docs/chapter_searching/binary_search_edge.md @@ -53,4 +53,4 @@ 代码在此省略,以下两点值得注意。 - 给定数组不包含小数,这意味着我们无须关心如何处理相等的情况。 -- 因为该方法引入了小数,所以需要将函数中的变量 `target` 改为浮点数类型。 +- 因为该方法引入了小数,所以需要将函数中的变量 `target` 改为浮点数类型(Python 无须改动)。 diff --git a/docs/chapter_searching/binary_search_insertion.md b/docs/chapter_searching/binary_search_insertion.md index e371d6c66..1f2040e67 100644 --- a/docs/chapter_searching/binary_search_insertion.md +++ b/docs/chapter_searching/binary_search_insertion.md @@ -6,7 +6,7 @@ !!! question - 给定一个长度为 $n$ 的有序数组 `nums` 和一个元素 `target` ,数组不存在重复元素。现将 `target` 插入数组 `nums` 中,并保持其有序性。若数组中已存在元素 `target` ,则插入到其左方。请返回插入后 `target` 在数组中的索引。 + 给定一个长度为 $n$ 的有序数组 `nums` 和一个元素 `target` ,数组不存在重复元素。现将 `target` 插入数组 `nums` 中,并保持其有序性。若数组中已存在元素 `target` ,则插入到其左方。请返回插入后 `target` 在数组中的索引。示例如下图所示。 ![二分查找插入点示例数据](binary_search_insertion.assets/binary_search_insertion_example.png) diff --git a/docs/chapter_searching/searching_algorithm_revisited.md b/docs/chapter_searching/searching_algorithm_revisited.md index d4489b1cb..5a4d350b7 100644 --- a/docs/chapter_searching/searching_algorithm_revisited.md +++ b/docs/chapter_searching/searching_algorithm_revisited.md @@ -81,4 +81,4 @@ - 适用于海量数据,因为树节点在内存中是分散存储的。 - 适合需要维护有序数据或范围查找的场景。 - 在持续增删节点的过程中,二叉搜索树可能产生倾斜,时间复杂度劣化至 $O(n)$ 。 -- 若使用 AVL 树或红黑树,则各项操作可在 $O(\log n)$ 效率下稳定运行,但维护树平衡的操作会增加额外开销。 +- 若使用 AVL 树或红黑树,则各项操作可在 $O(\log n)$ 效率下稳定运行,但维护树平衡的操作会增加额外的开销。 diff --git a/docs/chapter_sorting/bubble_sort.md b/docs/chapter_sorting/bubble_sort.md index d96523999..e6d15af94 100755 --- a/docs/chapter_sorting/bubble_sort.md +++ b/docs/chapter_sorting/bubble_sort.md @@ -29,7 +29,7 @@ 设数组的长度为 $n$ ,冒泡排序的步骤如下图所示。 -1. 首先,对 $n$ 个元素执行“冒泡”,**将数组的最大元素交换至正确位置**, +1. 首先,对 $n$ 个元素执行“冒泡”,**将数组的最大元素交换至正确位置**。 2. 接下来,对剩余 $n - 1$ 个元素执行“冒泡”,**将第二大元素交换至正确位置**。 3. 以此类推,经过 $n - 1$ 轮“冒泡”后,**前 $n - 1$ 大的元素都被交换至正确位置**。 4. 仅剩的一个元素必定是最小元素,无须排序,因此数组排序完成。 diff --git a/docs/chapter_sorting/bucket_sort.md b/docs/chapter_sorting/bucket_sort.md index 5833fb141..8a71217c0 100644 --- a/docs/chapter_sorting/bucket_sort.md +++ b/docs/chapter_sorting/bucket_sort.md @@ -24,9 +24,9 @@ 桶排序适用于处理体量很大的数据。例如,输入数据包含 100 万个元素,由于空间限制,系统内存无法一次性加载所有数据。此时,可以将数据分成 1000 个桶,然后分别对每个桶进行排序,最后将结果合并。 -- **时间复杂度 $O(n + k)$** :假设元素在各个桶内平均分布,那么每个桶内的元素数量为 $\frac{n}{k}$ 。假设排序单个桶使用 $O(\frac{n}{k} \log\frac{n}{k})$ 时间,则排序所有桶使用 $O(n \log\frac{n}{k})$ 时间。**当桶数量 $k$ 比较大时,时间复杂度则趋向于 $O(n)$** 。合并结果时需要遍历所有桶和元素,花费 $O(n + k)$ 时间。 +- **时间复杂度为 $O(n + k)$** :假设元素在各个桶内平均分布,那么每个桶内的元素数量为 $\frac{n}{k}$ 。假设排序单个桶使用 $O(\frac{n}{k} \log\frac{n}{k})$ 时间,则排序所有桶使用 $O(n \log\frac{n}{k})$ 时间。**当桶数量 $k$ 比较大时,时间复杂度则趋向于 $O(n)$** 。合并结果时需要遍历所有桶和元素,花费 $O(n + k)$ 时间。 - **自适应排序**:在最差情况下,所有数据被分配到一个桶中,且排序该桶使用 $O(n^2)$ 时间。 -- **空间复杂度 $O(n + k)$、非原地排序**:需要借助 $k$ 个桶和总共 $n$ 个元素的额外空间。 +- **空间复杂度为 $O(n + k)$、非原地排序**:需要借助 $k$ 个桶和总共 $n$ 个元素的额外空间。 - 桶排序是否稳定取决于排序桶内元素的算法是否稳定。 ## 如何实现平均分配 diff --git a/docs/chapter_sorting/counting_sort.md b/docs/chapter_sorting/counting_sort.md index d477ede1b..d75ad857f 100644 --- a/docs/chapter_sorting/counting_sort.md +++ b/docs/chapter_sorting/counting_sort.md @@ -71,8 +71,8 @@ $$ ## 算法特性 -- **时间复杂度 $O(n + m)$** :涉及遍历 `nums` 和遍历 `counter` ,都使用线性时间。一般情况下 $n \gg m$ ,时间复杂度趋于 $O(n)$ 。 -- **空间复杂度 $O(n + m)$、非原地排序**:借助了长度分别为 $n$ 和 $m$ 的数组 `res` 和 `counter` 。 +- **时间复杂度为 $O(n + m)$** :涉及遍历 `nums` 和遍历 `counter` ,都使用线性时间。一般情况下 $n \gg m$ ,时间复杂度趋于 $O(n)$ 。 +- **空间复杂度为 $O(n + m)$、非原地排序**:借助了长度分别为 $n$ 和 $m$ 的数组 `res` 和 `counter` 。 - **稳定排序**:由于向 `res` 中填充元素的顺序是“从右向左”的,因此倒序遍历 `nums` 可以避免改变相等元素之间的相对位置,从而实现稳定排序。实际上,正序遍历 `nums` 也可以得到正确的排序结果,但结果是非稳定的。 ## 局限性 diff --git a/docs/chapter_sorting/heap_sort.md b/docs/chapter_sorting/heap_sort.md index d90d5e5e7..337f5ef99 100644 --- a/docs/chapter_sorting/heap_sort.md +++ b/docs/chapter_sorting/heap_sort.md @@ -68,6 +68,6 @@ ## 算法特性 -- **时间复杂度 $O(n \log n)$、非自适应排序**:建堆操作使用 $O(n)$ 时间。从堆中提取最大元素的时间复杂度为 $O(\log n)$ ,共循环 $n - 1$ 轮。 -- **空间复杂度 $O(1)$、原地排序**:几个指针变量使用 $O(1)$ 空间。元素交换和堆化操作都是在原数组上进行的。 +- **时间复杂度为 $O(n \log n)$、非自适应排序**:建堆操作使用 $O(n)$ 时间。从堆中提取最大元素的时间复杂度为 $O(\log n)$ ,共循环 $n - 1$ 轮。 +- **空间复杂度为 $O(1)$、原地排序**:几个指针变量使用 $O(1)$ 空间。元素交换和堆化操作都是在原数组上进行的。 - **非稳定排序**:在交换堆顶元素和堆底元素时,相等元素的相对位置可能发生变化。 diff --git a/docs/chapter_sorting/insertion_sort.md b/docs/chapter_sorting/insertion_sort.md index 58cf7516e..eb420e2bf 100755 --- a/docs/chapter_sorting/insertion_sort.md +++ b/docs/chapter_sorting/insertion_sort.md @@ -27,11 +27,11 @@ ## 算法特性 -- **时间复杂度 $O(n^2)$、自适应排序**:在最差情况下,每次插入操作分别需要循环 $n - 1$、$n-2$、$\dots$、$2$、$1$ 次,求和得到 $(n - 1) n / 2$ ,因此时间复杂度为 $O(n^2)$ 。在遇到有序数据时,插入操作会提前终止。当输入数组完全有序时,插入排序达到最佳时间复杂度 $O(n)$ 。 -- **空间复杂度 $O(1)$、原地排序**:指针 $i$ 和 $j$ 使用常数大小的额外空间。 +- **时间复杂度为 $O(n^2)$、自适应排序**:在最差情况下,每次插入操作分别需要循环 $n - 1$、$n-2$、$\dots$、$2$、$1$ 次,求和得到 $(n - 1) n / 2$ ,因此时间复杂度为 $O(n^2)$ 。在遇到有序数据时,插入操作会提前终止。当输入数组完全有序时,插入排序达到最佳时间复杂度 $O(n)$ 。 +- **空间复杂度为 $O(1)$、原地排序**:指针 $i$ 和 $j$ 使用常数大小的额外空间。 - **稳定排序**:在插入操作过程中,我们会将元素插入到相等元素的右侧,不会改变它们的顺序。 -## 插入排序优势 +## 插入排序的优势 插入排序的时间复杂度为 $O(n^2)$ ,而我们即将学习的快速排序的时间复杂度为 $O(n \log n)$ 。尽管插入排序的时间复杂度更高,**但在数据量较小的情况下,插入排序通常更快**。 diff --git a/docs/chapter_sorting/merge_sort.md b/docs/chapter_sorting/merge_sort.md index 0be79106d..ef9970931 100755 --- a/docs/chapter_sorting/merge_sort.md +++ b/docs/chapter_sorting/merge_sort.md @@ -59,8 +59,8 @@ ## 算法特性 -- **时间复杂度 $O(n \log n)$、非自适应排序**:划分产生高度为 $\log n$ 的递归树,每层合并的总操作数量为 $n$ ,因此总体时间复杂度为 $O(n \log n)$ 。 -- **空间复杂度 $O(n)$、非原地排序**:递归深度为 $\log n$ ,使用 $O(\log n)$ 大小的栈帧空间。合并操作需要借助辅助数组实现,使用 $O(n)$ 大小的额外空间。 +- **时间复杂度为 $O(n \log n)$、非自适应排序**:划分产生高度为 $\log n$ 的递归树,每层合并的总操作数量为 $n$ ,因此总体时间复杂度为 $O(n \log n)$ 。 +- **空间复杂度为 $O(n)$、非原地排序**:递归深度为 $\log n$ ,使用 $O(\log n)$ 大小的栈帧空间。合并操作需要借助辅助数组实现,使用 $O(n)$ 大小的额外空间。 - **稳定排序**:在合并过程中,相等元素的次序保持不变。 ## 链表排序 diff --git a/docs/chapter_sorting/quick_sort.md b/docs/chapter_sorting/quick_sort.md index a32387bb1..878d34581 100755 --- a/docs/chapter_sorting/quick_sort.md +++ b/docs/chapter_sorting/quick_sort.md @@ -61,8 +61,8 @@ ## 算法特性 -- **时间复杂度 $O(n \log n)$、自适应排序**:在平均情况下,哨兵划分的递归层数为 $\log n$ ,每层中的总循环数为 $n$ ,总体使用 $O(n \log n)$ 时间。在最差情况下,每轮哨兵划分操作都将长度为 $n$ 的数组划分为长度为 $0$ 和 $n - 1$ 的两个子数组,此时递归层数达到 $n$ ,每层中的循环数为 $n$ ,总体使用 $O(n^2)$ 时间。 -- **空间复杂度 $O(n)$、原地排序**:在输入数组完全倒序的情况下,达到最差递归深度 $n$ ,使用 $O(n)$ 栈帧空间。排序操作是在原数组上进行的,未借助额外数组。 +- **时间复杂度为 $O(n \log n)$、自适应排序**:在平均情况下,哨兵划分的递归层数为 $\log n$ ,每层中的总循环数为 $n$ ,总体使用 $O(n \log n)$ 时间。在最差情况下,每轮哨兵划分操作都将长度为 $n$ 的数组划分为长度为 $0$ 和 $n - 1$ 的两个子数组,此时递归层数达到 $n$ ,每层中的循环数为 $n$ ,总体使用 $O(n^2)$ 时间。 +- **空间复杂度为 $O(n)$、原地排序**:在输入数组完全倒序的情况下,达到最差递归深度 $n$ ,使用 $O(n)$ 栈帧空间。排序操作是在原数组上进行的,未借助额外数组。 - **非稳定排序**:在哨兵划分的最后一步,基准数可能会被交换至相等元素的右侧。 ## 快速排序为什么快 diff --git a/docs/chapter_sorting/radix_sort.md b/docs/chapter_sorting/radix_sort.md index cfa630201..367b55c80 100644 --- a/docs/chapter_sorting/radix_sort.md +++ b/docs/chapter_sorting/radix_sort.md @@ -20,7 +20,7 @@ $$ x_k = \lfloor\frac{x}{d^{k-1}}\rfloor \bmod d $$ -其中 $\lfloor a \rfloor$ 表示对浮点数 $a$ 向下取整,而 $\bmod \: d$ 表示对 $d$ 取余。对于学号数据,$d = 10$ 且 $k \in [1, 8]$ 。 +其中 $\lfloor a \rfloor$ 表示对浮点数 $a$ 向下取整,而 $\bmod \: d$ 表示对 $d$ 取模(取余)。对于学号数据,$d = 10$ 且 $k \in [1, 8]$ 。 此外,我们需要小幅改动计数排序代码,使之可以根据数字的第 $k$ 位进行排序: @@ -36,6 +36,6 @@ $$ 相较于计数排序,基数排序适用于数值范围较大的情况,**但前提是数据必须可以表示为固定位数的格式,且位数不能过大**。例如,浮点数不适合使用基数排序,因为其位数 $k$ 过大,可能导致时间复杂度 $O(nk) \gg O(n^2)$ 。 -- **时间复杂度 $O(nk)$**:设数据量为 $n$、数据为 $d$ 进制、最大位数为 $k$ ,则对某一位执行计数排序使用 $O(n + d)$ 时间,排序所有 $k$ 位使用 $O((n + d)k)$ 时间。通常情况下,$d$ 和 $k$ 都相对较小,时间复杂度趋向 $O(n)$ 。 -- **空间复杂度 $O(n + d)$、非原地排序**:与计数排序相同,基数排序需要借助长度为 $n$ 和 $d$ 的数组 `res` 和 `counter` 。 +- **时间复杂度为 $O(nk)$**:设数据量为 $n$、数据为 $d$ 进制、最大位数为 $k$ ,则对某一位执行计数排序使用 $O(n + d)$ 时间,排序所有 $k$ 位使用 $O((n + d)k)$ 时间。通常情况下,$d$ 和 $k$ 都相对较小,时间复杂度趋向 $O(n)$ 。 +- **空间复杂度为 $O(n + d)$、非原地排序**:与计数排序相同,基数排序需要借助长度为 $n$ 和 $d$ 的数组 `res` 和 `counter` 。 - **稳定排序**:当计数排序稳定时,基数排序也稳定;当计数排序不稳定时,基数排序无法保证得到正确的排序结果。 diff --git a/docs/chapter_sorting/selection_sort.md b/docs/chapter_sorting/selection_sort.md index 08a276d58..552146a64 100644 --- a/docs/chapter_sorting/selection_sort.md +++ b/docs/chapter_sorting/selection_sort.md @@ -52,7 +52,7 @@ ## 算法特性 - **时间复杂度为 $O(n^2)$、非自适应排序**:外循环共 $n - 1$ 轮,第一轮的未排序区间长度为 $n$ ,最后一轮的未排序区间长度为 $2$ ,即各轮外循环分别包含 $n$、$n - 1$、$\dots$、$3$、$2$ 轮内循环,求和为 $\frac{(n - 1)(n + 2)}{2}$ 。 -- **空间复杂度 $O(1)$、原地排序**:指针 $i$ 和 $j$ 使用常数大小的额外空间。 +- **空间复杂度为 $O(1)$、原地排序**:指针 $i$ 和 $j$ 使用常数大小的额外空间。 - **非稳定排序**:如下图所示,元素 `nums[i]` 有可能被交换至与其相等的元素的右边,导致两者的相对顺序发生改变。 ![选择排序非稳定示例](selection_sort.assets/selection_sort_instability.png) diff --git a/docs/chapter_sorting/summary.md b/docs/chapter_sorting/summary.md index 8a5bbd137..bbb2b22c7 100644 --- a/docs/chapter_sorting/summary.md +++ b/docs/chapter_sorting/summary.md @@ -18,9 +18,7 @@ !!! question "排序算法稳定性在什么情况下是必需的?" - 在现实中,我们有可能是基于对象的某个属性进行排序。例如,学生有姓名和身高两个属性,我们希望实现一个多级排序: - - 先按照姓名进行排序,得到 `(A, 180) (B, 185) (C, 170) (D, 170)` ;再对身高进行排序。由于排序算法不稳定,因此可能得到 `(D, 170) (C, 170) (A, 180) (B, 185)` 。 + 在现实中,我们有可能基于对象的某个属性进行排序。例如,学生有姓名和身高两个属性,我们希望实现一个多级排序:先按照姓名进行排序,得到 `(A, 180) (B, 185) (C, 170) (D, 170)` ;再对身高进行排序。由于排序算法不稳定,因此可能得到 `(D, 170) (C, 170) (A, 180) (B, 185)` 。 可以发现,学生 D 和 C 的位置发生了交换,姓名的有序性被破坏了,而这是我们不希望看到的。 diff --git a/docs/chapter_stack_and_queue/deque.md b/docs/chapter_stack_and_queue/deque.md index 6f7e7f456..b35ed026a 100644 --- a/docs/chapter_stack_and_queue/deque.md +++ b/docs/chapter_stack_and_queue/deque.md @@ -10,14 +10,14 @@

  双向队列操作效率

-| 方法名 | 描述 | 时间复杂度 | -| ----------- | ---------------- | ---------- | -| pushFirst() | 将元素添加至队首 | $O(1)$ | -| pushLast() | 将元素添加至队尾 | $O(1)$ | -| popFirst() | 删除队首元素 | $O(1)$ | -| popLast() | 删除队尾元素 | $O(1)$ | -| peekFirst() | 访问队首元素 | $O(1)$ | -| peekLast() | 访问队尾元素 | $O(1)$ | +| 方法名 | 描述 | 时间复杂度 | +| ------------- | ---------------- | ---------- | +| `pushFirst()` | 将元素添加至队首 | $O(1)$ | +| `pushLast()` | 将元素添加至队尾 | $O(1)$ | +| `popFirst()` | 删除队首元素 | $O(1)$ | +| `popLast()` | 删除队尾元素 | $O(1)$ | +| `peekFirst()` | 访问队首元素 | $O(1)$ | +| `peekLast()` | 访问队尾元素 | $O(1)$ | 同样地,我们可以直接使用编程语言中已实现的双向队列类: diff --git a/docs/chapter_stack_and_queue/queue.md b/docs/chapter_stack_and_queue/queue.md index d48568f50..c0466b8ff 100755 --- a/docs/chapter_stack_and_queue/queue.md +++ b/docs/chapter_stack_and_queue/queue.md @@ -12,11 +12,11 @@

  队列操作效率

-| 方法名 | 描述 | 时间复杂度 | -| ------ | ---------------------------- | ---------- | -| push() | 元素入队,即将元素添加至队尾 | $O(1)$ | -| pop() | 队首元素出队 | $O(1)$ | -| peek() | 访问队首元素 | $O(1)$ | +| 方法名 | 描述 | 时间复杂度 | +| -------- | ---------------------------- | ---------- | +| `push()` | 元素入队,即将元素添加至队尾 | $O(1)$ | +| `pop()` | 队首元素出队 | $O(1)$ | +| `peek()` | 访问队首元素 | $O(1)$ | 我们可以直接使用编程语言中现成的队列类: @@ -310,7 +310,7 @@ ## 队列实现 -为了实现队列,我们需要一种数据结构,可以在一端添加元素,并在另一端删除元素。链表和数组都符合要求。 +为了实现队列,我们需要一种数据结构,可以在一端添加元素,并在另一端删除元素,链表和数组都符合要求。 ### 基于链表的实现 diff --git a/docs/chapter_stack_and_queue/stack.md b/docs/chapter_stack_and_queue/stack.md index 02fcb0dba..cb2ae5efd 100755 --- a/docs/chapter_stack_and_queue/stack.md +++ b/docs/chapter_stack_and_queue/stack.md @@ -1,6 +1,6 @@ # 栈 -「栈 stack」是一种遵循先入后出的逻辑的线性数据结构。 +「栈 stack」是一种遵循先入后出逻辑的线性数据结构。 我们可以将栈类比为桌面上的一摞盘子,如果想取出底部的盘子,则需要先将上面的盘子依次移走。我们将盘子替换为各种类型的元素(如整数、字符、对象等),就得到了栈这种数据结构。 @@ -8,17 +8,17 @@ ![栈的先入后出规则](stack.assets/stack_operations.png) -## 栈常用操作 +## 栈的常用操作 栈的常用操作如下表所示,具体的方法名需要根据所使用的编程语言来确定。在此,我们以常见的 `push()`、`pop()`、`peek()` 命名为例。

  栈的操作效率

-| 方法 | 描述 | 时间复杂度 | -| ------ | ---------------------- | ---------- | -| push() | 元素入栈(添加至栈顶) | $O(1)$ | -| pop() | 栈顶元素出栈 | $O(1)$ | -| peek() | 访问栈顶元素 | $O(1)$ | +| 方法 | 描述 | 时间复杂度 | +| -------- | ---------------------- | ---------- | +| `push()` | 元素入栈(添加至栈顶) | $O(1)$ | +| `pop()` | 栈顶元素出栈 | $O(1)$ | +| `peek()` | 访问栈顶元素 | $O(1)$ | 通常情况下,我们可以直接使用编程语言内置的栈类。然而,某些语言可能没有专门提供栈类,这时我们可以将该语言的“数组”或“链表”当作栈来使用,并在程序逻辑上忽略与栈无关的操作。 @@ -26,7 +26,7 @@ ```python title="stack.py" # 初始化栈 - # Python 没有内置的栈类,可以把 List 当作栈来使用 + # Python 没有内置的栈类,可以把 list 当作栈来使用 stack: list[int] = [] # 元素入栈 diff --git a/docs/chapter_tree/array_representation_of_tree.md b/docs/chapter_tree/array_representation_of_tree.md index aa7bec4c9..2f470b784 100644 --- a/docs/chapter_tree/array_representation_of_tree.md +++ b/docs/chapter_tree/array_representation_of_tree.md @@ -16,13 +16,13 @@ ## 表示任意二叉树 -完美二叉树是一个特例,在二叉树的中间层通常存在许多 $\text{None}$ 。由于层序遍历序列并不包含这些 $\text{None}$ ,因此我们无法仅凭该序列来推测 $\text{None}$ 的数量和分布位置。**这意味着存在多种二叉树结构都符合该层序遍历序列**。 +完美二叉树是一个特例,在二叉树的中间层通常存在许多 `None` 。由于层序遍历序列并不包含这些 `None` ,因此我们无法仅凭该序列来推测 `None` 的数量和分布位置。**这意味着存在多种二叉树结构都符合该层序遍历序列**。 如下图所示,给定一棵非完美二叉树,上述数组表示方法已经失效。 ![层序遍历序列对应多种二叉树可能性](array_representation_of_tree.assets/array_representation_without_empty.png) -为了解决此问题,**我们可以考虑在层序遍历序列中显式地写出所有 $\text{None}$** 。如下图所示,这样处理后,层序遍历序列就可以唯一表示二叉树了。示例代码如下: +为了解决此问题,**我们可以考虑在层序遍历序列中显式地写出所有 `None`** 。如下图所示,这样处理后,层序遍历序列就可以唯一表示二叉树了。示例代码如下: === "Python" @@ -120,9 +120,9 @@ ![任意类型二叉树的数组表示](array_representation_of_tree.assets/array_representation_with_empty.png) -值得说明的是,**完全二叉树非常适合使用数组来表示**。回顾完全二叉树的定义,$\text{None}$ 只出现在最底层且靠右的位置,**因此所有 $\text{None}$ 一定出现在层序遍历序列的末尾**。 +值得说明的是,**完全二叉树非常适合使用数组来表示**。回顾完全二叉树的定义,`None` 只出现在最底层且靠右的位置,**因此所有 `None` 一定出现在层序遍历序列的末尾**。 -这意味着使用数组表示完全二叉树时,可以省略存储所有 $\text{None}$ ,非常方便。下图给出了一个例子。 +这意味着使用数组表示完全二叉树时,可以省略存储所有 `None` ,非常方便。下图给出了一个例子。 ![完全二叉树的数组表示](array_representation_of_tree.assets/array_representation_complete_binary_tree.png) @@ -147,4 +147,4 @@ - 数组存储需要连续内存空间,因此不适合存储数据量过大的树。 - 增删节点需要通过数组插入与删除操作实现,效率较低。 -- 当二叉树中存在大量 $\text{None}$ 时,数组中包含的节点数据比重较低,空间利用率较低。 +- 当二叉树中存在大量 `None` 时,数组中包含的节点数据比重较低,空间利用率较低。 diff --git a/docs/chapter_tree/avl_tree.md b/docs/chapter_tree/avl_tree.md index ba5c1d8e3..de14137dc 100644 --- a/docs/chapter_tree/avl_tree.md +++ b/docs/chapter_tree/avl_tree.md @@ -1,16 +1,16 @@ # AVL 树 * -在“二叉搜索树”章节中,我们提到,在多次插入和删除操作后,二叉搜索树可能退化为链表。在这种情况下,所有操作的时间复杂度将从 $O(\log n)$ 恶化为 $O(n)$ 。 +在“二叉搜索树”章节中我们提到,在多次插入和删除操作后,二叉搜索树可能退化为链表。在这种情况下,所有操作的时间复杂度将从 $O(\log n)$ 劣化为 $O(n)$ 。 如下图所示,经过两次删除节点操作,这棵二叉搜索树便会退化为链表。 ![AVL 树在删除节点后发生退化](avl_tree.assets/avltree_degradation_from_removing_node.png) -再例如,在下图所示的完美二叉树中插入两个节点后,树将严重向左倾斜,查找操作的时间复杂度也随之恶化。 +再例如,在下图所示的完美二叉树中插入两个节点后,树将严重向左倾斜,查找操作的时间复杂度也随之劣化。 ![AVL 树在插入节点后发生退化](avl_tree.assets/avltree_degradation_from_inserting_node.png) -1962 年 G. M. Adelson-Velsky 和 E. M. Landis 在论文 "An algorithm for the organization of information" 中提出了「AVL 树」。论文中详细描述了一系列操作,确保在持续添加和删除节点后,AVL 树不会退化,从而使得各种操作的时间复杂度保持在 $O(\log n)$ 级别。换句话说,在需要频繁进行增删查改操作的场景中,AVL 树能始终保持高效的数据操作性能,具有很好的应用价值。 +1962 年 G. M. Adelson-Velsky 和 E. M. Landis 在论文“An algorithm for the organization of information”中提出了「AVL 树」。论文中详细描述了一系列操作,确保在持续添加和删除节点后,AVL 树不会退化,从而使得各种操作的时间复杂度保持在 $O(\log n)$ 级别。换句话说,在需要频繁进行增删查改操作的场景中,AVL 树能始终保持高效的数据操作性能,具有很好的应用价值。 ## AVL 树常见术语 @@ -206,7 +206,7 @@ AVL 树既是二叉搜索树也是平衡二叉树,同时满足这两类二叉 ``` -“节点高度”是指从该节点到其最远叶节点的距离,即所经过的“边”的数量。需要特别注意的是,叶节点的高度为 $0$ ,而空节点的高度为 $-1$ 。我们将创建两个工具函数,分别用于获取和更新节点的高度: +“节点高度”是指从该节点到它的最远叶节点的距离,即所经过的“边”的数量。需要特别注意的是,叶节点的高度为 $0$ ,而空节点的高度为 $-1$ 。我们将创建两个工具函数,分别用于获取和更新节点的高度: ```src [file]{avl_tree}-[class]{a_v_l_tree}-[func]{update_height} @@ -246,9 +246,9 @@ AVL 树的特点在于“旋转”操作,它能够在不影响二叉树的中 === "<4>" ![avltree_right_rotate_step4](avl_tree.assets/avltree_right_rotate_step4.png) -如下图所示,当节点 `child` 有右子节点(记为 `grandChild` )时,需要在右旋中添加一步:将 `grandChild` 作为 `node` 的左子节点。 +如下图所示,当节点 `child` 有右子节点(记为 `grand_child` )时,需要在右旋中添加一步:将 `grand_child` 作为 `node` 的左子节点。 -![有 grandChild 的右旋操作](avl_tree.assets/avltree_right_rotate_with_grandchild.png) +![有 grand_child 的右旋操作](avl_tree.assets/avltree_right_rotate_with_grandchild.png) “向右旋转”是一种形象化的说法,实际上需要通过修改节点指针来实现,代码如下所示: @@ -262,9 +262,9 @@ AVL 树的特点在于“旋转”操作,它能够在不影响二叉树的中 ![左旋操作](avl_tree.assets/avltree_left_rotate.png) -同理,如下图所示,当节点 `child` 有左子节点(记为 `grandChild` )时,需要在左旋中添加一步:将 `grandChild` 作为 `node` 的右子节点。 +同理,如下图所示,当节点 `child` 有左子节点(记为 `grand_child` )时,需要在左旋中添加一步:将 `grand_child` 作为 `node` 的右子节点。 -![有 grandChild 的左旋操作](avl_tree.assets/avltree_left_rotate_with_grandchild.png) +![有 grand_child 的左旋操作](avl_tree.assets/avltree_left_rotate_with_grandchild.png) 可以观察到,**右旋和左旋操作在逻辑上是镜像对称的,它们分别解决的两种失衡情况也是对称的**。基于对称性,我们只需将右旋的实现代码中的所有的 `left` 替换为 `right` ,将所有的 `right` 替换为 `left` ,即可得到左旋的实现代码: diff --git a/docs/chapter_tree/binary_search_tree.md b/docs/chapter_tree/binary_search_tree.md index dfde619da..32c46aa31 100755 --- a/docs/chapter_tree/binary_search_tree.md +++ b/docs/chapter_tree/binary_search_tree.md @@ -41,15 +41,15 @@ 给定一个待插入元素 `num` ,为了保持二叉搜索树“左子树 < 根节点 < 右子树”的性质,插入操作流程如下图所示。 -1. **查找插入位置**:与查找操作相似,从根节点出发,根据当前节点值和 `num` 的大小关系循环向下搜索,直到越过叶节点(遍历至 $\text{None}$ )时跳出循环。 -2. **在该位置插入节点**:初始化节点 `num` ,将该节点置于 $\text{None}$ 的位置。 +1. **查找插入位置**:与查找操作相似,从根节点出发,根据当前节点值和 `num` 的大小关系循环向下搜索,直到越过叶节点(遍历至 `None` )时跳出循环。 +2. **在该位置插入节点**:初始化节点 `num` ,将该节点置于 `None` 的位置。 ![在二叉搜索树中插入节点](binary_search_tree.assets/bst_insert.png) 在代码实现中,需要注意以下两点。 - 二叉搜索树不允许存在重复节点,否则将违反其定义。因此,若待插入节点在树中已存在,则不执行插入,直接返回。 -- 为了实现插入节点,我们需要借助节点 `pre` 保存上一轮循环的节点。这样在遍历至 $\text{None}$ 时,我们可以获取到其父节点,从而完成节点插入操作。 +- 为了实现插入节点,我们需要借助节点 `pre` 保存上一轮循环的节点。这样在遍历至 `None` 时,我们可以获取到其父节点,从而完成节点插入操作。 ```src [file]{binary_search_tree}-[class]{binary_search_tree}-[func]{insert} @@ -59,11 +59,7 @@ ### 删除节点 -先在二叉树中查找到目标节点,再将其删除。 - -与插入节点类似,我们需要保证在删除操作完成后,二叉搜索树的“左子树 < 根节点 < 右子树”的性质仍然满足。 - -因此,我们根据目标节点的子节点数量,分 0、1 和 2 三种情况,执行对应的删除节点操作。 +先在二叉树中查找到目标节点,再将其删除。与插入节点类似,我们需要保证在删除操作完成后,二叉搜索树的“左子树 < 根节点 < 右子树”的性质仍然满足。因此,我们根据目标节点的子节点数量,分 0、1 和 2 三种情况,执行对应的删除节点操作。 如下图所示,当待删除节点的度为 $0$ 时,表示该节点是叶节点,可以直接删除。 diff --git a/docs/chapter_tree/binary_tree.md b/docs/chapter_tree/binary_tree.md index 705c23b95..a36bf215f 100644 --- a/docs/chapter_tree/binary_tree.md +++ b/docs/chapter_tree/binary_tree.md @@ -193,7 +193,7 @@ 二叉树的常用术语如下图所示。 - 「根节点 root node」:位于二叉树顶层的节点,没有父节点。 -- 「叶节点 leaf node」:没有子节点的节点,其两个指针均指向 $\text{None}$ 。 +- 「叶节点 leaf node」:没有子节点的节点,其两个指针均指向 `None` 。 - 「边 edge」:连接两个节点的线段,即节点引用(指针)。 - 节点所在的「层 level」:从顶至底递增,根节点所在层为 1 。 - 节点的「度 degree」:节点的子节点的数量。在二叉树中,度的取值范围是 0、1、2 。 diff --git a/docs/chapter_tree/binary_tree_traversal.md b/docs/chapter_tree/binary_tree_traversal.md index a9f9ab504..b40b83ad7 100755 --- a/docs/chapter_tree/binary_tree_traversal.md +++ b/docs/chapter_tree/binary_tree_traversal.md @@ -8,7 +8,7 @@ 如下图所示,「层序遍历 level-order traversal」从顶部到底部逐层遍历二叉树,并在每一层按照从左到右的顺序访问节点。 -层序遍历本质上属于「广度优先遍历 breadth-first traversal, BFS」,它体现了一种“一圈一圈向外扩展”的逐层遍历方式。 +层序遍历本质上属于「广度优先遍历 breadth-first traversal」,也称「广度优先搜索 breadth-first search, BFS」,它体现了一种“一圈一圈向外扩展”的逐层遍历方式。 ![二叉树的层序遍历](binary_tree_traversal.assets/binary_tree_bfs.png) @@ -22,12 +22,12 @@ ### 复杂度分析 -- **时间复杂度 $O(n)$** :所有节点被访问一次,使用 $O(n)$ 时间,其中 $n$ 为节点数量。 -- **空间复杂度 $O(n)$** :在最差情况下,即满二叉树时,遍历到最底层之前,队列中最多同时存在 $(n + 1) / 2$ 个节点,占用 $O(n)$ 空间。 +- **时间复杂度为 $O(n)$** :所有节点被访问一次,使用 $O(n)$ 时间,其中 $n$ 为节点数量。 +- **空间复杂度为 $O(n)$** :在最差情况下,即满二叉树时,遍历到最底层之前,队列中最多同时存在 $(n + 1) / 2$ 个节点,占用 $O(n)$ 空间。 ## 前序、中序、后序遍历 -相应地,前序、中序和后序遍历都属于「深度优先遍历 depth-first traversal, DFS」,它体现了一种“先走到尽头,再回溯继续”的遍历方式。 +相应地,前序、中序和后序遍历都属于「深度优先遍历 depth-first traversal」,也称「深度优先搜索 depth-first search, DFS」,它体现了一种“先走到尽头,再回溯继续”的遍历方式。 下图展示了对二叉树进行深度优先遍历的工作原理。**深度优先遍历就像是绕着整棵二叉树的外围“走”一圈**,在每个节点都会遇到三个位置,分别对应前序遍历、中序遍历和后序遍历。 @@ -85,5 +85,5 @@ ### 复杂度分析 -- **时间复杂度 $O(n)$** :所有节点被访问一次,使用 $O(n)$ 时间。 -- **空间复杂度 $O(n)$** :在最差情况下,即树退化为链表时,递归深度达到 $n$ ,系统占用 $O(n)$ 栈帧空间。 +- **时间复杂度为 $O(n)$** :所有节点被访问一次,使用 $O(n)$ 时间。 +- **空间复杂度为 $O(n)$** :在最差情况下,即树退化为链表时,递归深度达到 $n$ ,系统占用 $O(n)$ 栈帧空间。 diff --git a/mkdocs-en.yml b/mkdocs-en.yml index a483e4f18..0872dfbe4 100644 --- a/mkdocs-en.yml +++ b/mkdocs-en.yml @@ -52,13 +52,14 @@ nav: - 3.3 Number Encoding *: chapter_data_structure/number_encoding.md - 3.4 Character Encoding *: chapter_data_structure/character_encoding.md - 3.5 Summary: chapter_data_structure/summary.md - # - Chapter 4. Array and Linked List: - # # [icon: material/view-list-outline] - # - chapter_array_and_linkedlist/index.md - # - 4.1 Array: chapter_array_and_linkedlist/array.md - # - 4.2 Linked List: chapter_array_and_linkedlist/linked_list.md - # - 4.3 List: chapter_array_and_linkedlist/list.md - # - 4.4 Summary: chapter_array_and_linkedlist/summary.md + - Chapter 4. Array and Linked List: + # [icon: material/view-list-outline] + - chapter_array_and_linkedlist/index.md + - 4.1 Array: chapter_array_and_linkedlist/array.md + - 4.2 Linked List: chapter_array_and_linkedlist/linked_list.md + - 4.3 List: chapter_array_and_linkedlist/list.md + - 4.4 Memory and Cache: chapter_array_and_linkedlist/ram_and_cache.md + - 4.5 Summary: chapter_array_and_linkedlist/summary.md # - Chapter 5. Stack and Queue: # # [icon: material/stack-overflow] # - chapter_stack_and_queue/index.md @@ -87,7 +88,7 @@ nav: # - chapter_heap/index.md # - 8.1 Heap: chapter_heap/heap.md # - 8.2 Building a Heap: chapter_heap/build_heap.md - # - 8.3 Top-K Problem: chapter_heap/top_k.md + # - 8.3 Top-k Problem: chapter_heap/top_k.md # - 8.4 Summary: chapter_heap/summary.md # - Chapter 9. Graph: # # [icon: material/graphql] diff --git a/mkdocs.yml b/mkdocs.yml index be3b5bfe5..9e90f2092 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -216,7 +216,7 @@ nav: - chapter_heap/index.md - 8.1   堆: chapter_heap/heap.md - 8.2   建堆操作: chapter_heap/build_heap.md - - 8.3   Top-K 问题: chapter_heap/top_k.md + - 8.3   Top-k 问题: chapter_heap/top_k.md - 8.4   小结: chapter_heap/summary.md - 第 9 章   图: # [icon: material/graphql]