树的重点:二叉树
{
1.二叉树的存储结构;
2.二叉树的性质;
3.二叉树的基本操作;
4.二叉树的遍历;
5.线索二叉树;
6.二叉树的排序;
7.哈夫曼树
1.二叉树的定义:
二叉树是n个结点的有穷集合D与D上关系结合R构成的结构。当n=0时,称该二叉树为空二叉树;否则,它便是包含了一个根结点以及两棵不相交的、分别称为左子树与右子树的二叉树。
二叉树的定义是一个递归定义,因为在定义中又用到了二叉树的概念。
2.二叉树的性质:
a.具有n个结点的非空二叉树有且仅有n-1个结点。
b.非空二叉树的第i层最多有2的i-1次方个结点。
c.深度为h的非空二叉树最多有2的h次方-1个结点。
d.非空二叉树中,若叶节点的数目为n0,度为2的结点数为n2,则有关系n0 = n2 + 1 成立。
e.具有n个结点的完全二叉树的深度为h = log2n + 1;
f.二叉树中编号为i的结点具有以下性质:
(1)若编号i>1,则编号为i的结点的双亲结点的编号为i/2;当n = 1时,编号为i的结点为二叉树的根结点,没有双亲结点。
(2)若2i<=n,则编号为i的左孩子结点的编号为2i;若2i > n,则编号为i的结点无左孩子。
(3)若2i + 1 <= n,则编号为i的结点的右孩子结点的编号为2i + 1;若2i + 1 > n,则编号为i的结点无右孩子;
二叉树的存储结构:
1.二叉树的顺序存储结构。
顺序存储结构,是把二叉树进行编号,从左到右依次进行,然后把没有结点的右结点进行假设,称为“虚结点”。然后把元素存入到数组中。这种存储结构仅对于完全二叉树来说比较方便,但是对于非完全二叉树,尤其是右子树时,很难来说明它的结构形式。并且进行插入和删除操作时,容易造成错误。故不可以这样做。
2.二叉树的链式存储结构。
链式存储结构来说,又可以分成两种存储的形势
a.二叉链表结构
即结点中只存在三个结点,一个数据域,两个指针域。并且,指针域中的指针分别指向了左右两个孩子。如果孩子不存在,则该指针域中存放空。
b.三叉链表结构
即在二叉链表的基础上又添加了一个指针域,来指向parents结点。这个属性,将在后续的线索二叉树中很有用。
总结:
二叉链表结构灵活,方便,结点的最大数目只受系统最大可用存储空间的限制。一般情况下,二叉链表结构不仅节省空间,而且操作方便。
3.二叉链表的代码定义:
#include "stdafx.h"
#include <iostream>
#define MaxSize 100
using namespace std;
typedef struct Node
{
char chElem;
struct Node* lchild,*rchild;
}BTNode,*BTREE;
// 二叉树的建立
void BuildBT(BTREE& T)
{
char chA = ' ';
cin >> chA;
if (chA == ' ')
{
T = NULL; // 建立一个空树;
}
else
{
T = (BTREE)malloc(sizeof(BTNode));
T->chElem = chA;
BuildBT(T->lchild);
BuildBT(T->rchild); // 递归的创建左右子树;
}
}
// 二叉树的复制;
BTREE CopyBT(BTREE& T)
{
BTREE TNEW;
if (T == NULL)
{
return NULL;
}
else
{
TNEW = (BTREE)malloc(sizeof(BTNode));
TNEW->chElem = T->chElem;
TNEW->lchild = CopyBT(T->lchild); // 递归复制左子树;
TNEW->rchild = CopyBT(T->rchild);// 递归复制右子树;
}
return TNEW;
}
// 二叉树的销毁主要方法为:
// 将所有的结点从二叉树中删除,并且释放各结点所占用的空间,然后将
// 跟结点的指针值为NULL,使之成为一棵空的二叉树。
// 当然销毁之前需要遍历二叉树,其中最好的遍历方法就是进行后序遍历。
void DestroyBT(BTREE& T)
{
// T 为二叉树根结点所在的位置
if (T)
{
DestroyBT(T->lchild);
DestroyBT(T->rchild);
free(T);
}
T = NULL;
// 删除并释放二叉树中所有的结点,使之成为一棵空的二叉树;
}
// 递归的使用。求二叉树的叶子的个数。
int Countleaf(BTREE T)
{
if (T == NULL)
{
cout << "没有叶子结点!" << endl;
return 0;
}
if (T->lchild == NULL && T->rchild == NULL)
{
return 1;
}
return Countleaf(T->lchild)+Countleaf(T->rchild);
}
// 求二叉树的深度。
// 算法等于其左子树与右子树最大深度值加上1;
int BTDepth(BTREE T)
{
int leftDepth = 0;
int rightDepth = 0;
if (T == NULL)
{
return 0;
}
else
{
leftDepth = BTDepth(T->lchild);
rightDepth = BTDepth(T->rchild);
if (leftDepth > rightDepth)
{
return rightDepth + 1;
}
else
{
return leftDepth + 1;
}
}
}
// 访问结点,直接输出结点的元素值。
void visit(BTREE T)
{
cout << T->chElem<< " ";
cout << endl;
}
// 二叉树的前序遍历;
void PreOrder(BTREE T)
{
if (T != NULL)
{
visit(T); // 访问T所指的结点;
}
}
// 中序遍历
void MidOrder(BTREE T)
{
if (T != NULL)
{
MidOrder(T->lchild); // 遍历T所指结点的左子树;
visit(T); // 访问结点中的元素
MidOrder(T->rchild); // 遍历T所指结点的右子树;
}
}
// 后序遍历
void PostOrder(BTREE T)
{
if (T != NULL)
{
PostOrder(T->lchild);
PostOrder(T->rchild);
visit(T);
}
}
// 测试二叉树是否为等价
int IsEqualBT(BTREE& T1,BTREE& T2)
{
if (T1 == NULL && T2 == NULL)
{
cout << "比较的两棵树为空树,因此相等!" << endl;
return 1;
}
if (T1 != NULL && T2 != NULL && T1->chElem == T2->chElem && IsEqualBT(T1->lchild,T2->lchild)
&& IsEqualBT(T1->rchild,T2->rchild))
{
return 1;
}
return 0;
}
// 求二叉树的深度问题
int DepthBT(BTREE& T)
{
BTREE Stack1[MaxSize],p = T;
int Stack2[MaxSize] = {0};
int iCurdepth = 0,iMaxdepth = 0,iTop = -1;
if (T != NULL)
{
iCurdepth = 1;
do
{
while(p)
{
Stack1[++ iTop] = p; // 当前p所指的结点进栈;
Stack2[iTop] = iCurdepth; // 当前深度值进栈;
p = p->lchild; // 将p移动到左孩子结点处;
iCurdepth ++; // 层次数加1;
}
p = Stack1[iTop];
iCurdepth = Stack2[iTop --]; // 退栈操作;
if (p->lchild == NULL && p->rchild == NULL) // 若p所指的结点为叶结点:
{
if (iCurdepth > iMaxdepth)
{
iMaxdepth = iCurdepth;
}
}
p = p->rchild;
iCurdepth ++;
} while (!(p == NULL && iTop == -1));
}
return iMaxdepth;
}
// 求二叉树结点所在的层次;
// 作个假设:二叉树中存在这样的结点,并且不多于一个。
// 求二叉树中所在层次的方法:利用后续遍历。
// 遍历中,判段是否满足条件,若是满足条件的结点,则此时堆栈中保留
// 的元素个数加1,即为该结点所在的层次数;
int LayerBT(BTREE& T,char chItem)
{
BTREE Stack1[MaxSize],p = T;
int Stack2[MaxSize],iFlag,iTop = -1;
do
{
while(p)
{
Stack1[++ iTop] = p; // 当前p所指结点进栈;
Stack2[iTop] = 0;// 标志0进栈;
p = p->lchild;
}
p = Stack1[iTop];
iFlag = Stack2[iTop --]; // 退栈;
if (iFlag == 0)
{
Stack1[++ iTop] = p; // 当前p所指结点的地址再次进栈;
Stack2[iTop] = 1; // 标志1进栈;
p = p->rchild;
}
else
{
if (p->chElem == chItem)
{
return iTop + 2; // 求的结点所在的层数;
}
p = NULL;
}
} while (!(p == NULL && iTop == -1));
}
// 二叉树的删除操作;
// 这里的删除操作主要是:删除数据信息为item的那个结点和以该结点为
// 根结点的子树;
// 分两步走:1、遍历找到数据信息为item的元素;
// 2.先将该结点的双亲结点的相应指针域置为null,然后释放以该结点为根结点
// 的子树的所有结点的存储空间;
BTREE DeleteBT(BTREE& T,char chItem)
{
BTREE Stack[MaxSize],q,p = T;
int iTop = -1;
// 如果根结点满足情况,则删除的是整棵二叉树;
if (T->chElem == chItem)
{
DestroyBT(T);
return NULL;
}
else
{
do
{
while(p)
{
if (p->chElem == chItem)
{
if (q->lchild == p)
{
q->lchild = NULL; // p的双亲结点的做指针域置空;
}
else
{
q->rchild = NULL;// 否则的话,右指针域置空;
}
DestroyBT(p); // 删除以p为根结点的子树;
return T;
}
Stack[++ iTop] = p; // 当前p所指结点的地址进栈;
q = p; // 保存当前p所指的地址;
p = p->lchild;
}
p = Stack[iTop --]; // 退栈;
q = p; // 保存当前p所指结点的地址;
p = p->rchild; // 将p移动到其右孩子结点;
} while (!(p == NULL && iTop == -1));
}
}
// 交换所有结点左右子树的位置
// 该操作采用按层次遍历的方法比较合适。遍历中访问一个结点时,就讲
// 该结点的左右子树的位置进行交换。
void ExchangeBT(BTREE& T)
{
BTREE Queue[MaxSize],temp,p = T;
int iFront,iRear;
if (T)
{
Queue[0] = T;
iFront = -1;
iRear = 0;
while(iFront < iRear)
p = Queue[++ iFront]; // 推出队头的元素送到p中
temp = p->lchild;
p->lchild = p->rchild;
p->rchild = temp; // 交换左右子树的位置;
if (p->lchild != NULL)
{
Queue[++ iRear] = p->lchild; // p 所指结点的左孩子的地址进队;
}
if (p->rchild)
{
Queue[++ iRear] = p->rchild;
}
}
}
int main(int argc,char* argv[])
{
BTREE head;
cout << "树的结点元素为单个字符,请输入一个字符: " << endl;
BuildBT(head);
return 0;
}