1. 程式人生 > >演算法基礎(八):超詳細最優二叉樹構建(1)

演算法基礎(八):超詳細最優二叉樹構建(1)

赫夫曼(Huffman)樹也稱最有二叉樹,是一類帶全路徑長度最短的樹,有著廣泛的應用。比如一棵判定樹,根據學生的成績劃分及格還是不及格還是優、中等、良好。顯然用if-else或者switch就可以簡單實現,當然可以直接毫不考慮的直接這樣寫,但是如果我們再肯花點功夫,就可以得到更加高效的程式。我們可以以學生的總的學科分數的佔的各個分數段的比率為權,構造一棵赫夫曼樹,這樣可以減少比較次數,提高程式執行效率。

構造赫夫曼樹的方法步驟其實很簡單--赫夫曼演算法:

(1). 根據給定的n個權值{w1,w2,w3...,wn}構成n棵二叉樹的集合F = {T1,T2,....Tn},其中每棵二叉樹Ti中只有一個帶權的wi的根節點,其左右子樹為空。

(2). 在F中選取兩棵根節點的權值最小的樹作為左右子樹構造一棵新的二叉樹,且置新的二叉樹的根節點的權值為其左、右子樹上根節點的權值之和。

(3). 在F中刪除這兩棵樹,同時將新得到的二叉樹加入到F中。

(4). 重複(2)、(3),知道F中含一棵樹為止。

下面是構造HuffmanTree的程式碼實現:

#include"stdafx.h"
#include<stdio.h>
#include<malloc.h>
#include<stdlib.h>
#include<string.h>

typedef struct
{
	unsigned int weight;
	unsigned int parent,lchild,rchild;
}HTNode,*HuffmanTree;		//動態分配陣列,儲存哈弗曼樹
typedef char** HuffmanCode;	//動態分配陣列儲存哈夫曼編碼表

void MaoPao(HuffmanTree HT,int* arr,int number,int &s1,int &s2)	//傳入HT、陣列首地址、陣列中元素的個數:m
{
	//只需進行兩趟比較即可,得到兩個最小數
	int i,j,m;
	printf("\n進入氣泡排序函式...");	
	for(i = 0;i<2;i++)		
	{
		for(j = 0;j<number - i;j++)	//number是陣列中的實際元素個數..
		{
		//	printf("\n進入迴圈");
			//printf("\n arrj = %d,arrj+1 = %d",arr[j],arr[j+1]);
		if(HT[ arr[j] ].weight < HT[ arr[j+1] ].weight)		
			{
				m = arr[j];
				arr[j] = arr[j+1];
				arr[j+1] = m;
			}
		}
	}			
	s1 = arr[number - 1];
	s2 = arr[number];
	printf("\n.........%d,%d",s1,s2);
}
void Select(HuffmanTree HT,int n,int &s1,int &s2)	//n是總的節點數量
{
	printf("\n進入Select函式");
	int arr[20] = {0};	//存放parent為0的節點的編號
	int m = 0;	
	for(int i = 1;i<=n;i++)
	{
		if(HT[i].parent == 0)
		{			
			arr[m] = i;	//從0開始存,那麼陣列中的元素個數就 = m
			m++;
			printf("\nparent = 0的節點號:%d",i);			
			if(m > 19)m = 19;
		}
	}
	//比如n = 8,表示一共8個數,最後m++ = 8。實際陣列是0~7的,那麼m就是陣列中的元素個數
	//OK,找到parent = 0 的節點,下面選擇weight最小的兩個,該用什麼演算法呢?我用的是冒泡法,上浮兩個最小數
	MaoPao(HT,arr,m,s1,s2);	//m是陣列中的元素個數..陣列從1開始,陣列中的最後一個元素是m,倒數一個是m-1得到兩個權值最小的節點


}


int HuffmanCoding(HuffmanTree &HT,HuffmanCode &HC,int* w,int n)	//n是葉子的數量,w存放n個字元(葉子)的權值
{
	int m;			//總共的節點數
	int s1 = 0;
	int s2 = 0;		//儲存選擇到的節點編號
	char* cd;		
	int c;
	int f;
	int start;
	HuffmanTree  p;
	int i = 0;
	if(n <= 1)
		return 0;
	m = 2 * n - 1;

	HT = (HuffmanTree)malloc((m + 1) * sizeof(HTNode));
	
	for(p = HT+1,i = 1;i <= n; ++i,++p,++w)//第一號單元空出來的,從1開始,這裡是初始化操作,分兩步,一步是有權值的,已不是沒有權值的!用if也是可以的
	{
		p->weight = *w;	
		p->parent = 0;
		p->lchild = 0;
		p->rchild = 0;
	}
	for(;i <= m;++i,++p)
	{
		printf("a..aaaaaaa......i = %d",i);
		p->weight = 0;
		p->parent = 0;
		p->lchild = 0;
		p->rchild = 0;
	}
	printf("初始化完畢..");	
	//建立哈弗曼樹
	//8,這是後面測試的。
	for(i = n+1;i<=m;++i)		//這裡要迴圈n-1次,也就是說,構造哈弗曼樹需要同一個操作做n-1次,m = 2n-1,2n-1-(n+1)+1 = n-1
	{		
		Select(HT,i-1,s1,s2);
		printf("\n此時...i = %d,m = %d,,,被選中的編號..%d,%d",i,m,s1,s2);
		HT[s1].parent = i;  HT[s2].parent = i;
		HT[i].lchild = s1;  HT[i].rchild = s2;
		HT[i].weight = HT[s1].weight + HT[s2].weight;
	}
	return 1;
	
}

void ShowHT(HuffmanTree &HT,int n)	//n是葉子的數量
{
	
	int m = 2*n - 1;	//總共的節點數量
	for(int i = 1;i<=m;i++)
	{
		printf("\n編號:%d--Weight:%d  Parent: %d  LeftChild: %d  RightChild;%d",i,HT[i].weight,HT[i].parent,HT[i].lchild,HT[i].rchild);		
	}
}
下面是main函式的測試:
#include"stdafx.h" 
//#include"BasicGraph2.h"
#include "HuffmanCode.h"
void main()
{ 
	HuffmanTree HT;
	HuffmanCode ch;
	int n = 8;
	int m = 15;
	int arr[] = {5,29,7,8,14,23,3,11};
	if(HuffmanCoding(HT,ch,arr,n))
		ShowHT(HT,n);
}

執行結果:


圖2:


說明:其實根據上面的權值,樹有多種,但是WPL都是一樣的。