SCAU--8609--哈夫曼树
原题描述
8609 哈夫曼树
时间限制:1000MS 代码长度限制:10KB
提交次数:3178 通过次数:1263
题型: 编程题 语言: G++;GCC
Description 利用静态链表建立赫夫曼树,建树过程中要求左子树权值小于右子树权值,求各结点的编码。要求:叶子结点的个数n及结点值由键盘录入。本题给出程序代码,要求修改以满足测试要求.
#include "stdio.h"
#include "malloc.h"
#include "string.h"
typedef struct{
unsigned int weight;
unsigned int parent,lchild,rchild;
}HTNode,*HuffmanTree;
typedef char **HuffmanCode;
void Select(HuffmanTree &HT, int n, int &s1, int &s2)
//在HT[1..n]中选择parent为0且weight最小的两个结点,
// 其序号分别为s1和s2。
{
int i;
s1=-1;s2=-1;
for (i=1;i<=n;i++) {
if (HT[i].parent==0)
if (s1==-1) s1=i;
else
if (s2==-1 )
if (HT[i].weight0),构造哈夫曼树HT,
// 并求出n个字符的哈夫曼编码HC
int i, j, m, s1, s2, start;
char *cd;
unsigned int c, f;
if (n<=1) return;
m = 2 * n - 1;
HT = (HuffmanTree)malloc((m+1) * sizeof(HTNode)); // 0号单元未用
for (i=1; i<=n; i++) { //初始化
HT[i].weight=w[i-1];
HT[i].parent=0;
HT[i].lchild=0;
HT[i].rchild=0;
}
for (i=n+1; i<=m; i++) { //初始化
HT[i].weight=0;
HT[i].parent=0;
HT[i].lchild=0;
HT[i].rchild=0;
}
printf("\n哈夫曼树的构造过程如下所示:\n");
printf("HT初态:\n 结点 weight parent lchild rchild");
for (i=1; i<=m; i++)
printf("\n%4d%8d%8d%8d%8d",i,HT[i].weight,
HT[i].parent,HT[i].lchild, HT[i].rchild);
printf(" 按任意键,继续 ...");
getch();
for (i=n+1; i<=m; i++) { // 建哈夫曼树
// 在HT[1..i-1]中选择parent为0且weight最小的两个结点,
// 其序号分别为s1和s2。
Select(HT, i-1, s1, s2);
HT[s1].parent = i; HT[s2].parent = i;
HT[i].lchild = s1; HT[i].rchild = s2;
HT[i].weight = HT[s1].weight + HT[s2].weight;
printf("\nselect: s1=%d s2=%d\n", s1, s2);
printf(" 结点 weight parent lchild rchild");
for (j=1; j<=i; j++)
printf("\n%4d%8d%8d%8d%8d",j,HT[j].weight,HT[j].parent,HT[j].lchild, HT[j].rchild);
printf(" 按任意键,继续 ...");
getch();
}
//--- 从叶子到根逆向求每个字符的哈夫曼编码 ---
cd = (char *)malloc(n*sizeof(char)); // 分配求编码的工作空间
cd[n-1] = '\0'; // 编码结束符。
for (i=1; i<=n; ++i) { // 逐个字符求哈夫曼编码
start = n-1; // 编码结束符位置
for (c=i, f=HT[i].parent; f!=0; c=f, f=HT[f].parent)
// 从叶子到根逆向求编码
if (HT[f].lchild==c) cd[--start] = '0';
else cd[--start] = '1';
HC[i] = (char *)malloc((n-start)*sizeof(char));
// 为第i个字符编码分配空间
strcpy(HC[i], &cd[start]); // 从cd复制编码(串)到HC
}
free(cd); //释放工作空间
} //HuffmanCoding
void main()
{
int i,n;
int *w;
HuffmanTree HT;
HuffmanCode HC;
printf("Node Number:");
scanf("%d",&n); //权值个数
w=(int *)malloc(n*sizeof(int));
printf("Input weights:");
for ( i=0;i<n;i++) //录入权值
scanf("%d",&w[i]);
HC=(char **)malloc((n+1)*sizeof(char*)); //0空间未用
HT=(HuffmanTree)malloc((2*n+1+1)*sizeof(HTNode));//0空间未用
HuffmanCoding(HT, HC, w, n);
printf("\n");
for (i = 1; i<n+1; i++){
puts(HC[i]); //输出哈夫曼编码
free(HC[i]); //释放空间
}
free(HC);
free(HT);
}//main
输入格式
第一行:权值个数
第二行:输入n个权值,用空格分隔
输出格式
输出n行
每行表示各权值对应的哈夫曼编码
输入样例
8
5 29 7 8 14 23 3 11
输出样例
0001
10
1110
1111
110
01
0000
001
思路
该题目属于填空题的类别。题目已经给出了大部分代码,剩下的需要自己琢磨。
需要解决的点:
- 题目中有过多的辅助性的printf语句,需要删除才能打印出题目需要的答案
- 完成Select函数的编写
- 分割Select函数和HuffmanCoding函数
由题可见,HuffmanCoding函数中运用了Select函数(用来挑选输入数据中paren值为0,且数值最小的两个数据)。即Select函数可以转换为求一个数组中第一小和第二小数的问题。我最初的想法是用两个for分别求两个值,但题目只运用一个for即解决问题。for部分如下
for (i=1;i<=n;i++) {
if (HT[i].parent==0)
if (s1==-1) s1=i;
else
if (s2==-1 )
if (HT[i].weight0),构造哈夫曼树HT, //其实这里有问题,和0无关
其实这里排版有点问题
for (i = 1; i <= n; i++)
{
if (HT[i].parent == 0)
{
if (s1 == -1) s1=i;
else if (s2 == -1 || HT[i].weight???)s2=i; //这里第二个条件即原来的最后一个if
}
}
显然,当前节点的parent为0时,进入第一个if范围,继续判断。
Select的作用是s1,s2作为最小值的下标记录变量,且初始化为-1后(数组中不存在下标),开始不断更新s1和s2的直到找到最小节点的下标。在这里,一旦进入for循环后,如果s1和s2的值还为-1则需要更新s1和s2的值,因为对于一个不存在的值无法与数组其他节点比较,总需要赋予一个初始值。且当找到一个更小值后,需要更新一次s1和s2。
且为了保证s1比s2小,应该让s1先去比较,再和s2比较,就会出现以下情况(被比的第三量用x表示):
1. x <= s1 < s2 //当出现这种情况,s1更新为x节点下标,s2=s1
2. s1 < x <=s2 //当出现这种情况,s1不变,更新s2
3. s1 < s2 <= x //当出现这种情况,s1和s2不变
修改后,for循环如下
for (i = 1; i <= n; i++) //在1~n这个区间找parent==0且weight最小的两个节点,分别记为s1,s2
{
if (HT[i].parent == 0) //满足第一个条件,parent==0
{
if (s1 == -1 || HT[s1].weight > HT[i].weight ) {s2=s1;s1=i;}
else if (s2 == -1 || HT[s2].weight > HT[i].weight) s2=i;
}
}
完整AC代码
#include <cstdio>
#include <cstdlib>
#include <cstring>
typedef struct{
unsigned int weight;
unsigned int parent,lchild,rchild;
}HTNode,*HuffmanTree;
typedef char **HuffmanCode;
void Select(HuffmanTree &HT, int n, int &s1, int &s2)
//在HT[1..n]中选择parent为0且weight最小的两个结点,
// 其序号分别为s1和s2。
{
int i;
s1 = -1;
s2 = -1;
for (i = 1; i <= n; i++) //在1~n这个区间找parent==0且weight最小的两个节点,分别记为s1,s2
{
if (HT[i].parent == 0) //满足第一个条件,parent==0
{
if (s1 == -1 || HT[s1].weight > HT[i].weight ) {s2=s1;s1=i;}
else if (s2 == -1 || HT[s2].weight > HT[i].weight) s2=i;
}
}
}
void HuffmanCoding(HuffmanTree &HT, HuffmanCode &HC, int* w, int n)
{
// 并求出n个字符的哈夫曼编码HC
int i, j, m, s1, s2, start;
char *cd;
unsigned int c, f;
if (n<=1) return;
m = 2 * n - 1;
HT = (HuffmanTree)malloc((m+1) * sizeof(HTNode)); // 0号单元未用
for (i=1; i<=n; i++)
{ //初始化
HT[i].weight=w[i-1]; //前n-1个单元都是初始就知道的值,这些值带权重
HT[i].parent=0;
HT[i].lchild=0;
HT[i].rchild=0;
}
for (i=n+1; i<=m; i++)
{ //初始化
HT[i].weight=0; //后n个单元需要根据前n-1个单元计算得出,初始时没有权重,
HT[i].parent=0;
HT[i].lchild=0;
HT[i].rchild=0;
}
// 建哈夫曼树
// 在HT[1..i-1]中选择parent为0且weight最小的两个结点,
// 其序号分别为s1和s2。
for (i=n+1; i<=m; i++) //计算后n个空单元
{
Select(HT, i-1, s1, s2);
HT[s1].parent = i; HT[s2].parent = i;
HT[i].lchild = s1; HT[i].rchild = s2;
HT[i].weight = HT[s1].weight + HT[s2].weight;
}
//--- 从叶子到根逆向求每个字符的哈夫曼编码 ---
cd = (char *)malloc(n*sizeof(char)); // 分配求编码的工作空间
cd[n-1] = '\0'; // 编码结束符。
for (i=1; i<=n; ++i)
{ // 逐个字符求哈夫曼编码
start = n-1; // 编码结束符位置
for (c=i, f=HT[i].parent; f!=0; c=f, f=HT[f].parent)
// 从叶子到根逆向求编码
if (HT[f].lchild==c) cd[--start] = '0';
else cd[--start] = '1';
HC[i] = (char *)malloc((n-start)*sizeof(char));
// 为第i个字符编码分配空间
strcpy(HC[i], &cd[start]); // 从cd复制编码(串)到HC
}
free(cd); //释放工作空间
} //HuffmanCoding
int main()
{
int i,n;
int *w;
HuffmanTree HT;
HuffmanCode HC;
scanf("%d",&n); //权值个数
w=(int *)malloc(n*sizeof(int));
for ( i=0;i<n;i++) //录入权值
scanf("%d",&w[i]);
HC=(char **)malloc((n+1)*sizeof(char*)); //0空间未用
HT=(HuffmanTree)malloc((2*n+1+1)*sizeof(HTNode));//0空间未用
HuffmanCoding(HT, HC, w, n);
for (i = 1; i<n+1; i++){
puts(HC[i]); //输出哈夫曼编码
free(HC[i]); //释放空间
}
free(HC);
free(HT);
}//main

浙公网安备 33010602011771号