1. 程式人生 > >哈夫曼樹詳細講解(帶例題和C語言程式碼實現——全註釋)

哈夫曼樹詳細講解(帶例題和C語言程式碼實現——全註釋)

**

哈夫曼樹詳細講解(帶例題和C語言程式碼實現——全註釋)

**

  1. 定義

哈夫曼樹又稱最優二叉樹,是一種帶權路徑長度最短的二叉樹。所謂樹的帶權路徑長度,就是樹中所有的葉結點的權值乘上其到根結點的 路徑長度(若根結點為0層,葉結點到根結點的路徑長度為葉結點的層數)

  1. 計算公式
    j計算公式

樹的帶權路徑長度記為WPL=(W1L1+W2L2+W3L3+…+WnLn) ,N個權值Wi(i=1,2,…n)構成一棵有N個葉結點的二叉樹,相應的葉結點的路徑長度為Li(i=1,2,…n)。 可以證明哈夫曼樹的WPL是最小的

  1. 運算過程

一、對給定的n個權值{W1,W2,W3,…,Wi,…,Wn}構成n棵二叉樹的初始集合F= {T1,T2,T3,…,Ti,…,Tn},其中每棵二叉樹Ti中只有一個權值為Wi的根結點,它的左右子樹均為空。(為方便在計算機上實現算 法,一般還要求以Ti的權值Wi的升序排列。)
二、在F中選取兩棵根結點權值最小的樹作為新構造的二叉樹的左右子樹,新二叉樹的根結點的權值為其左右子樹的根結點的權值之和。
三、從F中刪除這兩棵樹,並把這棵新的二叉樹同樣以升序排列加入到集合F中。
四、重複二和三兩步,直到集合F中只有一棵二叉樹為止

  1. 圖解過程
    在這裡插入圖片描述
    在這裡插入圖片描述
  2. 例題講解(重點)
    如例:已知某通訊系統在通訊聯絡中只可能出現8中字元,其概率分別別是0.05, 0.29, 0.07, 0.08, 0.14, 0.23, 0.03, 0.11, 試設計赫夫曼編碼
    根據所佔比例列出權值解答

    在這裡插入圖片描述
    注:A是0111
    一共有八個節點,則陣列中需要構建的節點 m = 2*n – 1 則為15個 給八行生成樹,餘下的記錄過程
    在這裡插入圖片描述
    在這裡插入圖片描述
    最終結果:
    在這裡插入圖片描述

C語言實現程式碼

(記錄人的姓名,成績,按照成績作為權值編碼將上文中字母作為名字,比例作為成績(權值))

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define n  6//葉子結點數目
#define m  (2*n-1) //總結點數目,可證明
#define MAXVALUE 10000 //最大權值
#define MAXBIT 20 //哈夫曼編碼最大長度
typedef struct{
	char name[5];
	int weight;
	int parent;
	int Lchild,Rchild;
}Htreetype;
typedef struct{
	int bit[n];//儲存位串
	int start;//編碼起始位置
	char name[5]; 
}Hcodetype;
void select(Htreetype tree[],int position,int *node1,int *node2);//選擇兩個權值最小的點(代價最小) 
void HuffmanTree(Htreetype t[]);//構造哈夫曼樹,用到了select選擇節點 
void HuffmanCode(Hcodetype code[],Htreetype tree[]);//開始編碼 
void show(Htreetype tree[],Hcodetype code[]);//顯示編碼 
int main(){
	Htreetype tree[m];//有m個位置的空樹
	Hcodetype code[n];//n個編碼位置
	
	HuffmanCode(code,tree);//編碼完成
	

	show(tree,code);//輸出 
	
	return 0;
} 

void select(Htreetype tree[],int position,int *node1,int *node2){
	//tree是哈夫曼樹 position是到 第幾個節點之前的都要檢索 node1 和node2 儲存兩個權值最小的節點的位置
	//一共n個待記錄點,n -- m裡面的是生成樹的時候新根節點,裡面放的是權值,當生成了新根節點,查詢最小就要從頭到新構成的位置
	*node1 = *node2 = 0;
	int min1,min2; 
	min1 = min2 = MAXVALUE;
	int i;
	for(i = 0; i < position; i++){
		if(tree[i].parent == -1){//如果沒有父節點說明還沒有成為樹的一部分 
			if(tree[i].weight < min1){//維護min1 < min2 也就是 node1的權值小 
				min2 = min1;//min2記錄min1的權值 
				min1 = tree[i].weight;//min1的值更新
				*node2 = *node1;//拋棄當前兩個節點中大的那個重新記錄 
				*node1 = i; 
			}else if(tree[i].weight < min2){//前面的min1沒有這個小但是min2比這個小所以直接用min2記錄,這樣就不用對兩個節點都比較之後才換節點 
				min2 = tree[i].weight;
				*node2 = i; 
			} 
			
		}
	}
}
void HuffmanTree(Htreetype tree[]){
	int i;//for迴圈的計數器
	int node1,node2;//找權值最小的兩個節點位置
	node1 = node2 = 0;
	
	char name[5];//暫存姓名 
	int now_weight;//暫存權值 
	for(i = 0; i < m; i++){//一共n個節點,則需要構建m個點儲存資訊 m = (2*n - 1) 
		tree[i].weight = 0;//沒有權值 
		tree[i].parent = -1;
		tree[i].Lchild = -1;
		tree[i].Rchild = -1;
	}
	
	printf("一共有%d個人\n",n);//構造節點資訊
	
	for(i = 0; i < n; i++){//輸入基本資訊 
		printf("請輸入姓名:");
		scanf("%s",&name);
		printf("請輸入成績:");
		scanf("%d",&now_weight);
		strcpy(tree[i].name,name); 
		tree[i].weight = now_weight;
	}
	
	for(i = n; i < m; i++){//構造哈夫曼樹 
		select(tree,i,&node1,&node2);//選好兩個節點 
		tree[node1].parent = i;//選好的兩個節點形成了樹 
		tree[node2].parent = i;//生成的節點放在當前位置
		tree[i].Lchild = node1;//node1權值小,也就是左子樹權值都小
		tree[i].Rchild = node2;
		tree[i].weight = tree[node1].weight + tree[node2].weight;//生成新樹完成 
	}
}
void HuffmanCode(Hcodetype code[],Htreetype tree[]){//code存放每個節點的編碼對應0 --- (n - 1)個節點編碼 
	int i;//計數器
	
	int parent_position,now_position;
	
	Hcodetype cd;//暫時存放編碼 
	
	HuffmanTree(tree);//構造好霍夫曼樹了在 tree裡面
	
	for(i = 0; i < n; i++){//給n個節點編碼 
		cd.start = n;//最長為n
		strcpy(cd.name,tree[i].name);//將名字也放進裡面,可省略換其餘方式輸出
		now_position = i;//從每個節點向上 
		parent_position = tree[i].parent;//找到對應的父節點看自己當前編碼為0還是1
		while(parent_position != -1){//沒有到最底層的樹
			cd.start--;
			if(tree[parent_position].Lchild == now_position){//當前位置的是父節點的左孩子則為0 
				cd.bit[cd.start] = '0';
			}else
				cd.bit[cd.start] = '1';
				
			now_position = parent_position;//向上移動 一定要移動 
			parent_position = tree[now_position].parent;
		} 
	
	code[i] = cd;//對第i個節點編碼完成後放進code中 	 
		 
	} 
} 
void show(Htreetype tree[],Hcodetype code[]){//顯示編碼 
	int i,j;//計數器
	for(i = 0; i < n; i++){
		printf("%s          ",code[i].name);
		for(j = code[i].start; j < n; j++)
			printf("%c ",code[i].bit[j]);
		
		printf("\n");
	} 
}

輸出結果
在這裡插入圖片描述

與貼圖不同的原因在於此程式碼將同級較小權值的字母放在了左面