用Huffman编码方法,实现对通信字符的编码和解码(C++)

算法思想

本算法的关键是构建最优二叉树(即哈夫曼树),接着对最优二叉树的叶子结点进行编码即可,接着就可以输入二进制数进行译码。

  1. 构建最优二叉树:将通信字符的结点初始化放入二叉树集,每个结点初始是一颗单节点的二叉树,每次取出两个权值最小的二叉树合并成一棵树,删除原始的两二叉树,重复该过程直至二叉树集中仅剩一棵树,这棵树即所求的哈弗曼树。
  2. 编码:从哈夫曼树的根结点出发,初始化空字符集,向左孩子移动则向字符集存入‘0’,向右孩子移动则存入‘1’,若遍历到叶子结点,则将字符集中的编码写入叶子结点即可。
  3. 译码:从根结点出发,按字符‘0’或‘1’确定找左孩子或右孩子,直至叶子结点,至叶子结点则译出一个字符,重新从根开始,重复上述过程。

代码实现(C++)

#include 
#include 
#include 

using namespace std;

typedef struct BiTreeNode_ {
	int weight;	//权值 
	char ch;	//通信字符
	vector<char> ch_code;	//存放最终编码 
	struct BiTreeNode_ *lchild, *rchild;
}BiTreeNode;

//初始化编码字符及权值,放入向量中,用于生成哈弗曼树 
void init_correspond_ch(vector<BiTreeNode*> &q, int n)
{
	for (int i = 1; i <= n; i ++ )
	{
		BiTreeNode* temp = new BiTreeNode;
		temp->lchild = NULL;
		temp->rchild = NULL;	//初始化新节点的左右孩子指针 
		
		cout << "第" << i << "个通信字符及出现频率(权值): ";
		cin >> temp->ch >> temp->weight;
		q.push_back(temp);	//输入到存放通信字符结点的向量中 
	}
}

//快排函数,根据结点权值降序排序 
void quick_sort(vector<BiTreeNode*> &q, int l, int r)
{
	if (l >= r) return;
	int i = l - 1, j = r + 1, key = q[(l + r) / 2]->weight;	//根据权值进行排序
	while (i < j)
	{
		do i ++ ; while (q[i]->weight > key);
		do j -- ; while (q[j]->weight < key);
		if (i < j) 
		{
			BiTreeNode* temp = q[j];
			q[j] = q[i];
			q[i] = temp;	
		}
	}//根据权值进行排序 
	quick_sort(q, l, j), quick_sort(q, j + 1, r);	//对左右两部分继续排序 
} 

void creat_huffman_tree(vector<BiTreeNode*> &q)
{
	//将权值最小的两个结点合并成一棵树,自下而上构建Huffman树 
	while (q.size() > 1)
	{
		int size = q.size();
		quick_sort(q, 0, size - 1);
		BiTreeNode* temp = new BiTreeNode;
		//两棵树合并成一棵树
		temp->weight = q[size - 1]->weight + q[size - 2]->weight;	
		temp->lchild = q[size - 1];	//新树的左孩子是权值小的树 
		temp->rchild = q[size - 2];	//新树的右孩子是权值大的树 
		temp->ch = '#';	//做个标记,方便调试 
		
		q.pop_back(); 	
		q.pop_back();
		q.push_back(temp); 	
	}	//跳出循环时,只剩一棵树,即所求最优二叉树	
} 

void pre_order(BiTreeNode *root)
{
	if (root && root->ch != '#') 
	{
		printf("字符:%c ", root->ch);
		printf("权值:%d ", root->weight);
		printf("编码: "); 
		for (int i = 0; i < root->ch_code.size(); i ++ )
		{
			printf("%c", root->ch_code[i]);
		}
		printf("\n");
	}
	if (root->lchild) pre_order(root->lchild);	
	if (root->rchild) pre_order(root->rchild);	
}

//哈夫曼编码,找到叶子结点,对其写入最终编码 
void huffman_code(BiTreeNode *root, vector<char> buffer)
{
	if (root->lchild)
	{
		buffer.push_back('0');
		huffman_code(root->lchild, buffer);
		buffer.pop_back();	//递归回来时,弹出该层压入的'0' 
	}
	if (root->rchild) 
	{
		buffer.push_back('1');
		huffman_code(root->rchild, buffer);
		buffer.pop_back();
	}
	if ( !(root->lchild) && !(root->rchild) )	//为叶子结点
	{
		root->ch_code = buffer;		 
	}
}

//哈夫曼译码,从根节点开始,走到叶子结点则解码一个字符 
void huffman_decode(BiTreeNode *T, BiTreeNode *root, string buffer)
{
	for (int i = 0; i < buffer.size(); i ++ )
	{
		if (root->lchild == NULL && root->rchild == NULL) 	//走到叶子结点,则译出一个通信字符 
		{
			printf("%c", root->ch);
			root =  T;
		}
		
		if (buffer[i] == '0') root = root->lchild; 
		if (buffer[i] == '1') root = root->rchild; 
		
		if (root->lchild == NULL && root->rchild == NULL) 	//走到叶子结点,则译出一个通信字符 
		{
			printf("%c", root->ch);
			root =  T;
		}
	}
}


int main()
{
	BiTreeNode* T;	
	vector<BiTreeNode*> q;
	int n;
	
	cout << "输入的通信字符的个数: "; 
	cin >> n;
	init_correspond_ch(q, n);
	
	creat_huffman_tree(q);
	T = q[0];
	
	vector<char> buf;
	buf.clear();
	huffman_code(T, buf);
	
	pre_order(T);
	
	string temp;
	cout << endl <<"输入待译码的二进制编码: ";
	cin >> temp;
	cout <<  "译码结果: ";
	huffman_decode(T, T, temp);
	
	 
	return 0;
}

END
—————————————————————————————————————
海盗船长一会儿想靠岸一会儿想扬帆妹妹你要上了贼船可别怪哥哥没说清自己是个混蛋
hh,歌名就是这么长~
—————————————————————————————————————

你可能感兴趣的:(数据结构)