数据结构——树
本文最后更新于:Saturday, August 22nd 2020, 3:22 pm
1、树的相关术语
度:一个结点的子结点数目。树的度指度数最大的那个结点的度
叶结点,分支结点
度为0的结点——
叶结点;度大于0的结点——分支结点结点的层数
(1) root(T)——层数为0
(2)其余结点层数为前驱结点层数 + 1
路径
同时满足$V_{i+1} $是$V_i$ (m <=i<=m+k-1)的子结点,则称结点序列为$V_m$ 到 $V_{m+1}$ 的路径。该路径经历的边数
k称为路径长度子孙结点、祖先结点
一棵树中若存在结点$V_m$ 到$V_n$的路径,则称为$V_n$是$V_m$的
子孙结点,为$V_n$是$V_m$的祖先结点。树的高度
树中结点的最大层数。
2、二叉树引理
设T为n个结点构成的二叉树,其中叶子节点个数为n~0~,度为2的结点个数为n~2~,则有:
证明:
满二叉树和完全二叉树满二叉树:每一层都充满了结点,一颗非空高度为k的满二叉树,有$2^{k+1}+1$个结点。
完全二叉树:只有最后一层不满,其余层全是满的。且最后一层从最左边开始填充。

若将n个结点的完全二叉树按
层次顺序从1开始编号,则对编号为i(1 <= i <= n)的结点有:- 若 i $\neq$ = 1,则编号为i的结点父节点编号为$\left \lfloor i/2 \right \rfloor$
- 若 2i $\leq$ n, 则编号为i的结点左孩子编号为2i,否则无左孩子
- 若 2i+1 $\leq$ n, 则编号为i的右孩子编号为2i + 1,否者无有孩子。
推论:一棵具有n个结点的完全二叉树,分支结点个数为$\left \lfloor n/2 \right \rfloor$。(最后一个结点n的父节点为$\left \lfloor n/2 \right \rfloor$)

具有n个结点的完全二叉树高度为$\left \lfloor log_2n \right \rfloor$
证明:
3、二叉树递归遍历
先根遍历(Preorder Traversal)
1
2
3
4
5Preorder(t)
IF t = NULL Then return.
print(data(t)). // 直接打印出根,然后往左找
Preorder(left(t))
Preorder(right(t))中根遍历(Inorder Traversal)
1
2
3
4
5Inorder( t )
IF t = NULL Then return.
INorder(left(t))
print(data(t))
INorder(right(t))后根遍历(Postorder Traversal)
1
2
3
4
5Postorder( t )
IF t = NULL Then return.
Postorder(left(t))
Postorder(right(t))
print(data(t))4、二叉树非递归遍历
1、先根遍历
策略:
- 一直往左走,只要不为空就访问,访问完进栈。当为空时弹栈,访问右子树。
- 进栈的是访问结点的右子树。
1 | |
2、中根遍历
策略:一直往走,中途结点全部压入栈。直到左子树为空,出栈访问,把右子树压栈。然后继续。
1 | |
3、二叉树的形态
中根和先根算法进出栈的顺序是一样的。进栈序列:先根序列;出栈序列:中根序列
假设先根序列为1…n时,有多少种中跟序列就有多少种二叉树形态。(中根+先根唯一确定一棵树)
即n个数有多少种出栈方式:
4、后根遍历
策略:
一直往左走,压栈。如果遇到右子树为空或者右子树已经遍历过了就出栈访问它。否者就访问右子树。开始新的遍历。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19Create(S); p = root(T);pre=NULL //pre存储p之前访问过的结点。
while(true)
{
while(p)
{
S.push(p);
p = p.left;
}
if(S.empty) return;
p = peak(S);
if(p.right == NULL or p.right == pre)
{
S.pop();
process(p.data);
pre = p; //始终记录上一次访问过的结点
p = NULL; //为了下一个循环中去父节点的右结点。
}
else p = p.right;
}
允许多次进出栈。栈元素为二元组(结点,标号i)
i = 1 :没有访问结点的任何子树。准备遍历其左子树
i = 2 :遍历完左子树,准备遍历右子树
i = 3:遍历完右子树
初始化:(root(T), 1)压入栈。弹栈,判断出栈元素标号:
i = 1, 则将(p, 2) 压栈,准备遍历左子树。把(left(p), 1)压栈
i = 2, 则将(p, 3)压栈,准备遍历右子树,即把(right(p),1)压栈
i = 3, 访问结点p
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22create(S);S.push((root(T),1));
while(!S.isempty())
{
(p,i) = S.pop();
if(p) // 防止空指针
{
if(i == 1)
{
S.push((p,2));
S.push((p.left, 1));
}
if(i == 2)
{
S.push((p,3));
S.push((p.right,1));
}
else
{
process(p.data)
}
}
}
5、层次遍历
1 | |
由中根遍历 + (层次/先根/后根)唯一确定一棵树
5、二叉树相关应用
1、搜索给定结点的父节点
1 | |
2、释放二叉树
1 | |
3、删除一颗小树
删除给定结点以及其左右子树
1
2
3
4
5
6
7
8
9
10
11
12DILR(t,p)
if(!p) return; // 为空
if(p == t)
{
Del(p);
t = NULL //删除整棵树 t还有定义,可以再赋值。
}
q = Father(t,p);
if(q.left == p) q.left = NULL; //修改原来指向p的指针为空
if(q.right == p) q.right == NULL;
Del(p);4、创建二叉树
由于先根序列不能体现左右子树为空的情况,所有用#表示空子结点。则可以唯一表示一课树。

1 | |
5、复制一颗二叉树
1 | |
6、后序遍历求结点个数
1 | |
7、计算二叉树的高度
1 | |
8、二叉树首尾结点
先, 中, 后(
不使用递归,不适应栈)

1 | |
本博客所有文章除特别声明外,均采用 CC BY-SA 3.0协议 。转载请注明出处!
