算法筆記---自底向上的赫夫曼編碼

題目描述

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

在這裏插入圖片描述

在本題中,讀入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;
}

下面爲輸出結果:

自底向上運行結果

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章