樹和哈夫曼編碼。哈夫曼編碼是哈夫曼樹的一個應用。哈夫曼編碼應用廣泛,如JPEG中就應用了哈夫曼編碼。 首先介紹什麼是哈夫曼樹。哈夫曼樹又稱最優二叉樹,是一種帶權路徑長度最短的二叉樹。所謂樹的帶權路徑長度,就是樹中所有的葉結點的權值乘上其到根結點的 路徑長度(若根結點爲0層,葉結點到根結點的路徑長度爲葉結點的層數)。樹的帶權路徑長度記爲
WPL= (W1*L1+W2*L2+W3*L3+...+Wn*Ln),N個權值Wi(i=1,2,...n)構成一棵有N個葉結點的二叉樹,相應的葉結點的路徑長度爲Li(i=1,2,...n)。可以證明哈夫曼樹的WPL是最小的。
哈夫曼編碼步驟:
一、對給定的n個權值{W1,W2,W3,...,Wi,...,Wn}構成n棵二叉樹的初始集合F= {T1,T2,T3,...,Ti,...,Tn},其中每棵二叉樹Ti中只有一個權值爲Wi的根結點,它的左右子樹均爲空。(爲方便在計算機上實現算法,一般還要求以Ti的權值Wi的升序排列。)
二、在F中選取兩棵根結點權值最小的樹作爲新構造的二叉樹的左右子樹,新二叉樹的根結點的權值爲其左右子樹的根結點的權值之和。
三、從F中刪除這兩棵樹,並把這棵新的二叉樹同樣以升序排列加入到集合F中。
四、重複二和三兩步,直到集合F中只有一棵二叉樹爲止。
簡易的理解就是,假如我有A,B,C,D,E五個字符,出現的頻率(即權值)分別爲5,4,3,2,1,那麼我們第一步先取兩個最小權值作爲左右子樹構造一個新樹,即取1,2構成新樹,其結點爲1+2=3,如圖:
虛線爲新生成的結點,第二步再把新生成的權值爲3的結點放到剩下的集合中,所以集合變成{5,4,3,3},再根據第二步,取最小的兩個權值構成新樹,如圖:
再依次建立哈夫曼樹,如下圖:
其中各個權值替換對應的字符即爲下圖:
按照左子樹0,右子樹1,遍歷整棵樹,就可以將各葉子節點對應的編碼寫出來:
所以各字符對應的編碼爲:A->11, B->10, C->00, D->011, E->010
霍夫曼編碼是一種無前綴編碼。解碼時不會混淆。其主要應用在數據壓縮,加密解密等場合。
C語言代碼實現:
#include <string.h>
#include <stdio.h>
#define MAXVALUE 10000 /*定義最大權值*/
#define MAXLEAF 30 /*定義哈夫曼樹葉結點個數*/
#define MAXNODE MAXLEAF*2 -1
#define MAXBIT 100 /*定義哈夫曼編碼的最大長度*/
//--------------------------------------------------------------------//
typedef struct
{
int bit[MAXBIT];
int start;
} HCodeType;
typedef struct
{
int weight;
int parent;
int lchild;
int rchild;
int value;
} HNodeType;
//--------------------------------------------------------------------//
//C語言實現哈弗曼樹編碼
typedef struct
{
int bit[MAXBIT];
int start;
} HCODETYPE;
typedef struct
{
int weight;
int parent;
int lchild;
int rchild;
} HNODETYPE;
char *getcode1(char *s1, char *s2, char *s3) /*首先去掉電文中的空格*/
{
char temp[128] = "", *p, *q;
p = s1;
while ((q = strstr(p, s2)) != NULL)
{
*q = '\0';
strcat(temp, p);
strcat(temp, s3);
p = q + strlen(s2);
}
strcat(temp, p);
strcpy(s1, temp);
return s1;
}
/*再去掉重複出現的字符(即壓縮電文),提取哈夫曼樹葉結點*/
char * getcode(char *s1)
{
char s2[26], s5[26];
char temp[200] = "", *p, *q, *r, *s3 = "";
int m, e, n = 0;
m = strlen(s1);
while (m > 0)
{
p = s1;
s2[0] = s1[0];
s2[1] = '\0';
r = s2;
e = 0;
while ((q = strstr(p, r)) != NULL)
{
*q = '\0';
strcat(temp, p);
strcat(temp, s3);
p = q + strlen(s2);
e++;
}
m -= e;
strcat(temp, p);
strcpy(s1, temp);
s5[n] = s2[0];
n++;
strcpy(temp, "");
}
s5[n] = '\0';
strcpy(s1, s5);
printf(" 壓縮後的電文(即葉結點): %s\n", s1);
return s1;
}
HNODETYPE huffmantree(char *s2, char s3[])
{
HNODETYPE huffnode[MAXNODE];
HCODETYPE huffcode[MAXLEAF], cd;
int sum, i, j, n1, n2, x1, x2, p, k, c;
char s1[26] = { 'a','b','c','d','e','f','g','h','i','j','k','l','m',
'n','o','p','q','r','s','t','u','v','w','x','y','z' };
char s5[MAXLEAF];
int ww[26] = { 0 }, n = 0;
strcpy(s5, s2);
sum = strlen(s2);
for (i = 0; i < 26; i++) /*統計所有字符出現的頻度*/
{
for (j = 0; j < sum; j++)
{
if (s2[j] == s1[i])
{
ww[i]++;
}
}
}
n = strlen(s3);
for (i = 0; i < 2 * n - 1; i++)
{
huffnode[i].weight = 0;
huffnode[i].parent = -1;
huffnode[i].lchild = -1;
huffnode[i].rchild = -1;
}
for (i = 0; i < n; i++) /*分配給各葉結點權值*/
{
for (j = 0; j < 26; j++)
{
if (s3[i] == s1[j])
{
huffnode[i].weight = ww[j];
}
}
}
for (i = 0; i < n - 1; i++) /*構造哈夫曼樹*/
{
n1 = n2 = MAXVALUE;
x1 = x2 = 0;
for (j = 0; j < n + i; j++)
{
if (huffnode[j].parent == -1 && huffnode[j].weight < n1)
{
n2 = n1;
x2 = x1;
n1 = huffnode[j].weight;
x1 = j;
}
else if (huffnode[j].parent == -1 && huffnode[j].weight < n2)
{
n2 = huffnode[j].weight; x2 = j;
}
}
//
huffnode[x1].parent = n + i;
huffnode[x2].parent = n + i;
huffnode[n + i].weight = huffnode[x1].weight + huffnode[x2].weight;
huffnode[n + i].lchild = x1;
huffnode[n + i].rchild = x2;
}
for (i = 0; i < n; i++) /*求每個葉結點的哈夫曼編碼*/
{
cd.start = n - 1;
c = i;
p = huffnode[c].parent;
while (p != -1)
{
if (huffnode[p].lchild == c)
{
cd.bit[cd.start] = 0;
}
else
{
cd.bit[cd.start] = 1;
}
cd.start--;
c = p;
p = huffnode[c].parent;
}
for (j = cd.start + 1; j < n; j++)
{
huffcode[i].bit[j] = cd.bit[j];
}
huffcode[i].start = cd.start;
}
printf(" 各葉結點對應哈夫曼編碼 : ");/*輸出每個葉結點的哈夫曼編碼*/
for (i = 0; i < n; i++)
{
for (j = huffcode[i].start + 1; j < n; j++)
{
printf("%d", huffcode[i].bit[j]);
}
printf(" ");
}
printf("\n 電文的全部哈夫曼編碼 : ");/*輸出電文的全部哈夫曼編碼*/
for (k = 0; k < sum; k++)
{
for (i = 0; i < n; i++)
{
if (s2[k] == s3[i])
{
for (j = huffcode[i].start + 1; j < n; j++)
{
printf("%d", huffcode[i].bit[j]);
}
printf(" ");
}
}
}
printf("\n");
}
void main1()
{
char s1[MAXLEAF], s2[MAXLEAF];
printf("\n 請輸入電文 : ");
//gets(s1);
strcpy(s1, "a b c d e");
strcpy(s2, getcode1(s1, " ", ""));
huffmantree(s1, getcode(s2));
}
//--------------------------------------------------------------------//
//--------------------------------------------------------------------//
//
void HuffmanTree(HNodeType HuffNode[MAXNODE], int n)
{
int i, j, m1, m2, x1, x2;
char nvalue[5] = { 'A','B','C','D','E' };//葉子節點n
int qvalue[5] = {5, 4, 3, 2, 1};
//總共樹的節點爲2n-1
for (i = 0; i < 2 * n - 1; i++)
{
HuffNode[i].weight = 0;//權值
HuffNode[i].parent = -1;
HuffNode[i].lchild = -1;
HuffNode[i].rchild = -1;
HuffNode[i].value = i; //實際值,可根據情況替換爲字母
}
for (i = 0; i < n; i++)
{
//printf("Please input weight of leaf node %d: \n", i);
//scanf("%d", &HuffNode[i].weight);
HuffNode[i].weight = n - i;
HuffNode[i].value = nvalue[i];
printf("the weight of leaf node %d is %d: \n", i, HuffNode[i].weight);
}
for (i = 0; i < n - 1; i++)//構造哈弗曼樹
{
m1 = m2 = MAXVALUE;
x1 = x2 = 0;
for (j = 0; j < n + i; j++) //找出最小的節點權重
{
if (HuffNode[j].weight < m1 && HuffNode[j].parent == -1)
{
m2 = m1;
x2 = x1;
m1 = HuffNode[j].weight;
x1 = j;
}
else if (HuffNode[j].weight < m2 && HuffNode[j].parent == -1)
{
m2 = HuffNode[j].weight;
x2 = j;
}
}
HuffNode[x1].parent = n + i;
HuffNode[x2].parent = n + i;
HuffNode[n + i].weight = HuffNode[x1].weight + HuffNode[x2].weight;
HuffNode[n + i].lchild = x1;
HuffNode[n + i].rchild = x2;
printf("x1.weight and x2.weight in round %d: %d, %d\n", i + 1, HuffNode[x1].weight, HuffNode[x2].weight);
//printf("\n");
}
}
//解碼
void decodeing(char string[], HNodeType Buf[], int Num)
{
int i, tmp = 0, code[1024];
int m = 2 * Num - 1;
char *nump;
char num[1024];
for (i = 0; i < strlen(string); i++)
{
if (string[i] == '0')
{
num[i] = 0;
}
else
{
num[i] = 1;
}
}
i = 0;
nump = &num[0];
while (nump < (&num[strlen(string)]))
{
//從根節點遍歷哈弗曼樹
tmp = m - 1;
while ((Buf[tmp].lchild != -1) && (Buf[tmp].rchild != -1))
{
if (*nump == 0)
{
tmp = Buf[tmp].lchild;
}
else
{
tmp = Buf[tmp].rchild;
}
nump++;
}
printf("%d(%c)", Buf[tmp].value, Buf[tmp].value);
}
}
int main2(void)
{
HNodeType HuffNode[MAXNODE];
HCodeType HuffCode[MAXLEAF], cd;
int i, j, c, p, n;
char pp[100];
//printf("Please input n:\n");
//scanf("%d", &n);
n = 5;
printf("the node num of huffman tree is %d\n", n);
//哈弗曼樹
HuffmanTree(HuffNode, n);
//哈夫曼編碼
for (i = 0; i < n; i++)
{
cd.start = n - 1;
c = i;
p = HuffNode[c].parent;
while (p != -1)
{
if (HuffNode[p].lchild == c) {
cd.bit[cd.start] = 0;
} else {
cd.bit[cd.start] = 1;
}
//
cd.start--;
c = p;
p = HuffNode[c].parent;
}
for (j = cd.start + 1; j < n; j++)//
{
HuffCode[i].bit[j] = cd.bit[j];
}
HuffCode[i].start = cd.start;
}
for (i = 0; i < n; i++)//
{
printf("%d 's Huffman code is: ", i);
for (j = HuffCode[i].start + 1; j < n; j++)
{
printf("%d", HuffCode[i].bit[j]);
}
printf(" start:%d", HuffCode[i].start);
printf("\n");
}
printf("\n");
printf("Decoding? Please Enter code:\n");
scanf("%s", &pp);
decodeing(pp, HuffNode, n);
getchar();
return 0;
}
運行結果: