1. 程式人生 > >資料結構:赫夫曼樹和赫夫曼編碼的儲存表示

資料結構:赫夫曼樹和赫夫曼編碼的儲存表示

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define  UINT_MAX 100  //假設各結點的權值不會超過100
typedef struct
{
	unsigned int weight; //權值
	unsigned int parent, lchild, rchild; 
}HTNode, *HuffmanTree;  //動態分配陣列儲存赫夫曼樹

typedef char **HuffmanCode;  //動態分配陣列儲存赫夫曼編碼表

int Min(HuffmanTree &HT,int i)
{
	//在HT[1...i]中選擇parent為0且權值最小的結點
	//返回該結點的下標值
	//此函式被Select函式呼叫
	int j;
	unsigned int k = UINT_MAX;//假設各結點的權值不會超過UINT_MAX
	int flag;
	for(j = 1; j <= i; ++j)
	{
		if(HT[j].weight < k && HT[j].parent == 0)//用父結點是否為0來判斷此結點是否已經被選過
		{
			k = HT[j].weight;
			flag = j;
		}
	}
	HT[flag].parent = 1;//作個標記,說明已經被選擇了,因為在Select函式中要選擇權值小的兩個結點
	return flag;
}

void Select(HuffmanTree &HT, int i, int &s1, int &s2)
{
	//在HT[1...i]中選擇parent為0且權值最小的兩個結點,其序號分別為s1,s2
	//s1 <= s2
	s1 = Min(HT,i);
	s2 = Min(HT,i);
}

void HuffmanCoding(HuffmanTree &HT, HuffmanCode &HC, int *w, int n)
{
	//w存放n個字元的權值(均>0),構造赫夫曼樹HT,並求出n個字元的赫夫曼編碼HC
	if(n <= 1)
		return ;
	int m = 2*n - 1; //嚴格的二叉樹中,有n個葉子結點,就有2n - 1個結點
	HT = (HuffmanTree)malloc((m+1)*sizeof(HTNode));  //0號單元不用
	HuffmanTree p;
	int i;
	for(p = HT+1, i =1;i <= n; ++i, ++p, ++w)
	{
		//初始化一棵赫夫曼樹
		(*p).weight = *w;
		(*p).parent = 0;
		(*p).lchild = 0;
		(*p).rchild = 0;
	}
	for(;i<=m;++i,++p)
	{
		(*p).weight = 0;
		(*p).parent = 0;
		(*p).lchild = 0;
		(*p).rchild = 0;
	}
	
	int s1,s2;//儲存權值最小的兩個結點的序號
	for(i=n+1; i <= m; ++i)//建立赫夫曼樹
	{
		Select(HT,i-1,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;
	}

	//--------從葉子到根逆向求每個字元的赫夫曼編碼--------
	HC = (HuffmanCode)malloc((n+1)*sizeof(char**));  //分配n個字元編碼的頭指標向量,0號單元未用
	char *cd = (char *)malloc(n*sizeof(char));  //分配求編碼的工作空間
	cd[n-1] = '\0';  //編碼結束符
	int start;
	unsigned chld,father;
	for(i = 1; i <= n; ++i)  //逐個字元求赫夫曼編碼
	{
		start = n-1;  //從編碼結束符的位置開始
		for(chld=i, father=HT[i].parent; father!=0; chld=father, father=HT[father].parent)//從葉子到根逆向求編碼
		{
			if(HT[father].lchild == chld)
				cd[--start] = '0';
			else
				cd[--start] = '1';
		}
		HC[i] = (char*)malloc((n-start)*sizeof(char));  //為第i個字元編碼分配空間
		strcpy(HC[i], &cd[start]);  //從cd複製編碼到HC
	}
	free(cd);  //釋放工作空間
}

int main()
{
	HuffmanTree HT;
	HuffmanCode HC;
	printf("請輸入權值的個數:\n");
	int n;
	scanf("%d",&n);
	int *w = (int*)malloc(n*sizeof(int));
	printf("請分別輸入%d個權值:\n",n);
	int i;
	for(i=0; i<n; ++i)
	{
		scanf("%d",w+i);
	}

	HuffmanCoding(HT, HC, w, n);

	printf("赫夫曼樹為:\n");
	printf("NO\tweight\tparent\tlchild\trchild\n");
	for(i=1; i <= 2*n-1; ++i)
	{
		printf("%d\t%d\t%d\t%d\t%d\n", i, HT[i].weight, HT[i].parent,
								HT[i].lchild, HT[i].rchild);
	}
	printf("赫夫曼編碼為:\n");
	for(i = 1; i <= n; ++i)
		printf("%d|  |-->%s\n", i, HC[i]);
	return 0;
}