Skip to content

Commit 2802e6e

Browse files
committed
更新文档
1 parent 2f94b9d commit 2802e6e

4 files changed

Lines changed: 54 additions & 18 deletions

File tree

assets/数据结构和算法.xmind

476 KB
Binary file not shown.

assets/树/二叉搜索树.eddx

136 KB
Binary file not shown.

assets/树/树.eddx

56.4 KB
Binary file not shown.

docs/树和二叉树.md

Lines changed: 54 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
- 每个非根节点可以分为多个不相交的子树。
1515
- 树里面没有环路。
1616

17+
![](https://raw.githubusercontent.com/dunwu/images/dev/snap/20220403163620.png)
18+
1719
### 树的术语
1820

1921
- **节点的度**:一个节点含有的子树的个数称为该节点的度;
@@ -23,18 +25,22 @@
2325
- **父节点**:若一个节点含有子节点,则这个节点称为其子节点的父节点;
2426
- **子节点**:一个节点含有的子树的根节点称为该节点的子节点;
2527
- **兄弟节点**:具有相同父节点的节点互称为兄弟节点;
26-
- 节点的**层次**:从根开始定义起,根为第 1 层,根的子节点为第 2 层,以此类推;
27-
- **深度**:对于任意节点 n,n 的深度为从根到 n 的唯一路径长,根的深度为 0;
28-
- **高度**:对于任意节点 n,n 的高度为从 n 到一片树叶的最长路径长,所有树叶的高度为 0;
2928
- **堂兄弟节点**:父节点在同一层的节点互为堂兄弟;
3029
- **节点的祖先**:从根到该节点所经分支上的所有节点;
3130
- **子孙**:以某节点为根的子树中任一节点都称为该节点的子孙。
3231
- **森林**:由 m(m>=0)棵互不相交的树的集合称为森林;
3332

33+
![](https://raw.githubusercontent.com/dunwu/images/dev/snap/20220403164732.png)
34+
35+
- **节点的高度**:节点到叶子节点的最长路径。高度是从下往上度量。
36+
- **节点的深度**:根节点到该节点的最长路径。深度是从上往下度量。
37+
- **节点的层次**:节点的深度 + 1。
38+
- **树的高度**:根节点的高度。
39+
3440
### 树的性质
3541

3642
- 树中的节点数等于所有节点的度数加 1。
37-
- 度为 m 的树中第 i 层上至多有 $$m^{i-1}$$ 个节点($$i ≥ 1$$)。
43+
- 度为 m 的树中第 `i` 层上至多有 $$m^{i-1}$$ 个节点($$i ≥ 1$$)。
3844
- 高度为 h 的 m 次树至多有 $$(m^h-1)/(m-1)$$ 个节点。
3945
- 具有 n 个节点的 m 次树的最小高度为 $$\log_m{(n(m-1)+1)}$$
4046

@@ -54,7 +60,7 @@
5460

5561
## 二叉树
5662

57-
二叉树(Binary Tree)是 N 个节点的有限集合,它或者是空树,或者是由一个根节点及两棵不想交的且分别称为左右子树的二叉树所组成
63+
二叉树中的每个节点最多有两个子节点,分别是**左子节点****右子节点**
5864

5965
### 二叉树的性质
6066

@@ -65,55 +71,85 @@
6571

6672
### 满二叉树
6773

68-
定义:高度为 h,并且由 **2<sup>h</sup>–1** 个结点的二叉树,被称为满二叉树
74+
除了叶子节点之外,每个节点都有左右两个子节点,这种二叉树就叫作**满二叉树**
6975

70-
![img](https://raw.githubusercontent.com/dunwu/images/dev/cs/data-structure/tree/满二叉树.png)
76+
![](https://raw.githubusercontent.com/dunwu/images/dev/snap/20220403183927.png)
7177

7278
### 完全二叉树
7379

74-
定义:一棵二叉树中,只有最下面两层结点的度可以小于 2,并且最下一层的叶结点集中在靠左的若干位置上。这样的二叉树称为完全二叉树
80+
叶子节点都在最底下两层,最后一层的叶子节点都靠左排列,并且除了最后一层,其他层的节点个数都要达到最大,这种二叉树叫作**完全二叉树**
7581

7682
特点:叶子结点只能出现在最下层和次下层,且最下层的叶子结点集中在树的左部。显然,一棵满二叉树必定是一棵完全二叉树,而完全二叉树未必是满二叉树。
7783

78-
![img](https://raw.githubusercontent.com/dunwu/images/dev/cs/data-structure/tree/完全二叉树.png)
84+
![](https://raw.githubusercontent.com/dunwu/images/dev/snap/20220403183640.png)
85+
86+
存储一棵二叉树,有两种方法,一种是基于指针或者引用的二叉链式存储法,一种是基于数组的顺序存储法。
87+
88+
**二叉链式存储法**
89+
90+
每个节点有三个字段,其中一个存储数据,另外两个是指向左右子节点的指针。
91+
92+
![](https://raw.githubusercontent.com/dunwu/images/dev/snap/20220403212249.png)
93+
94+
**顺序存储法**
95+
96+
![](https://raw.githubusercontent.com/dunwu/images/dev/snap/20220403214627.png)
97+
98+
如果节点 X 存储在数组中下标为 i 的位置,下标为 2 _ i 的位置存储的就是左子节点,下标为 2 _ i + 1 的位置存储的就是右子节点。反过来,下标为 i/2 的位置存储就是它的父节点。通过这种方式,我们只要知道根节点存储的位置(一般情况下,为了方便计算子节点,根节点会存储在下标为 1 的位置),这样就可以通过下标计算,把整棵树都串起来。
99+
100+
如果是非完全二叉树,其实会浪费比较多的数组存储空间。所以,如果某棵二叉树是一棵完全二叉树,那用数组存储无疑是最节省内存的一种方式。因为数组的存储方式并不需要像链式存储法那样,要存储额外的左右子节点的指针。这也是 为什么完全二叉树要求最后一层的子节点都靠左的原因。
101+
102+
### 二叉树的遍历
103+
104+
二叉树的遍历有三种方式:
105+
106+
- **前序遍历**:对于树中的任意节点来说,先打印这个节点,然后再打印它的左子树,最后打印它的右子树。
107+
- **中序遍历**:对于树中的任意节点来说,先打印它的左子树,然后再打印它本身,最后打印它的右子树。
108+
- **后序遍历**是指,对于树中的任意节点来说,先打印它的左子树,然后再打印它的右子树,最后打印这个节点本身。
109+
110+
![](https://raw.githubusercontent.com/dunwu/images/dev/snap/20220404201713.png)
79111

80112
## 二叉查找树
81113

82114
二叉查找树是二叉树中最常用的一种类型,也叫二叉搜索树。顾名思义,二叉查找树是为了实现快速查找而生的。不过,它不仅仅支持快速查找一个数据,还支持快速插入、删除一个数据。
83115

84116
**二叉查找树要求,在树中的任意一个节点,其左子树中的每个节点的值,都要小于这个节点的值,而右子树节点的值都大于这个节点的值。**
85117

86-
![img](https://raw.githubusercontent.com/dunwu/images/dev/snap/20220310172814.jpg)
118+
![](https://raw.githubusercontent.com/dunwu/images/dev/snap/20220405172359.png)
87119

88120
### 二叉查找树的查找
89121

90122
首先,我们看如何在二叉查找树中查找一个节点。我们先取根节点,如果它等于我们要查找的数据,那就返回。如果要查找的数据比根节点的值小,那就在左子树中递归查找;如果要查找的数据比根节点的值大,那就在右子树中递归查找。
91123

92-
![img](https://raw.githubusercontent.com/dunwu/images/dev/snap/20220310172913.jpg)
124+
![](https://raw.githubusercontent.com/dunwu/images/dev/snap/20220405172537.png)
93125

94126
### 二叉查找树的插入
95127

96128
如果要插入的数据比节点的数据大,并且节点的右子树为空,就将新数据直接插到右子节点的位置;如果不为空,就再递归遍历右子树,查找插入位置。同理,如果要插入的数据比节点数值小,并且节点的左子树为空,就将新数据插入到左子节点的位置;如果不为空,就再递归遍历左子树,查找插入位置。
97129

98-
![img](https://raw.githubusercontent.com/dunwu/images/dev/snap/20220310172949.jpg)
130+
![](https://raw.githubusercontent.com/dunwu/images/dev/snap/20220405172549.png)
99131

100132
### 二叉查找树的删除
101133

102-
第一种情况是,如果要删除的节点没有子节点,我们只需要直接将父节点中,指向要删除节点的指针置为 null。比如图中的删除节点 55。
134+
第一种情况是,如果要删除的节点没有子节点,我们只需要直接将父节点中,指向要删除节点的指针置为 null。
135+
136+
第二种情况是,如果要删除的节点只有一个子节点(只有左子节点或者右子节点),我们只需要更新父节点中,指向要删除节点的指针,让它指向要删除节点的子节点就可以了。
137+
138+
![](https://raw.githubusercontent.com/dunwu/images/dev/snap/20220405200219.png)
103139

104-
第二种情况是,如果要删除的节点只有一个子节点(只有左子节点或者右子节点),我们只需要更新父节点中,指向要删除节点的指针,让它指向要删除节点的子节点就可以了。比如图中的删除节点 13。
140+
![](https://raw.githubusercontent.com/dunwu/images/dev/snap/20220405200234.png)
105141

106-
第三种情况是,如果要删除的节点有两个子节点,这就比较复杂了。我们需要找到这个节点的右子树中的最小节点,把它替换到要删除的节点上。然后再删除掉这个最小节点,因为最小节点肯定没有左子节点(如果有左子结点,那就不是最小节点了),所以,我们可以应用上面两条规则来删除这个最小节点。比如图中的删除节点 18。
142+
第三种情况是,如果要删除的节点有两个子节点,这就比较复杂了。我们需要找到这个节点的右子树中的最小节点,把它替换到要删除的节点上。然后再删除掉这个最小节点,因为最小节点肯定没有左子节点(如果有左子结点,那就不是最小节点了),所以,我们可以应用上面两条规则来删除这个最小节点。
107143

108-
![img](https://raw.githubusercontent.com/dunwu/images/dev/snap/20220310173049.jpg)
144+
![](https://raw.githubusercontent.com/dunwu/images/dev/snap/20220405200456.png)
109145

110146
### 二叉查找树的时间复杂度
111147

112-
不管操作是插入、删除还是查找,**时间复杂度其实都跟树的高度成正比,也就是 O(logn)**
148+
不管操作是插入、删除还是查找,**时间复杂度其实都跟树的高度成正比,也就是 O(log n)**
113149

114150
二叉查找树的形态各式各样。比如这个图中,对于同一组数据,我们构造了三种二叉查找树。它们的查找、插入、删除操作的执行效率都是不一样的。图中第一种二叉查找树,根节点的左右子树极度不平衡,已经退化成了链表,所以查找的时间复杂度就变成了 O(n)。
115151

116-
![img](https://raw.githubusercontent.com/dunwu/images/dev/snap/20220310173253.jpg)
152+
![](https://raw.githubusercontent.com/dunwu/images/dev/snap/20220405234630.png)
117153

118154
### 为什么需要二叉查找树
119155

0 commit comments

Comments
 (0)