20172304 蓝墨云实验哈夫曼树

20172304 蓝墨云实验哈夫曼树

实验要求

  • 设有字符集:S={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}。

    给定一个包含26个英文字母的文件,统计每个字符出现的概率,根据计算的概率构造一颗哈夫曼树。

    并完成对英文文件的编码和解码。

  • 要求:
    • (1) 准备一个包含26个英文字母的英文文件(可以不包含标点符号等),统计各个字符的概率
    • (2) 构造哈夫曼树
    • (3) 对英文文件进行编码,输出一个编码后的文件
    • (4) 对编码文件进行解码,输出一个解码后的文件
    • (5) 撰写博客记录实验的设计和实现过程,并将源代码传到码云
    • (6) 把实验结果截图上传到云班课

哈夫曼编码

  • 哈夫曼编码(Huffman Coding),又称霍夫曼编码,是一种编码方式,哈夫曼编码是可变字长编码(VLC)的一种。Huffman于1952年提出一种编码方法,该方法完全依据字符出现概率来构造异字头的平均长度最短的码字,有时称之为最佳编码,一般就叫做Huffman编码(有时也称为霍夫曼编码)。

原理

这个老师上课说过。首先确定文件中各个字符的概率然后将其。按照概率大小。将概率小的放在离根节点较近的地方。

这里用数字来代表字符的概率然后,首先将概率最小的两个字符放到一起,将它们的概率合成父结点的概率,然后父结点参与比较再次在备选集中找到比较小的两两结合,然后再次合成父结点的概率。

具体实现哈夫曼树

因为哈夫曼需要考虑到父结点的影响,所以定义了相关的左孩子右孩子的方法。

package HuffmanTree;

public class HNode {
    /**
     * 节点类
     * @author LiRui
     *
     */
       public String code = "";// 节点的哈夫曼编码       public String data = "";// 节点的数据
       public int count;// 节点的权值
       public HNode lChild;
       public HNode rChild;

        public HNode() {
        }

        public HNode(String data, int count) {
            this.data = data;
            this.count = count;
        }

        public HNode(int count, HNode lChild, HNode rChild) {
            this.count = count;
            this.lChild = lChild;
            this.rChild = rChild;
        }

        public HNode(String data, int count, HNode lChild, HNode rChild) {
            this.data = data;
            this.count = count;
            this.lChild = lChild;
            this.rChild = rChild;
        }

    }
  • 构造哈夫曼树

    • 哈夫曼树的构造,并不需要像创建二叉树的那么多的方法,满足形成哈夫曼编码的部分就好。分享的那篇博客有相关的内容,我们只需要在此基础上添加每次创建的的编码值就可以,左侧0右侧1就可以。
    • 创建树,先将结点进行排序,利用结点类中实现的比较方法,分别将左孩子定义为列表中的倒数第二个,因为左侧编码为0,所以让该结点的编码为0;右孩子为列表中的倒数第一个,因为右侧编码为1,所以让该结点的编码为1,双亲结点根据所讲内容为左右结点权重相加之和,把双亲结点加入列表中,然后删除倒数两个结点并添加双亲结点,再次进行循环,排序,不断从列表中把倒数两个结点删除,直至跳出循环,此时列表中的结点只剩一个,该结点的左右部分包含了所有按照编码进行添加的元素内容。
    public HuffmanNode<T> createTree(List<HuffmanNode<T>> nodes) {
          while (nodes.size() > 1) {
              Collections.sort(nodes);
              HuffmanNode<T> left = nodes.get(nodes.size() - 2);//令其左孩子的编码为0
              left.setCode("0");
              HuffmanNode<T> right = nodes.get(nodes.size() - 1);//令其右孩子的编码为1
              right.setCode("1");
              HuffmanNode<T> parent = new HuffmanNode<T>(null, left.getWeight() + right.getWeight());
              parent.setlChild(left);
              parent.setrChild(right);
              nodes.remove(left);
              nodes.remove(right);
              nodes.add(parent);
          }
          return nodes.get(0);
      }
    • 输出编码,利用哈夫曼树的createTree方法来实现编码内容,然后输出每一个结点的编码值,按照每个分支的0/1进行,左侧0右侧1进行。
    public List<HuffmanNode<T>> breath(HuffmanNode<T> root) {
          List<HuffmanNode<T>> list = new ArrayList<HuffmanNode<T>>();
          Queue<HuffmanNode<T>> queue = new LinkedList<>();
          if (root != null) {
              queue.offer(root);
              root.getlChild().setCode(root.getCode() + "0");
              root.getrChild().setCode(root.getCode() + "1");
          }
    
          while (!queue.isEmpty()) {
              list.add(queue.peek());
              HuffmanNode<T> node = queue.poll();
              if (node.getlChild() != null)
                  node.getlChild().setCode(node.getCode() + "0");
              if (node.getrChild() != null)
                  node.getrChild().setCode(node.getCode() + "1");
              if (node.getlChild() != null)
                  queue.offer(node.getlChild());
              if (node.getrChild() != null)
                  queue.offer(node.getrChild());
    
          }
          return list;
      }
  • 编码与解码
    • 编码部分,需要按照读取的内容进行添加每一个的编码值,变量result即为编码之后的内容,将这部分内容写入一个文件。
     String result = "";
          for(int f = 0; f < sum; f++){
              for(int j = 0;j<letter.length;j++){
                  if(neirong.charAt(f) == letter[j].charAt(0))
                      result += code[j];
              }
          }

    • 解码部分,需要每次调出编码内容的一个0/1,然后每读出一位就要判断一次是否有对应的编码值,如果有就输出,如果没有就不断往下读取内容,并删除读出的内容避免重复读取,直至将编码的内容全部读完解码完成。利用两个循环,外层循环进行每一次的添加一个编码位,内层循环将添加一位的内容进行对比,符合就为编码值,不符合就需要在添加一位。
    for(int h = list4.size(); h > 0; h--){
              string1 = string1 + list4.get(0);
              list4.remove(0);
              for(int i=0;i<code.length;i++){
                  if (string1.equals(code[i])) {
                      string2 = string2+""+letter[i];
                      string1 = "";
                  }
              }
          }

  • 计算概率和重复次数
    • 从文件中进行读取,并按照英文字母的种类进行记录每一个字母的出现次数,这里我添加了一个记录空格、逗号和句号,便于编写文件内容。针对文件中的内容总不能针对每一个位置都要循环确定一遍是否含有那个字母,利用我在实验室尝试过的代码,Collections.frequency();来实现,里面的两个形式参数,按照第二个形式参数表现的内容,记录其在第一个列表内的重复次数,这样只需要将文件内容一每个字符的形式存放在一个列表中就行,然后进行比对即可。总的字符数目就是每一个字母的重复次数。
    for(int a = 0; a <= getFileLineCount(file); a++){
              String tp = bufferedReader.readLine();
              neirong += tp;
              for(int b = 0; b < tp.length(); b++){
                      list2.add(String.valueOf(tp.charAt(b)));
              }
              Esum[0] += Collections.frequency(list2, list1.get(0));
              Esum[1] += Collections.frequency(list2, list1.get(1));
              Esum[2] += Collections.frequency(list2, list1.get(2));
              Esum[3] += Collections.frequency(list2, list1.get(3));
              Esum[4] += Collections.frequency(list2, list1.get(4));
              Esum[5] += Collections.frequency(list2, list1.get(5));
              Esum[6] += Collections.frequency(list2, list1.get(6));
              Esum[7] += Collections.frequency(list2, list1.get(7));
              Esum[8] += Collections.frequency(list2, list1.get(8));
              Esum[9] += Collections.frequency(list2, list1.get(9));
              Esum[10] += Collections.frequency(list2, list1.get(10));
              Esum[11] += Collections.frequency(list2, list1.get(11));
              Esum[12] += Collections.frequency(list2, list1.get(12));
              Esum[13] += Collections.frequency(list2, list1.get(13));
              Esum[14] += Collections.frequency(list2, list1.get(14));
              Esum[15] += Collections.frequency(list2, list1.get(15));
              Esum[16] += Collections.frequency(list2, list1.get(16));
              Esum[17] += Collections.frequency(list2, list1.get(17));
              Esum[18] += Collections.frequency(list2, list1.get(18));
              Esum[19] += Collections.frequency(list2, list1.get(19));
              Esum[20] += Collections.frequency(list2, list1.get(20));
              Esum[21] += Collections.frequency(list2, list1.get(21));
              Esum[22] += Collections.frequency(list2, list1.get(22));
              Esum[23] += Collections.frequency(list2, list1.get(23));
              Esum[24] += Collections.frequency(list2, list1.get(24));
              Esum[25] += Collections.frequency(list2, list1.get(25));
              Esum[26] += Collections.frequency(list2, list1.get(26));
              Esum[27] += Collections.frequency(list2, list1.get(27));
              Esum[28] += Collections.frequency(list2, list1.get(28));
          }

    • 总个数
    for(int c = 0; c < Esum.length; c++)
              sum += Esum[c];
          System.out.println("总字母个数:" + sum);

    在此部分我用了一个可以确定文件内容行数的方法,便于当文件出现多行的时候的读写和编码、解码的相关操作。

  • 读写文件
    • 读写文件的部分只需要调用File相关类进行编写即可,我直接用了字符流直接将String类型的内容进行添加。
          File file = new File("英文文件.txt");
          File file1 = new File("编码文件.txt");
          File file2 = new File("解码文件.txt");
    
          if (!file1.exists() && !file2.exists())
          {
              file1.createNewFile();
              file2.createNewFile();
          }
          FileReader fileReader = new FileReader(file);
          BufferedReader bufferedReader = new BufferedReader(fileReader);
          FileWriter fileWriter1 = new FileWriter(file1);
          FileWriter fileWriter2 = new FileWriter(file2);
          fileWriter1.write(result);
          fileWriter2.write(string2);
          fileWriter1.close();
          fileWriter2.close();

码云链接

感悟

 哈夫曼是一种用于压缩的算法,是一种很实用的算法,但可能是个人能力的限制,在具体实现过程中遇见了很多困难,在具体的代码实现中,借鉴了很多网页和同学的思路。

参考资料

原文地址:https://www.cnblogs.com/15248252144dzx/p/10111583.html

时间: 2024-07-30 16:21:08

20172304 蓝墨云实验哈夫曼树的相关文章

哈夫曼树的编码实验

Java哈夫曼编码实验--哈夫曼树的建立,编码与解码 建树,造树,编码,解码 一.哈夫曼树编码介绍 1.哈夫曼树: (1)定义:假设有n个权值{w1, w2, ..., wn},试构造一棵含有n个叶子结点的二叉树,每个叶子节点带权威wi,则其中带权路径长度WPL最小的二叉树叫做最优二叉树或者哈夫曼树. (2)特点:哈夫曼树中没有度为1的结点,故由n0 = n2+1以及m= n0+n1+n2,n1=0可推出m=2*n0-1,即一棵有n个叶子节点的哈夫曼树共有2n-1个节点. 2.哈夫曼编码步骤:

20155317 2016-2017-2 蓝墨云班课考题第2周

20155317 2016-2017-2 蓝墨云班课考题第2周 三: 1.用gcc -g编译vi输入的代码 2.在main函数中设置一个行断点 3.在main函数增加一个空循环,循环次数为自己学号后4位,设置一个约为学号一半的条件断点 4.提交调试过程截图(一定包含条件断点的),要全屏,包含自己的学号信息 第一步:输入 gcc -g *.c -o mian 第二步:首先在mian中写入一个空循环,以学号为最终的结束点 第三步:再输入gdb main 进入调试阶段:在调试阶段里,首先输入start

20172310 蓝墨云ASL测试 2018-1938872

20172310 蓝墨云ASL测试 2018-1938872 题目: 已知线性表具有元素{5,13,19,21,37,56,64,75,80,88,92},如果使用折半查找法,ASL是多少? 解答:(今天因为去啦啦操彩排,所以现在完成这篇博客) 首先,因为没有上课,所以自己去理解折半查找法 在计算机科学中,折半搜索(英语:half-interval search),也称二分搜索(英语:binary search).对数搜索(英语:logarithmic search),是一种在有序数组中查找某一

20172301 哈夫曼树实验报告

20172301 哈夫曼树实验报告 课程:<Java软件结构与数据结构> 班级: 1723 姓名: 郭恺 学号:20172301 实验教师:王志强老师 实验日期:2018年12月9日 必修/选修: 必修 一.实验内容 二.实验过程 哈夫曼树 哈夫曼(Haffman)树,也称最优二叉树,是指对于一组带有确定权值的叶结点.构造的具有最小带权路径长度的二叉树. 二叉树的路径长度是指由根结点到所有的叶结点的路径长度之和. 一棵二叉树要想它的带权路径长度最小,必须使权值越大的叶结点越靠近根结点,而权值越

疫情下的在线上课方案:QQ直播+蓝墨云班课

目录 疫情下的在线上课方案:QQ直播+蓝墨云班课 使用QQ进行直播 材料 QQ直播步骤 其他问题 使用蓝墨云班课加强学习效果 教材问题 参考资料 疫情下的在线上课方案:QQ直播+蓝墨云班课 全国新型冠状病毒肺炎疫情肯定要影响到开学后上课了,很多学校都考虑线上开课方案了,我觉得这是提升教学信息化的一个良机. 多年来我倡导"老师做教练,学生做中学",积极推广翻转课堂.如果您熟悉翻转课堂,其实问题就解决了,不熟悉翻转课堂的老师可以参考一下我写的 "基于蓝墨云班课的翻转课堂实践&qu

新学的代码:哈夫曼树的生成实验

// Haffman.cpp : Defines the entry point for the console application.// #include "stdafx.h"#include <stdio.h>#include <string.h>typedef char DataType; struct element  //结点定义{        DataType data;   float weight;//此字符的权值      int par

哈夫曼树的创建 实验报告

代码参考严蔚敏<数据结构> 1 #include<string.h> 2 #include<stdlib.h> 3 #include<stdio.h> 4 5 int m,s1,s2; 6 7 typedef struct 8 { 9 unsigned int weight; 10 unsigned int parent,lchild,rchild; 11 } HTNode,*HuffmanTree; //动态分配数组存储哈夫曼树 12 typedef ch

5678: 数据结构实验:哈夫曼树和编码

描述 当用 n 个结点(都做叶子结点且都有各自的权值)试图构建一棵树时,如果构建的这棵树的带权路径长度最小,称这棵树为“最优二叉树”,有时也叫“赫夫曼树”或者“哈夫曼树”. 现给定若干权值,请构建一棵哈夫曼树,并输出各个权值对应的哈夫曼编码长度. 哈夫曼树中的结点定义如下: //哈夫曼树结点结构 typedef struct { int weight;//结点权重 int parent, left, right;//父结点.左孩子.右孩子在数组中的位置下标 }HTNode, *HuffmanTr

哈夫曼树+哈夫曼编码

前天acm实验课,老师教了几种排序,抓的一套题上有一个哈夫曼树的题,正好之前离散数学也讲过哈夫曼树,这里我就结合课本,写一篇关于哈夫曼树的博客. 哈夫曼树的介绍 Huffman Tree,中文名是哈夫曼树或霍夫曼树,它是最优二叉树. 定义:给定n个权值作为n个叶子结点,构造一棵二叉树,若树的带权路径长度达到最小,则这棵树被称为哈夫曼树. 这个定义里面涉及到了几个陌生的概念,下面就是一颗哈夫曼树,我们来看图解答. (01) 路径和路径长度 定义:在一棵树中,从一个结点往下可以达到的孩子或孙子结点之