目录
一、引言
✨欢迎来到二叉树知识的最后一节,本节我们将介绍有序二叉树的创建,查找以及哈夫曼树和哈夫曼编码,这在数据的存储、查找、传输领域都有很多的应用,话不多说,VS,启动!😆
二、有序二叉树的创建
在创建有序二叉树时,我们先要建立一个规则:数据比根节点大的放在右子树,数据比根节点小的放在左子树,比如要存放的数据依次为:
c , b , a , e , d
,那么c作为根节点,b
放在c
的左子树,a
放在b
的左子树,e
放在c
的右子树,d
放在e
的左子树(按ASCLL码的值)
在实际应用中,我们会先将数据按其权重递减排序,(权重可以理解为数据的常用程度或者重要程度),然后依次放入有序二叉树中,这样存放的数据在查找时非常方便
//逐字符创建有序二叉树
void Tree_Order_Creative(BinTree** T, char x)
{
if (*T == NULL) //若节点为空,为节点分配一个内存
{
*T = (BinTree*)malloc(sizeof(BinTree));
if (*T == NULL) //处理内存分配失败的情况
{
return;
}
else
{
(*T)->data = x;
(*T)->lchile = (*T)->rchile = NULL;
}
}
else
{
if (x > (*T)->data) //数据比根节点大,存储到右子树
Tree_Order_Creative(&((*T)->rchile), x);
else //否则存储到左子树
Tree_Order_Creative(&((*T)->lchile), x);
}
}
三、在有序二叉树中查找
在二叉树的查找中由于我们每次选择一个方向,类似于有序数组二分法的查找,因此其时间复杂度为
//查找元素
BinTree* Tree_Order_Find(BinTree* T, char x)
{
if (T == NULL)
return NULL; //若不存在该元素
else if (x == T->data)
return T; //查找成功返回该节点指针
else if (x < T->data)
return Tree_Order_Find(T->lchile, x); //数据小于根节点,访问左子树
else
return Tree_Order_Find(T->rchile, x); //数据大于根节点,访问右子树
}
四、哈夫曼树
1)哈夫曼编码
哈夫曼树也称为“最优树”,它只有叶子节点存放数据,离根节点越近的节点权值越高。在哈夫曼树中,记左子树的编码为0,右子树的编码为1,将访问路径的编码组合起来就是哈夫曼编码。
在数据传输领域使用哈夫曼编码,可以极大地对需要传输的数据进行压缩,提高传输的效率。
如下图中a的编码为000,b的编码为01,c为10,d为11,e为0010,f为0011
可以发现,对于每个数据的编码,其前缀都是不相同的,也就是说,将这些数据的哈夫曼编码连成一长串发送出去,对方解析出来的数据是唯一的
比如0010000001101011110,对着上面的编码一个个看,断开:0010 000 0011 01 01 11 10,很容易知道其数据为eafbbdc
2)哈夫曼树的创建
哈夫曼树的创建使用到优先队列
1、先将所有数据入队列,其顺序如上图右
2、选出最小权值的两个数据出队列合并为一棵二叉树,将其权值和入队列
3、重复1和2,直到队列为空
该过程如下(标红的为合并后的数据):
当队列最前面的两个元素都不是合并后的元素时,需要另开一棵二叉树,否则一直往上增就好
注意大的数据放在左边,小的数据放在右边
3)数据结构
typedef struct BinTree{
ElemType data;
struct BinTree *lchile;
struct BinTree *rchile;
char flag[10]; //Huffman编码
}BinTree;
4)哈夫曼编码的创建
//计算该树叶子的哈夫曼编码值flag,并通过前序遍历输出
//左子树编码为0,右子树编码为1
void Haffman_Tree(BinTree** root, int i)
{
if (root != NULL)
{
if((*root)->lchile == NULL && (*root)->rchile == NULL)
printf("%s", (*root)->flag);
if ((*root)->lchile != NULL) //如果是左子树,编码末尾添0
{
strcpy((*root)->lchile->flag, (*root)->flag);
(*root)->lchile->flag[i] = '0';
Haffman_Tree(&(*root)->lchile, i + 1);
}
if ((*root)->rchile != NULL) //如果是右子树,编码末尾添1
{
strcpy((*root)->rchile->flag, (*root)->flag);
(*root)->rchile->flag[i] = '1';
Haffman_Tree(&(*root)->rchile, i + 1);
}
}
}
5)解码
对哈夫曼编码的解码过程也很简单,对于一串字符串编码,从根节点开始:
1、如果编码为0,查找左子树,如果编码为1,查找右子树,同时字符串下标加一;
2、重复1,直到找到叶子节点,该节点的数据则为编码的一个数据;
3、找到叶子节点后重回根节点继续查找
4、重复1,2,3直到字符串达到末尾
//解码哈夫曼编码
void decoing(BinTree* root, char keynum[])
{
BinTree* Root = root;
for (int i = 0; keynum[i] != '\0';)
{
if (root->lchile == NULL && root->rchile == NULL)
{
printf("%c", root->data); //如果是叶子结点,输出当前字符
root = Root; //回到根节点继续搜寻
}
else if (keynum[i] == '0') //如果为0则往左搜寻
{
root = root->lchile;
i++;
}
else if (keynum[i] == '1') //如果为1则往右搜寻
{
root = root->rchile;
i++;
}
else
return;
}
return;
}
最后感谢你观看完我的文章,如果文章对你有帮助,可以点赞收藏评论,这是对作者最好的鼓励!不胜感激🥰