Huffman编码的设计与实现

文章目录

    • (一)设计描述
    • (二)需求分析
    • (三)详细设计
    • (四)代码实现与测试
    • (五)个人总结

(一)设计描述

1.题目描述
设计一个利用哈夫曼算法的编码和译码系统,重复地显示并处理以下项目,直到选择退出为止。

1) 输入一串字符,然后统计其中各个字符的个数,即每个字符的权值。
2)  进行初始化操作,并建立哈夫曼树;
3)  编码:利用建好的哈夫曼树生成哈夫曼编码,并输出编码;
4)  译码,即输入每个字符对应的二进制的编码(0或1),然后输出对应的字符。
5) 结束操作。

2.设计目的与要求

1)目的:

通过布置具有一定难度的实际程序设计项目,进一步理解和掌握课堂上所学各种基本抽象数据类型的逻辑结构、存储结构和操作实现算法

2)要求:

1.要求利用C\C++语言来完成系统的设计; 
2.突出C语言的函数特征(以多个函数实现每一个子功能)或者C++语言面向对象的编程思想;
3.画出功能模块图;
4.进行简单界面设计,能够实现友好的交互;
5.具有清晰的程序流程图和数据结构的详细定义;
6.熟练掌握C语言或者C++语言的各种操作。

(二)需求分析

第一步定义Treecount(哈夫曼树节点的个数),随后定义两个结构体,HTnode和HTcode,结构体HTnode储存数据信息,即(输入的字符(data),权值(weight),父结点(parent),左孩子(lchild),右孩子(rchild))。 结构体HTnode储存结果信息,即(输入的字符(data),BT数组(储存哈夫曼编码结果),start(哈夫曼编码的起始位置),num统计各个字符的出现的个数,即权值,input 输入的字符)
第二步定义字符指针P,数组letter[]和数组string[],数组letter用来调用数据 input,data,num;string[]用来输入字符,同时让指针P指向string[];由于数组是单个存储,判断字符是否相同,用account,num分别统计字母个数以及每个字符的对应次数,即求权值。
第三步首先定义两个数组huffnode,array[]分别储存结点与字符,然后进行哈夫曼树的初始化,然后找到权值最小的两个结点,并记录当前下标。由于是逆序输出,左右子树的结点位置为2n-1;得知左右子树的权值后,让左右子树权值进行相加,把值赋给一个新的结点。
第四步结构体HTcode实例化一个对象begin,并定义两个数组huffnode,huffcode,调用哈夫曼树,并记录编码的位置为n-1;倒序编码,并记录下标。然后回溯头结点,找到左孩子,赋值为0;找到右孩子赋值为1。然后进行start–,倒着计算编码,沿着父母结点往上走到结点,保存并求出每个叶子节点的哈夫曼编码与编码的起始位。
第五步定义数组code数组,字符指针q,输入0和1的数字组合,此时哈夫曼树对应的叶子结点下标为2n-2;然后进行译码,如果读到0,从根结点的左孩子继续读入,直到读到1,然后继续读入,即读到了一个叶子(字符),即翻译一个字符成功,然后进行遍历输出结果。
第六步用户选择结束系统

(三)详细设计

1.实现思想

1.首先要构造一棵哈夫曼树,哈夫曼树的结点结构包括权值,双亲,左右孩子;假如由n个字符来构造一棵哈夫曼树,则共有结点2n-1个;在构造前,先初始化,初始化操作是把双亲,左右孩子的下标值都赋为0;然后依次输入每个结点的权值

2.第二步是通过n-1次循环,每次先找输入的权值中最小的两个结点,把这两个结点的权值相加赋给一个新结点,,并且这个新结点的左孩子是权值最小的结点,右孩子是权值第二小的结点;鉴于上述找到的结点都是双亲为0的结点,为了下次能正确寻找到剩下结点中权值 最小的两个结点,每次循环要把找的权值最小的两个结点的双亲赋值不为0(i).就这样通过n-1循环下、操作,创建了一棵哈夫曼树,其中,前n个结点是叶子(输入的字符结点)后n-1个是度为2的结点

3.编码的思想是逆序编码,从叶子结点出发,向上回溯,如果该结点是回溯到上一个结点的左孩子,则在记录编码的数组里存“0”,否则存“1”,注意是倒着存;直到遇到根结点(结点双亲为0),每一次循环编码到根结点,把编码存在编码表中,然后开始编码下一个字符(叶子)

4.译码的思想是循环读入一串哈夫曼序列,读到“0”从根结点的左孩子继续读,读到“1”从右孩子继续,如果读到一个结点的左孩子和右孩子是否都为0,如果是说明已经读到了一个叶子(字符),翻译一个字符成功,把该叶子结点代表的字符存在一个存储翻译字符的数组中,然后继续从根结点开始读,直到读完这串哈夫曼序列,遇到结束符便退出翻译循环

3.2 创建哈夫曼树图示
已知叶子节点为{10,20,30,40},以这4个权值构建树b的过程为:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
3.实现流程图
在这里插入图片描述

(四)代码实现与测试

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<windows.h>#define Treecount 50            //最大叶结点个数
#define MAXSIZE 500
typedef struct node {char data;int weight;int parent;int lchild;int rchild;
} HTnode;
typedef struct code {char  data;int BT[MAXSIZE];      //数组,存放字符的哈夫曼编码int start;           //该编码在数组中的开始位置char input;int num;
} HTcode;
void Createhufftree(HTnode HuffNode[],int n,HTcode array[]) {   // array[]: 用来存储字符及其权值int i,j;int minfirst,minsecond;int Lnode,Rnode;for(i=0; i<2*n-1; i++) {HuffNode[i].data=0;HuffNode[i].weight=0;HuffNode[i].parent=-1;HuffNode[i].lchild=-1;HuffNode[i].rchild=-1;}for(i=0; i<n-1; i++) {minfirst=minsecond=32767;  //给最小和次小的树赋最大值Lnode=Rnode=-1;for(j=0; j<n+i; j++) {if(HuffNode[j].parent==-1 && HuffNode[j].weight<minfirst) {  	//每次找出剩下的最小权值,并将最小权值赋给次小minfirst=minsecond;Rnode=Lnode;minfirst=HuffNode[j].weight;Lnode=j;   //记录下标} else if(HuffNode[j].parent==-1 && HuffNode[j].weight<minsecond) {minsecond=HuffNode[j].weight;Rnode=j;}}HuffNode[Lnode].parent = n+i;HuffNode[Rnode].parent = n+i;HuffNode[n+i].weight = HuffNode[Lnode].weight+HuffNode[Rnode].weight;HuffNode[n+i].lchild = Lnode;HuffNode[n+i].rchild = Rnode;}for(i=0; i<n; i++) {HuffNode[i].weight=array[i].num;    //将字符个数赋给权值HuffNode[i].data=array[i].input;}
}
void QQ() {printf("请输入二进制译码启动密码,密码错误默认结束(即为默认退出):");long sercret;scanf("%d",&sercret);if(sercret==11011) {Sleep(1000);} else {Sleep(1000);printf("非法操作,结束系统");exit(-1);}
}
void  HuffmanCoding(int  n, HTcode array[]) {// array[]: 用来存储字符及其权值HTnode  HuffNode[1000];HTcode  HuffCode[Treecount];HTcode  begin;FILE *fp;fp=fopen("D:\\a.txt","w");Createhufftree(HuffNode,n,array);int  i, j ;int  loc,p;char code[30],*q;for(i=0; i<n; i++) {begin.start=n-1;                  //编码的开始位置loc=i;p=HuffNode[loc].parent;while(p!=-1) {if(HuffNode[p].lchild==loc)begin.BT[begin.start]=0;elsebegin.BT[begin.start]=1;begin.start--;                       //倒着计算编码loc=p;                               //沿着父母结点往上走到顶点p=HuffNode[loc].parent;}for(j=begin.start+1; j<n; j++)HuffCode[i].BT[j]=begin.BT[j];HuffCode[i].start=begin.start;                      //   保存求出的每个叶节点的哈夫曼编码和编码的起始位printf("\n");}printf("各个字符对应的二进制编码(1//0//1//0)如下:\n");for(i=0; i<n; ++i) {printf("字符%c的密文:",HuffNode[i].data);for(j=HuffCode[i].start + 1; j<n; j++)      //start走过了,加一恢复到开始位,编码个数小于nprintf("%d", HuffCode[i].BT[j]);printf("\n");}printf("\n");printf("编码成功,1s后打印哈夫曼编码:\n");Sleep(1000);for(i=0; i<n; ++i) {for(j=HuffCode[i].start+1; j<n; j++) {fprintf(fp,"%d",HuffCode[i].BT[j]);}for(j=HuffCode[i].start + 1; j<n; j++) {printf("%d", HuffCode[i].BT[j]);}}printf("\n\n");if(i>0) {QQ();}//   译码:system("cls");printf("            ----- ∮哈夫曼译码系统已经就绪 ∮-------        \n");printf("各字符的对应的编码如下:\n"); for(i=0; i<n; ++i) {printf("字符%c的密文:",HuffNode[i].data);for(j=HuffCode[i].start + 1; j<n; j++)      //start走过了,加一恢复到开始位,编码个数小于nprintf("%d", HuffCode[i].BT[j]);printf("\n");}printf("                  --> 请输入对应的二进制数字:              \n");scanf("%s",&code);q = code;loc = 2*n -2;                                  //    哈夫曼树的根节点下标printf("译码成功,正在打印译码结果:\n");Sleep(2000);while( *q != NULL) {if( *q== '0') {loc = i =HuffNode[loc].lchild;if(HuffNode[loc].lchild == -1 && HuffNode[loc].rchild == -1) {printf("%c",HuffNode[i].data);fprintf(fp,"%c",HuffNode[i].data);loc = 2*n - 2;}} else if(*q == '1') {loc = i = HuffNode[loc].rchild;if(HuffNode[loc].lchild == -1 && HuffNode[loc].rchild == -1) {fprintf(fp,"%c",HuffNode[i].data);printf("%c",HuffNode[i].data);loc = 2*n-2;                       // 重新从根遍历}} else {printf("非法输入!");}q++;}printf("\n");fclose(fp);
}
void menu() {system("color F");printf("                                                      ");printf("                                                     \n");printf("   ____〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓____    \n");printf("               ||  哈夫曼编码  ||                    \n");printf("        _________☆界面选择如下☆_________           \n");printf("                                                     \n");printf("             -> 1 ☆.编码与译码                      \n");printf("             -> 2 ☆.结束系统                        \n");printf("------------ ☆             ☆-----------------      \n");printf("              ∮请手动输入编号:              "); 
}
void Treeaction() {HTcode letter[50];char string[1000], *p;int i, account=0;printf("          ----- ∮哈夫曼编码系统已经就绪 ∮-------        \n");printf("            》》  请输入你要编码的字符串:");scanf("%s",&string);for(i=0; i<50; i++) {letter[i].input=0;letter[i].num=0;}p=string;while(*p!=NULL) {for(i=0; i<=account+1; i++) {if(letter[i].input==NULL) {      //letter[i].input存储所有的字符,用letter[i].num存储相同的字符的数量letter[i].input=*p;letter[i].num++;account++;                       // 统计不同字符的个数break;} else if(letter[i].input==*p) {letter[i].num++;break;}}p++;}printf("\n");printf("不同的明文(字符)个数:%d\n",account);printf("\n");printf("输出输入的字符的权值\n");for(i=0; i<account; i++) {       //输出不同字符及其所对应的权值printf("当前字符%c的权值为%d",letter[i].input,letter[i].num);printf("\n");}printf("\n");HuffmanCoding(account,letter);}
int main() {int  n,flaglist=1;char choice;while(flaglist) {menu();scanf("%d",&n);switch(n) {case 1:system("cls");Treeaction();printf("\n");printf("2秒后系统进行返回!\n") ;Sleep(2000);printf("\n");system("cls");printf("已经返回!\n"); printf("请根据个人意愿进行操作!\n");printf("\n");break;case 2:exit(0);}}return 0;
}

(五)个人总结

通过这次课程设计,我收获了很多,认清了课程设计的需求,更认清了自己。谈起缺点,自己代码实现的能力也是破绽百出,对相对较难的题目不能冷静解决,经常犯一些低级的错误。然无独有偶,自己也有优点,自己思想上能理解难题,并尝试解决难题,考虑比较周到。
这次的课程设计,难度处于中等地位,但实现起来并不没有想象中的那么容易。它需要考虑多种因素,必须充分理解题目要求,化大难题为多个小难题,把具体实现步骤细分,认真完成每一步步骤,然后综合实现题目对应的问题。 时间如白驹过隙,无声滑过指尖。
学期即将完结,即将大二,不免多生感想。回想一年时光,自己虽说付出努力,然并不能收益众多。深知自己与他人之差距,然必须成为优秀者,方可日后有一席之地。

我没有科学家的那样的天赋异禀,而我却又有一颗积极向上的心,以“努力不一定成功,但不努力绝不会成功”为座右铭,付诸行动与汗水,不忘初心,厚积薄发。正如诗人王之涣所说:“欲穷千里目,更上一层楼。”要有“燕雀安知鸿鹄之志”的眼光,实现能力之飞跃,思想之迅捷,然自己必须十分努力,才能看起来毫不费力。
风雨过后才会见到彩虹。你尽管去努力,剩下的交给天命。定当不忘学习,砥砺前行。肺腑之言,溢于言表。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/531026.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C / C++ 经典代码和常考类型

文章目录C语言常考类型经典代码1&#xff09; 兔子问题2&#xff09; 斐波那契数列3&#xff09; 百元买百鸡4&#xff09; 实心菱形5&#xff09; 空心菱形6&#xff09; 十进制转换为二进制7&#xff09; 水仙花8&#xff09; 大数相加9&#xff09; 冒泡排序10&#xff09; …

如何在GitHub上下载开源文件

具体实现步骤 1 . 在本地安装Git,下载地址&#xff1a;点击下载 2 . 下载完成后开始安装&#xff0c;安装过程勾选下面内容&#xff1a; 1.Git Bash here2.Git GUI here 然后一直点击next&#xff0c;安装完成即可。 3 . 在打开的git窗口中输入 git init 4 . 在下载的项目的…

IDEA连接mysql报Server returns invalid timezone. Go to 'Advanced' tab and set 'serverTimezone' 的错误

IDEA布置好项目后&#xff0c;连接Mysql&#xff0c;提示时区错误&#xff0c;解决方案如下&#xff1a; 在mysql的命令模式下&#xff0c;输入&#xff1a; set global time_zone8:00;如图所示&#xff1a;

C语言小游戏 ——推箱子

文章目录&#xff08;1&#xff09;内容描述&#xff08;2&#xff09;功能&#xff08;3&#xff09;设计目的&#xff08;4&#xff09;总体设计&#xff08;5&#xff09;明显缺点&#xff08;6&#xff09;个人总结7. 实例实例一实例二实例三实例四实例五&#xff1a;&…

Java GUI 实现登录界面

具体效果图&#xff1a; 详细代码&#xff1a; package java_gui;import java.awt.Color; import java.awt.Container; import java.awt.Cursor; import java.awt.Font; import java.awt.Image; import java.awt.event.ActionEvent; import java.awt.event.ActionListener;imp…

C语言 实现登录注册功能

文章目录加载动画实验结果图实验代码加载动画 #include<iostream> #include<windows.h> using namespace std;int main(){cout<<endl<<endl<<endl<<endl<<endl<<endl<<endl<<endl<<endl;cout.widen(65);co…

Java小案例(二) 用数组实现增删查改排序

文章目录案例一案例二案例三案例四案例五案例一 Student.Java package curd;public class Student {private String stuid;private String name;private int chinese;private int math;private int english;private int avg;private int sum;public int getAvg() {return avg;…

有趣的一行代码

文章目录爱心曼德勃罗集合打印99乘法表实现快速排序禅语漫画迷宫爱心 print(\n.join([.join([(❤❤❤❤❤❤❤❤❤❤❤❤[(x-y)%12]if((x*0.05)**2(y*0.1)**2-1)**3-(x*0.05)**2*(y*0.1)**3<0 else )for x in range(-30,30)])for y in range(15,-15,-1)]))效果图&#xff1…

JDBC实现图书管理小案例

文章目录项目文件结构项目运行效果项目详细代码项目文件下载相关案例案例一案例二案例三案例四案例五案例六案例七案例八项目文件结构 项目运行效果 项目详细代码 JDBCUtils.java package jdbc;import java.sql.*; import com.mysql.jdbc.Driver;public class JDBCUtils {pri…

基于TCP原理,采用Socket通信技术,实现聊天室

文章目录案例一案例二案例三案例四案例五相关案例案例一 Client.java package SocketCode;import java.awt.Color; import java.awt.Font; import java.awt.GridLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener;import java.io.*; import …

HTML+CSS+JS 实现登录注册界面

文章目录案例一 滑动样式案例二 滑动样式案例三 动态样式案例四 普通样式案例五 滑动样式案例六 普通样式具体怎么获取呢&#xff1f;案例一 滑动样式 login.html <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8">…

html+css 小案例(一)

文章目录点击下载index.html <!DOCTYPE html> <html lang"ch"> <head><meta charset"UTF-8"><title>Demo_Test</title><link rel"stylesheet" href"css/Dec.css"> </head> <body…

HTML+CSS+JS 表白代码

文章目录案例一 爱你到天昏地暗案例二 带有音乐的表白代码案例三 带有图片旋转的表白案例源码案例一 爱你到天昏地暗 <html> <style type"text/css"> <!-- .STYLE1 {font-size: medium} --> </style> <center><br><br>…

Linux常用命令与基本操作、填空题、简答题

文章目录基本知识点基本操作叙述什么是shell?重启虚拟机网络分区查看当前进程查看主机名重启关机查看IP地址文件搜索挂载查看网络配置linux系统中Vi的三种模式磁盘分区查看硬盘信息df&#xff0c;fdisk&#xff0c;lsblk区别进程用户用户信息查看用户密码信息修改密码添加用户…

客户端与服务器(C/S架构与B/S架构)、AJax学习

文章目录HTTP请求与服务器客户端与服务器1.C/S架构与B/S架构2.web资源介绍3.资源访问流程HTTP协议1.什么是协议&#xff1f;2.什么是HTTP协议3.HTTP组成部分4.请求的发送方式什么是Ajax技术Ajax可以做什么&#xff1f;伪造AjaxHTTP请求与服务器 客户端与服务器 1.C/S架构与B/…

前端必会技巧

文章目录点击输入框提示信息防止别人盗取视频设置输入边框透明按钮跳转a标签如何具有bootstra按钮样式formData的使用boostrap上传样式jq获取input标签的值jq获取最近一个td标签的值css设置几个div中的图片保持在一条水平线使div居中canvas 网页背景 粒子鼠标跟随磁力登录界面添…

linux基础必备

文章目录Linux系统中常用目录结构Linux下如何从普通用户切换到root用户Linux系统常用命令Linux系统文件中相关指令Linux查看开机自启动服务Linux常见服务列表Linux关闭某些服务&#xff08;临时关闭&#xff09;Linux关闭服务开机自启动Linux中在屏幕输入一句话Linux查看系统进…

Hadoop搭建集群中输入hdfs namenode -format格式化节点时,提示:hdfs: command not found 解决办法

分析原因&#xff1a; hadoop的安装路径没有加入到当前路径中 解决办法&#xff1a; 方法1&#xff1a; 进入hadoop的安装目录&#xff0c;在执行hadoop命令时在前面加上&#xff1a;./bin ./sbin 方法2: 进入/etc/profile文件中添加hadoop的安装路径 sudo vim /etc/profile添…

Linux高级编程实验(30个)

文章目录1&#xff09;输出Linux下的c也不是太难嘛&#xff01;在linux下编辑&#xff0c;编译&#xff0c;运行2&#xff09;编写一个简单地c语言程序,根据输入的两个整数求平均值并且在终端输出&#xff0c;通过gcc编译器得到它的汇编程序文件。3&#xff09;编写一个c语言程…

登录界面验证码的实现

文章目录Javaweb实现验证码前端后台Springboot添加验证码项目结构依赖控制类前端页面效果图Javaweb实现验证码 前端 添加样式 <meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0, minimum-scale1.…