# 20182326 2019-2020-1 《数据结构与面向对象程序设计》哈夫曼实验报告

20182326 2019-2020-1 《数据结构与面向对象程序设计》哈夫曼实验报告

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. 哈夫曼树
    哈夫曼树又称为最优树.
    • 路径和路径长度:
      在一棵树中,从一个结点往下可以达到的孩子或孙子结点之间的通路,称为路径。通路中分支的数目称为路径长度。若规定根结点的层数为1,则从根结点到第L层结点的路径长度为L-1。
    • 结点的权及带权路径长度:
      若将树中结点赋给一个有着某种含义的数值,则这个数值称为该结点的权。结点的带权路径长度为:从根结点到该结点之间的路径长度与该结点的权的乘积。
    • 树的带权路径长度:
      树的带权路径长度规定为所有叶子结点的带权路径长度之和,记为WPL。
  2. 调入文件

  3. 构建哈夫曼树
package com.liuhao.DataStructures;
 
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.List;
import java.util.Queue;
 
public class HuffmanTree {
 
    public static class Node<E> {
        E data;
        double weight;
        Node leftChild;
        Node rightChild;
 
        public Node(E data, double weight) {
            super();
            this.data = data;
            this.weight = weight;
        }
 
        public String toString() {
            return "Node[data=" + data + ", weight=" + weight + "]";
        }
    }
 
    public static void main(String[] args) {
        List<Node> nodes = new ArrayList<Node>();
 
        nodes.add(new Node("A", 40.0));
        nodes.add(new Node("B", 8.0));
        nodes.add(new Node("C", 10.0));
        nodes.add(new Node("D", 30.0));
        nodes.add(new Node("E", 10.0));
        nodes.add(new Node("F", 2.0));
        
        Node root = HuffmanTree.createTree(nodes);
        
        System.out.println(breadthFirst(root));
 
    }
 
    /**
     * 构造哈夫曼树
     * 
     * @param nodes
     *            节点集合
     * @return 构造出来的哈夫曼树的根节点
     */
    private static Node createTree(List<Node> nodes) {
        // 只要nodes数组中还有2个以上的节点
        while (nodes.size() > 1) {
            quickSort(nodes);
            //获取权值最小的两个节点
            Node left = nodes.get(nodes.size()-1);
            Node right = nodes.get(nodes.size()-2);
            
            //生成新节点,新节点的权值为两个子节点的权值之和
            Node parent = new Node(null, left.weight + right.weight);
            
            //让新节点作为两个权值最小节点的父节点
            parent.leftChild = left;
            parent.rightChild = right;
            
            //删除权值最小的两个节点
            nodes.remove(nodes.size()-1);
            nodes.remove(nodes.size()-1);
            
            //将新节点加入到集合中
            nodes.add(parent);
        }
        
        return nodes.get(0);
    }
 
    /**
     * 将指定集合中的i和j索引处的元素交换
     * 
     * @param nodes
     * @param i
     * @param j
     */
    private static void swap(List<Node> nodes, int i, int j) {
        Node tmp;
        tmp = nodes.get(i);
        nodes.set(i, nodes.get(j));
        nodes.set(j, tmp);
    }
 
    /**
     * 实现快速排序算法,用于对节点进行排序
     * 
     * @param nodes
     * @param start
     * @param end
     */
    private static void subSort(List<Node> nodes, int start, int end) {
        if (start < end) {
            // 以第一个元素作为分界值
            Node base = nodes.get(start);
            // i从左边搜索,搜索大于分界值的元素的索引
            int i = start;
            // j从右边开始搜索,搜索小于分界值的元素的索引
            int j = end + 1;
            while (true) {
                // 找到大于分界值的元素的索引,或者i已经到了end处
                while (i < end && nodes.get(++i).weight >= base.weight)
                    ;
                // 找到小于分界值的元素的索引,或者j已经到了start处
                while (j > start && nodes.get(--j).weight <= base.weight)
                    ;
 
                if (i < j) {
                    swap(nodes, i, j);
                } else {
                    break;
                }
            }
 
            swap(nodes, start, j);
 
            //递归左边子序列
            subSort(nodes, start, j - 1);
            //递归右边子序列
            subSort(nodes, j + 1, end);
        }
    }
    
    public static void quickSort(List<Node> nodes){
        subSort(nodes, 0, nodes.size()-1);
    }
    
    //广度优先遍历
    public static List<Node> breadthFirst(Node root){
        Queue<Node> queue = new ArrayDeque<Node>();
        List<Node> list = new ArrayList<Node>();
        
        if(root!=null){
            //将根元素加入“队列”
            queue.offer(root);
        }
        
        while(!queue.isEmpty()){
            //将该队列的“队尾”元素加入到list中
            list.add(queue.peek());
            Node p = queue.poll();
            
            //如果左子节点不为null,将它加入到队列
            if(p.leftChild != null){
                queue.offer(p.leftChild);
            }
            
            //如果右子节点不为null,将它加入到队列
            if(p.rightChild != null){
                queue.offer(p.rightChild);
            }
        }
        
        return list;
    }
}
  1. 编码,输出文件

  1. 解码,输出文件

  2. 代码
    结果:

3. 实验过程中遇到的问题和解决过程

  • 问题1:系统找不到指定文件

  • 问题1解决方案:当你创建文件时,首先应该创建文件的父目录(除非你手动创建过了父目录)。
    也就是这样写创建多级目录的文件:

File fileDir = new File(“C:/test/”); 
fileDir.mkdirs(); 
File file = new File(“test.txt”); 
file.createNewFile();

改动之后仍有错误,经检查发现是文件名写错了。。。

其他(感悟、思考等)

  • 太粗心了,没检查出错误(问题一)

参考资料

猜你喜欢

转载自www.cnblogs.com/20182326lyj/p/11914809.html