5.7 哈夫曼树及其应用
5.7.2 哈夫曼树的构造算法
c
#include <stdio.h>
#include <stdlib.h>
typedef struct
{
int weight;
int parent;
int lchild;
int rchild;
}HTNode, * HuffmanTree;
void CreateHuffmanTree(HuffmanTree& HT, int n);
void Select(HTNode* s, int n, int& s1, int& s2);
void OrderHuffmanTree(HuffmanTree& HT, int n);
int main()
{
HuffmanTree HT = NULL;
CreateHuffmanTree(HT, 8);
OrderHuffmanTree(HT, 8);
return 0;
}
void CreateHuffmanTree(HuffmanTree& HT, int n)
{
if (n < 1)
{
return;
}
//n = 1 时,只有一个结点,其权重为输入值,其双亲结点、左孩子、右孩子的结点下标均不存在(设为0).且n = 1时,程序无法生成其最优前缀编码(实际中用最短的0或1均可)。
int i = 0;
int m = 2 * n - 1;
int s1 = 0;
int s2 = 0;
HT = (HuffmanTree)malloc(sizeof(HTNode) * (m + 1));
//HT指向了一个一维数组的基地址,数组中的元素类型是HTNode,数组大小为m+1
//等价于HTNode HT[m+1]
//不说明数组大小时,为HTNode* HT,HuffmanTree HT
for (i = 1; i <= m; ++i)
{
HT[i].parent = 0;
HT[i].lchild = 0;
HT[i].rchild = 0;
}
for (i = 1; i <= n; i++)
{
printf("请输入第%d个叶子结点的权值:", i);
scanf_s("%d", &HT[i].weight);
}
/*-----------初始化工作结束,下面开始创建哈夫曼树----------*/
for (i = n + 1; i <= m; i++)
{
//选择
Select(HT, i - 1, s1, s2);
//删除
HT[s1].parent = i;
HT[s2].parent = i;
//合并
HT[i].weight = HT[s1].weight + HT[s2].weight;
HT[i].lchild = s1;
HT[i].rchild = s2;
}
}
//在大小为n的一维整数型数组s中,找到最小的两个整数,并将其坐标存储在变量s1和s2中,下标从1开始
void Select(HTNode* s, int n, int& s1, int& s2)
{
int m = 0; //最小的值,下标为s1
int p = 0; //第二小的值,下标为s2
int r = 1;
int t = 1;
//找到第一个双亲不为0的s[r]
while (s[r].parent != 0)
{
r++;
}
//找到第二个双亲不为0的s[t]
t = r + 1;
while (s[t].parent != 0)
{
t++;
}
//比较s[r]和s[t]
if (s[r].weight <= s[t].weight)
{
m = s[r].weight;
p = s[t].weight;
s1 = r;
s2 = t;
}
else
{
m = s[t].weight;
p = s[r].weight;
s1 = t;
s2 = r;
}
for (int k = 3; k <= n; k++)
{
if (s[k].parent == 0 && s[k].weight < m)
{
p = m;
s2 = s1;
m = s[k].weight;
s1 = k;
}
else if (s[k].parent == 0 && s[k].weight > m && s[k].weight < p)
{
p = s[k].weight;
s2 = k;
}
else
{
;
}
}
//将下标小的作为新结点的左孩子,将下标大的作为新结点的右孩子
int temp = 0;
if (s1 > s2)
{
temp = s1;
s1 = s2;
s2 = temp;
}
printf("\ns1 = %d, s2 = %d", s1, s2);
}
//遍历哈夫曼树(即遍历其HT数组各个结点元素的信息)
void OrderHuffmanTree(HuffmanTree& HT, int n)
{
int k = 0;
if (HT)
{
for (k = 1; k <= (2 * n - 1); k++)
{
printf("\n\nHT[%d].weight = %d, HT[%d].parent = %d, HT[%d].lchild = %d, HT[%d].rchild = %d ", k, HT[k].weight, k, HT[k].parent, k, HT[k].lchild, k, HT[k].rchild);
}
}
}
5.7.3 哈夫曼编码
c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct
{
int weight;
int parent;
int lchild;
int rchild;
}HTNode, * HuffmanTree;
typedef char** HuffmanCode;
void CreateHuffmanTree(HuffmanTree& HT, int n);
void Select(HTNode* s, int n, int& s1, int& s2);
void OrderHuffmanTree(HuffmanTree& HT, int n);
void CreatHuffmanCode(HuffmanTree HT, HuffmanCode& HC, int n);
int main()
{
HuffmanTree HT = NULL;
HuffmanCode HC = NULL;
int i = 0;
char* p = NULL;
CreateHuffmanTree(HT, 8);
OrderHuffmanTree(HT, 8);
CreatHuffmanCode(HT, HC,8);
printf("\n\n各权重对应的编码为:");
for (i = 1; i <= 8; i++)
{
printf("\n%d:", HT[i].weight);
for (p = HC[i]; *p != '\0'; p++)
{
printf("%c ", *p);
}
}
return 0;
}
void CreateHuffmanTree(HuffmanTree& HT, int n)
{
if (n <= 1)
{
return;
}
int i = 0;
int m = 2 * n - 1;
int s1 = 0;
int s2 = 0;
HT = (HuffmanTree)malloc(sizeof(HTNode) * (m + 1));
//HT指向了一个一维数组的基地址,数组中的元素类型是HTNode,数组大小为m+1
//等价于HTNode HT[m+1]
//不说明数组大小时,为HTNode* HT,HuffmanTree HT
for (i = 1; i <= m; ++i)
{
HT[i].parent = 0;
HT[i].lchild = 0;
HT[i].rchild = 0;
}
for (i = 1; i <= n; i++)
{
printf("请输入第%d个叶子结点的权值:", i);
scanf_s("%d", &HT[i].weight);
}
/*-----------初始化工作结束,下面开始创建哈夫曼树----------*/
for (i = n + 1; i <= m; i++)
{
//选择
Select(HT, i - 1, s1, s2);
//删除
HT[s1].parent = i;
HT[s2].parent = i;
//合并
HT[i].weight = HT[s1].weight + HT[s2].weight;
HT[i].lchild = s1;
HT[i].rchild = s2;
}
}
//在大小为n的一维整数型数组s中,找到最小的两个整数,并将其坐标存储在变量s1和s2中,下标从1开始
void Select(HTNode* s, int n, int& s1, int& s2)
{
int m = 0; //最小的值,下标为s1
int p = 0; //第二小的值,下标为s2
int r = 1;
int t = 1;
//找到第一个双亲不为0的s[r]
while (s[r].parent != 0)
{
r++;
}
//找到第二个双亲不为0的s[t]
t = r + 1;
while (s[t].parent != 0)
{
t++;
}
//比较s[r]和s[t]
if (s[r].weight <= s[t].weight)
{
m = s[r].weight;
p = s[t].weight;
s1 = r;
s2 = t;
}
else
{
m = s[t].weight;
p = s[r].weight;
s1 = t;
s2 = r;
}
for (int k = 3; k <= n; k++)
{
if (s[k].parent == 0 && s[k].weight < m)
{
p = m;
s2 = s1;
m = s[k].weight;
s1 = k;
}
else if (s[k].parent == 0 && s[k].weight > m && s[k].weight < p)
{
p = s[k].weight;
s2 = k;
}
else
{
;
}
}
//将下标小的作为新结点的左孩子,将下标大的作为新结点的右孩子
int temp = 0;
if (s1 > s2)
{
temp = s1;
s1 = s2;
s2 = temp;
}
printf("\ns1 = %d, s2 = %d", s1, s2);
}
//遍历哈夫曼树(即遍历其HT数组各个结点元素的信息)
void OrderHuffmanTree(HuffmanTree& HT, int n)
{
int k = 0;
if (HT)
{
for (k = 1; k <= (2 * n - 1); k++)
{
printf("\n\nHT[%d].weight = %d, HT[%d].parent = %d, HT[%d].lchild = %d, HT[%d].rchild = %d ", k, HT[k].weight, k, HT[k].parent, k, HT[k].lchild, k, HT[k].rchild);
}
}
}
//算法5.11 根据哈夫曼树求哈夫曼编码
void CreatHuffmanCode(HuffmanTree HT, HuffmanCode& HC, int n)
{
int i = 0;
int start = 0;
int c = 0;
int f = 0;
HC = (HuffmanCode)malloc(sizeof(char*) * (n + 1));
char* cd = (char*)malloc(sizeof(char) * (n));
cd[n - 1] = '\0';
for (i = 1; i <= n; ++i)
{
start = n - 1;
c = i; //结点c
f = HT[i].parent; //f为结点c的双亲
while (f != 0) //根结点与叶子结点及其它内部结点的区别
{
--start;
if (HT[f].lchild == c)
{
cd[start] = '0';
}
else //HT[f].rchild == c
{
cd[start] = '1';
}
c = f;
f = HT[f].parent;
}
HC[i] = (char*)malloc(sizeof(char) * (n - start));
strcpy_s(HC[i], n - start, &cd[start]);
/* strcpy_s(*a,strlen(b)+1,*b)将指针b开始指向的内容(包括空终止符'\0')复制到a指针
第一个参数:目标字符串指针 ;
第二个参数:要复制的字符串长度,可使用strlen()函数直接求出。切记在使用strlen()求出字符串长度时,因为不包括终止符'\0',因此需要+1;
第三个参数:待复制的字符串指针。 */
//由复制的长度n - start,这里复制的编码不包括终止符'\0',若要包括,长度再加一即可
}
free(cd);
}
哈夫曼译码
c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXSIZE 100
typedef struct
{
int weight;
int parent;
int lchild;
int rchild;
}HTNode, * HuffmanTree;
typedef char** HuffmanCode;
void CreateHuffmanTree(HuffmanTree& HT, int n);
void Select(HTNode* s, int n, int& s1, int& s2);
void OrderHuffmanTree(HuffmanTree HT, int n);
void CreateHuffmanCode(HuffmanTree HT, HuffmanCode& HC, int n);
void Decode(HuffmanTree HT,int n, char* s);
int getline(char s[], int limit);
int main()
{
HuffmanTree HT = NULL;
HuffmanCode HC = NULL;
int i = 0;
char* p = NULL;
char s[MAXSIZE];
int length = 0;
int num = 1;
char choice = '\0';
CreateHuffmanTree(HT, 8);
OrderHuffmanTree(HT, 8);
CreateHuffmanCode(HT, HC, 8);
printf("\n\n各权值对应的编码是:");
for (i = 1;i <= 8; i++)
{
printf("\n%d : ", HT[i].weight);
for (p = HC[i]; *p != '\0'; p++)
{
printf("%c", *p);
}
}
getchar(); //此处有一个回车需要回收
while (1)
{
printf("\n请输入第%d个哈夫曼编码:", num);
length = getline(s, MAXSIZE);
printf("该编码对应的权值是:");
Decode(HT, 8, s);
printf("\n是否继续?(y/n)");
scanf_s(" %c", &choice);
getchar();
num++;
if (choice != 'y' && choice != 'Y')
{
break;
}
}
return 0;
}
void CreateHuffmanTree(HuffmanTree& HT, int n)
{
if (n < 1)
{
return;
}
int i = 0;
int m = 2 * n - 1;
int s1 = 0;
int s2 = 0;
HT = (HuffmanTree)malloc(sizeof(HTNode) * (m + 1));
for (i = 1; i <= m; i++)
{
HT[i].parent = 0;
HT[i].lchild = 0;
HT[i].rchild = 0;
}
for (i = 1; i <= n; i++)
{
printf("请输入第%d个权值:", i);
scanf_s("%d", &HT[i].weight);
}
for (i = n+ 1; i <= m; i++)
{
//选择
Select(HT, i - 1, s1, s2);
//删除
HT[s1].parent = i;
HT[s2].parent = i;
//合并
HT[i].weight = HT[s1].weight + HT[s2].weight;
HT[i].lchild = s1;
HT[i].rchild = s2;
}
}
void Select(HuffmanTree HT, int n, int& s1, int& s2)
{
int m = 0; //最小值,下标是s1
int p = 0; //第二小值,下标是s2
int t = 1;
int r = 1;
int k = 1;
while (HT[t].parent != 0)
{
t++;
}
r = t + 1;
while (HT[r].parent != 0)
{
r++;
}
if (HT[t].weight <= HT[r].weight)
{
m = HT[t].weight;
s1 = t;
p = HT[r].weight;
s2 = r;
}
else
{
m = HT[r].weight;
s1 = r;
p = HT[t].weight;
s2 = t;
}
for (k = r+1; k <= n; k++)
{
if (HT[k].parent == 0 && HT[k].weight < m)
{
p = m;
s2 = s1;
m = HT[k].weight;
s1 = k;
}
else if(HT[k].parent == 0 && HT[k].weight > m && HT[k].weight < p)
{
p = HT[k].weight;
s2 = k;
}
else
{
;
}
}
if (s1 > s2)
{
int temp = s1;
s1 = s2;
s2 = temp;
}
}
void OrderHuffmanTree(HuffmanTree HT,int n)
{
if (HT)
{
for (int i = 1; i <= 2*n-1; i++)
{
printf("HT[%d].weight = %d, HT[%d].parent = %d, HT[%d].lchild = %d, HT[%d].rchild = %d\n",i,HT[i].weight,i,HT[i].parent,i,HT[i].lchild,i,HT[i].rchild);
}
}
}
void CreateHuffmanCode(HuffmanTree HT, HuffmanCode& HC, int n)
{
int i = 0;
int start = 0;
int c = 0;
int f = 0;
HC = (HuffmanCode)malloc(sizeof(char*) * (n + 1));
char* cd = (char*)malloc(sizeof(char) * n);
cd[n - 1] = '\0';
for (i = 1; i <= n; i++)
{
start = n - 1;
c = i;
f = HT[i].parent;
while (f != 0)
{
--start;
if (HT[f].lchild == c)
{
cd[start] = '0';
}
if (HT[f].rchild == c)
{
cd[start] = '1';
}
c = f;
f = HT[c].parent;
}
HC[i] = (char*)malloc(sizeof(char) * (n - start));
strcpy_s(HC[i], n - start, &cd[start]);
}
free(cd);
}
//译码
void Decode(HuffmanTree HT, int n,char *s)
{
int m = 2 * n - 1;
char *p = s;
int c = m; //从根节点开始往叶子结点走
for (p = s; *p != '\0'; p++)
{
//printf("\np = %c", *p);
if (*p == '0')
{
c = HT[c].lchild;
//printf("\nHT[c].weight = %d", HT[c].weight);
}
else if (*p == '1')
{
c = HT[c].rchild;
//printf("\nHT[c].weight = %d", HT[c].weight);
}
else
{
;
}
}
if (HT[c].lchild == 0 && HT[c].rchild == 0)
{
printf("%d", HT[c].weight);
}
else
{
printf("输入的编码有误。\n");
}
}
int getline(char s[], int limit)
{
int i = 0;
int c = 0;
for (i = 0; i < limit-1 && (c = getchar()) != EOF && c != '\n'; i++)
{
s[i] = c;
}
s[i] = '\0';
return i;
}