树、二叉树的应用
一、二叉排序树(BST)
1、二叉排序树的定义:也称二叉查找树(检索树)。或者是一棵空树。具有以下性质:1)若左子树非空,则左子树上所有结点的值均小于根节点的值;2)若右子树非空,则右子树上的值均大于根节点的值;3)左右子树分别是一棵二叉排序树。对二叉排序树进行中序遍历可以得到一个递增的有序序列。
2、二叉排序的查找:
//二叉排序树的非递归查找算法
BSTNode *BST_Search(BiTree T,ElemType key){
while(T!=NULL&&key!=T->data){//若树非空且不等于当前结点的值
if(key<T->data) T=T->lchild;
else T=T->rchild;
}
}
//递归实现效率较低
BSTNode *BST_Search(BiTree T,ElemType key){
if(T==NULL) return NULL;
if(T->data==key) return T;
else if(T->data<key) return BST_Search(T->rchild,key);
else return BST_Serach(T->lchild,key);
}
3、二叉排序树的插入
int BST_Insert(BiTree &T,ElemType key){
if(T==NULL){//若树为空,则新插结点为根节点
T=(BiTree)malloc(sizeof(BSTNode));
T->key=key;
T->lchild=T->rchild=NULL;
return 1;
}
else if(key==T->key)//遇到相同值,则插入失败
return 0;
else if(key<T->key)//向左走
return BST_Insert(T->lchild,key);
else//向右走
return BST_Insert(T->rchild,key);
}
4、二叉排序树的构造
//二叉排序树的构造
void Create_BST(BiTree &T,ElemType str[],int n){
T=NULL;
int i=0;
while(i<n){
BST_Insert(T,str[i]);//执行插入算法
i++;
}
}
5、二叉排序树的删除
二叉排序树的删除不能把以该结点为根的子树都删除,必须先把被删除结点从存储二叉排序树的链表上摘下,将因删除结点而断开的二叉链表重新连接起来,同时确保二叉排序树的性质不会丢失。
1、若删除的是叶节点,则直接删除;
2、若结点z只有一棵左子树或右子树,则让z的子树代替z的位置;
3、若z结点有左右两棵子树,则让z的直接后继替代z,然后从二叉排序树中删除这个直接后继。
//二叉排序树的删除
6、二叉排序树的查找效率分析
二叉排序树的查找效率主要取决于树的高度,若二叉排序树的左右子树的高度之差的绝对值不超过1,**则这样的二叉排序树称为平衡二叉树。**它的平均查找长度为O(log2n)。若二叉排序树只有左孩子或者右孩子,那么单链查找长度为O(n)。
二、平衡二叉树
平衡二叉树保证任意时刻结点左右子树的高度差不超过1。主要为了避免树的高度增长过快,影响二叉排序树的性能。
2、平衡二叉树的插入
每次插入节点后,需要判断是否破坏了平衡树的性质,若已破坏则需要调整使之重新平衡。注意:每次调整的都是最小不平衡子树。即以插入路径上离插入结点最近的平衡因子的绝对值大于1的结点作为根的子树。
四种调整策略:
1)LL平衡旋转(右单旋)
2)RR平衡旋转(左单旋)
3)LR平衡旋转(左右双旋)
4)RL平衡旋转(右左双旋)
3、平衡二叉树的查找
查找过程和二叉排序树相同。平衡二叉树的平均查找长度为O(log2n)。
三、哈夫曼树
1、哈夫曼树的定义
树中的结点常常被赋予某种意义的数值,称为权。从树的根结点到任意节点的路径长度乘以该结点上的权值,称为该结点的带权路径长度。树中所有叶节点的带权路径长度之和称为该树的带权路径长度。
在含有n个带权叶节点的二叉树中,**带权路径最小的二叉树称为哈夫曼树。**也称最优二叉树。
2、哈夫曼树的构造
将n个节点分别作为只含有一个节点的二叉树,构造一个新节点,从F中选取两个最小权值的树,构成该新节点的左右子树。将新的树加入森林F。从F中删除刚才选过的两个节点。
性质:每个初始节点最终都会称为叶节点,权值最小的节点到根节点的路径长度最大;构造过程中共新建了n-1个节点,因此哈夫曼树的总结点数为2n-1.每次构造构造都选择2棵树作为新节点的孩子,因此哈夫曼树中不存在度为1的节点。
3、哈夫曼编码
若允许对不同字符用不等长的二进制位表示,称为可变长度编码。其特点是:对频率高的字符赋予短编码,对频率低的字符使用场边码。从而使平均编码长度简短,起到数据压缩的效果。
**前缀编码:**若没有一个编码是另一个编码的前缀,则称这样的编码为前缀编码。
哈夫曼编码过程:首先将每个出现的字符当作一个独立的节点,其权值为它出现的频度,构造出对应的哈夫曼树。然后由01标记左右子树进行编码。可以计算带权路径长度WPL最优且唯一。