赫夫曼编码主要用于数据文件的压缩,用满二叉树实现的,字符都位于树叶上。赫夫曼算法:在输入的字符结点中,选择出现频率最小的2个结点,合并两个结点,形成新的树。新树频率为2个结点的频率之和。在新结点和原始结点(除去合并的2个结点)再寻找出现频率最小的个结点合并。依次下去,直到所有结点都合并在一个树上,此时生成的树为赫夫曼编码树。
a(10) e(15) i(12) s(3) t(4) p(13) n(1) (待编码的字符信息,后面括号中是该字符出现的频率)
n(1) s(3) t(4) a(10) i(12) p(13) e(15) (程序中的insert,将其插入到list中,按出现的频率由小到大排序)
n(1) s(3), t(4) T1(4) a(10) i(12) p(13) e(15) (选取出现频率最小的2个结点n、s合并成新树T1,将T1按出现频率大小插入到list中,T1的左儿子为n,右儿子s)
n(1) s(3), t(4) T1(4), T2(8) a(10) i(12) p(13) e(15) (选取出现频率最小从t开始,T2左儿子为t,右儿子为T1)
n(1) s(3), t(4) T1(4), T2(8) a(10), i(12) p(13) e(15) T3(18) (选取出现频率最小从T2开始,T3左儿子T2,右儿子a)
n(1) s(3), t(4) T1(4), T2(8) a(10), i(12) p(13), e(15) T3(18) T4(25) (选取出现频率最小从i开始,T4左儿子i,右儿子p)
n(1) s(3), t(4) T1(4), T2(8) a(10), i(12) p(13), e(15) T3(18), T4(25) T5(33) (选取出现频率最小从e开始,T5左儿子e,右儿子T3)
n(1) s(3), t(4) T1(4), T2(8) a(10), i(12) p(13), e(15) T3(18), T4(25) T5(33), T6(58) (选取出现频率最小从T4开始,T6左儿子T4,右儿子T5)
完成赫夫曼树的生成,如下图:
编码采用向左为0 ,向右为1,各字符对应的赫夫曼编码如下:
a:111 ;e:10 ;i:00 ;s:11011 ;t:1100 ;p:01 ;n:11010 。
对于程序中的code()函数如何实现记录赫夫曼码,如n的赫夫曼码获得过程:在生成赫夫曼时,n是T5的后代,而T5是根节点T6的右儿子,根据编规则向右为1,所以T5所有后代(e,t,n,s)的赫夫曼码push 1,因为T5在list中位置为k=11。在编程中判断k奇数,则该处结点的所有后代赫夫曼码push 1,T3、T2、T1对n的赫夫曼码分别是1 ,0,1。所以n赫夫曼码为11010。其实树中所有向右的结点在list中的位置为奇数,向左为偶数。在程序中判断该结点在list 中的位置,决定对其儿子赫夫曼码贡献是0还是1
代码如下:
Huuffman.h
#pragma once
#include<iostream>
#include<list>
#include<vector>
using namespace std;
template<typename Comparable>
struct HuffNode
{
HuffNode *left;
HuffNode *right;
int num;//出现的频率
Comparable name;//编码的字符串
vector<int>cod;//该字符的编码
HuffNode(Comparable n,int nu,HuffNode *l,HuffNode *r):name(n),num(nu),left(l),right(r){}
HuffNode(int nu,HuffNode *l,HuffNode *r):num(nu),left(l),right(r){}
HuffNode(HuffNode *l,HuffNode *r):left(l),right(r){}
};
template<typename Comparable>
class Huffmancode
{
public:
void insert(Comparable name,int num);//将输入的字符插入vector中
void huffcode();//进行赫夫曼编码
void code(HuffNode<Comparable> *&t,int k);
void findcode(Comparable x);//查找某元素的赫夫曼码
void find(string c);//已知赫夫曼码查找其对应的元素
private:
list<HuffNode<Comparable> >m_huffman;//保存总的要编码的字符
void findc(HuffNode<Comparable> *t,Comparable x);//查找某元素的赫夫曼码
void findx(HuffNode<Comparable> *t,vector<int>co);
};
Huuffman.cpp
#include "stdafx.h"
#include"Huffman.h"
#include<iostream>
#include<list>
#include<vector>
using namespace std;
template<typename Comparable>
void Huffmancode<Comparable>::insert(Comparable name,int num)
{
HuffNode<Comparable> huffnode(name,num,NULL,NULL);
list<HuffNode<Comparable> >::iterator it;
if(m_huffman.size()==0)
{
m_huffman.push_back(huffnode);
}
else
{
for(it=m_huffman.begin();it!=m_huffman.end();++it)//将待编码的按照出现频率的大小排序,存储在list中
{
if(it->num>huffnode.num)
break;
}
m_huffman.insert(it,huffnode);
}
}
template<typename Comparable>
void Huffmancode<Comparable>::huffcode()//生成赫夫曼树
{
int k=0;
list<HuffNode<Comparable> >::iterator it;
list<HuffNode<Comparable> >::iterator t=m_huffman.begin();
list<HuffNode<Comparable> >::iterator t2=++t;
list<HuffNode<Comparable> >::iterator t1=m_huffman.begin();
while(m_huffman.size()-k>1)
{
HuffNode<Comparable> huffnode(t1->num+t2->num,&(*t1),&(*t2));
HuffNode<Comparable>* m1=&(*t1);
HuffNode<Comparable>* m2=&(*t2);
code(m1,k); //进行编码
code(m2,k+1); //进行编码
k=k+2;
for(it=m_huffman.begin();it!=m_huffman.end();++it)
{
if(it->num>huffnode.num)
{
break;
}
}
m_huffman.insert(it,huffnode);
++(++t1);
++(++t2);
}
}
template<typename Comparable>
void Huffmancode<Comparable>::code(HuffNode<Comparable> *&t,int k)//编码
{
if(k%2==0)
{
if(t!=NULL)
{
t->cod.push_back(0);
code(t->left,k);
code(t->right,k);
}
}
else
{
if(t!=NULL)
{
t->cod.push_back(1);
code(t->left,k);
code(t->right,k);
}
}
}
//查找某个元素的编码
template<typename Comparable>
void Huffmancode<Comparable>::findcode(Comparable x)
{
list<HuffNode<Comparable> >::iterator it=m_huffman.end();
--it;
HuffNode<Comparable> *t=&(*it); //获取赫夫曼树的根节点的地址,由前面huffcode()函数生成赫夫曼树可知,根节点是存储在list中的最后一个位置
findc(t,x);
cout<<endl;
}
template<typename Comparable>
void Huffmancode<Comparable>::findc(HuffNode<Comparable> *t,Comparable x)
{
if(t!=NULL)
{
if(t->name==x) //采用遍历树的方法,直到找到name为x的结点,然后把其编码输出
{
for(int i=t->cod.size()-1;i>=0;i--)
cout<<t->cod[i]<<" ";
}
findc(t->left,x);
findc(t->right,x);
}
}
//解码,已知某个赫夫曼码找到对应的元素
template<typename Comparable>
void Huffmancode<Comparable>::find(string c)
{
list<HuffNode<Comparable> >::iterator it=m_huffman.end();
--it;
HuffNode<Comparable> *t=&(*it); //获取赫夫曼树的根节点
vector<int>co;
int temp;
for(int i=c.size()-1;i>=0;i--)//将输入的string类型每一位上转换为int并放在vector中
{
temp=(int)c[i]-48; //1是char类型时,转换为int类型,值为49,需要减48
co.push_back(temp);
}
findx(t,co); //co中保存的是赫夫曼码
cout<<endl;
}
template<typename Comparable>
void Huffmancode<Comparable>::findx(HuffNode<Comparable> *t,vector<int>co)
{
if(t!=NULL)
{
if(t->cod==co) //遍历赫夫曼树,找到赫夫曼码为co的结点,输出对应的元素,完成解码工作
cout<<"name:"<<t->name<<endl;
findx(t->left,co);
findx(t->right,co);
}
}
Algorithm-greedy2.cpp
// Algorithm-greedy2.cpp : 定义控制台应用程序的入口点。
//
//主要实现赫夫曼编码
#include "stdafx.h"
#include"Huffman.h"
#include"Huffman.cpp"
#include<iostream>
#include<string>
#include<list>
#include<vector>
using namespace std;
int _tmain(int argc, _TCHAR* argv[])
{
Huffmancode<string> c;
c.insert("a",10);
c.insert("e",15);
c.insert("i",12);;
c.insert("s",3);
c.insert("t",4);
c.insert("p",13);
c.insert("n",1);
c.huffcode();
c.findcode("a");//查找字符为a的赫夫曼码
c.find("11010");//查找赫夫曼码对应的字符
return 0;
}