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
posted @ 2020-05-10 21:17  miceputil  阅读(300)  评论(0)    收藏  举报