哈弗曼编码及译码

路径长度:从树的一个结点到另一个结点之间边的条数。

树的路径长度:从树根到每个叶子结点之间路径长度之和。

带权树的路径长度:每个叶子结点带有权值,树根到叶子结点的路径长度乘以该叶子结点的权值之和。

哈弗曼树:带权树的路径长度最小的树,又称作最小二叉树和最优二叉树。

哈夫曼树的构造过程:                                                                                                                                                                                 

1.      根据给定的n个带权的结点,构成含有n棵二叉树(每个结点是一棵树)的集合,该树的左右子树均为空。

2.      从含有n棵子树集合中找出两棵权值最小的树最为左右子树构成一个新的二叉树。

3.      从集合中删除这两棵权值最小的二叉树,将新的二叉树插入集合中。

4.      重复2和3步骤,知道最终只剩下一颗树为止,这棵树就是哈弗曼树。

哈弗曼树的应用:最优判定,哈弗曼编码。

前缀编码:字符设计长短不等的编码,且任一字符的编码都不是另一字符编码的前缀(比如0就是00的前缀)。

哈弗曼编码:一种二进制前缀编码。一棵二叉树左分支表示字符“0”,右分支表示字符“1”,从根结点到叶子结点的路径上分支字符组成的字符串作为该叶子结点的编码。求哈弗曼编码就是求一棵哈弗曼树的过程。

由于哈弗曼树中没有度为1的结点二叉树,则一棵有n个叶子的哈弗曼树共有2n-1个结点,可以存储在一个大小为2n-1的一维数组中。构成哈弗曼树后,编码是从叶子结点走出一条叶子结点到根的路径,而译码需从根结点出发走出一条从根到叶子的路径,这两个过程相反,左移编码或者译码时要注意调整二进制存储顺序。

一个字符串得到编码后,以二进制存储时需要额外存储这个字符串的字节数,因为在编码后的编码串转二进制时需要移位,没八个字节转为一个字节,若不满八个字节则需要用0补齐,所以在解码时为了去除掉补齐的0,需要原始字符的字节数,那么假如原始字符串大小为n,字符串编码后的二进制编码大小为m,那么实际二进制编码存储大小为m+1,压缩比就为(m+1)/n。

注意:哈弗曼编码在设计应用的时候需要注意的是编码端和译码端要有相同的字符频率表而且构造哈弗曼树的过程相同,否则没办法译码。

代码实现如下:

// 5.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <iostream>   
#include <string>      
#include <map> 
using namespace std;
typedef struct
{
	unsigned int weight;
	unsigned int parent,lchild,rchild;
}HTNode,*HuffmanTree;
typedef char* *HuffmanCode;

void SelectNode(HuffmanTree &HT,int n,int &Node1,int &Node2)
{
	int i,j;
	for ( i=1;i<=n;i++ )
	{
		if ( !HT[i].parent )
		{
			Node1 = i;
			break;
		}
	}
	for ( j=i+1;j<=n;j++ )
	{
		if ( !HT[j].parent )
		{
			Node2 = j;
			break;
		}
	}
	
	for ( i=1;i<=n;i++ )
		if ( !HT[i].parent && HT[i].weight<HT[Node1].weight && i!=Node2 )
			Node1 = i;
	for ( j=1;j<=n;j++ )
		if ( !HT[j].parent && HT[j].weight<HT[Node2].weight && j!=Node1 )
			Node2 = j;
	if ( HT[Node1].weight > HT[Node2].weight )
	{
		int tmp = Node1;
		Node1 = Node2;
		Node2 = tmp;
	}
		
}
void CreateHFTree(HuffmanTree &HT,int *weight,int n)
{
	if( n<=1 ) return;
	int m = 2*n-1;
	int *w = weight;
	HT = (HuffmanTree)malloc( (m+1)*sizeof(HTNode) );//0号单元未用
	int i = 1;
	for ( ;i<=n;i++ )
	{
		HT[i].weight = weight[i-1];
		HT[i].parent = 0;
		HT[i].lchild = 0;
		HT[i].rchild = 0;
	}
	for ( ;i<=m;i++  )
	{
		HT[i].weight = 0;
		HT[i].parent = 0;
		HT[i].lchild = 0;
		HT[i].rchild = 0;
		
	}
	for ( i=n+1;i<=m;i++ )
	{
		int Node1 = 1;
		int Node2 = 1;
		SelectNode(HT,i-1,Node1,Node2);
		HT[Node1].parent = i;
		HT[Node2].parent = i;
		HT[i].lchild = Node1;
		HT[i].rchild = Node2;
		HT[i].weight = HT[Node1].weight + HT[Node2].weight;
	}
}
void HuffmanEnCode(char ch[],HuffmanTree &HT,map<char,string> &HC,int n)
{
	//+++++++++++++++++++++++从叶子到根逆向求每个字符的哈弗曼编码+++++++++++++++++++++++
	for (int i=1;i<=n;i++ )
	{
		int c = i;
		int f = HT[i].parent;
		for ( ;f!=0;c=f,f=HT[f].parent  )
		{
			if ( HT[f].lchild == c )
				HC[ch[i-1]] = "0" + HC[ch[i-1]];
			else
				HC[ch[i-1]] = "1" + HC[ch[i-1]];
		}
		string s = HC[ch[i-1]];
	}
}
void HuffmanDecode(HuffmanTree &HT,int *w,char *code,int n,char*decode)
{
	int m = 2*n-1;
	int j = 0;
	while( *code )
	{
		int i=m;
		for ( ;HT[i].lchild!=0&&HT[i].rchild!=0; )
		{
			if ( *code=='0' )
				i = HT[i].lchild;
			else
				i = HT[i].rchild;
			++code;
		}
		decode[j++] = HT[i].weight;
	}
}

string B2S( const unsigned char szstr[] ) 
{
	string str;
	for ( int i=0;i<strlen((char*)szstr);i++ )
	{
		int tmp = 128;
		for ( int j=0;j<8;j++ )
		{
			str += (char)(bool)(szstr[i]&tmp) + '0';
			tmp>>=1;
		}
	}
	return str; 
}
unsigned char S2B(char str[])
{
	unsigned char tmp=0;
	for( int i=0;i<8;i++ )
		tmp = (tmp<<1) + (str[i]-'0'); 
	return tmp;
}
int main(int argc, char* argv[])
{
	//++++++++++++字符频率表++++++++++++/
	char chtable[] = {'a','b','c','d'} ;
	int  chfrequency[] ={10,20,30,40};
	int nTableSize = sizeof(chtable);
	//++++++++++++创建Huffman树+++++++++++/
	HuffmanTree HT = NULL;
	map<char,string> HC ;
	CreateHFTree(HT,chfrequency,nTableSize);
	//++++++++++++字符Huffman编码表++++++++++++/
	HuffmanEnCode(chtable,HT,HC,nTableSize);
	//++++++根据字符Huffman编码表对指定字符串进行编码++++++/
	char charSrc[] = "abcdabcdabcd";
	string sencode = "";
	unsigned char encode[255] = {0};
	char buf[9]={0};
	int cnt = 0;
	for ( int i=0;i<strlen(charSrc);i++ )
	{
		sencode += HC[charSrc[i]];
		for ( int j=0;j<HC[charSrc[i]].length();j++ )
		{
			buf[strlen(buf)] = HC[charSrc[i]][j];
			if ( 8 == strlen(buf) )
			{
				encode[cnt] = S2B(buf);
				cnt++;
				memset(buf,0,sizeof(buf));
			}
		}
	}
	if ( strlen(buf)>0 )
	{
		for( i=strlen(buf);i<8;i++ )
			buf[i] = '0';
		int nnnn = strlen(buf);
		encode[cnt] = S2B(buf);
		cnt++;
	}
	encode[cnt] = strlen(charSrc);
	//++++++根据字符Huffman编码表对指定二进制编码字符串进行解码++++++/
	char DecodeArr[255]={0};
	HuffmanDecode(HT,chfrequency,(char*)B2S(encode).c_str(),nTableSize,DecodeArr);
	string sdecode = "";
	int nLen = encode[strlen((char*)encode)-1];
	for ( i=0;i<strlen(DecodeArr);i++ )
	{
		for ( int j=0;j<nTableSize;j++ )
		{
			if ( DecodeArr[i] == chfrequency[j] )
			{
				sdecode += chtable[j];
			}
		}
		if ( sdecode.length() == nLen )
			break;
	}
	//++++++++++++++++++++输出+++++++++++++++++++++++++++++/
	printf("字符及对应的频率为:(a,10),(b,20),(c,30),(d,40)\n");
	printf("----------------------------------------------\n");
	printf("得到的字符Huffman编码表:\n");
	for (int z=0;z<nTableSize;z++)
	{
		printf("%c=%s\n",chtable[z],HC[chtable[z]].c_str());
	}
	printf("----------------------------------------------\n");
	printf("待编码字符串:%s\n",charSrc);
	printf("字符串编码后:%s,长度%d,\n实际存储长度为:%d,因为需要存储原始字符个数.\n",sencode.c_str(),cnt,cnt+1);
	printf("解码后为:%s\n",sdecode.c_str());
	printf("----------------------------------------------\n");
	printf("编码前字符串长度为:%d\n",strlen(charSrc));
	printf("编码后字符串长度为:%d\n",strlen((char*)encode));
	printf("编码后字符串压缩比为:%.2f%%\n",(double)strlen((char*)encode)/(double)strlen(charSrc)*100);
	getchar();
	return 0;
}

运行结果如下:


拓展及参考:http://blog.csdn.net/xadillax/article/details/6513928

                        数据结构(C语言版) 严蔚敏 吴为民

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是哈夫曼编码译码的C++代码示例: ```cpp #include <iostream> #include <queue> #include <map> #include <string> using namespace std; // 哈夫曼树节点结构体 struct HuffmanNode { char ch; // 字符 int freq; // 出现频率 HuffmanNode *left, *right; // 左右子节点 HuffmanNode(char c, int f) : ch(c), freq(f), left(NULL), right(NULL) {} // 重载小于号,用于优先队列排序 bool operator<(const HuffmanNode& n) const { return freq > n.freq; // 按出现频率从小到大排序 } }; // 构建哈夫曼树 HuffmanNode* buildHuffmanTree(const string& text) { // 统计字符出现频率 map<char, int> freqMap; for (char ch : text) { freqMap[ch]++; } // 构建优先队列,按出现频率从小到大排序 priority_queue<HuffmanNode> pq; for (auto it = freqMap.begin(); it != freqMap.end(); ++it) { pq.push(HuffmanNode(it->first, it->second)); } // 构建哈夫曼树 while (pq.size() > 1) { HuffmanNode* left = new HuffmanNode(pq.top().ch, pq.top().freq); pq.pop(); HuffmanNode* right = new HuffmanNode(pq.top().ch, pq.top().freq); pq.pop(); HuffmanNode* parent = new HuffmanNode('\0', left->freq + right->freq); parent->left = left; parent->right = right; pq.push(*parent); } return new HuffmanNode('\0', pq.top().freq); } // 递归编码 void encodeHelper(HuffmanNode* root, string code, map<char, string>& codeMap) { if (!root) { return; } if (root->left == NULL && root->right == NULL) { // 叶子节点 codeMap[root->ch] = code; } encodeHelper(root->left, code + "0", codeMap); encodeHelper(root->right, code + "1", codeMap); } // 哈夫曼编码 map<char, string> huffmanEncode(const string& text) { map<char, string> codeMap; HuffmanNode* root = buildHuffmanTree(text); encodeHelper(root, "", codeMap); return codeMap; } // 哈夫曼译码 string huffmanDecode(const string& text, HuffmanNode* root) { string result; HuffmanNode* p = root; for (char ch : text) { if (ch == '0') { p = p->left; } else { p = p->right; } if (p->left == NULL && p->right == NULL) { // 叶子节点 result += p->ch; p = root; // 重置为根节点 } } return result; } int main() { string text = "hello world"; cout << "原文:" << text << endl; // 哈夫曼编码 map<char, string> codeMap = huffmanEncode(text); cout << "编码表:" << endl; for (auto it = codeMap.begin(); it != codeMap.end(); ++it) { cout << it->first << ":" << it->second << endl; } string encodedText; for (char ch : text) { encodedText += codeMap[ch]; } cout << "编码结果:" << encodedText << endl; // 哈夫曼译码 HuffmanNode* root = buildHuffmanTree(text); string decodedText = huffmanDecode(encodedText, root); cout << "译码结果:" << decodedText << endl; // 释放内存 delete root; return 0; } ``` 输出结果: ``` 原文:hello world 编码表: :110 d:1110 e:10 h:0110 l:00 o:0111 r:1111 w:01101 编码结果:0110011000101110000010111110111011101110 译码结果:hello world ```

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值