数据结构 树与二叉树
基本概念
树和森林
森林:
是m(m>=0)棵互不相交的树的集合
树(Tree):
是n(n>=0)个结点的有限集。
n=0时称为空树。
在任意一颗非空树中:
(1)有且仅有一个特定的称为根(Root)的结点。
(2)当n>1时,其余结点可分为m(m>0)个互不相交的有限集T1、T2、.....、Tm,其中每一个集合本身又是一棵树,并且称为根的子树(SubTree)。
基本术语
根结点: 非空树中无前驱结点的结点
结点的度: 结点拥有的子树数
树的度: 树内各结点的度的最大值
叶子: 度为0的, 终端结点
分支结点: 度不为0, 非终端结点,内部结点
孩子, 双亲: 结点的子树的根称为该结点的孩子,该结点称为孩子的双亲
兄弟结点: 有共同的双亲
堂兄弟: 双亲在同一层的结点
树的深度: 树中结点的最大层次
有序树: 树中结点的各子树从左到右有次序
无序树:树中结点的各子树无次序.
二叉树
为什么要使用二叉树
定义
二叉树不是树的特殊情况,是另一个概念(因为二叉树区分左右子树)
二叉树(Binary Tree)
是n(n>=0)个结点的有限集合,
该集合或者为空集(空二叉树),
或者由一个根结点和两棵互不相交的、分别称为根结点的左子树和右子树的二叉树组成(子树也为二叉树)。
特点
每个结点最多有两棵子树,所以二叉树中不存在度大于2的结点。
左子树和右子树是有顺序的,次序不能任意颠倒。
即使树中某结点只有一棵子树,也要区分它是左子树还是右子树。
五种基本形态
空二叉树
只有一个根结点
根结点只有左子树
根结点只有右子树
根结点既有左子树又有右子树
抽象数据类型定义
性质
性质1:
在二叉树的第i层上至多有\(2^{i-1}\)个结点(i>=1)
(请问第i层最少有几个结点? 1个)
性质2:
深度为k的二叉树至多有\(2^k-1\)个结点(k>=1)
性质3:
对任何一棵二叉树T,如果其
叶子结点数为\(n_0\),度为2的结点数为\(n_2\),则\(n_0 = n_2+1\)。
证明:
一棵二叉树,除了叶子结点,就是度为1或2的结点。再设\(n_1\)度为1的结点数,
则树T 的结点总数n:\(n = n_0+n_1+n_2\)。
我们再换个角度,
求解树T的总边数m:
从入度角度:
由于根结特殊,没有分支进入,所以总边数为结点总数减去1。也就是 \(m=n-1\)
从出度角度:\(m=n_1+2×n_2\)
可得:\(n-1=n_1+2×n_2\);
可推导出:\(n_0+n_1+n_2-1=n_1+2×n_2\);
继续推导可得\(n_0 = n_2+1\)。
满二叉树
定义:
除最后一层无任何子节点外,每一层上的所有结点都有两个子结点。
或者说:
是一棵深度为k并且有\(2^k-1\)个结点的二叉树。
特点:
- 每层都铺满结点
- 所有叶子结点在同一层上。
性质:
1) 一颗树深度为h,最大层数为k,深度与最大层数相同,k=h;
2) 叶子数为2h;
3) 第k层的结点数是:2k-1;
4) 总结点数是:2k-1,且总节点数一定是奇数。
完全二叉树
定义:
若设二叉树的深度为h,除第 h 层外,其它各层的结点数都达到最大个数,第h层所有的结点都连续集中在最左边。
也就是说:
在满二叉树中,从最后一个结点开始,连续的去掉任意数量的结点,得到的树就完全二叉树。
性质
性质4:
具有n个结点的完全二叉树的深度为\(\lfloor log_2n \rfloor+1\)
(\(\lfloor x \rfloor\)向下取整:表示不大于X的最大整数)。
证明:
性质5:
如果对一颗有n个结点的完全二叉树的结点按层序编号(从第1层到第\(\lfloor log_2n \rfloor+1\)层,每层从左到右),对任一结点i(1<=i<=n)有:
- 如果i=1,则结点i是二叉树的根,无双亲;
-
- 如果i>1,则其双亲是结点\(\lfloor \frac{i}{2 } \rfloor\)。
- 如果2×i>n,则结点i无左孩子(结点i为叶子结点);
-
- 否则其左孩子是结点2×i。
- 如果2×i+1>n,则结点i无右孩子;
-
- 否则其右孩子是结点2×i+1。
线索二叉树
定义:
利用二叉链表中的空指针域:
如果某个结点的左孩子为空,则将空的左孩子指针域改为指向其前驱;
如果某结点的右孩子为空,则将空的右孩子指针域改为指向其后继;
——这种改变指向的指针称为“线索” ;
加上了线索的二叉树称为线索二叉树(Threaded Binary Tree)
对于叉树按基种遍历次序使其变为线索二叉树的过程叫线索化
存储结构
顺序存储
方式:
按满二叉树的结点层次编号,一次存放二叉树。
(使用maxINF代表不存在的结点。)
缺点:
- 不能反应逻辑关系;
- 对于特殊的二叉树(左斜树、右斜树),浪费存储空间。
结论
所以二叉树顺序存储结构一般只用于满二叉树/完全二叉树。
链式存储
使用二叉链表
/*二叉树的二叉链表结点结构定义*/
typedef struct BiNode
{
char data; /*结点数据*/
struct BiNode *lchild;//左孩子指针
struct BiNode *rchild;//右孩子指针
}BiNode,*BiTree;
注意:
二叉链表储存效率不是100%;
因为:在n个结点的二叉链表中,有(n+1)个空指针域。
证明:
一定有2n个链域,除了根节点入度一定为1,则入度一定为n-1,即有n-1个链域存放了指针。
( 2n -(n-1)=n+1 )
建立二叉树
遍历二叉树
定义:
二叉树的遍历是指从根结点出发,按照某种次序依次访问二叉树中所有结点,使得每个结点被访问一次且仅被访问一次。
目的:
因为计算机只会处理线性序列,而我们研究遍历,就是把树中的结点变成某种意义的线性序列,这给程序的实现带来了好处。