貪心-哈夫曼編碼

哈夫曼編碼:

所謂編碼就是對於任意給定的文本,通過查閱編碼表逐一將其中的字符轉譯爲二進制編碼,這些編碼依次串接起來即得到了全文的編碼。
編碼方案確定後,儘管編碼結果必然確定,但解碼過程和結果卻不見得唯一。例如,字符a的編碼爲01,b的編碼爲011,c的編碼爲11,d的編碼爲101。當給定一串字符011101,結果可以編譯爲aca,也可以編譯爲bd。從而導致歧義。分析不難發現,出現歧義的原因是a的編碼是b編碼的前綴。
爲了避免歧義的出現,在制定編碼方案時,我們應該保證每一個編碼節點不能是另一個編碼節點的父節點。
1952年,數學家D.A.Huffman提出了根據字符在文件中出現的頻率,用0/1的數字串表示各字符的最佳編碼方式。
在這裏插入圖片描述

算法思想:

哈夫曼編碼:根據字符的使用頻率作爲權值構建一棵哈夫曼樹,然後利用哈夫曼樹對字符進行編碼。構造一棵哈夫曼樹,是將所要編碼的字符作爲葉子節點,該字符的使用頻率作爲葉子節點的權值,以自底向上的方式,通過不斷合併後構造出的一棵樹。
算法採用的貪心策略是:在森林中不斷尋找兩個權值最小且沒有父節點的節點,然後將其合併爲一棵二叉樹,直至森林中僅剩一棵樹。

構造步驟:
  1. 獲取字符及其出現的頻率,將這樣的數值作爲樹節點放入森林
  2. 遍歷森林,找到節點權值最小且沒有父節點的兩個節點
  3. 合併2中的兩個節點,權值較小的作爲左孩子,另一個作爲右孩子
  4. 將新生成的樹放入森林,且轉至2,繼續查找,直至森林中僅剩一棵樹
實現思路:
  1. 爲了操作方便,我們聲明一個節點結構體,包含父節點、左孩子、右孩子、字符、以及出現的頻率等數據域,並且採用數組存儲這些節點。
  2. 初始狀態下(節點沒有父節點、左孩子、右孩子、出現頻率),因此我們首先對存儲數組初始化,初始化標準:父節點、左孩子、右孩子均爲-1,出現頻率爲0。
  3. 初始結束後,獲取各個節點的數據(字符、出現頻率)。通過遍歷存儲數組,不斷從其中找到權重較小的兩個節點合併。但是由於數值獲取的隨機性即權重較小的節點在數組中保存的位置隨機,因此我們不得不每次都對整個存儲數組進行遍歷,此外爲了避免已經合併過的節點再次合併,我們在查找過程中還需要加上一個條件-節點的父節點域爲-1(即不存在父節點)。
  4. 合併過程:因爲採用數組存儲,所以每次合併後,只需要將新合成的節點放入數組末尾即可。
代碼示例:
void create_huff_tree(int n){
 	int m1, m2, x1, x2;
 	for(int i = 0; i < n-1; i++){
  	m1 = m2 = MAX_VALUE;
  	x1 = x2 = 0;
  	for(int j = 0; j < n + i; j++){
   		if(huffNode[j].parent == -1){//查找父節點爲空中的
    			if(huffNode[j].weight < m1){//最小節點
     				m2 = m1;
     				x2 = x1;
     				m1 = huffNode[j].weight;
     				x1 = j;
    			} else if(huffNode[j].weight < m2){//次小節點
     				m2 = huffNode[j].weight;
     				x2 = j;
    			}
   		}
  	}
  	huffNode[x1].parent = n + i;//更新找到的兩個節點父節點
  	huffNode[x2].parent = n + i;
  	huffNode[n+i].weight = m1 + m2;//生成新節點,且加入森林
  	huffNode[n+i].left = x1;
  	huffNode[n+i].right = x2;
 }
編碼步驟:

分析:編碼過程中,每個待編碼字符都是樹的葉子節點,並且樹的節點採用數組存儲。因此,我們可以遍歷0-n號元素(0-n,爲待編碼字符節點),從葉子節點向根編碼。

  1. 爲了對應每一個待編碼節點,我們聲明一個結構體,包含記錄二進制信息的數組以及數組的開始位置。
  2. 因爲葉子節點的深度可能不一致,因此聲明一個臨時變量保存編碼信息
  3. 遍歷葉子節點到根節點中間的節點。在此規定,左子樹爲0,右子樹爲1,因此遍歷過程中需要判斷是左子樹還是右子樹。
代碼示例:
void huff_coding(int n){
 	Code temp;
 	int c, p;
 	for(int i = 0; i < n; i++){
  		temp.start = n-1;
  		c = i;
  		p = huffNode[c].parent;
  		while(p != -1){//從葉子到根遍歷
   			if(huffNode[p].left == c)
    				temp.bit[temp.start--] = 0;
   			else
    				temp.bit[temp.start--] = 1;
   			c = p;
  	 		p = huffNode[c].parent;
  		}
  		for(int j = temp.start+1; j < n; j++)//臨時變量賦值給對應變量保存編碼
   			huffCode[i].bit[j] = temp.bit[j];
  		huffCode[i].start = temp.start;
 	}
}
完整代碼:
#include<iostream>
using namespace std;

#define MAX_VALUE 1e7
#define MAX_SIZE 100

struct Node{
  	int weight;
  	int parent;
  	int left, right;
 	char chr;
};
struct Code{
 	int bit[MAX_SIZE];
 	int start;
};

Node huffNode[MAX_SIZE];
Code huffCode[MAX_SIZE];

void create_huff_tree(int n){
 	int m1, m2, x1, x2;
 	for(int i = 0; i < n-1; i++){
  		m1 = m2 = MAX_VALUE;
  		x1 = x2 = 0;
  		for(int j = 0; j < n + i; j++){
   			if(huffNode[j].parent == -1){
    				if(huffNode[j].weight < m1){
     					m2 = m1;
     					x2 = x1;
     					m1 = huffNode[j].weight;
     					x1 = j;
    				} else if(huffNode[j].weight < m2){
     					m2 = huffNode[j].weight;
     					x2 = j;
    				}
   			}
  		}
 	 	huffNode[x1].parent = n + i;
  		huffNode[x2].parent = n + i;
  		huffNode[n+i].weight = m1 + m2;
  		huffNode[n+i].left = x1;
  		huffNode[n+i].right = x2;
 	}
}
void huff_coding(int n){
 	Code temp;
 	int c, p;
 	for(int i = 0; i < n; i++){
  		temp.start = n-1;
  		c = i;
  		p = huffNode[c].parent;
  		while(p != -1){
   			if(huffNode[p].left == c)
    				temp.bit[temp.start--] = 0;
   			else
    				temp.bit[temp.start--] = 1;
   			c = p;
   			p = huffNode[c].parent;
  		}
  		for(int j = temp.start+1; j < n; j++)
   			huffCode[i].bit[j] = temp.bit[j];
  		huffCode[i].start = temp.start;
 	}
}

int main(){
 	int n;
  	cin >> n;
  	for(int i = 0; i < 2*n -1; i++){//初始化 
   		huffNode[i].weight = 0;
   		huffNode[i].parent = -1;
   		huffNode[i].left = -1;
  		huffNode[i].right = -1; 
 	}
 	for(int i = 0; i < n; i++){//輸入字符和權值 
  		cin >> huffNode[i].chr >> huffNode[i].weight;
 	}
 	create_huff_tree(n);
 	huff_coding(n);
 	for(int i = 0; i < n; i++){//打印輸出編碼
  		for(int j = huffCode[i].start+1; j < n; j++){
   			cout << huffCode[i].bit[j];
  		}
  		cout << endl;
 	}
  	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章