数据结构 - 二叉树

前面讲到树,让我们一起回顾一下树的定义
树的定义
还有二叉树定义涉及到的有关(术语?)的概念可以往前翻,看前面的博客(关于度什么的,传送门)能够看到二叉树是n(n>=0)个结点的有限集合,该集合或者为空集(称为空二叉树),或者由一个根结点和两棵互不相交的、分别称为根结点的左子树和右子树组成。

然后讲一讲二叉树的性质

1、在二叉树的第i层上最多有2i-1 个节点 。(i>=1)

2、二叉树中如果深度为k,那么最多有2k-1个节点。(k>=1)

3、n0=n2+1 n0表示度数为0的节点数,n2表示度数为2的节点数。

4、在完全二叉树中,具有n个节点的完全二叉树的深度为[log2n]+1,其中[log2n]是向下取整。

5、若对含 n 个结点的完全二叉树从上到下且从左至右进行 1 至 n 的编号,则对完全二叉树中任意一个编号为 i 的结点有如下特性:

若 i=1,则该结点是二叉树的根,无双亲, 否则,编号为 [i/2] 的结点为其双亲结点;
若 2i>n,则该结点无左孩子, 否则,编号为 2i 的结点为其左孩子结点;
若 2i+1>n,则该结点无右孩子结点, 否则,编号为2i+1 的结点为其右孩子结点。

然后二叉树里面还定义了特殊的二叉树,
比如斜树:所有的结点都只有左子树的二叉树叫左斜树。所有结点都是只有右子树的二叉树叫右斜树,这两者统称为斜树。

完全二叉树:对一颗具有n个结点的二叉树按层编号,如果编号为i(1<=i<=n)的结点与同样深度的满二叉树中编号为i的结点在二叉树中位置完全相同,则这棵二叉树称为完全二叉树。

顺便讲一下完全二叉树的特点:

叶子结点只能出现在最下层和次下层。
最下层的叶子结点集中在树的左部。
倒数第二层若存在叶子结点,一定在右部连续位置。
如果结点度为1,则该结点只有左孩子,即没有右子树。
同样结点数目的二叉树,完全二叉树深度最小。

满二叉树:在一棵二叉树中。如果所有分支结点都存在左子树和右子树,并且所有叶子都在同一层上,这样的二叉树称为满二叉树。

再顺便讲一下满二叉树的特点:

叶子只能出现在最下一层。出现在其它层就不可能达成平衡。
非叶子结点的度一定是2。
在同样深度的二叉树中,满二叉树的结点个数最多,叶子数最多。

注:满二叉树一定是完全二叉树,但反过来不一定成立

同理的,二叉树可以遍历询问,增删改查一系列操作。对于同一棵二叉树来说,它的存储结构也分为顺序存储和链式存储。

二叉树

顺序存储就是使用一维数组存储二叉树中的结点,并且结点的存储位置,就是数组的下标索引。
用图讲话:二叉树顺序存储

然后不难发现,顺序存储一般适用于完全二叉树(不容易造成空间浪费)。

okey来看链式存储,由二叉树定义可知,二叉树的每个结点最多有两个孩子。因此,可以将结点数据结构定义为一个数据和两个指针域,看起来就像是这个样子:
二叉树链式存储
然后可以解释一下二叉树链式存储的表示方式
二叉树链式存储表示方式
以及变成代码的样子,

typedef struct BiTNode{
    TElemType data;//数据
    struct BiTNode *lchild, *rchild;//左右孩子指针
} BiTNode, *BiTree;

然后就可以讲二叉树的遍历了,二叉树的遍历有点点复杂,可以分成4种不同的遍历方式,称之为:前序遍历、中序遍历、后序遍历和层序遍历。

一个一个来吧,层次遍历上课没有提到就不多做介绍了(因为我也不明白系列)

第一个前序遍历,通俗的说就是从二叉树的根结点出发,当第一次到达结点时就输出结点数据,按照先向左在向右的方向访问。还是以上面那棵树为例子,那么它的访问顺序应该是,从根结点出发,则第一次到达结点A,继续向左访问,访问结点B,再访问D,H,当到达叶子结点H,返回到D,进而向D右子树访问,D右子树不为空,则访问至I,I为叶子结点,则返回到D,D左右子树已经访问完毕,则返回到B,进而到B右子树,访问E,向E左子树,访问J,按照同样的访问规则,继续访问C、F、G,于是得到它的前序遍历输出为:ABDHIEJCFG

然后中序遍历,就是从二叉树的根结点出发,当第二次到达结点时就输出结点数据,按照先向左在向右的方向访问。对于上面那个树来讲,先从根结点出发,则第一次到达结点A,先不输出A,继续向左访问,第一次访问结点B,也不输出B;继续到达D,到达H,H左子树为空,则返回到H,此时第二次访问H,故输出H,H右子树为空,则返回至D,此时第二次到达D,故输出D,由D返回至B,第二次到达B,故输出B,按照同样规则继续访问,依次输出J、E、A、F、C、G,于是得到它的中序遍历输出为:HDIBJEAFCG

然后后序遍历,就是从二叉树的根结点出发,当第三次到达结点时就输出结点数据,按照先向左在向右的方向访问。一样的,从树的根结点出发,则第一次到达结点A,不输出A,继续向左访问,第一次访问结点B,不输出B;继续到达D,H,到达H,H左子树为空,则返回到H,此时第二次访问H,不输出H,H右子树为空,则返回至H,此时第三次到达H,故输出H,由H返回至D,第二次到达D,不输出D,继续访问至I,I左右子树均为空,故第三次访问I时,输出I,返回至D,此时第三次到达D,故输出D,按照同样规则继续访问,输出J、E、B、F、G、C,A,于是得到它的后序遍历输出为:HIDJEBFGCA

二叉树的遍历可以应用递归实现实现= =

//前序遍历
void PreOrderTraverse(BiTree T)
{
    if(T==NULL)
    return;
    printf("%c", T->data);  //显示结点数据,可以更改为其他对结点操作
    PreOrderTraverse(T->lchild);    //再先序遍历左子树
    PreOrderTraverse(T->rchild);    //最后先序遍历右子树
}

//中序遍历
void InOrderTraverse(BiTree T)
{
    if(T==NULL)
    return;
    InOrderTraverse(T->lchild); //中序遍历左子树
    printf("%c", T->data);  //显示结点数据,可以更改为其他对结点操作
    InOrderTraverse(T->rchild); //最后中序遍历右子树
}

//后序遍历
void PostOrderTraverse(BiTree T)
{
    if(T==NULL)
    return;
    PostOrderTraverse(T->lchild);   //先后序遍历左子树
    PostOrderTraverse(T->rchild);   //再后续遍历右子树
    printf("%c", T->data);  //显示结点数据,可以更改为其他对结点操作
}

部分转载自:https://www.jianshu.com/p/bf73c8d50dc2

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值