小任的第一篇部落格-哈夫曼樹

小任敲程式碼發表於2020-12-27

哈夫曼樹

主要介紹哈夫曼樹的構建,遍歷(先序遍歷、中序遍歷、後序遍歷)、哈弗曼編碼實現、哈夫曼譯碼實現。

哈夫曼樹功能實現

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <iostream>
using namespace std;

#define OK 1
#define N 100
typedef struct {
	int num;//下標
	int weight;
	int parent, lchild, rchild;
}HTNode, * HuffmanTree;

typedef char  ** HuffmanCode;

//選擇兩個最小的數構建哈夫曼樹
void Select(HuffmanTree& HT, int  m, int& s1, int& s2) {
	HuffmanTree p, q, pmin1 = NULL, pmin2 = NULL;
	p = HT + 1;//p指向下標為1的結點
	while (p->parent != 0)  p++;
	pmin1 = p; pmin2 = p + 1;
	while (pmin2->parent != 0) pmin2++;
	for (q = pmin2; q <= HT + m; q++) {                  //遍歷所有結點找到權值最小的兩個結點
		if (q->parent == 0 && q->weight < pmin1->weight) {
			pmin2 = pmin1; pmin1 = q;
		}//if
		else if (q->parent == 0 && q->weight < pmin2->weight) {
			pmin2 = q;
		}
	}//for
	s1 = pmin1->num;
	s2 = pmin2->num;
}

//Visit函式
int PrintHuffman(int weight){
	printf("%5d",weight);//輸出權重
	return weight;
}

//輸出哈夫曼樹
void PrintHuffmanTree(HuffmanTree& HT, int m) {
	int i;
	HuffmanTree p = HT + 1;
	printf("i\tweight\tparent\tlchild\trchild\n");
	for (i = 1; i <= m; i++, p++)
		printf("%d\t%d\t%d\t%d\t%d\n", p->num, p->weight, p->parent, p->lchild, p->rchild);

}
//編碼輸出函式
void 	PrintHuffmanCode(HuffmanTree HT,HuffmanCode HC,int n){
	int i;
	printf("輸出各權值對應的編碼:\n");
	printf("權值\t編碼\n");
	for(i=1;i<=n;i++)
		printf("%d\t%s\n",HT[i].weight,HC[i]);

}

//構建哈夫曼樹
void HuffmanBuding(HuffmanTree& HT, HuffmanCode& HC, int* w, int n) {
	//w存放n個字元的權值(均>0),構造哈夫曼樹HT,並求出n個字元的哈夫曼編碼HC
	
	int s1, s2, f, c, i, m, start;
	if (n <= 1) return;
	m = 2 * n - 1;
	HT = (HuffmanTree)malloc((m + 1) * sizeof(HTNode));   //0號單元未用
	for (i = 1; i <= n; ++i, ++w) {
		HT[i].num = i; 
		HT[i].weight = *w;//將下標為i的結點權值放入w中
		HT[i].parent = 0; HT[i].lchild = 0; HT[i].rchild = 0;//初始為0
	}
	for (;i <= m; ++i) {                 //i=n+1
		HT[i].num = i; HT[i].weight = 0; HT[i].parent = 0; HT[i].lchild = 0; HT[i].rchild = 0;
	}
	printf("輸出HT表的初態:\n");
	PrintHuffmanTree(HT, m);
	for (i = n + 1; i <= m; ++i) {
		//在HT[1..i-1]選擇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;
	}
	printf("輸出HT表的終態:\n");
	    PrintHuffmanTree(HT, m);
}
	
	//---從葉子到根逆向求每個字元的哈夫曼編碼---
	void HuffmanTreeCoding(HuffmanTree HT,HuffmanCode& HC, int* w, int n){
	char* cd;
	HC = (HuffmanCode)malloc((n + 1) * sizeof(char*));  //分配n個字元編碼的頭指標向量
	cd = (char*)malloc(n * sizeof(char));    //分配求編碼的工作空間
	cd[n - 1] = '\0';      //編碼結束符
	for (int i = 1; i <= n; ++i) {     //逐個字元求哈夫曼編碼
		int start = n - 1;         //編碼結束符位置
		for (int c = i, f = HT[i].parent; f != 0; c = f, f = HT[f].parent)  //從葉子到根逆向求編碼,若不是根結點則進行編碼
			if (HT[f].lchild == c) cd[--start] = '0';//如果為左孩子,編碼為0
			else cd[--start] = '1';//右孩子編碼為1
		HC[i] = (char*)malloc((n - start) * sizeof(char));  //為第i個字元編碼分配空間
		strcpy(HC[i], &cd[start]);     //從cd複製編碼(串)到HC
	}
	free(cd);
	PrintHuffmanCode(HT,HC,n);
	}

	//對哈夫曼樹進行先序遍歷
void PreOrder(HuffmanTree HT,int x,int(*Visit)(int weight)){
  if(x>0){
	  Visit(HT[x].weight);//輸出下標為x的結點的值
	  if(HT->lchild) PreOrder(HT,HT[x].lchild,Visit);
	  if(HT->rchild) PreOrder(HT,HT[x].rchild,Visit);

  }}

//哈夫曼樹中序遍歷
void MidOrder(HuffmanTree HT,int x,int(*Visit)(int weight)){
	if(x>0){
		MidOrder(HT,HT[x].lchild, Visit);//輸出左孩子
		Visit(HT[x].weight);
		MidOrder(HT,HT[x].rchild, Visit);//輸出右孩子
	}}


//後序遞迴遍歷哈夫曼樹
void PostOrder(HuffmanTree HT, int x,int(*Visit)(int weight)) {
	if (x>0) {
		PostOrder(HT,HT[x].lchild, Visit);
		PostOrder(HT,HT[x].rchild, Visit);
		Visit(HT[x].weight);

	}}

//哈夫曼譯碼
void HuffmanTranslating(HuffmanTree HT,HuffmanCode& HC,int n)
{
   int i=0,j,k;
   char temp[N];
   char a[N];
   k = 0;
   i = 0;
   int*w=NULL;
   HuffmanTreeCoding(HT,HC,w,n);
   printf("請根據上表,輸入所要譯出的編碼,不得輸入空格\n");
	scanf("%s",a);
	//遍歷輸入的編碼,從編碼表查詢。
   while(a[i]!='\0'){
   	temp[k] = a[i];//將字元陣列a中的字元附給temp,a繼續接受新的字串
   	k++;
   	temp[k] = '\0';
   	//從編碼表中找出相應的字元 
   	for(j=1;j<=n;j++){
   		if(!(strcmp(temp,HC[j]))){
   			k = 0;
			printf("%d",HT[j].weight);
   			break;
   		}
   	}
   	if(k>N)
   		printf("這串編碼有問題,請重新檢查!!\n"); //判斷k值是否超過陣列長度
   	i++;

   }}
void  main()
{
	HuffmanTree HT = NULL;
	HuffmanCode HC = NULL;
	int (*Visit)(int weight);
	Visit = PrintHuffman;
	int* w, * p;
	int enter=1;
	int x=0;
	int n, i,choice;
	while(enter){
	printf("\n");
	printf("******哈夫曼樹功能選單*******\n");
	printf("1、建立一個哈夫曼樹\n");
	printf("2、對哈夫曼樹進行遍歷\n");
	printf("3、對哈夫曼樹進行編碼\n");
	printf("4、對哈夫曼樹進行譯碼\n");
	printf("5、退出\n");
	printf("請輸入你的選擇:");
	scanf("%d",&choice);
	switch(choice){
	case 1:
	printf("請輸入哈夫曼結點個數:");
	cin >> n;
	x=2*n-1;
	printf("請輸入哈夫曼樹各個結點的權值:");
	w = (int*)malloc(n * sizeof(int));
	for (i = 1, p = w; i <= n; ++i, ++p)
	cin >> *p;
	HuffmanBuding(HT, HC, w, n);
	break;
	case 2:
	printf("先序遍歷結果為:");
	PreOrder(HT,x,Visit);
	printf("\n");
	printf("中序遍歷結果為:");
	MidOrder(HT,x,Visit);
	printf("\n");
	printf("後序遍歷結果為:");
	PostOrder(HT,x,Visit);
	break;
	case 3:
	HuffmanTreeCoding(HT, HC, w, n);
	break;
	case 4:
	HuffmanTranslating(HT,HC,n);
	break;
	case 5:
		enter=0;
		exit(0);
	default:
		printf("輸出有誤,請重新輸入:");
		break;
	}
	system("pause");
	}
}

以上為哈夫曼樹功能實現程式碼

輸出

在這裡插入圖片描述在這裡插入圖片描述
在這裡插入圖片描述
在這裡插入圖片描述
以上為哈夫曼樹各項功能具體實現結果。
感謝閱讀。

相關文章