目录
堆
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”,则向右走,一旦到达叶子结点,则译出一个字符;再重新从根出发,直到电文结束。