From 01056442324720fe48e6ccaf63d15beb160a031b Mon Sep 17 00:00:00 2001 From: krahets Date: Thu, 9 Nov 2023 05:13:48 +0800 Subject: [PATCH] build --- docs/chapter_appendix/contribution.md | 2 +- docs/chapter_appendix/index.md | 2 +- docs/chapter_array_and_linkedlist/array.md | 8 ++-- docs/chapter_array_and_linkedlist/index.md | 6 +-- .../linked_list.md | 8 ++-- .../backtracking_algorithm.md | 28 +++++------ docs/chapter_backtracking/index.md | 2 +- docs/chapter_backtracking/n_queens_problem.md | 8 ++-- .../permutations_problem.md | 10 ++-- .../subset_sum_problem.md | 10 ++-- .../chapter_computational_complexity/index.md | 2 +- .../iteration_and_recursion.md | 12 ++--- .../space_complexity.md | 10 ++-- .../time_complexity.md | 16 +++---- .../character_encoding.md | 6 +-- .../classification_of_data_structure.md | 6 +-- docs/chapter_data_structure/index.md | 2 +- .../chapter_data_structure/number_encoding.md | 4 +- .../binary_search_recur.md | 2 +- .../build_binary_tree_problem.md | 26 +++++----- .../divide_and_conquer.md | 6 +-- .../hanota_problem.md | 26 +++++----- docs/chapter_divide_and_conquer/index.md | 2 +- .../dp_problem_features.md | 8 ++-- .../dp_solution_pipeline.md | 36 +++++++------- .../edit_distance_problem.md | 36 +++++++------- docs/chapter_dynamic_programming/index.md | 2 +- .../intro_to_dynamic_programming.md | 10 ++-- .../knapsack_problem.md | 46 +++++++++--------- .../unbounded_knapsack_problem.md | 48 +++++++++---------- docs/chapter_graph/graph.md | 12 ++--- docs/chapter_graph/graph_operations.md | 20 ++++---- docs/chapter_graph/graph_traversal.md | 48 +++++++++---------- docs/chapter_graph/index.md | 2 +- .../fractional_knapsack_problem.md | 8 ++-- docs/chapter_greedy/greedy_algorithm.md | 4 +- docs/chapter_greedy/index.md | 2 +- docs/chapter_greedy/max_capacity_problem.md | 28 +++++------ .../max_product_cutting_problem.md | 8 ++-- docs/chapter_hashing/hash_algorithm.md | 2 +- docs/chapter_hashing/hash_collision.md | 6 +-- docs/chapter_hashing/hash_map.md | 8 ++-- docs/chapter_hashing/index.md | 2 +- docs/chapter_heap/build_heap.md | 2 +- docs/chapter_heap/heap.md | 42 ++++++++-------- docs/chapter_heap/index.md | 2 +- docs/chapter_heap/top_k.md | 22 ++++----- .../algorithms_are_everywhere.md | 14 +++--- docs/chapter_introduction/index.md | 2 +- docs/chapter_introduction/what_is_dsa.md | 4 +- docs/chapter_preface/about_the_book.md | 2 +- docs/chapter_preface/index.md | 2 +- docs/chapter_preface/suggestions.md | 12 ++--- docs/chapter_searching/binary_search.md | 18 +++---- docs/chapter_searching/binary_search_edge.md | 4 +- .../binary_search_insertion.md | 20 ++++---- docs/chapter_searching/index.md | 2 +- .../replace_linear_by_hashing.md | 8 ++-- .../searching_algorithm_revisited.md | 2 +- docs/chapter_sorting/bubble_sort.md | 16 +++---- docs/chapter_sorting/bucket_sort.md | 6 +-- docs/chapter_sorting/counting_sort.md | 18 +++---- docs/chapter_sorting/heap_sort.md | 24 +++++----- docs/chapter_sorting/index.md | 2 +- docs/chapter_sorting/insertion_sort.md | 4 +- docs/chapter_sorting/merge_sort.md | 22 ++++----- docs/chapter_sorting/quick_sort.md | 20 ++++---- docs/chapter_sorting/radix_sort.md | 2 +- docs/chapter_sorting/selection_sort.md | 24 +++++----- docs/chapter_sorting/sorting_algorithm.md | 2 +- docs/chapter_sorting/summary.md | 2 +- docs/chapter_stack_and_queue/deque.md | 22 ++++----- docs/chapter_stack_and_queue/index.md | 2 +- docs/chapter_stack_and_queue/queue.md | 14 +++--- docs/chapter_stack_and_queue/stack.md | 14 +++--- .../array_representation_of_tree.md | 8 ++-- docs/chapter_tree/avl_tree.md | 24 +++++----- docs/chapter_tree/binary_search_tree.md | 28 +++++------ docs/chapter_tree/binary_tree.md | 16 +++---- docs/chapter_tree/binary_tree_traversal.md | 26 +++++----- docs/chapter_tree/index.md | 2 +- docs/index.md | 4 +- overrides/stylesheets/extra.css | 25 +++++++--- 83 files changed, 516 insertions(+), 509 deletions(-) diff --git a/docs/chapter_appendix/contribution.md b/docs/chapter_appendix/contribution.md index c4711e36e..206dcdaa9 100644 --- a/docs/chapter_appendix/contribution.md +++ b/docs/chapter_appendix/contribution.md @@ -22,7 +22,7 @@ comments: true 2. 修改 Markdown 源文件内容,检查内容的正确性,并尽量保持排版格式的统一。 3. 在页面底部填写修改说明,然后点击“Propose file change”按钮。页面跳转后,点击“Create pull request”按钮即可发起拉取请求。 -![页面编辑按键](contribution.assets/edit_markdown.png) +![页面编辑按键](contribution.assets/edit_markdown.png){ class="animation-figure" }

图 16-1   页面编辑按键

diff --git a/docs/chapter_appendix/index.md b/docs/chapter_appendix/index.md index 73a6094d4..f9dc31458 100644 --- a/docs/chapter_appendix/index.md +++ b/docs/chapter_appendix/index.md @@ -7,7 +7,7 @@ icon: material/help-circle-outline
-![附录](../assets/covers/chapter_appendix.jpg){ width="600" } +![附录](../assets/covers/chapter_appendix.jpg){ class="cover-image" }
diff --git a/docs/chapter_array_and_linkedlist/array.md b/docs/chapter_array_and_linkedlist/array.md index 39c9fbd9a..efc608ed4 100755 --- a/docs/chapter_array_and_linkedlist/array.md +++ b/docs/chapter_array_and_linkedlist/array.md @@ -6,7 +6,7 @@ comments: true 「数组 array」是一种线性数据结构,其将相同类型元素存储在连续的内存空间中。我们将元素在数组中的位置称为该元素的「索引 index」。图 4-1 展示了数组的主要术语和概念。 -![数组定义与存储方式](array.assets/array_definition.png) +![数组定义与存储方式](array.assets/array_definition.png){ class="animation-figure" }

图 4-1   数组定义与存储方式

@@ -123,7 +123,7 @@ comments: true 数组元素被存储在连续的内存空间中,这意味着计算数组元素的内存地址非常容易。给定数组内存地址(即首元素内存地址)和某个元素的索引,我们可以使用图 4-2 所示的公式计算得到该元素的内存地址,从而直接访问此元素。 -![数组元素的内存地址计算](array.assets/array_memory_location_calculation.png) +![数组元素的内存地址计算](array.assets/array_memory_location_calculation.png){ class="animation-figure" }

图 4-2   数组元素的内存地址计算

@@ -291,7 +291,7 @@ comments: true 数组元素在内存中是“紧挨着的”,它们之间没有空间再存放任何数据。如图 4-3 所示,如果想要在数组中间插入一个元素,则需要将该元素之后的所有元素都向后移动一位,之后再把元素赋值给该索引。 -![数组插入元素示例](array.assets/array_insert_element.png) +![数组插入元素示例](array.assets/array_insert_element.png){ class="animation-figure" }

图 4-3   数组插入元素示例

@@ -468,7 +468,7 @@ comments: true 同理,如图 4-4 所示,若想要删除索引 $i$ 处的元素,则需要把索引 $i$ 之后的元素都向前移动一位。 -![数组删除元素示例](array.assets/array_remove_element.png) +![数组删除元素示例](array.assets/array_remove_element.png){ class="animation-figure" }

图 4-4   数组删除元素示例

diff --git a/docs/chapter_array_and_linkedlist/index.md b/docs/chapter_array_and_linkedlist/index.md index 49f3fb846..a87fd2af0 100644 --- a/docs/chapter_array_and_linkedlist/index.md +++ b/docs/chapter_array_and_linkedlist/index.md @@ -5,11 +5,7 @@ icon: material/view-list-outline # 第 4 章   数组与链表 -
- -![数组与链表](../assets/covers/chapter_array_and_linkedlist.jpg){ width="600" } - -
+![数组与链表](../assets/covers/chapter_array_and_linkedlist.jpg){ class="cover-image" } !!! abstract diff --git a/docs/chapter_array_and_linkedlist/linked_list.md b/docs/chapter_array_and_linkedlist/linked_list.md index 079b0dede..cb8a4dd2d 100755 --- a/docs/chapter_array_and_linkedlist/linked_list.md +++ b/docs/chapter_array_and_linkedlist/linked_list.md @@ -10,7 +10,7 @@ comments: true 链表的设计使得各个节点可以被分散存储在内存各处,它们的内存地址是无须连续的。 -![链表定义与存储方式](linked_list.assets/linkedlist_definition.png) +![链表定义与存储方式](linked_list.assets/linkedlist_definition.png){ class="animation-figure" }

图 4-5   链表定义与存储方式

@@ -405,7 +405,7 @@ comments: true 相比之下,在数组中插入元素的时间复杂度为 $O(n)$ ,在大数据量下的效率较低。 -![链表插入节点示例](linked_list.assets/linkedlist_insert_node.png) +![链表插入节点示例](linked_list.assets/linkedlist_insert_node.png){ class="animation-figure" }

图 4-6   链表插入节点示例

@@ -547,7 +547,7 @@ comments: true 请注意,尽管在删除操作完成后节点 `P` 仍然指向 `n1` ,但实际上遍历此链表已经无法访问到 `P` ,这意味着 `P` 已经不再属于该链表了。 -![链表删除节点](linked_list.assets/linkedlist_remove_node.png) +![链表删除节点](linked_list.assets/linkedlist_remove_node.png){ class="animation-figure" }

图 4-7   链表删除节点

@@ -1316,7 +1316,7 @@ comments: true } ``` -![常见链表种类](linked_list.assets/linkedlist_common_types.png) +![常见链表种类](linked_list.assets/linkedlist_common_types.png){ class="animation-figure" }

图 4-8   常见链表种类

diff --git a/docs/chapter_backtracking/backtracking_algorithm.md b/docs/chapter_backtracking/backtracking_algorithm.md index 51dac4f4a..2c451563e 100644 --- a/docs/chapter_backtracking/backtracking_algorithm.md +++ b/docs/chapter_backtracking/backtracking_algorithm.md @@ -206,7 +206,7 @@ comments: true [class]{}-[func]{preOrder} ``` -![在前序遍历中搜索节点](backtracking_algorithm.assets/preorder_find_nodes.png) +![在前序遍历中搜索节点](backtracking_algorithm.assets/preorder_find_nodes.png){ class="animation-figure" }

图 13-1   在前序遍历中搜索节点

@@ -477,37 +477,37 @@ comments: true 观察图 13-2 所示的过程,**我们可以将尝试和回退理解为“前进”与“撤销”**,两个操作是互为逆向的。 === "<1>" - ![尝试与回退](backtracking_algorithm.assets/preorder_find_paths_step1.png) + ![尝试与回退](backtracking_algorithm.assets/preorder_find_paths_step1.png){ class="animation-figure" } === "<2>" - ![preorder_find_paths_step2](backtracking_algorithm.assets/preorder_find_paths_step2.png) + ![preorder_find_paths_step2](backtracking_algorithm.assets/preorder_find_paths_step2.png){ class="animation-figure" } === "<3>" - ![preorder_find_paths_step3](backtracking_algorithm.assets/preorder_find_paths_step3.png) + ![preorder_find_paths_step3](backtracking_algorithm.assets/preorder_find_paths_step3.png){ class="animation-figure" } === "<4>" - ![preorder_find_paths_step4](backtracking_algorithm.assets/preorder_find_paths_step4.png) + ![preorder_find_paths_step4](backtracking_algorithm.assets/preorder_find_paths_step4.png){ class="animation-figure" } === "<5>" - ![preorder_find_paths_step5](backtracking_algorithm.assets/preorder_find_paths_step5.png) + ![preorder_find_paths_step5](backtracking_algorithm.assets/preorder_find_paths_step5.png){ class="animation-figure" } === "<6>" - ![preorder_find_paths_step6](backtracking_algorithm.assets/preorder_find_paths_step6.png) + ![preorder_find_paths_step6](backtracking_algorithm.assets/preorder_find_paths_step6.png){ class="animation-figure" } === "<7>" - ![preorder_find_paths_step7](backtracking_algorithm.assets/preorder_find_paths_step7.png) + ![preorder_find_paths_step7](backtracking_algorithm.assets/preorder_find_paths_step7.png){ class="animation-figure" } === "<8>" - ![preorder_find_paths_step8](backtracking_algorithm.assets/preorder_find_paths_step8.png) + ![preorder_find_paths_step8](backtracking_algorithm.assets/preorder_find_paths_step8.png){ class="animation-figure" } === "<9>" - ![preorder_find_paths_step9](backtracking_algorithm.assets/preorder_find_paths_step9.png) + ![preorder_find_paths_step9](backtracking_algorithm.assets/preorder_find_paths_step9.png){ class="animation-figure" } === "<10>" - ![preorder_find_paths_step10](backtracking_algorithm.assets/preorder_find_paths_step10.png) + ![preorder_find_paths_step10](backtracking_algorithm.assets/preorder_find_paths_step10.png){ class="animation-figure" } === "<11>" - ![preorder_find_paths_step11](backtracking_algorithm.assets/preorder_find_paths_step11.png) + ![preorder_find_paths_step11](backtracking_algorithm.assets/preorder_find_paths_step11.png){ class="animation-figure" }

图 13-2   尝试与回退

@@ -781,7 +781,7 @@ comments: true 剪枝是一个非常形象的名词。如图 13-3 所示,在搜索过程中,**我们“剪掉”了不满足约束条件的搜索分支**,避免许多无意义的尝试,从而提高了搜索效率。 -![根据约束条件剪枝](backtracking_algorithm.assets/preorder_find_constrained_paths.png) +![根据约束条件剪枝](backtracking_algorithm.assets/preorder_find_constrained_paths.png){ class="animation-figure" }

图 13-3   根据约束条件剪枝

@@ -1657,7 +1657,7 @@ comments: true 根据题意,我们在找到值为 $7$ 的节点后应该继续搜索,**因此需要将记录解之后的 `return` 语句删除**。图 13-4 对比了保留或删除 `return` 语句的搜索过程。 -![保留与删除 return 的搜索过程对比](backtracking_algorithm.assets/backtrack_remove_return_or_not.png) +![保留与删除 return 的搜索过程对比](backtracking_algorithm.assets/backtrack_remove_return_or_not.png){ class="animation-figure" }

图 13-4   保留与删除 return 的搜索过程对比

diff --git a/docs/chapter_backtracking/index.md b/docs/chapter_backtracking/index.md index 5dabb0892..adc6878e9 100644 --- a/docs/chapter_backtracking/index.md +++ b/docs/chapter_backtracking/index.md @@ -7,7 +7,7 @@ icon: material/map-marker-path
-![回溯](../assets/covers/chapter_backtracking.jpg){ width="600" } +![回溯](../assets/covers/chapter_backtracking.jpg){ class="cover-image" }
diff --git a/docs/chapter_backtracking/n_queens_problem.md b/docs/chapter_backtracking/n_queens_problem.md index 475afc8b5..b7684fc83 100644 --- a/docs/chapter_backtracking/n_queens_problem.md +++ b/docs/chapter_backtracking/n_queens_problem.md @@ -10,13 +10,13 @@ comments: true 如图 13-15 所示,当 $n = 4$ 时,共可以找到两个解。从回溯算法的角度看,$n \times n$ 大小的棋盘共有 $n^2$ 个格子,给出了所有的选择 `choices` 。在逐个放置皇后的过程中,棋盘状态在不断地变化,每个时刻的棋盘就是状态 `state` 。 -![4 皇后问题的解](n_queens_problem.assets/solution_4_queens.png) +![4 皇后问题的解](n_queens_problem.assets/solution_4_queens.png){ class="animation-figure" }

图 13-15   4 皇后问题的解

图 13-16 展示了本题的三个约束条件:**多个皇后不能在同一行、同一列、同一对角线**。值得注意的是,对角线分为主对角线 `\` 和次对角线 `/` 两种。 -![n 皇后问题的约束条件](n_queens_problem.assets/n_queens_constraints.png) +![n 皇后问题的约束条件](n_queens_problem.assets/n_queens_constraints.png){ class="animation-figure" }

图 13-16   n 皇后问题的约束条件

@@ -28,7 +28,7 @@ comments: true 如图 13-17 所示,为 $4$ 皇后问题的逐行放置过程。受画幅限制,图 13-17 仅展开了第一行的其中一个搜索分支,并且将不满足列约束和对角线约束的方案都进行了剪枝。 -![逐行放置策略](n_queens_problem.assets/n_queens_placing.png) +![逐行放置策略](n_queens_problem.assets/n_queens_placing.png){ class="animation-figure" }

图 13-17   逐行放置策略

@@ -44,7 +44,7 @@ comments: true 同理,**次对角线上的所有格子的 $row + col$ 是恒定值**。我们同样也可以借助数组 `diags2` 来处理次对角线约束。 -![处理列约束和对角线约束](n_queens_problem.assets/n_queens_cols_diagonals.png) +![处理列约束和对角线约束](n_queens_problem.assets/n_queens_cols_diagonals.png){ class="animation-figure" }

图 13-18   处理列约束和对角线约束

diff --git a/docs/chapter_backtracking/permutations_problem.md b/docs/chapter_backtracking/permutations_problem.md index 368c3f49f..e9d79b5be 100644 --- a/docs/chapter_backtracking/permutations_problem.md +++ b/docs/chapter_backtracking/permutations_problem.md @@ -32,7 +32,7 @@ comments: true 如图 13-5 所示,我们可以将搜索过程展开成一个递归树,树中的每个节点代表当前状态 `state` 。从根节点开始,经过三轮选择后到达叶节点,每个叶节点都对应一个排列。 -![全排列的递归树](permutations_problem.assets/permutations_i.png) +![全排列的递归树](permutations_problem.assets/permutations_i.png){ class="animation-figure" }

图 13-5   全排列的递归树

@@ -45,7 +45,7 @@ comments: true 如图 13-6 所示,假设我们第一轮选择 1 ,第二轮选择 3 ,第三轮选择 2 ,则需要在第二轮剪掉元素 1 的分支,在第三轮剪掉元素 1 和元素 3 的分支。 -![全排列剪枝示例](permutations_problem.assets/permutations_i_pruning.png) +![全排列剪枝示例](permutations_problem.assets/permutations_i_pruning.png){ class="animation-figure" }

图 13-6   全排列剪枝示例

@@ -481,7 +481,7 @@ comments: true 如图 13-7 所示,上述方法生成的排列有一半都是重复的。 -![重复排列](permutations_problem.assets/permutations_ii.png) +![重复排列](permutations_problem.assets/permutations_ii.png){ class="animation-figure" }

图 13-7   重复排列

@@ -495,7 +495,7 @@ comments: true 本质上看,**我们的目标是在某一轮选择中,保证多个相等的元素仅被选择一次**。 -![重复排列剪枝](permutations_problem.assets/permutations_ii_pruning.png) +![重复排列剪枝](permutations_problem.assets/permutations_ii_pruning.png){ class="animation-figure" }

图 13-8   重复排列剪枝

@@ -955,6 +955,6 @@ comments: true 图 13-9 展示了两个剪枝条件的生效范围。注意,树中的每个节点代表一个选择,从根节点到叶节点的路径上的各个节点构成一个排列。 -![两种剪枝条件的作用范围](permutations_problem.assets/permutations_ii_pruning_summary.png) +![两种剪枝条件的作用范围](permutations_problem.assets/permutations_ii_pruning_summary.png){ class="animation-figure" }

图 13-9   两种剪枝条件的作用范围

diff --git a/docs/chapter_backtracking/subset_sum_problem.md b/docs/chapter_backtracking/subset_sum_problem.md index 287ff9fa1..13a126f29 100644 --- a/docs/chapter_backtracking/subset_sum_problem.md +++ b/docs/chapter_backtracking/subset_sum_problem.md @@ -432,7 +432,7 @@ comments: true 这是因为搜索过程是区分选择顺序的,然而子集不区分选择顺序。如图 13-10 所示,先选 $4$ 后选 $5$ 与先选 $5$ 后选 $4$ 是两个不同的分支,但两者对应同一个子集。 -![子集搜索与越界剪枝](subset_sum_problem.assets/subset_sum_i_naive.png) +![子集搜索与越界剪枝](subset_sum_problem.assets/subset_sum_i_naive.png){ class="animation-figure" }

图 13-10   子集搜索与越界剪枝

@@ -454,7 +454,7 @@ comments: true 2. 前两轮选择 $4$ 和 $5$ ,生成子集 $[4, 5, \dots]$ 。 3. 若第一轮选择 $5$ ,**则第二轮应该跳过 $3$ 和 $4$** ,因为子集 $[5, 3, \dots]$ 和 $[5, 4, \dots]$ 与第 `1.` 和 `2.` 步中描述的子集完全重复。 -![不同选择顺序导致的重复子集](subset_sum_problem.assets/subset_sum_i_pruning.png) +![不同选择顺序导致的重复子集](subset_sum_problem.assets/subset_sum_i_pruning.png){ class="animation-figure" }

图 13-11   不同选择顺序导致的重复子集

@@ -908,7 +908,7 @@ comments: true 如图 13-12 所示,为将数组 $[3, 4, 5]$ 和目标元素 $9$ 输入到以上代码后的整体回溯过程。 -![子集和 I 回溯过程](subset_sum_problem.assets/subset_sum_i.png) +![子集和 I 回溯过程](subset_sum_problem.assets/subset_sum_i.png){ class="animation-figure" }

图 13-12   子集和 I 回溯过程

@@ -922,7 +922,7 @@ comments: true **造成这种重复的原因是相等元素在某轮中被多次选择**。在图 13-13 中,第一轮共有三个选择,其中两个都为 $4$ ,会产生两个重复的搜索分支,从而输出重复子集;同理,第二轮的两个 $4$ 也会产生重复子集。 -![相等元素导致的重复子集](subset_sum_problem.assets/subset_sum_ii_repeat.png) +![相等元素导致的重复子集](subset_sum_problem.assets/subset_sum_ii_repeat.png){ class="animation-figure" }

图 13-13   相等元素导致的重复子集

@@ -1427,6 +1427,6 @@ comments: true 图 13-14 展示了数组 $[4, 4, 5]$ 和目标元素 $9$ 的回溯过程,共包含四种剪枝操作。请你将图示与代码注释相结合,理解整个搜索过程,以及每种剪枝操作是如何工作的。 -![子集和 II 回溯过程](subset_sum_problem.assets/subset_sum_ii.png) +![子集和 II 回溯过程](subset_sum_problem.assets/subset_sum_ii.png){ class="animation-figure" }

图 13-14   子集和 II 回溯过程

diff --git a/docs/chapter_computational_complexity/index.md b/docs/chapter_computational_complexity/index.md index 231efa2ca..57f564211 100644 --- a/docs/chapter_computational_complexity/index.md +++ b/docs/chapter_computational_complexity/index.md @@ -7,7 +7,7 @@ icon: material/timer-sand
-![复杂度分析](../assets/covers/chapter_complexity_analysis.jpg){ width="600" } +![复杂度分析](../assets/covers/chapter_complexity_analysis.jpg){ class="cover-image" }
diff --git a/docs/chapter_computational_complexity/iteration_and_recursion.md b/docs/chapter_computational_complexity/iteration_and_recursion.md index c695e3d6c..c2a1e8732 100644 --- a/docs/chapter_computational_complexity/iteration_and_recursion.md +++ b/docs/chapter_computational_complexity/iteration_and_recursion.md @@ -184,7 +184,7 @@ comments: true 图 2-1 展示了该求和函数的流程框图。 -![求和函数的流程框图](iteration_and_recursion.assets/iteration.png) +![求和函数的流程框图](iteration_and_recursion.assets/iteration.png){ class="animation-figure" }

图 2-1   求和函数的流程框图

@@ -823,7 +823,7 @@ comments: true 图 2-2 给出了该嵌套循环的流程框图。 -![嵌套循环的流程框图](iteration_and_recursion.assets/nested_iteration.png) +![嵌套循环的流程框图](iteration_and_recursion.assets/nested_iteration.png){ class="animation-figure" }

图 2-2   嵌套循环的流程框图

@@ -1028,7 +1028,7 @@ comments: true 图 2-3 展示了该函数的递归过程。 -![求和函数的递归过程](iteration_and_recursion.assets/recursion_sum.png) +![求和函数的递归过程](iteration_and_recursion.assets/recursion_sum.png){ class="animation-figure" }

图 2-3   求和函数的递归过程

@@ -1051,7 +1051,7 @@ comments: true 如图 2-4 所示,在触发终止条件前,同时存在 $n$ 个未返回的递归函数,**递归深度为 $n$** 。 -![递归调用深度](iteration_and_recursion.assets/recursion_sum_depth.png) +![递归调用深度](iteration_and_recursion.assets/recursion_sum_depth.png){ class="animation-figure" }

图 2-4   递归调用深度

@@ -1227,7 +1227,7 @@ comments: true - **普通递归**:求和操作是在“归”的过程中执行的,每层返回后都要再执行一次求和操作。 - **尾递归**:求和操作是在“递”的过程中执行的,“归”的过程只需层层返回。 -![尾递归过程](iteration_and_recursion.assets/tail_recursion_sum.png) +![尾递归过程](iteration_and_recursion.assets/tail_recursion_sum.png){ class="animation-figure" }

图 2-5   尾递归过程

@@ -1432,7 +1432,7 @@ comments: true 观察以上代码,我们在函数内递归调用了两个函数,**这意味着从一个调用产生了两个调用分支**。如图 2-6 所示,这样不断递归调用下去,最终将产生一个层数为 $n$ 的「递归树 recursion tree」。 -![斐波那契数列的递归树](iteration_and_recursion.assets/recursion_tree.png) +![斐波那契数列的递归树](iteration_and_recursion.assets/recursion_tree.png){ class="animation-figure" }

图 2-6   斐波那契数列的递归树

diff --git a/docs/chapter_computational_complexity/space_complexity.md b/docs/chapter_computational_complexity/space_complexity.md index bfe9518f8..08089c2fc 100755 --- a/docs/chapter_computational_complexity/space_complexity.md +++ b/docs/chapter_computational_complexity/space_complexity.md @@ -24,7 +24,7 @@ comments: true 在分析一段程序的空间复杂度时,**我们通常统计暂存数据、栈帧空间和输出数据三部分**。 -![算法使用的相关空间](space_complexity.assets/space_types.png) +![算法使用的相关空间](space_complexity.assets/space_types.png){ class="animation-figure" }

图 2-15   算法使用的相关空间

@@ -717,7 +717,7 @@ O(1) < O(\log n) < O(n) < O(n^2) < O(2^n) \newline \end{aligned} $$ -![常见的空间复杂度类型](space_complexity.assets/space_complexity_common_types.png) +![常见的空间复杂度类型](space_complexity.assets/space_complexity_common_types.png){ class="animation-figure" }

图 2-16   常见的空间复杂度类型

@@ -1463,7 +1463,7 @@ $$ } ``` -![递归函数产生的线性阶空间复杂度](space_complexity.assets/space_complexity_recursive_linear.png) +![递归函数产生的线性阶空间复杂度](space_complexity.assets/space_complexity_recursive_linear.png){ class="animation-figure" }

图 2-17   递归函数产生的线性阶空间复杂度

@@ -1842,7 +1842,7 @@ $$ } ``` -![递归函数产生的平方阶空间复杂度](space_complexity.assets/space_complexity_recursive_quadratic.png) +![递归函数产生的平方阶空间复杂度](space_complexity.assets/space_complexity_recursive_quadratic.png){ class="animation-figure" }

图 2-18   递归函数产生的平方阶空间复杂度

@@ -2015,7 +2015,7 @@ $$ } ``` -![满二叉树产生的指数阶空间复杂度](space_complexity.assets/space_complexity_exponential.png) +![满二叉树产生的指数阶空间复杂度](space_complexity.assets/space_complexity_exponential.png){ class="animation-figure" }

图 2-19   满二叉树产生的指数阶空间复杂度

diff --git a/docs/chapter_computational_complexity/time_complexity.md b/docs/chapter_computational_complexity/time_complexity.md index 6684de4b4..f9819e643 100755 --- a/docs/chapter_computational_complexity/time_complexity.md +++ b/docs/chapter_computational_complexity/time_complexity.md @@ -462,7 +462,7 @@ $$ - 算法 `B` 中的打印操作需要循环 $n$ 次,算法运行时间随着 $n$ 增大呈线性增长。此算法的时间复杂度被称为“线性阶”。 - 算法 `C` 中的打印操作需要循环 $1000000$ 次,虽然运行时间很长,但它与输入数据大小 $n$ 无关。因此 `C` 的时间复杂度和 `A` 相同,仍为“常数阶”。 -![算法 A、B 和 C 的时间增长趋势](time_complexity.assets/time_complexity_simple_example.png) +![算法 A、B 和 C 的时间增长趋势](time_complexity.assets/time_complexity_simple_example.png){ class="animation-figure" }

图 2-7   算法 A、B 和 C 的时间增长趋势

@@ -661,7 +661,7 @@ $T(n)$ 是一次函数,说明其运行时间的增长趋势是线性的,因 如图 2-8 所示,计算渐近上界就是寻找一个函数 $f(n)$ ,使得当 $n$ 趋向于无穷大时,$T(n)$ 和 $f(n)$ 处于相同的增长级别,仅相差一个常数项 $c$ 的倍数。 -![函数的渐近上界](time_complexity.assets/asymptotic_upper_bound.png) +![函数的渐近上界](time_complexity.assets/asymptotic_upper_bound.png){ class="animation-figure" }

图 2-8   函数的渐近上界

@@ -950,7 +950,7 @@ O(1) < O(\log n) < O(n) < O(n \log n) < O(n^2) < O(2^n) < O(n!) \newline \end{aligned} $$ -![常见的时间复杂度类型](time_complexity.assets/time_complexity_common_types.png) +![常见的时间复杂度类型](time_complexity.assets/time_complexity_common_types.png){ class="animation-figure" }

图 2-9   常见的时间复杂度类型

@@ -1642,7 +1642,7 @@ $$ 图 2-10 对比了常数阶、线性阶和平方阶三种时间复杂度。 -![常数阶、线性阶和平方阶的时间复杂度](time_complexity.assets/time_complexity_constant_linear_quadratic.png) +![常数阶、线性阶和平方阶的时间复杂度](time_complexity.assets/time_complexity_constant_linear_quadratic.png){ class="animation-figure" }

图 2-10   常数阶、线性阶和平方阶的时间复杂度

@@ -2148,7 +2148,7 @@ $$ } ``` -![指数阶的时间复杂度](time_complexity.assets/time_complexity_exponential.png) +![指数阶的时间复杂度](time_complexity.assets/time_complexity_exponential.png){ class="animation-figure" }

图 2-11   指数阶的时间复杂度

@@ -2460,7 +2460,7 @@ $$ } ``` -![对数阶的时间复杂度](time_complexity.assets/time_complexity_logarithmic.png) +![对数阶的时间复杂度](time_complexity.assets/time_complexity_logarithmic.png){ class="animation-figure" }

图 2-12   对数阶的时间复杂度

@@ -2790,7 +2790,7 @@ $$ 图 2-13 展示了线性对数阶的生成方式。二叉树的每一层的操作总数都为 $n$ ,树共有 $\log_2 n + 1$ 层,因此时间复杂度为 $O(n \log n)$ 。 -![线性对数阶的时间复杂度](time_complexity.assets/time_complexity_logarithmic_linear.png) +![线性对数阶的时间复杂度](time_complexity.assets/time_complexity_logarithmic_linear.png){ class="animation-figure" }

图 2-13   线性对数阶的时间复杂度

@@ -2994,7 +2994,7 @@ $$ } ``` -![阶乘阶的时间复杂度](time_complexity.assets/time_complexity_factorial.png) +![阶乘阶的时间复杂度](time_complexity.assets/time_complexity_factorial.png){ class="animation-figure" }

图 2-14   阶乘阶的时间复杂度

diff --git a/docs/chapter_data_structure/character_encoding.md b/docs/chapter_data_structure/character_encoding.md index 27da47f86..27cf14ad3 100644 --- a/docs/chapter_data_structure/character_encoding.md +++ b/docs/chapter_data_structure/character_encoding.md @@ -10,7 +10,7 @@ comments: true 「ASCII 码」是最早出现的字符集,全称为“美国标准信息交换代码”。它使用 7 位二进制数(即一个字节的低 7 位)表示一个字符,最多能够表示 128 个不同的字符。如图 3-6 所示,ASCII 码包括英文字母的大小写、数字 0 ~ 9、一些标点符号,以及一些控制字符(如换行符和制表符)。 -![ASCII 码](character_encoding.assets/ascii_table.png) +![ASCII 码](character_encoding.assets/ascii_table.png){ class="animation-figure" }

图 3-6   ASCII 码

@@ -38,7 +38,7 @@ Unicode 是一种字符集标准,本质上是给每个字符分配一个编号 对于以上问题,**一种直接的解决方案是将所有字符存储为等长的编码**。如图 3-7 所示,“Hello”中的每个字符占用 1 字节,“算法”中的每个字符占用 2 字节。我们可以通过高位填 0 ,将“Hello 算法”中的所有字符都编码为 2 字节长度。这样系统就可以每隔 2 字节解析一个字符,恢复出这个短语的内容了。 -![Unicode 编码示例](character_encoding.assets/unicode_hello_algo.png) +![Unicode 编码示例](character_encoding.assets/unicode_hello_algo.png){ class="animation-figure" }

图 3-7   Unicode 编码示例

@@ -59,7 +59,7 @@ UTF-8 的编码规则并不复杂,分为以下两种情况。 之所以将 $10$ 当作校验符,是因为在 UTF-8 编码规则下,不可能有字符的最高两位是 $10$ 。这个结论可以用反证法来证明:假设一个字符的最高两位是 $10$ ,说明该字符的长度为 $1$ ,对应 ASCII 码。而 ASCII 码的最高位应该是 $0$ ,与假设矛盾。 -![UTF-8 编码示例](character_encoding.assets/utf-8_hello_algo.png) +![UTF-8 编码示例](character_encoding.assets/utf-8_hello_algo.png){ class="animation-figure" }

图 3-8   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 923f14423..637e3e222 100644 --- a/docs/chapter_data_structure/classification_of_data_structure.md +++ b/docs/chapter_data_structure/classification_of_data_structure.md @@ -15,7 +15,7 @@ comments: true - **线性数据结构**:数组、链表、栈、队列、哈希表。 - **非线性数据结构**:树、堆、图、哈希表。 -![线性与非线性数据结构](classification_of_data_structure.assets/classification_logic_structure.png) +![线性与非线性数据结构](classification_of_data_structure.assets/classification_logic_structure.png){ class="animation-figure" }

图 3-1   线性与非线性数据结构

@@ -33,7 +33,7 @@ comments: true **系统通过内存地址来访问目标位置的数据**。如图 3-2 所示,计算机根据特定规则为表格中的每个单元格分配编号,确保每个内存空间都有唯一的内存地址。有了这些地址,程序便可以访问内存中的数据。 -![内存条、内存空间、内存地址](classification_of_data_structure.assets/computer_memory_location.png) +![内存条、内存空间、内存地址](classification_of_data_structure.assets/computer_memory_location.png){ class="animation-figure" }

图 3-2   内存条、内存空间、内存地址

@@ -41,7 +41,7 @@ comments: true 如图 3-3 所示,**物理结构反映了数据在计算机内存中的存储方式**,可分为连续空间存储(数组)和分散空间存储(链表)。物理结构从底层决定了数据的访问、更新、增删等操作方法,同时在时间效率和空间效率方面呈现出互补的特点。 -![连续空间存储与分散空间存储](classification_of_data_structure.assets/classification_phisical_structure.png) +![连续空间存储与分散空间存储](classification_of_data_structure.assets/classification_phisical_structure.png){ class="animation-figure" }

图 3-3   连续空间存储与分散空间存储

diff --git a/docs/chapter_data_structure/index.md b/docs/chapter_data_structure/index.md index 8637624f5..30e69e750 100644 --- a/docs/chapter_data_structure/index.md +++ b/docs/chapter_data_structure/index.md @@ -7,7 +7,7 @@ icon: material/shape-outline
-![数据结构](../assets/covers/chapter_data_structure.jpg){ width="600" } +![数据结构](../assets/covers/chapter_data_structure.jpg){ class="cover-image" }
diff --git a/docs/chapter_data_structure/number_encoding.md b/docs/chapter_data_structure/number_encoding.md index cc8293d67..1ae7fe333 100644 --- a/docs/chapter_data_structure/number_encoding.md +++ b/docs/chapter_data_structure/number_encoding.md @@ -20,7 +20,7 @@ comments: true 图 3-4 展示了原码、反码和补码之间的转换方法。 -![原码、反码与补码之间的相互转换](number_encoding.assets/1s_2s_complement.png) +![原码、反码与补码之间的相互转换](number_encoding.assets/1s_2s_complement.png){ class="animation-figure" }

图 3-4   原码、反码与补码之间的相互转换

@@ -129,7 +129,7 @@ $$ \end{aligned} $$ -![IEEE 754 标准下的 float 的计算示例](number_encoding.assets/ieee_754_float.png) +![IEEE 754 标准下的 float 的计算示例](number_encoding.assets/ieee_754_float.png){ class="animation-figure" }

图 3-5   IEEE 754 标准下的 float 的计算示例

diff --git a/docs/chapter_divide_and_conquer/binary_search_recur.md b/docs/chapter_divide_and_conquer/binary_search_recur.md index e42e838eb..d1b898363 100644 --- a/docs/chapter_divide_and_conquer/binary_search_recur.md +++ b/docs/chapter_divide_and_conquer/binary_search_recur.md @@ -40,7 +40,7 @@ comments: true 图 12-4 展示了在数组中二分查找元素 $6$ 的分治过程。 -![二分查找的分治过程](binary_search_recur.assets/binary_search_recur.png) +![二分查找的分治过程](binary_search_recur.assets/binary_search_recur.png){ class="animation-figure" }

图 12-4   二分查找的分治过程

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 f1e6be241..b277941a7 100644 --- a/docs/chapter_divide_and_conquer/build_binary_tree_problem.md +++ b/docs/chapter_divide_and_conquer/build_binary_tree_problem.md @@ -8,7 +8,7 @@ comments: true 给定一个二叉树的前序遍历 `preorder` 和中序遍历 `inorder` ,请从中构建二叉树,返回二叉树的根节点。假设二叉树中没有值重复的节点。 -![构建二叉树的示例数据](build_binary_tree_problem.assets/build_tree_example.png) +![构建二叉树的示例数据](build_binary_tree_problem.assets/build_tree_example.png){ class="animation-figure" }

图 12-5   构建二叉树的示例数据

@@ -35,7 +35,7 @@ comments: true 2. 查找根节点 3 在 `inorder` 中的索引,利用该索引可将 `inorder` 划分为 `[ 9 | 3 | 1 2 7 ]` 。 3. 根据 `inorder` 划分结果,易得左子树和右子树的节点数量分别为 1 和 3 ,从而可将 `preorder` 划分为 `[ 3 | 9 | 2 1 7 ]` 。 -![在前序和中序遍历中划分子树](build_binary_tree_problem.assets/build_tree_preorder_inorder_division.png) +![在前序和中序遍历中划分子树](build_binary_tree_problem.assets/build_tree_preorder_inorder_division.png){ class="animation-figure" }

图 12-6   在前序和中序遍历中划分子树

@@ -63,7 +63,7 @@ comments: true 请注意,右子树根节点索引中的 $(m-l)$ 的含义是“左子树的节点数量”,建议配合图 12-7 理解。 -![根节点和左右子树的索引区间表示](build_binary_tree_problem.assets/build_tree_division_pointers.png) +![根节点和左右子树的索引区间表示](build_binary_tree_problem.assets/build_tree_division_pointers.png){ class="animation-figure" }

图 12-7   根节点和左右子树的索引区间表示

@@ -448,37 +448,37 @@ comments: true 图 12-8 展示了构建二叉树的递归过程,各个节点是在向下“递”的过程中建立的,而各条边(即引用)是在向上“归”的过程中建立的。 === "<1>" - ![构建二叉树的递归过程](build_binary_tree_problem.assets/built_tree_step1.png) + ![构建二叉树的递归过程](build_binary_tree_problem.assets/built_tree_step1.png){ class="animation-figure" } === "<2>" - ![built_tree_step2](build_binary_tree_problem.assets/built_tree_step2.png) + ![built_tree_step2](build_binary_tree_problem.assets/built_tree_step2.png){ class="animation-figure" } === "<3>" - ![built_tree_step3](build_binary_tree_problem.assets/built_tree_step3.png) + ![built_tree_step3](build_binary_tree_problem.assets/built_tree_step3.png){ class="animation-figure" } === "<4>" - ![built_tree_step4](build_binary_tree_problem.assets/built_tree_step4.png) + ![built_tree_step4](build_binary_tree_problem.assets/built_tree_step4.png){ class="animation-figure" } === "<5>" - ![built_tree_step5](build_binary_tree_problem.assets/built_tree_step5.png) + ![built_tree_step5](build_binary_tree_problem.assets/built_tree_step5.png){ class="animation-figure" } === "<6>" - ![built_tree_step6](build_binary_tree_problem.assets/built_tree_step6.png) + ![built_tree_step6](build_binary_tree_problem.assets/built_tree_step6.png){ class="animation-figure" } === "<7>" - ![built_tree_step7](build_binary_tree_problem.assets/built_tree_step7.png) + ![built_tree_step7](build_binary_tree_problem.assets/built_tree_step7.png){ class="animation-figure" } === "<8>" - ![built_tree_step8](build_binary_tree_problem.assets/built_tree_step8.png) + ![built_tree_step8](build_binary_tree_problem.assets/built_tree_step8.png){ class="animation-figure" } === "<9>" - ![built_tree_step9](build_binary_tree_problem.assets/built_tree_step9.png) + ![built_tree_step9](build_binary_tree_problem.assets/built_tree_step9.png){ class="animation-figure" }

图 12-8   构建二叉树的递归过程

每个递归函数内的前序遍历 `preorder` 和中序遍历 `inorder` 的划分结果如图 12-9 所示。 -![每个递归函数中的划分结果](build_binary_tree_problem.assets/built_tree_overall.png) +![每个递归函数中的划分结果](build_binary_tree_problem.assets/built_tree_overall.png){ class="animation-figure" }

图 12-9   每个递归函数中的划分结果

diff --git a/docs/chapter_divide_and_conquer/divide_and_conquer.md b/docs/chapter_divide_and_conquer/divide_and_conquer.md index 11d185f3f..23d5ce3cd 100644 --- a/docs/chapter_divide_and_conquer/divide_and_conquer.md +++ b/docs/chapter_divide_and_conquer/divide_and_conquer.md @@ -14,7 +14,7 @@ comments: true 1. **分**:递归地将原数组(原问题)划分为两个子数组(子问题),直到子数组只剩一个元素(最小子问题)。 2. **治**:从底至顶地将有序的子数组(子问题的解)进行合并,从而得到有序的原数组(原问题的解)。 -![归并排序的分治策略](divide_and_conquer.assets/divide_and_conquer_merge_sort.png) +![归并排序的分治策略](divide_and_conquer.assets/divide_and_conquer_merge_sort.png){ class="animation-figure" }

图 12-1   归并排序的分治策略

@@ -46,7 +46,7 @@ $$ O(n + (\frac{n}{2})^2 \times 2 + n) = O(\frac{n^2}{2} + 2n) $$ -![划分数组前后的冒泡排序](divide_and_conquer.assets/divide_and_conquer_bubble_sort.png) +![划分数组前后的冒泡排序](divide_and_conquer.assets/divide_and_conquer_bubble_sort.png){ class="animation-figure" }

图 12-2   划分数组前后的冒泡排序

@@ -74,7 +74,7 @@ $$ 比如在图 12-3 所示的“桶排序”中,我们将海量的数据平均分配到各个桶中,则可所有桶的排序任务分散到各个计算单元,完成后再进行结果合并。 -![桶排序的并行计算](divide_and_conquer.assets/divide_and_conquer_parallel_computing.png) +![桶排序的并行计算](divide_and_conquer.assets/divide_and_conquer_parallel_computing.png){ class="animation-figure" }

图 12-3   桶排序的并行计算

diff --git a/docs/chapter_divide_and_conquer/hanota_problem.md b/docs/chapter_divide_and_conquer/hanota_problem.md index 92c26b24d..57d71d908 100644 --- a/docs/chapter_divide_and_conquer/hanota_problem.md +++ b/docs/chapter_divide_and_conquer/hanota_problem.md @@ -14,7 +14,7 @@ comments: true 2. 每次只能移动一个圆盘。 3. 小圆盘必须时刻位于大圆盘之上。 -![汉诺塔问题示例](hanota_problem.assets/hanota_example.png) +![汉诺塔问题示例](hanota_problem.assets/hanota_example.png){ class="animation-figure" }

图 12-10   汉诺塔问题示例

@@ -25,10 +25,10 @@ comments: true 如图 12-11 所示,对于问题 $f(1)$ ,即当只有一个圆盘时,我们将它直接从 `A` 移动至 `C` 即可。 === "<1>" - ![规模为 1 问题的解](hanota_problem.assets/hanota_f1_step1.png) + ![规模为 1 问题的解](hanota_problem.assets/hanota_f1_step1.png){ class="animation-figure" } === "<2>" - ![hanota_f1_step2](hanota_problem.assets/hanota_f1_step2.png) + ![hanota_f1_step2](hanota_problem.assets/hanota_f1_step2.png){ class="animation-figure" }

图 12-11   规模为 1 问题的解

@@ -39,16 +39,16 @@ comments: true 3. 最后将小圆盘从 `B` 移至 `C` 。 === "<1>" - ![规模为 2 问题的解](hanota_problem.assets/hanota_f2_step1.png) + ![规模为 2 问题的解](hanota_problem.assets/hanota_f2_step1.png){ class="animation-figure" } === "<2>" - ![hanota_f2_step2](hanota_problem.assets/hanota_f2_step2.png) + ![hanota_f2_step2](hanota_problem.assets/hanota_f2_step2.png){ class="animation-figure" } === "<3>" - ![hanota_f2_step3](hanota_problem.assets/hanota_f2_step3.png) + ![hanota_f2_step3](hanota_problem.assets/hanota_f2_step3.png){ class="animation-figure" } === "<4>" - ![hanota_f2_step4](hanota_problem.assets/hanota_f2_step4.png) + ![hanota_f2_step4](hanota_problem.assets/hanota_f2_step4.png){ class="animation-figure" }

图 12-12   规模为 2 问题的解

@@ -65,16 +65,16 @@ comments: true 3. 令 `C` 为目标柱、`A` 为缓冲柱,将两个圆盘从 `B` 移动至 `C` 。 === "<1>" - ![规模为 3 问题的解](hanota_problem.assets/hanota_f3_step1.png) + ![规模为 3 问题的解](hanota_problem.assets/hanota_f3_step1.png){ class="animation-figure" } === "<2>" - ![hanota_f3_step2](hanota_problem.assets/hanota_f3_step2.png) + ![hanota_f3_step2](hanota_problem.assets/hanota_f3_step2.png){ class="animation-figure" } === "<3>" - ![hanota_f3_step3](hanota_problem.assets/hanota_f3_step3.png) + ![hanota_f3_step3](hanota_problem.assets/hanota_f3_step3.png){ class="animation-figure" } === "<4>" - ![hanota_f3_step4](hanota_problem.assets/hanota_f3_step4.png) + ![hanota_f3_step4](hanota_problem.assets/hanota_f3_step4.png){ class="animation-figure" }

图 12-13   规模为 3 问题的解

@@ -88,7 +88,7 @@ comments: true 对于这两个子问题 $f(n-1)$ ,**可以通过相同的方式进行递归划分**,直至达到最小子问题 $f(1)$ 。而 $f(1)$ 的解是已知的,只需一次移动操作即可。 -![汉诺塔问题的分治策略](hanota_problem.assets/hanota_divide_and_conquer.png) +![汉诺塔问题的分治策略](hanota_problem.assets/hanota_divide_and_conquer.png){ class="animation-figure" }

图 12-14   汉诺塔问题的分治策略

@@ -485,7 +485,7 @@ comments: true 如图 12-15 所示,汉诺塔问题形成一个高度为 $n$ 的递归树,每个节点代表一个子问题、对应一个开启的 `dfs()` 函数,**因此时间复杂度为 $O(2^n)$ ,空间复杂度为 $O(n)$** 。 -![汉诺塔问题的递归树](hanota_problem.assets/hanota_recursive_tree.png) +![汉诺塔问题的递归树](hanota_problem.assets/hanota_recursive_tree.png){ class="animation-figure" }

图 12-15   汉诺塔问题的递归树

diff --git a/docs/chapter_divide_and_conquer/index.md b/docs/chapter_divide_and_conquer/index.md index a942cfea0..4afa1758e 100644 --- a/docs/chapter_divide_and_conquer/index.md +++ b/docs/chapter_divide_and_conquer/index.md @@ -7,7 +7,7 @@ icon: material/set-split
-![分治](../assets/covers/chapter_divide_and_conquer.jpg){ width="600" } +![分治](../assets/covers/chapter_divide_and_conquer.jpg){ class="cover-image" }
diff --git a/docs/chapter_dynamic_programming/dp_problem_features.md b/docs/chapter_dynamic_programming/dp_problem_features.md index ba578f04d..92fad5298 100644 --- a/docs/chapter_dynamic_programming/dp_problem_features.md +++ b/docs/chapter_dynamic_programming/dp_problem_features.md @@ -22,7 +22,7 @@ comments: true 如图 14-6 所示,若第 $1$、$2$、$3$ 阶的代价分别为 $1$、$10$、$1$ ,则从地面爬到第 $3$ 阶的最小代价为 $2$ 。 -![爬到第 3 阶的最小代价](dp_problem_features.assets/min_cost_cs_example.png) +![爬到第 3 阶的最小代价](dp_problem_features.assets/min_cost_cs_example.png){ class="animation-figure" }

图 14-6   爬到第 3 阶的最小代价

@@ -300,7 +300,7 @@ $$ 图 14-7 展示了以上代码的动态规划过程。 -![爬楼梯最小代价的动态规划过程](dp_problem_features.assets/min_cost_cs_dp.png) +![爬楼梯最小代价的动态规划过程](dp_problem_features.assets/min_cost_cs_dp.png){ class="animation-figure" }

图 14-7   爬楼梯最小代价的动态规划过程

@@ -545,7 +545,7 @@ $$ 例如图 14-8 ,爬上第 $3$ 阶仅剩 $2$ 种可行方案,其中连续三次跳 $1$ 阶的方案不满足约束条件,因此被舍弃。 -![带约束爬到第 3 阶的方案数量](dp_problem_features.assets/climbing_stairs_constraint_example.png) +![带约束爬到第 3 阶的方案数量](dp_problem_features.assets/climbing_stairs_constraint_example.png){ class="animation-figure" }

图 14-8   带约束爬到第 3 阶的方案数量

@@ -567,7 +567,7 @@ dp[i, 2] = dp[i-2, 1] + dp[i-2, 2] \end{cases} $$ -![考虑约束下的递推关系](dp_problem_features.assets/climbing_stairs_constraint_state_transfer.png) +![考虑约束下的递推关系](dp_problem_features.assets/climbing_stairs_constraint_state_transfer.png){ class="animation-figure" }

图 14-9   考虑约束下的递推关系

diff --git a/docs/chapter_dynamic_programming/dp_solution_pipeline.md b/docs/chapter_dynamic_programming/dp_solution_pipeline.md index 9f0883bf1..a23553745 100644 --- a/docs/chapter_dynamic_programming/dp_solution_pipeline.md +++ b/docs/chapter_dynamic_programming/dp_solution_pipeline.md @@ -41,7 +41,7 @@ comments: true 图 14-10 展示了一个例子,给定网格的最小路径和为 $13$ 。 -![最小路径和示例数据](dp_solution_pipeline.assets/min_path_sum_example.png) +![最小路径和示例数据](dp_solution_pipeline.assets/min_path_sum_example.png){ class="animation-figure" }

图 14-10   最小路径和示例数据

@@ -53,7 +53,7 @@ comments: true 至此,我们就得到了图 14-11 所示的二维 $dp$ 矩阵,其尺寸与输入网格 $grid$ 相同。 -![状态定义与 dp 表](dp_solution_pipeline.assets/min_path_sum_solution_step1.png) +![状态定义与 dp 表](dp_solution_pipeline.assets/min_path_sum_solution_step1.png){ class="animation-figure" }

图 14-11   状态定义与 dp 表

@@ -73,7 +73,7 @@ $$ dp[i, j] = \min(dp[i-1, j], dp[i, j-1]) + grid[i, j] $$ -![最优子结构与状态转移方程](dp_solution_pipeline.assets/min_path_sum_solution_step2.png) +![最优子结构与状态转移方程](dp_solution_pipeline.assets/min_path_sum_solution_step2.png){ class="animation-figure" }

图 14-12   最优子结构与状态转移方程

@@ -89,7 +89,7 @@ $$ 如图 14-13 所示,由于每个格子是由其左方格子和上方格子转移而来,因此我们使用采用循环来遍历矩阵,外循环遍历各行、内循环遍历各列。 -![边界条件与状态转移顺序](dp_solution_pipeline.assets/min_path_sum_solution_step3.png) +![边界条件与状态转移顺序](dp_solution_pipeline.assets/min_path_sum_solution_step3.png){ class="animation-figure" }

图 14-13   边界条件与状态转移顺序

@@ -368,7 +368,7 @@ $$ 本质上看,造成重叠子问题的原因为:**存在多条路径可以从左上角到达某一单元格**。 -![暴力搜索递归树](dp_solution_pipeline.assets/min_path_sum_dfs.png) +![暴力搜索递归树](dp_solution_pipeline.assets/min_path_sum_dfs.png){ class="animation-figure" }

图 14-14   暴力搜索递归树

@@ -697,7 +697,7 @@ $$ 如图 14-15 所示,在引入记忆化后,所有子问题的解只需计算一次,因此时间复杂度取决于状态总数,即网格尺寸 $O(nm)$ 。 -![记忆化搜索递归树](dp_solution_pipeline.assets/min_path_sum_dfs_mem.png) +![记忆化搜索递归树](dp_solution_pipeline.assets/min_path_sum_dfs_mem.png){ class="animation-figure" }

图 14-15   记忆化搜索递归树

@@ -1039,40 +1039,40 @@ $$ 数组 `dp` 大小为 $n \times m$ ,**因此空间复杂度为 $O(nm)$** 。 === "<1>" - ![最小路径和的动态规划过程](dp_solution_pipeline.assets/min_path_sum_dp_step1.png) + ![最小路径和的动态规划过程](dp_solution_pipeline.assets/min_path_sum_dp_step1.png){ class="animation-figure" } === "<2>" - ![min_path_sum_dp_step2](dp_solution_pipeline.assets/min_path_sum_dp_step2.png) + ![min_path_sum_dp_step2](dp_solution_pipeline.assets/min_path_sum_dp_step2.png){ class="animation-figure" } === "<3>" - ![min_path_sum_dp_step3](dp_solution_pipeline.assets/min_path_sum_dp_step3.png) + ![min_path_sum_dp_step3](dp_solution_pipeline.assets/min_path_sum_dp_step3.png){ class="animation-figure" } === "<4>" - ![min_path_sum_dp_step4](dp_solution_pipeline.assets/min_path_sum_dp_step4.png) + ![min_path_sum_dp_step4](dp_solution_pipeline.assets/min_path_sum_dp_step4.png){ class="animation-figure" } === "<5>" - ![min_path_sum_dp_step5](dp_solution_pipeline.assets/min_path_sum_dp_step5.png) + ![min_path_sum_dp_step5](dp_solution_pipeline.assets/min_path_sum_dp_step5.png){ class="animation-figure" } === "<6>" - ![min_path_sum_dp_step6](dp_solution_pipeline.assets/min_path_sum_dp_step6.png) + ![min_path_sum_dp_step6](dp_solution_pipeline.assets/min_path_sum_dp_step6.png){ class="animation-figure" } === "<7>" - ![min_path_sum_dp_step7](dp_solution_pipeline.assets/min_path_sum_dp_step7.png) + ![min_path_sum_dp_step7](dp_solution_pipeline.assets/min_path_sum_dp_step7.png){ class="animation-figure" } === "<8>" - ![min_path_sum_dp_step8](dp_solution_pipeline.assets/min_path_sum_dp_step8.png) + ![min_path_sum_dp_step8](dp_solution_pipeline.assets/min_path_sum_dp_step8.png){ class="animation-figure" } === "<9>" - ![min_path_sum_dp_step9](dp_solution_pipeline.assets/min_path_sum_dp_step9.png) + ![min_path_sum_dp_step9](dp_solution_pipeline.assets/min_path_sum_dp_step9.png){ class="animation-figure" } === "<10>" - ![min_path_sum_dp_step10](dp_solution_pipeline.assets/min_path_sum_dp_step10.png) + ![min_path_sum_dp_step10](dp_solution_pipeline.assets/min_path_sum_dp_step10.png){ class="animation-figure" } === "<11>" - ![min_path_sum_dp_step11](dp_solution_pipeline.assets/min_path_sum_dp_step11.png) + ![min_path_sum_dp_step11](dp_solution_pipeline.assets/min_path_sum_dp_step11.png){ class="animation-figure" } === "<12>" - ![min_path_sum_dp_step12](dp_solution_pipeline.assets/min_path_sum_dp_step12.png) + ![min_path_sum_dp_step12](dp_solution_pipeline.assets/min_path_sum_dp_step12.png){ class="animation-figure" }

图 14-16   最小路径和的动态规划过程

diff --git a/docs/chapter_dynamic_programming/edit_distance_problem.md b/docs/chapter_dynamic_programming/edit_distance_problem.md index 3787167d8..2b000fbe9 100644 --- a/docs/chapter_dynamic_programming/edit_distance_problem.md +++ b/docs/chapter_dynamic_programming/edit_distance_problem.md @@ -14,7 +14,7 @@ comments: true 如图 14-27 所示,将 `kitten` 转换为 `sitting` 需要编辑 3 步,包括 2 次替换操作与 1 次添加操作;将 `hello` 转换为 `algo` 需要 3 步,包括 2 次替换操作和 1 次删除操作。 -![编辑距离的示例数据](edit_distance_problem.assets/edit_distance_example.png) +![编辑距离的示例数据](edit_distance_problem.assets/edit_distance_example.png){ class="animation-figure" }

图 14-27   编辑距离的示例数据

@@ -24,7 +24,7 @@ comments: true 从决策树的角度看,本题的目标是求解节点 `hello` 和节点 `algo` 之间的最短路径。 -![基于决策树模型表示编辑距离问题](edit_distance_problem.assets/edit_distance_decision_tree.png) +![基于决策树模型表示编辑距离问题](edit_distance_problem.assets/edit_distance_decision_tree.png){ class="animation-figure" }

图 14-28   基于决策树模型表示编辑距离问题

@@ -53,7 +53,7 @@ comments: true 2. 删除 $s[i-1]$ ,则剩余子问题 $dp[i-1, j]$ 。 3. 将 $s[i-1]$ 替换为 $t[j-1]$ ,则剩余子问题 $dp[i-1, j-1]$ 。 -![编辑距离的状态转移](edit_distance_problem.assets/edit_distance_state_transfer.png) +![编辑距离的状态转移](edit_distance_problem.assets/edit_distance_state_transfer.png){ class="animation-figure" }

图 14-29   编辑距离的状态转移

@@ -446,49 +446,49 @@ $$ 如图 14-30 所示,编辑距离问题的状态转移过程与背包问题非常类似,都可以看作是填写一个二维网格的过程。 === "<1>" - ![编辑距离的动态规划过程](edit_distance_problem.assets/edit_distance_dp_step1.png) + ![编辑距离的动态规划过程](edit_distance_problem.assets/edit_distance_dp_step1.png){ class="animation-figure" } === "<2>" - ![edit_distance_dp_step2](edit_distance_problem.assets/edit_distance_dp_step2.png) + ![edit_distance_dp_step2](edit_distance_problem.assets/edit_distance_dp_step2.png){ class="animation-figure" } === "<3>" - ![edit_distance_dp_step3](edit_distance_problem.assets/edit_distance_dp_step3.png) + ![edit_distance_dp_step3](edit_distance_problem.assets/edit_distance_dp_step3.png){ class="animation-figure" } === "<4>" - ![edit_distance_dp_step4](edit_distance_problem.assets/edit_distance_dp_step4.png) + ![edit_distance_dp_step4](edit_distance_problem.assets/edit_distance_dp_step4.png){ class="animation-figure" } === "<5>" - ![edit_distance_dp_step5](edit_distance_problem.assets/edit_distance_dp_step5.png) + ![edit_distance_dp_step5](edit_distance_problem.assets/edit_distance_dp_step5.png){ class="animation-figure" } === "<6>" - ![edit_distance_dp_step6](edit_distance_problem.assets/edit_distance_dp_step6.png) + ![edit_distance_dp_step6](edit_distance_problem.assets/edit_distance_dp_step6.png){ class="animation-figure" } === "<7>" - ![edit_distance_dp_step7](edit_distance_problem.assets/edit_distance_dp_step7.png) + ![edit_distance_dp_step7](edit_distance_problem.assets/edit_distance_dp_step7.png){ class="animation-figure" } === "<8>" - ![edit_distance_dp_step8](edit_distance_problem.assets/edit_distance_dp_step8.png) + ![edit_distance_dp_step8](edit_distance_problem.assets/edit_distance_dp_step8.png){ class="animation-figure" } === "<9>" - ![edit_distance_dp_step9](edit_distance_problem.assets/edit_distance_dp_step9.png) + ![edit_distance_dp_step9](edit_distance_problem.assets/edit_distance_dp_step9.png){ class="animation-figure" } === "<10>" - ![edit_distance_dp_step10](edit_distance_problem.assets/edit_distance_dp_step10.png) + ![edit_distance_dp_step10](edit_distance_problem.assets/edit_distance_dp_step10.png){ class="animation-figure" } === "<11>" - ![edit_distance_dp_step11](edit_distance_problem.assets/edit_distance_dp_step11.png) + ![edit_distance_dp_step11](edit_distance_problem.assets/edit_distance_dp_step11.png){ class="animation-figure" } === "<12>" - ![edit_distance_dp_step12](edit_distance_problem.assets/edit_distance_dp_step12.png) + ![edit_distance_dp_step12](edit_distance_problem.assets/edit_distance_dp_step12.png){ class="animation-figure" } === "<13>" - ![edit_distance_dp_step13](edit_distance_problem.assets/edit_distance_dp_step13.png) + ![edit_distance_dp_step13](edit_distance_problem.assets/edit_distance_dp_step13.png){ class="animation-figure" } === "<14>" - ![edit_distance_dp_step14](edit_distance_problem.assets/edit_distance_dp_step14.png) + ![edit_distance_dp_step14](edit_distance_problem.assets/edit_distance_dp_step14.png){ class="animation-figure" } === "<15>" - ![edit_distance_dp_step15](edit_distance_problem.assets/edit_distance_dp_step15.png) + ![edit_distance_dp_step15](edit_distance_problem.assets/edit_distance_dp_step15.png){ class="animation-figure" }

图 14-30   编辑距离的动态规划过程

diff --git a/docs/chapter_dynamic_programming/index.md b/docs/chapter_dynamic_programming/index.md index 94a1a980b..6bf09ad3c 100644 --- a/docs/chapter_dynamic_programming/index.md +++ b/docs/chapter_dynamic_programming/index.md @@ -7,7 +7,7 @@ icon: material/table-pivot
-![动态规划](../assets/covers/chapter_dynamic_programming.jpg){ width="600" } +![动态规划](../assets/covers/chapter_dynamic_programming.jpg){ class="cover-image" }
diff --git a/docs/chapter_dynamic_programming/intro_to_dynamic_programming.md b/docs/chapter_dynamic_programming/intro_to_dynamic_programming.md index f748370d5..af1c7c552 100644 --- a/docs/chapter_dynamic_programming/intro_to_dynamic_programming.md +++ b/docs/chapter_dynamic_programming/intro_to_dynamic_programming.md @@ -14,7 +14,7 @@ comments: true 如图 14-1 所示,对于一个 $3$ 阶楼梯,共有 $3$ 种方案可以爬到楼顶。 -![爬到第 3 阶的方案数量](intro_to_dynamic_programming.assets/climbing_stairs_example.png) +![爬到第 3 阶的方案数量](intro_to_dynamic_programming.assets/climbing_stairs_example.png){ class="animation-figure" }

图 14-1   爬到第 3 阶的方案数量

@@ -405,7 +405,7 @@ $$ 这意味着在爬楼梯问题中,各个子问题之间存在递推关系,**原问题的解可以由子问题的解构建得来**。图 14-2 展示了该递推关系。 -![方案数量递推关系](intro_to_dynamic_programming.assets/climbing_stairs_state_transfer.png) +![方案数量递推关系](intro_to_dynamic_programming.assets/climbing_stairs_state_transfer.png){ class="animation-figure" }

图 14-2   方案数量递推关系

@@ -640,7 +640,7 @@ $$ 图 14-3 展示了暴力搜索形成的递归树。对于问题 $dp[n]$ ,其递归树的深度为 $n$ ,时间复杂度为 $O(2^n)$ 。指数阶属于爆炸式增长,如果我们输入一个比较大的 $n$ ,则会陷入漫长的等待之中。 -![爬楼梯对应递归树](intro_to_dynamic_programming.assets/climbing_stairs_dfs_tree.png) +![爬楼梯对应递归树](intro_to_dynamic_programming.assets/climbing_stairs_dfs_tree.png){ class="animation-figure" }

图 14-3   爬楼梯对应递归树

@@ -975,7 +975,7 @@ $$ 观察图 14-4 ,**经过记忆化处理后,所有重叠子问题都只需被计算一次,时间复杂度被优化至 $O(n)$** ,这是一个巨大的飞跃。 -![记忆化搜索对应递归树](intro_to_dynamic_programming.assets/climbing_stairs_dfs_memo_tree.png) +![记忆化搜索对应递归树](intro_to_dynamic_programming.assets/climbing_stairs_dfs_memo_tree.png){ class="animation-figure" }

图 14-4   记忆化搜索对应递归树

@@ -1229,7 +1229,7 @@ $$ 图 14-5 模拟了以上代码的执行过程。 -![爬楼梯的动态规划过程](intro_to_dynamic_programming.assets/climbing_stairs_dp.png) +![爬楼梯的动态规划过程](intro_to_dynamic_programming.assets/climbing_stairs_dp.png){ class="animation-figure" }

图 14-5   爬楼梯的动态规划过程

diff --git a/docs/chapter_dynamic_programming/knapsack_problem.md b/docs/chapter_dynamic_programming/knapsack_problem.md index 5d11b04ab..27bd936fb 100644 --- a/docs/chapter_dynamic_programming/knapsack_problem.md +++ b/docs/chapter_dynamic_programming/knapsack_problem.md @@ -14,7 +14,7 @@ comments: true 观察图 14-17 ,由于物品编号 $i$ 从 $1$ 开始计数,数组索引从 $0$ 开始计数,因此物品 $i$ 对应重量 $wgt[i-1]$ 和价值 $val[i-1]$ 。 -![0-1 背包的示例数据](knapsack_problem.assets/knapsack_example.png) +![0-1 背包的示例数据](knapsack_problem.assets/knapsack_example.png){ class="animation-figure" }

图 14-17   0-1 背包的示例数据

@@ -320,7 +320,7 @@ $$ 观察递归树,容易发现其中存在重叠子问题,例如 $dp[1, 10]$ 等。而当物品较多、背包容量较大,尤其是相同重量的物品较多时,重叠子问题的数量将会大幅增多。 -![0-1 背包的暴力搜索递归树](knapsack_problem.assets/knapsack_dfs.png) +![0-1 背包的暴力搜索递归树](knapsack_problem.assets/knapsack_dfs.png){ class="animation-figure" }

图 14-18   0-1 背包的暴力搜索递归树

@@ -656,7 +656,7 @@ $$ 图 14-19 展示了在记忆化递归中被剪掉的搜索分支。 -![0-1 背包的记忆化搜索递归树](knapsack_problem.assets/knapsack_dfs_mem.png) +![0-1 背包的记忆化搜索递归树](knapsack_problem.assets/knapsack_dfs_mem.png){ class="animation-figure" }

图 14-19   0-1 背包的记忆化搜索递归树

@@ -969,46 +969,46 @@ $$ 如图 14-20 所示,时间复杂度和空间复杂度都由数组 `dp` 大小决定,即 $O(n \times cap)$ 。 === "<1>" - ![0-1 背包的动态规划过程](knapsack_problem.assets/knapsack_dp_step1.png) + ![0-1 背包的动态规划过程](knapsack_problem.assets/knapsack_dp_step1.png){ class="animation-figure" } === "<2>" - ![knapsack_dp_step2](knapsack_problem.assets/knapsack_dp_step2.png) + ![knapsack_dp_step2](knapsack_problem.assets/knapsack_dp_step2.png){ class="animation-figure" } === "<3>" - ![knapsack_dp_step3](knapsack_problem.assets/knapsack_dp_step3.png) + ![knapsack_dp_step3](knapsack_problem.assets/knapsack_dp_step3.png){ class="animation-figure" } === "<4>" - ![knapsack_dp_step4](knapsack_problem.assets/knapsack_dp_step4.png) + ![knapsack_dp_step4](knapsack_problem.assets/knapsack_dp_step4.png){ class="animation-figure" } === "<5>" - ![knapsack_dp_step5](knapsack_problem.assets/knapsack_dp_step5.png) + ![knapsack_dp_step5](knapsack_problem.assets/knapsack_dp_step5.png){ class="animation-figure" } === "<6>" - ![knapsack_dp_step6](knapsack_problem.assets/knapsack_dp_step6.png) + ![knapsack_dp_step6](knapsack_problem.assets/knapsack_dp_step6.png){ class="animation-figure" } === "<7>" - ![knapsack_dp_step7](knapsack_problem.assets/knapsack_dp_step7.png) + ![knapsack_dp_step7](knapsack_problem.assets/knapsack_dp_step7.png){ class="animation-figure" } === "<8>" - ![knapsack_dp_step8](knapsack_problem.assets/knapsack_dp_step8.png) + ![knapsack_dp_step8](knapsack_problem.assets/knapsack_dp_step8.png){ class="animation-figure" } === "<9>" - ![knapsack_dp_step9](knapsack_problem.assets/knapsack_dp_step9.png) + ![knapsack_dp_step9](knapsack_problem.assets/knapsack_dp_step9.png){ class="animation-figure" } === "<10>" - ![knapsack_dp_step10](knapsack_problem.assets/knapsack_dp_step10.png) + ![knapsack_dp_step10](knapsack_problem.assets/knapsack_dp_step10.png){ class="animation-figure" } === "<11>" - ![knapsack_dp_step11](knapsack_problem.assets/knapsack_dp_step11.png) + ![knapsack_dp_step11](knapsack_problem.assets/knapsack_dp_step11.png){ class="animation-figure" } === "<12>" - ![knapsack_dp_step12](knapsack_problem.assets/knapsack_dp_step12.png) + ![knapsack_dp_step12](knapsack_problem.assets/knapsack_dp_step12.png){ class="animation-figure" } === "<13>" - ![knapsack_dp_step13](knapsack_problem.assets/knapsack_dp_step13.png) + ![knapsack_dp_step13](knapsack_problem.assets/knapsack_dp_step13.png){ class="animation-figure" } === "<14>" - ![knapsack_dp_step14](knapsack_problem.assets/knapsack_dp_step14.png) + ![knapsack_dp_step14](knapsack_problem.assets/knapsack_dp_step14.png){ class="animation-figure" }

图 14-20   0-1 背包的动态规划过程

@@ -1024,22 +1024,22 @@ $$ 图 14-21 展示了在单个数组下从第 $i = 1$ 行转换至第 $i = 2$ 行的过程。请思考正序遍历和倒序遍历的区别。 === "<1>" - ![0-1 背包的空间优化后的动态规划过程](knapsack_problem.assets/knapsack_dp_comp_step1.png) + ![0-1 背包的空间优化后的动态规划过程](knapsack_problem.assets/knapsack_dp_comp_step1.png){ class="animation-figure" } === "<2>" - ![knapsack_dp_comp_step2](knapsack_problem.assets/knapsack_dp_comp_step2.png) + ![knapsack_dp_comp_step2](knapsack_problem.assets/knapsack_dp_comp_step2.png){ class="animation-figure" } === "<3>" - ![knapsack_dp_comp_step3](knapsack_problem.assets/knapsack_dp_comp_step3.png) + ![knapsack_dp_comp_step3](knapsack_problem.assets/knapsack_dp_comp_step3.png){ class="animation-figure" } === "<4>" - ![knapsack_dp_comp_step4](knapsack_problem.assets/knapsack_dp_comp_step4.png) + ![knapsack_dp_comp_step4](knapsack_problem.assets/knapsack_dp_comp_step4.png){ class="animation-figure" } === "<5>" - ![knapsack_dp_comp_step5](knapsack_problem.assets/knapsack_dp_comp_step5.png) + ![knapsack_dp_comp_step5](knapsack_problem.assets/knapsack_dp_comp_step5.png){ class="animation-figure" } === "<6>" - ![knapsack_dp_comp_step6](knapsack_problem.assets/knapsack_dp_comp_step6.png) + ![knapsack_dp_comp_step6](knapsack_problem.assets/knapsack_dp_comp_step6.png){ class="animation-figure" }

图 14-21   0-1 背包的空间优化后的动态规划过程

diff --git a/docs/chapter_dynamic_programming/unbounded_knapsack_problem.md b/docs/chapter_dynamic_programming/unbounded_knapsack_problem.md index 18bb9d9c1..7911a15e6 100644 --- a/docs/chapter_dynamic_programming/unbounded_knapsack_problem.md +++ b/docs/chapter_dynamic_programming/unbounded_knapsack_problem.md @@ -12,7 +12,7 @@ comments: true 给定 $n$ 个物品,第 $i$ 个物品的重量为 $wgt[i-1]$、价值为 $val[i-1]$ ,和一个容量为 $cap$ 的背包。**每个物品可以重复选取**,问在不超过背包容量下能放入物品的最大价值。 -![完全背包问题的示例数据](unbounded_knapsack_problem.assets/unbounded_knapsack_example.png) +![完全背包问题的示例数据](unbounded_knapsack_problem.assets/unbounded_knapsack_example.png){ class="animation-figure" }

图 14-22   完全背包问题的示例数据

@@ -347,22 +347,22 @@ $$ 这个遍历顺序与 0-1 背包正好相反。请借助图 14-23 来理解两者的区别。 === "<1>" - ![完全背包的空间优化后的动态规划过程](unbounded_knapsack_problem.assets/unbounded_knapsack_dp_comp_step1.png) + ![完全背包的空间优化后的动态规划过程](unbounded_knapsack_problem.assets/unbounded_knapsack_dp_comp_step1.png){ class="animation-figure" } === "<2>" - ![unbounded_knapsack_dp_comp_step2](unbounded_knapsack_problem.assets/unbounded_knapsack_dp_comp_step2.png) + ![unbounded_knapsack_dp_comp_step2](unbounded_knapsack_problem.assets/unbounded_knapsack_dp_comp_step2.png){ class="animation-figure" } === "<3>" - ![unbounded_knapsack_dp_comp_step3](unbounded_knapsack_problem.assets/unbounded_knapsack_dp_comp_step3.png) + ![unbounded_knapsack_dp_comp_step3](unbounded_knapsack_problem.assets/unbounded_knapsack_dp_comp_step3.png){ class="animation-figure" } === "<4>" - ![unbounded_knapsack_dp_comp_step4](unbounded_knapsack_problem.assets/unbounded_knapsack_dp_comp_step4.png) + ![unbounded_knapsack_dp_comp_step4](unbounded_knapsack_problem.assets/unbounded_knapsack_dp_comp_step4.png){ class="animation-figure" } === "<5>" - ![unbounded_knapsack_dp_comp_step5](unbounded_knapsack_problem.assets/unbounded_knapsack_dp_comp_step5.png) + ![unbounded_knapsack_dp_comp_step5](unbounded_knapsack_problem.assets/unbounded_knapsack_dp_comp_step5.png){ class="animation-figure" } === "<6>" - ![unbounded_knapsack_dp_comp_step6](unbounded_knapsack_problem.assets/unbounded_knapsack_dp_comp_step6.png) + ![unbounded_knapsack_dp_comp_step6](unbounded_knapsack_problem.assets/unbounded_knapsack_dp_comp_step6.png){ class="animation-figure" }

图 14-23   完全背包的空间优化后的动态规划过程

@@ -666,7 +666,7 @@ $$ 给定 $n$ 种硬币,第 $i$ 种硬币的面值为 $coins[i - 1]$ ,目标金额为 $amt$ ,**每种硬币可以重复选取**,问能够凑出目标金额的最少硬币个数。如果无法凑出目标金额则返回 $-1$ 。 -![零钱兑换问题的示例数据](unbounded_knapsack_problem.assets/coin_change_example.png) +![零钱兑换问题的示例数据](unbounded_knapsack_problem.assets/coin_change_example.png){ class="animation-figure" }

图 14-24   零钱兑换问题的示例数据

@@ -1070,49 +1070,49 @@ $$ 图 14-25 展示了零钱兑换的动态规划过程,和完全背包非常相似。 === "<1>" - ![零钱兑换问题的动态规划过程](unbounded_knapsack_problem.assets/coin_change_dp_step1.png) + ![零钱兑换问题的动态规划过程](unbounded_knapsack_problem.assets/coin_change_dp_step1.png){ class="animation-figure" } === "<2>" - ![coin_change_dp_step2](unbounded_knapsack_problem.assets/coin_change_dp_step2.png) + ![coin_change_dp_step2](unbounded_knapsack_problem.assets/coin_change_dp_step2.png){ class="animation-figure" } === "<3>" - ![coin_change_dp_step3](unbounded_knapsack_problem.assets/coin_change_dp_step3.png) + ![coin_change_dp_step3](unbounded_knapsack_problem.assets/coin_change_dp_step3.png){ class="animation-figure" } === "<4>" - ![coin_change_dp_step4](unbounded_knapsack_problem.assets/coin_change_dp_step4.png) + ![coin_change_dp_step4](unbounded_knapsack_problem.assets/coin_change_dp_step4.png){ class="animation-figure" } === "<5>" - ![coin_change_dp_step5](unbounded_knapsack_problem.assets/coin_change_dp_step5.png) + ![coin_change_dp_step5](unbounded_knapsack_problem.assets/coin_change_dp_step5.png){ class="animation-figure" } === "<6>" - ![coin_change_dp_step6](unbounded_knapsack_problem.assets/coin_change_dp_step6.png) + ![coin_change_dp_step6](unbounded_knapsack_problem.assets/coin_change_dp_step6.png){ class="animation-figure" } === "<7>" - ![coin_change_dp_step7](unbounded_knapsack_problem.assets/coin_change_dp_step7.png) + ![coin_change_dp_step7](unbounded_knapsack_problem.assets/coin_change_dp_step7.png){ class="animation-figure" } === "<8>" - ![coin_change_dp_step8](unbounded_knapsack_problem.assets/coin_change_dp_step8.png) + ![coin_change_dp_step8](unbounded_knapsack_problem.assets/coin_change_dp_step8.png){ class="animation-figure" } === "<9>" - ![coin_change_dp_step9](unbounded_knapsack_problem.assets/coin_change_dp_step9.png) + ![coin_change_dp_step9](unbounded_knapsack_problem.assets/coin_change_dp_step9.png){ class="animation-figure" } === "<10>" - ![coin_change_dp_step10](unbounded_knapsack_problem.assets/coin_change_dp_step10.png) + ![coin_change_dp_step10](unbounded_knapsack_problem.assets/coin_change_dp_step10.png){ class="animation-figure" } === "<11>" - ![coin_change_dp_step11](unbounded_knapsack_problem.assets/coin_change_dp_step11.png) + ![coin_change_dp_step11](unbounded_knapsack_problem.assets/coin_change_dp_step11.png){ class="animation-figure" } === "<12>" - ![coin_change_dp_step12](unbounded_knapsack_problem.assets/coin_change_dp_step12.png) + ![coin_change_dp_step12](unbounded_knapsack_problem.assets/coin_change_dp_step12.png){ class="animation-figure" } === "<13>" - ![coin_change_dp_step13](unbounded_knapsack_problem.assets/coin_change_dp_step13.png) + ![coin_change_dp_step13](unbounded_knapsack_problem.assets/coin_change_dp_step13.png){ class="animation-figure" } === "<14>" - ![coin_change_dp_step14](unbounded_knapsack_problem.assets/coin_change_dp_step14.png) + ![coin_change_dp_step14](unbounded_knapsack_problem.assets/coin_change_dp_step14.png){ class="animation-figure" } === "<15>" - ![coin_change_dp_step15](unbounded_knapsack_problem.assets/coin_change_dp_step15.png) + ![coin_change_dp_step15](unbounded_knapsack_problem.assets/coin_change_dp_step15.png){ class="animation-figure" }

图 14-25   零钱兑换问题的动态规划过程

@@ -1450,7 +1450,7 @@ $$ 给定 $n$ 种硬币,第 $i$ 种硬币的面值为 $coins[i - 1]$ ,目标金额为 $amt$ ,每种硬币可以重复选取,**问在凑出目标金额的硬币组合数量**。 -![零钱兑换问题 II 的示例数据](unbounded_knapsack_problem.assets/coin_change_ii_example.png) +![零钱兑换问题 II 的示例数据](unbounded_knapsack_problem.assets/coin_change_ii_example.png){ class="animation-figure" }

图 14-26   零钱兑换问题 II 的示例数据

diff --git a/docs/chapter_graph/graph.md b/docs/chapter_graph/graph.md index 1f48c475d..821462e97 100644 --- a/docs/chapter_graph/graph.md +++ b/docs/chapter_graph/graph.md @@ -16,7 +16,7 @@ $$ 如果将顶点看作节点,将边看作连接各个节点的引用(指针),我们就可以将图看作是一种从链表拓展而来的数据结构。如图 9-1 所示,**相较于线性关系(链表)和分治关系(树),网络关系(图)的自由度更高**,从而更为复杂。 -![链表、树、图之间的关系](graph.assets/linkedlist_tree_graph.png) +![链表、树、图之间的关系](graph.assets/linkedlist_tree_graph.png){ class="animation-figure" }

图 9-1   链表、树、图之间的关系

@@ -27,7 +27,7 @@ $$ - 在无向图中,边表示两顶点之间的“双向”连接关系,例如微信或 QQ 中的“好友关系”。 - 在有向图中,边具有方向性,即 $A \rightarrow B$ 和 $A \leftarrow B$ 两个方向的边是相互独立的,例如微博或抖音上的“关注”与“被关注”关系。 -![有向图与无向图](graph.assets/directed_graph.png) +![有向图与无向图](graph.assets/directed_graph.png){ class="animation-figure" }

图 9-2   有向图与无向图

@@ -36,13 +36,13 @@ $$ - 对于连通图,从某个顶点出发,可以到达其余任意顶点。 - 对于非连通图,从某个顶点出发,至少有一个顶点无法到达。 -![连通图与非连通图](graph.assets/connected_graph.png) +![连通图与非连通图](graph.assets/connected_graph.png){ class="animation-figure" }

图 9-3   连通图与非连通图

我们还可以为边添加“权重”变量,从而得到图 9-4 所示的「有权图 weighted graph」。例如在王者荣耀等手游中,系统会根据共同游戏时间来计算玩家之间的“亲密度”,这种亲密度网络就可以用有权图来表示。 -![有权图与无权图](graph.assets/weighted_graph.png) +![有权图与无权图](graph.assets/weighted_graph.png){ class="animation-figure" }

图 9-4   有权图与无权图

@@ -62,7 +62,7 @@ $$ 如图 9-5 所示,设邻接矩阵为 $M$、顶点列表为 $V$ ,那么矩阵元素 $M[i, j] = 1$ 表示顶点 $V[i]$ 到顶点 $V[j]$ 之间存在边,反之 $M[i, j] = 0$ 表示两顶点之间无边。 -![图的邻接矩阵表示](graph.assets/adjacency_matrix.png) +![图的邻接矩阵表示](graph.assets/adjacency_matrix.png){ class="animation-figure" }

图 9-5   图的邻接矩阵表示

@@ -78,7 +78,7 @@ $$ 「邻接表 adjacency list」使用 $n$ 个链表来表示图,链表节点表示顶点。第 $i$ 条链表对应顶点 $i$ ,其中存储了该顶点的所有邻接顶点(即与该顶点相连的顶点)。图 9-6 展示了一个使用邻接表存储的图的示例。 -![图的邻接表表示](graph.assets/adjacency_list.png) +![图的邻接表表示](graph.assets/adjacency_list.png){ class="animation-figure" }

图 9-6   图的邻接表表示

diff --git a/docs/chapter_graph/graph_operations.md b/docs/chapter_graph/graph_operations.md index a9f9d9995..eac245c92 100644 --- a/docs/chapter_graph/graph_operations.md +++ b/docs/chapter_graph/graph_operations.md @@ -16,19 +16,19 @@ comments: true - **初始化**:传入 $n$ 个顶点,初始化长度为 $n$ 的顶点列表 `vertices` ,使用 $O(n)$ 时间;初始化 $n \times n$ 大小的邻接矩阵 `adjMat` ,使用 $O(n^2)$ 时间。 === "初始化邻接矩阵" - ![邻接矩阵的初始化、增删边、增删顶点](graph_operations.assets/adjacency_matrix_initialization.png) + ![邻接矩阵的初始化、增删边、增删顶点](graph_operations.assets/adjacency_matrix_initialization.png){ class="animation-figure" } === "添加边" - ![adjacency_matrix_add_edge](graph_operations.assets/adjacency_matrix_add_edge.png) + ![adjacency_matrix_add_edge](graph_operations.assets/adjacency_matrix_add_edge.png){ class="animation-figure" } === "删除边" - ![adjacency_matrix_remove_edge](graph_operations.assets/adjacency_matrix_remove_edge.png) + ![adjacency_matrix_remove_edge](graph_operations.assets/adjacency_matrix_remove_edge.png){ class="animation-figure" } === "添加顶点" - ![adjacency_matrix_add_vertex](graph_operations.assets/adjacency_matrix_add_vertex.png) + ![adjacency_matrix_add_vertex](graph_operations.assets/adjacency_matrix_add_vertex.png){ class="animation-figure" } === "删除顶点" - ![adjacency_matrix_remove_vertex](graph_operations.assets/adjacency_matrix_remove_vertex.png) + ![adjacency_matrix_remove_vertex](graph_operations.assets/adjacency_matrix_remove_vertex.png){ class="animation-figure" }

图 9-7   邻接矩阵的初始化、增删边、增删顶点

@@ -1056,19 +1056,19 @@ comments: true - **初始化**:在邻接表中创建 $n$ 个顶点和 $2m$ 条边,使用 $O(n + m)$ 时间。 === "初始化邻接表" - ![邻接表的初始化、增删边、增删顶点](graph_operations.assets/adjacency_list_initialization.png) + ![邻接表的初始化、增删边、增删顶点](graph_operations.assets/adjacency_list_initialization.png){ class="animation-figure" } === "添加边" - ![adjacency_list_add_edge](graph_operations.assets/adjacency_list_add_edge.png) + ![adjacency_list_add_edge](graph_operations.assets/adjacency_list_add_edge.png){ class="animation-figure" } === "删除边" - ![adjacency_list_remove_edge](graph_operations.assets/adjacency_list_remove_edge.png) + ![adjacency_list_remove_edge](graph_operations.assets/adjacency_list_remove_edge.png){ class="animation-figure" } === "添加顶点" - ![adjacency_list_add_vertex](graph_operations.assets/adjacency_list_add_vertex.png) + ![adjacency_list_add_vertex](graph_operations.assets/adjacency_list_add_vertex.png){ class="animation-figure" } === "删除顶点" - ![adjacency_list_remove_vertex](graph_operations.assets/adjacency_list_remove_vertex.png) + ![adjacency_list_remove_vertex](graph_operations.assets/adjacency_list_remove_vertex.png){ class="animation-figure" }

图 9-8   邻接表的初始化、增删边、增删顶点

diff --git a/docs/chapter_graph/graph_traversal.md b/docs/chapter_graph/graph_traversal.md index f7d81635c..de511ff29 100644 --- a/docs/chapter_graph/graph_traversal.md +++ b/docs/chapter_graph/graph_traversal.md @@ -12,7 +12,7 @@ comments: true **广度优先遍历是一种由近及远的遍历方式,从某个节点出发,始终优先访问距离最近的顶点,并一层层向外扩张**。如图 9-9 所示,从左上角顶点出发,先遍历该顶点的所有邻接顶点,然后遍历下一个顶点的所有邻接顶点,以此类推,直至所有顶点访问完毕。 -![图的广度优先遍历](graph_traversal.assets/graph_bfs.png) +![图的广度优先遍历](graph_traversal.assets/graph_bfs.png){ class="animation-figure" }

图 9-9   图的广度优先遍历

@@ -421,37 +421,37 @@ BFS 通常借助队列来实现。队列具有“先入先出”的性质,这 代码相对抽象,建议对照图 9-10 来加深理解。 === "<1>" - ![图的广度优先遍历步骤](graph_traversal.assets/graph_bfs_step1.png) + ![图的广度优先遍历步骤](graph_traversal.assets/graph_bfs_step1.png){ class="animation-figure" } === "<2>" - ![graph_bfs_step2](graph_traversal.assets/graph_bfs_step2.png) + ![graph_bfs_step2](graph_traversal.assets/graph_bfs_step2.png){ class="animation-figure" } === "<3>" - ![graph_bfs_step3](graph_traversal.assets/graph_bfs_step3.png) + ![graph_bfs_step3](graph_traversal.assets/graph_bfs_step3.png){ class="animation-figure" } === "<4>" - ![graph_bfs_step4](graph_traversal.assets/graph_bfs_step4.png) + ![graph_bfs_step4](graph_traversal.assets/graph_bfs_step4.png){ class="animation-figure" } === "<5>" - ![graph_bfs_step5](graph_traversal.assets/graph_bfs_step5.png) + ![graph_bfs_step5](graph_traversal.assets/graph_bfs_step5.png){ class="animation-figure" } === "<6>" - ![graph_bfs_step6](graph_traversal.assets/graph_bfs_step6.png) + ![graph_bfs_step6](graph_traversal.assets/graph_bfs_step6.png){ class="animation-figure" } === "<7>" - ![graph_bfs_step7](graph_traversal.assets/graph_bfs_step7.png) + ![graph_bfs_step7](graph_traversal.assets/graph_bfs_step7.png){ class="animation-figure" } === "<8>" - ![graph_bfs_step8](graph_traversal.assets/graph_bfs_step8.png) + ![graph_bfs_step8](graph_traversal.assets/graph_bfs_step8.png){ class="animation-figure" } === "<9>" - ![graph_bfs_step9](graph_traversal.assets/graph_bfs_step9.png) + ![graph_bfs_step9](graph_traversal.assets/graph_bfs_step9.png){ class="animation-figure" } === "<10>" - ![graph_bfs_step10](graph_traversal.assets/graph_bfs_step10.png) + ![graph_bfs_step10](graph_traversal.assets/graph_bfs_step10.png){ class="animation-figure" } === "<11>" - ![graph_bfs_step11](graph_traversal.assets/graph_bfs_step11.png) + ![graph_bfs_step11](graph_traversal.assets/graph_bfs_step11.png){ class="animation-figure" }

图 9-10   图的广度优先遍历步骤

@@ -469,7 +469,7 @@ BFS 通常借助队列来实现。队列具有“先入先出”的性质,这 **深度优先遍历是一种优先走到底、无路可走再回头的遍历方式**。如图 9-11 所示,从左上角顶点出发,访问当前顶点的某个邻接顶点,直到走到尽头时返回,再继续走到尽头并返回,以此类推,直至所有顶点遍历完成。 -![图的深度优先遍历](graph_traversal.assets/graph_dfs.png) +![图的深度优先遍历](graph_traversal.assets/graph_dfs.png){ class="animation-figure" }

图 9-11   图的深度优先遍历

@@ -829,37 +829,37 @@ BFS 通常借助队列来实现。队列具有“先入先出”的性质,这 为了加深理解,建议将图示与代码结合起来,在脑中(或者用笔画下来)模拟整个 DFS 过程,包括每个递归方法何时开启、何时返回。 === "<1>" - ![图的深度优先遍历步骤](graph_traversal.assets/graph_dfs_step1.png) + ![图的深度优先遍历步骤](graph_traversal.assets/graph_dfs_step1.png){ class="animation-figure" } === "<2>" - ![graph_dfs_step2](graph_traversal.assets/graph_dfs_step2.png) + ![graph_dfs_step2](graph_traversal.assets/graph_dfs_step2.png){ class="animation-figure" } === "<3>" - ![graph_dfs_step3](graph_traversal.assets/graph_dfs_step3.png) + ![graph_dfs_step3](graph_traversal.assets/graph_dfs_step3.png){ class="animation-figure" } === "<4>" - ![graph_dfs_step4](graph_traversal.assets/graph_dfs_step4.png) + ![graph_dfs_step4](graph_traversal.assets/graph_dfs_step4.png){ class="animation-figure" } === "<5>" - ![graph_dfs_step5](graph_traversal.assets/graph_dfs_step5.png) + ![graph_dfs_step5](graph_traversal.assets/graph_dfs_step5.png){ class="animation-figure" } === "<6>" - ![graph_dfs_step6](graph_traversal.assets/graph_dfs_step6.png) + ![graph_dfs_step6](graph_traversal.assets/graph_dfs_step6.png){ class="animation-figure" } === "<7>" - ![graph_dfs_step7](graph_traversal.assets/graph_dfs_step7.png) + ![graph_dfs_step7](graph_traversal.assets/graph_dfs_step7.png){ class="animation-figure" } === "<8>" - ![graph_dfs_step8](graph_traversal.assets/graph_dfs_step8.png) + ![graph_dfs_step8](graph_traversal.assets/graph_dfs_step8.png){ class="animation-figure" } === "<9>" - ![graph_dfs_step9](graph_traversal.assets/graph_dfs_step9.png) + ![graph_dfs_step9](graph_traversal.assets/graph_dfs_step9.png){ class="animation-figure" } === "<10>" - ![graph_dfs_step10](graph_traversal.assets/graph_dfs_step10.png) + ![graph_dfs_step10](graph_traversal.assets/graph_dfs_step10.png){ class="animation-figure" } === "<11>" - ![graph_dfs_step11](graph_traversal.assets/graph_dfs_step11.png) + ![graph_dfs_step11](graph_traversal.assets/graph_dfs_step11.png){ class="animation-figure" }

图 9-12   图的深度优先遍历步骤

diff --git a/docs/chapter_graph/index.md b/docs/chapter_graph/index.md index 8cfb2957c..0c39cf6e2 100644 --- a/docs/chapter_graph/index.md +++ b/docs/chapter_graph/index.md @@ -7,7 +7,7 @@ icon: material/graphql
-![图](../assets/covers/chapter_graph.jpg){ width="600" } +![图](../assets/covers/chapter_graph.jpg){ class="cover-image" }
diff --git a/docs/chapter_greedy/fractional_knapsack_problem.md b/docs/chapter_greedy/fractional_knapsack_problem.md index 36d652350..e5bdc274c 100644 --- a/docs/chapter_greedy/fractional_knapsack_problem.md +++ b/docs/chapter_greedy/fractional_knapsack_problem.md @@ -8,7 +8,7 @@ comments: true 给定 $n$ 个物品,第 $i$ 个物品的重量为 $wgt[i-1]$、价值为 $val[i-1]$ ,和一个容量为 $cap$ 的背包。每个物品只能选择一次,**但可以选择物品的一部分,价值根据选择的重量比例计算**,问在不超过背包容量下背包中物品的最大价值。 -![分数背包问题的示例数据](fractional_knapsack_problem.assets/fractional_knapsack_example.png) +![分数背包问题的示例数据](fractional_knapsack_problem.assets/fractional_knapsack_example.png){ class="animation-figure" }

图 15-3   分数背包问题的示例数据

@@ -19,7 +19,7 @@ comments: true 1. 对于物品 $i$ ,它在单位重量下的价值为 $val[i-1] / wgt[i-1]$ ,简称为单位价值。 2. 假设放入一部分物品 $i$ ,重量为 $w$ ,则背包增加的价值为 $w \times val[i-1] / wgt[i-1]$ 。 -![物品在单位重量下的价值](fractional_knapsack_problem.assets/fractional_knapsack_unit_value.png) +![物品在单位重量下的价值](fractional_knapsack_problem.assets/fractional_knapsack_unit_value.png){ class="animation-figure" }

图 15-4   物品在单位重量下的价值

@@ -31,7 +31,7 @@ comments: true 2. 遍历所有物品,**每轮贪心地选择单位价值最高的物品**。 3. 若剩余背包容量不足,则使用当前物品的一部分填满背包即可。 -![分数背包的贪心策略](fractional_knapsack_problem.assets/fractional_knapsack_greedy_strategy.png) +![分数背包的贪心策略](fractional_knapsack_problem.assets/fractional_knapsack_greedy_strategy.png){ class="animation-figure" }

图 15-5   分数背包的贪心策略

@@ -483,6 +483,6 @@ comments: true 如图 15-6 所示,如果将物品重量和物品单位价值分别看作一个 2D 图表的横轴和纵轴,则分数背包问题可被转化为“求在有限横轴区间下的最大围成面积”。这个类比可以帮助我们从几何角度理解贪心策略的有效性。 -![分数背包问题的几何表示](fractional_knapsack_problem.assets/fractional_knapsack_area_chart.png) +![分数背包问题的几何表示](fractional_knapsack_problem.assets/fractional_knapsack_area_chart.png){ class="animation-figure" }

图 15-6   分数背包问题的几何表示

diff --git a/docs/chapter_greedy/greedy_algorithm.md b/docs/chapter_greedy/greedy_algorithm.md index 5ee3f6267..224ff58cf 100644 --- a/docs/chapter_greedy/greedy_algorithm.md +++ b/docs/chapter_greedy/greedy_algorithm.md @@ -19,7 +19,7 @@ comments: true 本题的贪心策略如图 15-1 所示。给定目标金额,**我们贪心地选择不大于且最接近它的硬币**,不断循环该步骤,直至凑出目标金额为止。 -![零钱兑换的贪心策略](greedy_algorithm.assets/coin_change_greedy_strategy.png) +![零钱兑换的贪心策略](greedy_algorithm.assets/coin_change_greedy_strategy.png){ class="animation-figure" }

图 15-1   零钱兑换的贪心策略

@@ -299,7 +299,7 @@ comments: true - **反例 $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){ class="animation-figure" }

图 15-2   贪心无法找出最优解的示例

diff --git a/docs/chapter_greedy/index.md b/docs/chapter_greedy/index.md index c790d14b3..92cde33cb 100644 --- a/docs/chapter_greedy/index.md +++ b/docs/chapter_greedy/index.md @@ -7,7 +7,7 @@ icon: material/head-heart-outline
-![贪心](../assets/covers/chapter_greedy.jpg){ width="600" } +![贪心](../assets/covers/chapter_greedy.jpg){ class="cover-image" }
diff --git a/docs/chapter_greedy/max_capacity_problem.md b/docs/chapter_greedy/max_capacity_problem.md index afa8b26ff..3ff4b5bdf 100644 --- a/docs/chapter_greedy/max_capacity_problem.md +++ b/docs/chapter_greedy/max_capacity_problem.md @@ -12,7 +12,7 @@ comments: true 请在数组中选择两个隔板,使得组成的容器的容量最大,返回最大容量。 -![最大容量问题的示例数据](max_capacity_problem.assets/max_capacity_example.png) +![最大容量问题的示例数据](max_capacity_problem.assets/max_capacity_example.png){ class="animation-figure" }

图 15-7   最大容量问题的示例数据

@@ -30,7 +30,7 @@ $$ 这道题还有更高效率的解法。如图 15-8 所示,现选取一个状态 $[i, j]$ ,其满足索引 $i < j$ 且高度 $ht[i] < ht[j]$ ,即 $i$ 为短板、$j$ 为长板。 -![初始状态](max_capacity_problem.assets/max_capacity_initial_state.png) +![初始状态](max_capacity_problem.assets/max_capacity_initial_state.png){ class="animation-figure" }

图 15-8   初始状态

@@ -38,13 +38,13 @@ $$ 这是因为在移动长板 $j$ 后,宽度 $j-i$ 肯定变小;而高度由短板决定,因此高度只可能不变( $i$ 仍为短板)或变小(移动后的 $j$ 成为短板)。 -![向内移动长板后的状态](max_capacity_problem.assets/max_capacity_moving_long_board.png) +![向内移动长板后的状态](max_capacity_problem.assets/max_capacity_moving_long_board.png){ class="animation-figure" }

图 15-9   向内移动长板后的状态

反向思考,**我们只有向内收缩短板 $i$ ,才有可能使容量变大**。因为虽然宽度一定变小,**但高度可能会变大**(移动后的短板 $i$ 可能会变长)。例如在图 15-10 中,移动短板后面积变大。 -![向内移动短板后的状态](max_capacity_problem.assets/max_capacity_moving_short_board.png) +![向内移动短板后的状态](max_capacity_problem.assets/max_capacity_moving_short_board.png){ class="animation-figure" }

图 15-10   向内移动短板后的状态

@@ -58,31 +58,31 @@ $$ 4. 循环执行第 `2.` 和 `3.` 步,直至 $i$ 和 $j$ 相遇时结束。 === "<1>" - ![最大容量问题的贪心过程](max_capacity_problem.assets/max_capacity_greedy_step1.png) + ![最大容量问题的贪心过程](max_capacity_problem.assets/max_capacity_greedy_step1.png){ class="animation-figure" } === "<2>" - ![max_capacity_greedy_step2](max_capacity_problem.assets/max_capacity_greedy_step2.png) + ![max_capacity_greedy_step2](max_capacity_problem.assets/max_capacity_greedy_step2.png){ class="animation-figure" } === "<3>" - ![max_capacity_greedy_step3](max_capacity_problem.assets/max_capacity_greedy_step3.png) + ![max_capacity_greedy_step3](max_capacity_problem.assets/max_capacity_greedy_step3.png){ class="animation-figure" } === "<4>" - ![max_capacity_greedy_step4](max_capacity_problem.assets/max_capacity_greedy_step4.png) + ![max_capacity_greedy_step4](max_capacity_problem.assets/max_capacity_greedy_step4.png){ class="animation-figure" } === "<5>" - ![max_capacity_greedy_step5](max_capacity_problem.assets/max_capacity_greedy_step5.png) + ![max_capacity_greedy_step5](max_capacity_problem.assets/max_capacity_greedy_step5.png){ class="animation-figure" } === "<6>" - ![max_capacity_greedy_step6](max_capacity_problem.assets/max_capacity_greedy_step6.png) + ![max_capacity_greedy_step6](max_capacity_problem.assets/max_capacity_greedy_step6.png){ class="animation-figure" } === "<7>" - ![max_capacity_greedy_step7](max_capacity_problem.assets/max_capacity_greedy_step7.png) + ![max_capacity_greedy_step7](max_capacity_problem.assets/max_capacity_greedy_step7.png){ class="animation-figure" } === "<8>" - ![max_capacity_greedy_step8](max_capacity_problem.assets/max_capacity_greedy_step8.png) + ![max_capacity_greedy_step8](max_capacity_problem.assets/max_capacity_greedy_step8.png){ class="animation-figure" } === "<9>" - ![max_capacity_greedy_step9](max_capacity_problem.assets/max_capacity_greedy_step9.png) + ![max_capacity_greedy_step9](max_capacity_problem.assets/max_capacity_greedy_step9.png){ class="animation-figure" }

图 15-11   最大容量问题的贪心过程

@@ -384,7 +384,7 @@ $$ cap[i, i+1], cap[i, i+2], \dots, cap[i, j-2], cap[i, j-1] $$ -![移动短板导致被跳过的状态](max_capacity_problem.assets/max_capacity_skipped_states.png) +![移动短板导致被跳过的状态](max_capacity_problem.assets/max_capacity_skipped_states.png){ class="animation-figure" }

图 15-12   移动短板导致被跳过的状态

diff --git a/docs/chapter_greedy/max_product_cutting_problem.md b/docs/chapter_greedy/max_product_cutting_problem.md index a5c9126be..db63e28a8 100644 --- a/docs/chapter_greedy/max_product_cutting_problem.md +++ b/docs/chapter_greedy/max_product_cutting_problem.md @@ -8,7 +8,7 @@ comments: true 给定一个正整数 $n$ ,将其切分为至少两个正整数的和,求切分后所有整数的乘积最大是多少。 -![最大切分乘积的问题定义](max_product_cutting_problem.assets/max_product_cutting_definition.png) +![最大切分乘积的问题定义](max_product_cutting_problem.assets/max_product_cutting_definition.png){ class="animation-figure" }

图 15-13   最大切分乘积的问题定义

@@ -42,7 +42,7 @@ $$ **贪心策略一**:如果切分方案中包含 $\geq 4$ 的因子,那么它就应该被继续切分。最终的切分方案只应出现 $1$、$2$、$3$ 这三种因子。 -![切分导致乘积变大](max_product_cutting_problem.assets/max_product_cutting_greedy_infer1.png) +![切分导致乘积变大](max_product_cutting_problem.assets/max_product_cutting_greedy_infer1.png){ class="animation-figure" }

图 15-14   切分导致乘积变大

@@ -52,7 +52,7 @@ $$ **贪心策略二**:在切分方案中,最多只应存在两个 $2$ 。因为三个 $2$ 总是可以被替换为两个 $3$ ,从而获得更大乘积。 -![最优切分因子](max_product_cutting_problem.assets/max_product_cutting_greedy_infer2.png) +![最优切分因子](max_product_cutting_problem.assets/max_product_cutting_greedy_infer2.png){ class="animation-figure" }

图 15-15   最优切分因子

@@ -349,7 +349,7 @@ $$ [class]{}-[func]{maxProductCutting} ``` -![最大切分乘积的计算方法](max_product_cutting_problem.assets/max_product_cutting_greedy_calculation.png) +![最大切分乘积的计算方法](max_product_cutting_problem.assets/max_product_cutting_greedy_calculation.png){ class="animation-figure" }

图 15-16   最大切分乘积的计算方法

diff --git a/docs/chapter_hashing/hash_algorithm.md b/docs/chapter_hashing/hash_algorithm.md index c858c569d..2b87dd47c 100644 --- a/docs/chapter_hashing/hash_algorithm.md +++ b/docs/chapter_hashing/hash_algorithm.md @@ -8,7 +8,7 @@ comments: true 如果哈希冲突过于频繁,哈希表的性能则会急剧劣化。如图 6-8 所示,对于链地址哈希表,理想情况下键值对平均分布在各个桶中,达到最佳查询效率;最差情况下所有键值对都被存储到同一个桶中,时间复杂度退化至 $O(n)$ 。 -![哈希冲突的最佳与最差情况](hash_algorithm.assets/hash_collision_best_worst_condition.png) +![哈希冲突的最佳与最差情况](hash_algorithm.assets/hash_collision_best_worst_condition.png){ class="animation-figure" }

图 6-8   哈希冲突的最佳与最差情况

diff --git a/docs/chapter_hashing/hash_collision.md b/docs/chapter_hashing/hash_collision.md index 4b72ff63e..77399b2d3 100644 --- a/docs/chapter_hashing/hash_collision.md +++ b/docs/chapter_hashing/hash_collision.md @@ -17,7 +17,7 @@ comments: true 在原始哈希表中,每个桶仅能存储一个键值对。「链式地址 separate chaining」将单个元素转换为链表,将键值对作为链表节点,将所有发生冲突的键值对都存储在同一链表中。图 6-5 展示了一个链式地址哈希表的例子。 -![链式地址哈希表](hash_collision.assets/hash_table_chaining.png) +![链式地址哈希表](hash_collision.assets/hash_table_chaining.png){ class="animation-figure" }

图 6-5   链式地址哈希表

@@ -1333,7 +1333,7 @@ comments: true 图 6-6 展示了开放寻址(线性探测)哈希表的键值对分布。根据此哈希函数,最后两位相同的 `key` 都会被映射到相同的桶。而通过线性探测,它们被依次存储在该桶以及之下的桶中。 -![开放寻址和线性探测](hash_collision.assets/hash_table_linear_probing.png) +![开放寻址和线性探测](hash_collision.assets/hash_table_linear_probing.png){ class="animation-figure" }

图 6-6   开放寻址和线性探测

@@ -1341,7 +1341,7 @@ comments: true 值得注意的是,**我们不能在开放寻址哈希表中直接删除元素**。这是因为删除元素会在数组内产生一个空桶 $\text{None}$ ,而当查询元素时,线性探测到该空桶就会返回,因此在该空桶之下的元素都无法再被访问到,程序可能误判这些元素不存在。 -![在开放寻址中删除元素导致的查询问题](hash_collision.assets/hash_table_open_addressing_deletion.png) +![在开放寻址中删除元素导致的查询问题](hash_collision.assets/hash_table_open_addressing_deletion.png){ class="animation-figure" }

图 6-7   在开放寻址中删除元素导致的查询问题

diff --git a/docs/chapter_hashing/hash_map.md b/docs/chapter_hashing/hash_map.md index da1121a77..15a8134e7 100755 --- a/docs/chapter_hashing/hash_map.md +++ b/docs/chapter_hashing/hash_map.md @@ -8,7 +8,7 @@ comments: true 如图 6-1 所示,给定 $n$ 个学生,每个学生都有“姓名”和“学号”两项数据。假如我们希望实现“输入一个学号,返回对应的姓名”的查询功能,则可以采用图 6-1 所示的哈希表来实现。 -![哈希表的抽象表示](hash_map.assets/hash_table_lookup.png) +![哈希表的抽象表示](hash_map.assets/hash_table_lookup.png){ class="animation-figure" }

图 6-1   哈希表的抽象表示

@@ -493,7 +493,7 @@ index = hash(key) % capacity 设数组长度 `capacity = 100`、哈希算法 `hash(key) = key` ,易得哈希函数为 `key % 100` 。图 6-2 以 `key` 学号和 `value` 姓名为例,展示了哈希函数的工作原理。 -![哈希函数工作原理](hash_map.assets/hash_function.png) +![哈希函数工作原理](hash_map.assets/hash_function.png){ class="animation-figure" }

图 6-2   哈希函数工作原理

@@ -1650,7 +1650,7 @@ index = hash(key) % capacity 如图 6-3 所示,两个学号指向了同一个姓名,这显然是不对的。我们将这种多个输入对应同一输出的情况称为「哈希冲突 hash collision」。 -![哈希冲突示例](hash_map.assets/hash_collision.png) +![哈希冲突示例](hash_map.assets/hash_collision.png){ class="animation-figure" }

图 6-3   哈希冲突示例

@@ -1658,7 +1658,7 @@ index = hash(key) % capacity 如图 6-4 所示,扩容前键值对 `(136, A)` 和 `(236, D)` 发生冲突,扩容后冲突消失。 -![哈希表扩容](hash_map.assets/hash_table_reshash.png) +![哈希表扩容](hash_map.assets/hash_table_reshash.png){ class="animation-figure" }

图 6-4   哈希表扩容

diff --git a/docs/chapter_hashing/index.md b/docs/chapter_hashing/index.md index a8de2bc16..6ae86f54e 100644 --- a/docs/chapter_hashing/index.md +++ b/docs/chapter_hashing/index.md @@ -7,7 +7,7 @@ icon: material/table-search
-![哈希表](../assets/covers/chapter_hashing.jpg){ width="600" } +![哈希表](../assets/covers/chapter_hashing.jpg){ class="cover-image" }
diff --git a/docs/chapter_heap/build_heap.md b/docs/chapter_heap/build_heap.md index ca973e2df..4e9496b71 100644 --- a/docs/chapter_heap/build_heap.md +++ b/docs/chapter_heap/build_heap.md @@ -213,7 +213,7 @@ comments: true 接下来我们来进行更为准确的计算。为了减小计算难度,假设给定一个节点数量为 $n$ ,高度为 $h$ 的“完美二叉树”,该假设不会影响计算结果的正确性。 -![完美二叉树的各层节点数量](build_heap.assets/heapify_operations_count.png) +![完美二叉树的各层节点数量](build_heap.assets/heapify_operations_count.png){ class="animation-figure" }

图 8-5   完美二叉树的各层节点数量

diff --git a/docs/chapter_heap/heap.md b/docs/chapter_heap/heap.md index 2418434f9..e9f5493d8 100644 --- a/docs/chapter_heap/heap.md +++ b/docs/chapter_heap/heap.md @@ -9,7 +9,7 @@ comments: true - 「大顶堆 max heap」:任意节点的值 $\geq$ 其子节点的值。 - 「小顶堆 min heap」:任意节点的值 $\leq$ 其子节点的值。 -![小顶堆与大顶堆](heap.assets/min_heap_and_max_heap.png) +![小顶堆与大顶堆](heap.assets/min_heap_and_max_heap.png){ class="animation-figure" }

图 8-1   小顶堆与大顶堆

@@ -367,7 +367,7 @@ comments: true 如图 8-2 所示,给定索引 $i$ ,其左子节点索引为 $2i + 1$ ,右子节点索引为 $2i + 2$ ,父节点索引为 $(i - 1) / 2$(向下取整)。当索引越界时,表示空节点或节点不存在。 -![堆的表示与存储](heap.assets/representation_of_heap.png) +![堆的表示与存储](heap.assets/representation_of_heap.png){ class="animation-figure" }

图 8-2   堆的表示与存储

@@ -718,31 +718,31 @@ comments: true 考虑从入堆节点开始,**从底至顶执行堆化**。如图 8-3 所示,我们比较插入节点与其父节点的值,如果插入节点更大,则将它们交换。然后继续执行此操作,从底至顶修复堆中的各个节点,直至越过根节点或遇到无须交换的节点时结束。 === "<1>" - ![元素入堆步骤](heap.assets/heap_push_step1.png) + ![元素入堆步骤](heap.assets/heap_push_step1.png){ class="animation-figure" } === "<2>" - ![heap_push_step2](heap.assets/heap_push_step2.png) + ![heap_push_step2](heap.assets/heap_push_step2.png){ class="animation-figure" } === "<3>" - ![heap_push_step3](heap.assets/heap_push_step3.png) + ![heap_push_step3](heap.assets/heap_push_step3.png){ class="animation-figure" } === "<4>" - ![heap_push_step4](heap.assets/heap_push_step4.png) + ![heap_push_step4](heap.assets/heap_push_step4.png){ class="animation-figure" } === "<5>" - ![heap_push_step5](heap.assets/heap_push_step5.png) + ![heap_push_step5](heap.assets/heap_push_step5.png){ class="animation-figure" } === "<6>" - ![heap_push_step6](heap.assets/heap_push_step6.png) + ![heap_push_step6](heap.assets/heap_push_step6.png){ class="animation-figure" } === "<7>" - ![heap_push_step7](heap.assets/heap_push_step7.png) + ![heap_push_step7](heap.assets/heap_push_step7.png){ class="animation-figure" } === "<8>" - ![heap_push_step8](heap.assets/heap_push_step8.png) + ![heap_push_step8](heap.assets/heap_push_step8.png){ class="animation-figure" } === "<9>" - ![heap_push_step9](heap.assets/heap_push_step9.png) + ![heap_push_step9](heap.assets/heap_push_step9.png){ class="animation-figure" }

图 8-3   元素入堆步骤

@@ -1095,34 +1095,34 @@ comments: true 如图 8-4 所示,**“从顶至底堆化”的操作方向与“从底至顶堆化”相反**,我们将根节点的值与其两个子节点的值进行比较,将最大的子节点与根节点交换。然后循环执行此操作,直到越过叶节点或遇到无须交换的节点时结束。 === "<1>" - ![堆顶元素出堆步骤](heap.assets/heap_pop_step1.png) + ![堆顶元素出堆步骤](heap.assets/heap_pop_step1.png){ class="animation-figure" } === "<2>" - ![heap_pop_step2](heap.assets/heap_pop_step2.png) + ![heap_pop_step2](heap.assets/heap_pop_step2.png){ class="animation-figure" } === "<3>" - ![heap_pop_step3](heap.assets/heap_pop_step3.png) + ![heap_pop_step3](heap.assets/heap_pop_step3.png){ class="animation-figure" } === "<4>" - ![heap_pop_step4](heap.assets/heap_pop_step4.png) + ![heap_pop_step4](heap.assets/heap_pop_step4.png){ class="animation-figure" } === "<5>" - ![heap_pop_step5](heap.assets/heap_pop_step5.png) + ![heap_pop_step5](heap.assets/heap_pop_step5.png){ class="animation-figure" } === "<6>" - ![heap_pop_step6](heap.assets/heap_pop_step6.png) + ![heap_pop_step6](heap.assets/heap_pop_step6.png){ class="animation-figure" } === "<7>" - ![heap_pop_step7](heap.assets/heap_pop_step7.png) + ![heap_pop_step7](heap.assets/heap_pop_step7.png){ class="animation-figure" } === "<8>" - ![heap_pop_step8](heap.assets/heap_pop_step8.png) + ![heap_pop_step8](heap.assets/heap_pop_step8.png){ class="animation-figure" } === "<9>" - ![heap_pop_step9](heap.assets/heap_pop_step9.png) + ![heap_pop_step9](heap.assets/heap_pop_step9.png){ class="animation-figure" } === "<10>" - ![heap_pop_step10](heap.assets/heap_pop_step10.png) + ![heap_pop_step10](heap.assets/heap_pop_step10.png){ class="animation-figure" }

图 8-4   堆顶元素出堆步骤

diff --git a/docs/chapter_heap/index.md b/docs/chapter_heap/index.md index d42cc1f8b..5210d70cf 100644 --- a/docs/chapter_heap/index.md +++ b/docs/chapter_heap/index.md @@ -7,7 +7,7 @@ icon: material/family-tree
-![堆](../assets/covers/chapter_heap.jpg){ width="600" } +![堆](../assets/covers/chapter_heap.jpg){ class="cover-image" }
diff --git a/docs/chapter_heap/top_k.md b/docs/chapter_heap/top_k.md index e8cd17df0..f84685a4f 100644 --- a/docs/chapter_heap/top_k.md +++ b/docs/chapter_heap/top_k.md @@ -16,7 +16,7 @@ comments: true 此方法只适用于 $k \ll n$ 的情况,因为当 $k$ 与 $n$ 比较接近时,其时间复杂度趋向于 $O(n^2)$ ,非常耗时。 -![遍历寻找最大的 k 个元素](top_k.assets/top_k_traversal.png) +![遍历寻找最大的 k 个元素](top_k.assets/top_k_traversal.png){ class="animation-figure" }

图 8-6   遍历寻找最大的 k 个元素

@@ -30,7 +30,7 @@ comments: true 显然,该方法“超额”完成任务了,因为我们只需要找出最大的 $k$ 个元素即可,而不需要排序其他元素。 -![排序寻找最大的 k 个元素](top_k.assets/top_k_sorting.png) +![排序寻找最大的 k 个元素](top_k.assets/top_k_sorting.png){ class="animation-figure" }

图 8-7   排序寻找最大的 k 个元素

@@ -44,31 +44,31 @@ comments: true 4. 遍历完成后,堆中保存的就是最大的 $k$ 个元素。 === "<1>" - ![基于堆寻找最大的 k 个元素](top_k.assets/top_k_heap_step1.png) + ![基于堆寻找最大的 k 个元素](top_k.assets/top_k_heap_step1.png){ class="animation-figure" } === "<2>" - ![top_k_heap_step2](top_k.assets/top_k_heap_step2.png) + ![top_k_heap_step2](top_k.assets/top_k_heap_step2.png){ class="animation-figure" } === "<3>" - ![top_k_heap_step3](top_k.assets/top_k_heap_step3.png) + ![top_k_heap_step3](top_k.assets/top_k_heap_step3.png){ class="animation-figure" } === "<4>" - ![top_k_heap_step4](top_k.assets/top_k_heap_step4.png) + ![top_k_heap_step4](top_k.assets/top_k_heap_step4.png){ class="animation-figure" } === "<5>" - ![top_k_heap_step5](top_k.assets/top_k_heap_step5.png) + ![top_k_heap_step5](top_k.assets/top_k_heap_step5.png){ class="animation-figure" } === "<6>" - ![top_k_heap_step6](top_k.assets/top_k_heap_step6.png) + ![top_k_heap_step6](top_k.assets/top_k_heap_step6.png){ class="animation-figure" } === "<7>" - ![top_k_heap_step7](top_k.assets/top_k_heap_step7.png) + ![top_k_heap_step7](top_k.assets/top_k_heap_step7.png){ class="animation-figure" } === "<8>" - ![top_k_heap_step8](top_k.assets/top_k_heap_step8.png) + ![top_k_heap_step8](top_k.assets/top_k_heap_step8.png){ class="animation-figure" } === "<9>" - ![top_k_heap_step9](top_k.assets/top_k_heap_step9.png) + ![top_k_heap_step9](top_k.assets/top_k_heap_step9.png){ class="animation-figure" }

图 8-8   基于堆寻找最大的 k 个元素

diff --git a/docs/chapter_introduction/algorithms_are_everywhere.md b/docs/chapter_introduction/algorithms_are_everywhere.md index 59401608f..47ed5eef5 100644 --- a/docs/chapter_introduction/algorithms_are_everywhere.md +++ b/docs/chapter_introduction/algorithms_are_everywhere.md @@ -15,19 +15,19 @@ comments: true 3. 不断重复步骤 `1.` 和 步骤 `2.` ,直至找到拼音首字母为 $r$ 的页码为止。 === "<1>" - ![查字典步骤](algorithms_are_everywhere.assets/binary_search_dictionary_step1.png) + ![查字典步骤](algorithms_are_everywhere.assets/binary_search_dictionary_step1.png){ class="animation-figure" } === "<2>" - ![binary_search_dictionary_step2](algorithms_are_everywhere.assets/binary_search_dictionary_step2.png) + ![binary_search_dictionary_step2](algorithms_are_everywhere.assets/binary_search_dictionary_step2.png){ class="animation-figure" } === "<3>" - ![binary_search_dictionary_step3](algorithms_are_everywhere.assets/binary_search_dictionary_step3.png) + ![binary_search_dictionary_step3](algorithms_are_everywhere.assets/binary_search_dictionary_step3.png){ class="animation-figure" } === "<4>" - ![binary_search_dictionary_step4](algorithms_are_everywhere.assets/binary_search_dictionary_step4.png) + ![binary_search_dictionary_step4](algorithms_are_everywhere.assets/binary_search_dictionary_step4.png){ class="animation-figure" } === "<5>" - ![binary_search_dictionary_step5](algorithms_are_everywhere.assets/binary_search_dictionary_step5.png) + ![binary_search_dictionary_step5](algorithms_are_everywhere.assets/binary_search_dictionary_step5.png){ class="animation-figure" }

图 1-1   查字典步骤

@@ -39,7 +39,7 @@ comments: true 2. 在无序部分抽出一张扑克牌,插入至有序部分的正确位置;完成后最左 2 张扑克已经有序。 3. 不断循环步骤 `2.` ,每一轮将一张扑克牌从无序部分插入至有序部分,直至所有扑克牌都有序。 -![扑克排序步骤](algorithms_are_everywhere.assets/playing_cards_sorting.png) +![扑克排序步骤](algorithms_are_everywhere.assets/playing_cards_sorting.png){ class="animation-figure" }

图 1-2   扑克排序步骤

@@ -53,7 +53,7 @@ comments: true 4. 从剩余可选项中拿出最大的 $1$ 元,剩余 $1 - 1 = 0$ 元。 5. 完成找零,方案为 $20 + 10 + 1 = 31$ 元。 -![货币找零过程](algorithms_are_everywhere.assets/greedy_change.png) +![货币找零过程](algorithms_are_everywhere.assets/greedy_change.png){ class="animation-figure" }

图 1-3   货币找零过程

diff --git a/docs/chapter_introduction/index.md b/docs/chapter_introduction/index.md index 2fe164f0d..166543d0b 100644 --- a/docs/chapter_introduction/index.md +++ b/docs/chapter_introduction/index.md @@ -7,7 +7,7 @@ icon: material/calculator-variant-outline
-![初识算法](../assets/covers/chapter_introduction.jpg){ width="600" } +![初识算法](../assets/covers/chapter_introduction.jpg){ class="cover-image" }
diff --git a/docs/chapter_introduction/what_is_dsa.md b/docs/chapter_introduction/what_is_dsa.md index d417f7fad..690520a5e 100644 --- a/docs/chapter_introduction/what_is_dsa.md +++ b/docs/chapter_introduction/what_is_dsa.md @@ -33,13 +33,13 @@ comments: true - 算法是数据结构发挥作用的舞台。数据结构本身仅存储数据信息,结合算法才能解决特定问题。 - 算法通常可以基于不同的数据结构进行实现,但执行效率可能相差很大,选择合适的数据结构是关键。 -![数据结构与算法的关系](what_is_dsa.assets/relationship_between_data_structure_and_algorithm.png) +![数据结构与算法的关系](what_is_dsa.assets/relationship_between_data_structure_and_algorithm.png){ class="animation-figure" }

图 1-4   数据结构与算法的关系

数据结构与算法犹如图 1-5 所示的拼装积木。一套积木,除了包含许多零件之外,还附有详细的组装说明书。我们按照说明书一步步操作,就能组装出精美的积木模型。 -![拼装积木](what_is_dsa.assets/assembling_blocks.png) +![拼装积木](what_is_dsa.assets/assembling_blocks.png){ class="animation-figure" }

图 1-5   拼装积木

diff --git a/docs/chapter_preface/about_the_book.md b/docs/chapter_preface/about_the_book.md index b6d1441af..fac42a031 100644 --- a/docs/chapter_preface/about_the_book.md +++ b/docs/chapter_preface/about_the_book.md @@ -30,7 +30,7 @@ comments: true - **数据结构**:基本数据类型,数据结构的分类方法。数组、链表、栈、队列、哈希表、树、堆、图等数据结构的定义、优缺点、常用操作、常见类型、典型应用、实现方法等。 - **算法**:搜索、排序、分治、回溯、动态规划、贪心等算法的定义、优缺点、效率、应用场景、解题步骤、示例题目等。 -![Hello 算法内容结构](about_the_book.assets/hello_algo_mindmap.jpg) +![Hello 算法内容结构](about_the_book.assets/hello_algo_mindmap.jpg){ class="animation-figure" }

图 0-1   Hello 算法内容结构

diff --git a/docs/chapter_preface/index.md b/docs/chapter_preface/index.md index f45ef77dd..92b7a55fa 100644 --- a/docs/chapter_preface/index.md +++ b/docs/chapter_preface/index.md @@ -7,7 +7,7 @@ icon: material/book-open-outline
-![前言](../assets/covers/chapter_preface.jpg){ width="600" } +![前言](../assets/covers/chapter_preface.jpg){ class="cover-image" }
diff --git a/docs/chapter_preface/suggestions.md b/docs/chapter_preface/suggestions.md index bec3065fe..58d5b28da 100644 --- a/docs/chapter_preface/suggestions.md +++ b/docs/chapter_preface/suggestions.md @@ -177,7 +177,7 @@ comments: true 如果你在阅读本书时,发现某段内容提供了图 0-2 所示的动画或图解,**请以图为主、以文字为辅**,综合两者来理解内容。 -![动画图解示例](../index.assets/animation.gif) +![动画图解示例](../index.assets/animation.gif){ class="animation-figure" }

图 0-2   动画图解示例

@@ -189,7 +189,7 @@ comments: true 与阅读代码相比,编写代码的过程往往能带来更多收获。**动手学,才是真的学**。 -![运行代码示例](../index.assets/running_code.gif) +![运行代码示例](../index.assets/running_code.gif){ class="animation-figure" }

图 0-3   运行代码示例

@@ -205,13 +205,13 @@ git clone https://github.com/krahets/hello-algo.git 当然,你也可以在图 0-4 所示的位置,点击“Download ZIP”直接下载代码压缩包,然后在本地解压即可。 -![克隆仓库与下载代码](suggestions.assets/download_code.png) +![克隆仓库与下载代码](suggestions.assets/download_code.png){ class="animation-figure" }

图 0-4   克隆仓库与下载代码

**第三步:运行源代码**。如图 0-5 所示,对于顶部标有文件名称的代码块,我们可以在仓库的 `codes` 文件夹内找到对应的源代码文件。源代码文件可一键运行,将帮助你节省不必要的调试时间,让你能够专注于学习内容。 -![代码块与对应的源代码文件](suggestions.assets/code_md_to_repo.png) +![代码块与对应的源代码文件](suggestions.assets/code_md_to_repo.png){ class="animation-figure" }

图 0-5   代码块与对应的源代码文件

@@ -221,7 +221,7 @@ git clone https://github.com/krahets/hello-algo.git 如图 0-6 所示,每篇文章的底部都配有评论区。希望你能多关注评论区的内容。一方面,你可以了解大家遇到的问题,从而查漏补缺,激发更深入的思考。另一方面,期待你能慷慨地回答其他小伙伴的问题,分享您的见解,帮助他人进步。 -![评论区示例](../index.assets/comment.gif) +![评论区示例](../index.assets/comment.gif){ class="animation-figure" }

图 0-6   评论区示例

@@ -235,6 +235,6 @@ git clone https://github.com/krahets/hello-algo.git 如图 0-7 所示,本书内容主要涵盖“第一阶段”,旨在帮助你更高效地展开第二和第三阶段的学习。 -![算法学习路线](suggestions.assets/learning_route.png) +![算法学习路线](suggestions.assets/learning_route.png){ class="animation-figure" }

图 0-7   算法学习路线

diff --git a/docs/chapter_searching/binary_search.md b/docs/chapter_searching/binary_search.md index a02120f57..37a115d87 100755 --- a/docs/chapter_searching/binary_search.md +++ b/docs/chapter_searching/binary_search.md @@ -10,7 +10,7 @@ comments: true 给定一个长度为 $n$ 的数组 `nums` ,元素按从小到大的顺序排列,数组不包含重复元素。请查找并返回元素 `target` 在该数组中的索引。若数组不包含该元素,则返回 $-1$ 。 -![二分查找示例数据](binary_search.assets/binary_search_example.png) +![二分查找示例数据](binary_search.assets/binary_search_example.png){ class="animation-figure" }

图 10-1   二分查找示例数据

@@ -27,25 +27,25 @@ comments: true 若数组不包含目标元素,搜索区间最终会缩小为空。此时返回 $-1$ 。 === "<1>" - ![二分查找流程](binary_search.assets/binary_search_step1.png) + ![二分查找流程](binary_search.assets/binary_search_step1.png){ class="animation-figure" } === "<2>" - ![binary_search_step2](binary_search.assets/binary_search_step2.png) + ![binary_search_step2](binary_search.assets/binary_search_step2.png){ class="animation-figure" } === "<3>" - ![binary_search_step3](binary_search.assets/binary_search_step3.png) + ![binary_search_step3](binary_search.assets/binary_search_step3.png){ class="animation-figure" } === "<4>" - ![binary_search_step4](binary_search.assets/binary_search_step4.png) + ![binary_search_step4](binary_search.assets/binary_search_step4.png){ class="animation-figure" } === "<5>" - ![binary_search_step5](binary_search.assets/binary_search_step5.png) + ![binary_search_step5](binary_search.assets/binary_search_step5.png){ class="animation-figure" } === "<6>" - ![binary_search_step6](binary_search.assets/binary_search_step6.png) + ![binary_search_step6](binary_search.assets/binary_search_step6.png){ class="animation-figure" } === "<7>" - ![binary_search_step7](binary_search.assets/binary_search_step7.png) + ![binary_search_step7](binary_search.assets/binary_search_step7.png){ class="animation-figure" }

图 10-2   二分查找流程

@@ -627,7 +627,7 @@ comments: true 由于“双闭区间”表示中的左右边界都被定义为闭区间,因此指针 $i$ 和 $j$ 缩小区间操作也是对称的。这样更不容易出错,**因此一般建议采用“双闭区间”的写法**。 -![两种区间定义](binary_search.assets/binary_search_ranges.png) +![两种区间定义](binary_search.assets/binary_search_ranges.png){ class="animation-figure" }

图 10-3   两种区间定义

diff --git a/docs/chapter_searching/binary_search_edge.md b/docs/chapter_searching/binary_search_edge.md index 6074dec41..10654fb2b 100644 --- a/docs/chapter_searching/binary_search_edge.md +++ b/docs/chapter_searching/binary_search_edge.md @@ -211,7 +211,7 @@ comments: true 如图 10-7 所示,查找完成后,指针 $i$ 指向最左一个 `target + 1`(如果存在),而 $j$ 指向最右一个 `target` ,**因此返回 $j$ 即可**。 -![将查找右边界转化为查找左边界](binary_search_edge.assets/binary_search_right_edge_by_left_edge.png) +![将查找右边界转化为查找左边界](binary_search_edge.assets/binary_search_right_edge_by_left_edge.png){ class="animation-figure" }

图 10-7   将查找右边界转化为查找左边界

@@ -428,7 +428,7 @@ comments: true - 查找最左一个 `target` :可以转化为查找 `target - 0.5` ,并返回指针 $i$ 。 - 查找最右一个 `target` :可以转化为查找 `target + 0.5` ,并返回指针 $j$ 。 -![将查找边界转化为查找元素](binary_search_edge.assets/binary_search_edge_by_element.png) +![将查找边界转化为查找元素](binary_search_edge.assets/binary_search_edge_by_element.png){ class="animation-figure" }

图 10-8   将查找边界转化为查找元素

diff --git a/docs/chapter_searching/binary_search_insertion.md b/docs/chapter_searching/binary_search_insertion.md index ae183062f..75a851c8d 100644 --- a/docs/chapter_searching/binary_search_insertion.md +++ b/docs/chapter_searching/binary_search_insertion.md @@ -12,7 +12,7 @@ comments: true 给定一个长度为 $n$ 的有序数组 `nums` 和一个元素 `target` ,数组不存在重复元素。现将 `target` 插入到数组 `nums` 中,并保持其有序性。若数组中已存在元素 `target` ,则插入到其左方。请返回插入后 `target` 在数组中的索引。 -![二分查找插入点示例数据](binary_search_insertion.assets/binary_search_insertion_example.png) +![二分查找插入点示例数据](binary_search_insertion.assets/binary_search_insertion_example.png){ class="animation-figure" }

图 10-4   二分查找插入点示例数据

@@ -285,7 +285,7 @@ comments: true 1. 执行二分查找,得到任意一个 `target` 的索引,记为 $k$ 。 2. 从索引 $k$ 开始,向左进行线性遍历,当找到最左边的 `target` 时返回。 -![线性查找重复元素的插入点](binary_search_insertion.assets/binary_search_insertion_naive.png) +![线性查找重复元素的插入点](binary_search_insertion.assets/binary_search_insertion_naive.png){ class="animation-figure" }

图 10-5   线性查找重复元素的插入点

@@ -299,28 +299,28 @@ comments: true 循环完成后,$i$ 指向最左边的 `target` ,$j$ 指向首个小于 `target` 的元素,**因此索引 $i$ 就是插入点**。 === "<1>" - ![二分查找重复元素的插入点的步骤](binary_search_insertion.assets/binary_search_insertion_step1.png) + ![二分查找重复元素的插入点的步骤](binary_search_insertion.assets/binary_search_insertion_step1.png){ class="animation-figure" } === "<2>" - ![binary_search_insertion_step2](binary_search_insertion.assets/binary_search_insertion_step2.png) + ![binary_search_insertion_step2](binary_search_insertion.assets/binary_search_insertion_step2.png){ class="animation-figure" } === "<3>" - ![binary_search_insertion_step3](binary_search_insertion.assets/binary_search_insertion_step3.png) + ![binary_search_insertion_step3](binary_search_insertion.assets/binary_search_insertion_step3.png){ class="animation-figure" } === "<4>" - ![binary_search_insertion_step4](binary_search_insertion.assets/binary_search_insertion_step4.png) + ![binary_search_insertion_step4](binary_search_insertion.assets/binary_search_insertion_step4.png){ class="animation-figure" } === "<5>" - ![binary_search_insertion_step5](binary_search_insertion.assets/binary_search_insertion_step5.png) + ![binary_search_insertion_step5](binary_search_insertion.assets/binary_search_insertion_step5.png){ class="animation-figure" } === "<6>" - ![binary_search_insertion_step6](binary_search_insertion.assets/binary_search_insertion_step6.png) + ![binary_search_insertion_step6](binary_search_insertion.assets/binary_search_insertion_step6.png){ class="animation-figure" } === "<7>" - ![binary_search_insertion_step7](binary_search_insertion.assets/binary_search_insertion_step7.png) + ![binary_search_insertion_step7](binary_search_insertion.assets/binary_search_insertion_step7.png){ class="animation-figure" } === "<8>" - ![binary_search_insertion_step8](binary_search_insertion.assets/binary_search_insertion_step8.png) + ![binary_search_insertion_step8](binary_search_insertion.assets/binary_search_insertion_step8.png){ class="animation-figure" }

图 10-6   二分查找重复元素的插入点的步骤

diff --git a/docs/chapter_searching/index.md b/docs/chapter_searching/index.md index 2fcb9ee8e..b709e4dca 100644 --- a/docs/chapter_searching/index.md +++ b/docs/chapter_searching/index.md @@ -7,7 +7,7 @@ icon: material/text-search
-![搜索](../assets/covers/chapter_searching.jpg){ width="600" } +![搜索](../assets/covers/chapter_searching.jpg){ class="cover-image" }
diff --git a/docs/chapter_searching/replace_linear_by_hashing.md b/docs/chapter_searching/replace_linear_by_hashing.md index 906378d42..e5b1ac36d 100755 --- a/docs/chapter_searching/replace_linear_by_hashing.md +++ b/docs/chapter_searching/replace_linear_by_hashing.md @@ -14,7 +14,7 @@ comments: true 考虑直接遍历所有可能的组合。如图 10-9 所示,我们开启一个两层循环,在每轮中判断两个整数的和是否为 `target` ,若是则返回它们的索引。 -![线性查找求解两数之和](replace_linear_by_hashing.assets/two_sum_brute_force.png) +![线性查找求解两数之和](replace_linear_by_hashing.assets/two_sum_brute_force.png){ class="animation-figure" }

图 10-9   线性查找求解两数之和

@@ -237,13 +237,13 @@ comments: true 2. 将键值对 `nums[i]` 和索引 `i` 添加进哈希表。 === "<1>" - ![辅助哈希表求解两数之和](replace_linear_by_hashing.assets/two_sum_hashtable_step1.png) + ![辅助哈希表求解两数之和](replace_linear_by_hashing.assets/two_sum_hashtable_step1.png){ class="animation-figure" } === "<2>" - ![two_sum_hashtable_step2](replace_linear_by_hashing.assets/two_sum_hashtable_step2.png) + ![two_sum_hashtable_step2](replace_linear_by_hashing.assets/two_sum_hashtable_step2.png){ class="animation-figure" } === "<3>" - ![two_sum_hashtable_step3](replace_linear_by_hashing.assets/two_sum_hashtable_step3.png) + ![two_sum_hashtable_step3](replace_linear_by_hashing.assets/two_sum_hashtable_step3.png){ class="animation-figure" }

图 10-10   辅助哈希表求解两数之和

diff --git a/docs/chapter_searching/searching_algorithm_revisited.md b/docs/chapter_searching/searching_algorithm_revisited.md index 6aec0c978..80fee7f95 100644 --- a/docs/chapter_searching/searching_algorithm_revisited.md +++ b/docs/chapter_searching/searching_algorithm_revisited.md @@ -44,7 +44,7 @@ comments: true 给定大小为 $n$ 的一组数据,我们可以使用线性搜索、二分查找、树查找、哈希查找等多种方法在该数据中搜索目标元素。各个方法的工作原理如图 10-11 所示。 -![多种搜索策略](searching_algorithm_revisited.assets/searching_algorithms.png) +![多种搜索策略](searching_algorithm_revisited.assets/searching_algorithms.png){ class="animation-figure" }

图 10-11   多种搜索策略

diff --git a/docs/chapter_sorting/bubble_sort.md b/docs/chapter_sorting/bubble_sort.md index 8e9053311..dc8098d92 100755 --- a/docs/chapter_sorting/bubble_sort.md +++ b/docs/chapter_sorting/bubble_sort.md @@ -9,25 +9,25 @@ comments: true 如图 11-4 所示,冒泡过程可以利用元素交换操作来模拟:从数组最左端开始向右遍历,依次比较相邻元素大小,如果“左元素 > 右元素”就交换它俩。遍历完成后,最大的元素会被移动到数组的最右端。 === "<1>" - ![利用元素交换操作模拟冒泡](bubble_sort.assets/bubble_operation_step1.png) + ![利用元素交换操作模拟冒泡](bubble_sort.assets/bubble_operation_step1.png){ class="animation-figure" } === "<2>" - ![bubble_operation_step2](bubble_sort.assets/bubble_operation_step2.png) + ![bubble_operation_step2](bubble_sort.assets/bubble_operation_step2.png){ class="animation-figure" } === "<3>" - ![bubble_operation_step3](bubble_sort.assets/bubble_operation_step3.png) + ![bubble_operation_step3](bubble_sort.assets/bubble_operation_step3.png){ class="animation-figure" } === "<4>" - ![bubble_operation_step4](bubble_sort.assets/bubble_operation_step4.png) + ![bubble_operation_step4](bubble_sort.assets/bubble_operation_step4.png){ class="animation-figure" } === "<5>" - ![bubble_operation_step5](bubble_sort.assets/bubble_operation_step5.png) + ![bubble_operation_step5](bubble_sort.assets/bubble_operation_step5.png){ class="animation-figure" } === "<6>" - ![bubble_operation_step6](bubble_sort.assets/bubble_operation_step6.png) + ![bubble_operation_step6](bubble_sort.assets/bubble_operation_step6.png){ class="animation-figure" } === "<7>" - ![bubble_operation_step7](bubble_sort.assets/bubble_operation_step7.png) + ![bubble_operation_step7](bubble_sort.assets/bubble_operation_step7.png){ class="animation-figure" }

图 11-4   利用元素交换操作模拟冒泡

@@ -40,7 +40,7 @@ comments: true 3. 以此类推,经过 $n - 1$ 轮“冒泡”后,**前 $n - 1$ 大的元素都被交换至正确位置**。 4. 仅剩的一个元素必定是最小元素,无须排序,因此数组排序完成。 -![冒泡排序流程](bubble_sort.assets/bubble_sort_overview.png) +![冒泡排序流程](bubble_sort.assets/bubble_sort_overview.png){ class="animation-figure" }

图 11-5   冒泡排序流程

diff --git a/docs/chapter_sorting/bucket_sort.md b/docs/chapter_sorting/bucket_sort.md index 007b9f9cb..ba5ad96f9 100644 --- a/docs/chapter_sorting/bucket_sort.md +++ b/docs/chapter_sorting/bucket_sort.md @@ -16,7 +16,7 @@ comments: true 2. 对每个桶分别执行排序(本文采用编程语言的内置排序函数)。 3. 按照桶的从小到大的顺序,合并结果。 -![桶排序算法流程](bucket_sort.assets/bucket_sort_overview.png) +![桶排序算法流程](bucket_sort.assets/bucket_sort_overview.png){ class="animation-figure" }

图 11-13   桶排序算法流程

@@ -409,7 +409,7 @@ comments: true 如图 11-14 所示,这种方法本质上是创建一个递归树,目标是让叶节点的值尽可能平均。当然,不一定要每轮将数据划分为 3 个桶,具体划分方式可根据数据特点灵活选择。 -![递归划分桶](bucket_sort.assets/scatter_in_buckets_recursively.png) +![递归划分桶](bucket_sort.assets/scatter_in_buckets_recursively.png){ class="animation-figure" }

图 11-14   递归划分桶

@@ -417,6 +417,6 @@ comments: true 如图 11-15 所示,我们假设商品价格服从正态分布,这样就可以合理地设定价格区间,从而将商品平均分配到各个桶中。 -![根据概率分布划分桶](bucket_sort.assets/scatter_in_buckets_distribution.png) +![根据概率分布划分桶](bucket_sort.assets/scatter_in_buckets_distribution.png){ class="animation-figure" }

图 11-15   根据概率分布划分桶

diff --git a/docs/chapter_sorting/counting_sort.md b/docs/chapter_sorting/counting_sort.md index 26f784c52..4bd2e6c9d 100644 --- a/docs/chapter_sorting/counting_sort.md +++ b/docs/chapter_sorting/counting_sort.md @@ -14,7 +14,7 @@ comments: true 2. **借助 `counter` 统计 `nums` 中各数字的出现次数**,其中 `counter[num]` 对应数字 `num` 的出现次数。统计方法很简单,只需遍历 `nums`(设当前数字为 `num`),每轮将 `counter[num]` 增加 $1$ 即可。 3. **由于 `counter` 的各个索引天然有序,因此相当于所有数字已经被排序好了**。接下来,我们遍历 `counter` ,根据各数字的出现次数,将它们按从小到大的顺序填入 `nums` 即可。 -![计数排序流程](counting_sort.assets/counting_sort_overview.png) +![计数排序流程](counting_sort.assets/counting_sort_overview.png){ class="animation-figure" }

图 11-16   计数排序流程

@@ -339,28 +339,28 @@ $$ 遍历完成后,数组 `res` 中就是排序好的结果,最后使用 `res` 覆盖原数组 `nums` 即可。图 11-17 展示了完整的计数排序流程。 === "<1>" - ![计数排序步骤](counting_sort.assets/counting_sort_step1.png) + ![计数排序步骤](counting_sort.assets/counting_sort_step1.png){ class="animation-figure" } === "<2>" - ![counting_sort_step2](counting_sort.assets/counting_sort_step2.png) + ![counting_sort_step2](counting_sort.assets/counting_sort_step2.png){ class="animation-figure" } === "<3>" - ![counting_sort_step3](counting_sort.assets/counting_sort_step3.png) + ![counting_sort_step3](counting_sort.assets/counting_sort_step3.png){ class="animation-figure" } === "<4>" - ![counting_sort_step4](counting_sort.assets/counting_sort_step4.png) + ![counting_sort_step4](counting_sort.assets/counting_sort_step4.png){ class="animation-figure" } === "<5>" - ![counting_sort_step5](counting_sort.assets/counting_sort_step5.png) + ![counting_sort_step5](counting_sort.assets/counting_sort_step5.png){ class="animation-figure" } === "<6>" - ![counting_sort_step6](counting_sort.assets/counting_sort_step6.png) + ![counting_sort_step6](counting_sort.assets/counting_sort_step6.png){ class="animation-figure" } === "<7>" - ![counting_sort_step7](counting_sort.assets/counting_sort_step7.png) + ![counting_sort_step7](counting_sort.assets/counting_sort_step7.png){ class="animation-figure" } === "<8>" - ![counting_sort_step8](counting_sort.assets/counting_sort_step8.png) + ![counting_sort_step8](counting_sort.assets/counting_sort_step8.png){ class="animation-figure" }

图 11-17   计数排序步骤

diff --git a/docs/chapter_sorting/heap_sort.md b/docs/chapter_sorting/heap_sort.md index 32b9d5735..8fa429a5d 100644 --- a/docs/chapter_sorting/heap_sort.md +++ b/docs/chapter_sorting/heap_sort.md @@ -29,40 +29,40 @@ comments: true 实际上,元素出堆操作中也包含第 `2.` 和 `3.` 步,只是多了一个弹出元素的步骤。 === "<1>" - ![堆排序步骤](heap_sort.assets/heap_sort_step1.png) + ![堆排序步骤](heap_sort.assets/heap_sort_step1.png){ class="animation-figure" } === "<2>" - ![heap_sort_step2](heap_sort.assets/heap_sort_step2.png) + ![heap_sort_step2](heap_sort.assets/heap_sort_step2.png){ class="animation-figure" } === "<3>" - ![heap_sort_step3](heap_sort.assets/heap_sort_step3.png) + ![heap_sort_step3](heap_sort.assets/heap_sort_step3.png){ class="animation-figure" } === "<4>" - ![heap_sort_step4](heap_sort.assets/heap_sort_step4.png) + ![heap_sort_step4](heap_sort.assets/heap_sort_step4.png){ class="animation-figure" } === "<5>" - ![heap_sort_step5](heap_sort.assets/heap_sort_step5.png) + ![heap_sort_step5](heap_sort.assets/heap_sort_step5.png){ class="animation-figure" } === "<6>" - ![heap_sort_step6](heap_sort.assets/heap_sort_step6.png) + ![heap_sort_step6](heap_sort.assets/heap_sort_step6.png){ class="animation-figure" } === "<7>" - ![heap_sort_step7](heap_sort.assets/heap_sort_step7.png) + ![heap_sort_step7](heap_sort.assets/heap_sort_step7.png){ class="animation-figure" } === "<8>" - ![heap_sort_step8](heap_sort.assets/heap_sort_step8.png) + ![heap_sort_step8](heap_sort.assets/heap_sort_step8.png){ class="animation-figure" } === "<9>" - ![heap_sort_step9](heap_sort.assets/heap_sort_step9.png) + ![heap_sort_step9](heap_sort.assets/heap_sort_step9.png){ class="animation-figure" } === "<10>" - ![heap_sort_step10](heap_sort.assets/heap_sort_step10.png) + ![heap_sort_step10](heap_sort.assets/heap_sort_step10.png){ class="animation-figure" } === "<11>" - ![heap_sort_step11](heap_sort.assets/heap_sort_step11.png) + ![heap_sort_step11](heap_sort.assets/heap_sort_step11.png){ class="animation-figure" } === "<12>" - ![heap_sort_step12](heap_sort.assets/heap_sort_step12.png) + ![heap_sort_step12](heap_sort.assets/heap_sort_step12.png){ class="animation-figure" }

图 11-12   堆排序步骤

diff --git a/docs/chapter_sorting/index.md b/docs/chapter_sorting/index.md index 6849c6f94..97265cf2f 100644 --- a/docs/chapter_sorting/index.md +++ b/docs/chapter_sorting/index.md @@ -7,7 +7,7 @@ icon: material/sort-ascending
-![排序](../assets/covers/chapter_sorting.jpg){ width="600" } +![排序](../assets/covers/chapter_sorting.jpg){ class="cover-image" }
diff --git a/docs/chapter_sorting/insertion_sort.md b/docs/chapter_sorting/insertion_sort.md index d4a22812c..57d22edd0 100755 --- a/docs/chapter_sorting/insertion_sort.md +++ b/docs/chapter_sorting/insertion_sort.md @@ -10,7 +10,7 @@ comments: true 图 11-6 展示了数组插入元素的操作流程。设基准元素为 `base` ,我们需要将从目标索引到 `base` 之间的所有元素向右移动一位,然后再将 `base` 赋值给目标索引。 -![单次插入操作](insertion_sort.assets/insertion_operation.png) +![单次插入操作](insertion_sort.assets/insertion_operation.png){ class="animation-figure" }

图 11-6   单次插入操作

@@ -23,7 +23,7 @@ comments: true 3. 选取第 3 个元素作为 `base` ,将其插入到正确位置后,**数组的前 3 个元素已排序**。 4. 以此类推,在最后一轮中,选取最后一个元素作为 `base` ,将其插入到正确位置后,**所有元素均已排序**。 -![插入排序流程](insertion_sort.assets/insertion_sort_overview.png) +![插入排序流程](insertion_sort.assets/insertion_sort_overview.png){ class="animation-figure" }

图 11-7   插入排序流程

diff --git a/docs/chapter_sorting/merge_sort.md b/docs/chapter_sorting/merge_sort.md index b3e115803..8c006140f 100755 --- a/docs/chapter_sorting/merge_sort.md +++ b/docs/chapter_sorting/merge_sort.md @@ -9,7 +9,7 @@ comments: true 1. **划分阶段**:通过递归不断地将数组从中点处分开,将长数组的排序问题转换为短数组的排序问题。 2. **合并阶段**:当子数组长度为 1 时终止划分,开始合并,持续地将左右两个较短的有序数组合并为一个较长的有序数组,直至结束。 -![归并排序的划分与合并阶段](merge_sort.assets/merge_sort_overview.png) +![归并排序的划分与合并阶段](merge_sort.assets/merge_sort_overview.png){ class="animation-figure" }

图 11-10   归并排序的划分与合并阶段

@@ -23,34 +23,34 @@ comments: true “合并阶段”从底至顶地将左子数组和右子数组合并为一个有序数组。需要注意的是,从长度为 1 的子数组开始合并,合并阶段中的每个子数组都是有序的。 === "<1>" - ![归并排序步骤](merge_sort.assets/merge_sort_step1.png) + ![归并排序步骤](merge_sort.assets/merge_sort_step1.png){ class="animation-figure" } === "<2>" - ![merge_sort_step2](merge_sort.assets/merge_sort_step2.png) + ![merge_sort_step2](merge_sort.assets/merge_sort_step2.png){ class="animation-figure" } === "<3>" - ![merge_sort_step3](merge_sort.assets/merge_sort_step3.png) + ![merge_sort_step3](merge_sort.assets/merge_sort_step3.png){ class="animation-figure" } === "<4>" - ![merge_sort_step4](merge_sort.assets/merge_sort_step4.png) + ![merge_sort_step4](merge_sort.assets/merge_sort_step4.png){ class="animation-figure" } === "<5>" - ![merge_sort_step5](merge_sort.assets/merge_sort_step5.png) + ![merge_sort_step5](merge_sort.assets/merge_sort_step5.png){ class="animation-figure" } === "<6>" - ![merge_sort_step6](merge_sort.assets/merge_sort_step6.png) + ![merge_sort_step6](merge_sort.assets/merge_sort_step6.png){ class="animation-figure" } === "<7>" - ![merge_sort_step7](merge_sort.assets/merge_sort_step7.png) + ![merge_sort_step7](merge_sort.assets/merge_sort_step7.png){ class="animation-figure" } === "<8>" - ![merge_sort_step8](merge_sort.assets/merge_sort_step8.png) + ![merge_sort_step8](merge_sort.assets/merge_sort_step8.png){ class="animation-figure" } === "<9>" - ![merge_sort_step9](merge_sort.assets/merge_sort_step9.png) + ![merge_sort_step9](merge_sort.assets/merge_sort_step9.png){ class="animation-figure" } === "<10>" - ![merge_sort_step10](merge_sort.assets/merge_sort_step10.png) + ![merge_sort_step10](merge_sort.assets/merge_sort_step10.png){ class="animation-figure" }

图 11-11   归并排序步骤

diff --git a/docs/chapter_sorting/quick_sort.md b/docs/chapter_sorting/quick_sort.md index bd5c9bd26..068dd38f3 100755 --- a/docs/chapter_sorting/quick_sort.md +++ b/docs/chapter_sorting/quick_sort.md @@ -13,31 +13,31 @@ comments: true 3. 循环执行步骤 `2.` ,直到 `i` 和 `j` 相遇时停止,最后将基准数交换至两个子数组的分界线。 === "<1>" - ![哨兵划分步骤](quick_sort.assets/pivot_division_step1.png) + ![哨兵划分步骤](quick_sort.assets/pivot_division_step1.png){ class="animation-figure" } === "<2>" - ![pivot_division_step2](quick_sort.assets/pivot_division_step2.png) + ![pivot_division_step2](quick_sort.assets/pivot_division_step2.png){ class="animation-figure" } === "<3>" - ![pivot_division_step3](quick_sort.assets/pivot_division_step3.png) + ![pivot_division_step3](quick_sort.assets/pivot_division_step3.png){ class="animation-figure" } === "<4>" - ![pivot_division_step4](quick_sort.assets/pivot_division_step4.png) + ![pivot_division_step4](quick_sort.assets/pivot_division_step4.png){ class="animation-figure" } === "<5>" - ![pivot_division_step5](quick_sort.assets/pivot_division_step5.png) + ![pivot_division_step5](quick_sort.assets/pivot_division_step5.png){ class="animation-figure" } === "<6>" - ![pivot_division_step6](quick_sort.assets/pivot_division_step6.png) + ![pivot_division_step6](quick_sort.assets/pivot_division_step6.png){ class="animation-figure" } === "<7>" - ![pivot_division_step7](quick_sort.assets/pivot_division_step7.png) + ![pivot_division_step7](quick_sort.assets/pivot_division_step7.png){ class="animation-figure" } === "<8>" - ![pivot_division_step8](quick_sort.assets/pivot_division_step8.png) + ![pivot_division_step8](quick_sort.assets/pivot_division_step8.png){ class="animation-figure" } === "<9>" - ![pivot_division_step9](quick_sort.assets/pivot_division_step9.png) + ![pivot_division_step9](quick_sort.assets/pivot_division_step9.png){ class="animation-figure" }

图 11-8   哨兵划分步骤

@@ -366,7 +366,7 @@ comments: true 2. 然后,对左子数组和右子数组分别递归执行“哨兵划分”。 3. 持续递归,直至子数组长度为 1 时终止,从而完成整个数组的排序。 -![快速排序流程](quick_sort.assets/quick_sort_overview.png) +![快速排序流程](quick_sort.assets/quick_sort_overview.png){ class="animation-figure" }

图 11-9   快速排序流程

diff --git a/docs/chapter_sorting/radix_sort.md b/docs/chapter_sorting/radix_sort.md index 2890bf086..eafb328cb 100644 --- a/docs/chapter_sorting/radix_sort.md +++ b/docs/chapter_sorting/radix_sort.md @@ -16,7 +16,7 @@ comments: true 2. 对学号的第 $k$ 位执行“计数排序”。完成后,数据会根据第 $k$ 位从小到大排序。 3. 将 $k$ 增加 $1$ ,然后返回步骤 `2.` 继续迭代,直到所有位都排序完成后结束。 -![基数排序算法流程](radix_sort.assets/radix_sort_overview.png) +![基数排序算法流程](radix_sort.assets/radix_sort_overview.png){ class="animation-figure" }

图 11-18   基数排序算法流程

diff --git a/docs/chapter_sorting/selection_sort.md b/docs/chapter_sorting/selection_sort.md index 7eab27707..01da8ac48 100644 --- a/docs/chapter_sorting/selection_sort.md +++ b/docs/chapter_sorting/selection_sort.md @@ -15,37 +15,37 @@ comments: true 5. 仅剩的一个元素必定是最大元素,无须排序,因此数组排序完成。 === "<1>" - ![选择排序步骤](selection_sort.assets/selection_sort_step1.png) + ![选择排序步骤](selection_sort.assets/selection_sort_step1.png){ class="animation-figure" } === "<2>" - ![selection_sort_step2](selection_sort.assets/selection_sort_step2.png) + ![selection_sort_step2](selection_sort.assets/selection_sort_step2.png){ class="animation-figure" } === "<3>" - ![selection_sort_step3](selection_sort.assets/selection_sort_step3.png) + ![selection_sort_step3](selection_sort.assets/selection_sort_step3.png){ class="animation-figure" } === "<4>" - ![selection_sort_step4](selection_sort.assets/selection_sort_step4.png) + ![selection_sort_step4](selection_sort.assets/selection_sort_step4.png){ class="animation-figure" } === "<5>" - ![selection_sort_step5](selection_sort.assets/selection_sort_step5.png) + ![selection_sort_step5](selection_sort.assets/selection_sort_step5.png){ class="animation-figure" } === "<6>" - ![selection_sort_step6](selection_sort.assets/selection_sort_step6.png) + ![selection_sort_step6](selection_sort.assets/selection_sort_step6.png){ class="animation-figure" } === "<7>" - ![selection_sort_step7](selection_sort.assets/selection_sort_step7.png) + ![selection_sort_step7](selection_sort.assets/selection_sort_step7.png){ class="animation-figure" } === "<8>" - ![selection_sort_step8](selection_sort.assets/selection_sort_step8.png) + ![selection_sort_step8](selection_sort.assets/selection_sort_step8.png){ class="animation-figure" } === "<9>" - ![selection_sort_step9](selection_sort.assets/selection_sort_step9.png) + ![selection_sort_step9](selection_sort.assets/selection_sort_step9.png){ class="animation-figure" } === "<10>" - ![selection_sort_step10](selection_sort.assets/selection_sort_step10.png) + ![selection_sort_step10](selection_sort.assets/selection_sort_step10.png){ class="animation-figure" } === "<11>" - ![selection_sort_step11](selection_sort.assets/selection_sort_step11.png) + ![selection_sort_step11](selection_sort.assets/selection_sort_step11.png){ class="animation-figure" }

图 11-2   选择排序步骤

@@ -290,6 +290,6 @@ comments: true - **空间复杂度 $O(1)$、原地排序**:指针 $i$ 和 $j$ 使用常数大小的额外空间。 - **非稳定排序**:如图 11-3 所示,元素 `nums[i]` 有可能被交换至与其相等的元素的右边,导致两者相对顺序发生改变。 -![选择排序非稳定示例](selection_sort.assets/selection_sort_instability.png) +![选择排序非稳定示例](selection_sort.assets/selection_sort_instability.png){ class="animation-figure" }

图 11-3   选择排序非稳定示例

diff --git a/docs/chapter_sorting/sorting_algorithm.md b/docs/chapter_sorting/sorting_algorithm.md index c26c7062b..ae0d678c9 100644 --- a/docs/chapter_sorting/sorting_algorithm.md +++ b/docs/chapter_sorting/sorting_algorithm.md @@ -8,7 +8,7 @@ comments: true 如图 11-1 所示,排序算法中的数据类型可以是整数、浮点数、字符或字符串等。排序的判断规则可根据需求设定,如数字大小、字符 ASCII 码顺序或自定义规则。 -![数据类型和判断规则示例](sorting_algorithm.assets/sorting_examples.png) +![数据类型和判断规则示例](sorting_algorithm.assets/sorting_examples.png){ class="animation-figure" }

图 11-1   数据类型和判断规则示例

diff --git a/docs/chapter_sorting/summary.md b/docs/chapter_sorting/summary.md index dd134ebe6..f75aed484 100644 --- a/docs/chapter_sorting/summary.md +++ b/docs/chapter_sorting/summary.md @@ -16,7 +16,7 @@ comments: true - 总的来说,我们希望找到一种排序算法,具有高效率、稳定、原地以及正向自适应性等优点。然而,正如其他数据结构和算法一样,没有一种排序算法能够同时满足所有这些条件。在实际应用中,我们需要根据数据的特性来选择合适的排序算法。 - 图 11-19 对比了主流排序算法的效率、稳定性、就地性和自适应性等。 -![排序算法对比](summary.assets/sorting_algorithms_comparison.png) +![排序算法对比](summary.assets/sorting_algorithms_comparison.png){ class="animation-figure" }

图 11-19   排序算法对比

diff --git a/docs/chapter_stack_and_queue/deque.md b/docs/chapter_stack_and_queue/deque.md index f6aa671dd..87e90b3f8 100644 --- a/docs/chapter_stack_and_queue/deque.md +++ b/docs/chapter_stack_and_queue/deque.md @@ -6,7 +6,7 @@ comments: true 在队列中,我们仅能在头部删除或在尾部添加元素。如图 5-7 所示,「双向队列 double-ended queue」提供了更高的灵活性,允许在头部和尾部执行元素的添加或删除操作。 -![双向队列的操作](deque.assets/deque_operations.png) +![双向队列的操作](deque.assets/deque_operations.png){ class="animation-figure" }

图 5-7   双向队列的操作

@@ -365,19 +365,19 @@ comments: true 如图 5-8 所示,我们将双向链表的头节点和尾节点视为双向队列的队首和队尾,同时实现在两端添加和删除节点的功能。 === "LinkedListDeque" - ![基于链表实现双向队列的入队出队操作](deque.assets/linkedlist_deque.png) + ![基于链表实现双向队列的入队出队操作](deque.assets/linkedlist_deque.png){ class="animation-figure" } === "pushLast()" - ![linkedlist_deque_push_last](deque.assets/linkedlist_deque_push_last.png) + ![linkedlist_deque_push_last](deque.assets/linkedlist_deque_push_last.png){ class="animation-figure" } === "pushFirst()" - ![linkedlist_deque_push_first](deque.assets/linkedlist_deque_push_first.png) + ![linkedlist_deque_push_first](deque.assets/linkedlist_deque_push_first.png){ class="animation-figure" } === "popLast()" - ![linkedlist_deque_pop_last](deque.assets/linkedlist_deque_pop_last.png) + ![linkedlist_deque_pop_last](deque.assets/linkedlist_deque_pop_last.png){ class="animation-figure" } === "popFirst()" - ![linkedlist_deque_pop_first](deque.assets/linkedlist_deque_pop_first.png) + ![linkedlist_deque_pop_first](deque.assets/linkedlist_deque_pop_first.png){ class="animation-figure" }

图 5-8   基于链表实现双向队列的入队出队操作

@@ -2008,19 +2008,19 @@ comments: true 如图 5-9 所示,与基于数组实现队列类似,我们也可以使用环形数组来实现双向队列。 === "ArrayDeque" - ![基于数组实现双向队列的入队出队操作](deque.assets/array_deque.png) + ![基于数组实现双向队列的入队出队操作](deque.assets/array_deque.png){ class="animation-figure" } === "pushLast()" - ![array_deque_push_last](deque.assets/array_deque_push_last.png) + ![array_deque_push_last](deque.assets/array_deque_push_last.png){ class="animation-figure" } === "pushFirst()" - ![array_deque_push_first](deque.assets/array_deque_push_first.png) + ![array_deque_push_first](deque.assets/array_deque_push_first.png){ class="animation-figure" } === "popLast()" - ![array_deque_pop_last](deque.assets/array_deque_pop_last.png) + ![array_deque_pop_last](deque.assets/array_deque_pop_last.png){ class="animation-figure" } === "popFirst()" - ![array_deque_pop_first](deque.assets/array_deque_pop_first.png) + ![array_deque_pop_first](deque.assets/array_deque_pop_first.png){ class="animation-figure" }

图 5-9   基于数组实现双向队列的入队出队操作

diff --git a/docs/chapter_stack_and_queue/index.md b/docs/chapter_stack_and_queue/index.md index eb8644b95..542c41137 100644 --- a/docs/chapter_stack_and_queue/index.md +++ b/docs/chapter_stack_and_queue/index.md @@ -7,7 +7,7 @@ icon: material/stack-overflow
-![栈与队列](../assets/covers/chapter_stack_and_queue.jpg){ width="600" } +![栈与队列](../assets/covers/chapter_stack_and_queue.jpg){ class="cover-image" }
diff --git a/docs/chapter_stack_and_queue/queue.md b/docs/chapter_stack_and_queue/queue.md index 3706e6ca6..966447dac 100755 --- a/docs/chapter_stack_and_queue/queue.md +++ b/docs/chapter_stack_and_queue/queue.md @@ -8,7 +8,7 @@ comments: true 如图 5-4 所示,我们将队列的头部称为“队首”,尾部称为“队尾”,将把元素加入队尾的操作称为“入队”,删除队首元素的操作称为“出队”。 -![队列的先入先出规则](queue.assets/queue_operations.png) +![队列的先入先出规则](queue.assets/queue_operations.png){ class="animation-figure" }

图 5-4   队列的先入先出规则

@@ -325,13 +325,13 @@ comments: true 如图 5-5 所示,我们可以将链表的“头节点”和“尾节点”分别视为“队首”和“队尾”,规定队尾仅可添加节点,队首仅可删除节点。 === "LinkedListQueue" - ![基于链表实现队列的入队出队操作](queue.assets/linkedlist_queue.png) + ![基于链表实现队列的入队出队操作](queue.assets/linkedlist_queue.png){ class="animation-figure" } === "push()" - ![linkedlist_queue_push](queue.assets/linkedlist_queue_push.png) + ![linkedlist_queue_push](queue.assets/linkedlist_queue_push.png){ class="animation-figure" } === "pop()" - ![linkedlist_queue_pop](queue.assets/linkedlist_queue_pop.png) + ![linkedlist_queue_pop](queue.assets/linkedlist_queue_pop.png){ class="animation-figure" }

图 5-5   基于链表实现队列的入队出队操作

@@ -1216,13 +1216,13 @@ comments: true 可以看到,入队和出队操作都只需进行一次操作,时间复杂度均为 $O(1)$ 。 === "ArrayQueue" - ![基于数组实现队列的入队出队操作](queue.assets/array_queue.png) + ![基于数组实现队列的入队出队操作](queue.assets/array_queue.png){ class="animation-figure" } === "push()" - ![array_queue_push](queue.assets/array_queue_push.png) + ![array_queue_push](queue.assets/array_queue_push.png){ class="animation-figure" } === "pop()" - ![array_queue_pop](queue.assets/array_queue_pop.png) + ![array_queue_pop](queue.assets/array_queue_pop.png){ class="animation-figure" }

图 5-6   基于数组实现队列的入队出队操作

diff --git a/docs/chapter_stack_and_queue/stack.md b/docs/chapter_stack_and_queue/stack.md index df29bb9bf..86e7c7d95 100755 --- a/docs/chapter_stack_and_queue/stack.md +++ b/docs/chapter_stack_and_queue/stack.md @@ -10,7 +10,7 @@ comments: true 如图 5-1 所示,我们把堆叠元素的顶部称为“栈顶”,底部称为“栈底”。将把元素添加到栈顶的操作叫做“入栈”,删除栈顶元素的操作叫做“出栈”。 -![栈的先入后出规则](stack.assets/stack_operations.png) +![栈的先入后出规则](stack.assets/stack_operations.png){ class="animation-figure" }

图 5-1   栈的先入后出规则

@@ -325,13 +325,13 @@ comments: true 如图 5-2 所示,对于入栈操作,我们只需将元素插入链表头部,这种节点插入方法被称为“头插法”。而对于出栈操作,只需将头节点从链表中删除即可。 === "LinkedListStack" - ![基于链表实现栈的入栈出栈操作](stack.assets/linkedlist_stack.png) + ![基于链表实现栈的入栈出栈操作](stack.assets/linkedlist_stack.png){ class="animation-figure" } === "push()" - ![linkedlist_stack_push](stack.assets/linkedlist_stack_push.png) + ![linkedlist_stack_push](stack.assets/linkedlist_stack_push.png){ class="animation-figure" } === "pop()" - ![linkedlist_stack_pop](stack.assets/linkedlist_stack_pop.png) + ![linkedlist_stack_pop](stack.assets/linkedlist_stack_pop.png){ class="animation-figure" }

图 5-2   基于链表实现栈的入栈出栈操作

@@ -1089,13 +1089,13 @@ comments: true 使用数组实现栈时,我们可以将数组的尾部作为栈顶。如图 5-3 所示,入栈与出栈操作分别对应在数组尾部添加元素与删除元素,时间复杂度都为 $O(1)$ 。 === "ArrayStack" - ![基于数组实现栈的入栈出栈操作](stack.assets/array_stack.png) + ![基于数组实现栈的入栈出栈操作](stack.assets/array_stack.png){ class="animation-figure" } === "push()" - ![array_stack_push](stack.assets/array_stack_push.png) + ![array_stack_push](stack.assets/array_stack_push.png){ class="animation-figure" } === "pop()" - ![array_stack_pop](stack.assets/array_stack_pop.png) + ![array_stack_pop](stack.assets/array_stack_pop.png){ class="animation-figure" }

图 5-3   基于数组实现栈的入栈出栈操作

diff --git a/docs/chapter_tree/array_representation_of_tree.md b/docs/chapter_tree/array_representation_of_tree.md index 16adb3940..534cfefcd 100644 --- a/docs/chapter_tree/array_representation_of_tree.md +++ b/docs/chapter_tree/array_representation_of_tree.md @@ -14,7 +14,7 @@ comments: true 根据层序遍历的特性,我们可以推导出父节点索引与子节点索引之间的“映射公式”:**若节点的索引为 $i$ ,则该节点的左子节点索引为 $2i + 1$ ,右子节点索引为 $2i + 2$** 。图 7-12 展示了各个节点索引之间的映射关系。 -![完美二叉树的数组表示](array_representation_of_tree.assets/array_representation_binary_tree.png) +![完美二叉树的数组表示](array_representation_of_tree.assets/array_representation_binary_tree.png){ class="animation-figure" }

图 7-12   完美二叉树的数组表示

@@ -26,7 +26,7 @@ comments: true 如图 7-13 所示,给定一个非完美二叉树,上述的数组表示方法已经失效。 -![层序遍历序列对应多种二叉树可能性](array_representation_of_tree.assets/array_representation_without_empty.png) +![层序遍历序列对应多种二叉树可能性](array_representation_of_tree.assets/array_representation_without_empty.png){ class="animation-figure" }

图 7-13   层序遍历序列对应多种二叉树可能性

@@ -126,7 +126,7 @@ comments: true ``` -![任意类型二叉树的数组表示](array_representation_of_tree.assets/array_representation_with_empty.png) +![任意类型二叉树的数组表示](array_representation_of_tree.assets/array_representation_with_empty.png){ class="animation-figure" }

图 7-14   任意类型二叉树的数组表示

@@ -134,7 +134,7 @@ comments: true 这意味着使用数组表示完全二叉树时,可以省略存储所有 $\text{None}$ ,非常方便。图 7-15 给出了一个例子。 -![完全二叉树的数组表示](array_representation_of_tree.assets/array_representation_complete_binary_tree.png) +![完全二叉树的数组表示](array_representation_of_tree.assets/array_representation_complete_binary_tree.png){ class="animation-figure" }

图 7-15   完全二叉树的数组表示

diff --git a/docs/chapter_tree/avl_tree.md b/docs/chapter_tree/avl_tree.md index c3b8468b5..fc16e1762 100644 --- a/docs/chapter_tree/avl_tree.md +++ b/docs/chapter_tree/avl_tree.md @@ -8,13 +8,13 @@ comments: true 如图 7-24 所示,经过两次删除节点操作,这个二叉搜索树便会退化为链表。 -![AVL 树在删除节点后发生退化](avl_tree.assets/avltree_degradation_from_removing_node.png) +![AVL 树在删除节点后发生退化](avl_tree.assets/avltree_degradation_from_removing_node.png){ class="animation-figure" }

图 7-24   AVL 树在删除节点后发生退化

再例如,在图 7-25 的完美二叉树中插入两个节点后,树将严重向左倾斜,查找操作的时间复杂度也随之恶化。 -![AVL 树在插入节点后发生退化](avl_tree.assets/avltree_degradation_from_inserting_node.png) +![AVL 树在插入节点后发生退化](avl_tree.assets/avltree_degradation_from_inserting_node.png){ class="animation-figure" }

图 7-25   AVL 树在插入节点后发生退化

@@ -610,22 +610,22 @@ AVL 树的特点在于“旋转”操作,它能够在不影响二叉树的中 如图 7-26 所示,节点下方为平衡因子。从底至顶看,二叉树中首个失衡节点是“节点 3”。我们关注以该失衡节点为根节点的子树,将该节点记为 `node` ,其左子节点记为 `child` ,执行“右旋”操作。完成右旋后,子树已经恢复平衡,并且仍然保持二叉搜索树的特性。 === "<1>" - ![右旋操作步骤](avl_tree.assets/avltree_right_rotate_step1.png) + ![右旋操作步骤](avl_tree.assets/avltree_right_rotate_step1.png){ class="animation-figure" } === "<2>" - ![avltree_right_rotate_step2](avl_tree.assets/avltree_right_rotate_step2.png) + ![avltree_right_rotate_step2](avl_tree.assets/avltree_right_rotate_step2.png){ class="animation-figure" } === "<3>" - ![avltree_right_rotate_step3](avl_tree.assets/avltree_right_rotate_step3.png) + ![avltree_right_rotate_step3](avl_tree.assets/avltree_right_rotate_step3.png){ class="animation-figure" } === "<4>" - ![avltree_right_rotate_step4](avl_tree.assets/avltree_right_rotate_step4.png) + ![avltree_right_rotate_step4](avl_tree.assets/avltree_right_rotate_step4.png){ class="animation-figure" }

图 7-26   右旋操作步骤

如图 7-27 所示,当节点 `child` 有右子节点(记为 `grandChild` )时,需要在右旋中添加一步:将 `grandChild` 作为 `node` 的左子节点。 -![有 grandChild 的右旋操作](avl_tree.assets/avltree_right_rotate_with_grandchild.png) +![有 grandChild 的右旋操作](avl_tree.assets/avltree_right_rotate_with_grandchild.png){ class="animation-figure" }

图 7-27   有 grandChild 的右旋操作

@@ -856,13 +856,13 @@ AVL 树的特点在于“旋转”操作,它能够在不影响二叉树的中 相应的,如果考虑上述失衡二叉树的“镜像”,则需要执行图 7-28 所示的“左旋”操作。 -![左旋操作](avl_tree.assets/avltree_left_rotate.png) +![左旋操作](avl_tree.assets/avltree_left_rotate.png){ class="animation-figure" }

图 7-28   左旋操作

同理,如图 7-29 所示,当节点 `child` 有左子节点(记为 `grandChild` )时,需要在左旋中添加一步:将 `grandChild` 作为 `node` 的右子节点。 -![有 grandChild 的左旋操作](avl_tree.assets/avltree_left_rotate_with_grandchild.png) +![有 grandChild 的左旋操作](avl_tree.assets/avltree_left_rotate_with_grandchild.png){ class="animation-figure" }

图 7-29   有 grandChild 的左旋操作

@@ -1093,7 +1093,7 @@ AVL 树的特点在于“旋转”操作,它能够在不影响二叉树的中 对于图 7-30 中的失衡节点 3 ,仅使用左旋或右旋都无法使子树恢复平衡。此时需要先对 `child` 执行“左旋”,再对 `node` 执行“右旋”。 -![先左旋后右旋](avl_tree.assets/avltree_left_right_rotate.png) +![先左旋后右旋](avl_tree.assets/avltree_left_right_rotate.png){ class="animation-figure" }

图 7-30   先左旋后右旋

@@ -1101,7 +1101,7 @@ AVL 树的特点在于“旋转”操作,它能够在不影响二叉树的中 如图 7-31 所示,对于上述失衡二叉树的镜像情况,需要先对 `child` 执行“右旋”,然后对 `node` 执行“左旋”。 -![先右旋后左旋](avl_tree.assets/avltree_right_left_rotate.png) +![先右旋后左旋](avl_tree.assets/avltree_right_left_rotate.png){ class="animation-figure" }

图 7-31   先右旋后左旋

@@ -1109,7 +1109,7 @@ AVL 树的特点在于“旋转”操作,它能够在不影响二叉树的中 图 7-32 展示的四种失衡情况与上述案例逐个对应,分别需要采用右旋、左旋、先右后左、先左后右的旋转操作。 -![AVL 树的四种旋转情况](avl_tree.assets/avltree_rotation_cases.png) +![AVL 树的四种旋转情况](avl_tree.assets/avltree_rotation_cases.png){ class="animation-figure" }

图 7-32   AVL 树的四种旋转情况

diff --git a/docs/chapter_tree/binary_search_tree.md b/docs/chapter_tree/binary_search_tree.md index fd32635d2..87c8466c6 100755 --- a/docs/chapter_tree/binary_search_tree.md +++ b/docs/chapter_tree/binary_search_tree.md @@ -9,7 +9,7 @@ comments: true 1. 对于根节点,左子树中所有节点的值 $<$ 根节点的值 $<$ 右子树中所有节点的值。 2. 任意节点的左、右子树也是二叉搜索树,即同样满足条件 `1.` 。 -![二叉搜索树](binary_search_tree.assets/binary_search_tree.png) +![二叉搜索树](binary_search_tree.assets/binary_search_tree.png){ class="animation-figure" }

图 7-16   二叉搜索树

@@ -26,16 +26,16 @@ comments: true - 若 `cur.val = num` ,说明找到目标节点,跳出循环并返回该节点。 === "<1>" - ![二叉搜索树查找节点示例](binary_search_tree.assets/bst_search_step1.png) + ![二叉搜索树查找节点示例](binary_search_tree.assets/bst_search_step1.png){ class="animation-figure" } === "<2>" - ![bst_search_step2](binary_search_tree.assets/bst_search_step2.png) + ![bst_search_step2](binary_search_tree.assets/bst_search_step2.png){ class="animation-figure" } === "<3>" - ![bst_search_step3](binary_search_tree.assets/bst_search_step3.png) + ![bst_search_step3](binary_search_tree.assets/bst_search_step3.png){ class="animation-figure" } === "<4>" - ![bst_search_step4](binary_search_tree.assets/bst_search_step4.png) + ![bst_search_step4](binary_search_tree.assets/bst_search_step4.png){ class="animation-figure" }

图 7-17   二叉搜索树查找节点示例

@@ -325,7 +325,7 @@ comments: true 1. **查找插入位置**:与查找操作相似,从根节点出发,根据当前节点值和 `num` 的大小关系循环向下搜索,直到越过叶节点(遍历至 $\text{None}$ )时跳出循环。 2. **在该位置插入节点**:初始化节点 `num` ,将该节点置于 $\text{None}$ 的位置。 -![在二叉搜索树中插入节点](binary_search_tree.assets/bst_insert.png) +![在二叉搜索树中插入节点](binary_search_tree.assets/bst_insert.png){ class="animation-figure" }

图 7-18   在二叉搜索树中插入节点

@@ -753,13 +753,13 @@ comments: true 如图 7-19 所示,当待删除节点的度为 $0$ 时,表示该节点是叶节点,可以直接删除。 -![在二叉搜索树中删除节点(度为 0 )](binary_search_tree.assets/bst_remove_case1.png) +![在二叉搜索树中删除节点(度为 0 )](binary_search_tree.assets/bst_remove_case1.png){ class="animation-figure" }

图 7-19   在二叉搜索树中删除节点(度为 0 )

如图 7-20 所示,当待删除节点的度为 $1$ 时,将待删除节点替换为其子节点即可。 -![在二叉搜索树中删除节点(度为 1 )](binary_search_tree.assets/bst_remove_case2.png) +![在二叉搜索树中删除节点(度为 1 )](binary_search_tree.assets/bst_remove_case2.png){ class="animation-figure" }

图 7-20   在二叉搜索树中删除节点(度为 1 )

@@ -771,16 +771,16 @@ comments: true 2. 将 `tmp` 的值覆盖待删除节点的值,并在树中递归删除节点 `tmp` 。 === "<1>" - ![在二叉搜索树中删除节点(度为 2 )](binary_search_tree.assets/bst_remove_case3_step1.png) + ![在二叉搜索树中删除节点(度为 2 )](binary_search_tree.assets/bst_remove_case3_step1.png){ class="animation-figure" } === "<2>" - ![bst_remove_case3_step2](binary_search_tree.assets/bst_remove_case3_step2.png) + ![bst_remove_case3_step2](binary_search_tree.assets/bst_remove_case3_step2.png){ class="animation-figure" } === "<3>" - ![bst_remove_case3_step3](binary_search_tree.assets/bst_remove_case3_step3.png) + ![bst_remove_case3_step3](binary_search_tree.assets/bst_remove_case3_step3.png){ class="animation-figure" } === "<4>" - ![bst_remove_case3_step4](binary_search_tree.assets/bst_remove_case3_step4.png) + ![bst_remove_case3_step4](binary_search_tree.assets/bst_remove_case3_step4.png){ class="animation-figure" }

图 7-21   在二叉搜索树中删除节点(度为 2 )

@@ -1464,7 +1464,7 @@ comments: true 利用中序遍历升序的性质,我们在二叉搜索树中获取有序数据仅需 $O(n)$ 时间,无须进行额外的排序操作,非常高效。 -![二叉搜索树的中序遍历序列](binary_search_tree.assets/bst_inorder_traversal.png) +![二叉搜索树的中序遍历序列](binary_search_tree.assets/bst_inorder_traversal.png){ class="animation-figure" }

图 7-22   二叉搜索树的中序遍历序列

@@ -1488,7 +1488,7 @@ comments: true 然而,如果我们在二叉搜索树中不断地插入和删除节点,可能导致二叉树退化为图 7-23 所示的链表,这时各种操作的时间复杂度也会退化为 $O(n)$ 。 -![二叉搜索树的退化](binary_search_tree.assets/bst_degradation.png) +![二叉搜索树的退化](binary_search_tree.assets/bst_degradation.png){ class="animation-figure" }

图 7-23   二叉搜索树的退化

diff --git a/docs/chapter_tree/binary_tree.md b/docs/chapter_tree/binary_tree.md index a9c21567e..bc6779ec1 100644 --- a/docs/chapter_tree/binary_tree.md +++ b/docs/chapter_tree/binary_tree.md @@ -191,7 +191,7 @@ comments: true **在二叉树中,除叶节点外,其他所有节点都包含子节点和非空子树**。如图 7-1 所示,如果将“节点 2”视为父节点,则其左子节点和右子节点分别是“节点 4”和“节点 5”,左子树是“节点 4 及其以下节点形成的树”,右子树是“节点 5 及其以下节点形成的树”。 -![父节点、子节点、子树](binary_tree.assets/binary_tree_definition.png) +![父节点、子节点、子树](binary_tree.assets/binary_tree_definition.png){ class="animation-figure" }

图 7-1   父节点、子节点、子树

@@ -208,7 +208,7 @@ comments: true - 节点的「深度 depth」:从根节点到该节点所经过的边的数量。 - 节点的「高度 height」:从距离该节点最远的叶节点到该节点所经过的边的数量。 -![二叉树的常用术语](binary_tree.assets/binary_tree_terminology.png) +![二叉树的常用术语](binary_tree.assets/binary_tree_terminology.png){ class="animation-figure" }

图 7-2   二叉树的常用术语

@@ -416,7 +416,7 @@ comments: true 与链表类似,在二叉树中插入与删除节点可以通过修改指针来实现。图 7-3 给出了一个示例。 -![在二叉树中插入与删除节点](binary_tree.assets/binary_tree_add_remove.png) +![在二叉树中插入与删除节点](binary_tree.assets/binary_tree_add_remove.png){ class="animation-figure" }

图 7-3   在二叉树中插入与删除节点

@@ -569,7 +569,7 @@ comments: true 请注意,在中文社区中,完美二叉树常被称为「满二叉树」。 -![完美二叉树](binary_tree.assets/perfect_binary_tree.png) +![完美二叉树](binary_tree.assets/perfect_binary_tree.png){ class="animation-figure" }

图 7-4   完美二叉树

@@ -577,7 +577,7 @@ comments: true 如图 7-5 所示,「完全二叉树 complete binary tree」只有最底层的节点未被填满,且最底层节点尽量靠左填充。 -![完全二叉树](binary_tree.assets/complete_binary_tree.png) +![完全二叉树](binary_tree.assets/complete_binary_tree.png){ class="animation-figure" }

图 7-5   完全二叉树

@@ -585,7 +585,7 @@ comments: true 如图 7-6 所示,「完满二叉树 full binary tree」除了叶节点之外,其余所有节点都有两个子节点。 -![完满二叉树](binary_tree.assets/full_binary_tree.png) +![完满二叉树](binary_tree.assets/full_binary_tree.png){ class="animation-figure" }

图 7-6   完满二叉树

@@ -593,7 +593,7 @@ comments: true 如图 7-7 所示,「平衡二叉树 balanced binary tree」中任意节点的左子树和右子树的高度之差的绝对值不超过 1 。 -![平衡二叉树](binary_tree.assets/balanced_binary_tree.png) +![平衡二叉树](binary_tree.assets/balanced_binary_tree.png){ class="animation-figure" }

图 7-7   平衡二叉树

@@ -604,7 +604,7 @@ comments: true - 完美二叉树是理想情况,可以充分发挥二叉树“分治”的优势。 - 链表则是另一个极端,各项操作都变为线性操作,时间复杂度退化至 $O(n)$ 。 -![二叉树的最佳与最差结构](binary_tree.assets/binary_tree_best_worst_cases.png) +![二叉树的最佳与最差结构](binary_tree.assets/binary_tree_best_worst_cases.png){ class="animation-figure" }

图 7-8   二叉树的最佳与最差结构

diff --git a/docs/chapter_tree/binary_tree_traversal.md b/docs/chapter_tree/binary_tree_traversal.md index 047f1fa49..e9909fb7b 100755 --- a/docs/chapter_tree/binary_tree_traversal.md +++ b/docs/chapter_tree/binary_tree_traversal.md @@ -14,7 +14,7 @@ comments: true 层序遍历本质上属于「广度优先遍历 breadth-first traversal」,它体现了一种“一圈一圈向外扩展”的逐层遍历方式。 -![二叉树的层序遍历](binary_tree_traversal.assets/binary_tree_bfs.png) +![二叉树的层序遍历](binary_tree_traversal.assets/binary_tree_bfs.png){ class="animation-figure" }

图 7-9   二叉树的层序遍历

@@ -335,7 +335,7 @@ comments: true 图 7-10 展示了对二叉树进行深度优先遍历的工作原理。**深度优先遍历就像是绕着整个二叉树的外围“走”一圈**,在每个节点都会遇到三个位置,分别对应前序遍历、中序遍历和后序遍历。 -![二叉搜索树的前、中、后序遍历](binary_tree_traversal.assets/binary_tree_dfs.png) +![二叉搜索树的前、中、后序遍历](binary_tree_traversal.assets/binary_tree_dfs.png){ class="animation-figure" }

图 7-10   二叉搜索树的前、中、后序遍历

@@ -764,37 +764,37 @@ comments: true 2. “归”表示函数返回,代表当前节点已经访问完毕。 === "<1>" - ![前序遍历的递归过程](binary_tree_traversal.assets/preorder_step1.png) + ![前序遍历的递归过程](binary_tree_traversal.assets/preorder_step1.png){ class="animation-figure" } === "<2>" - ![preorder_step2](binary_tree_traversal.assets/preorder_step2.png) + ![preorder_step2](binary_tree_traversal.assets/preorder_step2.png){ class="animation-figure" } === "<3>" - ![preorder_step3](binary_tree_traversal.assets/preorder_step3.png) + ![preorder_step3](binary_tree_traversal.assets/preorder_step3.png){ class="animation-figure" } === "<4>" - ![preorder_step4](binary_tree_traversal.assets/preorder_step4.png) + ![preorder_step4](binary_tree_traversal.assets/preorder_step4.png){ class="animation-figure" } === "<5>" - ![preorder_step5](binary_tree_traversal.assets/preorder_step5.png) + ![preorder_step5](binary_tree_traversal.assets/preorder_step5.png){ class="animation-figure" } === "<6>" - ![preorder_step6](binary_tree_traversal.assets/preorder_step6.png) + ![preorder_step6](binary_tree_traversal.assets/preorder_step6.png){ class="animation-figure" } === "<7>" - ![preorder_step7](binary_tree_traversal.assets/preorder_step7.png) + ![preorder_step7](binary_tree_traversal.assets/preorder_step7.png){ class="animation-figure" } === "<8>" - ![preorder_step8](binary_tree_traversal.assets/preorder_step8.png) + ![preorder_step8](binary_tree_traversal.assets/preorder_step8.png){ class="animation-figure" } === "<9>" - ![preorder_step9](binary_tree_traversal.assets/preorder_step9.png) + ![preorder_step9](binary_tree_traversal.assets/preorder_step9.png){ class="animation-figure" } === "<10>" - ![preorder_step10](binary_tree_traversal.assets/preorder_step10.png) + ![preorder_step10](binary_tree_traversal.assets/preorder_step10.png){ class="animation-figure" } === "<11>" - ![preorder_step11](binary_tree_traversal.assets/preorder_step11.png) + ![preorder_step11](binary_tree_traversal.assets/preorder_step11.png){ class="animation-figure" }

图 7-11   前序遍历的递归过程

diff --git a/docs/chapter_tree/index.md b/docs/chapter_tree/index.md index 7d7bc4649..5044703b0 100644 --- a/docs/chapter_tree/index.md +++ b/docs/chapter_tree/index.md @@ -7,7 +7,7 @@ icon: material/graph-outline
-![树](../assets/covers/chapter_tree.jpg){ width="600" } +![树](../assets/covers/chapter_tree.jpg){ class="cover-image" }
diff --git a/docs/index.md b/docs/index.md index c78f830a9..c5d77e9bc 100644 --- a/docs/index.md +++ b/docs/index.md @@ -8,8 +8,8 @@ hide:

- - + +

《 Hello 算法 》

diff --git a/overrides/stylesheets/extra.css b/overrides/stylesheets/extra.css index 0db2dc4ff..e7a289b66 100644 --- a/overrides/stylesheets/extra.css +++ b/overrides/stylesheets/extra.css @@ -25,7 +25,7 @@ --md-default-fg-color: #adbac7; --md-default-bg-color: #22272e; - --md-code-bg-color: #1D2126; + --md-code-bg-color: #1d2126; --md-code-fg-color: #adbac7; --md-accent-fg-color: #aaa; @@ -43,6 +43,23 @@ color: var(--md-default-fg-color) !important; } +/* Figure class */ +.animation-figure { + border-radius: 0.63rem; + display: block; + margin: 0 auto; +} + +/* Cover image class */ +.cover-image { + width: 28rem; + height: auto; + border-radius: 0.3rem; + box-shadow: var(--md-shadow-z2); + display: block; + margin: 0 auto; +} + /* Center Markdown Tables (requires md_in_html extension) */ .center-table { text-align: center; @@ -82,12 +99,6 @@ text-transform: none; } -/* Image align center */ -.center { - display: block; - margin: 0 auto; -} - /* font-family setting for Win10 */ body { --md-text-font-family: -apple-system, BlinkMacSystemFont,