[C++]哈夫曼樹(最優滿二叉樹) / 哈夫曼編碼(貪心算法)


一 哈夫曼樹

1.1 基本概念

1.2 算法描述/構造過程

1.3 算法實現

  • 1> 定義存儲結構
# define MAXNEGATIVE -9999 //最大負值 
# define MAXPOSITIVE  9999 //最大正值 

typedef struct HuffmanNode{ // 加 typedef : 避免發生編譯錯誤 " variable or field 'functionName' declared void "
	double weight;
	int parent, lchild, rchild; //當前節點的雙親結點、左右孩子結點 
}*HuffmanTree;
  • 2> 確定最小的兩個結點的選擇策略(貪婪策略)【次核心】
/**
 * 選擇權值結點最小的兩個結點 s1、s2 
 */
void Select(HuffmanTree &HT,int k,int *s1,int *s2){ 
	int min[2]={0, 0}; // 升序排名, 保留 HT中 權重值最小的兩個結點的下標 
	cout<<"Select k:"<<k<<endl;
	HT[0].weight = MAXPOSITIVE; // 初始化為最大正值,方便被 初始結點比最小而比下去 
	for(int i=1;i<=k;i++){
//		cout<<"i:"<<i<<"\tweight:"<<HT[i].weight<<"\tparent:"<<HT[i].parent<<"\tparent.weight:"<<HT[HT[i].parent].weight<<endl;
		if( HT[i].parent==0 && (HT[i].weight<HT[min[1]].weight)){
			if(HT[i].weight<HT[min[0]].weight){ // 小於 最小者 
				min[1] = min[0]; min[0] = i; cout<<i<<"小於最小者!"<<endl;
			} else {
				min[1] = i;cout<<i<<"小於次小者!"<<endl;	 //僅小於 次小者 
			}
		}
	} 
	*s1 = min[0]; // 最小者下標
	*s2 = min[1]; // 次小者下標 
//	cout<<"s1:"<<*s1<<"\ts2:"<<*s2<<endl; 
}
  • 3> 創建 Huffman 樹【核心】
/**
 * 創建 Huffman 樹
 *   n : 初試結點(待編碼結點)數 
 */
void CreateHuffmanTree(HuffmanTree &HT, int n){
	int m = 2*n-1;
	HT = new HuffmanNode[m+1]; // 0 位空余
	cout<<"Please input initiative node's weight : "<<endl; // 輸入結點數據
	for(int i=1;i<=n;i++){
		cin>>HT[i].weight;
	}
	for(int k=1;k<=m;k++){ // 初始化所有結點 
		HT[k].lchild = 0;
		HT[k].rchild = 0;
		HT[k].parent = 0;
	}
	for(int j=n+1;j<=m;j++){
		int s1,s2;
		Select(HT, j-1, &s1, &s2); // 選擇權值結點最小的兩個結點s1、s2 
		HT[s1].parent = j; // 合並左右結點 
		HT[s2].parent = j; 
		HT[j].lchild = s1;
		HT[j].rchild = s2;
		HT[j].weight = HT[s1].weight + HT[s2].weight;
	}
}
  • 4> 輸出哈夫曼樹
void OutputHuffmanTree(HuffmanTree &HT, int n){ // 輸出 哈夫曼樹 中 結點情況 
	int m = 2*n-1;
	for(int i=1;i<=m;i++){
		cout<<"<"<<i<<"> weight: "<<HT[i].weight<<"\t"<<"parent: "<<HT[i].parent<<"(weight:"<<HT[HT[i].parent].weight<<")\t";
		cout<<"parent.lchild:"<<HT[HT[i].parent].lchild<<"(weight:"<<HT[HT[HT[i].parent].lchild].weight<<")"<<"\t";
		cout<<"parent.rchild:"<<HT[HT[i].parent].rchild<<"(weight:"<<HT[HT[HT[i].parent].rchild].weight<<")"<<endl;
	}
}
  • 5> 執行:Main函數
int main(){
	HuffmanTree HT;
	int n;
	cout<<"Please input Huffman initiative Node Number:"; // 輸入哈夫曼結點初始結點數目 n
	int s1, s2;
	cin>>n; 
	CreateHuffmanTree(HT, n);
	OutputHuffmanTree(HT, n);
	return 0;
}

二 哈夫曼編碼

2.1 哈夫曼編碼的主要思想

2.2 【前綴編碼】

2.3 【哈夫曼編碼】

2.4 應用:文件的編碼與譯碼

2.5 哈夫曼編碼的算法實現 (根據哈夫曼樹,求哈夫曼編碼)

  • 1> 定義存儲結構
//由於 每個哈夫曼編碼是變長編碼;因此,使用指針數組來存儲每個字符編碼串的首地址 
typedef char **HuffmanCode; // 動態分配數組,存儲哈夫曼編碼表 (char 前面不能隨意加 struct 關鍵字)
  • 2> 生成哈夫曼編碼(表)(借助於哈夫曼樹)
/**
 * 根據哈夫曼樹,求哈夫曼編碼
 */
void CreateHuffmanCodeByHuffmanTree(HuffmanTree HT, HuffmanCode &HC,int n){//從葉子到根,逆向求每個字符的哈夫曼編碼	
	HC = new char *[n+1]; // 分配存儲n個字符編碼的編碼表空間 
	char *cd = new char [n]; // 分配臨時存放每個字符編碼的動態數組空間
	cd[n-1] = '\0'; // 編碼結束符 
	for(int i=1;i<=n;i++){ // 逐個字符求哈夫曼編碼 
		int start = n-1; // start 初始化指向最后位置 即 編碼結束符位置,逆向存儲 從葉子到根結點路徑的0/1編碼
		int c = i; 
		int f = HT[i].parent; // f 指向結點C 的雙親結點
		while(f!=0){ // 直到 f 滑動到根節點為止 
			--start; // 編碼表游標向前滑動
			if(HT[f].lchild == c){
				cd[start] = '0';
			} else {
				cd[start]= '1';
			}
			c = f;
			f = HT[f].parent; // 逆向, 自(樹)底向(樹)上 
		}
		HC[i] = new char [n-start]; //為第i個字符編碼分配存儲空間 
		strcpy(HC[i], &cd[start]); // 頭文件: #include <string.h> 、<stdio.h>
	}
	delete cd;
}
  • 3> 輸出哈夫曼編碼(表)
void OutputHuffmanCodes(HuffmanCode &HC,int n){
	for(int i=1;i<=n;i++){
		cout<<"第"<<i<<"個哈夫曼編碼:";
		for(int j=0,size=sizeof(HC[i])/sizeof(char);j<size;j++){
			cout<<HC[i][j];
		}
		cout<<endl;
	}
}
  • 4> 執行:Main函數
int main(){
	HuffmanTree HT;
	int n;
	cout<<"Please input Huffman initiative Node Number:"; // 輸入哈夫曼結點初始結點數目 n
	int s1, s2;
	cin>>n; 
	CreateHuffmanTree(HT, n);
	OutputHuffmanTree(HT, n);
	
	HuffmanCode HC;
	CreateHuffmanCodeByHuffmanTree(HT, HC, n);
	OutputHuffmanCodes(HC, n);
	return 0;
}

三 哈夫曼譯(解)碼

四 文獻

4.1 參考文獻

  • 《數據結構(C語言版):嚴蔚敏/吳偉民》
  • 《算法設計與分析基礎([美] Anany Levitin. 潘彥 譯)》
    • 貪心算法思想的適用范圍

4.2 推薦文獻

五 二叉樹 補充:紅黑樹


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM