树的应用(堆和哈夫曼树)

n个元素的序列,K = {k1, k2,…,kn},把它的所有元素按完全二叉树层序存储在一个一维数组中,并满足:Ki <= K2i 且 Ki<=K2i+1 ,或Ki >= K2i 且 Ki >= K2i+1,则称为小堆(或大堆)。
最大堆: 根节点最大的堆
最小堆: 根节点最小的堆。

ps:堆中的元素可以重复
堆的操作

#include<stdio.h>
#include<stdlib.h>
typedef struct HeapStruct* MaxHeap;
struct HeapStruct {
	int* data;
	int size;
	int capacity;
};
int maxdata = 1000;
//最大堆的创建-->创建容量为maxsize的空的最大堆
MaxHeap Create(int maxsize)
{
	MaxHeap H = (MaxHeap)malloc(sizeof(MaxHeap));
	H->data = (int*)malloc((maxsize+1)*sizeof(int));
	H->size = 0;
	H->capacity = maxsize;
	H->data[0] = maxdata;
	return H;
}
//最大堆的插入-->将元素item插入到最大堆H中
void Insert(MaxHeap H, int item)
{
	int i;
	for (i = H->size + 1; H->data[i / 2] < item; i /= 2)
	{
		H->data[i] = H->data[i / 2];
	}
	H->data[i] = item;
}
//最大堆的删除-->删除最大堆H的最大元素
int DeleteMax(MaxHeap H)
{
	int parent, child;
	int maxitem, temp;
	if (!H->size)
	{
		printf("最大堆已空");
		return;
	}
    maxitem = H->data[1];
	temp = H->data[H->size--];
	for (parent = 1; parent * 2 <= H->size;parent=child)
	{
		child = parent * 2;
		if ((child != H->size) && (H->data[child] < H->data[child + 1]))
		{
			child++;//child指向左右子结点的较大者
		}
		if (temp >= H->data[child]) break;
		else //移动temp元素到下一层
			H->data[parent] = H->data[child];
	}
	H->data[parent] = temp;
	return maxitem;
}
//最大堆的建立-->将已经存在的n个元素按照最大堆的要求放在一维数组中
MaxHeap BuildMaxHeap(MaxHeap H)
{
	//假设所有的size个元素已经存入data中
	//本函数将元素调整,使满足最大堆的有序性
	int parent, child;
	int temp;
	for (int i = H->size / 2; i >= 1; i--)
	{
		temp = H->data[i];
		for (parent = i; parent * 2 <= H->size; parent = child)
		{
			child = parent * 2;
			if ((child != H->size) && (H->data[child] < H->data[child + 1]))
			{
				child++;
			}
			if (temp >= H->data[child]) break;
			else
				H->data[parent] = H->data[child];
		}
		H->data[parent] = temp;
	}
	return H;
}

哈夫曼树(最优二叉树)

  • :将树中结点赋给一个有着某种含义的数值,则这个数值称为该结点的权。
  • 结点的带权路径长度:从根结点到该结点之间的路径长度与该结点所带权值的乘积。
  • 树的带权路径长度:树中所有叶子结点的带权路径长度之和
  • 哈夫曼树(最优二叉树):带权路径长度 (WPL) 最短的二叉树

哈夫曼算法(构造哈夫曼树的方法)

(1)根据 n 个给定的权值 {w1, w2, …, wn} 构成 n 棵二叉树的森林F={T1, T2, …, Tn},其中 Ti 只有一个带权为 wi 的根结点。

(2)在 F 中选取两棵根结点的权值最小的树作为左右子树,构造一棵新的二叉树,且置新的二叉树的根结点的权值为其左右子树上根结点的权值之和。

(3)在 F 中删除这两棵树,同时将新得到的二叉树加入森林中。

(4)重复 (2) 和 (3),直到森林中只有一棵树为止,即为哈夫曼树。

typedef struct TreeNode* HuffmanTree;
struct TreeNode {
	int weight;
	HuffmanTree left,right;
};
HuffmanTree Huffman(MinHeap H)
{
	BuildMinHeap(H); //调整为最小堆
	for (int i = 1; i < H->size; i++)
	{
		HuffmanTree T = (HuffmanTree)malloc(sizeof(Huffman));
		T->left = DeleteMin(H);
		T->right = DeleteMin(H);
		T->weight = T->left->weight + T->right->weight;
		Insert(H, T);
	}
	T = DeleteMin(H);
	return T;
}

哈夫曼编码

为了获得更好的压缩效果,考虑实际应用中各字符的出现频度不相同,采用如下编码方式: 短(长)编码表示频率大 (小)的字符
ps:要求任一字符的编码都不能是另一字符编码的前缀!

例:假设用于通信的电文仅由8个字母 {a, b, c, d, e, f, g, h} 构成,它们在电文中出现的概率分别为{ 0.07, 0.19, 0.02, 0.06, 0.32, 0.03, 0.21, 0.10},试为这8个字母设计哈夫曼编码。

解:先将概率放大100倍,以方便构造哈夫曼树。权值集合 w={7, 19, 2, 6, 32, 3, 21, 10},按哈夫曼树构造规则(合并、删除、替换),可得到哈夫曼树。
在这里插入图片描述
在这里插入图片描述
译码

从哈夫曼树根开始,对待译码电文逐位取码。若编码是“0”,则向左走;若编码是“1”,则向右走,一旦到达叶子结点,则译出一个字符;再重新从根出发,直到电文结束。
在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值