一、二叉树的定义与基本操作
(1)定义
***每个结点的度都不大于2
***每个结点的孩子结点次序不能任意颠倒(孩子结点有左右之分,位于左边的孩子称为左孩子,
位于右边的称为右孩子)
(2)基本操作
《1》Initiate(bt)
将bt初始化为空二叉树
《2》Create(bt)
创建一棵非空二叉树bt
《3》Destory(bt)
销毁二叉树bt
《4》Empty(bt)
若bt为空,则返回TRUE,否则返回FALSE
《5》Root(bt)
求二叉树bt的根节点。若bt为空二叉树,则返回NULL
《6》Parent(bt,x)
求二叉树bt中结点x的双亲结点。若结点x是二叉树的根结点或二叉树bt中没有结点x,
则返回NULL
《7》LeftChild(bt,x)
求二叉树bt中结点x的左孩子。若结点x没有左孩子或二叉树中没有结点x,则返回NULL。
《8》RightChild(bt,x)
求二叉树bt中结点x的右孩子。若结点x没有右孩子或二叉树中没有结点x,则返回NULL。
《9》Traverse(bt)
遍历操作。按某个次序依次访问二叉树中每个结点一次且仅一次。
《10》Clear(bt)
清除操作。将二叉树bt置为空树。
二、二叉树的性质
(1)在二叉树的第i层上至多有2的(i-1)次方个结点(i>.=1)
(2)深度为k的二叉树至多有2的k次方减1个结点(i>.=1)
(3)对任意一棵二叉树T,若终端结点数为n0,而其度数为2的结点数为n2,则n0 = n2+1
(4)具有n个结点的完全二叉树的深度为|_log2n_|+1
(5)对于具有n个结点的完全二叉树,如果按照从上到下和从左到右的顺序对二叉树中的所有结点从1
开始编号,则对于任意的序号为i的结点有:
***如果i= 1,则序号为i的结点是根节点,无双亲结点;如i>1,则序号为i的结点的
双亲结点序号为|_i/2_|
***如2i>n,则序号为i的结点没有左孩子;如2i<=n,则序号为i的结点的左孩子结点的序号为2i
***如2i+1>n,则序号为i的结点没有右孩子;如2i+1<=n,则序号为i的结点的右孩子结点的
序号为2i+1
三、二叉树的存储结构
(1)顺序存储结构
对于完全二叉树来说,可以将其数据元素逐层存放到一组连续的存储单元中。
根据二叉树的性质5,可得结点i的左孩子的位置为LChild(i) = 2i,右孩子的位置为RChild(i) = 2i+1。
对于一般的二叉树,必须使用“虚结点”将其补成一棵“完全二叉树”来存储,这就会造成
空间浪费。
(2)链式存储结构
设计每个结点至少包括三个域:数据域、左孩子域和右孩子域。
结点结构
LChild域指向该结点的左孩子,Data域记录该结点的信息,RChild域指向该结点的右孩子。
由此结点结构形成的二叉树称为二叉链表。
若一棵二叉树含有n个结点,则它的二叉链表中必有2n个指针域,其中必有n+1个空的链域。
链表的头指针指向二叉树的根节点。
数据结构:
typedef struct node
{
DataType data ;
struct node *LChild;
struct node *RChild;
}BiTNode,*BiTree;
四、二叉树的遍历与线索化
(1)二叉树的遍历
二叉树的遍历是指按一定规律对二叉树中的每个结点进行访问切仅访问一次。
遍历操作就是将二叉树中结点按一定规律线性化的操作,目的在于将非线性化结构变成线性化的。实质是对一个
非线性结构进行线性化操作,使每个结点(除第一个和最后一个外)在这些线性序列中有且仅有一个直接前驱和
直接后继。
访问序列。
二叉树是由3个基本单元组成:根节点、左子树和右子树,因此,若能依次遍历这三部分,便是遍历了整个二叉树。
遍历思想(递归方式遍历):
《1》先序遍历
***访问根结点
***按先序遍历左子树
***按先序遍历右子树
《2》中序遍历
***按中序遍历左子树
***访问根结点
***按中序遍历右子树
《3》后序遍历
***按后序遍历左子树
***按后序遍历右子树
***访问根结点
遍历算法:
《1》先序遍历
void PreOrder(BiTree root)
{
if(root != NULL)
{
Visit(root->data);
PreOrder(root->LChild);
PreOrder(root->RChild);
}
}
《2》中序遍历
void InOrder(BiTree root)
{
if(root != NULL)
{
InOrder(root->LChild);
Visit(root->data);
InOrder(root->RChild);
}
}
《3》后续遍历
void PostOrder(BiTree root)
{
if(root != NULL)
{
PostOrder(root->LChild);
PostOrder(root->RChild);
Visit(root->data);
}
}
(2)遍历算法应用
《1》先序遍历输出二叉树中的结点
void PreOrder(BiTree root)
{
if(root != NULL)
{
printf(root->data);
PreOrder(root->LChild);
PreOrder(root->RChild);
}
}
《2》先序遍历输出二叉树总的叶子结点
void PreOrder(BiTree root)
{
if(root != NULL)
{
if(root->LChild == NULL && root->RChild == NULL)
{
printf(root->data);
}
PreOrder(root->LChild);
PreOrder(root->RChild);
}
}
《3》后序遍历统计叶子结点数目
int leafCount = 0;
void leaf(BiTree root)
{
if(root != NULL)
{
leaf(root->LChild);
leaf(root->RChild);
if(root->LChild == NULL && root->RChild == NULL)
{
leafCount++;
}
}
}
《4》后序遍历求二叉树的高度
思想:
若root为空,则高度为0;
若root非空,则高度为其左、右子树高度的最大值加1
int PostTreeDepth(BiTree root)
{
int hl,hr,max;
if(root != NULL)
{
hl = PostTreeDepth(root->LChild);
hr = PostTreeDepth(root->RChild);
max = hl>hr?hl:hr;
return max+1;
}
else
{
return 0;
}
}
《5》先序遍历创建二叉链表
void CreateBiTree(BiTree *bt)
{
char ch;
ch = getchar();
if(ch = '.')
{
*bt = NULL;
}
else
{
*bt = (BiTree)malloc(sizeof(BiTNode));
(*bt)->data = ch;
CreateBiTree(&((*bt)->LChild));
CreateBiTree(&((*bt)->RChild));
}
}