mirror of
https://github.com/krahets/hello-algo.git
synced 2026-04-02 10:13:00 +08:00
feat: Revised the book (#978)
* Sync recent changes to the revised Word. * Revised the preface chapter * Revised the introduction chapter * Revised the computation complexity chapter * Revised the chapter data structure * Revised the chapter array and linked list * Revised the chapter stack and queue * Revised the chapter hashing * Revised the chapter tree * Revised the chapter heap * Revised the chapter graph * Revised the chapter searching * Reivised the sorting chapter * Revised the divide and conquer chapter * Revised the chapter backtacking * Revised the DP chapter * Revised the greedy chapter * Revised the appendix chapter * Revised the preface chapter doubly * Revised the figures
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
# 二叉树
|
||||
|
||||
「二叉树 binary tree」是一种非线性数据结构,代表着祖先与后代之间的派生关系,体现着“一分为二”的分治逻辑。与链表类似,二叉树的基本单元是节点,每个节点包含:值、左子节点引用、右子节点引用。
|
||||
「二叉树 binary tree」是一种非线性数据结构,代表“祖先”与“后代”之间的派生关系,体现了“一分为二”的分治逻辑。与链表类似,二叉树的基本单元是节点,每个节点包含值、左子节点引用和右子节点引用。
|
||||
|
||||
=== "Python"
|
||||
|
||||
@@ -205,7 +205,7 @@
|
||||
|
||||
!!! tip
|
||||
|
||||
请注意,我们通常将“高度”和“深度”定义为“走过边的数量”,但有些题目或教材可能会将其定义为“走过节点的数量”。在这种情况下,高度和深度都需要加 1 。
|
||||
请注意,我们通常将“高度”和“深度”定义为“经过的边的数量”,但有些题目或教材可能会将其定义为“经过的节点的数量”。在这种情况下,高度和深度都需要加 1 。
|
||||
|
||||
## 二叉树基本操作
|
||||
|
||||
@@ -223,7 +223,7 @@
|
||||
n3 = TreeNode(val=3)
|
||||
n4 = TreeNode(val=4)
|
||||
n5 = TreeNode(val=5)
|
||||
# 构建引用指向(即指针)
|
||||
# 构建节点之间的引用(指针)
|
||||
n1.left = n2
|
||||
n1.right = n3
|
||||
n2.left = n4
|
||||
@@ -240,7 +240,7 @@
|
||||
TreeNode* n3 = new TreeNode(3);
|
||||
TreeNode* n4 = new TreeNode(4);
|
||||
TreeNode* n5 = new TreeNode(5);
|
||||
// 构建引用指向(即指针)
|
||||
// 构建节点之间的引用(指针)
|
||||
n1->left = n2;
|
||||
n1->right = n3;
|
||||
n2->left = n4;
|
||||
@@ -256,7 +256,7 @@
|
||||
TreeNode n3 = new TreeNode(3);
|
||||
TreeNode n4 = new TreeNode(4);
|
||||
TreeNode n5 = new TreeNode(5);
|
||||
// 构建引用指向(即指针)
|
||||
// 构建节点之间的引用(指针)
|
||||
n1.left = n2;
|
||||
n1.right = n3;
|
||||
n2.left = n4;
|
||||
@@ -273,7 +273,7 @@
|
||||
TreeNode n3 = new(3);
|
||||
TreeNode n4 = new(4);
|
||||
TreeNode n5 = new(5);
|
||||
// 构建引用指向(即指针)
|
||||
// 构建节点之间的引用(指针)
|
||||
n1.left = n2;
|
||||
n1.right = n3;
|
||||
n2.left = n4;
|
||||
@@ -290,7 +290,7 @@
|
||||
n3 := NewTreeNode(3)
|
||||
n4 := NewTreeNode(4)
|
||||
n5 := NewTreeNode(5)
|
||||
// 构建引用指向(即指针)
|
||||
// 构建节点之间的引用(指针)
|
||||
n1.Left = n2
|
||||
n1.Right = n3
|
||||
n2.Left = n4
|
||||
@@ -306,7 +306,7 @@
|
||||
let n3 = TreeNode(x: 3)
|
||||
let n4 = TreeNode(x: 4)
|
||||
let n5 = TreeNode(x: 5)
|
||||
// 构建引用指向(即指针)
|
||||
// 构建节点之间的引用(指针)
|
||||
n1.left = n2
|
||||
n1.right = n3
|
||||
n2.left = n4
|
||||
@@ -323,7 +323,7 @@
|
||||
n3 = new TreeNode(3),
|
||||
n4 = new TreeNode(4),
|
||||
n5 = new TreeNode(5);
|
||||
// 构建引用指向(即指针)
|
||||
// 构建节点之间的引用(指针)
|
||||
n1.left = n2;
|
||||
n1.right = n3;
|
||||
n2.left = n4;
|
||||
@@ -340,7 +340,7 @@
|
||||
n3 = new TreeNode(3),
|
||||
n4 = new TreeNode(4),
|
||||
n5 = new TreeNode(5);
|
||||
// 构建引用指向(即指针)
|
||||
// 构建节点之间的引用(指针)
|
||||
n1.left = n2;
|
||||
n1.right = n3;
|
||||
n2.left = n4;
|
||||
@@ -357,7 +357,7 @@
|
||||
TreeNode n3 = new TreeNode(3);
|
||||
TreeNode n4 = new TreeNode(4);
|
||||
TreeNode n5 = new TreeNode(5);
|
||||
// 构建引用指向(即指针)
|
||||
// 构建节点之间的引用(指针)
|
||||
n1.left = n2;
|
||||
n1.right = n3;
|
||||
n2.left = n4;
|
||||
@@ -373,7 +373,7 @@
|
||||
let n3 = TreeNode::new(3);
|
||||
let n4 = TreeNode::new(4);
|
||||
let n5 = TreeNode::new(5);
|
||||
// 构建引用指向(即指针)
|
||||
// 构建节点之间的引用(指针)
|
||||
n1.borrow_mut().left = Some(n2.clone());
|
||||
n1.borrow_mut().right = Some(n3);
|
||||
n2.borrow_mut().left = Some(n4);
|
||||
@@ -390,7 +390,7 @@
|
||||
TreeNode *n3 = newTreeNode(3);
|
||||
TreeNode *n4 = newTreeNode(4);
|
||||
TreeNode *n5 = newTreeNode(5);
|
||||
// 构建引用指向(即指针)
|
||||
// 构建节点之间的引用(指针)
|
||||
n1->left = n2;
|
||||
n1->right = n3;
|
||||
n2->left = n4;
|
||||
@@ -546,13 +546,13 @@
|
||||
|
||||
!!! note
|
||||
|
||||
需要注意的是,插入节点可能会改变二叉树的原有逻辑结构,而删除节点通常意味着删除该节点及其所有子树。因此,在二叉树中,插入与删除操作通常是由一套操作配合完成的,以实现有实际意义的操作。
|
||||
需要注意的是,插入节点可能会改变二叉树的原有逻辑结构,而删除节点通常意味着删除该节点及其所有子树。因此,在二叉树中,插入与删除通常是由一套操作配合完成的,以实现有实际意义的操作。
|
||||
|
||||
## 常见二叉树类型
|
||||
|
||||
### 完美二叉树
|
||||
|
||||
「完美二叉树 perfect binary tree」所有层的节点都被完全填满。在完美二叉树中,叶节点的度为 $0$ ,其余所有节点的度都为 $2$ ;若树高度为 $h$ ,则节点总数为 $2^{h+1} - 1$ ,呈现标准的指数级关系,反映了自然界中常见的细胞分裂现象。
|
||||
如下图所示,「完美二叉树 perfect binary tree」所有层的节点都被完全填满。在完美二叉树中,叶节点的度为 $0$ ,其余所有节点的度都为 $2$ ;若树的高度为 $h$ ,则节点总数为 $2^{h+1} - 1$ ,呈现标准的指数级关系,反映了自然界中常见的细胞分裂现象。
|
||||
|
||||
!!! tip
|
||||
|
||||
@@ -580,20 +580,20 @@
|
||||
|
||||
## 二叉树的退化
|
||||
|
||||
下图展示了二叉树的理想与退化状态。当二叉树的每层节点都被填满时,达到“完美二叉树”;而当所有节点都偏向一侧时,二叉树退化为“链表”。
|
||||
下图展示了二叉树的理想结构与退化结构。当二叉树的每层节点都被填满时,达到“完美二叉树”;而当所有节点都偏向一侧时,二叉树退化为“链表”。
|
||||
|
||||
- 完美二叉树是理想情况,可以充分发挥二叉树“分治”的优势。
|
||||
- 链表则是另一个极端,各项操作都变为线性操作,时间复杂度退化至 $O(n)$ 。
|
||||
|
||||

|
||||

|
||||
|
||||
如下表所示,在最佳和最差结构下,二叉树的叶节点数量、节点总数、高度等达到极大或极小值。
|
||||
如下表所示,在最佳结构和最差结构下,二叉树的叶节点数量、节点总数、高度等达到极大值或极小值。
|
||||
|
||||
<p align="center"> 表 <id> 二叉树的最佳与最差情况 </p>
|
||||
<p align="center"> 表 <id> 二叉树的最佳结构与最差结构 </p>
|
||||
|
||||
| | 完美二叉树 | 链表 |
|
||||
| ----------------------- | ------------------ | ------- |
|
||||
| 第 $i$ 层的节点数量 | $2^{i-1}$ | $1$ |
|
||||
| 高度 $h$ 树的叶节点数量 | $2^h$ | $1$ |
|
||||
| 高度 $h$ 树的节点总数 | $2^{h+1} - 1$ | $h + 1$ |
|
||||
| 节点总数 $n$ 树的高度 | $\log_2 (n+1) - 1$ | $n - 1$ |
|
||||
| | 完美二叉树 | 链表 |
|
||||
| --------------------------- | ------------------ | ------- |
|
||||
| 第 $i$ 层的节点数量 | $2^{i-1}$ | $1$ |
|
||||
| 高度为 $h$ 的树的叶节点数量 | $2^h$ | $1$ |
|
||||
| 高度为 $h$ 的树的节点总数 | $2^{h+1} - 1$ | $h + 1$ |
|
||||
| 节点总数为 $n$ 的树的高度 | $\log_2 (n+1) - 1$ | $n - 1$ |
|
||||
|
||||
Reference in New Issue
Block a user