From 0bec52d7ccd5cba8037da79187ca3b8b630f4feb Mon Sep 17 00:00:00 2001 From: krahets Date: Sun, 9 Apr 2023 19:12:37 +0800 Subject: [PATCH] =?UTF-8?q?Polish=20the=20chapter=20of=20heap,=20introduct?= =?UTF-8?q?ion,=20preface.=20Replace=20"=E5=85=B6=E5=AE=83"=20with=20"?= =?UTF-8?q?=E5=85=B6=E4=BB=96"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- codes/c/chapter_sorting/bucket_sort.c | 18 ++++--- .../chapter_graph/graph_adjacency_list.cpp | 54 ++++++++++++------- codes/cpp/chapter_sorting/bucket_sort.cpp | 20 ++++--- .../chapter_graph/graph_adjacency_list.cs | 2 +- .../go/chapter_graph/graph_adjacency_list.go | 2 +- codes/go/chapter_sorting/bucket_sort.go | 2 +- .../chapter_graph/graph_adjacency_list.java | 2 +- codes/java/chapter_sorting/bucket_sort.java | 2 +- .../chapter_graph/graph_adjacency_list.js | 2 +- .../javascript/chapter_sorting/bucket_sort.js | 2 +- .../chapter_graph/graph_adjacency_list.py | 2 +- codes/python/chapter_sorting/bucket_sort.py | 2 +- .../chapter_graph/graph_adjacency_list.swift | 2 +- codes/swift/chapter_sorting/bucket_sort.swift | 2 +- .../chapter_graph/graph_adjacency_list.ts | 2 +- .../typescript/chapter_sorting/bucket_sort.ts | 2 +- docs/chapter_array_and_linkedlist/summary.md | 2 +- .../time_complexity.md | 2 +- .../chapter_data_structure/data_and_memory.md | 2 +- docs/chapter_heap/heap.md | 52 +++++++++--------- docs/chapter_heap/summary.md | 12 ++--- .../algorithms_are_everywhere.md | 22 ++++---- docs/chapter_introduction/summary.md | 10 ++-- docs/chapter_introduction/what_is_dsa.md | 31 +++++------ docs/chapter_preface/about_the_book.md | 34 ++++++------ docs/chapter_preface/suggestions.md | 41 +++++++------- docs/chapter_preface/summary.md | 12 ++--- docs/chapter_reference/index.md | 6 +-- docs/chapter_sorting/summary.md | 2 +- 29 files changed, 185 insertions(+), 161 deletions(-) diff --git a/codes/c/chapter_sorting/bucket_sort.c b/codes/c/chapter_sorting/bucket_sort.c index 33ba12c1..f923edb2 100644 --- a/codes/c/chapter_sorting/bucket_sort.c +++ b/codes/c/chapter_sorting/bucket_sort.c @@ -7,23 +7,24 @@ #include "../include/include.h" /* 冒泡排序 */ -void bucketSort(double nums[], int size) { +void bucketSort(double nums[], int size) +{ // 初始化 k = n/2 个桶,预期向每个桶分配 2 个元素 int k = size / 2; - // 1. 将数组元素分配到各个桶中 + // 1. 将数组元素分配到各个桶中 // 输入数据范围 [0, 1),使用 num * k 映射到索引范围 [0, k-1] // 将 num 添加进桶 i - // 2. 对各个桶执行排序 + // 2. 对各个桶执行排序 - // 使用内置切片排序函数,也可以替换成其它排序算法 - - // 3. 遍历桶合并结果 + // 使用内置切片排序函数,也可以替换成其他排序算法 + // 3. 遍历桶合并结果 } /* Driver Code */ -int main() { +int main() +{ // 设输入数据为浮点数,范围为 [0, 1) double nums[] = {0.49, 0.96, 0.82, 0.09, 0.57, 0.43, 0.91, 0.75, 0.15, 0.37}; int size = sizeof(nums) / sizeof(double); @@ -31,7 +32,8 @@ int main() { printf("桶排序完成后 nums = "); printf("["); - for (int i = 0; i < size - 1; i++) { + for (int i = 0; i < size - 1; i++) + { printf("%g, ", nums[i]); } printf("]"); diff --git a/codes/cpp/chapter_graph/graph_adjacency_list.cpp b/codes/cpp/chapter_graph/graph_adjacency_list.cpp index 174b31d9..8ff2b5dc 100644 --- a/codes/cpp/chapter_graph/graph_adjacency_list.cpp +++ b/codes/cpp/chapter_graph/graph_adjacency_list.cpp @@ -7,15 +7,19 @@ #include "../include/include.hpp" /* 基于邻接表实现的无向图类 */ -class GraphAdjList { +class GraphAdjList +{ public: // 邻接表,key: 顶点,value:该顶点的所有邻接顶点 - unordered_map> adjList; - + unordered_map> adjList; + /* 在 vector 中删除指定节点 */ - void remove(vector &vec, Vertex *vet) { - for (int i = 0; i < vec.size(); i++) { - if (vec[i] == vet) { + void remove(vector &vec, Vertex *vet) + { + for (int i = 0; i < vec.size(); i++) + { + if (vec[i] == vet) + { vec.erase(vec.begin() + i); break; } @@ -23,9 +27,11 @@ public: } /* 构造方法 */ - GraphAdjList(const vector>& edges) { + GraphAdjList(const vector> &edges) + { // 添加所有顶点和边 - for (const vector& edge : edges) { + for (const vector &edge : edges) + { addVertex(edge[0]); addVertex(edge[1]); addEdge(edge[0], edge[1]); @@ -36,7 +42,8 @@ public: int size() { return adjList.size(); } /* 添加边 */ - void addEdge(Vertex* vet1, Vertex* vet2) { + void addEdge(Vertex *vet1, Vertex *vet2) + { if (!adjList.count(vet1) || !adjList.count(vet2) || vet1 == vet2) throw invalid_argument("不存在顶点"); // 添加边 vet1 - vet2 @@ -45,7 +52,8 @@ public: } /* 删除边 */ - void removeEdge(Vertex* vet1, Vertex* vet2) { + void removeEdge(Vertex *vet1, Vertex *vet2) + { if (!adjList.count(vet1) || !adjList.count(vet2) || vet1 == vet2) throw invalid_argument("不存在顶点"); // 删除边 vet1 - vet2 @@ -54,30 +62,36 @@ public: } /* 添加顶点 */ - void addVertex(Vertex* vet) { - if (adjList.count(vet)) return; + void addVertex(Vertex *vet) + { + if (adjList.count(vet)) + return; // 在邻接表中添加一个新链表 - adjList[vet] = vector(); + adjList[vet] = vector(); } /* 删除顶点 */ - void removeVertex(Vertex* vet) { + void removeVertex(Vertex *vet) + { if (!adjList.count(vet)) throw invalid_argument("不存在顶点"); // 在邻接表中删除顶点 vet 对应的链表 adjList.erase(vet); - // 遍历其它顶点的链表,删除所有包含 vet 的边 - for (auto& [key, vec] : adjList) { + // 遍历其他顶点的链表,删除所有包含 vet 的边 + for (auto &[key, vec] : adjList) + { remove(vec, vet); } } /* 打印邻接表 */ - void print() { + void print() + { cout << "邻接表 =" << endl; - for (auto& adj : adjList) { - const auto& key= adj.first; - const auto& vec = adj.second; + for (auto &adj : adjList) + { + const auto &key = adj.first; + const auto &vec = adj.second; cout << key->val << ": "; PrintUtil::printVector(vetsToVals(vec)); } diff --git a/codes/cpp/chapter_sorting/bucket_sort.cpp b/codes/cpp/chapter_sorting/bucket_sort.cpp index f3e97b26..997c369d 100644 --- a/codes/cpp/chapter_sorting/bucket_sort.cpp +++ b/codes/cpp/chapter_sorting/bucket_sort.cpp @@ -7,33 +7,39 @@ #include "../include/include.hpp" /* 桶排序 */ -void bucketSort(vector &nums) { +void bucketSort(vector &nums) +{ // 初始化 k = n/2 个桶,预期向每个桶分配 2 个元素 int k = nums.size() / 2; vector> buckets(k); // 1. 将数组元素分配到各个桶中 - for (float num : nums) { + for (float num : nums) + { // 输入数据范围 [0, 1),使用 num * k 映射到索引范围 [0, k-1] int i = num * k; // 将 num 添加进桶 bucket_idx buckets[i].push_back(num); } // 2. 对各个桶执行排序 - for (vector &bucket : buckets) { - // 使用内置排序函数,也可以替换成其它排序算法 + for (vector &bucket : buckets) + { + // 使用内置排序函数,也可以替换成其他排序算法 sort(bucket.begin(), bucket.end()); } // 3. 遍历桶合并结果 int i = 0; - for (vector &bucket : buckets) { - for (float num : bucket) { + for (vector &bucket : buckets) + { + for (float num : bucket) + { nums[i++] = num; } } } /* Driver Code */ -int main() { +int main() +{ // 设输入数据为浮点数,范围为 [0, 1) vector nums = {0.49f, 0.96f, 0.82f, 0.09f, 0.57f, 0.43f, 0.91f, 0.75f, 0.15f, 0.37f}; bucketSort(nums); diff --git a/codes/csharp/chapter_graph/graph_adjacency_list.cs b/codes/csharp/chapter_graph/graph_adjacency_list.cs index 67a403fa..5de7b3a3 100644 --- a/codes/csharp/chapter_graph/graph_adjacency_list.cs +++ b/codes/csharp/chapter_graph/graph_adjacency_list.cs @@ -70,7 +70,7 @@ public class GraphAdjList throw new InvalidOperationException(); // 在邻接表中删除顶点 vet 对应的链表 adjList.Remove(vet); - // 遍历其它顶点的链表,删除所有包含 vet 的边 + // 遍历其他顶点的链表,删除所有包含 vet 的边 foreach (List list in adjList.Values) { list.Remove(vet); diff --git a/codes/go/chapter_graph/graph_adjacency_list.go b/codes/go/chapter_graph/graph_adjacency_list.go index 9ff60a81..cdd35f8d 100644 --- a/codes/go/chapter_graph/graph_adjacency_list.go +++ b/codes/go/chapter_graph/graph_adjacency_list.go @@ -79,7 +79,7 @@ func (g *graphAdjList) removeVertex(vet Vertex) { } // 在邻接表中删除顶点 vet 对应的链表 delete(g.adjList, vet) - // 遍历其它顶点的链表,删除所有包含 vet 的边 + // 遍历其他顶点的链表,删除所有包含 vet 的边 for _, list := range g.adjList { DeleteSliceElms(list, vet) } diff --git a/codes/go/chapter_sorting/bucket_sort.go b/codes/go/chapter_sorting/bucket_sort.go index acbf7fa6..32ce56fb 100644 --- a/codes/go/chapter_sorting/bucket_sort.go +++ b/codes/go/chapter_sorting/bucket_sort.go @@ -23,7 +23,7 @@ func bucketSort(nums []float64) { } // 2. 对各个桶执行排序 for i := 0; i < k; i++ { - // 使用内置切片排序函数,也可以替换成其它排序算法 + // 使用内置切片排序函数,也可以替换成其他排序算法 sort.Float64s(buckets[i]) } // 3. 遍历桶合并结果 diff --git a/codes/java/chapter_graph/graph_adjacency_list.java b/codes/java/chapter_graph/graph_adjacency_list.java index 67fe95a2..0ae2bdc4 100644 --- a/codes/java/chapter_graph/graph_adjacency_list.java +++ b/codes/java/chapter_graph/graph_adjacency_list.java @@ -62,7 +62,7 @@ class GraphAdjList { throw new IllegalArgumentException(); // 在邻接表中删除顶点 vet 对应的链表 adjList.remove(vet); - // 遍历其它顶点的链表,删除所有包含 vet 的边 + // 遍历其他顶点的链表,删除所有包含 vet 的边 for (List list : adjList.values()) { list.remove(vet); } diff --git a/codes/java/chapter_sorting/bucket_sort.java b/codes/java/chapter_sorting/bucket_sort.java index f047d13e..cc3f5938 100644 --- a/codes/java/chapter_sorting/bucket_sort.java +++ b/codes/java/chapter_sorting/bucket_sort.java @@ -26,7 +26,7 @@ public class bucket_sort { } // 2. 对各个桶执行排序 for (List bucket : buckets) { - // 使用内置排序函数,也可以替换成其它排序算法 + // 使用内置排序函数,也可以替换成其他排序算法 Collections.sort(bucket); } // 3. 遍历桶合并结果 diff --git a/codes/javascript/chapter_graph/graph_adjacency_list.js b/codes/javascript/chapter_graph/graph_adjacency_list.js index c06ed18d..c6797b81 100644 --- a/codes/javascript/chapter_graph/graph_adjacency_list.js +++ b/codes/javascript/chapter_graph/graph_adjacency_list.js @@ -61,7 +61,7 @@ class GraphAdjList { } // 在邻接表中删除顶点 vet 对应的链表 this.adjList.delete(vet); - // 遍历其它顶点的链表,删除所有包含 vet 的边 + // 遍历其他顶点的链表,删除所有包含 vet 的边 for (let set of this.adjList.values()) { const index = set.indexOf(vet); if (index > -1) { diff --git a/codes/javascript/chapter_sorting/bucket_sort.js b/codes/javascript/chapter_sorting/bucket_sort.js index e2242df0..12dcbc1d 100644 --- a/codes/javascript/chapter_sorting/bucket_sort.js +++ b/codes/javascript/chapter_sorting/bucket_sort.js @@ -21,7 +21,7 @@ function bucketSort(nums) { } // 2. 对各个桶执行排序 for (const bucket of buckets) { - // 使用内置排序函数,也可以替换成其它排序算法 + // 使用内置排序函数,也可以替换成其他排序算法 bucket.sort((a, b) => a - b); } // 3. 遍历桶合并结果 diff --git a/codes/python/chapter_graph/graph_adjacency_list.py b/codes/python/chapter_graph/graph_adjacency_list.py index 313481f1..39806ccc 100644 --- a/codes/python/chapter_graph/graph_adjacency_list.py +++ b/codes/python/chapter_graph/graph_adjacency_list.py @@ -56,7 +56,7 @@ class GraphAdjList: raise ValueError # 在邻接表中删除顶点 vet 对应的链表 self.adj_list.pop(vet) - # 遍历其它顶点的链表,删除所有包含 vet 的边 + # 遍历其他顶点的链表,删除所有包含 vet 的边 for vertex in self.adj_list: if vet in self.adj_list[vertex]: self.adj_list[vertex].remove(vet) diff --git a/codes/python/chapter_sorting/bucket_sort.py b/codes/python/chapter_sorting/bucket_sort.py index 72ac9c4c..15dd9e15 100644 --- a/codes/python/chapter_sorting/bucket_sort.py +++ b/codes/python/chapter_sorting/bucket_sort.py @@ -18,7 +18,7 @@ def bucket_sort(nums: list[float]) -> None: buckets[i].append(num) # 2. 对各个桶执行排序5 for bucket in buckets: - # 使用内置排序函数,也可以替换成其它排序算法 + # 使用内置排序函数,也可以替换成其他排序算法 bucket.sort() # 3. 遍历桶合并结果 i = 0 diff --git a/codes/swift/chapter_graph/graph_adjacency_list.swift b/codes/swift/chapter_graph/graph_adjacency_list.swift index ae5d04ba..637efb08 100644 --- a/codes/swift/chapter_graph/graph_adjacency_list.swift +++ b/codes/swift/chapter_graph/graph_adjacency_list.swift @@ -63,7 +63,7 @@ public class GraphAdjList { } // 在邻接表中删除顶点 vet 对应的链表 adjList.removeValue(forKey: vet) - // 遍历其它顶点的链表,删除所有包含 vet 的边 + // 遍历其他顶点的链表,删除所有包含 vet 的边 for key in adjList.keys { adjList[key]?.removeAll(where: { $0 == vet }) } diff --git a/codes/swift/chapter_sorting/bucket_sort.swift b/codes/swift/chapter_sorting/bucket_sort.swift index f090cbdf..1967695c 100644 --- a/codes/swift/chapter_sorting/bucket_sort.swift +++ b/codes/swift/chapter_sorting/bucket_sort.swift @@ -18,7 +18,7 @@ func bucketSort(nums: inout [Double]) { } // 2. 对各个桶执行排序 for i in buckets.indices { - // 使用内置排序函数,也可以替换成其它排序算法 + // 使用内置排序函数,也可以替换成其他排序算法 buckets[i].sort() } // 3. 遍历桶合并结果 diff --git a/codes/typescript/chapter_graph/graph_adjacency_list.ts b/codes/typescript/chapter_graph/graph_adjacency_list.ts index 0ce5fbbf..31436c47 100644 --- a/codes/typescript/chapter_graph/graph_adjacency_list.ts +++ b/codes/typescript/chapter_graph/graph_adjacency_list.ts @@ -61,7 +61,7 @@ class GraphAdjList { } // 在邻接表中删除顶点 vet 对应的链表 this.adjList.delete(vet); - // 遍历其它顶点的链表,删除所有包含 vet 的边 + // 遍历其他顶点的链表,删除所有包含 vet 的边 for (let set of this.adjList.values()) { const index: number = set.indexOf(vet); if (index > -1) { diff --git a/codes/typescript/chapter_sorting/bucket_sort.ts b/codes/typescript/chapter_sorting/bucket_sort.ts index 3aeb4dc4..0d8d01d5 100644 --- a/codes/typescript/chapter_sorting/bucket_sort.ts +++ b/codes/typescript/chapter_sorting/bucket_sort.ts @@ -21,7 +21,7 @@ function bucketSort(nums: number[]): void { } // 2. 对各个桶执行排序 for (const bucket of buckets) { - // 使用内置排序函数,也可以替换成其它排序算法 + // 使用内置排序函数,也可以替换成其他排序算法 bucket.sort((a, b) => a - b); } // 3. 遍历桶合并结果 diff --git a/docs/chapter_array_and_linkedlist/summary.md b/docs/chapter_array_and_linkedlist/summary.md index b7497a4c..6833db8e 100644 --- a/docs/chapter_array_and_linkedlist/summary.md +++ b/docs/chapter_array_and_linkedlist/summary.md @@ -19,7 +19,7 @@ !!! note "缓存局部性" - 在计算机中,数据读写速度排序是“硬盘 < 内存 < CPU 缓存”。当我们访问数组元素时,计算机不仅会加载它,还会缓存其周围的其它数据,从而借助高速缓存来提升后续操作的执行速度。链表则不然,计算机只能挨个地缓存各个节点,这样的多次“搬运”降低了整体效率。 + 在计算机中,数据读写速度排序是“硬盘 < 内存 < CPU 缓存”。当我们访问数组元素时,计算机不仅会加载它,还会缓存其周围的其他数据,从而借助高速缓存来提升后续操作的执行速度。链表则不然,计算机只能挨个地缓存各个节点,这样的多次“搬运”降低了整体效率。 - 下表对比了数组与链表在各种操作上的效率。 diff --git a/docs/chapter_computational_complexity/time_complexity.md b/docs/chapter_computational_complexity/time_complexity.md index eee3867e..ec08e3f0 100755 --- a/docs/chapter_computational_complexity/time_complexity.md +++ b/docs/chapter_computational_complexity/time_complexity.md @@ -741,7 +741,7 @@ $$ ### 2) 判断渐近上界 -**时间复杂度由多项式 $T(n)$ 中最高阶的项来决定**。这是因为在 $n$ 趋于无穷大时,最高阶的项将发挥主导作用,其它项的影响都可以被忽略。 +**时间复杂度由多项式 $T(n)$ 中最高阶的项来决定**。这是因为在 $n$ 趋于无穷大时,最高阶的项将发挥主导作用,其他项的影响都可以被忽略。 以下表格展示了一些例子,其中一些夸张的值是为了强调“系数无法撼动阶数”这一结论。当 $n$ 趋于无穷大时,这些常数变得无足轻重。 diff --git a/docs/chapter_data_structure/data_and_memory.md b/docs/chapter_data_structure/data_and_memory.md index e1dbfdb0..68bafce0 100644 --- a/docs/chapter_data_structure/data_and_memory.md +++ b/docs/chapter_data_structure/data_and_memory.md @@ -37,7 +37,7 @@ 3. 当所有 bits 为 0 时代表数字 $0$ ,从零开始增大,可得最大正数为 $2^{31} - 1$; 4. 剩余 $2^{31}$ 个数字全部用来表示负数,因此最小负数为 $-2^{31}$ ;具体细节涉及“源码、反码、补码”的相关知识,有兴趣的同学可以查阅学习; -其它整数类型 byte, short, long 的取值范围的计算方法与 int 类似,在此不再赘述。 +其他整数类型 byte, short, long 的取值范围的计算方法与 int 类似,在此不再赘述。 ### 浮点数表示方式 * diff --git a/docs/chapter_heap/heap.md b/docs/chapter_heap/heap.md index a1e5d255..7c503db9 100644 --- a/docs/chapter_heap/heap.md +++ b/docs/chapter_heap/heap.md @@ -1,25 +1,25 @@ # 堆 -「堆 Heap」是一棵限定条件下的「完全二叉树」。根据成立条件,堆主要分为两种类型: +「堆 Heap」是一种满足特定条件的完全二叉树,可分为两种类型: - 「大顶堆 Max Heap」,任意节点的值 $\geq$ 其子节点的值; - 「小顶堆 Min Heap」,任意节点的值 $\leq$ 其子节点的值; ![小顶堆与大顶堆](heap.assets/min_heap_and_max_heap.png) -## 堆术语与性质 +堆作为完全二叉树的一个特例,具有以下特性: -- 由于堆是完全二叉树,因此最底层节点靠左填充,其它层节点皆被填满。 -- 二叉树中的根节点对应「堆顶」,底层最靠右节点对应「堆底」。 -- 对于大顶堆 / 小顶堆,其堆顶元素(即根节点)的值最大 / 最小。 +- 最底层节点靠左填充,其他层的节点都被填满。 +- 我们将二叉树的根节点称为「堆顶」,将底层最靠右的节点称为「堆底」。 +- 对于大顶堆(小顶堆),堆顶元素(即根节点)的值分别是最大(最小)的。 ## 堆常用操作 -值得说明的是,多数编程语言提供的是「优先队列 Priority Queue」,其是一种抽象数据结构,**定义为具有出队优先级的队列**。 +需要指出的是,许多编程语言提供的是「优先队列 Priority Queue」,这是一种抽象数据结构,定义为具有优先级排序的队列。 -而恰好,**堆的定义与优先队列的操作逻辑完全吻合**,大顶堆就是一个元素从大到小出队的优先队列。从使用角度看,我们可以将「优先队列」和「堆」理解为等价的数据结构。因此,本文与代码对两者不做特别区分,统一使用「堆」来命名。 +实际上,**堆通常用作实现优先队列,大顶堆相当于元素按从大到小顺序出队的优先队列**。从使用角度来看,我们可以将「优先队列」和「堆」看作等价的数据结构。因此,本书对两者不做特别区分,统一使用「堆」来命名。 -堆的常用操作见下表,方法名需根据编程语言确定。 +堆的常用操作见下表,方法名需要根据编程语言来确定。
@@ -33,11 +33,11 @@
-我们可以直接使用编程语言提供的堆类(或优先队列类)。 +在实际应用中,我们可以直接使用编程语言提供的堆类(或优先队列类)。 !!! tip - 类似于排序中“从小到大排列”和“从大到小排列”,“大顶堆”和“小顶堆”可仅通过修改 Comparator 来互相转换。 + 类似于排序算法中的“从小到大排列”和“从大到小排列”,我们可以通过修改 Comparator 来实现“小顶堆”与“大顶堆”之间的转换。 === "Java" @@ -303,19 +303,19 @@ ## 堆的实现 -下文实现的是「大顶堆」,若想转换为「小顶堆」,将所有大小逻辑判断取逆(例如将 $\geq$ 替换为 $\leq$ )即可,有兴趣的同学可自行实现。 +下文实现的是大顶堆。若要将其转换为小顶堆,只需将所有大小逻辑判断取逆(例如,将 $\geq$ 替换为 $\leq$ )。感兴趣的读者可以自行实现。 ### 堆的存储与表示 -在二叉树章节我们学过,「完全二叉树」非常适合使用「数组」来表示,而堆恰好是一棵完全二叉树,**因而我们采用「数组」来存储「堆」**。 +我们在二叉树章节中学习到,完全二叉树非常适合用数组来表示。由于堆正是一种完全二叉树,**我们将采用数组来存储堆**。 -**二叉树指针**。使用数组表示二叉树时,元素代表节点值,索引代表节点在二叉树中的位置,**而节点指针通过索引映射公式来实现**。 +当使用数组表示二叉树时,元素代表节点值,索引代表节点在二叉树中的位置。**节点指针通过索引映射公式来实现**。 -具体地,给定索引 $i$ ,那么其左子节点索引为 $2i + 1$ 、右子节点索引为 $2i + 2$ 、父节点索引为 $(i - 1) / 2$ (向下整除)。当索引越界时,代表空节点或节点不存在。 +具体而言,给定索引 $i$ ,其左子节点索引为 $2i + 1$ ,右子节点索引为 $2i + 2$ ,父节点索引为 $(i - 1) / 2$(向下取整)。当索引越界时,表示空节点或节点不存在。 ![堆的表示与存储](heap.assets/representation_of_heap.png) -我们将索引映射公式封装成函数,以便后续使用。 +我们可以将索引映射公式封装成函数,方便后续使用。 === "Java" @@ -419,7 +419,7 @@ ### 访问堆顶元素 -堆顶元素是二叉树的根节点,即列表首元素。 +堆顶元素即为二叉树的根节点,也就是列表的首个元素。 === "Java" @@ -483,9 +483,9 @@ ### 元素入堆 -给定元素 `val` ,我们先将其添加到堆底。添加后,由于 `val` 可能大于堆中其它元素,此时堆的成立条件可能已经被破坏,**因此需要修复从插入节点到根节点这条路径上的各个节点**,该操作被称为「堆化 Heapify」。 +给定元素 `val` ,我们首先将其添加到堆底。添加之后,由于 val 可能大于堆中其他元素,堆的成立条件可能已被破坏。因此,**需要修复从插入节点到根节点的路径上的各个节点**,这个操作被称为「堆化 Heapify」。 -考虑从入堆节点开始,**从底至顶执行堆化**。具体地,比较插入节点与其父节点的值,若插入节点更大则将它们交换;并循环以上操作,从底至顶地修复堆中的各个节点;直至越过根节点时结束,或当遇到无需交换的节点时提前结束。 +考虑从入堆节点开始,**从底至顶执行堆化**。具体来说,我们比较插入节点与其父节点的值,如果插入节点更大,则将它们交换。然后继续执行此操作,从底至顶修复堆中的各个节点,直至越过根节点或遇到无需交换的节点时结束。 === "<1>" ![元素入堆步骤](heap.assets/heap_push_step1.png) @@ -505,7 +505,7 @@ === "<6>" ![heap_push_step6](heap.assets/heap_push_step6.png) -设节点总数为 $n$ ,则树的高度为 $O(\log n)$ ,易得堆化操作的循环轮数最多为 $O(\log n)$ ,**因而元素入堆操作的时间复杂度为 $O(\log n)$** 。 +设节点总数为 $n$ ,则树的高度为 $O(\log n)$ 。由此可知,堆化操作的循环轮数最多为 $O(\log n)$ ,**元素入堆操作的时间复杂度为 $O(\log n)$** 。 === "Java" @@ -589,13 +589,13 @@ ### 堆顶元素出堆 -堆顶元素是二叉树根节点,即列表首元素,如果我们直接将首元素从列表中删除,则二叉树中所有节点都会随之发生移位(索引发生变化),这样后续使用堆化修复就很麻烦了。为了尽量减少元素索引变动,采取以下操作步骤: +堆顶元素是二叉树的根节点,即列表首元素。如果我们直接从列表中删除首元素,那么二叉树中所有节点的索引都会发生变化,这将使得后续使用堆化修复变得困难。为了尽量减少元素索引的变动,我们采取以下操作步骤: 1. 交换堆顶元素与堆底元素(即交换根节点与最右叶节点); -2. 交换完成后,将堆底从列表中删除(注意,因为已经交换,实际上删除的是原来的堆顶元素); +2. 交换完成后,将堆底从列表中删除(注意,由于已经交换,实际上删除的是原来的堆顶元素); 3. 从根节点开始,**从顶至底执行堆化**; -顾名思义,**从顶至底堆化的操作方向与从底至顶堆化相反**,我们比较根节点的值与其两个子节点的值,将最大的子节点与根节点执行交换,并循环以上操作,直到越过叶节点时结束,或当遇到无需交换的节点时提前结束。 +顾名思义,**从顶至底堆化的操作方向与从底至顶堆化相反**,我们将根节点的值与其两个子节点的值进行比较,将最大的子节点与根节点交换;然后循环执行此操作,直到越过叶节点或遇到无需交换的节点时结束。 === "<1>" ![堆顶元素出堆步骤](heap.assets/heap_pop_step1.png) @@ -627,7 +627,7 @@ === "<10>" ![heap_pop_step10](heap.assets/heap_pop_step10.png) -与元素入堆操作类似,**堆顶元素出堆操作的时间复杂度为 $O(\log n)$** 。 +与元素入堆操作相似,堆顶元素出堆操作的时间复杂度也为 $O(\log n)$。 === "Java" @@ -711,6 +711,6 @@ ## 堆常见应用 -- **优先队列**。堆常作为实现优先队列的首选数据结构,入队和出队操作时间复杂度为 $O(\log n)$ ,建队操作为 $O(n)$ ,皆非常高效。 -- **堆排序**。给定一组数据,我们使用其建堆,并依次全部弹出,则可以得到有序的序列。当然,堆排序一般无需弹出元素,仅需每轮将堆顶元素交换至数组尾部并减小堆的长度即可。 -- **获取最大的 $k$ 个元素**。这既是一道经典算法题目,也是一种常见应用,例如选取热度前 10 的新闻作为微博热搜,选取前 10 销量的商品等。 +- **优先队列**:堆通常作为实现优先队列的首选数据结构,其入队和出队操作的时间复杂度均为 $O(\log n)$,而建队操作为 $O(n)$,这些操作都非常高效。 +- **堆排序**:给定一组数据,我们可以用它们建立一个堆,然后依次将所有元素弹出,从而得到一个有序序列。当然,堆排序的实现方法并不需要弹出元素,而是每轮将堆顶元素交换至数组尾部并缩小堆的长度。 +- **获取最大的 $k$ 个元素**:这是一个经典的算法问题,同时也是一种典型应用,例如选择热度前 10 的新闻作为微博热搜,选取销量前 10 的商品等。 diff --git a/docs/chapter_heap/summary.md b/docs/chapter_heap/summary.md index c8773d28..d53d7d50 100644 --- a/docs/chapter_heap/summary.md +++ b/docs/chapter_heap/summary.md @@ -1,8 +1,8 @@ # 小结 -- 堆是一棵限定条件下的完全二叉树,根据成立条件可分为大顶堆和小顶堆。大(小)顶堆的堆顶元素最大(小)。 -- 优先队列定义为一种具有出队优先级的队列。堆是实现优先队列的最常用数据结构。 -- 堆的常用操作和对应时间复杂度为元素入堆 $O(\log n)$ 、堆顶元素出堆 $O(\log n)$ 、访问堆顶元素 $O(1)$ 等。 -- 完全二叉树非常适合用数组来表示,因此我们一般用数组来存储堆。 -- 堆化操作用于修复堆的特性,在入堆和出堆操作中都会使用到。 -- 输入 $n$ 个元素并建堆的时间复杂度可以被优化至 $O(n)$ ,非常高效。 +- 堆是一棵完全二叉树,根据成立条件可分为大顶堆和小顶堆。大(小)顶堆的堆顶元素是最大(小)的。 +- 优先队列的定义是具有出队优先级的队列,通常使用堆来实现。 +- 堆的常用操作及其对应的时间复杂度包括:元素入堆 $O(\log n)$ 、堆顶元素出堆 $O(\log n)$ 和访问堆顶元素 $O(1)$ 等。 +- 完全二叉树非常适合用数组表示,因此我们通常使用数组来存储堆。 +- 堆化操作用于维护堆的性质,在入堆和出堆操作中都会用到。 +- 输入 $n$ 个元素并建堆的时间复杂度可以优化至 $O(n)$,非常高效。 diff --git a/docs/chapter_introduction/algorithms_are_everywhere.md b/docs/chapter_introduction/algorithms_are_everywhere.md index 92785f36..cd11dba8 100644 --- a/docs/chapter_introduction/algorithms_are_everywhere.md +++ b/docs/chapter_introduction/algorithms_are_everywhere.md @@ -1,18 +1,18 @@ # 算法无处不在 -听到“算法”这个词,我们一般会联想到数学。但实际上,大多数算法并不包含复杂的数学,而更像是在考察基本逻辑,而这些逻辑在我们日常生活中处处可见。 +当我们听到“算法”这个词时,很自然地会想到数学。然而实际上,许多算法并不涉及复杂数学,而是更多地依赖于基本逻辑,这些逻辑在我们的日常生活中处处可见。 -在正式介绍算法之前,我想告诉你一件有趣的事:**其实,你在过去已经学会了很多算法,并且已经习惯将它们应用到日常生活中**。接下来,我将介绍两个具体例子来佐证。 +在正式探讨算法之前,有一个有趣的事实值得分享:**实际上,你已经学会了许多算法,并习惯将他们应用到日常生活中了**。下面,我将举两个具体例子来证实这一点。 -**例一:拼积木**。一套积木,除了有许多部件之外,还会附送详细的拼装说明书。我们按照说明书上一步步操作,即可拼出复杂的积木模型。 +**例一:组装积木**。一套积木,除了包含许多零件之外,还附有详细的组装说明书。我们按照说明书一步步操作,就能组装出精美的积木模型。 -如果从数据结构与算法的角度看,大大小小的「积木」就是数据结构,而「拼装说明书」上的一系列步骤就是算法。 +从数据结构与算法的角度来看,积木的各种形状和连接方式代表数据结构,而组装说明书上的一系列步骤则是算法。 -**例二:查字典**。在字典中,每个汉字都有一个对应的拼音,而字典是按照拼音的英文字母表顺序排列的。假设需要在字典中查询任意一个拼音首字母为 $r$ 的字,一般我们会这样做: +**例二:查阅字典**。在字典里,每个汉字都对应一个拼音,而字典是按照拼音的英文字母顺序排列的。假设我们需要查找一个拼音首字母为 $r$ 的字,通常会这样操作: -1. 打开字典大致一半页数的位置,查看此页的首字母是什么(假设为 $m$ ); -2. 由于在英文字母表中 $r$ 在 $m$ 的后面,因此应排除字典前半部分,查找范围仅剩后半部分; -3. 循环执行步骤 1-2 ,直到找到拼音首字母为 $r$ 的页码时终止。 +1. 翻开字典约一半的页数,查看该页首字母是什么(假设为 $m$ ); +2. 由于在英文字母表中 $r$ 位于 $m$ 之后,所以排除字典前半部分,查找范围缩小到后半部分; +3. 不断重复步骤 1-2 ,直至找到拼音首字母为 $r$ 的页码为止。 === "<1>" ![查字典步骤](algorithms_are_everywhere.assets/look_up_dictionary_step_1.png) @@ -29,10 +29,10 @@ === "<5>" ![look_up_dictionary_step_5](algorithms_are_everywhere.assets/look_up_dictionary_step_5.png) -查字典这个小学生的标配技能,实际上就是大名鼎鼎的「二分查找」。从数据结构角度,我们可以将字典看作是一个已排序的「数组」;而从算法角度,我们可将上述查字典的一系列指令看作是「二分查找」算法。 +查阅字典这个小学生必备技能,实际上就是著名的「二分查找」。从数据结构的角度,我们可以把字典视为一个已排序的「数组」;从算法的角度,我们可以将上述查字典的一系列操作看作是「二分查找」算法。 -小到烹饪一道菜、大到星际航行,几乎所有问题的解决都离不开算法。计算机的出现,使我们可以通过编程将数据结构存储在内存中,也可以编写代码来调用 CPU, GPU 执行算法,从而将生活中的问题搬运到计算机中,更加高效地解决各式各样的复杂问题。 +小到烹饪一道菜,大到星际航行,几乎所有问题的解决都离不开算法。计算机的出现使我们能够通过编程将数据结构存储在内存中,同时编写代码调用 CPU 和 GPU 执行算法。这样一来,我们就能把生活中的问题转移到计算机上,以更高效的方式解决各种复杂问题。 !!! tip - 读到这里,如果你感到对数据结构、算法、数组、二分查找等此类概念一知半解,那么就太好了!因为这正是本书存在的价值,接下来,本书将会一步步地引导你进入数据结构与算法的知识殿堂。 + 阅读至此,如果你对数据结构、算法、数组和二分查找等概念仍感到一知半解,那么太好了!因为这正是本书存在的意义。接下来,这本书将一步步引导你深入数据结构与算法的知识殿堂。 diff --git a/docs/chapter_introduction/summary.md b/docs/chapter_introduction/summary.md index d449ed00..08734e69 100644 --- a/docs/chapter_introduction/summary.md +++ b/docs/chapter_introduction/summary.md @@ -1,7 +1,7 @@ # 小结 -- 算法在生活中随处可见,并不高深莫测。我们已经不知不觉地学习到许多“算法”,用于解决生活中大大小小的问题。 -- “查字典”的原理和二分查找算法一致。二分体现分而治之的重要算法思想。 -- 算法是在有限时间内解决特定问题的一组指令或操作步骤,数据结构是在计算机中组织与存储数据的方式。 -- 数据结构与算法两者紧密联系。数据结构是算法的底座,算法是发挥数据结构的舞台。 -- 乐高积木对应数据,积木形状和连接形式对应数据结构,拼装积木的流程步骤对应算法。 +- 算法在日常生活中无处不在,并不是遥不可及的高深知识。实际上,我们已经在不知不觉中学习了许多“算法”,用以解决生活中的大小问题。 +- 查阅字典的原理与二分查找算法相一致。二分查找体现了分而治之的重要算法思想。 +- 算法是在有限时间内解决特定问题的一组指令或操作步骤,而数据结构是计算机中组织和存储数据的方式。 +- 数据结构与算法紧密相连。数据结构是算法的基石,而算法则是发挥数据结构作用的舞台。 +- 乐高积木对应于数据,积木形状和连接方式代表数据结构,拼装积木的步骤则对应算法。 diff --git a/docs/chapter_introduction/what_is_dsa.md b/docs/chapter_introduction/what_is_dsa.md index 03196eab..a0d8c205 100644 --- a/docs/chapter_introduction/what_is_dsa.md +++ b/docs/chapter_introduction/what_is_dsa.md @@ -4,32 +4,31 @@ 「算法 Algorithm」是在有限时间内解决特定问题的一组指令或操作步骤。算法具有以下特性: -- 问题是明确的,需要拥有明确的输入和输出定义。 -- 解具有确定性,即给定相同输入时,输出一定相同。 -- 具有可行性,可在有限步骤、有限时间、有限内存空间下完成。 -- 独立于编程语言,即可用多种语言实现。 +- 问题是明确的,具有清晰的输入和输出定义。 +- 解具有确定性,即给定相同的输入时,输出始终相同。 +- 具有可行性,在有限步骤、时间和内存空间下可完成。 ## 数据结构定义 -「数据结构 Data Structure」是在计算机中组织与存储数据的方式。为了提高数据存储和操作性能,数据结构的设计原则有: +「数据结构 Data Structure」是计算机中组织和存储数据的方式。为了提高数据存储和操作性能,数据结构的设计目标包括: -- 空间占用尽可能小,节省计算机内存。 -- 数据操作尽量快,包括数据访问、添加、删除、更新等。 -- 提供简洁的数据表示和逻辑信息,以便算法高效运行。 +- 空间占用尽量减少,节省计算机内存。 +- 数据操作尽可能快速,涵盖数据访问、添加、删除、更新等。 +- 提供简洁的数据表示和逻辑信息,以利于算法高效运行。 -数据结构的设计是一个充满权衡的过程,这意味着如果获得某方面的优势,则往往需要在另一方面做出妥协。例如,链表相对于数组,数据添加删除操作更加方便,但牺牲了数据的访问速度;图相对于链表,提供了更多的逻辑信息,但需要占用更多的内存空间。 +数据结构设计是一个充满权衡的过程,这意味着要在某方面取得优势,往往需要在另一方面作出妥协。例如,链表相较于数组,在数据添加和删除操作上更加便捷,但牺牲了数据访问速度;图相较于链表,提供了更丰富的逻辑信息,但需要占用更大的内存空间。 ## 数据结构与算法的关系 -「数据结构」与「算法」是高度相关、紧密嵌合的,体现在: +「数据结构」与「算法」高度相关且紧密结合,具体表现在: -- 数据结构是算法的底座。数据结构为算法提供结构化存储的数据,以及操作数据的对应方法。 -- 算法是数据结构发挥的舞台。数据结构仅存储数据信息,结合算法才可解决特定问题。 -- 算法有对应最优的数据结构。给定算法,一般可基于不同的数据结构实现,而最终执行效率往往相差很大。 +- 数据结构是算法的基石。数据结构为算法提供了结构化存储的数据,以及用于操作数据的方法。 +- 算法是数据结构发挥的舞台。数据结构本身仅存储数据信息,通过结合算法才能解决特定问题。 +- 特定算法通常有对应最优的数据结构。算法通常可以基于不同的数据结构进行实现,但最终执行效率可能相差很大。 ![数据结构与算法的关系](what_is_dsa.assets/relationship_between_data_structure_and_algorithm.png) -如果将「LEGO 乐高」类比到「数据结构与算法」,那么可以得到下表所示的对应关系。 +类比「LEGO 乐高」和「数据结构与算法」,则对应关系如下表所示。
@@ -42,6 +41,8 @@
+值得注意的是,数据结构与算法独立于编程语言。正因如此,本书得以提供多种编程语言的实现。 + !!! tip "约定俗成的简称" - 在实际讨论中,我们通常会将「数据结构与算法」直接简称为「算法」。例如,我们熟称的 LeetCode 算法题目,实际上同时考察了数据结构和算法两部分知识。 + 在实际讨论时,我们通常会将「数据结构与算法」简称为「算法」。例如,众所周知的 LeetCode 算法题目,实际上同时考察了数据结构和算法两方面的知识。 diff --git a/docs/chapter_preface/about_the_book.md b/docs/chapter_preface/about_the_book.md index c2c17884..8ea5964b 100644 --- a/docs/chapter_preface/about_the_book.md +++ b/docs/chapter_preface/about_the_book.md @@ -1,18 +1,18 @@ # 关于本书 -本项目致力于构建一本开源免费、新手友好的数据结构与算法入门书。 +本项目旨在创建一本开源免费、新手友好的数据结构与算法入门教程。 - 全书采用动画图解,结构化地讲解数据结构与算法知识,内容清晰易懂、学习曲线平滑; - 算法源代码皆可一键运行,支持 Java, C++, Python, Go, JS, TS, C#, Swift, Zig 等语言; -- 鼓励读者在章节讨论区互帮互助、共同进步,提问与评论一般能在两日内得到回复; +- 鼓励读者在章节讨论区互帮互助、共同进步,提问与评论通常可在两日内得到回复; ## 读者对象 -如果您是「算法初学者」,完全没有接触过算法,或者已经有少量刷题,对数据结构与算法有朦胧的理解,在会与不会之间反复横跳,那么这本书就是为你而写! +若您是「算法初学者」,从未接触过算法,或者已经有一些刷题经验,对数据结构与算法有模糊的认识,在会与不会之间反复横跳,那么这本书正是为您量身定制! -如果您是「算法老手」,已经积累一定刷题量,接触过大多数题型,那么本书可以帮助你回顾与梳理算法知识体系,仓库源代码可以被当作“刷题工具库”或“算法字典”来使用。 +如果您是「算法老手」,已经积累一定刷题量,熟悉大部分题型,那么本书可助您回顾与梳理算法知识体系,仓库源代码可以被当作“刷题工具库”或“算法字典”来使用。 -如果您是「算法大佬」,希望可以得到你的宝贵意见建议,或者[一起参与创作](https://www.hello-algo.com/chapter_appendix/contribution/)。 +若您是「算法专家」,我们期待收到您的宝贵建议,或者[一起参与创作](https://www.hello-algo.com/chapter_appendix/contribution/)。 !!! success "前置条件" @@ -20,26 +20,26 @@ ## 内容结构 -本书主要内容有: +本书主要内容包括: - **复杂度分析**:数据结构与算法的评价维度、算法效率的评估方法。时间复杂度、空间复杂度,包括推算方法、常见类型、示例等。 -- **数据结构**:常用的基本数据类型,数据在内存中的存储方式、数据结构分类方法。数组、链表、栈、队列、散列表、树、堆、图等数据结构,内容包括定义、优劣势、常用操作、常见类型、典型应用、实现方法等。 -- **算法**:查找算法、排序算法、搜索与回溯、动态规划、分治算法,内容包括定义、使用场景、优劣势、时空效率、实现方法、示例题目等。 +- **数据结构**:常见基本数据类型,数据在内存中的存储形式、数据结构的分类方法。涉及数组、链表、栈、队列、散列表、树、堆、图等数据结构,内容包括定义、优缺点、常用操作、常见类型、典型应用、实现方法等。 +- **算法**:查找算法、排序算法、搜索与回溯、动态规划、分治算法等,内容涵盖定义、应用场景、优缺点、时空效率、实现方法、示例题目等。 ![Hello 算法内容结构](about_the_book.assets/hello_algo_mindmap.png) ## 致谢 -本书的成书过程中,我获得了许多人的帮助,包括但不限于: +在本书的创作过程中,我得到了许多人的帮助,包括但不限于: -- 感谢我在公司的导师李汐博士,在一次畅谈时您告诉我“觉得应该做就去做”,坚定了我写这本书的决心。 -- 感谢我的女朋友泡泡担任本书的首位读者,从算法小白的视角提出了许多建议,使这本书更加适合初学者来阅读。 -- 感谢腾宝、琦宝、飞宝为本书起了个好听又有梗名字,直接唤起我最初敲下第一行代码 "Hello World!" 的回忆。 -- 感谢苏潼为本书设计了封面和 LOGO ,在我的强迫症下前后多次帮忙修改,谢谢你的耐心。 -- 感谢 @squidfunk 给出的写作排版建议,以及优秀开源项目 [Material-for-MkDocs](https://github.com/squidfunk/mkdocs-material/tree/master) 。 +- 感谢我在公司的导师李汐博士,在深入交谈中您鼓励我“行动起来”,坚定了我写这本书的决心。 +- 感谢我的女朋友泡泡作为本书的首位读者,从算法小白的角度提出许多宝贵建议,使得本书更适合新手阅读。 +- 感谢腾宝、琦宝、飞宝为本书起了一个富有创意的名字,唤起大家写下第一行代码 "Hello World!" 的美好回忆。 +- 感谢苏潼为本书设计了精美的封面和 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-en)、[李沐老师 bilibili 主页](https://space.bilibili.com/1567748478)。 +在写作过程中,我阅读了许多关于数据结构与算法的教材和文章。这些作品为本书提供了优秀的范本,确保了本书内容的准确性与品质。在此感谢所有老师和前辈们的杰出贡献! -在写作过程中,我阅读了许多数据结构与算法的教材与文章,这些著作为本书作出了很好的榜样,保证了本书内容的正确性与质量,感谢各位老师与前辈的精彩创作! +本书倡导“手脑并用”的学习方法,在此方面深受《动手学深度学习》的启发。在此向各位读者强烈推荐这本优秀著作,包括[中文版](https://github.com/d2l-ai/d2l-zh)、[英文版](https://github.com/d2l-ai/d2l-en)、[李沐老师 bilibili 主页](https://space.bilibili.com/1567748478)。 -感谢父母,你们一贯的支持与鼓励给了我自由度来做这些有趣的事。 +衷心感谢我的父母,正是你们一直以来的支持与鼓励,让我有机会做这些富有趣味的事。 diff --git a/docs/chapter_preface/suggestions.md b/docs/chapter_preface/suggestions.md index b7c345d4..0d8e033a 100644 --- a/docs/chapter_preface/suggestions.md +++ b/docs/chapter_preface/suggestions.md @@ -1,26 +1,28 @@ # 如何使用本书 -建议通读本节内容,以获取最佳阅读体验。 +!!! tip + + 为了获得最佳的阅读体验,建议您通读本节内容。 ## 算法学习路线 -总体上看,我认为可将学习数据结构与算法的过程分为三个阶段。 +从总体上看,我们可以将学习数据结构与算法的过程划分为三个阶段: -1. **算法入门**。熟悉各种数据结构的特点、用法,学习各种算法的原理、流程、用途、效率等。 -2. **刷算法题**。可以先从热门题单开刷,推荐[剑指 Offer](https://leetcode.cn/problem-list/xb9nqhhg/)、[LeetCode Hot 100](https://leetcode.cn/problem-list/2cktkvj/),先积累至少 100 道题量,熟悉大多数的算法问题。刚开始刷题时,“遗忘”是最大的困扰点,但这是很正常的,请不要担心。学习中有一种概念叫“周期性回顾”,同一道题隔段时间做一次,在重复 3 轮以上后,往往就能牢记于心了。 -3. **搭建知识体系**。在学习方面,可以阅读算法专栏文章、解题框架、算法教材,不断地丰富知识体系。在刷题方面,可以开始采用进阶刷题方案,例如按专题分类、一题多解、一解多题等,相关刷题心得可以在各个社区中找到。 +1. **算法入门**。我们需要熟悉各种数据结构的特点和用法,学习不同算法的原理、流程、用途和效率等方面内容。 +2. **刷算法题**。建议从热门题目开刷,如[剑指 Offer](https://leetcode.cn/problem-list/xb9nqhhg/)和[LeetCode Hot 100](https://leetcode.cn/problem-list/2cktkvj/),先积累至少 100 道题目,熟悉主流的算法问题。初次刷题时,“知识遗忘”可能是一个挑战,但请放心,这是很正常的。我们可以按照“艾宾浩斯遗忘曲线”来复习题目,通常在进行 3-5 轮的重复后,就能将其牢记在心。 +3. **搭建知识体系**。在学习方面,我们可以阅读算法专栏文章、解题框架和算法教材,以不断丰富知识体系。在刷题方面,可以尝试采用进阶刷题策略,如按专题分类、一题多解、一解多题等,相关的刷题心得可以在各个社区找到。 -作为一本入门教程,**本书内容主要对应“第一阶段”**,致力于帮助你更高效地开展第二、三阶段的学习。 +作为一本入门教程,本书内容主要涵盖“第一阶段”,旨在帮助你更高效地展开第二和第三阶段的学习。 ![算法学习路线](suggestions.assets/learning_route.png) ## 行文风格约定 -标题后标注 `*` 的是选读章节,内容相对较难。如果你的时间有限,建议可以先跳过。 +标题后标注 `*` 的是选读章节,内容相对困难。如果你的时间有限,建议可以先跳过。 -文章中的重要名词会用 `「括号」` 标注,例如 `「数组 Array」` 。建议记住这些名词,包括英文翻译,以便后续阅读文献时使用。 +文章中的重要名词会用 `「 」` 括号标注,例如 `「数组 Array」` 。请务必记住这些名词,包括英文翻译,以便后续阅读文献时使用。 -重点内容、总起句、总结句会被 **加粗** ,此类文字值得特别关注。 +**加粗的文字** 表示重点内容或总结性语句,这类文字值得特别关注。 专有名词和有特指含义的词句会使用 `“双引号”` 标注,以避免歧义。 @@ -156,41 +158,40 @@ ## 在动画图解中高效学习 -视频和图片相比于文字的信息密度和结构化程度更高,更容易理解。在本书中,**知识重难点会主要以动画、图解的形式呈现**,而文字的作用则是作为动画和图的解释与补充。 +相较于文字,视频和图片具有更高的信息密度和结构化程度,因此更易于理解。在本书中,**重点和难点知识将主要通过动画和图解形式展示**,而文字则作为动画和图片的解释与补充。 -阅读本书时,若发现某段内容提供了动画或图解,**建议你以图为主线**,将文字内容(一般在图的上方)对齐到图中内容,综合来理解。 +在阅读本书时,如果发现某段内容提供了动画或图解,**建议以图为主线**,以文字(通常位于图像上方)为辅,综合两者来理解内容。 ![动画图解示例](suggestions.assets/animation.gif) ## 在代码实践中加深理解 -本书的配套代码托管在[GitHub 仓库](https://github.com/krahets/hello-algo),**源代码包含详细注释,配有测试样例,可以直接运行**。 +本书的配套代码托管在[GitHub 仓库](https://github.com/krahets/hello-algo),**源代码包含详细注释,并附有测试样例,可直接运行**。 -- 若学习时间紧张,**建议至少将所有代码通读并运行一遍**。 -- 若时间允许,**强烈建议对照着代码自己敲一遍**。相比于读代码,写代码的过程往往能带来新的收获。 +如果学习时间有限,建议你至少通读并运行所有代码。如果时间充裕,**建议参照代码自行敲一遍**。与仅阅读代码相比,编写代码的过程往往能带来更多收获。 ![运行代码示例](suggestions.assets/running_code.gif) -**第一步:安装本地编程环境**。参照[附录教程](https://www.hello-algo.com/chapter_appendix/installation/),如果已有可直接跳过。 +**第一步:安装本地编程环境**。请参照[附录教程](https://www.hello-algo.com/chapter_appendix/installation/)进行安装,如果已安装则可跳过此步骤。 -**第二步:下载代码仓**。如果已经安装 [Git](https://git-scm.com/downloads) ,可以通过命令行来克隆代码仓。 +**第二步:下载代码仓**。如果已经安装 [Git](https://git-scm.com/downloads) ,可以通过以下命令克隆本仓库。 ```shell git clone https://github.com/krahets/hello-algo.git ``` -当然,你也可以点击“Download ZIP”直接下载代码压缩包,本地解压即可。 +当然,你也可以点击“Download ZIP”直接下载代码压缩包,然后在本地解压即可。 ![克隆仓库与下载代码](suggestions.assets/download_code.png) -**第三步:运行源代码**。若代码块的顶部标有文件名称,则可在仓库 `codes` 文件夹中找到对应的 **源代码文件**。源代码文件可以帮助你省去不必要的调试时间,将精力集中在学习内容上。 +**第三步:运行源代码**。如果代码块顶部标有文件名称,则可以在仓库的 `codes` 文件夹中找到相应的源代码文件。源代码文件将帮助你节省不必要的调试时间,让你能够专注于学习内容。 ![代码块与对应的源代码文件](suggestions.assets/code_md_to_repo.png) ## 在提问讨论中共同成长 -阅读本书时,请不要“惯着”那些弄不明白的知识点。**欢迎在评论区留下你的问题**,小伙伴们和我都会给予解答,您一般 2 日内会得到回复。 +阅读本书时,请不要“惯着”那些没学明白的知识点。**欢迎在评论区提出你的问题**,我和其他小伙伴们将竭诚为你解答,一般情况下可在两天内得到回复。 -同时,也希望你可以多花时间逛逛评论区。一方面,可以看看大家遇到了什么问题,反过来查漏补缺,这往往可以引起更加深度的思考。另一方面,也希望你可以慷慨地解答小伙伴们的问题、分享自己的见解,大家互相学习与进步! +同时,也希望您能在评论区多花些时间。一方面,您可以了解大家遇到的问题,从而查漏补缺,这将有助于激发更深入的思考。另一方面,希望您能慷慨地回答其他小伙伴的问题、分享您的见解,让大家共同学习和进步。 ![评论区示例](suggestions.assets/comment.gif) diff --git a/docs/chapter_preface/summary.md b/docs/chapter_preface/summary.md index 55a5abbe..cd7f9b86 100644 --- a/docs/chapter_preface/summary.md +++ b/docs/chapter_preface/summary.md @@ -1,8 +1,8 @@ # 小结 -- 本书主要面向算法初学者。对于已经有一定积累的同学,这本书可以帮助你系统回顾算法知识,源代码可被当作“刷题工具库”来使用。 -- 书中内容主要分为复杂度分析、数据结构、算法三部分,覆盖了该领域的大部分主题。 -- 对于算法小白,在初学阶段阅读一本入门书是非常有必要的,可以少走许多弯路。 -- 书内的动画和图解往往介绍的是重点和难点知识,在阅读时应该多加关注。 -- 实践是学习编程的最佳方式,强烈推荐运行源代码,动手敲代码。 -- 本书提供了讨论区,遇到疑惑可以随时提问。 +- 本书的主要受众是算法初学者。对于已具备一定积累的同学,本书能帮助系统回顾算法知识,同时源代码可作为“刷题工具库”使用。 +- 书中内容主要包括复杂度分析、数据结构、算法三部分,涵盖了该领域的绝大部分主题。 +- 对于算法新手,在初学阶段阅读一本入门书籍至关重要,有助于避免走弯路。 +- 书内的动画和图解通常用于介绍重点和难点知识,阅读时应给予更多关注。 +- 实践乃学习编程之最佳途径,强烈建议运行源代码并亲自敲打代码。 +- 本书设有讨论区,欢迎随时分享你的疑惑。 diff --git a/docs/chapter_reference/index.md b/docs/chapter_reference/index.md index ef838d0b..208d1f92 100644 --- a/docs/chapter_reference/index.md +++ b/docs/chapter_reference/index.md @@ -4,11 +4,11 @@ [2] Aditya Bhargava. Grokking Algorithms: An Illustrated Guide for Programmers and Other Curious People (1st Edition). -[3] 严蔚敏. 数据结构( C 语言版). +[3] 严蔚敏. 数据结构(C 语言版). -[4] 邓俊辉. 数据结构( C++ 语言版,第三版). +[4] 邓俊辉. 数据结构(C++ 语言版,第三版). -[5] 马克·艾伦·维斯著,陈越译. 数据结构与算法分析:Java语言描述(第三版). +[5] 马克 艾伦 维斯著,陈越译. 数据结构与算法分析:Java语言描述(第三版). [6] 程杰. 大话数据结构. diff --git a/docs/chapter_sorting/summary.md b/docs/chapter_sorting/summary.md index 87ce03e4..6f841641 100644 --- a/docs/chapter_sorting/summary.md +++ b/docs/chapter_sorting/summary.md @@ -10,4 +10,4 @@ ![排序算法对比](summary.assets/sorting_algorithms_comparison.png) -- 总体来看,我们追求运行快、稳定、原地、正向自适应性的排序。显然,如同其它数据结构与算法一样,同时满足这些条件的排序算法并不存在,我们需要根据问题特点来选择排序算法。 +- 总体来看,我们追求运行快、稳定、原地、正向自适应性的排序。显然,如同其他数据结构与算法一样,同时满足这些条件的排序算法并不存在,我们需要根据问题特点来选择排序算法。