快速上手树与二叉树

前言:

        很多人会认为树这种数据结构很难,但实际上树是一种很基本的数据结构,本篇文章也主要讲解基本的树结构,并作少量的拓展,可以更快捷的理解树。


一、概述:

        树形结构是一类重要的 非线性 结构。它与基本的线性数据结构存在一个较大的差别就是:树型结构是 一对多 的关系;而普通的线性结构 例如:线性表---就是一对一的关系。

        树形结构是节点之间有 分支 ,并具有 层次关系 的结构,它非常类似于自然界中的树。树结构在客观世界中是大量存在的,如家谱、行政组织机构都可用树形象地表示。树在计算机领域中也有着广泛的应用,如在编译程序中,用树来表示源程序语法结构;在数据库系统中,可用树来组织信息。

1、树的定义

        树(tree)是n(n>0)个节点的有限集合T,n=0的树称为空树,当n>0的时候满足如下两个条件:

1) 有且仅有一个称为根(Root)的节点。

2) 其余的节点可分为m(m≥0)个互不相交的有限集合T_{1},T_{2},T_{3},....T_{m},其中每个集合又是一颗树,并且称其为根节点的子树(Sub Tree)。*

图a

* 如图a中的实例,A为Root,其中B和C为它的两棵SubTree的Root,节点E属于SubTree-B,节点F属于SubTree-C,如果节点E和F相交(两个节点分属于不同的子树),那么这就不是一棵树了。

注意: 树的定义是一个递归的定义,即一棵树是由若干棵子树构成的,而子树又可由若干棵更小的子树构成。树中的每一个节点都是该树中某一棵子树的根。

2、树的特点

非空的树具有以下的特点:

1) 有且仅有一个节点没有直接前趋,称它为根节点。

2) 有一个或多个节点没有直接后继,称它为终端节点,即叶子节点。

3) 除开始节点外,树中其他任一节点都有且仅有一个直接前趋

4) 除终端节点外,树中其他任一节点都有一个或多个直接后继

3、树的基本术语

图b

1) 节点:树中包含的一个数据元素及若干指向其子树的分支。

2) 节点的度:一个节点所具有的子树的个数,如图b中的A节点,其度为2。

3) 树的度:树中度数最大的节点的度,如图b所示的树,其度为2。

4) 叶子度为0的节点即为叶子节点(又称为终端节点),如图b中的H、I、J、K、F、G。

5) 非终端节点:也称为内部节点,是指除了根节点外,其他度数不为0的节点,如图b中的B、C、D。

6) 双亲、孩子节点:树中某个节点的子树的根称为该节点的孩子,相应的该节点称为孩子节点的双亲。如图b中,B的双亲节点是A,而B的孩子节点是D、E。

7) 兄弟节点:同一个双亲的孩子之间互称为兄弟,如图b中,节点B、C的双亲节点都是A,即B、C为兄弟节点。

8) 祖先:从根到该节点所经分支上的所有节点,如图b中E的祖先为B、A。

9) 子孙:以某一节点为根的子树中的任意节点都称为该节点的子孙,如图b中C的子孙为F、G。

10) 树的高度:树中节点的最大层数称为该树的高度,如图b中树的高度为4。

11) 无序树、有序树:如果将树中的各个节点的子树看做从左至右有顺序的不可互换的,则称为有序树,反之则为无序树。

12) 森林:m(m≥0)棵互不相交的树的集合。例如图b中,将A节点去除,那么B树和C树就成为了两棵互不相交的树,这时就是一个由B、C两棵树组成的森林。

接下来讲的二叉树是基本树结构里的重点。

二、二叉树:

        二叉树是一个非常重要的树形结构,一般的树也能简单地转换为二叉树,由于二叉树的存储结构及其算法都比较简单,因此经常被用来解决实际中的树形问题,是一种常用的也是基本的树形结构。

1) 二叉树的定义

        二叉树(Binary Tree)是n(n≥0)个节点所构成的有限集合,当n=0时称为空二叉树;当n>0时一个根节点以及两棵互不相交的、分别称为左子树和右子树的子二叉树组成。

2) 二叉树的特点

        二叉树中的子树具有左右之分,并且每个节点最多只能有两棵子树。所以二叉树的度最多为2,基于这句话,“二叉树的度为2”这个命题便是错误的,因为二叉树的度n为0\leqslant n\leqslant 2。在二叉树中,即使是一个孩子也有左右之分(LChild/RChild)。

3) 二叉树的五种基本形态

图c

4) 二叉树的性质

性质1:在二叉树第 i 层上最多有 2^{i-1} (i\geqslant 1)个节点。

证明:如图d。

图d

 性质2:深度为k的二叉树至多有2^{k}-1 (k\geqslant 1)个节点。

证明:根据性质1,可知深度为k的二叉树的节点数量最多为:

\sum_{i=1}^{k}2^{i-1}=2^{k}-1

性质3:对于任意一棵二叉树,如果其叶子节点的个数为n_{0},度为2的节点的个数为n_{2},则n_{0}=n_{2}+1

证明略。

下面介绍两种特殊情形的二叉树:满二叉树和完全二叉树。


满二叉树(Full Binary Tree)

        深度为k且有2^{k}-1(全满)个节点的二叉树称为满二叉树。如图e是一个深度为4的满二叉树。

 图e

         满二叉树的特点是每一层上的节点数都达到最大值,即对给定的高度 k,它的节点数
都为最大值2^{k}-1。满二叉树中的每个分支节点均有两棵高度相同的子树,且树叶都在最下
一层上。


完全二叉树(Complete Binary Tree)

         完全二叉树(Complete Binary Tree)是深度为k的,有n个节点的二叉树,当且仅当其每-
个节点都与深度为k的满二叉树中的编号从 1~n一一对应时,称该树为完全二叉树。如图f是一个深度为4的完全二叉树。

图f

         很显然,满二叉树是一种完全二叉树,但完全二叉树不一定是满二叉树。

        深度为k的完全二叉树的特点如下:

        1) 叶子节点只可能出现在第k层或第k-1层上。

        2) 对任一节点,若其右分支下的子孙的最大层次为h,则其左分支下的子孙的最大层次为h或h+1。

        因此在完全二叉树中,若某个节点没有左孩子,则它一定没有右孩子,即该节点必是叶子节点。如图g所示,节点F没有左孩子而有右孩子M,所以它并不是一棵完全二叉树。

图g

注意:深度为k的完全二叉树如果相对于同深度的满二叉树有缺失,则它的缺失一定存在于第k层,并且满足从右往左连续缺失的特点。深度为k的完全二叉树的第k-1层以上一定处于饱和状态。

性质4:具有n个节点的完全二叉树的深度为\left \lfloor \log _{2}n \right \rfloor+1。(符号\left \lfloor x \right \rfloor表示不大于x的最大整数)

证明:设所求完全二叉树的深度为k,根据其定义以及性质2所知:

2^{k-1}-1\leqslant n\leqslant 2^{k}-1

由此可以推出:  2^{k-1}\leqslant n\leqslant 2^{k}。对于不等式两边取对数后:

k-1\leqslant \log _{2}n< k

因为k是整数,所以:k=\left \lfloor \log _{2}n \right \rfloor+1

性质5:一棵具有n个节点的完全二叉树,如果按从上往下、从左至右的顺序对二叉树中的所有节点从1开始编号,则对于任意节点i(1\leqslant i\leqslant n)有如下的特点。

        1) 若i=1,则 i 为二叉树的根节点,无双亲;若i>1,则 i 的双亲节点为\left \lfloor i/2 \right \rfloor

        2) 若2i>n,则节点 i 无左孩子(节点i是叶子节点),否则 i 的左孩子为2i。

        3) 若2i+1>n,则节点 i 无右孩子,否则 i 的右孩子为2i+1。


二叉树的储存结构

        类似于线性表,二叉树也可以采用顺序和链式两种存储方式。

1.顺序存储

        这种存储方式较适用于完全二叉树和满二叉树。顺序存储是将二叉树的所有节点,按照一定的节点顺序,存储到一个连续的存储单元中。因此,为了能反映出各个节点之间的逻辑关系,必须把节点安排成一个适当的线性序列。对于具有 九个节点的完全二叉树,从根节点开始自上而下,从左到右,给所有节点从1开始编号,就可以得到一个反映整个二叉树结构的线性序列,如 图h(a)所示。在完全二叉树中对于任意节点的编号i,根据二叉树的性质 5可推得其双亲节点、左右孩子节点、兄弟等节点的编号。图h(b)所示为 图h(a)的顺序存储结构。

图h

 对于一般二叉树而言,例如图h中的D、F、H节点移除后,为了继续保持其他节点的逻辑关系,图中不存在的节点在顺序表中将被置“0”,假如我们移除了图h中的D、F、H节点,那么顺序储存的结果将是:

ABC0E0G0I

很显然浪费了很多的储存空间。

2.链式存储结构

        二叉树通常采用链式存储结构存储二叉树中的节点及其相互之间的关系,根据二叉树的定义可知,链表中的每个节点除了存储数据元素外,还至少要有两个指针域分别指向其左右孩子节点,才能表示二叉树的层次关系,比较常用的链式存储结构有二叉链表、三叉链表。图 i (a)所示为二叉链表中的节点结构。在此结构中对于任意节点而言,寻找其左右孩子节点非常方便,但是寻找其双亲节点又比较困难,需要从根指针出发逐个排查。所以,在具体应用中,如果经常要在二叉树中寻找某节点的双亲,则可以在每个节点上再加一个指向其双亲的指针域 parent,图 i (b)所示即为三叉链表中的节点结构。

图i

 下面给出二叉链表中节点结构在C语言中的类型定义。

typedef int Datatype;    //可以修改成需要的数据类型
typedef struct BiTnode
{
	Datatype data;    //数据域
	BiTnode* lchild, * rchild;    //左右孩子指针域
}BiTnode,*BiTree;    //类型别名

如图 j (a)为二叉树的逻辑关系,图 j (b)为二叉树的链式存储结构。

图j

 3.二叉树的遍历

        二叉树的遍历是指沿某条搜索路径访问二叉树中的每个节点,并且对树中每个节点仅访问一次。对节点的访问可以是输出、更新、增加、删除等操作。由前面文章可知,对于一 个线性结构的遍历很容易,只需要从开始节点出发顺序扫描每个节点即可。由于二叉树的每个节点可以有两个孩子节点,因此,需要寻找一种规律来访问树中各节点。下面主要讲解三种遍历的递归算法

        1) 先根遍历二叉树(DLR)

        访问根节点 -> 先根遍历左子树 -> 先根遍历右子树。代码如下:

void PreOrderTraverse(BiTree T)
{
    if(T)    //判断树是否为空
    {
        cout << T->data;   //访问根节点
        PreOrderTraverse(T->lchild);  
        PreOrderTraverse(T->rchild);
    }
}

        1) 中根遍历二叉树(LDR)

        中根遍历左子树 -> 访问根节点 -> 中根遍历右子树。代码如下:

void InOrderTraverse(BiTree T)
{
    if(T)    //判断树是否为空
    {
        InOrderTraverse(T->lchild);
        cout << T->data;   //访问根节点  
        InOrderTraverse(T->rchild);
    }
}

        1) 后根遍历二叉树(LDR)

        后根遍历左子树 -> 后根遍历右子树 -> 访问根节点。代码如下:

void PostOrderTraverse(BiTree T)
{
    if(T)    //判断树是否为空
    {
        PostOrderTraverse(T->lchild);
        PostOrderTraverse(T->rchild);
        cout << T->data;   //访问根节点  
    }
}

                                                                                                                                      琛宝头发+1

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值