1. 程式人生 > >從頭結點開始編碼的赫夫曼樹

從頭結點開始編碼的赫夫曼樹

str style struct tarray enum tdi color num 輸入

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

#ifndef MACRO_//
#define MACRO_
#define OK 1
#define FALSE 0
#define ERROR 0
#define TURE 1
#define OVERFLOW -2
#define INFEASIBLE -1
typedef int Status;
typedef int BOOL;
#endif 
/*註意,自頂向下求編碼會破壞HT的原本數據,都變成2,要想克服,需采用自底向上求編碼
      或者另設記錄每個元素訪問記錄的數組
    同時,也無法正確解碼 -----------------嚴蔚敏教材缺陷---------------
*/ typedef struct { int weight; int parent, lchild, rchild; }HTNode, *HuffmanTree;//動態分配數組存儲赫夫曼樹 typedef char **HuffmanCode;//動態分配數組存儲赫夫曼編碼表 Status Input(int *ElemNum, int **WeightArray); //輸入權值 //成功返回OK,ElemNum不合適返回ERROR void SlectMin(int i, int *S1, int *S2, HuffmanTree HT); //從HuffmanTree中選出Parent為0,權值最小的兩個數,分別將下標存放在S1和S2中,其中S1中存放的值<S2中存放的值
//i表示當前有效結點的下標 void CreatHuffmanTree(HuffmanTree *HT, int *WeightArray, int ElemNum); //WeightArray存放ElemNum個字符的權值(均>0),構造赫夫曼樹HT void HuffmanCoding_FromRoot(HuffmanTree HT, HuffmanCode *HC, int ElemNum); //從頭結點開始遍歷,求出n個字符的赫夫曼編碼HC void Decode(HuffmanTree HT, int ElemNum); //輸入哈夫曼編碼,解碼 int main() { int
ElemNum, *WeightArray; HuffmanTree HT; HuffmanCode HC; while (Input(&ElemNum, &WeightArray) == ERROR) printf("The value of ElemNum is not feasible ! \n"); CreatHuffmanTree(&HT, WeightArray, ElemNum); HuffmanCoding_FromRoot(HT, &HC, ElemNum); for (int i = 1; i <= ElemNum; ++i) printf("%-3d ,coding is : %s\n", HT[i].weight, HC[i]); /*註意,自頂向下求編碼會破壞HT的原本數據,都變成2,要想克服,需采用自底向上求編碼 或者另設記錄每個元素訪問記錄的數組 -----------------嚴蔚敏教材缺陷--------------- */ Decode(HT, ElemNum); system("pause"); } Status Input(int *ElemNum,int **WeightArray) //輸入權值 //成功返回OK,ElemNum不合適返回ERROR { printf("How many elems ? \n"); scanf("%d", ElemNum); if (*ElemNum>1)//小於2還建什麽Huffman樹呢? { *WeightArray = (int *)malloc(sizeof(int)*(*ElemNum)); if (!(*WeightArray)) exit(OVERFLOW); printf("\nPlease Enter the weight\n"); for (int i = 0; i < (*ElemNum); ++i) scanf("%d", &(*WeightArray)[i]); return OK; }//if return ERROR; }//CrearHuffmanTree void CreatHuffmanTree(HuffmanTree *HT,int *WeightArray,int ElemNum) //WeightArray存放ElemNum個字符的權值(均>0),構造赫夫曼樹HT { for (int i = 0; i < ElemNum; ++i) printf("%d ", WeightArray[i]); int TotalNum = 2 * ElemNum - 1;//總結點數 *HT = (HuffmanTree)malloc(sizeof(HTNode)*(TotalNum + 1));//0號單元棄用,動態分配數組存儲赫夫曼樹 if (!*HT)exit(OVERFLOW);//分配失敗 HuffmanTree p = *HT; int i,S1,S2; for (i = 1; i <=ElemNum; ++i) (*HT)[i] = {WeightArray[i-1], 0, 0, 0 };//初始化赫夫曼樹前ElemNum個結點 for (i; i <= TotalNum; ++i) (*HT)[i] = { 0, 0, 0, 0 };//初始化後面的結點 for (i = 0; i < ElemNum-1; ++i)//ElemNum需循環建樹ElemNum-1次 { SlectMin(i + ElemNum, &S1, &S2, *HT);//選出兩個最小值S1,S2,其中S1的值小於S2的值 //printf("\nTake %d %d ", S1, S2); (*HT)[S1].parent = (*HT)[S2].parent = i + ElemNum + 1;//i+ElemNum+1的值是父節點的下標 (*HT)[i + ElemNum + 1].lchild = S1; (*HT)[i + ElemNum + 1].rchild = S2; (*HT)[i + ElemNum + 1].weight = (*HT)[S1].weight + (*HT)[S2].weight; }//for printf("\n---------------------Creat HuffmanTree OK !---------------------\n"); }//CreatHuffmanTree void SlectMin(int i,int *S1,int *S2,HuffmanTree HT) //從HuffmanTree中選出Parent為0,權值最小的兩個數,分別將下標存放在S1和S2中,其中S1中存放的值<S2中存放的值 //i表示當前有效結點的下標 { int j; *S1 = *S2 = INT_MAX; for (j = 1; j <= i; ++j)//選出第一個最小值 { if (HT[j].parent != 0)//父節點需為0 continue; if (HT[*S1].weight > HT[j].weight) *S1 = j; }//for for (j = 1; j <= i; ++j)//選出第二個最小值 { if (HT[j].parent != 0)//父節點需為0 continue; if (j == *S1)//不能選剛選過的 continue; if (HT[*S2].weight > HT[j].weight) *S2 = j; }//for }//SlectMin void HuffmanCoding_FromRoot(HuffmanTree HT,HuffmanCode *HC,int ElemNum) //從頭結點開始遍歷,求出n個字符的赫夫曼編碼HC { *HC = (HuffmanCode)malloc(sizeof(char *)*(ElemNum + 1)); if (!(*HC))exit(OVERFLOW); char *buffer = (char *)malloc(sizeof(char)*ElemNum);//分配編碼緩沖區大小 if (!buffer)exit(OVERFLOW); int CodeNum = 2 * ElemNum - 1;//正在訪問結點的下標 int CodeLen = 0;//編碼長度 for (int i = 1; i <= CodeNum; ++i) HT[i].weight = 0;//遍歷赫夫曼樹時用作結點狀態標誌(相當於初始化訪問次數) while (CodeNum)//訪問到根結點的父節點即0,說明遍歷完該樹 { if (HT[CodeNum].weight == 0)//還沒被訪問過 { HT[CodeNum].weight = 1;//標記訪問一次 if (HT[CodeNum].lchild != 0)//有左子樹,訪問它的左子樹 { CodeNum = HT[CodeNum].lchild; buffer[CodeLen++] = 0;//向左訪問,編碼為0 }//if else if (HT[CodeNum].rchild == 0)//左子樹右子樹都為0,說明它是葉子結點 { buffer[CodeLen] = \0; (*HC)[CodeNum] = (char *)malloc(sizeof(char)*(CodeLen + 1)); if (!(*HC)[CodeNum])exit(OVERFLOW);//分配存儲空間失敗 strcpy((*HC)[CodeNum], buffer);//將編碼拷貝 }//else if }//if else if (HT[CodeNum].weight==1)//被訪問過一次,即該結點左子樹被訪問過,接下來訪問它的右子樹 { HT[CodeNum].weight = 2;//標記訪問2次 if (HT[CodeNum].rchild != 0)//有右子樹,訪問它的右子樹 { CodeNum = HT[CodeNum].rchild; buffer[CodeLen++] = 1;//向右訪問,編碼為1 }//if }//else if else//被訪問過2次,即左子樹右子樹都被訪問過,回溯 { --CodeLen; CodeNum = HT[CodeNum].parent; }//else }//while }//HuffmanCoding void Decode(HuffmanTree HT,int ElemNum) //輸入哈夫曼編碼,解碼 { int i = 2*ElemNum-1; char buffer[20]; fflush(stdin); printf("Enter the huffmanCode : "); gets(buffer); int j = 0; while (buffer[j]!=\0) { if (buffer[j]==0) i = HT[i].lchild; else i = HT[i].rchild; ++j; }//while printf("The num is %d", HT[i].weight); }//Decode

從頭結點開始編碼的赫夫曼樹