Java数据结构之《哈夫曼编码大全》(难度系数100)

news2024/11/23 13:30:58

一、前言:

  这是怀化学院的:Java数据结构中的一道难度偏难(偏难理解)的一道编程题(此方法为博主自己研究与学习一名叫qing影的博主,问题基本解决,若有bug欢迎下方评论提出意见,我会第一时间改进代码,谢谢!) 后面其他编程题只要我写完,并成功实现,会陆续更新,记得三连哈哈! 所有答案供参考,不是标准答案,是博主自己研究的写法。(这一个题书上也有现成类似的代码,重要的是理解它的算法原理!)

二、题目要求如下:

(第 9 题) 哈夫曼编码大全(难度系数100)

题目: 哈夫曼编码大全

描述:

关于哈夫曼树的建立,编码,解码。

输入

第一行输入数字N,代表总共有多少个字符以及权值

第二第三行分别是一行字符串,以及每个字符对应的权值

接下来输入一个数M,表示接下来有M行字符串,要求你对每个字符串进行编码

再输入一个数X,表示接下来有X行编码,要求你对每行编码进行解码

输出

第一行输出所有节点的权重

接下来输出N行,每行以 “a:001”的格式输出每个字符对应的编码

接着输出M行,对输入的字符串的编码结果

最后,输出X行的解码结果

输入样例

6
abcdef
50 10 5 5 20 10
2
abcdef
defabaabbc
2
011001100100110110101101100
1100011000110101100101100

输出样例

50 10 5 5 20 10 10 20 30 50 100
a:0
b:100
c:1100
d:1101
e:111
f:101
010011001101111101
11011111010100001001001100
accbdfadb
cacadacfb

三、代码实现: (代码的做题原理全部在代码注释中) 

<1>因为学校的提交测试的网站:不能有自己创建的包的声明,不能有代码注释以及要把所有的操作放在同一个类中等等......,所以我首先放一个干净的代码实现:(此题提交成功!),有注释的代码在下方!!!!!!

<但是这次我向学校网站提交时没有删除注释,能提交成功......>

import java.util.Scanner;

public class Main {

    public static class Node{
        private int data;
        private int lChild;
        private int rChild;
        private int parent;
        public Node(){
            this.data=0;
            this.lChild=0;
            this.rChild=0;
            this.parent=0;
        }
    }

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int N = sc.nextInt();
        String node01 = sc.next();
        Node[] node = new Node[(2*N)-1];
        for(int i=0;i<N;i++) {
            node[i] = new Node();
            node[i].data = sc.nextInt();
        }

        for(int j=0;j<N-1;j++){
            int low01=-1;
            int low02=-1;
            for(int k=0;k<j+N;k++){
                if(node[k].parent==0&&(low01==-1||node[k].data<node[low01].data)){
                    low02=low01;
                    low01=k;
                }else if(node[k].parent==0&&(low02==-1||node[k].data<node[low02].data)){
                    low02=k;
                }

            }

            node[N+j]=new Node();
            node[N+j].data=node[low01].data+node[low02].data;
            node[N+j].lChild=low01;
            node[N+j].rChild=low02;
            node[low01].parent=node[low02].parent=N+j;
        }
        for(int i=0;i<2*N-1;i++){
            System.out.print(node[i].data+" ");
        }
        System.out.println();

        String[] code = new String[N];
        for(int i=0;i<N;i++){
            StringBuilder str01 = new StringBuilder();
            int Child=i;
            int Parent= node[i].parent;
            while(Parent!=0){
                if(node[Parent].lChild==Child){
                    str01.append('0');
                }
                else if(node[Parent].rChild==Child) {
                    str01.append('1');
                }
                Child=Parent;
                Parent=node[Parent].parent;
            }
            str01.reverse();
            code[i] = str01.toString();
            System.out.println(node01.charAt(i)+":"+code[i]);

        }

        int M=sc.nextInt();
        for(int j=0;j<M;j++){
            String Node_test=sc.next();
            int len =Node_test.length();
            for(int k=0;k<len;k++){
                int index = node01.indexOf(Node_test.charAt(k));
                System.out.print(code[index]);
            }
            System.out.println();
        }

        int X = sc.nextInt();
        for(int k=0;k<X;k++){
            String Code_test=sc.next();
            int present=0;
            int len =Code_test.length();
            while(present<len) {
                for (int h = 0; h < N; h++) {
                    int index = Code_test.indexOf(code[h], present);
                    if (index == present) {
                        present=present+code[h].length();
                        System.out.print(node01.charAt(h));
                        break;
                    }
                }
            }
            System.out.println();
        }

    }
}

补充:题目意思一定要深度揣摩一下,没有提示就得自己根据它题目给的输入输出来推一下原理了,不然就是盲目下手出错很多,大家一定要先去我之前那篇《构造哈夫曼树》去参考一下原理,再来攻克这道大题,(书上也提到了<使用哈夫曼树的算法求报文字符编码,以及根据编码去解码>)

(1)我把所有的实现细节包括哈夫曼树的创建、字符的编码,以及解码操作等等都放在一个类中。

import java.util.Scanner;

public class Main {
    //静态结点内部类,不然主方法用不了
    public static class Node{
        private int data;  //代表当前的哈夫曼树总权值
        private int lChild;  //左孩子
        private int rChild;  //右孩子
        private int parent;  //父结点
        public Node(){
            this.data=0;
            //刚开始所有的叶子结点默认都没有孩子结点和父亲结点
            this.lChild=0;
            this.rChild=0;
            this.parent=0;
        }
    }

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int N = sc.nextInt();
        String node01 = sc.next();  //代表N个最开始的叶子结点的字符名称
        //先创建一个结点数组来存储:最终构成的哈夫曼树的所有结点
        //因为叶子结点个数为N,则对于非空二叉树来说:拥有2度的结点的个数=叶子结点个数减一,则总结点数=N-1+N=2*N-1
        Node[] node = new Node[(2*N)-1];
        for(int i=0;i<N;i++) {
            node[i] = new Node();  //依次创建新结点,并先把最初的叶子结点放入
            node[i].data = sc.nextInt();
        }
        //接下来就是开始构造哈夫曼树了
        //控制最大只能到(2*N-1)-1下标
        for(int j=0;j<N-1;j++){
            //默认最小结点的下标
            int low01=-1;
            //默认次小结点的下标
            int low02=-1;
            //因为当两个较小的结点生成一个父结点时,那个父节点也可以被当作一个孩子结点与另外一个较小的没有父结点的结点生成一个新的哈夫曼树
            for(int k=0;k<j+N;k++){
                //首先条件是无父亲结点,再依次去找一个最小权值的下标
                if(node[k].parent==0&&(low01==-1||node[k].data<node[low01].data)){
                    low02=low01;  //如果找到了就先把当前"较小"值的下标给"次小"
                    low01=k;  //然后再把之前"最小"小标更新成真正的最小值的下标
                }else if(node[k].parent==0&&(low02==-1||node[k].data<node[low02].data)){
                    //最小下标条件不满足,就给次小下标赋值,再依次遍历直到合成完所有的结点变成一个最终的哈夫曼树
                    low02=k;
                }

            }
            //每次找到当前最小和次小下标的值时就要创建一个新的父结点
            node[N+j]=new Node(); //每次创建出的新的父结点都要登记进数组去
            node[N+j].data=node[low01].data+node[low02].data;  //记得给新结点赋权值
            //它对应的孩子结点是,左边为最小的,右边为次小的
            node[N+j].lChild=low01;
            node[N+j].rChild=low02;
            //父结点
            node[low01].parent=node[low02].parent=N+j; //在原有的结点个数的位置上加1
        }
        //输出当前哈夫曼树的所有结点的权重
        for(int i=0;i<2*N-1;i++){
            System.out.print(node[i].data+" ");
        }
        System.out.println();

        //接下来进行对刚刚输入的字符串中的每个字符进行编码
        String[] code = new String[N];  //用来储存对应字符的编码值,之前有N个字符嘛
        for(int i=0;i<N;i++){
            //这里用到StringBuilder类,它不保证同步,因为它的长度是可变的(String类的字符串长度不可变),所以用这个方便增删操作
            StringBuilder str01 = new StringBuilder();
            //我们要假设每棵左子树的根节点的边为"0",而每棵右子树的根节点的边为"1",我们从最开始输入的根节点"a"向上到最终的根结点为止再遍历其他的前N个根节点
            int Child=i;  //这个意思就是当前要编码的字符
            int Parent= node[i].parent;  //就是向上找父结点,直到找到最上面的根结点为止
            while(Parent!=0){  //只要有父结点,就一直向上
                if(node[Parent].lChild==Child){  //这个意思就是当前要编码的字符的父结点,它到当前要编码的字符是通过左子树边连接还是右子树边连接
                    str01.append('0');
                }
                else if(node[Parent].rChild==Child) {
                    str01.append('1');
                }
                //每次编码成功一位就要继续向上,就要更新
                Child=Parent;  //之前的孩子结点变成当前的父节点
                Parent=node[Parent].parent;  //上一层的父节点要重新赋值
            }
            str01.reverse();  //因为编码是从下往上的,所以最后记得反过来
            code[i] = str01.toString();  //返回其String类型的字符串表达形式,并存入code数组中
            //从第一个加入的字符开始输出每个字符的最终编码
            System.out.println(node01.charAt(i)+":"+code[i]);

        }

        //上面已经得到所有输入的字符的各自的编码,现在就可以对输入的测试字符串进行编码
        int M=sc.nextInt();  //测试的字符串个数
        for(int j=0;j<M;j++){
            //这是每次需要编码的字符串
            String Node_test=sc.next();
            int len =Node_test.length();
            for(int k=0;k<len;k++){
                //这样写的妙处在于,每次找到对应字符串中某个字符第一次出现的位置,它对应的下标也是该字符对应的编码值储存位置的下标
                int index = node01.indexOf(Node_test.charAt(k));
                System.out.print(code[index]);  //依次输出就行
            }
            System.out.println();  //每全部编码一次记得换行
        }

        //现在进行解码,就是编码匹配正确的输出对应的字符
        int X = sc.nextInt();  //需要解码的总次数
        for(int k=0;k<X;k++){
            String Code_test=sc.next();  //需要解码的字符串
            //这个地方用到一个很妙的匹配方法:indexOf(String str, int fromIndex) 返回指定子串的第一次出现的字符串中的索引,从指定的索引开始。
            int present=0; //要用一个变量来记录:当前遍历到需要解码的字符串的哪一个位置了
            int len =Code_test.length();
            while(present<len) {
                for (int h = 0; h < N; h++) {  //最大只能到N个
                    //就是从需要解码的字符串中的0索引下标开始向后找,与每个字符对应的编码相等的第一个位置
                    int index = Code_test.indexOf(code[h], present);
                    //如果第一个位置就是从0开始,那么直接把下次寻找的开始点更新
                    if (index == present) {
                        present=present+code[h].length();  //如果找到一个就要把记录当前到哪的下标更新,以便下次从这开始继续向后解码
                        //并且找到了相匹配就要输出该字符串编码对应的字符,因为前面对应的是code[h],则其编码值对应的字符也是h位置
                        System.out.print(node01.charAt(h));
                        break;  //退出当前循环,去进行下一次后面的寻找,直到下标不满足需要解码的字符串长度为止
                    }
                }
            }
            System.out.println();   //每解码完成功一个字符串就要换行
        }


    }
}

四、不同情况的代码测试运行结果:

<1>题目中的测试输入样例:(最好手打输入测试,直接复制可能格式问题导致报错!)

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

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

相关文章

poe与chatgpt那个功能更强大

在当前的人工智能领域&#xff0c;Poe Al Chat以其卓越的聊天能力和实用的功能&#xff0c;受到了大家的广泛关注和喜爱。本文好为您个绍Poe Al Chat的功能&#xff0c;以及我们国内用户如何进行充值订阅。Poe Al Chat是一个基于OpenAl的GPT模型开发的人工智能聊天工具。它能够…

前端使用视频作为背景图的方法

实现思路 通过 video source 引入视频&#xff0c;并对视频播放属性进行设置&#xff0c;再通过 css 使视频覆盖背景即可。 代码 <!DOCTYPE html> <html> <head> <meta charset"utf-8"> <title>有开发问题可联系作者</title>…

MarsEdit 5 for Mac(博客编辑软件) - 博客创作的完美拍档!

您是一位热爱写作和分享的博主吗&#xff1f;如果是的话&#xff0c;那么MarsEdit 5 for Mac将成为您创作之旅中的完美拍档&#xff01;这款博客编辑软件为Mac用户提供了无与伦比的便捷和灵活性。 MarsEdit 5具有直观的界面和强大的功能&#xff0c;让您轻松管理和编辑多个博客…

酷开科技以创新为动力用大数据提升品牌认知

在21世纪的今天&#xff0c;我们生活在一个被互联网深深改变的世界。互联网不仅改变了我们的生活方式&#xff0c;也正在改变我们的思维方式和工作方式。而互联网作为一种新的发展趋势&#xff0c;更是为我们提供了无数的机会和无限可能性&#xff0c;从电子商务时代到社交网络…

基于Maven构建OSGI应用(Maven和OSGI结合)

基于Maven构建OSGI应用。 使用Maven来构建项目&#xff0c;包括项目的创建、子模块buldle的创建等。使用OSGI来实现动态模块化管理&#xff0c;实现模块的热插拔效果&#xff08;即插即用&#xff09;。 创建一个Maven项目&#xff1a;helloworld&#xff0c;并在该项目下创建…

[ROS2] --- service

1 service介绍 1.1 service概念 话题通信是基于订阅/发布机制的&#xff0c;无论有没有订阅者&#xff0c;发布者都会周期发布数据&#xff0c;这种模式适合持续数据的收发&#xff0c;比如传感器数据。机器人系统中还有另外一些配置性质的数据&#xff0c;并不需要周期处理&…

迅为3588开发板 sudo: 无法解析主机:/DNS配置

环境申明 RK3588 ubuntu 22.04 jammy 迅为开发板 hostname 看是否有Host .&#xff0c;如果没有&#xff0c; sudo vim /etc/hostname在里面加一行&#xff0c;我这就这一个 iTOP-RK3588hosts 修改本地hosts sudo vim /etc/hosts127.0.0.1 localhost localhost iTOP-RK3…

ai人工智能洗稿软件免费有哪些好用?【最新AI洗稿软件盘点】

在当今信息时代&#xff0c;内容创作已成为人们工作和生活中不可或缺的一部分。为了提高创作效率&#xff0c;越来越多的人转向人工智能洗稿软件。本文将专心分享一些优质的免费AI洗稿软件。 免费AI洗稿软件的崛起 免费AI洗稿软件的崛起为许多创作者带来了便利&#xff0c;使他…

贪心算法背包问题c

在背包问题中&#xff0c;贪心算法通常用来解决0-1背包问题&#xff0c;也就是每种物品都有固定数量&#xff0c;你可以选择拿或者不拿&#xff0c;但不可以拿走部分。以下是一个用C语言实现的贪心算法的例子&#xff1a; #include <stdio.h>#define MAX_N 1000#define …

卷王开启验证码后无法登陆问题解决

问题描述 使用 docker 部署&#xff0c;后台设置开启验证&#xff0c;重启服务器之后&#xff0c;docker重启&#xff0c;再次访问系统&#xff0c;验证码获取失败&#xff0c;导致无法进行验证&#xff0c;也就无法登陆系统。 如果不了解卷王的&#xff0c;可以去官网看下。…

从零开始搭建企业管理系统(三):集成 Spring Data Jpa

集成 Spring Data Jpa 什么是 Jpa什么是 Spring Data Jpa什么是 HibernateJPA、Spring Data Jpa、Hibernate 之间的关系集成 Spring Data JpaPOM 依赖配置文件UserEntity启动程序Jpa 配置Jpa 注解UserRepositoryUserServiceUserServiceImplUserControllerBaseEntity 什么是 Jpa…

.NET Core 依赖注入 Microsoft.Extensions.DependencyInjection

文章目录 前言什么是依赖注入C# 使用依赖注入框架介绍 Microsoft.Extensions.DependencyInjectionNuget安装简单单例使用打印结果 自动装配举例自动装配测试用例打印结果自动装配执行顺序测试用例有歧义构造函数渐进式构造函数循环依赖 自动装配结论 手动装配手动注入别名注入 …

ShardingSphere数据分片之分表操作

1、概述 Apache ShardingSphere 是一款分布式的数据库生态系统&#xff0c; 可以将任意数据库转换为分布式数据库&#xff0c;并通过数据分片、弹性伸缩、加密等能力对原有数据库进行增强。 Apache ShardingSphere 设计哲学为 Database Plus&#xff0c;旨在构建异构数据库上…

L1-030:一帮一

题目描述 “一帮一学习小组”是中小学中常见的学习组织方式&#xff0c;老师把学习成绩靠前的学生跟学习成绩靠后的学生排在一组。本题就请你编写程序帮助老师自动完成这个分配工作&#xff0c;即在得到全班学生的排名后&#xff0c;在当前尚未分组的学生中&#xff0c;将名次最…

使用Postman进行自动化集成测试

1 前言 笔者在使用Node开发HTTP接口的过程中&#xff0c;发现当接口数量越来越多&#xff0c;且接口之间互相依赖时&#xff0c;接口测试流程就会变得十分繁琐&#xff0c;且容易出错。那如何才能高效且全面地对接口进行测试呢&#xff1f; 通过实践&#xff0c;笔者发现可以…

程序员的职业连续性就那么重要吗?

大家好&#xff0c;我是风筝&#xff0c;微信搜「古时的风筝」&#xff0c;更多干货 年初的时候时候一个同学跟我聊天说起最近面试的经历。说投了不少简历&#xff0c;但是面试的机会不多&#xff0c;而且有的负责照片的 HR 直接跟他说&#xff1a;“你的工作连续性不达标&…

AI专题报告:AI多模态提升商业化价值,应用多点开花验证景气度

今天分享的AI系列深度研究报告&#xff1a;《AI专题报告&#xff1a;AI多模态提升商业化价值&#xff0c;应用多点开花验证景气度》。 &#xff08;报告出品方&#xff1a;太平洋证券&#xff09; 报告共计&#xff1a;21页 1 一周行情回顾 上周上证综指、深证成指、创业板指…

两个月软考-高项上岸

文章目录 前言结缘软考功亏一篑有始有终2个月计划资料部分计划截图 总结 前言 我们看小说或者电视剧电影都会看到这样的情节&#xff0c;主角一开始锦衣玉食&#xff0c;突然家道中落&#xff0c;啥都没了&#xff0c;主角再一路奋起重新找回了属于自己的一切&#xff1b;还有…

视频汇聚/音视频流媒体视频平台/视频监控EasyCVR分享页面无法播放,该如何解决?

国标GB28181安防视频监控/视频集中存储/云存储EasyCVR平台可拓展性强、视频能力灵活、部署轻快&#xff0c;可支持的主流标准协议有国标GB28181、RTSP/Onvif、RTMP等&#xff0c;以及支持厂家私有协议与SDK接入&#xff0c;包括海康Ehome、海大宇等设备的SDK等。平台既具备传统…