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"); } }
以上就是我的大概的步骤