数据结构(C语言)实验七:哈夫曼树与哈夫曼编码

一、实验目的

熟练掌握huffman树的构建方法以及huffman编码。

二、预备知识

1. 哈夫曼树的存储结构

typedef struct
{
    unsigned int weight;
    unsigned int parent, lchild, rchild;
}HTNode,*HuffmanTree;     //动态分配数组存储哈夫曼树

2. 哈夫曼编码的存储结构

typedef char  * *HuffmanCode; //动态分配数组存储哈夫曼编码表 

三、实验题目

从键盘接收任意一个字符串。以字符串中某字符出现的次数,作为该字符的权值。利用得到的权值构造huffman树、并输出每个字符对应的huffman编码。

四、实验要求

1)哈夫曼树和哈夫曼编码数据类型定义、select()函数(求两最小权值结点)、哈夫曼树构造、求编码函数、字符串输入处理函数等的声明放在HuffmanDef.h文件;

2)相关预编译命令等放在公共头文件CommonDef.h文件;

3)select()函数、哈夫曼树构造函数、哈夫曼编码函数的实现可放在Huffman.c文件;

4)测试程序放在HuffmanTestApp.c中。

五、需求分析

本实验通过C语言来实现huffman树的构建方法以及huffman编码。

  1. 从键盘接收任意一个字符串。以字符串中某字符出现的次数,作为该字符的权值。利用得到的权值构造huffman树、并输出每个字符对应的huffman编码。

本程序没有边界约束,违反输入规则的均会导致程序运行失败。

六、概要设计

1.抽象数据类型的定义

哈夫曼树的存储结构

typedef struct
{
    unsigned int weight;
    unsigned int parent, lchild, rchild;
}HTNode,*HuffmanTree;     //动态分配数组存储哈夫曼树

哈夫曼编码的存储结构

typedef char  * *HuffmanCode;//动态分配数组存储哈夫曼编码表

2.主程序的流程图

3.程序各功能模块调用关系图

七、详细设计

1.程序开始预编译部分如下:

//头文件
#include <stdio.h>
#include "HuffmanDef.h"
#include <stdlib.h>
#include <string.h>

#define ARRAY_MAX_SIZE 100//存储输入字符的最大数
//定义类型
typedef struct {
    unsigned int weight;
    unsigned int parent, lchild, rchild;
} HTNode, *HuffmanTree;     //动态分配数组存储哈夫曼树
typedef char **HuffmanCode;//动态分配数组存储哈夫曼编码表

2.程序主要功能函数如下:

void Select(HuffmanTree HT, int a, int *p1, int *p2);//Select函数,选出HT树到a为止,权值最小且parent为0的2个节点
void HuffmanCoding(HuffmanTree *HT, HuffmanCode *HC);//构建哈夫曼树HT,并求出哈夫曼编码HC
int InputString(int (*ch)[ARRAY_MAX_SIZE], int (*num)[ARRAY_MAX_SIZE]);//统计输入的字符和个数,默认用静态分配内存空间
void test();//测试函数

3.函数调用关系图

八、调试分析

  1. 将哈夫曼树的构建和编码合成到一个函数HuffmanCoding中,其实是可以分开的,但我考虑到,哈夫曼编码时需要哈夫曼树的节点数,我又不想传参,也不想单独写一个测结点数量的函数,还有构建树和编码有很多参数都会被重复用到,所以就合并成一个函数了,主要是方便了自己,不需要重构代码,偷了个懒。
  2. 测输入字符串中字符的种类和数量(权值),我采用两个数组来存储,一个存放字符,一个存放字符的权值,两个数组的下标一一对应。

为啥使用数组存储?偷个懒,可以用动态分配存储空间。

为啥用两个数组?一是为了方便储存,二更主要是考虑到,编码大部分输入的字符串会很长,使用两个数组可以在测数量时循环只做一次,牺牲空间来换取时间。

默认两对数组(存储字符串信息的两个数组和HT、HC)第0位置不使用的原因是:在构建哈夫曼树时要做很多循环,主要还是为了对好结点的下标,第一结点就是数组的第一元素,增强代码的可读性,也怕自己搞混,同样的,HT和HC的第0位置的存储空间也是默认不使用的,统一下标。

九、测试数据与结果

本次实验没有考虑到输入边界的问题,本次实验的主要目的是实验哈夫曼编码的功能,故没有测试环节,下面是功能实现。

1.运行开始

2.输入字符串(以回车键结束):

aaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbcccccccddddddddeeeeeeeeeeeeeefffffffffffffffffffffffggghhhhhhhhhhh+回车键

附录——源代码清单

CommonDef.h

#include <stdio.h>
#include "HuffmanDef.h"
#include <stdlib.h>
#include <string.h>

HuffmanDef.h

#define ARRAY_MAX_SIZE 100//存储输入字符的最大数
typedef struct {
    unsigned int weight;
    unsigned int parent, lchild, rchild;
} HTNode, *HuffmanTree;     //动态分配数组存储哈夫曼树
typedef char **HuffmanCode;//动态分配数组存储哈夫曼编码表
extern void Select(HuffmanTree HT, int a, int *p1, int *p2);//Select函数,选出HT树到a为止,权值最小且parent为0的2个节点
extern void HuffmanCoding(HuffmanTree *HT, HuffmanCode *HC);//构建哈夫曼树HT,并求出哈夫曼编码HC
extern int InputString(int (*ch)[ARRAY_MAX_SIZE], int (*num)[ARRAY_MAX_SIZE]);//统计输入的字符和个数,默认用静态分配内存空间
extern void test();//测试函数

Huffman.c

#include "CommonDef.h"

int InputString(int (*ch)[ARRAY_MAX_SIZE], int (*num)[ARRAY_MAX_SIZE]) {//统计输入的字符和个数,默认用静态分配内存空间
    int length = 0;
    int i;
    int c;//输入的字符
    printf("请输入要编码的字符串:\n");
    while ((c = getchar()) != '\n') {
        for (i = 0; i < length; ++i) {
            if (c == (*ch)[i + 1]) {
                (*num)[i + 1] += 1;
                break;
            }
        }
        if (i == length) {
            (*ch)[length + 1] = c;
            (*num)[length + 1] = 1;
            length++;
        }
    }
    printf("字符对应的权值如下:\n");
    for (i = 1; i < length + 1; ++i)
        printf("%c  %d\n", (*ch)[i], (*num)[i]);
    return length;
}

void Select(HuffmanTree HT, int a, int *p1, int *p2) {//Select函数,选出HT树到a为止,权值最小且parent为0的2个节点
    int i, j, x, y, count, temp;
    for (j = 1, count = 1; j <= a; j++) {
        if (HT[j].parent == 0) {
            if (count == 1)
                x = j;
            if (count == 2)
                y = j;
            count++;
        }
        if (count > 2)
            break;
    }
    if (HT[x].weight > HT[y].weight)//令x结点权值小于y结点权值
    {
        temp = y;
        y = x;
        x = temp;
    }
    i = (x > y ? x : y) + 1;
    while (i <= a) {
        if (HT[i].parent == 0) {
            if (HT[i].weight < HT[x].weight) {
                y = x;
                x = i;
            } else {
                if (HT[i].weight >= HT[x].weight && HT[i].weight < HT[y].weight)
                    y = i;
            }
        }
        i++;
    }
    *p1 = HT[x].weight <= HT[y].weight ? x : y;
    *p2 = HT[x].weight > HT[y].weight ? x : y;
}

void HuffmanCoding(HuffmanTree *HT, HuffmanCode *HC)//构建哈夫曼树HT,并求出n个字符的哈夫曼编码HC
{
    int i, start, c, f, m;
    int length;//字符串长度
    int ch[ARRAY_MAX_SIZE];//存储输入的字符数组
    int num[ARRAY_MAX_SIZE];//存储输入的字符的个数的数组
    int p1, p2;
    char *cd;
    length = InputString(&ch, &num);
    if (length <= 1)
        exit(1);
    m = 2 * length - 1;//n个叶子结点的哈夫曼树共有2n-1个结点
    *HT = (HuffmanTree) malloc((m + 1) * sizeof(HTNode));//0号单元未使用
    for (i = 1; i <= length; i++)//初始化n个叶子结点
    {
        (*HT)[i].weight = num[i];
        (*HT)[i].parent = 0;
        (*HT)[i].lchild = 0;
        (*HT)[i].rchild = 0;
    }
    for (i = length + 1; i <= m; i++)//初始化其余结点
    {

        (*HT)[i].weight = 0;
        (*HT)[i].parent = 0;
        (*HT)[i].lchild = 0;
        (*HT)[i].rchild = 0;
    }
    for (i = length + 1; i <= m; i++)//建立哈夫曼树
    {
        Select((*HT), i - 1, &p1, &p2);
        (*HT)[p1].parent = i;
        (*HT)[p2].parent = i;
        (*HT)[i].lchild = p1;
        (*HT)[i].rchild = p2;
        (*HT)[i].weight = (*HT)[p1].weight + (*HT)[p2].weight;
    }
    //打印Huffman表
    printf("Huffman表如下:\n");
    for (i = 1; i < m + 1; ++i) {
        printf("<%-2i>%4d%4u%4u%4u\n", i, (*HT)[i].weight, (*HT)[i].parent, (*HT)[i].lchild, (*HT)[i].rchild);
    }


    //从叶子到根逆向求每个字符的哈夫曼编码
    *HC = (HuffmanCode) malloc((length + 1) * sizeof(char *));
    cd = (char *) malloc(length * sizeof(char));
    cd[length - 1] = '\0';
    printf("对应的哈夫曼编码如下:\n");
    for (i = 1; i <= length; i++) {
        start = length - 1;
        for (c = i, f = (int) (*HT)[i].parent; f != 0; c = f, f = (int) (*HT)[f].parent) {
            if ((*HT)[f].lchild == c)
                cd[--start] = '0';
            else
                cd[--start] = '1';
        }
        (*HC)[i] = (char *) malloc((length - start) * sizeof(char));
        strcpy((*HC)[i], &cd[start]);
        printf("第%d个字符对应的Huffman编码:%s\n", i, (*HC)[i]);
    }
    free(cd);
}

int main() {
    test();
    return 0;
}

HuffmanTestApp.c

#include "HuffmanDef.h"

void test(){//测试函数
    HuffmanTree HT;
    HuffmanCode HC;
    HuffmanCoding(&HT, &HC);
}

猜你喜欢

转载自blog.csdn.net/qq_40100414/article/details/113814893