20182310 哈夫曼编码

20182310 《数据结构与面向对象程序设计》实践作业——哈夫曼树的编码

课程:《数据结构与面向对象程序设计》
班级:1823
姓名:周烔
学号:20182310
实验老师:王志强
选修/必修:必修

1.任务详情:

设有字符集: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)把实验结果截图上传到云班课

2.分析和思路:

  • 1.首先应该了解哈夫曼树的概念,哈夫曼(Haffman)树,也称最优二叉树,是指对于一组带有确定权值的叶结点、构造的具有最小带权路径长度的二叉树。
    二叉树的路径长度是指由根结点到所有的叶结点的路径长度之和。
    一棵二叉树要想它的带权路径长度最小,必须使权值越大的叶结点越靠近根结点,而权值越小的叶结点越远离根结点。

  • 2.哈夫曼算法大致如下:

  • 3.接着了解哈夫曼编码:哈夫曼编码是一个通过哈夫曼树进行的一种编码,一般情况下,以字符:‘0’与‘1’表示。编码的实现过程很简单,只要实现哈夫曼树,通过遍历哈夫曼树,规定向左子树遍历一个节点编码为“0”,向右遍历一个节点编码为“1”,结束条件就是遍历到叶子节点。

  • 4.大概思路:首先要把字符集从文件中读出来,并保存在一个数组里面,计算每一个字符出现的频率,出现的概率存在另一个数组中。
    然后通过这两个步骤,构建出一个二维数组,每一个字符对应一个出现的频率。
    然后写一个compareTo方法,对该二维数组的第二个元素进行排序,得到从小到大的一个顺序。
    用构造哈夫曼树的方法,依次递归,找到最小的两个元素然后相加构成一个子树,直到最终的和为1结束。
    通过遍历哈夫曼树,得到每一个元素的编码值,并存进一个新的数组中。编码的话,通过循环的方式,对编码进行输出和解密 具体还是在代码中体现

3.实践过程以及遇到的问题及解决:

  • 1.首先创建结点,还应该定义左,右结点。
  • 2.其次是读写文件的代码,这个和之前学习的客户端接收服务器的代码有异曲同工之妙,于是我试着编了一下:
File file = new File("D:\\text.txt");
        Reader reader = new FileReader(file);
        BufferedReader bufferedReader = new BufferedReader(reader);
        String temp = bufferedReader.readLine();

在这里遇到了一个问题,就是在读文件之后运行代码时,当我的text文件放在C盘时,总是会出现如下问题:

没找到解决方法...最后尝试放在了D盘,结果成功。
写文件:

File file2 = new File("D:text.txt");
        Writer writer = new FileWriter(file2);
        writer.write(result1);
        writer.close();
  • 3.HuffmanTree类:创建树,当还有结点时,对结点进行排序,然后左孩子为数组中的个数-2的结点,右孩子为数组中的个数-1的结点,令左孩子的编码为0,右孩子的编码为1,双亲结点则为左右孩子相加的权重,把双亲结点加入链表中,从链表中把旧的左右孩子删除,直至链表中的结点只剩一个(也就是根结点)。
public static  Node createTree(List<Node> nodes) {
        // 只要nodes数组中有2个以上的节点
        while (nodes.size() > 1) {
            //进行从大到小的排序
            Collections.sort(nodes);
            //获取权值最小的两个节点
            Node left = nodes.get(nodes.size() - 1);
            Node right = nodes.get(nodes.size() - 2);
            //生成新节点,新节点的权值为两个子节点的权值之和
            Node parent = new Node('无', left.getWeight() + right.getWeight());
            //让新节点作为两个权值最小节点的父节点
            parent.setLeft(left);
            left.setCodenumber("0");
            parent.setRight(right);
            right.setCodenumber("1");
            //删除权值最小的两个节点
            nodes.remove(left);
            nodes.remove(right);
            //将新节点加入到集合中
            nodes.add(parent);
        }

当只剩下根节点时,

List<Node> list = new ArrayList<Node>();
        Queue<Node> queue = new ArrayDeque<Node>();

        //将根元素加入“队列
        if (root != null) {
            queue.offer(root);
            root.getLeft().setCodenumber(root.getCodenumber() + "0");
            root.getRight().setCodenumber(root.getCodenumber() + "1");
        }

        while (!queue.isEmpty()) {
            //将该队列的“队尾”元素加入到list中
            list.add(queue.peek());
            Node node = queue.poll();

            //如果左子节点不为null,将它加入到队列
            if (node.getLeft() != null) {
                queue.offer(node.getLeft());
                node.getLeft().setCodenumber(node.getCodenumber() + "0");
            }
            //如果右子节点不为null,将它加入到队列
            if (node.getRight() != null) {
                queue.offer(node.getRight());
                node.getRight().setCodenumber(node.getCodenumber() + "1");
            }
        }

以上就是我的大概的步骤

4.最后结果:


5.码云链接:

哈夫曼树

posted @ 2019-11-20 19:53  tursws  阅读(158)  评论(0编辑  收藏  举报