第6章树
6.1 树的定义
线性结构:一对一。树形结构:一对多。图形结构:多对多。
1、树(Tree)是n(n>=0)个节点的有限集。n=0时称为空树。在任意一棵非空树中:(1)有且仅有一个特定的称为根(Root)的节点;(2)当n>1时,其余节点可分为m(m>0)个互不相交的有限集T1,T2,......,Tm,其中每一个集合本身又是一棵树,并且称为根的子树(SubTree)。
(1)数据结构中的树只能有一个根节点。
(2)子树的个数没有限制,但它们一定是互不相交的。
2、树的节点包含一个数据元素及若干指向其子树的分支。节点拥有的子树数目称为节点的度(Degree)。度为0的节点称为叶节点或终端节点。度不为0的节点称为非终端节点或分支节点。除根节点外,分支节点也称为内部节点。树的度是树内各节点的度的最大值。
3、节点的子树的根称为该节点的孩子(Child),相应的,该节点称为孩子的双亲。同一个双亲的孩子之间合成兄弟。节点的祖先是从根到该节点所经分支上的所有节点,反之,以某节点为根的子树中的任一节点都称为该节点的子孙。
4、双亲在同一层次的节点互为堂兄弟。树中节点的最大层次称为树的深度(Depth)或高度。如果树中节点的各子树看成从左至右是有次序的,不能互换的,则该树称为有序树,否则称为无序树。
5、森林(Forest)是m(m>=0)棵互不相交的树的集合。对树中的每个节点而言,其子树的集合即是森林。
线性结构:
(1)第一个数据元素:无前驱。
(2)最后一个数据元素:无后继。
(3)中间元素:一个前驱一个后继。
树结构:
(1)根节点:无双亲,唯一。
(2)叶节点:无孩子,可以有多个。
(3)中间节点:一个双亲多个孩子。
6.2 树的存储结构
顺序存储和链式存储。
6.2.1双亲表示法
除了根节点外,其余每个节点,它不一定有孩子,但是一定有且仅有一个双亲。
节点结构由一个数据域和一个指针域组成。
data |
parent |
data是数据域,存储节点的数据信息。parent是指针域,存储该节点的双亲在数组中的下标(用数组存储)。约定根节点的指针域设置为-1。
类似的还有孩子表示方法、孩子兄弟表示法。
6.3 二叉树
6.3.1二叉树的定义
1、二叉树(Binary Tree)是n(n>=0)个节点的有限集合,该集合或者为空集(称为空二叉树),或者由一个根节点和两棵互不相交的、分别称为根节点的左子树和有子树的二叉树组成。
特点:
(1)每个节点最多有两个子树,二叉树树中不存在度大于2的节点。不是只有两棵子树,而是最多有。
(2)左子树和右子树是有顺序的。
(3)即使某节点只有一棵子树,也要区分它是左子树还是右子树。
2、特殊二叉树。
(1)斜树,左斜树、右斜树。
(2)满二叉树
(3)完全二叉树。
6.3.2二叉树的性质
1、二叉树的第i层最多有2i-1个节点。
2、深度为k的二叉树至多有2k-1个节点。
3、对任何一个二叉树,如果叶子节点数为n0,度为2的节点数n2,则n0=n2+1;
证明:总节点数= n0 + n1 + n2;总节点数 = 总分支数 + 1;总分支数 = n1 + 2n2;
得到n0 = n2 + 1;
4、具有n个节点的完全二叉树的深度为[log2n] + 1。
5、如果对一棵有n个节点的完全二叉树(其深度为[log2n] + 1)的节点按层序标号,对任一节点i(1<=i<=n)有:
(1)如果i=1,则节点是二叉树的根,无双亲。如果i>1,则其双亲是节点[i/2]。
(2)如果2i>n,则节点i无左孩子(节点i为叶子节点);否则其左孩子是节点2i。
(3)如果2i+1>n,则节点i无右孩子;否则其右孩子是节点2i+1;
6.3.3二叉树的存储结构
1、顺序存储
如果为完全二叉树,则可按层序顺序存储,如果不是完全二叉树,则将缺失的位置也分配存储空间,只是什么数据都不存储。浪费空间。
2 、链式存储
二叉树每个节点最多有两个孩子,所以为它设计一个数据域和两个指针域,称这样的链表叫做二叉链表。
lchild |
data |
rchild |
typedef struct BitNode
{
int data;
struct BitNode *lchild, *rchild;
} BitNode, *BitTree;
6.3.4遍历二叉树
二叉树的遍历:从根节点出发,按照某种次序依次访问二叉树中的所有节点,使得每个节点被访问依次仅且访问一次。
1、前序遍历:根节点,左子树,右子树。
void PreOrderTraverse(BitTree T)
{
if(T == NULL)
return ;
printf("%c",T->data);/*显示节点数据,可以更改为其它对节点操作。*/
PreOrderTraverse(T->lchild);
PreOrderTraverse(T->rchild);
}
2、中序遍历:左子树,根节点,右子树。
void InOrderTraverse(BitTree T)
{
if(T == NULL)
return ;
InOrderTraverse(T->lchild);
printf("%c",T->data);/*显示节点数据,可以更改为其它对节点操作。*/
InOrderTraverse(T->rchild);
}
3、后序遍历:左子树,右子树,根节点
void PostOrderTraverse(BitTree T)
{
if(T == NULL)
return ;
PostOrderTraverse(T->lchild);
PostOrderTraverse(T->rchild);
printf("%c",T->data);/*显示节点数据,可以更改为其它对节点操作。*/
}
前序遍历的第一个节点为根节点。中序遍历的根节点会在中间出现,其左面的为左子树的节点,右面的右子树的节点。后序遍历的最后一个节点为根节点。
已知前序遍历和中序遍历或者后序遍历和中序遍历均可确定一棵二叉树。但是已知前序遍历和后序遍历不能唯一确定一棵二叉树。
6.4 二叉树的建立
1、建立步骤
(1)将一个二叉树变为扩展二叉树。扩展二叉树是指将将一个非空节点的空孩子用#表示,就变为扩展二叉树,当遇到#时,就知道此节点为空。
(2)按前序遍历扩展二叉树(也可按其他顺序,创建的代码就会有所不同)。
如:AB#D##C##。
(3)实现的算法(前序遍历的序列)
void CreatBitTree(BitTree *T)
{
char ch;
scanf("%c",&ch); // 依次输入AB#D##C##
if(ch == '#')
*T = NULL;
else
{
*T = (BitTree) malloc(sizeof(BitNode));
if(!*T)
exit(OVERFLOW);
(*T)->data = ch;/*生成根节点*/
CreateBitTree(&(*T)->lchild);/*构造左子树*/
CreateBitTree(&(*T)->rchild); /*构造右子树*/
}
}
6.5 线索二叉树
假如一个二叉树节点数目为n,则指针域有2n个,但其中包含空指针域有n+1个。将这些空闲的位置用来存储节点的前驱和后继。称相应的二叉树为线索二叉树。
如果一个节点的lchild为空,则让它指向该节点的前驱,且ltag=1。否则指向左孩子,ltag=0;
如果一个节点的rchild为空,则让它指向该节点的后继,,且rtag=1。否则指向右孩子,rtag=0;