二叉树的表达

前言

  本文旨在复习数据结构之二叉树,并结合一道例题详细分析。

一、二叉树

  定义:如果一棵树拥有1个根结点,且所有结点的子结点数都不超过2(不是只有0和2),那么这棵树成为有根二叉树。如下图所示就是一个二叉树的例子。
二叉树实例

二、相关概念简介

  1、:根是唯一一个没有父结点的结点。
  2、叶结点:我们将没有子结点的结点成为外部结点(external node)或叶结点(leaf)。
  3、内部结点:除叶结点以外的结点称为内部结点(internal node)。

三、例题

  设给定二叉树拥有 n n n个结点,编号分别为 0 0 0 n − 1 n-1 n1。如下图所示:

二叉树实例

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;
}

六、总结

  寒假闲来无事,复习数据结构,并做些笔记,请各位大佬指点,共同学习。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值