算法分析与设计实验报告——实现哈夫曼编码

本文介绍了一个关于哈夫曼编码的实验报告,包括实验的目的、要求、原理等,并详细介绍了实验的具体步骤,运行结果及作者的心得体会。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

算法分析与设计实验报告——实现哈夫曼编码

一、 实验目的

掌握贪心算法的基本思想和解决问题的基本步骤,认识贪心算法和动态规划的联系与区别,对比解决同一问题的两种算法设计策略的时间复杂性。

二、实验要求

用c++语言实现用贪心算法解决哈夫曼编码问题,分析时间复杂性,体会贪心算法解决问题的基本思路和步骤。

三、 实验原理

1、哈夫曼树的定义:假设有n个权值,试构造一颗有n个叶子节点的二叉树,每个叶子带权值为wi,其中树带权路径最小的二叉树成为哈夫曼树或者最优二叉树;
2、哈夫曼树的构造:weight为输入的字符权值,parent为这个节点的父节点的位置,lchild为这个节点的左孩子的位置,rchild为这个节点的右孩子的位置,即每一个HTNode对应一个结点。然后根据weight的值,取出现存子树中的最小与次小的MinCode,并且这两个子树没有父节点,把他们俩相连接成为一颗新树。如此往复构造出哈夫曼树。通过已经构造出的哈夫曼树,编码时自底向上进行查找判断,直到parent为树的顶点为止。这样,根据每次向上搜索后,原节点为父节点的左孩子还是右孩子,来记录0或1,这样,每个字符都会有一个01编码与之唯一对应,并且任何编码没有前部分是同其他完整编码一样的;解码时从顶部向下查找,直到查找到叶子结点,输出对应的字符。

四、 实验过程(步骤)

见附件一
实验步骤、特点
重要源代码(流操作的部分要醒目的提示并注释)

五、 运行结果

见附件二

六、实验分析与讨论

一开始对哈夫曼树树不是很了解,构造的时候不知道如何去构造,查找资料后选择了一种以数组为主要操作对象的算法。还有这次写的程序比较长,有些变量应用错误,所以出现了很多奇怪的的bug。

七、实验特色与心得

这次试验写了很长的程序,直到我写完才发觉。这次实验让我对贪心算法与哈夫曼树有了更深的了解。而且这次程序比较长,所以我出现了应用变量错误的现象。这种错误也是我对问题的理解深度不够造成的。以后写程序要在深刻理解的基础上再动键盘。

附件一 实验过程(步骤)

#include <bits/stdc++.h>

#define maxn 1000
//Format width
#define fmw 10

using namespace std;
//哈夫曼树结点结构体
typedef struct {
    int weight;
    int parent;
    int lchild;
    int rchild;
} HTNode, *HuffmanTree;
//需要编码的字符串数组
static char N[maxn];
//定义哈夫曼编码数组(char型二级指针(指向指针的指针))
typedef char **HuffmanCode;
//最小与次小的结点
typedef struct {
    int s1;
    int s2;
} MinCode;

//函数声明
//报错函数
void Error(string message);

//哈夫曼编码函数
HuffmanCode HuffmanCoding(HuffmanTree &HT, HuffmanCode HC, int *w, int n);

//选出最小与次小的函数
MinCode Select(HuffmanTree HT, int n);

void Error(string message) {
    cout << "Error:" << message << endl;
    exit(1);
}

//哈夫曼树,哈夫曼编码,权值数组,叶子结点数量
HuffmanCode HuffmanCoding(HuffmanTree &HT, HuffmanCode HC, int *w, int n) {

    int i, s1 = 0, s2 = 0;
    //哈夫曼树
    HuffmanTree p;
    //暂存哈夫曼编码数组
    char *cd;
    //m为总结点数
    int f, c, start, m;
    //最小与次小节点
    MinCode min;
    //如果结点只有一个,不进行编码
    if (n <= 1)
        Error("Code too small");
    //计算总结点数量
    m = 2 * n - 1;
    //动态创建哈夫曼树
    HT = (HuffmanTree) malloc((m + 1) * sizeof(HTNode));
    //HT前n个为叶子节点,后面n-1个为非叶子节点
    //初始化n个叶子节点
    for (p = HT, i = 0; i <= n; i++, p++, w++) {
        p->weight = *w;
        p->parent = 0;
        p->lchild = 0;
        p->rchild = 0;
    }
    //初始化从后面数n-1个非叶子节点
    for (; i <= m; i++, p++) {
        p->weight = 0;
        p->parent = 0;
        p->lchild = 0;
        p->rchild = 0;
    }
    //创建哈夫曼树
    for (i = n + 1; i <= m; i++) {
        //寻找最小与次小
        min = Select(HT, i - 1);
        //最小
        s1 = min.s1;
        //次小
        s2 = min.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;//合并权值
    }
    //输出创建的树
    cout << "哈夫曼树列表:" << endl;
    cout << setw(fmw) << left << "序号" << setw(fmw) << left << "权值" << setw(fmw) << left << "父节点" << setw(fmw) << left
         << "左孩子" << setw(fmw) << left << "右孩子" << endl;
    for (i = 1; i <= m; i++) {
        cout << setw(fmw) << left << i << setw(fmw) << left << HT[i].weight << setw(fmw) << left << HT[i].parent
             << setw(fmw) << left << HT[i].lchild << setw(fmw) << left << HT[i].rchild << endl;
    }
    //动态创建哈夫曼编码
    HC = (HuffmanCode) malloc((n + 1) * sizeof(char *));
    //动态创建暂存哈夫曼编码数组
    cd = (char *) malloc(n * sizeof(char *));
    //最后一位赋值为结束符
    cd[n - 1] = '\0';
    //求解编码
    for (i = 1; i <= n; i++) {

        start = n - 1;
        /*
         * 左为0,右为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';
        }
        //对第i个字符分配空间
        HC[i] = (char *) malloc((n - start) * sizeof(char *));
        //将cd暂存的复制到HC
        strcpy(HC[i], &cd[start]);
    }
    free(cd);
    return HC;

}

//寻找最小与次小的结点
MinCode Select(HuffmanTree HT, int n) {
    //最小,次小
    int min, secmin;
    int i, s1, s2;
    MinCode code;
    s1 = 1;
    s2 = 1;
    //寻找最小值
    min = 0x3f3f3f3f;
    for (i = 1; i <= n; i++) {
        if (HT[i].weight < min && HT[i].parent == 0) {
            min = HT[i].weight;
            s1 = i;
        }
    }
    //寻找次小值
    secmin = 0x3f3f3f3f;
    for (i = 1; i <= n; i++) {
        if ((HT[i].weight < secmin) && (i != s1) && HT[i].parent == 0) {
            secmin = HT[i].weight;
            s2 = i;
        }
    }
    //封装返回
    code.s1 = s1;
    code.s2 = s2;
    return code;

}

//解码 哈夫曼树,叶子结点数,解码字符
void HuffmanTranslateCoding(HuffmanTree HT, int n, char *ch) {
    int m = 2 * n - 1;
    int i, j = 0;
    cout << "解码后:" << endl;
    //循环到最后
    while (ch[j] != '\0') {
        i = m;
        //从顶部向下查找
        while (HT[i].lchild != 0 && HT[i].rchild != 0) {
            if (ch[j] == '0')
                //向左查找
                i = HT[i].lchild;
            else
                //向右查找
                i = HT[i].rchild;
            j++;
        }
        //输出对应字符
        cout << N[i - 1];
    }
    cout << endl;
}

//主函数
int main() {
    HuffmanTree HT = NULL;
    HuffmanCode HC = NULL;
    int *w = NULL;
    int i, n;
    char tran[maxn];

    cout << "请输入需要编码的字符串N:";
    gets(N);
    n = strlen(N);
    w = (int *) malloc((n + 1) * sizeof(int *));
    w[0] = 0;

    cout << "请依次输入权值:" << endl;
    for (i = 1; i <= n; i++) {
        printf("w[%d]=", i);
        cin >> w[i];
    }
    HC = HuffmanCoding(HT, HC, w, n);
    //打印哈夫曼编码表
    cout << "哈夫曼编码:" << endl;
    cout << setw(fmw) << left << "字符" << setw(fmw) << left << "权值" << setw(fmw) << left << "编码" << endl;
    for (i = 1; i <= n; i++) {
        cout << setw(fmw) << left << N[i - 1] << setw(fmw) << left << w[i] << setw(fmw) << left << HC[i] << endl;
    }
    cout << "哈夫曼编码:";
    for (i = 1; i <= n; i++) {
        cout << HC[i];
    }
    cout << endl;

    cout << "请输入需要解码的字符串T:";
    getchar();
    gets(tran);
    HuffmanTranslateCoding(HT, n, tran);
    return 0;
}
/*
abcdef
 45
 13
 12
 16
 9
 5
 */
/*
computer
19
21
2
3
6
7
10
32
 */

附件二 运行结果

在这里插入图片描述

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值