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

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

其他(感悟、思考等)

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

参考资料

posted on 2019-11-22 23:17  20182326  阅读(202)  评论(0编辑  收藏  举报