本章的主要内容
树的逻辑结构
树的存储结构
二叉树的逻辑结构
二叉树的存储结构及实现
树、森林与二叉树的转换
哈夫曼树
树的抽象数据类型定义
ADT Tree
Data
树是由一个根结点和若干棵子树构成,
树中结点具有相同数据类型及层次关系
Operation
InitTree
前置条件:树不存在
输入:无
功能:初始化一棵树
输出:无
后置条件:构造一个空树
DestroyTree
前置条件:树已存在
输入:无
功能:销毁一棵树
输出:无
后置条件:释放该树占用的存储空间
Root
前置条件:树已存在
输入:无
功能:求树的根结点
输出:树的根结点的信息
后置条件:树保持不变
Parent
前置条件:树已存在
输入:结点x
功能:求结点x的双亲
输出:结点x的双亲的信息
后置条件:树保持不变
Depth
前置条件:树已存在
输入:无
功能:求树的深度
输出:树的深度
后置条件:树保持不变
前序遍历
树的前序遍历操作定义为:
若树为空,不进行遍历;否则
⑴ 访问根结点;
⑵ 按照从左到右的顺序前序遍历根结点的每一棵子树。
后序遍历
树的后序遍历操作定义为:
若树为空,则遍历结束;否则
⑴ 按照从左到右的顺序后序遍历根结点的每一棵子树;
⑵ 访问根结点。
层序遍历
树的层序遍历操作定义为:
从树的第一层(即根结点)开始,自上而下逐层遍历,在同一层中,按从左到右的顺序对结点逐个访问。
树的存储结构
孩子链表表示法(每个节点创建一个单链表)
把每个结点的孩子排列起来,看成是一个线性表,且以单链表存储,则n个结点共有 n 个孩子链表。
这 n 个单链表共有 n 个头指针,这 n 个头指针又组成了一个线性表。
为了便于进行查找采用顺序存储存储每个链表的头指针。
最后,将存放 n 个头指针的数组和存放n个结点的数组结合起来,构成孩子链表的表头数组。
孩子兄弟表示法
某结点的第一个孩子是惟一的
某结点的右兄弟是惟一的
设置两个分别指向该结点的第一个孩子和右兄弟的指针
data:数据域,存储该结点的数据信息;
firstchild:指针域,指向该结点第一个孩子;
rightsib:指针域,指向该结点的右兄弟结点。
template <class T>
struct TNode{
T data;
TNode <T> *firstchild, *rightsib;
};
二叉树的定义
二叉树是n(n≥0)个结点的有限集合,该集合或者为空集(称为空二叉树),或者由一个根结点和两棵互不相交的、分别称为根结点的左子树和右子树的二叉树组成。
二叉树的特点:
⑴ 每个结点最多有两棵子树;
⑵ 二叉树是有序的,其次序不能任意颠倒。
斜树
1 .所有结点都只有左子树的二叉树称为左斜树;
2 .所有结点都只有右子树的二叉树称为右斜树;
3.左斜树和右斜树统称为斜树。
斜树的特点:
- 在斜树中,每一层只有一个结点;
2.斜树的结点个数与其深度相同。
满二叉树
在一棵二叉树中,如果所有分支结点都存在左子树和右子树,并且所有叶子都在同一层上。
满二叉树的特点:
叶子只能出现在最下一层;
只有度为0和度为2的结点。
特殊的二叉树
对一棵具有n个结点的二叉树按层序编号,如果编号为i(1≤i≤n)的结点与同样深度的满二叉树中编号为i的结点在二叉树中的位置完全相同。
完全二叉树的特点**
- 叶子结点只能出现在最下两层,且最下层的叶子结点都集中在二叉树的左部;
- 完全二叉树中如果有度为1的结点,只可能有一个,且该结点只有左孩子。
- 深度为k的完全二叉树在k-1层上一定是满二叉树。
二叉树的抽象数据类型定义
ADT BiTree
Data
由一个根结点和两棵互不相交的左右子树构成,
结点具有相同数据类型及层次关系
Operation
InitBiTree
前置条件:无
输入:无
功能:初始化一棵二叉树
输出:无
后置条件:构造一个空的二叉树
DestroyBiTree
前置条件:二叉树已存在
输入:无
功能:销毁一棵二叉树
输出:无
后置条件:释放二叉树占用的存储空间
InsertL
前置条件:二叉树已存在
输入:数据值x,指针parent
功能:将数据域为x的结点插入到二叉树中,作为结点parent的左孩子。如果结点parent原来有左孩子,则将结点parent原来的左孩子作为结点x的左孩子
输出:无
后置条件:如果插入成功,得到一个新的二叉树
DeleteL
前置条件:二叉树已存在
输入:指针parent
功能:在二叉树中删除结点parent的左子树
输出:无
后置条件:如果删除成功,得到一个新的二叉树
Search
前置条件:二叉树已存在
输入:数据值x
功能:在二叉树中查找数据元素x
输出:指向该元素结点的指针
后置条件:二叉树不变
PreOrder
前置条件:二叉树已存在
输入:无
功能:前序遍历二叉树
输出:二叉树中结点的一个线性排列
后置条件:二叉树不变
InOrder
前置条件:二叉树已存在
输入:无
功能:中序遍历二叉树
输出:二叉树中结点的一个线性排列
后置条件:二叉树不变
PostOrder
前置条件:二叉树已存在
输入:无
功能:后序遍历二叉树
输出:二叉树中结点的一个线性排列
后置条件:二叉树不变
LeverOrder
前置条件:二叉树已存在
输入:无
功能:层序遍历二叉树
输出:二叉树中结点的一个线性排列
后置条件:二叉树不变
endADT
二叉树的遍历操作
前序(根)遍历
若二叉树为空,则空操作返回;否则:
①访问根结点;
②前序遍历根结点的左子树;
③前序遍历根结点的右子树。
void Preorder(int root, char data[]){
if(data[root]!='\0'){
cout<<data[root] ;
Preorder(2*root,data);
Preorder(2*root+1,data);
}
return;
}
中序(根)遍历
若二叉树为空,则空操作返回;否则:
①中序遍历根结点的左子树;
②访问根结点;
③中序遍历根结点的右子树。
void InOrder(int root, char data[]){
if(data[root]!='\0'){
InOrder(2*root,data);
cout<<data[root] ;
InOrder(2*root+1,data);
}
return;
}
后序(根)遍历
若二叉树为空,则空操作返回;否则:
①后序遍历根结点的左子树;
②后序遍历根结点的右子树。
③访问根结点;
void PostOrder(int root, char data[]){
if(data[root]!='\0'){
PostOrder(2*root,data);
PostOrder(2*root+1,data);
cout<<data[root] ;
}
return;
}
层序遍历
二叉树的层次遍历是指从二叉树的第一层(即根结点)开始,从上至下逐层遍历,在同一层中,则按从左到右的顺序对结点逐个访问。
Creat创建二叉树
void create(char preorder[],char inorder[],int start_p, int end_p,int start_i,int end_i, char data[],int root){
if(start_p>end_p)
return ;
else{
int k;
for(int i=start_i;i<=end_i;i++){
if(inorder[i]==preorder[start_p]){
k=i;
break;
}
}
data[root]=preorder[start_p];
create(preorder,inorder,start_p+1,start_p+k-start_i,start_i,k-1,data, 2*root);
create(preorder,inorder,start_p+k-start_i+1,end_p,k+1,end_i,data,2*root+1);
}
return ;
}
二叉链表
基本思想:令二叉树的每个结点对应一个链表结点,链表结点除了存放与二叉树结点有关的数据信息外,还要设置指示左右孩子的指针。
其中,data:数据域,存放该结点的数据信息;
lchild:左指针域,存放指向左孩子的指针;
rchild:右指针域,存放指向右孩子的指针。
template <class T>
struct BiNode
{
T data;
BiNode<T> *lchild, *rchild;
};
二叉链表存储结构的类声明
template
class BiTree
{
public:
BiTree();
~BiTree( );
void PreOrder(){PreOrder(root);}
void InOrder() {InOrder(root);}
void PostOrder() {PostOrder(root);}
void LevelOrder(){LeverOrder(root)};
private:
BiNode *root;
BiNode * Creat( );
void Release(BiNode *root);
void PreOrder(BiNode *root);
void InOrder(BiNode *root);
void PostOrder(BiNode *root);
void LevelOrder(BiNode *root);
};
前序遍历——递归算法
template
void BiTree::PreOrder(BiNode *root)
{
if (root ==NULL) return;
else {
cout<data;
PreOrder( );
PreOrder( );
}
}
前序遍历——非递归算法(伪代码)
1.栈s初始化(空栈);
2.循环直到root为空且栈s为空
2.1 当root不空时循环
2.1.1 输出root->data;
2.1.2 将指针root的值保存到栈中;
2.1.3 继续遍历root的左子树(root=root->lchild)
2.2 如果栈s不空,则
2.2.1 将栈顶元素弹出至root(root=s.pop());
2.2.2 准备遍历root的右子树(root=root->rchild);
template <class T>
void BiTree::PreOrder(BiNode<T> *root) {
SeqStack<BiNode<T> *> s;
while (root!=NULL | | !s.empty()) {
while (root!= NULL) {
cout<<root->data;
s.push(root);
root=root->lchild;
}
if (!s.empty()) {
root=s.pop();
root=root->rchild;
}
}
}
template <class T>
void BiTree::PreOrder(BiNode<T> *root) {
SeqStack<BiNode<T> *> s;
while (root!=NULL | | !s.empty()) {
while (root!= NULL) {
cout<<root->data;
s.push=root;
root=root->lchild;
}
if (!s.empty()) {
root=s.pop();
root=root->rchild;
}
}
}
二叉树的建立
1.按前序扩展遍历序列输入输入节点的值
1.1如果输入节点之为“#”,则建立一棵空的子树
1.2否则,根结点申请空间,将输入值写入数据域中,
2.以相同方法的创建根节点的左子树
3.以相同的方法创建根节点的右子树
递归方法
template <class T>
BiTree ::BiTree(){
root=creat();
}
template <class T>
BiNode<T> * BiTree ::Creat(){
BiNode<T> *root; char ch;
cin>>ch;
if (ch=='# ') root=NULL;
else {
root=new BiNode<T>;
root->data=ch;
root->lchild=creat();
root->rchild= creat();
}
return root
}
template <class T>
BiNode<T> * BiTree ::Creat(){
BiNode<T> *root; char ch;
cin>>ch;
if (ch=='# ') root=NULL;
else {
root=new BiNode<T>;
root->data=ch;
root->lchild=creat();
root->rchild= creat();
}
return root
}