哈夫曼編碼C語言實現
阿新 • • 發佈:2019-01-27
#include "stdafx.h"
#include <stdio.h>
#include <stdlib.h>
#define debug 0
typedef struct HuffNode {
int parent;
int lchild;
int rchild;
int wet;
}*HuffTree, HNode;//樹的節點
/**/
int Weight_min(HuffTree HT, int k);
int SelectTwoMinNode(HuffTree HT, int k, int &min1, int &min2);
HuffTree CreatHuffmanTree(HuffTree HT, int *wet, int amount);
void Test(HuffTree HT, int amount);
void ReverseRight(HuffTree HT, HuffTree Head);
void HuffManEncode(HuffTree HT, int amount);
int main()
{
printf("請輸入建立的節點個數:");/*建立的節點在哈夫曼編碼中都是葉子節點*/
int amount;
scanf_s("%d", &amount);
int *wet = (int*)malloc(amount * sizeof(int));
//對權賦值;可以自己手動輸入特定權值
for (int i = 0; i < amount; i++) {
*(wet + i) = i+10;
}
HuffTree HT = NULL;//對於指標一定要賦初值
HT = CreatHuffmanTree(HT,wet,amount);
Test(HT, amount);
HuffTree Head = &HT[2 * amount - 2];
ReverseRight(HT, Head);
printf("線序遍歷之後的:\n");
Test(HT, amount);
HuffManEncode(HT, amount);
printf("here");
system("Pause");
return 0;
}
//在前k個節點找到權值最小的節點
int Weight_min(HuffTree HT,int k) {
int min,min_wei;
int i = 0;
while (HT[i].parent != -1) i++;//跳過樹中的節點;
min_wei = HT[i].wet;//初始值一定不能為樹中的節點;
min = i;
//若直接把HT[0].wet賦值給min_wei,恰好HT[0].wet為最小值,後面迴圈出問題;
while (HT[i].wet < min_wei&&HT[i].parent == -1&&i<k) {//權值小且不為樹中的節點;
min_wei = HT[i].wet;
min = i;
i++;
}
HT[min].parent = 1;//parent域賦值,否則節點會重複;
return min;
}
int SelectTwoMinNode(HuffTree HT,int k, int &min1, int &min2) {
min1 = Weight_min(HT, k);
min2 = Weight_min(HT, k);
return 1;
}
HuffTree CreatHuffmanTree(HuffTree HT,int *wet,int amount) {
int n = amount * 2 - 1;//哈夫曼樹的節點個數是原來的(amount*2-1)個
HT = (HuffTree)malloc(n * sizeof(HNode));//建立樹;
int i;
//前amount個節點為原來的節點,初始化(原來的節點都是葉子節點)
for (i = 0; i < amount; i++) {
HT[i].parent = -1;
HT[i].lchild= -1;
HT[i].rchild= -1;
HT[i].wet= *wet;//將權值賦值給樹中的節點
wet++;
}
//i在這裡為amount-1;從amount開始為哈夫曼樹增加的節點
//後面節點初始化;
int min1, min2;
for (i; i < n; i++) {
HT[i].parent = -1;
HT[i].lchild = -1;
HT[i].rchild = -1;
HT[i].wet = 0;
SelectTwoMinNode(HT, i, min1, min2);//選出權值最小的節點
//#if debug
//printf("#最小的兩個權值:%d %d#", min1, min2);
//#endif
HT[min1].parent = i;//
HT[min2].parent = i;
HT[i].lchild = min1;
HT[i].rchild = min2;
HT[i].wet = HT[min1].wet + HT[min2].wet;//該節點權值=兩個權值想加
#if debug
printf("第%d個節點權值為:%d\n", i, HT[i].wet);
#endif
}
return HT;
}
void Test(HuffTree HT,int amount) {
int n = amount * 2 - 1;
for (int i = 0; i < n; i++) {
printf("%d %d %d ", i, HT[i].wet, HT[i].parent);
printf("\n");
}
}
//先序遍歷將左子樹權值變為0,右子樹值變為1;
void ReverseRight(HuffTree HT, HuffTree Head) {//改變權值,為哈夫曼編碼做準備;
if (1) {
HT[Head->lchild].wet = 0;
//printf("1");
HT[Head->rchild].wet = 1;
if (Head->lchild == -1 && Head->rchild == -1)
return;
}
ReverseRight(HT, &HT[Head->lchild]);
ReverseRight(HT, &HT[Head->rchild]);
}
/*權值重新更改之後,在根據根節點到葉子節點所經過的路徑來編碼*/
void HuffManEncode(HuffTree HT,int amount) {
//從根節點開始遍歷。
int i;
for (i = 0; i < amount; i++) {//從陣列第一個元素開始尋找父節點
int father = HT[i].parent;
int j = i;//用j記錄當前節點
int *Code = (int*)malloc(amount * sizeof(int));//動態陣列,根據amount大小來
int k = amount - 1;
while (father != -1) {//其雙親節點不為根節點
*(Code + k) = HT[j].wet;
k--;
j = father;
father = HT[father].parent;
}
for (int j = k + 1; j < amount; j++) {
printf("%d", *(Code + j));
}
printf("\n");
free(Code);
}
}