思路:
利用结构体数组,结构体中存放parent(双亲),weight(权值),lchild(左孩子),rchild(右孩子);注:0位置不使用。
初始化哈夫曼树
首先我们从主函数中传入n个叶子结点的权值(也就是我们存放的n个数值,存放数组传递),初始化哈夫曼树时,我们需要将n个叶子结点的权值分别赋值,parent、lchild、rchild全部赋值为0;
因为我们存放n个字符,结构体数组的0位置还不使用,总的数据结点总数本该为2*n-1,现在数组的大小需要+1。
我们使用1~ n存放叶子结点,n+1 ~ 2n存放非叶子结点,非叶子结点四个参数初始时全部赋值为0;
我们利用循环操作,查找出结构体数组中两个权值最小的两个,将两个权值相加得到新的权值,然后改变数组n位置之后的权值(最小两权值的和),lchild,rchild分别存放两个最小权值的位置,(所以我们写的选择函数需要返回两个最小权值的结点的位置),改变两个最小权值结点的parent,将新得到的位置进行赋值操作;
代码实现:
void init_huffmantree(HuffmanTree ht, int w[], int n) //因为0单位不使用,所以需要建立ht[M+1],使用w[]存放n个权重值;
{
int i;
for(i=1; i<=n; i++) //1~n存放叶子节点
{
ht[i].weight = w[i-1];
ht[i].parent = 0;
ht[i].lchild = 0;
ht[i].rchild = 0;
}
int m = 2 * n - 1;
for(i=n+1; i<=m ;i++) //n+1~m用来存放非叶子节点
{
ht[i].weight = 0;
ht[i].parent = 0;
ht[i].lchild = 0;
ht[i].rchild = 0;
}
int s1,s2;
for(i=n+1; i<=m; i++)
{
Select(ht, i-1, &s1, &s2);
ht[i].weight = ht[s1].weight + ht[s2].weight;
ht[i].lchild = s1;
ht[i].rchild = s2;
ht[s1].parent = i;
ht[s2].parent = i;
}
}
编写选择函数
我们需要选择结构体数组中1~最新的到的结点之间的权值(weight)中挑选两个最小的【大前提是必须是parent==0的结点,因为parent!=0结点都已经有了双亲】,所以需要四个参数【1.数组,2.遍历循环挑选的次数,3.返回的第一个序号,4.返回的第二个序号】
代码实现:
void Select(HuffmanTree ht, int n, int *s1, int *s2)//n表示叶子节点数,因为需要返回两个最小权重值的序号,所以需要两个指针用来接收返回;
{
int i, min1 = MAX, min2 = MAX;
*s1 = 0;
*s2 = 0;
for(i=1;i<=n;i++)
{
if(ht[i].parent == 0)
{
if(ht[i].weight < min1)
{
min2 = min1;
*s2 = *s1;
min1 = ht[i].weight;
*s1 = i;
}
else if(ht[i].weight < min2)
{
min2 = ht[i].weight;
*s2 = i;
}
}
}
}
最后便是存码,printf 输出(相当于解码)
由上至下,我们使用结点位于一层左节点一次,添加一个0,反之右节点添加一个1;
该函数的参数有三个【1.结构体数组,2.由主函数传入的存放字符的数组,3.最大哈夫曼树的深度】,因为我使用的是每一个字符的密码使用一个数组进行存放,解码相当于遍历输出数组,所以需要第三个参数限制数组的遍历的次数,此方法需要每次进行清空数组操作;
这里我是用倒推哈夫曼树,每一次循环都从叶子节点推至根节点
代码实现:
void create_huffmantree(HuffmanTree ht, char s[], int n) //s[]用来接收main函数传入的字符,这里我们使用变长编码;
{
char *HFM = (char *)malloc(sizeof(char) * n);
for(int i=1;i<=n;i++)
{
int start = n-1, c = i, p = ht[i].parent;
while(p != 0)
{
--start;
if(ht[p].lchild == c)
{
HFM[start] = '0'; //左孩子为0
}
else
{
HFM[start] = '1';//右孩子为1
}
c = p;
p = ht[p].parent; //向上到推
}
printf(" %c 的哈夫曼编码为:", s[i-1]);
for(int j=0; j<n; j++) //输出哈夫曼编码
{
if(HFM[j] == '0' || HFM[j] == '1')
{
printf("%c",HFM[j]);
}
}
printf("\n");
memset(HFM, -1, n); //每一次都清空数组
}
}
全部代码:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define N 30 //定义最大叶子节点个数
#define M 2 * N - 1 //定义结点的最大值
#define MAX 999999
typedef struct{
int parent;
int weight;
int lchild;
int rchild;
}HTNode,HuffmanTree[M+1]; //HuffmanTree是结构体数组,0单位不用
void Select(HuffmanTree ht, int n, int *s1, int *s2)//n表示叶子节点数,因为需要返回两个最小权重值的序号,所以需要两个指针用来接收返回;
{
int i, min1 = MAX, min2 = MAX;
*s1 = 0;
*s2 = 0;
for(i=1;i<=n;i++)
{
if(ht[i].parent == 0)
{
if(ht[i].weight < min1)
{
min2 = min1;
*s2 = *s1;
min1 = ht[i].weight;
*s1 = i;
}
else if(ht[i].weight < min2)
{
min2 = ht[i].weight;
*s2 = i;
}
}
}
}
//哈夫曼树初始化操作 ,建立哈夫曼编码
void init_huffmantree(HuffmanTree ht, int w[], int n) //因为0单位不使用,所以需要建立ht[M+1],使用w[]存放n个权重值;
{
int i;
for(i=1; i<=n; i++) //1~n存放叶子节点
{
ht[i].weight = w[i-1];
ht[i].parent = 0;
ht[i].lchild = 0;
ht[i].rchild = 0;
}
int m = 2 * n - 1;
for(i=n+1; i<=m ;i++) //n+1~m用来存放非叶子节点
{
ht[i].weight = 0;
ht[i].parent = 0;
ht[i].lchild = 0;
ht[i].rchild = 0;
}
int s1,s2;
for(i=n+1; i<=m; i++)
{
Select(ht, i-1, &s1, &s2);
ht[i].weight = ht[s1].weight + ht[s2].weight;
ht[i].lchild = s1;
ht[i].rchild = s2;
ht[s1].parent = i;
ht[s2].parent = i;
}
}
void create_huffmantree(HuffmanTree ht, char s[], int n) //s[]用来接收main函数传入的字符,这里我们使用变长编码;
{
char *HFM = (char *)malloc(sizeof(char) * n);
for(int i=1;i<=n;i++)
{
int start = n-1, c = i, p = ht[i].parent;
while(p != 0)
{
--start;
if(ht[p].lchild == c)
{
HFM[start] = '0'; //左孩子为0
}
else
{
HFM[start] = '1';//右孩子为1
}
c = p;
p = ht[p].parent; //向上到推
}
printf(" %c 的哈夫曼编码为:", s[i-1]);
for(int j=0; j<n; j++) //输出哈夫曼编码
{
if(HFM[j] == '0' || HFM[j] == '1')
{
printf("%c",HFM[j]);
}
}
printf("\n");
memset(HFM, -1, n); //每一次都清空数组
}
}
int main(){
HuffmanTree ht;
int i,w[4] = {1, 2, 3, 4}; //权值
char str[4] = {'c', 'h', 'e', 'n'};// 叶子节点的字符
init_huffmantree(ht, w, 4);
printf("哈夫曼树的所有结点值:\n");
for(i=1;i<=7;i++)
{
printf("%d ", ht[i].weight);
}
printf("\n");
create_huffmantree(ht, str, 4);
return 0;
}