哈夫曼树
1.哈夫曼树:假设由m个权值{W1,W2,…,Wm},可以构造一棵含m个叶子结点的二叉树,第i个叶子结点的权为Wi,则其中带权路径长度WPL最小的二叉树称作最优二叉树或哈夫曼树。
2.哈夫曼树的目的:找出存放一串字符所需的最少的二进制编码
3.哈夫曼树的建立:Huffman为了减少通信系统中字符编码所需要的二进制位长度,提出了用于产生不定长的前缀编码算法,所谓前缀编码是指任意一个编码都不是其它编码的前缀。前缀编码的思想就是对于出现概率较大的字符采用短编码方式,对于出现概率比较小的字符采用长编码方式。下面是一个具体实例。
在构造完成后,将二叉树向左的分支加上0,向右的分支加上1然后就可以得出每一个叶子节点的哈夫曼编码。
4.算法实现:
思路:首先将数据构造成最小堆。堆顶就是最小的元素,然后将堆顶的元素和堆里面的最后一个元素交换,将堆的最后一个元素也就是堆里面最小的元素删除并且返回给哈夫曼树的一个结点作为这个结点的左子树。然后用剩余的元素再次构成最小堆,重复上面的步骤。不过这一次是将这个元素作为结点的右子树。然后将这个结点的左右孩子的权值相加作为结点的权值,再将结点插入堆中。重复进行,直到最后堆里面只有一个结点。
#include<iostream>
#include<vector>
#include<queue>
using namespace::std;
typedef struct HuffmanNode
{
int weight; //权值
struct HuffmanNode* lchild,*rchild;
};
struct MinHeap
{
HuffmanNode* Node;
int Size; //堆里面现在有的元素
int MaxSize; //堆里面可以容纳的最大元素
};
//交换两个结点
void Swap(MinHeap* H, int parent, int child)
{
HuffmanNode mid = H->Node[parent];
H->Node[parent] = H->Node[child];
H->Node[child] = mid;
}
//生成最小堆
void HeapSort(MinHeap* H)
{
int parent = H->Size / 2 - 1;
for (int i = parent; i >= 0; i--) //从有孩子节点的最后一个根节点开始
{
parent = i;
int child = 2 * parent + 1;
while (child < H->Size) //将这个根节点变成一个最小堆
{
if (child+1<H->Size&&H->Node[child].weight > H->Node[child + 1].weight) //如果右孩子的值比左孩子小,child则指向右孩子
{
child++;
}
if (H->Node[parent].weight > H->Node[child].weight) //如果根节点的值小于左右节点中最小的那一个,则与小的那一个交换
{
Swap(H, parent, child); //交换两个结点的值
parent = child;
}
child = child * 2 + 1;
}
}
}
//创建并且初始化堆
MinHeap* CreatHeap(int a[], int len)
{
MinHeap* H = new MinHeap;
H->Node = new HuffmanNode[len];
H->MaxSize = len;
H->Size = 0;
//初始化堆,将堆的每一个节点都初始化为Huffman结构
for(int i = 0; i < len; i++)
{
H->Node[i].weight = a[i];
H->Node[i].lchild = NULL;
H->Node[i].rchild = NULL;
H->Size++;
}
return H;
}
//删除最小的那个根节点并返回
HuffmanNode* DeleteHeap(MinHeap* H)
{
HuffmanNode* res = new HuffmanNode;
res->lchild = H->Node[0].lchild;
res->rchild = H->Node[0].rchild;
res->weight = H->Node[0].weight;
Swap(H, 0, H->Size - 1); //将堆里面的第一个元素和最后一个元素互换
H->Size--; //删除最后一个元素
HeapSort(H); //删除结点后,重新构成最小堆
return res;
}
void InsertHeap(MinHeap* H, HuffmanNode* T)
{
H->Size++;
H->Node[H->Size - 1].weight = T->weight;
H->Node[H->Size - 1].lchild = T->lchild;
H->Node[H->Size - 1].rchild = T->rchild;
HeapSort(H); //构成最小堆
}
//先序遍历
void PreOrder(HuffmanNode* t)
{
if (t)
{
cout << t->weight<<" "; //输出结点数据
PreOrder(t->lchild);//访问左孩子
PreOrder(t->rchild);//访问右孩子
}
}
int main()
{
int a[10] = { 10,9,8,7,6,5,4,3,2,1 };
int len = sizeof(a) / sizeof(int); //得到a的长度
cout << "初始的序列为" << endl;
for (int i = 0; i < len; i++)
{
cout << a[i] << " ";
}
cout << endl;
MinHeap* H = CreatHeap(a, len);
HeapSort(H);
cout << "最小堆的数据:" << endl;
for (int i = 0; i < len; i++)
cout << H->Node[i].weight << " ";
cout << endl;
while (H->Size > 1) //至少有两个结点
{
HuffmanNode* T = new HuffmanNode;
T->lchild = DeleteHeap(H); //删除最小堆里面那个权值最小的结点并返回删除的结点
T->rchild = DeleteHeap(H);
T->weight = T->lchild->weight + T->rchild->weight;
InsertHeap(H, T);
}
HuffmanNode* Huffman = H->Node;
cout << "哈夫曼树的序列是" << endl;
//先序遍历
PreOrder(Huffman);
}