树[数据结构]

一.树的定义

1.什么是树

有且只有一个根节点,有若干个互不相交的子树。
这里写图片描述

2.专业术语

这里写图片描述

如上图:我们解释父节点、子节点、兄弟节点、堂兄弟节点。

父节点:A为父节点
子节点:B、C是A的子节点
兄弟节点:B和C是兄弟节点
堂兄弟节点:D与E是堂兄弟节点
深度:树中节点的最大层次(从根节点到最底层节点的层数,图中深度为3)
叶子节点:没有子节点的节点(B、E是叶子节点)
度:子节点的个数

二.树的分类

1.一般树

一般树:任意一个节点的子节点个数都不受限制。

2.二叉树

二叉树:任意一个节点的子节点个数最多为2个,且子节点位置不可更改。

3.森林

森林:n个互不相交的树的集合。

扩展:二叉树的分类

1.一般二叉树
2.满二叉树:在不增加树层次的前提下,不可添加新节点
3.完全二叉树:将满二叉树最底层最右边的连续若干个节点

如图,是一个满二叉树:
这里写图片描述

变成完全二叉树,可以不删除节点(完全二叉树包含满二叉树),也可以删除 G、GE、GEF、GEFD.

三.树的存储

1.连续存储:

连续存储必须是完全二叉树的形式
为什么呢?


如下图,一颗普通树:
这里写图片描述

假设我们使用层次的关系(从左到右,从上到下的关系来存储)

该树在数组中的存储结构如下:
这里写图片描述


我们根据此数组将树还原:

到底是这种形式呢?
这里写图片描述

还是这种形式呢?

这里写图片描述

有歧义,不能确定节点的逻辑关系。
也就是,如果一棵树不是完全二叉树的形式,我们不能根据连续的存储结构来还原一棵树。
所以,连续的存储结构必须是完全二叉树的形式。

我们可以如下存储:

这里写图片描述

至此,我们总结一下连续结构存储完全二叉树的优缺点:

优点:
1.查找某个节点的父节点与子节点很快
2.可以通过编号确定节点在第几层

缺点:
浪费空间(如上面存储,还得存储空节点)

2.链式存储

我们将树的节点分为3部分构成:
pLeft指向左子树、pRight指向右子树、data存放节点数据。
如下图:
这里写图片描述

我们可以使用 先序遍历、中序遍历、后序遍历 遍历这棵树。

链式存储相对于连续存储来说,更节省空间,所以使用范围广。

3.非链式存储

对于下面树的存储:
这里写图片描述

1.双亲表示法

这里写图片描述
A:-1(代表A是根节点)
B:0(代表B的父节点是A)
C:0(代表C的父节点是A)
D:1(代表D的父节点是B)
E:1(代表E的父节点是B)

特点:求父节点很方便,但是求子节点麻烦。

2.孩子表示法

这里写图片描述
特点:求子节点很方便,但是求父节点麻烦。

3.双亲-孩子表示法

这里写图片描述
特点:求子节点很方便,求父节点也很方便,但是浪费空间。

4.二叉树表示法

对于一般树的存储,我们可以使用双亲表示法、孩子表示法、双亲-孩子表示法。
我们也可以将一棵树转换成二叉树来存储。
因为现在,我们对二叉树的算法比较成熟,对普通树的算法不成熟。所以我们通常将普通树转换成二叉树来存储,便于以后的操作。

我们将一颗普通树转变为二叉树来存储的规则是:左指针域指向第1个孩子,右指针域指向兄弟。

这里写图片描述

总结:
连续存储结构:浪费空间(必须是完全二叉树),但是查找速度快
链式存储结构:节约空间,查找速度还可以
非链式存储结构:最浪费空间
我们最常用的存储结构是:链式二叉树

四.树的操作

1.遍历

这里写图片描述

1.1前序遍历

前序遍历的顺序是:根节点-左子树-右子树
上面图片树的前序遍历为:A-B-D-E-C-F

1.2中序遍历

中序遍历的顺序是:左子树-根节点-右子树
上面图片树的前序遍历为:D-B-E-A-F-C

1.3后序遍历

后序遍历的顺序是:左子树-右子树-根节点
上面图片树的前序遍历为:D-E-B-F-C-A

2.根据遍历结果还原一棵树

我们可以根据:
先序遍历+中序遍历—>还原一棵树
中序遍历+后序遍历—>还原一棵树
但是不能:
前序遍历+后序遍历—>还原一棵树

因为:
前序遍历可以确定根节点,但是不能确定左子树、右子树
后序遍历可以确定根节点,但是不能确定左子树、右子树
中序遍历可以确定左子树、右子树,但是不能确定根节点
所以:
先序遍历或后序遍历只有和中序遍历结合,才能确定一棵树的根节点、左子树、右子树。

我们在根据 先序+中序—>还原一棵树 或 中序+后序—>还原一棵树 的时候,有如下规律:

先序:最先出现的是根节点
后序:最后出现的是根节点
我们根据先序和后序的规律,再结合中序,就能轻松的还原一棵树。

具体的练习就不写了,其实并不难。

五.树的应用

1.是数据库中数据组织的一种重要方式(如图书管理数据库)
2.操作系统子父进程的关系就是一棵树。
3.面向对象中类的继承关系
等等…

六.编码实战

我们创建下面图中的这棵树,并且实现它的前序遍历,中序遍历、后序遍历。

这里写图片描述

#include <stdio.h>
#include <stdlib.h>
struct BTNode
{
    char data;
    struct BTNode * PTLchild;
    struct BTNode * PTRchild;
};
struct BTNode *createBTree();
void preTraverseBTree();
void inTraverseBTree();
void postTraverseBTree();
int main()
{
    struct BTNode * pT=createBTree();
    preTraverseBTree(pT);
    inTraverseBTree(pT);
    postTraverseBTree(pT);
    return 0;
}
//创建一棵树
struct BTNode *createBTree()
{
    struct BTNode * pA=(struct BTNode *)malloc(sizeof(struct BTNode));
    struct BTNode * pB=(struct BTNode *)malloc(sizeof(struct BTNode));
    struct BTNode * pC=(struct BTNode *)malloc(sizeof(struct BTNode));
    struct BTNode * pD=(struct BTNode *)malloc(sizeof(struct BTNode));
    struct BTNode * pE=(struct BTNode *)malloc(sizeof(struct BTNode));
    pA->data='A';
    pB->data='B';
    pC->data='C';
    pD->data='D';
    pE->data='E';
    pA->PTLchild=pB;
    pA->PTRchild=pC;
    pB->PTLchild=NULL;
    pB->PTRchild=NULL;
    pC->PTLchild=pD;
    pC->PTRchild=NULL;
    pD->PTLchild=NULL;
    pD->PTRchild=pE;
    pE->PTLchild=NULL;
    pE->PTRchild=NULL;
    return pA;
}
//先序遍历
void preTraverseBTree(struct BTNode * pT)
{
    if(pT!=NULL)
    {
        printf("%c\n",pT->data);
        if(pT->PTLchild!=NULL)
        {
             preTraverseBTree(pT->PTLchild);
        }
        if(pT->PTRchild!=NULL)
        {
            preTraverseBTree(pT->PTRchild);
        }
    }
}
//中序遍历
void inTraverseBTree(struct BTNode * pT)
{
    if(pT!=NULL)
    {
        if(pT->PTLchild!=NULL)
        {
             inTraverseBTree(pT->PTLchild);
        }
        printf("%c\n",pT->data);
        if(pT->PTRchild!=NULL)
        {
            inTraverseBTree(pT->PTRchild);
        }
    }

}
//后序遍历
void postTraverseBTree(struct BTNode * pT)
{
    if(pT!=NULL)
    {
        if(pT->PTLchild!=NULL)
        {
             postTraverseBTree(pT->PTLchild);
        }
        if(pT->PTRchild!=NULL)
        {
            postTraverseBTree(pT->PTRchild);
        }
        printf("%c\n",pT->data);
    }

}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值