題目描述
在通訊領域,經常需要將需要傳送的文字轉換成由二進制字符組成的字符串。在實際應用中,由於總是希望被傳送的內容總長儘可能的短,如果對每個字符設計長度不等的編碼,且讓內容中出現次數較多的字符采用儘可能短的編碼,則整個內容的總長便可以減少。另外,需要保證任何一個字符的編碼都不是另一個字符的編碼前綴,這種編碼成爲前綴編碼。
而赫夫曼編碼就是一種二進制前綴編碼,其從葉子到根(自底向上)逆向求出每個字符的算法可以表示如下:
在本題中,讀入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;
}
下面爲輸出結果: