1. 程式人生 > >【資料結構】哈夫曼樹的編碼與譯碼

【資料結構】哈夫曼樹的編碼與譯碼

#include <stdio.h>
#include <malloc.h>
#include <string.h>

typedef struct {
	char info;
	int weight;
	int parent, lchild, rchild;
} HTNode, *HuffmanTree;

typedef char* *HuffmanCode;

HuffmanTree HT;
HuffmanCode HC;
int n = 8;

/* 
   在 HT[1...t] 中選擇 parent 不為 0 且權值最小的兩個結點,
   其序號分別為 s1 和 s2 
*/
void Select(HuffmanTree HT, int t, int *s1, int *s2) {
	int i, temp1, temp2;
	temp1 = temp2 = 1000; 
	
	for (i = 1; i <= t; i++) {
		if (HT[i].parent == 0 && (HT[i].weight < temp1 || HT[i].weight < temp2)) {
			if (temp1 < temp2) {
				temp2 = HT[i].weight;
				*s1 = i;
			} else {
				temp1 = HT[i].weight;
				*s2 = i;
			}
		}
	}
	/* s1 放較小的序號 */ 
	if (*s1 > *s2) {
		i = *s1;
		*s1 = *s2;
		*s2 = i;
	}
} 

HuffmanTree HuffmanCoding(int *w, int n, char *info) {
	// n 為字串個數
	HuffmanTree HT, p;
	char *cd;
	int m, s1, s2, i, start, f, c;
	
	if (n <= 1) {
		return 0;
	} 
	m = 2 * n - 1;
	HT = (HuffmanTree)malloc((m+1)*sizeof(HTNode));
	p = HT + 1;
	
	for (i = 1; i <= n; ++i, ++p, ++w) {
		// 不用第 0 號單元
		p->weight = *w;
		p->info = info[i-1];
		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;
	}
	for (i = n+1; i <= m; ++i) {
		// 在 HT 中選擇 parent 為 0 且 weight 最小的兩個結點,其序號分別為 s1, s2 
		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 *));
	cd = (char *)malloc(n*sizeof(char));
	cd[n-1] = '\0';
	
	for (i = 1; i <= n; ++i) {
		start = n - 1;
		for (c = i, f = HT[i].parent; f != 0; c = f, f = HT[f].parent) {
			// 從子葉到根逆向2求編碼  f 為父結點  c 為子結點
			if (HT[f].lchild == c) {
				cd[--start] = '0';
			} else {
				cd[--start] = '1';
			}
		}
		HC[i] = (char *)malloc((n-start)*sizeof(char));
		strcpy(HC[i], &cd[start]);
	} 
	free(cd);
	return HT;
}

/* 編碼 */
void TextToCode() {
	char str[50];
	int i, j, p;
	
	if (HT == NULL) {
		printf("請先進行初始化!\n");
		return ;
	}
	
	printf("請輸入要編碼的文字(小寫英文字母):\n");
	getchar();
	gets(str);
	printf("編碼結果如下:\n");
	p = strlen(str);
	for (i = 1; i <= p; i++) {
		for (j = 1; j <= n; j++) {
			if (str[i-1] == HT[j].info) {
				printf("%s", HC[j]);
				break;
			}
		}
	}
	printf("\n");
} 

/* 譯碼 */ 
void CodeToText() {
	int i = 1, j, key;
	int i1, i2;
	char str[100];
	int a = 1;
	int m = 2 * n - 1;
	
	if (HT == NULL) {
		printf("請先進行初始化!\n");
		return ;
	} 
	
	printf("\n每個字母對應的哈夫曼編碼:\n");
	
	/* 以一定格式輸出字母對應的編碼 */ 
	for (i1 = i; i1 <= 2; i1++) {
		printf("┌———————┐ ┌———————┐ ┌———————┐ ┌———————┐\n");
		for (i2 = 1; i2 <= 4; i2++) {
			printf("| %c:%-11s |", HT[a].info, HC[a]);
			a++;
		} 
		printf("\n");
		printf("└———————┘ └———————┘ └———————┘ └———————┘\n");
	}
	
	printf("\n請輸入哈夫曼編碼:\n");
	scanf("%s", str);
	j = strlen(str);
	key = m;
	printf("哈夫曼編碼譯碼如下:\n");
	while (i <= j) {
		while (HT[key].lchild != 0) {
			if (str[i-1] == '0') {
				key = HT[key].lchild;
				i++; continue;
			}
			if (str[i-1] == '1') {
				key = HT[key].rchild;
				i++; continue;
			}
		}
		printf("%c", HT[key].info);
		key = m;
	}
	
	printf("\n");
}

void menu() {
	printf("┌—————————————————————————┐\n");
	printf("| (1)                             構造哈夫曼樹      | \n");
	printf("└—————————————————————————┘\n");
	printf("┌—————————————————————————┐\n");
	printf("| (2)                             哈夫曼編碼        | \n");
	printf("└—————————————————————————┘\n");
	printf("┌—————————————————————————┐\n");
	printf("| (3)                             哈夫曼譯碼        | \n");
	printf("└—————————————————————————┘\n");
	printf("┌—————————————————————————┐\n");
	printf("| (0)                             退出              | \n");
	printf("└—————————————————————————┘\n");
}

int main() {
	int i, j = 1;

	int wei[8] = {5, 25, 7, 8, 14, 20, 3, 11};
	char info[8] = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'};
					
	for (; j; ) {
		menu();
		scanf("%d", &i);
		switch(i) {
			case 1:
				HT = HuffmanCoding(wei, n, info);
				break;
			case 2:
				TextToCode();
				break;
			case 3:
				CodeToText();
				break;
			case 0:
				j = 0;
				break;
			default: printf("輸入錯誤重新輸入\n");
		}
	} 
	return 0; 
}

注意:main函式中的陣列 wei, info 可自行更改(其中 wei 中的值對應 info 中字元的權值)