树与二叉树(二)

1.线索二叉树
(1)线索二叉树的结点结构
在二叉树进行线索化时,规定:若无左子树,令lchild指向其前驱结点;若无右子树,则令rchild指向其后继结点,如下图所示,还需增加两个标志域表明当前指针域所指对象是指向左(右)子节点还是指向直接前驱(后继)。

标志域1左指针域数据域右指针域标志域2
Itaglchilddatarchildrtag

其中,标志域的含义如下:
ltag=0,lchild指向结点的左孩子
ltag=1,lchild指向结点的前驱
rtag=0,rchild指向结点的右孩子
rtag=1,rchild指向结点的后继

线索二叉树的存储结构描述如下:
typedef struct ThreadNode{
	ElemType data;		//
	struct ThreadNode *lchild,rchild;	//左右指针域
	int ltag,rtag;		//左右线索标志
	}ThreadNode,*ThreadTree;

(2)线索二叉树的构造
对二叉树的线索化,实质上就是遍历一次二叉树,在遍历过程中,检查当前结点左、右指针域是否为空,若为空,则将它们改为指向前驱结点或后继结点的线索。
中序线索二叉树及其二叉链表表示如下:
在这里插入图片描述

通过中序遍历对二叉树线索化的递归算法如下:
void InThread(ThreadTree &p,ThreadTree &pre){
//通过中序遍历对二叉树线索化的递归算法
if(p!=NULL){
	InThread(p->lchild,pre);	//递归,线索化左子树
	if(p->lchild==NULL){		//左子树为空,建立前驱线索
	p->child=pre;
	p->ltag=1;
	}
   if(pre!=NULL&&pre->rchild==NULL){
   pre->rchild=p;			//建立前驱的后继
   pre->rtag=1;
   }
   pre=p;		//标记当前已被访问
   InThread(p->rchild,pre);		//递归,边缘化右子树
   }//
   }
通过中序遍历建立中序线索二叉树的主过程算法如下:
void CreateInThread(ThreadTree T){
ThreadTree pre=NULL;		//
if(T!=NULL){		//线索化非空
InThread(T,pre);	//线索化
p->rchild=NULL;		//处理遍历的最后一个结点
pre->rtag=1;
}
}

2.树、森林
(1)树的存储结构
1)双亲表示法
该方式采用一组连续空间存储每个结点,同时在每个结点中增设一个伪指针,指示其双亲结点在数组中的位置,如下图所示,根结点下标为0,其伪指针域为-1。
在这里插入图片描述

双亲表示法的存储描述如下:
#define MAX_TREE_SIZE 100	//树的最大节点数
typedef struct{			//结点定义
ElemType data;			
int parent;	//双亲位置域
}PTNode;
typedef struct{			//树的类型定义
PTNode nodes[MAX_TREE_SIZE];	//双亲表示
int n;
}PTree;

2)孩子表示法
孩子表示法是将每个结点的孩子结点使用单链表连接起来构成一个线性结构,此时,若有n个结点,则有n个孩子链表(叶子结点的孩子链表为空),如下图所示
在这里插入图片描述
该种方式寻找子女结点的操作比较直接,而寻找双亲结点需要遍历n个结点中孩子链表指针域所指向的n个孩子链表。
3)孩子兄弟表示法
孩子兄弟表示法,每个结点包括三部分内容:结点值、指向结点的第一个孩子结点的指针和指向下一个兄弟结点的指针,如下所示:
在这里插入图片描述

孩子-兄弟表示法分存储结构描述如下:
typedef struct CSNode{
	ElemType data;	
	struct CSNode *firstchild,*nextsibling;//第一个孩子和右兄弟指针
}CSNode,*CSTree;

(2)树、森林与二叉树的转换
树转换成二叉树:连兄弟,留长子,删孩子

a) 连线,连接所有兄弟结点。

b) 删线,仅保留双亲与长子结点的连线,删除与其他孩子结点之间的连线。

c) 整理,原有的长子结点为左子树,从兄弟转换为孩子的结点为右子树。

d) 注意,由于树根没有兄弟结点,固树转换为二叉树后,二叉树根结点的右子树必为空。

在这里插入图片描述
与此类似,将森林转换成二叉树:连树根及兄弟,留长子,删孩子;
在这里插入图片描述
将二叉树转换成树:连左孩子的右孩子及其右孩子…,删原树右孩子
在这里插入图片描述

将二叉树转换成森林:连左孩子的右孩子及其右孩子…,删原树右孩子
在这里插入图片描述
(本文有关树和二叉树及森林转换的部分内容借鉴了网络资源,源网址为:森林与二叉树的转换

(3)树的应用—并查集
并查集为一种简单的集合表示,其支持以下三种操作:
1)Union(S,Root1,Root2):将集合S的子集合Root2并入子集合Root1,要求两者互不相交,否则不执行合并;
2)Find(S,x):查找集合S中单元素x所在的子集合,并返回该子集合的名字;
3)Initial(S):将集合S中的每一个元素都初始化为只有一个单元素的子集合。

并查集的结构定义如下:
#define SIZE 100
int UFSets[SIZE];  //集合元素数组(双亲指针数组)

并查集的初始化操作(S为并查集)
void Initial(int S[]){
	for(int i=0;i<size;i++)		//每一个自成单元集合
	S[i]=-1;
	}
Find操作(函数在并查集S中查找并返回元素x的树的根)
int Find(int S[],int x){
while

3…树与二叉树的应用
(1)二叉排序树
1)定义
二叉排序树(BST),或者是一棵空树,或者满足以下定义:
若右子树非空,则左子树所有结点关键字值小于根结点的关键字值;
若左子树非空,则右子树多有结点关键字值小于根结点的关键字值;
左右子树分别也是一棵二叉树。
2)查找
二叉排序树的查找是从根结点出发,沿某个分支逐层向下比较的过程。若二叉树非空,则将定值与根结点关键字相比较,若相等,则查找成功;若不等,则当根结点的关键字值大于给定的关键字值时,在根结点的左子树中查找,否则在根结点的右子树中查找。

二叉排序树的非递归查找算法:
BSTNode *BST_Search(BiTree T,Elemtype key,BSTNode *&p){
//该查找函数返回的是关键字key的结点指针,若不存在,返回NULL
p=NULL;	//p指向被查找结点的双亲,用于插入和删除
while(T!=NULL&&key!=T->data){
p=T;
if(key<T->data)
T=T->child;
else
T=T->rchild;
}
return T;
}

3)插入
由于二叉排序树是递归定义的,因此插入节点的过程如下:若原二叉排序树为空,则直接插入结点;否则关键字k小于根结点关键字,则插入左子树,若关键字k大于根结点关键字,则插入右子树,算法描述如下:

int BST_Insert(BiTree &T,KeyType k){
//树中插入一个关键字为k结点
if(T==NULL){
//原树若为空,则新插入的记录为根结点
T=(BiTree)malloc(sizeof(BSTNode));
T->key=k;
T->lchild=T->rchild=NULL;
return 1;
}
else if(k==T->key)
return 0;
else if(k<T->key)
return BST_Insert(T->lchild,k);
else
return BST_Insrt(T->rchild,k);
}

4)构造
算法描述如下:

void Create_BST(BiTree &T,KeyType str[],int n){
//用关键字数组建立一个二叉树
T=NULL;
int i=0;
while(i<n){
BST_Insert(T,str[i]);
i++;
}
}

5)删除
在二叉排序树中删除一个结点时,不能把以该结点为根的子树上的结点全部删除,而是先把需要删除的结点从存储二叉树的链表上摘下来,再将两者连接起来。
具体思路:
二叉排序树为空树,则新插入的结点为新的根结点;否则,新插入的结点必为一个新的叶子结点,其插入位置由查找不成功的位置确定。
二叉排序树的3种删除情况具体如下:
在这里插入图片描述
平均查找长度ASL=(所在层数*该层所含个数的n层累和)/n;
(2)平衡二叉树
1)平衡二叉树(简称平衡树AVL):任意结点左右子树高度差不超过1.
2)平衡二叉树的插入
基本思想:若在二叉排序树中插入或删除一个结点时,首先要检查其插入路径是否因为此次操作导致了不平衡,若导致了不平衡,则需要做出相应的调整,一般的调整情况有下面四种:
LL(右单旋转)
RR(左单旋转)
LR(先左后右双旋转)
RL(先右后左双旋转)
(3)哈夫曼树和哈夫曼编码
1)定义
含有n个带权叶子节点的二叉树中,其中带权路径长度(WPL)最小的二叉树称为哈夫曼树,也称最优二叉树。
2)构造
算法基本思想:
给定m个权值,首先构造m课扩充二叉树,每颗只有一个外部结点(根结点);
在已经构造的所有扩充二叉树中,选择根结点权值最小的和次小的两颗,将它们作为左、右子树,构造一颗新的扩充二叉树,它的根结点的权值为其左右子树根结点的权值之和;
重复上一步,每次使得扩充二叉树的数目减一,当只剩下最后一颗扩充二叉树时,其便是所求的哈夫曼树。
3)哈夫曼编码
图源网络

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值