C语言—哈夫曼编码译码器

1.介绍

设计一个利用哈夫曼算法的编码和译码系统,重复地显示并处理以下业务,直到选择退出为止。
(说明:在代码中使用 while 循环,并设置一个跳出循环即退出的字符,例如: e ,当输入 ’e’ 时,跳
出循环,重复结束)
(1) 初始化:键盘输入 n 个字符和 n 个权值,建立哈夫曼树 (n>=5)
(说明:哈夫曼树使用静态三叉链表结构,有权重, parent, lchild, rchild ;哈夫曼编码用指向叶 子的指针,叶子结点的数目,和一个存储编码的结构 HuffmanCode 组成)
(2) 能够将数据存放在数据文件 ( 文件名为 data.txt ,位于当前目录中 )
(3) 编码:利用建好的哈夫曼树生成哈夫曼编码,输出编码;
(4) 输入编码,完成译码。
设计要求:
(1) 符合课题要求,实现相应功能;
(2) 要求界面友好美观,操作方便易行;
(3) 注意程序的实用性、安全性。

2.代码

#include <stdio.h>
#include <conio.h>
#include <string.h>
#include <malloc.h>

#define OK 1
#define ERROR 0
#define OVERFLOW -1
#define MAX_NUM 10086
#define MAX 60

//1.哈夫曼树的三叉链表结点的结构
typedef struct
{   
     int weight;//权重
     int parent, lchild, rchild;
     
}HTNode, *HuffmanTree;

//2.数组用来存储哈夫曼编码表
typedef char **HuffmanCode;

//3.哈夫曼树存储结点结构体
typedef struct
{
     char *c;//指向字符数组的指针
     int  longth;//权重
     HuffmanTree TREE;//它代表了哈夫曼树的另一个结点。
     HuffmanCode BM;
     //用于存储与当前结点关联的哈夫曼编码
}Huffman;

//4.作用是在一组哈夫曼树结点中找到两个具有最小权值的结点
void Select(HuffmanTree HT, int end, int *s1, int *s2) 

{
     int i;
     int min1 = MAX_NUM;
	 //用于表示初始时最大的权值。这个变量将用于存储找到的第一个最小权值的结点的索引。
     int min2;
     
     //找到的第二个最小权值的结点的索引。由于我们首先寻找两个权值最小的结点,
	 //所以这个变量一开始没有被初始化,但是在函数中它会被更新。
	 
     for (i = 1;i <= end;i++)
	 {
         if((HT[i].parent == 0)&&(HT[i].weight < min1))
		 {
             *s1 = i;
             min1 = HT[i].weight;
         }
     }
     min2 = MAX_NUM;
     for(i = 1;i <= end;i++)
	 {
         if(HT[i].parent == 0&&(*s1 != i)&&min2 > HT[i].weight)
		 {
             *s2 = i;
             min2 = HT[i].weight;
         }
     }
     //*s1和*s2分别包含了两个权值最小且父结点为零的哈夫曼树结点的索引。
}

//5.输入哈夫曼树的叶子数目及字符和权重,为建立哈夫曼树做准备
Huffman InputHuffman(Huffman Hfm)
{
     int i,n;
	 //i将用作循环变量,而n用于存储用户输入的字符数。
     printf("\n*****新建初始化哈夫曼树*****\n");
     printf("#请输入要输入的字符数(>=5): ");
     scanf("%d",&n);
	 //从键盘读取用户输入的字符数量,并将其赋值给变量n。
     
     Hfm.TREE = (HuffmanTree)malloc((2*n)*sizeof(HTNode));
     // 使用malloc函数为哈夫曼树的结点分配足够大的连续内存空间。
     Hfm.c = (char *)malloc((n+1)*sizeof(char));
     //以容纳n个输入字符及一个额外的字符用于数组的终止符
     
     for(i = 1;i <= n;i++)//遍历
	 {
         printf("#请分别输入第%d个字符:", i);
         scanf("%s",&Hfm.c[i]);
         printf("#其权重为:");
         scanf("%d", &Hfm.TREE[i].weight);
         Hfm.TREE[i].parent = 0;
         Hfm.TREE[i].lchild = 0;
         Hfm.TREE[i].rchild = 0;
     }
     /*将字符数组Hfm.c[i]设置为用户输入的字符。每个结点的
	 rent、lchild和rchild域都被设置为0,表示它们还没有被连接到树的其他部分。*/
     for(;i <= 2*n - 1;++i)
	 {
         Hfm.TREE[i].weight = 0;
         Hfm.TREE[i].parent = 0;
         Hfm.TREE[i].lchild = 0;
         Hfm.TREE[i].rchild=0;
     }
     //循环结束后,再执行一次赋值操作以填充剩余的哈夫曼树结点。将它们的权重、父节点、左子节点和右子节点都设置为0。
     Hfm.longth = n;
     //表示哈夫曼树中实际使用的结点数量。
     return Hfm;
}

//*(重要)6.创建哈夫曼树并进行哈夫曼编码
Huffman HuffmanCoding(Huffman Hfm)
{
     int i,n,m,s1,s2,start;
     /*i将用作循环变量
	 n表示哈夫曼树中叶子结点的数量
	 m表示哈夫曼树中总的结点数量
	 s1和s2用于暂时存储找到的两个最小权值结点的索引
	 start用于记录当前结点在编码字符串中的起始位置*/
     int c,f;
     char *cd;
     n = Hfm.longth;
	 //n赋值为哈夫曼树结构体中存储的叶子结点数量。
     
     if(n <= 1)   
     {
     	return Hfm;
	 }
	 	
    m = 2*n-1;//计算哈夫曼树中总的结点数量
     
     for(i = n + 1;i <= m;++i)

	 {
         Select(Hfm.TREE,i - 1,&s1,&s2);
         Hfm.TREE[s1].parent = i;
         Hfm.TREE[s2].parent = i;
         Hfm.TREE[i].lchild = s1;
         Hfm.TREE[i].rchild = s2;
         Hfm.TREE[i].weight = Hfm.TREE[s1].weight + Hfm.TREE[s2].weight;
     }
    //从i = n + 1到i <= m进行遍历。
	//每次循环调用Select函数找到两个权值最小的结点s1和s2。
	//然后将这两个结点的索引赋值给哈夫曼树结点i的左右子节点域lchild和rchild。
	//将哈夫曼树结点i的权重设置为它的左右子节点的权重之和。
	//将i赋值给这两个子节点的父节点域parent。
     
     Hfm.BM = (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;
         for(c = i,f = Hfm.TREE[i].parent; f != 0;c = f,f = Hfm.TREE[f].parent)
		 {
             if(c == Hfm.TREE[f].lchild)  
             {
             	cd[--start] = '0';
			 }
            
             else cd[--start] = '1';
         }
         Hfm.BM[i] = (char *)malloc((n - start)*sizeof(char));
         strcpy(Hfm.BM[i],&cd[start]);
     }
    //从i = 1到i <= n进行遍历。
	//对于每个叶子结点,从根结点开始向下遍历哈夫曼树,直到到达该叶子结点。
	//在遍历过程中,将遇到的每一个分支标记为'0'或'1',并将其存入数组cd中。
	//start变量用于追踪当前结点在编码字符串中的起始位置。
     
     free(cd);
     return Hfm;
     //返回经过哈夫曼编码处理后的哈夫曼树结构体。
}


//7.将哈夫曼存入文件中
Huffman InitbuildHuffman(Huffman Hfm)
{
     int i;
     FILE *fp;
	 //定义了一个指向FILE类型的指针fp,用于表示文件的引用。
     
     fp = fopen("data.txt","wt");
     //使用fopen函数打开一个名为"data.txt"的文件进行写入操作。如果该文件不存在,它将被创建。
     
     Hfm = InputHuffman(Hfm);
     //调用InputHuffman函数来初始化哈夫曼树。用户将在此过程中输入字符和权重的值。
     
     Hfm = HuffmanCoding(Hfm);
     //调用HuffmanCoding函数来创建哈夫曼树并生成哈夫曼编码。
     
     fprintf(fp,"%d\n",Hfm.longth);
     //将哈夫曼树中叶子结点的数量写入文件中,并在末尾添加一个换行符。
     
     for(i = 1;i <= Hfm.longth;i++)
     {
     	fprintf(fp,"%c %d %s ", Hfm.c[i], Hfm.TREE[i].weight, Hfm.BM[i]); 	
	 }
    
     rewind(fp);
     //使用rewind函数将文件指针移动到文件的开头。
     
     fclose(fp);
     //使用fclose函数关闭文件句柄。
     
     printf("#初始化创建已完毕\n#已保存到文件'data.txt'中\n");
     return Hfm;
     //返回初始化并构建完成的哈夫曼树结构体的引用。
}

//8.译码
void Decoding(Huffman Hfm)
{
     HuffmanTree p;
     //用于指向哈夫曼树中的某个结点。
     
     int i,n;
     //i将用作临时变量,用于存储正在处理的结点的索引。
	 //n表示哈夫曼树中叶子结点的数量。
     
     int j = 0;
     char d[50];
     // 定义了一个字符数组d,用于存储输入的哈夫曼编码字符串。
     
     n = Hfm.longth;
     //将变量n赋值为哈夫曼树结构体中存储的叶子结点数量。
     
     printf("\n**********译码**********\n");
     printf("#请输入码文\n");
     scanf("%s",&d);
     printf("#译码为 : ");
     while(d[j])
      {
         p = &Hfm.TREE[2*n - 1];
         
         while(p->lchild||p->rchild)
         {
             if(d[j] == '0')
             { 
				 i = p->lchild;  
                  p = &Hfm.TREE[i]; 
			 }
             else
             { 
				 i = p->rchild;  
                 p = &Hfm.TREE[i]; 
			 }
             j++;
         }
         printf("%c",Hfm.c[i]);
     }
     //while(1)
     //当d[j]不为空(即d[j]表示的字符不是字符串的结束标记\0)时,继续执行循环。
	 //j变量用于追踪当前正在处理的字符在输入编码字符串中的位置。
	 
	 //while(2)
	 //在哈夫曼树中按照输入的哈夫曼编码进行搜索。
	 //如果当前编码为'0',则将p指向其左子结点;如果当前编码为'1',则将p指向其右子结点。
	 //j变量在每次比较后递增,以便处理编码字符串中的下一个字符。
} 

//9.将所有的叶子的哈夫曼编码输出
void Output(Huffman Hfm)
{
     int i,n;
     //i将用作循环变量,从1到n遍历哈夫曼树的所有叶子结点。n表示哈夫曼树中叶子结点的数量。
     
     n = Hfm.longth;
     printf("\n*****所有编码输出******\n");
     for(i = 1;i <= n;i++)
	 {
         printf("\n");
         printf("#字符: %c  ",Hfm.c[i]);
         printf("#编码: ");
         puts(Hfm.BM[i]);
         //将当前叶子结点的哈夫曼编码打印出来。puts函数会自动在编码字符串末尾添加一个换行符。
     }
}

//10.主函数
int main()
{ 
     printf("\n*——欢迎使用哈夫曼编码译码器!——*\n");
     printf("*在此系统中可以进行以下操作:\n"); 
     Huffman Hfm;
     char choice = 'a';
     while(choice != 'd')
     {
         printf("***************************\n");
         printf("*a. 新建初始化哈夫曼树    *\n");
         printf("*b. 进行译码              *\n");
         printf("*c. 输出所有编码          *\n");
         printf("*d. 退出哈夫曼编码译码器  *\n");
         printf("***************************\n");
         printf("\n请选择(选项对应的字母): ");
         scanf(" %c",&choice);
         switch(choice)
         {
             case 'a':
             Hfm = InitbuildHuffman(Hfm);
             printf("\n***请继续选择操作(回车)***\n");
             getch(); break;
      
			 case 'b':
             Decoding(Hfm);
             printf("\n***请继续选择操作(回车)***\n");
             getch(); break;
      
             case 'c':
             Output(Hfm);
             printf("\n***请继续选择操作(回车)***\n");
             getch(); break;
           
             case 'd':
             printf("\n|==欢迎下次使用(回车后退出程序)==|\n");
             getch(); break;
      
             default:
             printf("$$$输入信息错误!请重新输入!$$$(回车)\n\n");
             getch(); break;
         }
     }
     return 0;
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

reactor和proactor模型

Reactor模型是非阻塞的同步IO模型。在主线程中也就是IO处理单元中&#xff0c;只负责监听文件描述符上是否有事件发生&#xff0c;有的话就立即将事件通知工作线程&#xff0c;将socket可读可写事件放入请求队列&#xff0c;交给工作线程处理。 总而言之就是主线程监听有事件发…

期末考试结束,老师该如何私发成绩?

随着期末考试的落幕&#xff0c;校园里又恢复了往日的宁静。然而&#xff0c;对于老师们来说&#xff0c;这并不意味着工作的结束&#xff0c;相反&#xff0c;一系列繁琐的任务才刚刚开始。 成绩单的发放&#xff0c;就是其中一项让人头疼的工作。家长们焦急地等待着孩子的考试…

可视化作品集(08):能源电力领域

能源电力领域的可视化大屏&#xff0c;有着巨大的用武之地&#xff0c;不要小看它。 监控能源生产和消耗情况&#xff1a; 通过可视化大屏&#xff0c;可以实时监控能源生产和消耗情况&#xff0c;包括发电量、能源供应情况、能源消耗情况等&#xff0c;帮助管理者及时了解能…

14-39 剑和诗人13 - 顶级大模型测试分析和建议

​​​​​ 随着对高级语言功能的需求不断飙升&#xff0c;市场上涌现出大量语言模型&#xff0c;每种模型都拥有独特的优势和功能。然而&#xff0c;驾驭这个错综复杂的生态系统可能是一项艰巨的任务&#xff0c;开发人员和研究人员经常面临选择最适合其特定需求的模型的挑战。…

React中的useMemo和memo

引言 React是一个声明式的JavaScript库&#xff0c;用于构建用户界面。在开发过程中&#xff0c;性能优化是一个重要的方面。useMemo和memo是React提供的工具&#xff0c;用于帮助开发者避免不必要的渲染和计算&#xff0c;从而提升应用性能。 问题背景 在React应用中&#…

Golang | Leetcode Golang题解之第214题最短回文串

题目&#xff1a; 题解&#xff1a; func shortestPalindrome(s string) string {n : len(s)fail : make([]int, n)for i : 0; i < n; i {fail[i] -1}for i : 1; i < n; i {j : fail[i - 1]for j ! -1 && s[j 1] ! s[i] {j fail[j]}if s[j 1] s[i] {fail[i…

【密码学】密码学中的四种攻击方式和两种攻击手段

在密码学中&#xff0c;攻击方式通常指的是密码分析者试图破解加密信息或绕过安全机制的各种策略。根据密码分析者对明文、密文以及加密算法的知识程度&#xff0c;攻击可以分为以下四种基本类型&#xff1a; 一、四种攻击的定义 &#xff08;1&#xff09;唯密文攻击(COA, C…

MySQL学习(7):4种常用函数

1.字符串函数 mysql中内置了很多字符串函数&#xff0c;常用的几种如下&#xff1a; concat(s1,s2,s3...)字符串拼接&#xff0c;将s1,s2,s3...拼接成一个字符串 lower(s1) 将字符串s1全部转为小写upper(s1)将字符串s1全部转为大写lpad(s1,5,*) 如果字符串s1不足5位&#xff…

对BSV区块链的曼达拉网络通俗易懂的解释

​​发表时间&#xff1a;2023年6月15日 BSV区块链正在引入“曼达拉”升级&#xff0c;使BSV区块链网络的拓扑结构能够适配Teranode&#xff0c;适配这个可以大幅扩容的节点软件。BSV区块链上曼达拉网络的概念并不会改变整个系统的核心规则&#xff1b;相反&#xff0c;它能够引…

vue3使用方式汇总

1、引入iconfont阿里图库图标&#xff1a; 1.1 进入阿里图标网站&#xff1a; iconfont阿里&#xff1a;https://www.iconfont.cn/ 1.2 添加图标&#xff1a; 1.3 下载代码&#xff1a; 1.4 在vue3中配置代码&#xff1a; 将其代码复制到src/assets/fonts/目录下&#xff1…

Python打开Excel文档并读取数据

Python 版本 目前 Python 3 版本为主流版本&#xff0c;这里测试的版本是&#xff1a;Python 3.10.5。 常用库说明 Python 操作 Excel 的常用库有&#xff1a;xlrd、xlwt、xlutils、openpyxl、pandas。这里主要说明下 Excel 文档 .xls 格式和 .xlsx 格式的文档打开和读取。 …

python爬虫入门(三)之HTML网页结构

一、什么是HTML 1、网页的三大技术要素&#xff1a; HTML定义网页的结构和信息&#xff08;骨架血肉&#xff09;CSS定义网页的样式&#xff08;衣服&#xff09;JavaScript定义用户和网页的交互逻辑&#xff08;动作&#xff09; 2、一个最简单的HTML&#xff1a;用<>…

【TB作品】51单片机 Proteus仿真 超声波读取+LCD1602显示仿真12MHZ

实验报告&#xff1a;51单片机 Proteus仿真 超声波读取LCD1602显示仿真 一、实验背景 本实验旨在使用51单片机&#xff08;AT89C51&#xff09;结合超声波传感器HC-SR04和LCD1602液晶显示屏&#xff0c;通过Proteus仿真平台实现超声波测距功能&#xff0c;并将测得的距离显示…

基于Python API的机械臂UDP上报设置及读取

睿尔曼机械臂提供了1个可持续读取机械臂状态的接口&#xff0c;UDP通信状态反馈接口。 该接口提供了json协议、API的读取&#xff0c;设置通信开启之后无需再进行设置即可以固定频率读取。 Python程序源码可从以下网盘地址获取&#xff08;地址永久有效&#xff09;&#xff1…

排序(2)

我们在排序&#xff08;1&#xff09;中说到选择排序的代码&#xff1a; void SelectSort(int* a,int n) {int begin0,endn-1;int minibegin,maxbegin;for(int ibegin1;i<end;i){if(a[i]>a[max]){maxii;}if(a[i]<a[mini]){minii;}begin;--end;}Swap(&a[beign],&a…

【NTN 卫星通信】Starlink基于终端用户的测量以及测试概述

1 概述 收集了一些starlink的资料&#xff0c;是基于终端侧部署在野外的一些测试以及测量结果。 2 低地球轨道卫星网络概述 低地球轨道卫星网络(lsn)被认为是即将到来的6G中真正实现全球覆盖的关键基础设施。本文介绍了我们对Starlink端到端网络特征的初步测量结果和观测结果&…

澳大利亚媒体发稿:怎样用图表提高易读性?-华媒舍

媒体发稿的可读性变得尤为重要。读者们不会再有时间与耐心去阅读文章繁琐的文本&#xff0c;他们更喜欢简洁明了的信息展现形式&#xff0c;在其中图表是一种极为高效的专用工具。下面我们就详细介绍怎么使用图表提高澳大利亚新闻媒体发稿的可读性&#xff0c;以适应读者的需要…

day01:项目概述,环境搭建

文章目录 软件开发整体介绍软件开发流程角色分工软件环境 外卖平台项目介绍项目介绍定位功能架构 产品原型技术选型 开发环境搭建整体结构&#xff1a;前后端分离开发前后端混合开发缺点前后端分离开发 前端环境搭建Nginx 后端环境搭建熟悉项目结构使用Git进行版本控制数据库环…

VSCode使用SSH无需输入密码远程连接服务器

目录 一、密钥生成 1、使用windows11自带的命令行 2、使用putty工具 二、查看密钥 三、设置服务器 这个过程是比较简单的&#xff0c;为了方便后续留用和查看&#xff0c;整理个笔记放着。 一、密钥生成 1、使用windows11自带的命令行 在任一文件夹中&#xff0c;空白处…

2024世界人工智能大会,神仙打架

B站&#xff1a;啥都会一点的研究生公众号&#xff1a;啥都会一点的研究生 AI圈最近又发生了啥新鲜事&#xff1f; 该栏目以周更频率总结国内外前沿AI动态&#xff0c;感兴趣的可以点击订阅合集以及时收到最新推送 B站首秀世界人工智能大会&#xff0c;展示自研AI技术与AIGC…