树的定义:
树是n个结点的有限集。 n = 0 称为空树。如果n>0,则:
(1)有一个特定的称之为根的结点,它只有直接后继,但没有直接前驱。
(2)除根以外的其他结点划分为m个互不相交的有限集合,每个集合又是一棵树,并且称之为根的子树。每棵子树的根节点有且仅有一个直接前驱,但可以有0个或多个直接后继。
与树相关的定义:
节点:表示树中的元素,包括数据元素的内容及其指向其子树的分支。
节点的度:节点的分支数。
终端节点(叶子节点):度为0的节点。
非终端节点:度不为0的结点。
节点的层次:树中根节点的层次为1,根节点子树的根为第二层,以此类推。树中结点的最大层次称为树的深度或高度。
有序树、无序树:如果树中每棵子树从左向右的排列拥有一定的顺序,不得互 换,则称为有序树,否则称为无序树。
在树结构中,节点之间的关系又可以用家族关系描述。
(1)孩子、双亲:某个结点的子树称为这个结点的孩子,而这个结点又被称为孩子的双亲。
(2)子孙:以某节点为根的子树中的所有节点都被称为该节点的子孙。
(3)兄弟:同一个双亲的孩子之间互为兄弟。
(4)堂兄弟:双亲在同一层的节点互为堂兄弟。
树的存储:双亲表示法 孩子表示法 双亲孩子表示法
1、二叉树的定义
二叉树(Binary Tree)是n(n>=0)个结点的有限集合,该集合或者为空集(称为空二叉树),或者由一个根结点和两棵互不相交的、分别称为根结点的左子树和右子树的二叉树组成。
1.1 二叉树的特点
二叉树的特点有:
1)每个结点最多有两棵子树,所以二叉树中不存在度大于2的结点。注意不是只有两棵子树,而是最多有。没有子树或者有一棵子树都是可以的。
2)左子树和右子树是有顺序的,次序不能任意颠倒。
3)即使树中某结点只有一棵子树,也要区分它是左子树还是右子树。
1.2 特殊二叉树
1、斜树
所有的结点都只有左子树的二叉树叫左斜树。所有结点都是只有右子树的二叉树叫右斜树。这两者统称为斜树。
斜树有很明显的特点:就是每一层都只有一个结点,结点的个数与二叉树的深度相同。 这也叫树,与线性表结构不是一样?其实线性表结构就可以理解为是树的一种极其特殊的表现形式
2、满二叉树
在一棵二叉树中,如果所有分支结点都存在左子树和右子树,并且所有叶子都在同一层上,这样的二叉树称为满二叉树。 单是每个节点都存在左右子树,不能算是满二叉树,还必须要所有的叶子都在同一层,这就做到了整棵树的平衡。
满二叉树的特点是:
1)叶子结点只能出现在最下一层。出现在其它层就不可能达到平衡。
2)非叶子结点的度一定是2。
3)在同样深度的二叉树中,满二叉树的结点个数最多,叶子数最多。
3、完全二叉树
对一棵具有n个结点的二叉树按层序编号,如果编号为i(1<=i<=n)的结点域同样深度的满二叉树编号为i的结点在二叉树中的位置完全相同,则这棵二叉树称为完全二叉树。
满二叉树一定是一棵完全二叉树,但完全二叉树不一定是满的。
按照层序编号,出线位置编号空档,所以不是完全二叉树。尽管不是满二叉树,但是编号是连续的,所以它是完全二叉树。
完全二叉树的特点:
1)叶子结点只能出现在最下两层
2)最下层的叶子一定集中在左部连续位置
3)倒数二层,若有叶子结点,一定都在右部连续位置
4)如果结点度为1,则该结点只有左孩子,即不存在只有右子树的情况
5)同样结点树的二叉树,完全二叉树的深度最小
判断某二叉树是否是完全二叉树的办法:那就是看着树的示意图,心中默默给每个节点按照满二叉树的结构逐层顺序编号,如果编号出现空档,就说明不是完全二叉树。
2、二叉树的性质
性质1:在二叉树的第i层上至多有2i-1个结点(i>=1)
性质2:深度为k的二叉树至多有2k -1个结点(i>=1)
性质3: 对于任何一棵二叉树T,如果其终端结点数位n0,度为2的结点数位n2,则n0=n2+1。
终端节点数就是叶子节点数,而一颗二叉树,除了叶子节点外,剩下的就是度为1或2的结点数了。
性质4:具有n个结点的完全二叉树的深度为|log2n| + 1(|x|表示不大于x的最大整数)
性质5:如果对一棵有n个结点的完全二叉树(其深度为|log2n| + 1)的结点按层序编号(从第1层到第|log2n| + 1层,每层从左到右),对任一结点i(1<=i<=n)
有:
1)如果i=1,则结点i为二叉树的根,无双亲;如i>1,则其双亲是结点|i/2|
2)如果2i>n,则结点i无左孩子(结点i为叶子结点);否则其左孩子是结点2i
3)如果2i+1>n,则结点i无右孩子;否则其右孩子是结点2i+1
3、二叉树的存储结构
3.1 二叉树的顺序存储结构
二叉树的顺序存储结构就是用一维数组存储二叉数中的结点,并且结点的存储位置,也就是数组的下标要能体现结点之间的逻辑关系,比如双亲与孩子的关系、左右兄弟的关系等。
先来看看完全二叉树的顺序存储,一棵完全的二叉树如下图所示。
将这棵二叉树保存到数组中,相应的下标对应其同样的位置,如下图所示。
当然,对于一般的二叉树,尽管层序编号不能反映逻辑关系,但是可以将其按完全二叉树编号,只不过,把不存在的结点设置为"^"而已。如下图所示,注意浅色结点表示不存在。
考虑一种极端的情况,一棵深度为k的右斜树,它只有k个结点,却需要分配2k-1个存储单元空间,这显然是对存储空间的浪费。所以,顺序存储结构一般只用于完全二叉树。
3.2 二叉链表
既然顺序存储适应性不强,就要考虑链式存储结构。二叉树每个结点最多有两个孩子,所以为它设计一个数据域和两个指针域是比较自然的想法,称这一的链表叫做二叉链表。
以下是二叉链表的结点结构定义代码。
1 2 3 4 5 6 | /* 二叉树的二叉链表结点定义 */ typedef struct BitNode /*结点结构*/ { ElemType data; /* 结点数据 */ struct BitNode *lchild, *rchild; /* 左右孩子结点指针 */ }BitNode, *BiTree; |
结构示意图如下所示。
data是数据域,lchild和rchild都是指针域,分别存放指向左孩子和右孩子的指针。
如果有需要,我们可以再增加一个指向其双亲的指针域,那样就称之为三叉链表。
参考:严蔚敏老师之《数据结构》、程杰之《大话数据结构》