哈夫曼树,哈夫曼编码及解码详解

本文详细介绍了哈夫曼树的构建过程,包括顺序表、栈、队列和链表等数据结构的应用。通过实例展示了如何为26个字母及空格构建哈夫曼树,并生成哈夫曼编码。此外,还提供了字符转码和解码的实现,模拟了网络发送端和接收端的编码解码操作,加深了对哈夫曼编码在网络传输中的应用理解。
摘要由CSDN通过智能技术生成

🌍新人小白的博客
⌛️希望大家多多关注
🌱一起加油,共同成长
🎃以后会经常更新哒~🙈
⭐️个人主页: 收藏加关注,永远不迷路~⭐️

数据结构系列👀

一:顺序表的操作,你真的学会了吗?
二:顺序栈的基本操作
三:循环队列的基本操作,你学会了吗?
四:单链表的操作(超详细),保证你看完不后悔
五:5分钟带你快速解决二叉树问题,你确定不进来看看?


前言😺

Tips:文章有点长,小主耐心一点哦~

介绍哈夫曼树,掌握哈夫曼树及哈夫曼编码的构造过程,体会网络发送端和接收端编码和译码过程及其工作原理。
在这里插入图片描述


一、问题描述😜

给定报文中26个字母a-z及空格的出现频率{64, 13, 22, 32, 103, 21, 15, 47, 57, 1, 5, 32, 20, 57, 63, 15, 1, 48, 51, 80, 23, 8, 18, 1, 16, 1, 168},构建哈夫曼树并为这27个字符编制哈夫曼编码,并输出。模拟发送端,从键盘输入字符串,以%为结束标记,在屏幕上输出输入串的编码;模拟接收端,从键盘上输入0-1哈夫曼编码串,翻译出对应的原文。


二、实现步骤🙊

1.定义存储表示

2.定义操作函数

定义选择权值最小的函数Select,构造哈夫曼树的函数CreatHuffmanTree,创建哈夫曼编码的函数CreateHCode,打印哈夫曼编码函数PrintHTree,按输入的字符打印编码函数Printe,字符转码函数change,解码函数decoding

3. 采用菜单样式让操作更加方便清楚。

4.调试实验代码并进行相关测试,得出实验结果。

三、完整代码🐻

代码可直接运行😄

#include <iostream>
#include <cstring>
#include <algorithm>
#include <iomanip>

using namespace std;

int m,n;

// int letter[27] = {64, 13, 22, 32, 103, 21, 15, 47, 57, 1, 5, 32, 20, 57, 63, 15, 1, 48, 51, 80, 23, 8, 18, 1, 16, 1, 168};
// 64 13 22 32 103 21 15 47 57 1 5 32 20 57 63 15 1 48 51 80 23 8 18 1 16 1 168
int letter[28]; // 用于存储26个字母以及空格的出现的概率次数
char lee[28] = {' ','a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z',' '};

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

// 哈夫曼树的存储结构
typedef struct
{
    int w;
    int parent,lchild,rchild;
} HTNode,*HTree;

// 全局调用
HTree T;
HCode HC;

// ======================= 哈夫曼树的构造 ===========================

// 在树T中,k为节点的总个数,获取两个节点权值最小的值
void Select(HTree T,int k,int &s1,int &s2)
{
    int tmp = 100000,tmp1 = 100000;
    // 先获得一个最小的,然后再获得倒数第二小的,依次遍历,求最小权值
    for(int i = 1; i <= k; i ++)
    {
        if(!T[i].parent)
        {
            if(tmp > T[i].w)
            {
                tmp = T[i].w;
                s1 = i;
            }
        }
    }
    for(int i = 1; i <= k; i ++)
    {
        if(!T[i].parent && i != s1)
        {
            if(tmp1 > T[i].w)
            {
                tmp1 = T[i].w;
                s2 = i;
            }
        }
    }
}

// 构造哈夫曼编码树
void CreateHuffmanTree(HTree &T,int n) // n 是指叶子节点的个数
{
    if(n <= 1) return ;
    m = 2 * n - 1; // 拥有n个叶子结点的哈夫曼树共有2n-1个结点
    T = new HTNode[m + 1]; // 分配单元,存放结点
    for(int i = 1; i <= m; i ++)
    {
        T[i].parent = 0;
        T[i].lchild = 0;
        T[i].rchild = 0;
        T[i].w = 0;
    }
    for(int i = 1; i <= n; i ++) T[i].w = letter[i]; // 将输入的权值赋给每个结点的权值

    for(int i = n+1; i <= m; i ++)
    {
        int s1 = 0,s2 = 0;
        // 选择两个双亲域为0,且权值最小的节点,并返回他们的序号s1,s2;
        Select(T,i - 1, s1,s2);

        // 得到新节点i,从森林中删除s1,s2,将s1,s2的双亲域由0变为i
        T[s1].parent = i;
        T[s2].parent = i;

        // s1,s2为新节点的两个孩子,i的权值是两个子孩子的和
        T[i].lchild = s1;
        T[i].rchild = s2;
        T[i].w = T[s1].w + T[s2].w;
    }
}

// ======================= 哈夫曼树编码的生成 ===========================

// 创建哈夫曼编码
void CreateHCode(HTree T, HCode &HC, int n)
{
    HC = new char* [n + 1]; // 存放n个字符的编码的表
    char* cd = new char[n]; // 临时存放每个字符的编码

    cd[n - 1] = '\0'; // 编码结束符

    // 一次得到每个结点的编码
    for(int i = 1; i <= n; i ++)
    {
        // start 一般指向最后一个 ,倒着开始遍历
        int start = n - 1;
        int c = 0, f = 0;
        c = i;
        f = T[i].parent;
        while(f != 0)
        {
            -- start ;
            if(T[f].lchild == c) cd[start] = '0';
            else cd[start] = '1';
            c = f ;
            f = T[f].parent;
        }
        HC[i] = new char[n - start];
        // 从 start 后的所有值 复制给 HC[i]
        strcpy(HC[i], &cd[start]);
    }
    delete cd;
}

// 打印哈夫曼编码的打印
void PrintHTree(HCode &HC,int n)
{
    cout << std::left << setw(10) <<"字母" << std::left << setw(10) << "编码" << endl;

    for(int i = 1; i <= n; i ++)
    {
        // std::left 使后面输出的值向左对齐
        cout << std::left << setw(10) << lee[i] ;
        string str = HC[i];
        cout << std::left << setw(10) << str << endl;
    }
}


//======================= 字符的转码功能 ============================


// 按输入的字符打印编码
void Printe(HCode &HC,int l)
{
    string str = HC[l];
    cout <<str;
}

// 字符的转码
void change()
{
    cin.ignore();
    char a[100]; // 最多输入一百个字符,包含空格
    cin.getline(a,100);
    int len = strlen(a);
    for(int i = 0; i < len ; i ++)
    {
        int l = a[i]; // 转化为ASCII值
        if(a[i] == ' ') l = 27;
        else if(l >= 97)
        {
            l -= 96;
        }
        else if(l < 97 && l >= 65)
        {
            l -= 64;
        }
        Printe(HC,l);
    }
    cout << endl;
}


// ======================== 哈夫曼编码解码 =================================

void decoding(HCode &HC,int n,string s)
{
    string temp = ""; // 依次获取一个字符,并和编码表里面的字符串对比
    char str[1000]; // 用来存放解码后的数字
    for(int i = 0; i < 1000; i ++) str[i] = '1'; // 每次定义char ,防止上次的结果影响这次的答案
    int h = 0; // str 存储的下标
    for(unsigned int i = 0; i < s.length(); i ++)
    {
        temp = temp + s[i]; // 依次获取一个字符
        for(int j = 1; j <= n; j ++)
        {
            string str2 = HC[j];
            if(temp == str2)
            {
                str[h ++] = lee[j];
                temp = "";
            }
            else if(i == s.length() - 1&& j == n  && temp != "")
            {
                cout <<"解码错误";
                break;
            }
        }
    }
    for(int i = 0; i < 1000 ; i ++)
    {
        if(str[i] != '1') cout << str[i];
        else break;
    }
    cout << endl;
}

// ==================== 功能的展示 ============================

void show()
{
    cout <<"1 ---- 输入26字母的出现频率" << endl;
    cout <<"2 ---- 创建哈夫曼树和编码表,并输出编码表" << endl;
    cout <<"3 ---- 输入字符,并实现转码" << endl;
    cout <<"4 ---- 输入编码,并翻译为字符" << endl;
    cout <<"5 ---- 退出" << endl;

}

int main()
{
    show();
    memset(letter, 0,sizeof letter);
    n = 27;
    m = 2*n - 1;
    char fir;
    while(cin >> fir)
    {
        if(fir == '1')
        {
            for(int i = 1; i <= n; i ++)
            {
                cin >> letter[i];
            }
        }
        else if(fir == '2')
        {
            CreateHuffmanTree(T,n);
            CreateHCode(T,HC,n);
            PrintHTree(HC,n);
        }
        else if(fir == '3')
        {
            change();
        }
        else if(fir == '4')
        {
            string s;
            cin >> s;
            decoding(HC,n,s);
        }
        else if(fir == '5')
        {
            exit(0);
        }
        else
        {
            cout << "请输入合法的字符"<<endl;
            show();
        }
    }

    return 0;
}


四、运行结果🐾

这里是运行的结果哦 ❤️

1.模拟发送端
输入:I love you
输出:01101111011110011100000010111100011100100001
2.模拟接收端 输入
输入:01101101111011000111111010111101101001100001
输出:it is a dog
在这里插入图片描述
在这里插入图片描述


结语🌍

通过该实验,理解哈夫曼树的概念,掌握哈夫曼树哈夫曼编码的构造过程,体会网络发送端接收端编码和译码过程及其工作原理。🚀🚀🚀

在这里插入图片描述


如果有什么问题或者建议,欢迎在评论区留言哦,博主会第一时间回复哒❤️❤️❤️
在这里插入图片描述

  • 39
    点赞
  • 174
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 8
    评论
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

白衣折扇y

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值