目录
哈夫曼树的建立有两种方式,一种是通过静态数组的方式来建立(这种方式比较简洁明了好理解),由于不想篇幅太长了,我还是po出一篇大佬的优秀文章来解释建立哈夫曼树吧
哈夫曼树静态数组形式建立
哈夫曼树二叉链表形式建立
方法:
(1)由原始数据生成森林;
(2) 在森林中选取两棵根结点权值最小的和次小的二叉树作为左右子树构造一棵新的二叉树,其根结点的权值为左右子树根结点权值之和。规定左子树根结点的权值小于右子树根结点的权值。 (3)将新的二叉树加入到森林F中,去除原两棵权值最小的树;
(4)重复2、3步骤,直至F中只剩一棵树为止。 注意:参看书中P146的例子。
类定义(要用到最小堆)
#include "heap.h"
const int DefaultSize = 20; //缺省权值集合大小
template <class T, class E>
struct HuffmanNode { //树结点的类定义
E data; //结点数据
HuffmanNode<T, E> *parent;
HuffmanNode<T, E> *leftChild, *rightChild;
//左、右子女和父结点指针
HuffmanNode () : Parent(NULL), leftChild(NULL),
rightChild(NULL) { } //构造函数
HuffmanNode (E elem, //构造函数
HuffmanNode<T, E> *pr = NULL,
HuffmanNode<T, E> *left = NULL,
HuffmanNode<T, E> *right = NULL)
: data (elem), parent (pr), leftChild (left),
rightChild (right) { }
};
template <class T, class E>
class HuffmanTree { //Huffman树类定义
public:
HuffmanTree (E w[], int n); //构造函数
~HuffmanTree() { delete Tree(root);} //析构函数
protected:
HuffmanNode<T, E> *root; //树的根
void deleteTree (HuffmanNode<T, E> *t); //删除以 t 为根的子树
void mergeTree (HuffmanNode<T, E>& ht1, HuffmanNode<T, E>& ht2,
HuffmanNode<T, E> *& parent);
};
实现算法
template <class T, class E>
HuffmanTree<T, E>::HuffmanTree (E w[], int n) {
//给出 n 个权值w[1]~w[n], 构造Huffman树
minHeap<T, E> hp; //使用最小堆存放森林
HuffmanNode<T, E> *parent, &first, &second;
HuffmanNode<T, E> *NodeList =new HuffmanNode<T,E>[n]; //森林
for (int i = 0; i < n; i++) {
NodeList[i].data = w[i+1];
NodeList[i].leftChild = NULL;
NodeList[i].rightChild = NULL;
NodeList[i].parent = NULL;
hp.Insert(NodeList[i]); //插入最小堆中
}
for (i = 0; i < n-1; i++) { //n-1趟, 建Huffman树
hp.Remove (first); //根权值最小的树
hp.Remove (second); //根权值次小的树
merge (first, second, parent); //合并
hp.Insert (*parent); //重新插入堆中
root = parent; //建立根结点
}
};
template <class T, class E>
void HuffmanTree<T, E>::
mergeTree (HuffmanNode<T, E>& bt1, HuffmanNode<T, E>& bt2,
HuffmanNode<T, E> *& parent) {
parent = new E;
parent->leftChild = &bt1;
parent->rightChild = &bt2;
parent->data.key =bt1.root->data.key+bt2.root->data.key;
bt1.root->parent = bt2.root->parent = parent;
};
求WPL
思路非常棒
int WPLrec(LBTree* lbt,int n)
{
int wpl = 0;
if (lbt != NULL)
{
if (lbt->lchild == NULL && lbt->rchild == NULL)
wpl += n * lbt->weigth;
wpl += WPLrec(lbt->lchild, n + 1);
wpl += WPLrec(lbt->rchild, n + 1);
}
return wpl;
}
递归算法好理解
下面的例子中深度要初始化为0;
哈夫曼树编码解码
编码:
里面的字母的权值代表字母出现的次数;
方法:
(1)用{ 2,4, 2,3, 3 }作为叶子结点的权值生成一棵哈夫曼树,并将对应权值wi的叶子结点注明对应的字符;
(2)约定左分支表示字符“0”,右分支表示字符‘1’
(3)从叶子结点开始,顺着双亲反推上去,直到根结点,路径上的‘0’或‘1’连接的序列就是结点对应的字符的二进制编码的逆序。
哈夫曼树的建立、编码以及WPL值的计算_SSPUmyl的博客-CSDN博客_哈夫曼树wpl假设用于通信的电文由字符集{A,B,C,D,E,F}中的字母构成,这些字母在电文中出现的概率分别为{0.10,0.19,0.20,0.35,0.12,0.04},要求: 1、构造一棵Huffman树,填写下表,要求左结点的权不大于右结点的权 2、在下表中填入各字符的Huffman编码(左分支为”0”,右分支为”1”) 3、求带权路径长度 解析: 1、哈夫曼树的构造 将电文概率由大到https://blog.csdn.net/SSPUmyl/article/details/53467604?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522163505694316780357218848%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=163505694316780357218848&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduend~default-2-53467604.first_rank_v2_pc_rank_v29&utm_term=wpl&spm=1018.2226.3001.4187数据结构(15)--哈夫曼树以及哈夫曼编码的实现_小白的专栏-CSDN博客_博夫曼
更新
太难顶了上面
我这边弄了个初始化Huffman树的简洁代码,没有那么多接口的
初始化
//w[]为每个字符的权重,c[]为每个字符的内容,n为字符的个数,静态实现
struct huffmannode
{
float weigth;//每个结点的权值
char s; //每个结点的数据
int right, left, parent;
int code[20];//哈夫曼编码
int codesize;//每个节点的哈夫曼编码长度
};
huffmanTree::huffmanTree(float w[], char c[], int n) {
huffmannode *x;
x = new huffmannode[100]; //长度自定
int countcode = n;
int elemsize = countcode * 2 - 1;
for (int i = 0; i < elemsize; i++) //初始化
{
x[i].s = c[i];
x[i].weigth = w[i];
x[i].left = x[i].right = x[i].parent = -1;
}
int minfirst, minsecond;
int postag1, postag2;
for (int i = 0; i < countcode - 1; i++) {
minfirst = minsecond = 1000000; //初始为最大
postag1 = postag2 = 0;
for (int j = 0; j < countcode + i; j++)
{
if (x[j].weigth < minfirst && x[j].parent == -1)
{
minsecond = minfirst; //最小保存到次小
postag2 = postag1;
minfirst = x[j].weigth;
postag1 = j;
}
else if (x[j].weigth < minsecond && x[j].parent == -1)
{
minsecond = x[j].weigth;
postag2 = j;
}
}
x[postag1].parent = countcode + i;
x[postag2].parent = countcode + i;
x[countcode + i].weigth = x[postag1].weigth + x[postag2].weigth;
x[countcode + i].left = postag1;
x[countcode + i].right = postag2;
x[countcode + i].parent = -1;
}
}
编码
//countcode为字符个数,x为Huffman树,利用栈来实现编码解码
void huffmanTree::encode(int countcode,huffmanTree *x) {
stack<int> s;
int j, count;
for (int i = 0; i < countcode; i++)
{
j = i;
while (x[j].parent != -1)
{
if (x[x[j].parent].left== j) { s.push(0); }
else if (x[x[j].parent].right == j) { s.push(1); }
j = x[j].parent;
}
int temp = 0;
while (!s.empty())
{
x[i].code[temp++] = s.top();
s.pop();
}
x[i].codesize = temp;
}
cout << "编码成功!" << endl;
}
解码
//elemsize是静态数组哈夫曼树中开辟的空间,即:空间大小=节点个数*2-1
void huffmanTree::decode(int elemsize) {
int w = elemsize - 1;
char co;
while (cin>>co) {
co=in.get(); //读co的‘0’,‘1’字符
if (co == '0') {
w = x[w].left;
}
else if (co == '1') {
w = x[w].right;
}
if (x[w].left == -1 && x[w].right == -1) {
cout << x[w].s;
w = elemsize - 1;
}
}
cout << "译码成功!" << endl;
}