• # 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();
    

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

    其他(感悟、思考等)

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

    参考资料

  • 相关阅读:
    Git 将当前修改提交到指定分支
    Linux 安装中文字体
    枚举的处理,MybaitsPlus+JackSon
    SpringBoot JackSon全局配置
    SQL查询数据库中所有表名
    Feign url配置/注解
    如何让py生成pyd
    第二十九篇 -- PY程序返回值问题
    解决VS2017调试卡住的问题
    第二十八篇 -- 自定义窗口切换
  • 原文地址:https://www.cnblogs.com/20182326lyj/p/11914809.html
Copyright © 2020-2023  润新知