這里講的哈夫曼樹有創建哈夫曼樹,輸出哈夫曼樹,遞歸進行哈夫曼樹編碼,哈夫曼解碼這些功能。
1.創建哈夫曼樹:(函數參數為整型數組)
(1)引入哈夫曼樹指針數組並申請空間,為每棵哈夫曼樹復制,將其左右節點賦值為NULL。
(2)將(n-1)棵哈夫曼樹合並:a.引入兩個整形變量始終代表最小和次小的下標
b.比較權值,使兩個下標成為最小的兩個權值的下標
c.合並一次,並將最小下標的哈夫曼樹賦值為新的哈夫曼樹,次小下表的哈夫曼樹賦值為空。
2.輸出哈夫曼樹:根節點不為空,輸出權重,若左右子樹不為空,依次輸出“(”, 左子樹,“,”,右子樹,“)”
3.遞歸進行哈夫曼編碼:由於哈夫曼樹的特點是所有的原始樹都為葉子節點,故可以用這方法來進行編碼。
4.哈夫曼解碼:while(已解碼長度<字符串長度){找到某一葉子節點並打印}
以下是完整代碼:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef int ELEMTYPE;
// 哈夫曼樹結點結構體
typedef struct HuffmanTree
{
ELEMTYPE weight;
ELEMTYPE id; // id用來主要用以區分權值相同的結點,這里代表了下標
struct HuffmanTree* lchild;
struct HuffmanTree* rchild;
}HuffmanNode;
// 構建哈夫曼樹
HuffmanNode* createHuffmanTree(int* a, int n)
{
int i, j;
HuffmanNode **temp, *hufmTree;
temp = (HuffmanNode **)malloc(n*sizeof(HuffmanNode));
for (i=0; i<n; ++i) // 將數組a中的權值賦給結點中的weight
{
temp[i] = (HuffmanNode*)malloc(sizeof(HuffmanNode));
temp[i]->weight = a[i];
temp[i]->id = i;
temp[i]->lchild = temp[i]->rchild = NULL;
}
for (i=0; i<n-1; ++i) // 構建哈夫曼樹需要n-1合並
{
int small1=-1, small2; // small1、small2分別作為最小和次小權值的下標
for (j=0; j<n; ++j) // 先將最小的兩個下標賦給small1、small2(注意:對應權值未必最小)
{
if (temp[j] != NULL && small1==-1)
{
small1 = j;
continue;
} else if(temp[j] != NULL)
{
small2 = j;
break;
}
}
for (j=small2; j<n; ++j) // 比較權值,挪動small1和small2使之分別成為最小和次小權值的下標
{
if (temp[j] != NULL)
{
if (temp[j]->weight < temp[small1]->weight)
{
small2 = small1;
small1 = j;
} else if (temp[j]->weight < temp[small2]->weight)
{
small2 = j;
}
}
}
hufmTree = (HuffmanNode*)malloc(sizeof(HuffmanNode));
hufmTree->weight = temp[small1]->weight + temp[small2]->weight;
hufmTree->lchild = temp[small1];
hufmTree->rchild = temp[small2];
temp[small1] = hufmTree;
temp[small2] = NULL;
}
free(temp);
return hufmTree;
}
// 以廣義表的形式打印哈夫曼樹
void PrintHuffmanTree(HuffmanNode* hufmTree)
{
if (hufmTree)
{
printf("%d", hufmTree->weight);
if (hufmTree->lchild != NULL || hufmTree->rchild != NULL)
{
printf("(");
PrintHuffmanTree(hufmTree->lchild);
printf(",");
PrintHuffmanTree(hufmTree->rchild);
printf(")");
}
}
}
// 遞歸進行哈夫曼編碼
void HuffmanCode(HuffmanNode* hufmTree, int depth) // depth是哈夫曼樹的深度
{
static int code[10];
if (hufmTree)
{
if (hufmTree->lchild==NULL && hufmTree->rchild==NULL)
{
printf("id為%d權值為%d的葉子結點的哈夫曼編碼為 ", hufmTree->id, hufmTree->weight);
int i;
for (i=0; i<depth; ++i)
{
printf("%d", code[i]);
}
printf("\n");
} else
{
code[depth] = 0;
HuffmanCode(hufmTree->lchild, depth+1);
code[depth] = 1;
HuffmanCode(hufmTree->rchild, depth+1);
}
}
}
// 哈夫曼解碼
void HuffmanDecode(char ch[], HuffmanNode* hufmTree, char string[]) // ch是要解碼的01串,string是結點對應的字符
{
int i;
int num[100];
HuffmanNode* tempTree = NULL;
for (i=0; i<strlen(ch); ++i)
{
if (ch[i] == '0')
num[i] = 0;
else
num[i] = 1;
}
if(hufmTree)
{
i = 0; // 計數已解碼01串的長度
while(i<strlen(ch))
{
tempTree = hufmTree;
while(tempTree->lchild!=NULL && tempTree->rchild!=NULL)
{
if (num[i] == 0)
{
tempTree = tempTree->lchild;
} else
{
tempTree = tempTree->rchild;
}
++i;
}
printf("%c", string[tempTree->id]); // 輸出解碼后對應結點的字符
}
}
}
int main()
{
int i, n;
printf("請輸入葉子結點的個數:\n");
while(1)
{
scanf("%d", &n);
if (n>1)
break;
else
printf("輸入錯誤,請重新輸入n值!");
}
int* arr;
arr=(int*)malloc(n*sizeof(ELEMTYPE));
printf("請輸入%d個葉子結點的權值:\n", n);
for (i=0; i<n; ++i)
{
scanf("%d", &arr[i]);
}
char ch[100], string[100];
printf("請連續輸入這%d個葉子結點各自所代表的字符:\n", n);
fflush(stdin); // 強行清除緩存中的數據,也就是上面輸入權值結束時的回車符
gets(string);
HuffmanNode* hufmTree = NULL;
hufmTree = createHuffmanTree(arr, n);
printf("此哈夫曼樹的廣義表形式為:\n");
PrintHuffmanTree(hufmTree);
printf("\n各葉子結點的哈夫曼編碼為:\n");
HuffmanCode(hufmTree, 0);
printf("要解碼嗎?請輸入編碼:\n");
gets(ch);
printf("解碼結果為:\n");
HuffmanDecode(ch, hufmTree, string);
printf("\n");
free(arr);
free(hufmTree);
return 0;
}