哈夫曼编码与解码

这是我的第一篇博客,希望大神们批评指正。

首先介绍以下什么是哈夫曼树(来自百度百科)

哈夫曼树─即最优二叉树,带权路径长度最小的二叉树,经常应用于数据压缩。 在计算机信息处理中,“哈夫曼编码”是一种一致性编码法(又称“熵编码法”),用于数据的无损耗压缩。这一术语是指使用一张特殊的编码表将源字符(例如某文件中的一个符号)进行编码。这张编码表的特殊之处在于,它是根据每一个源字符出现的估算概率而建立起来的(出现概率高的字符使用较短的编码,反之出现概率低的则使用较长的编码,这便使编码之后的字符串的平均期望长度降低,从而达到无损压缩数据的目的)。

构造哈夫曼树的主要思想:

构造哈夫曼树非常简单,将所有的节点放到一个队列中,用一个节点替换两个频率最低的节点,新节点的频率就是这两个节点的频率之和。这样,新节点就是两个被替换节点的父节点了。如此循环,直到队列中只剩一个节点(树根)。


这里用到了最小优先队列。

我这里用STL来实现。(这里有优先队列的介绍)

template<typename T>
struct cmp
{
    bool operator()(TreeNode<T>* t1, TreeNode<T>*  t2)
    {
        return !(*t1 < *t2);
    }
};

优先队列的定义:

priority_queue<TreeNode*,vector<TreeNode* >,cmp > pri_que;

哈夫曼树节点结构

template<typename T>
class TreeNode
{
public:
    TreeNode():pfather(NULL),plchild(NULL),prchild(NULL)
    {

    }
    T data;
    TreeNode *pfather;
    TreeNode *plchild;
    TreeNode *prchild;

    bool operator < (const TreeNode& rhs)
    {
        return data < rhs.data;
    }

};

构造哈夫曼树

每次从最小优先队列取头两个节点,合并后放回最小优先队列,如此重复。

template<typename T>
TreeNode<T>* MakeHuffmanTree(T* begin, T* end) //构造哈夫曼树
{
    priority_queue<TreeNode<T>*,vector<TreeNode<T>* >,cmp<T> > pri_que;
    T *iter = begin;
    TreeNode<T>* pNode;
    TreeNode<T>* pf = NULL;
    while(iter != end)
    {
        pNode = new TreeNode<T>;
        pNode->data = *iter++;
        pNode->pfather = pf;
        pri_que.push(pNode);
    }
    TreeNode<T>* plchild;
    TreeNode<T>* prchild;
    while(pri_que.size() > 1)//取两个小的合并
    {
        plchild = pri_que.top();
        pri_que.pop();
        prchild = pri_que.top();
        pri_que.pop();

        pNode = new TreeNode<T>;
        pNode->plchild = plchild;
        pNode->prchild = prchild;
        pNode->data = plchild->data + prchild->data;

        pri_que.push(pNode);
    }

    pNode = pri_que.top();
    pri_que.pop();
    return pNode;
}


 构造哈夫曼树这个函数的参数是一个结构体,保存着对应字符,其频率,编码值。 

重载它的+运算符,为了合并时的+运算(只是频率相加)。

template<typename T>
struct mydata
{
    mydata(){}
    mydata(int i):freq(i)
    {

    }
    string coded;
    int freq;
    T data;

    bool operator<(const mydata& rhs)
    {
        return freq < rhs.freq;
    }

    mydata operator+(mydata& rhs)
    {
        return mydata(freq + rhs.freq);
    }
};
到此为止,已经可以把哈夫曼树生成出来了。

我们可以通过DFS将每个叶子节点的路径记录下来(用一个全局变量数组path),然后得到它的编码。

当发现当前节点是叶子节点,就把当前的路径赋值至该叶子节点的编码属性(coded)。

const int MAXLEN = 20;
char path[MAXLEN] = {0};
template<typename T>
void DFS(T* root,int deep = -1, char a = '-')  //DFS 得到叶子节点的编码
{
    if(root == NULL)
        return;

    if(a != '-')
        path[deep] = a;

    if(root->plchild == NULL || root->prchild == NULL)//leaf
        (root->data).coded = string(path,path + deep + 1);

    if(root->plchild != NULL)
        DFS(root->plchild, deep + 1, '0');
    if(root->prchild != NULL)
        DFS(root->prchild, deep + 1, '1');
}
这样整个哈夫曼编码工作已经完成,为了查看我们的编码结果,我们可以用BFS跟DFS来看到我们的结果。在这里我选择了BFS。

当遍历到叶子节点,就将其编码属性(coded)和其对应字符输出。

template<typename T,typename U>
void BFS(T* root, mydata<U>* data) //BFS 将叶子节点的编码,提到data指向的数据
{
    queue<T*> que;
    que.push(root);

    T* pT = NULL;
    while(!que.empty())
    {
        pT = que.front();
        //cout<<pT->data.freq<<endl;
        que.pop();

        if(pT->plchild != NULL)
            que.push(pT->plchild);
        if(pT->prchild != NULL)
            que.push(pT->prchild);
        if(pT->plchild == NULL || pT->prchild == NULL)// leaf 提取叶子节点的编码
        {
			//cout<<(pT->data).data<<":"<<(pT->data).coded<<endl;
			mydata<U>* pd = data;
			while((pT->data).data != pd->data)
				pd++;
			assert(pd->data == (pT->data).data);
			pd->coded = (pT->data).coded;
		}
    }
}


测试驱动代码
    mydata<char> *pdata = new mydata<char>[4];
    pdata[0].data = 'a';
    pdata[0].freq = 7;
    pdata[1].data = 'b';
    pdata[1].freq = 5;
    pdata[2].data = 'c';
    pdata[2].freq = 2;
    pdata[3].data = 'd';
    pdata[3].freq = 4;
    TreeNode<mydata<char> >* pihuffmanTree = MakeHuffmanTree(pdata, pdata + 4);

    DFS(pihuffmanTree);
    BFS(pihuffmanTree);

为了更方便的使用我将这些封装到一个类里面。

template<typename T>
class Huffman
{
public:
    void Coded(string& coded);//传入待输出的编码
    void DeCode(const string& codedstr,string& decodestr);//输入待解码字符串,输出解码字符串
    void InputData(T* begin,T* end);//传入数据

private:
    string FindVal(char c);
    void m_CalcFreq(T* begin, T* end);//计算输入数据的频率
    TreeNode<mydata<T> > *root;//huffman根节点
    mydata<T>* data;
    int data_size;
	
	T* m_begin;//保存原始数据的开始与结束的位置
	T* m_end;
	//string codedstr;

};

输入数据并计算其频率。

用map容器来统计输入字符每个出现的个数。

template<typename T>
void Huffman<T>::InputData(T* begin, T* end)
{
	this->m_begin = begin;
	this->m_end = end;
        m_CalcFreq(begin, end);

}

template<typename T>
void Huffman<T>::m_CalcFreq(T* begin, T* end)
{

    int len = end - begin;
    //data_size = len;
    if(len == 0)
        return;

    map<T,int> countMap;
    map<T,int>::iterator mapIter = countMap.begin();
    T *pT = begin;
    while(pT != end)
    {
        mapIter = countMap.find(*pT);
		if(mapIter != countMap.end())//在map里有没有字符*iter
			++mapIter->second;
		else
		{
			countMap.insert(make_pair(*pT,1));
		}
        pT++;
    }

	data_size = countMap.size();
	data = new mydata<T>[data_size];
	int i = 0;
	for (mapIter = countMap.begin(); mapIter != countMap.end(); ++mapIter)
	{
		data[i].data = mapIter->first;		
		data[i].freq = mapIter->second;
		i++;
	}

}
编码

template<typename T>
void Huffman<T>::Coded(string& coded)
{
	root = MakeHuffmanTree(data,data + data_size);
	DFS(root);

	BFS(root,data);

	cout<<"code:"<<endl;
	for (int i = 0; i < data_size; ++i)
	{
		cout<<data[i].data<<":"<<data[i].coded<<endl;
	}

	
	T *begin = m_begin;
	while (begin != m_end)
	{
		coded += FindVal(*begin);
		begin++;
	}
	//string subcode = 


}

解码

template<typename T>
void Huffman<T>::DeCode(const string& codedstr,string& decodestr)
{
	string::const_iterator iter = codedstr.begin();
	TreeNode<mydata<T> >* curNode = root;
	while (iter != codedstr.end())
	{
		if (curNode->plchild == NULL || curNode->prchild == NULL)
		{
			decodestr += (curNode->data).data;
			curNode = root;
			continue;
		}
		if (*iter == '0')
			curNode = curNode->plchild;
		
		if(*iter == '1')
			curNode = curNode->prchild;

		iter++;
	}
}

测试驱动程序

char *pmystr = "cbcddddbbbbaaaaaaa";

    Huffman<char> h;
    h.InputData(pmystr, pmystr + 18);

	cout<<"originstr: "<<pmystr<<endl;

	string coded;
	h.Coded(coded);

	cout<<"coded: "<<coded<<endl;

	string decode;
	h.DeCode(coded,decode);
	cout<<"decode: "<<decode<<endl;


完整程序(环境:VS2012)

#include <iostream>
//#include <algorithm>
#include <queue>
#include <string>
#include <vector>
#include <cassert>
#include <map>
using namespace std;


template<typename T>
class TreeNode
{
public:
    TreeNode():pfather(NULL),plchild(NULL),prchild(NULL)
    {


    }
    T data;
    TreeNode *pfather;
    TreeNode *plchild;
    TreeNode *prchild;


    bool operator < (const TreeNode& rhs)
    {
        return data < rhs.data;
    }


};


template<typename T>
struct cmp
{
    bool operator()(TreeNode<T>* t1, TreeNode<T>*  t2)
    {
        return !(*t1 < *t2);
    }
};




template<typename T>
TreeNode<T>* MakeHuffmanTree(T* begin, T* end) //构造哈夫曼树
{
    priority_queue<TreeNode<T>*,vector<TreeNode<T>* >,cmp<T> > pri_que;
    T *iter = begin;
    TreeNode<T>* pNode;
    TreeNode<T>* pf = NULL;
    while(iter != end)
    {
        pNode = new TreeNode<T>;
        pNode->data = *iter++;
        pNode->pfather = pf;
        pri_que.push(pNode);
    }
    TreeNode<T>* plchild;
    TreeNode<T>* prchild;
    while(pri_que.size() > 1)//取两个小的合并
    {
        //cout<<static_cast<TreeNode<T>* >(pri_que.top())->data<<endl;
        //pri_que.pop();
        plchild = pri_que.top();
        pri_que.pop();
        prchild = pri_que.top();
        pri_que.pop();


        pNode = new TreeNode<T>;
        pNode->plchild = plchild;
        pNode->prchild = prchild;
        pNode->data = plchild->data + prchild->data;


        pri_que.push(pNode);
    }


    pNode = pri_que.top();
    pri_que.pop();
    return pNode;
}


template<typename T>
struct mydata
{
    mydata(){}
    mydata(int i):freq(i)
    {


    }
    string coded;
    int freq;
    T data;


    bool operator<(const mydata& rhs)
    {
        return freq < rhs.freq;
    }


    mydata operator+(mydata& rhs)
    {
        return mydata(freq + rhs.freq);
    }
};


template<typename T,typename U>
void BFS(T* root, mydata<U>* data) //BFS 将叶子节点的编码,提到data指向的数据
{
    queue<T*> que;
    que.push(root);


    T* pT = NULL;
    while(!que.empty())
    {
        pT = que.front();
        //cout<<pT->data.freq<<endl;
        que.pop();


        if(pT->plchild != NULL)
            que.push(pT->plchild);
        if(pT->prchild != NULL)
            que.push(pT->prchild);
        if(pT->plchild == NULL || pT->prchild == NULL)// leaf 提取叶子节点的编码
        {
			//cout<<(pT->data).data<<":"<<(pT->data).coded<<endl;
			mydata<U>* pd = data;
			while((pT->data).data != pd->data)
				pd++;
			assert(pd->data == (pT->data).data);
			pd->coded = (pT->data).coded;
		}
    }
}


const int MAXLEN = 20;
char path[MAXLEN] = {0};
template<typename T>
void DFS(T* root,int deep = -1, char a = '-')  //DFS 得到叶子节点的编码
{
    if(root == NULL)
        return;


    if(a != '-')
        path[deep] = a;


    if(root->plchild == NULL || root->prchild == NULL)//leaf
        (root->data).coded = string(path,path + deep + 1);


    if(root->plchild != NULL)
        DFS(root->plchild, deep + 1, '0');
    if(root->prchild != NULL)
        DFS(root->prchild, deep + 1, '1');
}


template<typename T>
class Huffman
{
public:
    void Coded(string& coded);
    void DeCode(const string& codedstr,string& decodestr);
    void InputData(T* begin,T* end);


private:
	string FindVal(char c);
    void m_CalcFreq(T* begin, T* end);
    TreeNode<mydata<T> > *root;
    mydata<T>* data;
    int data_size;
	
	T* m_begin;
	T* m_end;
	//string codedstr;


};


template<typename T>
void Huffman<T>::InputData(T* begin, T* end)
{
	this->m_begin = begin;
	this->m_end = end;
    m_CalcFreq(begin, end);


}


template<typename T>
void Huffman<T>::m_CalcFreq(T* begin, T* end)
{


    int len = end - begin;
    //data_size = len;
    if(len == 0)
        return;


    map<T,int> countMap;
    map<T,int>::iterator mapIter = countMap.begin();
    T *pT = begin;
    while(pT != end)
    {
        mapIter = countMap.find(*pT);
		if(mapIter != countMap.end())
			++mapIter->second;
		else
		{
			countMap.insert(make_pair(*pT,1));
		}
        pT++;
    }


	data_size = countMap.size();
	data = new mydata<T>[data_size];
	int i = 0;
	for (mapIter = countMap.begin(); mapIter != countMap.end(); ++mapIter)
	{
		data[i].data = mapIter->first;		
		data[i].freq = mapIter->second;
		i++;
	}


}


template<typename T>
void Huffman<T>::Coded(string& coded)
{
	root = MakeHuffmanTree(data,data + data_size);
	DFS(root);


	BFS(root,data);


	cout<<"code:"<<endl;
	for (int i = 0; i < data_size; ++i)
	{
		cout<<data[i].data<<":"<<data[i].coded<<endl;
	}


	
	T *begin = m_begin;
	while (begin != m_end)
	{
		coded += FindVal(*begin);
		begin++;
	}
	//string subcode = 




}


template<typename T>
void Huffman<T>::DeCode(const string& codedstr,string& decodestr)
{
	string::const_iterator iter = codedstr.begin();
	TreeNode<mydata<T> >* curNode = root;
	while (iter != codedstr.end())
	{
		if (curNode->plchild == NULL || curNode->prchild == NULL)
		{
			decodestr += (curNode->data).data;
			curNode = root;
			continue;
		}
		if (*iter == '0')
			curNode = curNode->plchild;
		
		if(*iter == '1')
			curNode = curNode->prchild;


		iter++;
	}
}


template<typename T>
string Huffman<T>::FindVal(char c)
{
	for (int i = 0; i < data_size ; ++i)
	{
		if (c != data[i].data)
			continue;
		return data[i].coded;
	}
	return string();
}


int main()
{
    //mydata<char> *pdata = new mydata<char>[4];
    //pdata[0].data = 'a';
    //pdata[0].freq = 7;
    //pdata[1].data = 'b';
    //pdata[1].freq = 5;
    //pdata[2].data = 'c';
    //pdata[2].freq = 2;
    //pdata[3].data = 'd';
    //pdata[3].freq = 4;
    int a[12]={14,10,56,7,83,22,36,91,3,47,72,0};
    //TreeNode<mydata<char> >* pihuffmanTree = MakeHuffmanTree(pdata, pdata + 4);


    //DFS(pihuffmanTree);


    //BFS(pihuffmanTree);


    //string str = "cbcddddbbbbaaaaaaa";
	char *pmystr = "cbcddddbbbbaaaaaaa";


    Huffman<char> h;
    h.InputData(pmystr, pmystr + 18);


	cout<<"originstr: "<<pmystr<<endl;


	string coded;
	h.Coded(coded);


	cout<<"coded: "<<coded<<endl;


	string decode;
	h.DeCode(coded,decode);
	cout<<"decode: "<<decode<<endl;


    return 0;
}




  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,以下是哈夫曼编码解码C++实现代码: ```c++ #include <iostream> #include <queue> #include <unordered_map> using namespace std; // 定义哈夫曼树的节点结构体 struct TreeNode { char data; // 字符 int weight; // 权重 TreeNode* left; // 左子节点 TreeNode* right; // 右子节点 TreeNode(char d, int w): data(d), weight(w), left(nullptr), right(nullptr) {} }; // 定义哈夫曼树节点的比较函数 struct cmp { bool operator()(const TreeNode* a, const TreeNode* b) const { return a->weight > b->weight; } }; // 建立哈夫曼树 TreeNode* buildHuffmanTree(const string& text) { // 统计每个字符在字符串中出现的次数 unordered_map<char, int> charFreq; for (char c : text) { charFreq[c]++; } // 将每个字符的出现次数和字符本身存入优先队列中 priority_queue<TreeNode*, vector<TreeNode*>, cmp> pq; for (auto p : charFreq) { pq.push(new TreeNode(p.first, p.second)); } // 不断取出优先队列中的两个最小节点,合并成一个新节点,再将新节点放回优先队列中 while (pq.size() > 1) { TreeNode* left = pq.top(); pq.pop(); TreeNode* right = pq.top(); pq.pop(); TreeNode* parent = new TreeNode('\0', left->weight + right->weight); parent->left = left; parent->right = right; pq.push(parent); } // 最后的队列头即为整个哈夫曼树的根节点 return pq.top(); } // 哈夫曼编码 void encodeHuffman(TreeNode* root, string code, unordered_map<char, string>& huffmanCode) { if (!root) { return; } if (!root->left && !root->right) { huffmanCode[root->data] = code; return; } encodeHuffman(root->left, code + "0", huffmanCode); encodeHuffman(root->right, code + "1", huffmanCode); } // 哈夫曼解码 string decodeHuffman(TreeNode* root, string huffmanCode) { string decodeText = ""; TreeNode* cur = root; for (char c : huffmanCode) { if (c == '0') { cur = cur->left; } else { cur = cur->right; } if (!cur->left && !cur->right) { decodeText += cur->data; cur = root; } } return decodeText; } int main() { string text = "this is a test text"; TreeNode* root = buildHuffmanTree(text); unordered_map<char, string> huffmanCode; encodeHuffman(root, "", huffmanCode); for (auto p : huffmanCode) { cout << p.first << ": " << p.second << endl; } string huffmanCodeText = ""; for (char c : text) { huffmanCodeText += huffmanCode[c]; } cout << "Huffman code: " << huffmanCodeText << endl; string decodeText = decodeHuffman(root, huffmanCodeText); cout << "Decode text: " << decodeText << endl; return 0; } ``` 上述代码首先实现了哈夫曼树的建立,然后通过递归实现了哈夫曼编码解码。在编码时,对于每个字符,我们都可以通过哈夫曼树得到其对应的哈夫曼编码;在解码时,我们只需要从根节点开始,依次遍历哈夫曼树,直到遇到叶子节点,就可以得到该叶子节点所代表的字符。最终输出的结果是原始字符串的哈夫曼编码解码后的字符串。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值