前言
本文旨在复习数据结构之二叉树,并结合一道例题详细分析。
一、二叉树
定义:如果一棵树拥有1个根结点,且所有结点的子结点数都不超过2(不是只有0和2),那么这棵树成为有根二叉树。如下图所示就是一个二叉树的例子。
二、相关概念简介
1、根:根是唯一一个没有父结点的结点。
2、叶结点:我们将没有子结点的结点成为外部结点(external node)或叶结点(leaf)。
3、内部结点:除叶结点以外的结点称为内部结点(internal node)。
三、例题
设给定二叉树拥有 n n n个结点,编号分别为 0 0 0至 n − 1 n-1 n−1。如下图所示:
1.输入
第 1 1 1行输入结点个数 n n n,接下来 n n n行按照id left right的顺序依次输入各结点的信息,每个结点占 1 1 1行,子结点不存在时,left(right)为-1。
2.输出
依次输出
n
n
n个结点的信息如下:
node id: parent = p, sibling = s, degree = deg, depth = dep, height = h, type
3.输入及输出示例
输入
9
0 1 4
1 2 3
2 -1 -1
3 -1 -1
4 5 8
5 6 7
6 -1 -1
7 -1 -1
8 -1 -1
输出
node 0: parent = -1, sibling = -1, degree = 2, depth = 0, height = 3, root
node 1: parent = 0, sibling = 4, degree = 2, depth = 1, height = 1, internal node
node 2: parent = 1, sibling = 3, degree = 0, depth = 2, height = 0, leaf
node 3: parent = 1, sibling = 2, degree = 0, depth = 2, height = 0, leaf
node 4: parent = 0, sibling = 1, degree = 2, depth = 1, height = 2, internal node
node 5: parent = 4, sibling = 8, degree = 2, depth = 2, height = 1, internal node
node 6: parent = 5, sibling = 7, degree = 0, depth = 3, height = 0, leaf
node 7: parent = 5, sibling = 6, degree = 0, depth = 3, height = 0, leaf
node 8: parent = 4, sibling = 5, degree = 0, depth = 2, height = 0, leaf
四、代码讲解
1.结构体定义
//定义结点结构体:结点编号,左右孩子的结点编号
struct Node { int parent, left, right; };
如上所示,由于二叉树的结点信息量有限,我们可以通过父子表示法,即一个结点的信息包括父结点和左右子结点三个信息,我们用C++的结构体可以实现。
2.深度配置函数
void setDepth(int u, int d) {
//递归结束条件:左右孩子都为空
if(u == NIL) return;
D[u] = d;
setDepth(T[u].left, d + 1);
setDepth(T[u].right, d + 1);
}
树结构很方便使用递归,故我们通过递归方法遍历每一个node,然后分别对每一个结点进行深度计算。
递归第一步,确定递归结束条件。本题中递归结束条件为该结点为空,即递归到叶结点的孩子结点结束,即
if(u == NIL) return;
递归第二步,确定递归体。本题中的递归体较简单,不再阐述,需要注意的是,每递归一次,需要将深度d加一。
setDepth(T[u].left, d + 1);
setDepth(T[u].right, d + 1);
第三步,确定每一次递归中需要完成的功能。对于本题,我们需要完成的仅仅是对每一个结点进行深度函数D[u]的赋值。
D[u]=d;
我们进行赋值的时候,只需要调用如下代码即可。
setDepth(root, 0);
具体递归路径如下:
3.高度配置函数
这里需要对高度和深度进行说明,高度是指该结点到叶结点的最大距离,是从下而上的距离;而深度是指从root结点开始,从上而下的路径长度。
int setHeight(int u) {
//高度是向下计算,所以每次都要重置0
int h1 = 0, h2 = 0;
if(T[u].left != NIL) {
h1 = setHeight(T[u].left) + 1;
}
if(T[u].right != NIL) {
h2 = setHeight(T[u].right) + 1;
}
return H[u] = (h1 > h2 ? h1 : h2);
}
所以我们对每个结点u都要先进行初始化,即将h1和h2都置零,然后对左右子树分别向下递归,取左右子树高度的最大值+1即为该结点的高度,详见上述代码。
4.兄弟结点函数
我们需要找u结点的父结点,然后再找u的父结点的子结点(和u互异)即为u的兄弟结点,过程较为简单,只需注意特殊情况的处理即可,具体代码如下:
int getSibling(int u) {
//为了防止出现T[-1]的情况
if(T[u].parent == NIL) return NIL;
if(T[T[u].parent].left != NIL && T[T[u].parent].left != u) {
return T[T[u].parent].left;
}
if(T[T[u].parent].right != NIL && T[T[u].parent].right != u) {
return T[T[u].parent].right;
}
return NIL;
}
五、完整代码
#include<cstdio>
#define MAX 10000
#define NIL -1
//定义结点结构体:结点编号,左右孩子的结点编号
struct Node { int parent, left, right; };
//开辟空间
Node T[MAX];//结点数组
int H[MAX], D[MAX];//高度数组和深度数组
//每个结点深度设置函数
void setDepth(int u, int d) {
//递归结束条件:左右孩子都为空
if(u == NIL) return;
D[u] = d;
setDepth(T[u].left, d + 1);
setDepth(T[u].right, d + 1);
}
//每个结点高度设置函数
int setHeight(int u) {
//高度是向下计算,所以每次都要重置0
int h1 = 0, h2 = 0;
if(T[u].left != NIL) {
h1 = setHeight(T[u].left) + 1;
}
if(T[u].right != NIL) {
h2 = setHeight(T[u].right) + 1;
}
return H[u] = (h1 > h2 ? h1 : h2);
}
//返回结点u的兄弟结点编号
int getSibling(int u) {
//为了防止出现T[-1]的情况
if(T[u].parent == NIL) return NIL;
if(T[T[u].parent].left != NIL && T[T[u].parent].left != u) {
return T[T[u].parent].left;
}
if(T[T[u].parent].right != NIL && T[T[u].parent].right != u) {
return T[T[u].parent].right;
}
return NIL;
}
//打印结点内容的函数
//结点编号,深度,父结点,高,兄弟结点,结点种类,子结点数
void print(int u) {
printf("node %d: ", u);
printf("parent = %d, ", T[u].parent);
printf("sibling = %d, ", getSibling(u));
int deg = 0;
if(T[u].left != NIL) deg++;
if(T[u].right != NIL) deg++;
printf("degree = %d, ", deg);
printf("depth = %d, ", D[u]);
printf("height = %d, ", H[u]);
if(T[u].parent == NIL) printf("root\n");
else if(T[u].left == NIL && T[u].right == NIL) printf("leaf\n");
else printf("internal node\n");
}
//主函数
int main() {
int n;//结点个数
int v, l, r;//分别对应每个结点的编号和左右孩子的编号
int root = 0;//根结点的编号
scanf("%d", &n);
//对每个结点进行初始化
for(int i = 0; i < n; i++) {
T[i].parent = NIL;
}
//对每个结点进行赋值
for(int i = 0; i < n; i++) {
scanf("%d %d %d", &v, &l, &r);
T[v].left = l;
T[v].right = r;
if(l != NIL) T[l].parent = v;
if(r != NIL) T[r].parent = v;
}
//寻找根结点,为了后面的高度和深度的参数配置
for(int i = 0; i < n; i++) {
if(T[i].parent == NIL) {
root = i;
break;
}
}
//设置深度和高度
setDepth(root, 0);
setHeight(root);
//打印
for(int i = 0; i < n; i++) print(i);
return 0;
}
六、总结
寒假闲来无事,复习数据结构,并做些笔记,请各位大佬指点,共同学习。