代码来自博主https://blog.csdn.net/sinat_28826891/article/details/79802739
自己添加了一些更详细注释。
#include "stdafx.h"
#define MAXSIZE 50
#include<string.h>
#include<stdio.h>
typedef struct
{
char data;/*结点值*/
int weight;/*权值*/
int parent;/*父结点*/
int left;/*左结点*/ //这里定义成int,只记录结点的下标,不指向结点
int right;/*右结点*/
int flag;/*标志位*/
}huffnode;
typedef struct/*结点编码结构体*/
{
char code[MAXSIZE];
int start;
}huffcode;
huffnode htree[2 * MAXSIZE];
huffcode hcode[MAXSIZE];
int select(int i)/*找出权值最小的结点*/
{
int k = 0x7fffffff;
int j, q;
for (j = 0; j <= i; j++)
if (htree[j].weight<k&&htree[j].flag == -1)
{
k = htree[j].weight;
q = j;
}
htree[q].flag = 1;/*将找到的结点标志位置1*/
return q;
}
void creat_hufftree(int n)/*创建哈夫曼树*/
{
int i, l, r;
for (i = 0; i<2 * n - 1; i++) //n个结点生成赫夫曼树之后结点就变成2*n-1
htree[i].parent = htree[i].left = htree[i].right = htree[i].flag = -1;/*均置-1*/
for (i = n; i<2 * n - 1; i++)
{
l = select(i - 1); /*找出权值最小的两个结点的下标*/
r = select(i - 1); //找第二个结点的时候flag已经变成了1,就会找到当前序列第二小的结点,并且把新合成的结点也加入进去参与计算了。
htree[l].parent = i; //他们的parent是下标为i的结点
htree[r].parent = i;
htree[i].weight = htree[l].weight + htree[r].weight;/*左右结点权值相加等于新结点的权值*/
htree[i].left = l;
htree[i].right = r;
}
}
void creat_huffcode(int n)/*求哈夫曼编码*/
{
int i, f, c;
huffcode d;
for (i = 0; i<n; i++)
{
d.start = n + 1; //d.start 一开始都是等于7的,然后一个一个往前面填01
c = i;
f = htree[i].parent;
while (f != -1) //根结点在creat的时候只是有了左右子树的值,没有父节点的值,父节点被初始化为-1了
{
if (htree[f].left == c)/*判断c是否是左子树*/
d.code[--d.start] = '0';/*左边编码为0*/ //位置从下往上移动,所以应该--,反过来才是编码的顺序
else
d.code[--d.start] = '1';/*右边编码为1*/
c = f;
f = htree[f].parent;
}
hcode[i] = d; //d是一个结构体,里面包含一个code的数组,数组里存着编码,现在把这个结构体赋给hcode[i],i即代表第i个结点
}
}
void display_huffcode(int n)/*输入各结点编码*/
{
int i, k;
printf("huffman is:\n");
for (i = 0; i<n; i++)
{
printf("%c:", htree[i].data);/*输出结点*/
for (k = hcode[i].start; k <= n; k++)
printf("%c", hcode[i].code[k]);/*输出每个结点对应的编码*/ //把code输出,start是从n开始往前填充的,start一直在--,已经减到了最开始的位置,正向输出就行
printf("\n");
}
}
void main()
{
int n = 6;
htree[0].data = 'a';
htree[0].weight = 45;
htree[1].data = 'b';
htree[1].weight = 13;
htree[2].data = 'c';
htree[2].weight = 12;
htree[3].data = 'd';
htree[3].weight = 16;
htree[4].data = 'e';
htree[4].weight = 9;
htree[5].data = 'f';
htree[5].weight = 5;
creat_hufftree(n);/*调用函数创建哈夫曼树*/
creat_huffcode(n);/*调用函数构造哈夫曼编码*/
display_huffcode(n);/*显示各结点哈夫曼编码*/
}
按照上面的数据搞了一张图出来
写赫夫曼树的时候也看了不同的人写的代码。这个代码相对来说非常简洁,有一点不同就是,他的parent,lchild,rchild并没有用指针连起来。只是在里面保存了下标。
开始那几个数据abcdef以及他们的weight是按照输入顺序没有动的。然后再连成一颗树的时候,通过保存下标的方法首先连接的是n+1到2n-1中最小的14,然后是25,30…最上面的100没有parent,有一个初始化了的-1。
还有就是把那个01的编码保存到数组的方式也很精妙,注释写了不赘述了。
以下是废话,不用看:
mlgb,最开始想要用最小堆实现最小优先级队列,在已有的叶节点上新建结点,insert,然后用最小优先级队列的extract-min实现连接,思路很简洁。but…写不出来啊,还搞了好久,最后向大佬的代码低头。结构体做成树还是数组的结构体那种,我真的写出来就报错,最重要的是,当你把功能模块化了之后,眼看着每块都能实现了。他连不起来啊。。。。这种去循环找到最小的两个值复杂度应该是o(n^2),最小堆的队列去找到o(nlgn)就行了。看了网上大家写的代码基本上没有用优先级队列去的。用了队列的是用的c++的algorithm的头文件里面一个sort queue。牛逼,看了一下C好像没有类似的功能。每次看别人的代码一来总是using namespace ,iostream等,用C++在写的人确实很多呢。也渐渐感到一些C++的优势,emm,伪工科的我只学过C语言呢,虽然两者很像,我基本也能看懂C++,但是我觉得还是有必要学习一下C++,特别是今天写赫夫曼的时候。马上就要上java的课了,不知道对这些有没有帮助。