题目描述
在通讯领域,经常需要将需要传送的文字转换成由二进制字符组成的字符串。在实际应用中,由于总是希望被传送的内容总长尽可能的短,如果对每个字符设计长度不等的编码,且让内容中出现次数较多的字符采用尽可能短的编码,则整个内容的总长便可以减少。另外,需要保证任何一个字符的编码都不是另一个字符的编码前缀,这种编码成为前缀编码。
而赫夫曼编码就是一种二进制前缀编码,其从叶子到根(自底向上)逆向求出每个字符的算法可以表示如下:
在本题中,读入n个字符所对应的权值,生成赫夫曼编码,并依次输出计算出的每一个赫夫曼编码。
输入:
输入的第一行包含一个正整数n,表示共有n个字符需要编码。其中n不超过100。
第二行中有n个用空格隔开的正整数,分别表示n个字符的权值。
输出:
共n行,每行一个字符串,表示对应字符的赫夫曼编码。
样例:
输入:
8
5 29 7 8 14 23 3 11
输出:
0110
10
1110
1111
110
00
0111
010
解题思路:
1、结构体中包含权值、左子树、右子树、父结点
2、初始化时,所有结点的父节点设置为0,权值按照输入的权值进行赋值
3、编写获取集合中最小的两个元素的下标。
4、构造哈夫曼树
5、得出哈夫曼编码
6、打印输出
注意:
该题与 算法笔记—自顶向下的赫夫曼编码 区别不同在于编码的方式不同
此外,该题使用vector数据结果来存放哈夫曼编码,数组下标是从 0 开始。
当然读者可以设置为从 1 开始,这样更加方便。例如在编写 算法笔记—自顶向下的赫夫曼编码 的代码中,数组的下标博主就是从 1 开始的,这样便于书写构造哈夫曼编码的代码。
下面为AC代码:
#include<iostream>
#include<algorithm>
#include<vector>
#include<string>
#include<limits.h> //INT_MAX的头文件,不包含编译可能不会通过
using namespace std;
struct huffman_node {
int weight;//权值
int lchild, rchild;//左右子树
int parent;//父结点
};
//************************************
// Method: select_tow_min 找出集合中两个最小的值
// FullName: select_tow_min
// Access: public
// Returns: void
// Qualifier:
// Parameter: vector<huffman_node> & huffman_tree 存放哈夫曼树的集合
// Parameter: int n 当前哈夫曼树共有多少结点
// Parameter: int & first_min 第一个最小结点
// Parameter: int & second_min 第二个最小结点
//************************************
void select_tow_min(vector<huffman_node>& huffman_tree, int n, int &first_min, int &second_min)
{
int min_value = INT_MAX;
for (int i = 0;i < n;i++)//找出第一个最小结点
{
if (huffman_tree[i].parent == 0 && huffman_tree[i].weight < min_value)
{
min_value = huffman_tree[i].weight;
first_min = i;
}
}
min_value = INT_MAX;
for (int i = 0;i < n;i++)//找出第二个最小结点
{
if (huffman_tree[i].parent == 0 && huffman_tree[i].weight < min_value && i != first_min)
{
min_value = huffman_tree[i].weight;
second_min = i;
}
}
if (first_min > second_min)
{
swap(first_min, second_min);//保证编码唯一。因为给定题目是按照数字下标来区分左右子树的。
}
}
//************************************
// Method: huffman_solve 求出哈夫曼编码
// FullName: huffman_solve
// Access: public
// Returns: void
// Qualifier:
// Parameter: vector<huffman_node> & huffman_tree 存放哈夫曼树的结点
// Parameter: vector<string> & code 存放哈夫曼编码
// Parameter: vector<int> & weight 各个输入结点的权值
// Parameter: int n 输入结点的总个数
//************************************
void huffman_solve(vector<huffman_node> & huffman_tree, vector<string>& code, vector<int> &weight, int n) {
if (n <= 1)
{
return;
}
int m = n * 2 - 1;//哈夫曼树的所有结点数目
huffman_tree.resize(m);
//初始化
for (int i = 0;i < n;i++)
{
huffman_tree[i].weight = weight[i];
huffman_tree[i].parent = huffman_tree[i].lchild = huffman_tree[i].rchild = 0;
}
for (int i = n;i < m;i++)
{
huffman_tree[i].parent = huffman_tree[i].lchild = huffman_tree[i].rchild = 0;
}
//构造哈夫曼树
for (int i = n;i < m;i++)
{
int first_min, second_min;
select_tow_min(huffman_tree, i, first_min, second_min);//找出两个最小值
huffman_tree[i].lchild = first_min;//左子树
huffman_tree[i].rchild = second_min;//右子树
huffman_tree[first_min].parent = huffman_tree[second_min].parent = i;//父结点
huffman_tree[i].weight = huffman_tree[first_min].weight + huffman_tree[second_min].weight;//父结点的 weight
}
//求出每个叶节点的编码
for (int i = 0; i < n; i++) {
string str = "";
int child = i;//当前结点为孩子结点
int parent = huffman_tree[i].parent;//当前结点的父节点
do
{
if (huffman_tree[parent].lchild == child)
{
str = '0' + str;
}
else
{
str = '1' + str;
}
child = parent;
parent = huffman_tree[child].parent;
} while (parent != 0);//自底向上求出哈夫曼编码
code.push_back(str);
}
}
int main() {
int n;
cin >> n;
vector<int> v;
int data;
for (int i = 0;i < n; i++)
{
cin >> data;
v.push_back(data);
}
vector<huffman_node> huffman_tree;//哈夫曼树的结点集合
vector<string> code;//哈夫曼编码
huffman_solve(huffman_tree, code, v, n);
for (vector<string>::iterator it = code.begin(); it != code.end(); it++) {
cout << (*it) << endl;
}
//system("pause");
return 0;
}
下面为输出结果: