哈夫曼编码
20182302 2019-2020-1 《数据结构与面向对象程序设计》哈夫曼实践报告
课程:《程序设计与数据结构》
班级: 1823
姓名: 孙嘉伟
学号:20182302
实验教师:王志强
实验日期:2019年11月22日
必修/选修: 必修
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. 实验过程及结果
- 第一步先构造一个结点类啦,定义字符、权值、左结点、右结点,进行哈夫曼编码。
-
public class HuffmanNode<T> implements Comparable<HuffmanNode<T>> { private char data;字符 private double weight;权值 private HuffmanNode left;左结点 private HuffmanNode right;右结点 String code;哈夫曼编码 public HuffmanNode(char data, double weight){ this.data = data; this.weight = weight; this.code =""; }
- 第二步就是开始构造哈夫曼树了
-
}public static HuffmanNode createTree(List<HuffmanNode> nodes) { 只要nodes数组中有2个以上的节点 while (nodes.size() > 1) { 这个是将结点进行排序,这个实现,是在结点类中自己写了一个比较的方法 Collections.sort(nodes); 左侧编码为0,将左孩子定义为列表中的倒数第二个 HuffmanNode left = nodes.get(nodes.size() - 1); 右侧编码为1,右孩子为列表中的倒数第一个 HuffmanNode right = nodes.get(nodes.size() - 2); 他们的双亲结点就是他们的权值之和 HuffmanNode parent = new HuffmanNode('无', left.getWeight() + right.getWeight()); 这里把双亲结点就放进去 parent.setLeft(left); left.setCode("0"); parent.setRight(right); right.setCode("1"); 删除权值最小的两个节点 nodes.remove(left); nodes.remove(right); 将新节点加入到集合中 nodes.add(parent); } return nodes.get(0);
- 接下来我们就要考虑输出编码啦
-
public static List<HuffmanNode> breadth(HuffmanNode root) {这里利用了广度优先遍历的内容 List<HuffmanNode> list = new ArrayList<HuffmanNode>(); Queue<HuffmanNode> queue = new ArrayDeque<HuffmanNode>(); 如果根结点存在,那么我们就将根放入列表中 if (root != null) { queue.offer(root); root.getLeft().setCode(root.getCode() + "0"); root.getRight().setCode(root.getCode() + "1"); } 如果队列不是空的,那么我们就队尾放进列表中 while (!queue.isEmpty()) { list.add(queue.peek()); HuffmanNode node = queue.poll(); 如果左子节点不为null,将它加入到队列 if (node.getLeft() != null) { queue.offer(node.getLeft()); node.getLeft().setCode(node.getCode() + "0"); } 如果右子节点不为null,将它加入到队列 if (node.getRight() != null) { queue.offer(node.getRight()); node.getRight().setCode(node.getCode() + "1"); } } return list; }
最后进行测试
在文件中分别写出字符和哈夫曼的编码
通过循环的方式,对编码进行输出和解密
-
}public static void main(String[] args) throws IOException { 把字符集从文件中读出来,并保存在一个数组里 File file = new File("C:\\Users\\hs\\Desktop\\javatest.txt"); Reader reader = new FileReader(file); BufferedReader bufferedReader = new BufferedReader(reader); String temp = bufferedReader.readLine(); char characters[] = new char[temp.length()]; for (int i = 0; i < temp.length(); i++) { characters[i] = temp.charAt(i); } System.out.println("原字符集为:" + Arrays.toString(characters)); double frequency[] = new double[27];//26个字母加空格 int numbers = 0;//空格的个数 for (int i = 0; i < characters.length; i++) { if (characters[i] == ' ') { numbers++; } frequency[26] = (float) numbers / characters.length; } System.out.println("字符集为"); for (int j = 97; j <= 122; j++) { int number = 0;//给字母计数 for (int m = 0; m < characters.length; m++) { if (characters[m] == (char) j) { number++; } frequency[j - 97] = (float) number / characters.length; } System.out.print((char) j + ","); } System.out.println("空格"); System.out.println("每个字符的概率是" + "\n" + Arrays.toString(frequency)); double result = 0.0; for (int z = 0; z < 27; z++) { result += frequency[z]; } System.out.println("总概率之和为" + result); List<HuffmanNode> nodes = new ArrayList<HuffmanNode>(); for (int o = 97; o <= 122; o++) { nodes.add(new HuffmanNode((char) o, frequency[o - 97])); } nodes.add(new HuffmanNode(' ', frequency[26])); HuffmanNode root = HuffmanTree.createTree(nodes); System.out.println("哈夫曼树为:"); System.out.println(breadth(root)); 对英文文件进行编码,输出一个编码后的文件 String result1 = ""; List<HuffmanNode> temp1 = breadth(root); for (int i = 0; i < characters.length; i++) { for (int j = 0; j < temp1.size(); j++) { if (characters[i] == temp1.get(j).getData()) { result1 += temp1.get(j).getCode(); } } } System.out.println("对文件进行编码后的结果为:"); System.out.println(result1); File file2 = new File("C:\\Users\\hs\\Desktop\\result1.txt); Writer writer = new FileWriter(file2); writer.write(result1); writer.close();e 对英文文件进行解码,输出一个解码后的文件 List<String> newlist = new ArrayList<>(); for(int m=0;m < temp1.size();m++) { if(temp1.get(m).getData()!='无') newlist.add(String.valueOf(temp1.get(m).getData())); } System.out.println("字符:"+newlist); List<String> newlist1 = new ArrayList<>(); for(int m=0;m < temp1.size();m++) { if(temp1.get(m).getData()!='无') newlist1.add(String.valueOf(temp1.get(m).getCode())); } System.out.println("对应编码:"+newlist1); //先从编完码的文件中读出密文 FileReader fileReader = new FileReader("C:\\Users\\hs\\Desktop\\result1.txt"); BufferedReader bufferedReader1 = new BufferedReader(fileReader); String secretline = bufferedReader1.readLine(); 将读出的密文存在secretText列表中 List<String> secretText = new ArrayList<String>(); for (int i = 0; i < secretline.length(); i++) { secretText.add(secretline.charAt(i) + ""); } 解密 String result2 = ""; String current=""; while(secretText.size()>0) { current = current + "" + secretText.get(0); secretText.remove(0); for (int p = 0; p < newlist1.size(); p++) { if (current.equals(newlist1.get(p))) { result2 = result2 + "" + newlist.get(p); current=""; } } } System.out.println("解码后的结果:"+result2); File file3 = new File("C:\\Users\\hs\\Desktop\\result2.txt); Writer writer1 = new FileWriter(file3); writer1.write(result2); writer.close();
实验结果