diff --git a/codes/python/chapter_divide_and_conquer/build_tree.py b/codes/python/chapter_divide_and_conquer/build_tree.py index 85266162c..b8b2db293 100644 --- a/codes/python/chapter_divide_and_conquer/build_tree.py +++ b/codes/python/chapter_divide_and_conquer/build_tree.py @@ -18,7 +18,7 @@ def dfs( l: int, r: int, ) -> TreeNode | None: - """构建二叉树 DFS""" + """构建二叉树:分治""" # 子树区间为空时终止 if r - l < 0: return None @@ -26,9 +26,9 @@ def dfs( root = TreeNode(preorder[i]) # 查询 m ,从而划分左右子树 m = hmap[preorder[i]] - # 递归构建左子树 + # 子问题:构建左子树 root.left = dfs(preorder, inorder, hmap, i + 1, l, m - 1) - # 递归构建右子树 + # 子问题:构建右子树 root.right = dfs(preorder, inorder, hmap, i + 1 + m - l, m + 1, r) # 返回根节点 return root diff --git a/docs/chapter_data_structure/summary.md b/docs/chapter_data_structure/summary.md index a01f6332b..74cdc549e 100644 --- a/docs/chapter_data_structure/summary.md +++ b/docs/chapter_data_structure/summary.md @@ -22,6 +22,6 @@ 哈希表底层是数组,而为了解决哈希冲突,我们可能会使用“拉链法”(后续散列表章节会讲)。在拉链法中,数组中每个地址(桶)指向一个链表;当这个链表长度超过一定阈值时,又可能被转化为树(通常为红黑树)。因此,哈希表可能同时包含线性(数组、链表)和非线性(树)数据结构。 -!!! question "char 类型的长度是 1 bytes 吗?" +!!! question "char 类型的长度是 1 byte 吗?" - 这个与编程语言采用的编码方法有关。例如,Java, JS, TS, C# 都采用 UTF-16 编码(保存 Unicode 码点),因此 char 类型的长度为 2 bytes 。 + char 类型的长度由编程语言采用的编码方法决定。例如,Java, JS, TS, C# 都采用 UTF-16 编码(保存 Unicode 码点),因此 char 类型的长度为 2 bytes 。 diff --git a/docs/chapter_divide_and_conquer/build_binary_tree.assets/build_tree_division_pointers.png b/docs/chapter_divide_and_conquer/build_binary_tree_problem.assets/build_tree_division_pointers.png similarity index 100% rename from docs/chapter_divide_and_conquer/build_binary_tree.assets/build_tree_division_pointers.png rename to docs/chapter_divide_and_conquer/build_binary_tree_problem.assets/build_tree_division_pointers.png diff --git a/docs/chapter_divide_and_conquer/build_binary_tree.assets/build_tree_example.png b/docs/chapter_divide_and_conquer/build_binary_tree_problem.assets/build_tree_example.png similarity index 100% rename from docs/chapter_divide_and_conquer/build_binary_tree.assets/build_tree_example.png rename to docs/chapter_divide_and_conquer/build_binary_tree_problem.assets/build_tree_example.png diff --git a/docs/chapter_divide_and_conquer/build_binary_tree.assets/build_tree_preorder_inorder_division.png b/docs/chapter_divide_and_conquer/build_binary_tree_problem.assets/build_tree_preorder_inorder_division.png similarity index 100% rename from docs/chapter_divide_and_conquer/build_binary_tree.assets/build_tree_preorder_inorder_division.png rename to docs/chapter_divide_and_conquer/build_binary_tree_problem.assets/build_tree_preorder_inorder_division.png diff --git a/docs/chapter_divide_and_conquer/build_binary_tree.assets/built_tree_step1.png b/docs/chapter_divide_and_conquer/build_binary_tree_problem.assets/built_tree_step1.png similarity index 100% rename from docs/chapter_divide_and_conquer/build_binary_tree.assets/built_tree_step1.png rename to docs/chapter_divide_and_conquer/build_binary_tree_problem.assets/built_tree_step1.png diff --git a/docs/chapter_divide_and_conquer/build_binary_tree.assets/built_tree_step10.png b/docs/chapter_divide_and_conquer/build_binary_tree_problem.assets/built_tree_step10.png similarity index 100% rename from docs/chapter_divide_and_conquer/build_binary_tree.assets/built_tree_step10.png rename to docs/chapter_divide_and_conquer/build_binary_tree_problem.assets/built_tree_step10.png diff --git a/docs/chapter_divide_and_conquer/build_binary_tree.assets/built_tree_step2.png b/docs/chapter_divide_and_conquer/build_binary_tree_problem.assets/built_tree_step2.png similarity index 100% rename from docs/chapter_divide_and_conquer/build_binary_tree.assets/built_tree_step2.png rename to docs/chapter_divide_and_conquer/build_binary_tree_problem.assets/built_tree_step2.png diff --git a/docs/chapter_divide_and_conquer/build_binary_tree.assets/built_tree_step3.png b/docs/chapter_divide_and_conquer/build_binary_tree_problem.assets/built_tree_step3.png similarity index 100% rename from docs/chapter_divide_and_conquer/build_binary_tree.assets/built_tree_step3.png rename to docs/chapter_divide_and_conquer/build_binary_tree_problem.assets/built_tree_step3.png diff --git a/docs/chapter_divide_and_conquer/build_binary_tree.assets/built_tree_step4.png b/docs/chapter_divide_and_conquer/build_binary_tree_problem.assets/built_tree_step4.png similarity index 100% rename from docs/chapter_divide_and_conquer/build_binary_tree.assets/built_tree_step4.png rename to docs/chapter_divide_and_conquer/build_binary_tree_problem.assets/built_tree_step4.png diff --git a/docs/chapter_divide_and_conquer/build_binary_tree.assets/built_tree_step5.png b/docs/chapter_divide_and_conquer/build_binary_tree_problem.assets/built_tree_step5.png similarity index 100% rename from docs/chapter_divide_and_conquer/build_binary_tree.assets/built_tree_step5.png rename to docs/chapter_divide_and_conquer/build_binary_tree_problem.assets/built_tree_step5.png diff --git a/docs/chapter_divide_and_conquer/build_binary_tree.assets/built_tree_step6.png b/docs/chapter_divide_and_conquer/build_binary_tree_problem.assets/built_tree_step6.png similarity index 100% rename from docs/chapter_divide_and_conquer/build_binary_tree.assets/built_tree_step6.png rename to docs/chapter_divide_and_conquer/build_binary_tree_problem.assets/built_tree_step6.png diff --git a/docs/chapter_divide_and_conquer/build_binary_tree.assets/built_tree_step7.png b/docs/chapter_divide_and_conquer/build_binary_tree_problem.assets/built_tree_step7.png similarity index 100% rename from docs/chapter_divide_and_conquer/build_binary_tree.assets/built_tree_step7.png rename to docs/chapter_divide_and_conquer/build_binary_tree_problem.assets/built_tree_step7.png diff --git a/docs/chapter_divide_and_conquer/build_binary_tree.assets/built_tree_step8.png b/docs/chapter_divide_and_conquer/build_binary_tree_problem.assets/built_tree_step8.png similarity index 100% rename from docs/chapter_divide_and_conquer/build_binary_tree.assets/built_tree_step8.png rename to docs/chapter_divide_and_conquer/build_binary_tree_problem.assets/built_tree_step8.png diff --git a/docs/chapter_divide_and_conquer/build_binary_tree.assets/built_tree_step9.png b/docs/chapter_divide_and_conquer/build_binary_tree_problem.assets/built_tree_step9.png similarity index 100% rename from docs/chapter_divide_and_conquer/build_binary_tree.assets/built_tree_step9.png rename to docs/chapter_divide_and_conquer/build_binary_tree_problem.assets/built_tree_step9.png diff --git a/docs/chapter_divide_and_conquer/build_binary_tree.md b/docs/chapter_divide_and_conquer/build_binary_tree_problem.md similarity index 83% rename from docs/chapter_divide_and_conquer/build_binary_tree.md rename to docs/chapter_divide_and_conquer/build_binary_tree_problem.md index 63f46bb8e..872ffeeab 100644 --- a/docs/chapter_divide_and_conquer/build_binary_tree.md +++ b/docs/chapter_divide_and_conquer/build_binary_tree_problem.md @@ -4,7 +4,7 @@ 给定一个二叉树的前序遍历 `preorder` 和中序遍历 `inorder` ,请从中构建二叉树,返回二叉树的根节点。 -![构建二叉树的示例数据](build_binary_tree.assets/build_tree_example.png) +![构建二叉树的示例数据](build_binary_tree_problem.assets/build_tree_example.png) 原问题定义为从 `preorder` 和 `inorder` 构建二叉树。我们首先从分治的角度分析这道题: @@ -25,7 +25,7 @@ 2. 查找根节点在 `inorder` 中的索引,基于该索引可将 `inorder` 划分为 `[ 9 | 3 | 1 2 7 ]` ; 3. 根据 `inorder` 划分结果,可得左子树和右子树分别有 1 个和 3 个节点,从而可将 `preorder` 划分为 `[ 3 | 9 | 2 1 7 ]` ; -![在前序和中序遍历中划分子树](build_binary_tree.assets/build_tree_preorder_inorder_division.png) +![在前序和中序遍历中划分子树](build_binary_tree_problem.assets/build_tree_preorder_inorder_division.png) 至此,**我们已经推导出根节点、左子树、右子树在 `preorder` 和 `inorder` 中的索引区间**。而为了描述这些索引区间,我们需要借助几个指针变量: @@ -47,7 +47,7 @@ 请注意,右子树根节点索引中的 $(m-l)$ 的含义是“左子树的节点数量”,建议配合下图理解。 -![根节点和左右子树的索引区间表示](build_binary_tree.assets/build_tree_division_pointers.png) +![根节点和左右子树的索引区间表示](build_binary_tree_problem.assets/build_tree_division_pointers.png) 接下来就可以实现代码了。为了提升查询 $m$ 的效率,我们借助一个哈希表 `hmap` 来存储 `inorder` 列表元素到索引的映射。 @@ -142,34 +142,34 @@ 下图展示了构建二叉树的递归过程,各个节点是在向下“递”的过程中建立的,而各条边是在向上“归”的过程中建立的。 === "<1>" - ![built_tree_step1](build_binary_tree.assets/built_tree_step1.png) + ![构建二叉树的递归过程](build_binary_tree_problem.assets/built_tree_step1.png) === "<2>" - ![built_tree_step2](build_binary_tree.assets/built_tree_step2.png) + ![built_tree_step2](build_binary_tree_problem.assets/built_tree_step2.png) === "<3>" - ![built_tree_step3](build_binary_tree.assets/built_tree_step3.png) + ![built_tree_step3](build_binary_tree_problem.assets/built_tree_step3.png) === "<4>" - ![built_tree_step4](build_binary_tree.assets/built_tree_step4.png) + ![built_tree_step4](build_binary_tree_problem.assets/built_tree_step4.png) === "<5>" - ![built_tree_step5](build_binary_tree.assets/built_tree_step5.png) + ![built_tree_step5](build_binary_tree_problem.assets/built_tree_step5.png) === "<6>" - ![built_tree_step6](build_binary_tree.assets/built_tree_step6.png) + ![built_tree_step6](build_binary_tree_problem.assets/built_tree_step6.png) === "<7>" - ![built_tree_step7](build_binary_tree.assets/built_tree_step7.png) + ![built_tree_step7](build_binary_tree_problem.assets/built_tree_step7.png) === "<8>" - ![built_tree_step8](build_binary_tree.assets/built_tree_step8.png) + ![built_tree_step8](build_binary_tree_problem.assets/built_tree_step8.png) === "<9>" - ![built_tree_step9](build_binary_tree.assets/built_tree_step9.png) + ![built_tree_step9](build_binary_tree_problem.assets/built_tree_step9.png) === "<10>" - ![built_tree_step10](build_binary_tree.assets/built_tree_step10.png) + ![built_tree_step10](build_binary_tree_problem.assets/built_tree_step10.png) 设树的节点数量为 $n$ ,初始化每一个节点(执行一个递归函数 `dfs()` )使用 $O(1)$ 时间。**因此总体时间复杂度为 $O(n)$** 。 diff --git a/docs/chapter_dynamic_programming/intro_to_dynamic_programming.md b/docs/chapter_dynamic_programming/intro_to_dynamic_programming.md index 68ecaf738..5fbf49d26 100644 --- a/docs/chapter_dynamic_programming/intro_to_dynamic_programming.md +++ b/docs/chapter_dynamic_programming/intro_to_dynamic_programming.md @@ -476,5 +476,5 @@ $$ 总的看来,**子问题分解是一种通用的算法思路,在分治、动态规划、回溯中各有特点**: - 分治算法将原问题划分为几个独立的子问题,然后递归解决子问题,最后合并子问题的解得到原问题的解。例如,归并排序将长数组不断划分为两个短子数组,再将排序好的子数组合并为排序好的长数组。 -- 动态规划也是将原问题分解为多个子问题,但与分治算法的主要区别是,**动态规划中的子问题往往不是相互独立的**,原问题的解依赖于子问题的解,而子问题的解又依赖于更小的子问题的解。因此,动态规划通常会引入记忆化,保存已经解决的子问题的解,避免重复计算。 -- 回溯算法在尝试和回退中穷举所有可能的解,并通过剪枝避免不必要的搜索分支。原问题的解由一系列决策步骤构成,我们可以将每个决策步骤之后的剩余问题看作为一个子问题。 +- 动态规划也是将原问题分解为多个子问题,但与分治算法的主要区别是,**动态规划中的子问题往往不是相互独立的**,原问题的解依赖于子问题的解,而子问题的解又依赖于更小的子问题的解。 +- 回溯算法在尝试和回退中穷举所有可能的解,并通过剪枝避免不必要的搜索分支。原问题的解由一系列决策步骤构成,我们可以将每个决策步骤之前的子序列看作为一个子问题。 diff --git a/docs/chapter_sorting/bucket_sort.md b/docs/chapter_sorting/bucket_sort.md index bcb40b7c8..22ece0854 100644 --- a/docs/chapter_sorting/bucket_sort.md +++ b/docs/chapter_sorting/bucket_sort.md @@ -86,7 +86,7 @@ ## 算法特性 -- **时间复杂度 $O(n + k)$** :假设元素在各个桶内平均分布,那么每个桶内的元素数量为 $\frac{n}{k}$ 。假设排序单个桶使用 $O(\frac{n}{k} \log\frac{n}{k})$ 时间,则排序所有桶使用 $O(n \log\frac{n}{k})$ 时间。**当桶数量 $k$ 比较大时,时间复杂度则趋向于 $O(n)$** 。合并结果时需要遍历 $n$ 个桶,花费 $O(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$ 个元素的额外空间。 - 桶排序是否稳定取决于排序桶内元素的算法是否稳定。