哈夫曼编码
应用
哈夫曼树─即最优二叉树,带权路径长度最小的二叉树,经常应用于数据压缩
实现思路
采用二叉树建立哈夫曼树
首先,队列中存放所哈夫曼树节点
- 创建一个哈夫曼树节点p
- p的左孩子为队列权值最小的节点a,然后a出队
- p的右孩子为队列权值最小的节点b,然后b出队
- p入队
- 重复1、2、3、4,直到队列中只剩下一个节点,这个节点为哈夫曼树的root
huffman.h
#include <iostream>
#include <list>
#include <stdio.h>
#include <string>
#include <map>
#define HfmElemType char
using namespace std;
//哈夫曼树节点
typedef struct HfmLeafNode
{
HfmElemType Symbol;
int Weight;
HfmLeafNode *lchild, *rchild;
} HfmLeafNode, *HfmTree;
//构建哈夫曼树中,取权值频率最小的排序
bool Order(HfmLeafNode d1, HfmLeafNode d2)
{
//排序
return d1.Weight < d2.Weight;
}
//初始化节点数据
HfmTree InitNode(HfmLeafNode &Node);
//构建哈夫曼树
HfmTree CreateHuffman(list<HfmLeafNode> &List);
//构建哈夫曼编码表
bool CreateHfmTable(map<int, string> &M);
//编码
bool Encode(HfmTree T, int index, string code, map<int, string> &M);
//解码
bool Decode(HfmTree T, string code);
//计算带权路径长度
void GetWPL(map<int, string> m);
huffman.cpp
#include "huffman.h"
//初始化节点
HfmTree InitNode(HfmLeafNode &Node)
{
Node.lchild = Node.rchild = NULL;
Node.Symbol = '#';
Node.Weight = 0;
return &Node;
}
void Copy(HfmTree T, HfmLeafNode node)
{
T->Symbol = node.Symbol;
T->Weight = node.Weight;
T->lchild = node.lchild;
T->rchild = node.rchild;
}
//构建哈夫曼树
HfmTree CreateHuffman(list<HfmLeafNode> &List)
{
List.sort(Order);
while (List.size() != 1)
{
HfmTree T = new HfmLeafNode;
HfmTree Left = new HfmLeafNode;
HfmTree Right = new HfmLeafNode;
T = InitNode(*T);
//复制节点数据,因为List删除节点会清空数据
Copy(Left, List.front());
//权值最小成为左孩子
T->lchild = Left;
T->Weight = Left->Weight;
//出队
List.pop_front();
//权值第二小成为右孩子
Copy(Right, List.front());
T->rchild = Right;
T->Weight += List.front().Weight;
//出队
List.pop_front();
//形成新的节点入队
List.push_front(*T);
//再根据权值大小排序
List.sort(Order);
}
return &List.front();
}
//构建哈夫曼编码表
bool CreateHfmTable(int weight, string code, map<int, string> &M)
{
M.insert(pair<int, string>(weight, code));
return true;
}
//解码
bool Decode(HfmTree T, string code)
{
printf("解码结果:");
HfmTree temp = T;
for (int i = 0; i < code.length(); i++)
{
int c = (int)code[i] - 48; //字符转数字
if (c < 0 || c > 1)
{
printf("编码错误!\n");
}
if (c == 0 && temp->lchild != NULL)
{
temp = temp->lchild;
}
else if (c == 1 && temp->rchild != NULL)
{
temp = temp->rchild;
}
if (temp->Symbol != '#')
{
cout << temp->Symbol;
temp = T;//重新遍历哈夫曼树
}
}
cout << endl;
return true;
}
//编码,从root自顶向下递归遍历
bool Encode(HfmTree T, int index, string code, map<int, string> &M)
{
if (T->lchild == NULL && T->rchild == NULL)
{
code = code.substr(0, code.find("-")); //字符串切片0到-
cout << T->Symbol << ":" << code << endl;
CreateHfmTable(T->Weight, code, M);
}
if (T->lchild != NULL)
{
code[index] = '0';
Encode(T->lchild, index + 1, code, M);
}
if (T->rchild != NULL)
{
code[index] = '1';
Encode(T->rchild, index + 1, code, M);
}
return true;
}
//获取WPL
void GetWPL(map<int, string> m)
{
int wpl = 0;
for (map<int, string>::iterator it = m.begin(); it != m.end(); it++)
{
cout << "weight=" << it->first << " code=" << it->second << endl;
wpl += it->first * it->second.length();
}
cout << "WPL:" << wpl << endl;
}
int main()
{
// a 45 c 12 b 13 f 5 e 9 d 16
// f 25 e 18 d 9 a 2 b 3 c 7
//节点队列
list<HfmLeafNode> L;
//哈夫曼编码表
map<int, string> M;
for (int i = 0; i < 6; i++)
{
HfmLeafNode h;
cin >> h.Symbol >> h.Weight;
h.lchild = h.rchild = NULL;
L.push_back(h);
}
HfmTree T = CreateHuffman(L);
printf("编码结果:\n");
string code = "-------";
Encode(T, 0, code, M);
string decode = "0101100111110111000101111";
Decode(T, decode);
GetWPL(M);
return 0;
}