文章目录
1. 什么是树
树是一种数据类型,小区、街道、家乡都有。确实,它有一个特点:树枝永远不会重新会合。
树由许多个节点构成,每个节点会引出其他的节点,其他的节点无法汇合。也有节点后面不再引出节点,这种节点叫做叶子。树的起点叫做根节点,其他的叫做子节点。
这是一棵树
这不是一棵树,因为D和E相连了
2. 经典二叉树
2.1 二叉树的定义
二叉树,顾名思义就是任意一个节点最多只有两个子节点的树,其中自己左下方的节点叫左孩子,右下方的节点叫右孩子
2.2 完全二叉树
叶子节点都在最底下两层,最后一层的叶子节点都靠左排列,并且除了最后一层,其他层的节点个数都要达到最大,这种二叉树叫做完全二叉树。
2.3 满二叉树
满二叉树确保了每一层节点数都是最多的
2.4 存储方式
上图有以下存储方式
2.4.1 二维存储方式
从根节点开始,将节点存入
t
r
e
e
0
,
0
tree_{0, 0}
tree0,0 的位置
T
r
e
e
i
,
j
的左孩子位置
=
T
r
e
e
i
+
1
,
j
∗
2
Tree_{i, j}的左孩子位置=Tree_{i+1, j*2}
Treei,j的左孩子位置=Treei+1,j∗2
T
r
e
e
i
,
j
的右孩子位置
=
T
r
e
e
i
+
1
,
j
∗
2
+
1
Tree_{i, j}的右孩子位置=Tree_{i+1, j*2+1}
Treei,j的右孩子位置=Treei+1,j∗2+1
T
r
e
e
i
,
j
的父亲位置
=
T
r
e
e
i
−
1
,
⌊
j
÷
2
⌋
Tree_{i, j}的父亲位置=Tree_{i-1, \left \lfloor j\div2 \right \rfloor}
Treei,j的父亲位置=Treei−1,⌊j÷2⌋
Tree | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | j |
---|---|---|---|---|---|---|---|---|---|
0 | A | ||||||||
1 | B | C | |||||||
2 | D | E | F | G | |||||
3 | H | I | J | K | L | M | N | O | |
i |
但这种方式耗内存,不太建议
2.4.2 一维存储方式
从根节点开始,将根节点存入第1项
左孩子位置
=
当前节点
×
2
左孩子位置=当前节点\times2
左孩子位置=当前节点×2
右孩子位置
=
当前节点
×
2
+
1
右孩子位置=当前节点\times2+1
右孩子位置=当前节点×2+1
父亲位置
=
⌊
当前节点
÷
2
⌋
父亲位置=\left \lfloor当前节点\div2 \right \rfloor
父亲位置=⌊当前节点÷2⌋
i | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
T r e e i Tree_i Treei | A | B | C | D | E | F | G | F | I | J | K | L | M | N | O |
2.5 节点计算公式
二叉树第
h
层最大节点个数
=
2
h
−
1
二叉树第h层最大节点个数=2^{h-1}
二叉树第h层最大节点个数=2h−1
二叉树截止第
h
层最大总节点数
=
2
h
−
1
二叉树截止第h层最大总节点数=2^h-1
二叉树截止第h层最大总节点数=2h−1
3. 二叉树
3.1 存储方式
3.1.1 父亲表示法
使用一个数组 F F F ,其中 F [ i ] F[i] F[i] 等于自己的父亲编号
i | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
T r e e i Tree_i Treei | - | A | A | B | B | C | C | D | D | E | E | F | F | G | G |
注意现实的题目一般不会使用字符作为节点名称
以下代码先输入两个数字 n n n 和 m m m ,表示有n个节点,m条边
接下来m行,每行两个数 v v v 和 w w w ,表示节点v和节点w连着一条边( v , w ≠ 0 v, w \neq 0 v,w=0)
再输入一个数字 x x x ,从节点x开始往节点1遍历
输出一串数字,表示从节点x到节点1的路程
#include <bits/stdc++.h>
using namespace std;
const int N = 100050; // 可替换为节点最大值
int F[N];
int v, w;
int n, m, x;
int main () {
cin >> n >> m;
for(int i=1; i<=m; i++) {
cin >> v >> w;
F[w] = v;
}
cin >> x;
cout << x;
while(x!=1) {
x = F[x];
cout << "->" << x;
}
return 0;
}
3.1.2 儿子表示法
使用一个二维数组
G
G
G ,其中每一项表示它的儿子
A
:
B
C
A: B~C
A:B C
B
:
D
E
B: D~E
B:D E
C
:
F
G
C: F~G
C:F G
D
:
H
I
D: H~I
D:H I
E
:
J
K
E: J~K
E:J K
F
:
L
M
F: L~M
F:L M
G
:
N
O
G: N~O
G:N O
.
.
.
其余为空
...其余为空
...其余为空
以下代码先输入两个数字 n n n 和 m m m ,表示有n个节点,m条边
接下来m行,每行两个数 v v v 和 w w w ,表示节点v和节点w连着一条边( v , w ≠ 0 v, w \neq 0 v,w=0)
最终输出几行数字,表示从1往下遍历的路径
以这张图片为例
输入:9 8 1 2 1 3 2 4 2 5 3 6 3 7 4 8 4 9
最终输出:
1->2->4->8 1->2->4->9 1->2->5 1->3->6 1->3->7
#include <bits/stdc++.h>
using namespace std;
const int N = 1050; // 根据 n 的大小设定
int v, w;
int n, m;
vector<int> G[N];
bool vis[N]; // vis[i] 表示第 i 个节点是否被遍历过
vector<int> res; // 表示当前路径
void traverse (int x) {
if(G[x].empty()) { //表示已经无路可走了
for(auto it : res) {
cout << it << "->";
}
cout << x << endl;
return; // 切记return,否则后面会继续遍历的!!!
}
res.push_back(x);
for(auto it : G[x]) {
if(!vis[it]) {
vis[it] = 1;
traverse(it);
}
}
res.pop_back();
}
int main () {
cin >> n >> m;
for(int i=1; i<=m; i++) {
cin >> v >> w;
G[v].push_back(w);
}
vis[1] = 1;
traverse(1);
return 0;
}