This commit is contained in:
krahets
2023-11-09 05:13:48 +08:00
parent 9701430089
commit 0105644232
83 changed files with 516 additions and 509 deletions
@@ -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" }
<p align="center"> 图 7-12 &nbsp; 完美二叉树的数组表示 </p>
@@ -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" }
<p align="center"> 图 7-13 &nbsp; 层序遍历序列对应多种二叉树可能性 </p>
@@ -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" }
<p align="center"> 图 7-14 &nbsp; 任意类型二叉树的数组表示 </p>
@@ -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" }
<p align="center"> 图 7-15 &nbsp; 完全二叉树的数组表示 </p>
+12 -12
View File
@@ -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" }
<p align="center"> 图 7-24 &nbsp; AVL 树在删除节点后发生退化 </p>
再例如,在图 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" }
<p align="center"> 图 7-25 &nbsp; AVL 树在插入节点后发生退化 </p>
@@ -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" }
<p align="center"> 图 7-26 &nbsp; 右旋操作步骤 </p>
如图 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" }
<p align="center"> 图 7-27 &nbsp; 有 grandChild 的右旋操作 </p>
@@ -856,13 +856,13 @@ AVL 树的特点在于“旋转”操作,它能够在不影响二叉树的中
相应的,如果考虑上述失衡二叉树的“镜像”,则需要执行图 7-28 所示的“左旋”操作。
![左旋操作](avl_tree.assets/avltree_left_rotate.png)
![左旋操作](avl_tree.assets/avltree_left_rotate.png){ class="animation-figure" }
<p align="center"> 图 7-28 &nbsp; 左旋操作 </p>
同理,如图 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" }
<p align="center"> 图 7-29 &nbsp; 有 grandChild 的左旋操作 </p>
@@ -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" }
<p align="center"> 图 7-30 &nbsp; 先左旋后右旋 </p>
@@ -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" }
<p align="center"> 图 7-31 &nbsp; 先右旋后左旋 </p>
@@ -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" }
<p align="center"> 图 7-32 &nbsp; AVL 树的四种旋转情况 </p>
+14 -14
View File
@@ -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" }
<p align="center"> 图 7-16 &nbsp; 二叉搜索树 </p>
@@ -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" }
<p align="center"> 图 7-17 &nbsp; 二叉搜索树查找节点示例 </p>
@@ -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" }
<p align="center"> 图 7-18 &nbsp; 在二叉搜索树中插入节点 </p>
@@ -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" }
<p align="center"> 图 7-19 &nbsp; 在二叉搜索树中删除节点(度为 0 ) </p>
如图 7-20 所示,当待删除节点的度为 $1$ 时,将待删除节点替换为其子节点即可。
![在二叉搜索树中删除节点(度为 1 ](binary_search_tree.assets/bst_remove_case2.png)
![在二叉搜索树中删除节点(度为 1 ](binary_search_tree.assets/bst_remove_case2.png){ class="animation-figure" }
<p align="center"> 图 7-20 &nbsp; 在二叉搜索树中删除节点(度为 1 ) </p>
@@ -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" }
<p align="center"> 图 7-21 &nbsp; 在二叉搜索树中删除节点(度为 2 ) </p>
@@ -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" }
<p align="center"> 图 7-22 &nbsp; 二叉搜索树的中序遍历序列 </p>
@@ -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" }
<p align="center"> 图 7-23 &nbsp; 二叉搜索树的退化 </p>
+8 -8
View File
@@ -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" }
<p align="center"> 图 7-1 &nbsp; 父节点、子节点、子树 </p>
@@ -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" }
<p align="center"> 图 7-2 &nbsp; 二叉树的常用术语 </p>
@@ -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" }
<p align="center"> 图 7-3 &nbsp; 在二叉树中插入与删除节点 </p>
@@ -569,7 +569,7 @@ comments: true
请注意,在中文社区中,完美二叉树常被称为「满二叉树」。
![完美二叉树](binary_tree.assets/perfect_binary_tree.png)
![完美二叉树](binary_tree.assets/perfect_binary_tree.png){ class="animation-figure" }
<p align="center"> 图 7-4 &nbsp; 完美二叉树 </p>
@@ -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" }
<p align="center"> 图 7-5 &nbsp; 完全二叉树 </p>
@@ -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" }
<p align="center"> 图 7-6 &nbsp; 完满二叉树 </p>
@@ -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" }
<p align="center"> 图 7-7 &nbsp; 平衡二叉树 </p>
@@ -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" }
<p align="center"> 图 7-8 &nbsp; 二叉树的最佳与最差结构 </p>
+13 -13
View File
@@ -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" }
<p align="center"> 图 7-9 &nbsp; 二叉树的层序遍历 </p>
@@ -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" }
<p align="center"> 图 7-10 &nbsp; 二叉搜索树的前、中、后序遍历 </p>
@@ -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" }
<p align="center"> 图 7-11 &nbsp; 前序遍历的递归过程 </p>
+1 -1
View File
@@ -7,7 +7,7 @@ icon: material/graph-outline
<div class="center-table" markdown>
![](../assets/covers/chapter_tree.jpg){ width="600" }
![](../assets/covers/chapter_tree.jpg){ class="cover-image" }
</div>