函数SelectTwoMin(int upbound, HuffmanTree HT, int &s1, int &s2)是从1到upbound中找出father为0的节点赋给s1,s2,(为了保证答案唯一,请让s1的节点编号小于s2),函数HuffmanCoding(HuffmanTree &HT, HuffmanCode &HC, int *w, int n)是构造哈夫曼树以及计算哈夫曼编码。保证输入的权重值小于1000。
函数接口定义:
void SelectTwoMin(int upbound, HuffmanTree HT, int &s1, int &s2);
void HuffmanCoding(HuffmanTree &HT, HuffmanCode &HC, int *w, int n);
其中 upbound
编号,HT
是哈夫曼树,HC
是哈夫曼编码,w
是权值,n
是叶子节点个数。
裁判测试程序样例:
#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 SelectTwoMin(int upbound, HuffmanTree HT, int &s1, int &s2);
void HuffmanCoding(HuffmanTree &HT, HuffmanCode &HC, int *w, int n);
int main() {
HuffmanTree ht;
HuffmanCode hc;
int n;
scanf("%d", &n);
int *w = (int *) malloc (n * sizeof(int));
for(int i = 0; i < n; ++ i)
scanf("%d", &w[i]);
HuffmanCoding(ht, hc, w, n);
for (int i = 1; i <= 2 * n - 1; ++ i) {
printf("%d %d %d %d\n",
ht[i].weight, ht[i].parent, ht[i].lchild, ht[i].rchild);
}
for (int i = 1; i <= n; ++ i)
printf("%s\n", hc[i]);
free(w);
free(ht);
for (int i = 1; i <= n; ++ i)
free(hc[i]);
return 0;
}
/* 你的代码将被嵌在这里 */
####输入格式: 第一行输入一个数n,表示叶子节点的个数,接下去输入n个整数,表示每个节点的值
####输出格式: 只要建树即可,输出已经确定了
输入样例:
4
1 2 3 4
输出样例:
1 5 0 0
2 5 0 0
3 6 0 0
4 7 0 0
3 6 1 2
6 7 3 5
10 0 4 6
110
111
10
0
这里直接上代码,思路全在代码的注释里。
// 逆置编码
void reverse(char *CH)
{
int n = strlen(CH);
for(int i = 0; i < n / 2; i++){
char temp;
temp = CH[i];
CH[i] = CH[n-i-1];
CH[n-i-1] = temp;
}
}
// 选出当前权值最小的两个结点
void SelectTwoMin(int upbound, HuffmanTree HT, int &s1, int &s2)
{
int x1 = 0;
int x2 = 0;
int w1 = 1000;
int w2 = 1000;
for(int i = 1; i <= upbound; i++){
if(HT[i].parent == 0 && HT[i].weight < w1){
x2 = x1;
w2 = w1;
x1 = i;
w1 = HT[i].weight;
}
else if(HT[i].parent == 0 && HT[i].weight < w2){
x2 = i;
w2 = HT[i].weight;
}
}
s1 = x1;
s2 = x2;
}
void HuffmanCoding(HuffmanTree &HT, HuffmanCode &HC, int *w, int n)
{
// 当前权值最小的两个结点s1,s2
int s1 = 0;
int s2 = 0;
// 给哈夫曼树和哈夫曼编码分配空间
HT = (HuffmanTree)malloc(sizeof(HTNode)*(2*n)); // 一共2*n-1个结点,这个分配2*n个结点的空间
HC = (char **)malloc(sizeof(char *)*(n+1)); // 一共n个叶节点,这里分配n+1个结点空间
// 初始化哈夫曼编码
for(int i = 1; i <= n; i++){
HC[i] = (char *)malloc(sizeof(char)*(n+1));
memset(HC[i], 0, sizeof(char)*(n+1));
}
// 初始化哈夫曼树
for(int i = 0; i < n; i++){ // 因只给叶节点编码,所以这个为n(n个叶结点)
HT[i+1].weight = w[i];
}
for(int i = 1; i <= 2*n-1; i++){ // 一共有2*n-1个结点
HT[i].parent = 0;
HT[i].lchild = 0;
HT[i].rchild = 0;
}
// 构建哈夫曼树
for(int i = 0; i < n - 1; i++){
SelectTwoMin(n+i, HT, s1, s2); // 在当前哈夫曼树中的n+i个有效结点中找出权值最小的两个结点(初始化时有n个叶结点,n~(2*n-1)为分支结点)
// 给第n+i+1个节点赋值
HT[n+i+1].lchild = s1;
HT[n+i+1].rchild = s2;
HT[n+i+1].weight = HT[s1].weight + HT[s2].weight;
HT[s1].parent = n+i+1;
HT[s2].parent = n+i+1;
}
// 根据哈夫曼树进行哈夫曼编码
for(int i = 1; i <= n; i++){
int c = i;
int parent = HT[c].parent;
while(parent){
if(HT[parent].lchild == c){
strncat(HC[i], "0", 1);
}else{
strncat(HC[i], "1", 1);
}
c = parent;
parent = HT[parent].parent;
}
reverse(HC[i]); // 因这里是从叶节点到根节点的编码,所以要使用reverse()函数来逆置编码使其变为哈夫曼编码
}
}
由于本人也是一名初学者,水平有限。这里参考了其他作者的代码,我只是将这位作者的代码按照自己的思路来理了一遍,希望能帮助像我一样的初学者能更好地理解哈夫曼树和哈夫曼编码的代码实现过程吧!
参考代码来自:https://blog.csdn.net/weixin_43843978/article/details/89451845