• 20182304 2018-2019-1《程序设计与数据结构》哈夫曼树编码与解码


    哈夫曼树简介

    • 哈夫曼编码(Huffman Coding),是Huffman于1952年提出一种编码方法,该方法完全依据字符出现概率来构造异字头的平均长度最短的码字,有时称之为最佳编码,主要目的是根据使用频率来最大化节省字符(编码)的存储空间
    • 定义:给定n个权值作为n个叶子结点,构造一棵二叉树,若带权路径长度达到最小,称这样的二叉树为最优二叉树,也称为哈夫曼树(Huffman Tree)。哈夫曼树是带权路径长度最短的树,权值较大的结点离根较近
    • 带权路径长度
      • 结点的权:在一些应用中,赋予树中结点的一个有某种意义的实数
      • 结点的带权路径长度:结点到树根之间的路径长度与该结点上权的乘积
      • 树的带权路径长度:定义为树中所有叶结点的带权路径长度之和
    • 哈夫曼树并不唯一,但带权路径长度一定是相同的

    哈夫曼编码过程记录

    要实现的功能:

    设有字符集: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)对编码文件进行解码,输出一个解码后的文件

    实现过程:

    • 编写节点类,首先设置一个HaffNode类作为实现的基础,为了方便比较,要重写一下compareTo方法
    public class HaffNode implements Comparable {
        private HaffNode left,right,father;
        private char ch;
        private int n;
        private String code;
    
        public void setLeft(HaffNode left) {
            this.left = left;
        }
    
        public void setRight(HaffNode right) {
            this.right = right;
        }
    
        public void setFather(HaffNode father) {
            this.father = father;
        }
    
        public void setCh(char ch) {
            this.ch = ch;
        }
    
    
        public void setN(int sum) {
            this.n = sum;
        }
    
        public void setCode(String code) {
            this.code = code;
        }
    
        public HaffNode getLeft() {
            return left;
        }
    
        public HaffNode getRight() {
            return right;
        }
    
    
    
        public char getCh() {
            return ch;
        }
    
        public int getN() {
            return n;
        }
    
        public String getCode() {
            return code;
        }
    
        public int compareTo(Object o) {
            HaffNode a=(HaffNode)o;
            if(this.n>a.n)
                return 1;
            else if(this.n==a.n)
                return 0;
            else
                return -1;
        }
    
    
    • 读取文件
    public void read(String address,String name) throws IOException {
    
            int i=0;
            File f=new File(address,name);
            Reader reader=new FileReader(f);
            while(reader.ready()){
                a[i++]=(char)reader.read();
            }
            reader.close();
    
            System.out.print("压缩前:");
            for(int k=0;k<a.length;k++){
                System.out.print(a[k]);
            }
            System.out.println();
        }
    
    • 构造哈夫曼树类,实现编码读取、储存,哈夫曼树的根节点,保持编码值的数组等
     private static char[] ch={'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',' '};
    //包含空格
        private char[] a=new char[300];
    
        private int[] sum=new int[27];
    
        private HaffNode root;
    
        private String[] strings=new String[28];//保存编码
    
        private LinkedList treelist=new LinkedList<HaffNode>();//列表
    
    • 用createTree构造一棵树
    public LinkedList createTree() {
            count();//匹配
            
            for (int i = 0; i < 27; i++) {
                HaffNode node = new HaffNode();
                node.setCh(ch[i]);
                node.setN(sum[i]);
                treelist.add(i, node);
            }
            Collections.sort(treelist);
            while (treelist.size() > 1) {//获得权值最小节点
                HaffNode first = (HaffNode) treelist.removeFirst();
                HaffNode second = (HaffNode) treelist.removeFirst();
                //构造成父节点
                HaffNode parent = new HaffNode();
                parent.setN(first.getN() + second.getN());
                parent.setLeft(first);
                parent.setRight(second);
                //把父节点添加进列表,并重新排序
                treelist.add(parent);
                Collections.sort(treelist);
            }
            root= (HaffNode) treelist.getFirst();
            return  treelist;
        }
    //频数统计,计算
     public void count(){
    
            for(int k=0;k<a.length;k++){
                for (int j=0;j<ch.length;j++){
                    if(a[k]==ch[j])
                        sum[j]++;
                }
            }
        }
    
    
    • 根据字符找到节点,从而找到该字符编码
    
     public HaffNode search(HaffNode root,char c) {
            if(root.getCh()==c){
                return root;
            }
            if(root.getLeft()!=null||root.getRight()!=null) {
                HaffNode a=search(root.getLeft(),c);
                HaffNode b=search(root.getRight(),c);
                if(a!=null)
                    return a;
                if(b!=null)
                    return b;
            }
            return null;
        }
    
        public HaffNode getRoot() {
            return root;
        }
    
    • 文件压缩与写入编码
     public void Compress(String path) throws IOException {
            String result="";
            for(int i=0;i<27;i++){
                result+=ch[i]+""+sum[i]+",";
            }
            String content="";
            for(int i=0;i<a.length;i++){
                for(int k=0;k<ch.length;k++){
                    if(a[i]==ch[k])
                        content+=search(root,ch[k]).getCode()+" ";
                }
            }
            result+=content;
            File f=new File(path);
            if(!f.exists()){
                f.createNewFile();
            }
            System.out.println("编码结果:"+content);
            Writer writer=new FileWriter(f);
            BufferedWriter bufferedWriter=new BufferedWriter(writer);
            bufferedWriter.write(result);
            bufferedWriter.flush();
            bufferedWriter.close();
        }
    
    • 读取文件、解压缩、再次写入文件
     public void read2(String address,String name) throws IOException {
            //读取文件
            File file=new File(address,name);
            Reader reader=new FileReader(file);
            BufferedReader bufferedReader=new BufferedReader((new InputStreamReader(new FileInputStream(file),"GBK")));
            String str="";
            String temp="";
            while((temp=bufferedReader.readLine())!=null){
                System.out.println("压缩后文件内容:"+temp);
                str=temp;
            }
            //获取每个字符的频数,逗号分割
            StringTokenizer s =new StringTokenizer(str,",");
            int i=0;
            while (s.hasMoreTokens()){
                strings[i++]=s.nextToken();
            }
        }
    
        public LinkedList createTree2(){
            for(int i=0;i<27;i++){
                HaffNode temp=new HaffNode();
                temp.setCh(strings[i].charAt(0));
                temp.setN(strings[i].charAt(1)-'0');
                treelist.add(temp);
            }
            Collections.sort(treelist);
            while (treelist.size() > 1) {
                //获得两个权值最小的节点
                HaffNode first = (HaffNode) treelist.removeFirst();
                HaffNode second = (HaffNode) treelist.removeFirst();
                //构造成父节点
                HaffNode parent = new HaffNode();
                parent.setN(first.getN() + second.getN());
                parent.setLeft(first);
                parent.setRight(second);
                //添加进列表,并重新排序
                treelist.add(parent);
                Collections.sort(treelist);
            }
            root= (HaffNode) treelist.getFirst();
            return treelist;
        }
    
        public void reCompress(String address) throws IOException {
            String t=strings[27];
            String result="";
            StringTokenizer stringTokenizer=new StringTokenizer(t);
            while(stringTokenizer.hasMoreTokens()){
                String temp=stringTokenizer.nextToken();
                result+=search2(root,temp).getCh();
            }
            System.out.println("解码后:"+result);
    
            File f=new File(address);
            Writer writer=new FileWriter(f);
            BufferedWriter bufferedWriter=new BufferedWriter(writer);
            bufferedWriter.write(result);
            bufferedWriter.flush();
            bufferedWriter.close();
        }
    //根据字符找到节点,从而找到该字符编码
        public HaffNode search2(HaffNode root,String code) {
            if (root.getCode() == null) {
                if (root.getLeft() != null || root.getRight() != null) {
                    HaffNode a = search2(root.getLeft(), code);
                    HaffNode b = search2(root.getRight(), code);
                    if (a != null)
                        return a;
                    if (b != null)
                        return b;
                }
                return null;
            }
            else if(root.getCode().equals(code)){
                return root;
            }
            return null;
        }
    
    • 测试类
      public static void main(String[] args) throws IOException {
            HaffmanTree tree1=new HaffmanTree();
            //读取文件
            tree1.read("C:\Users\666\abcd","zzzz.txt");
            //构建哈夫曼树
            LinkedList temp=tree1.createTree();
            //获得节点编码
            for(int i=0;i<temp.size();i++){
                tree1.getCode((HaffNode) temp.get(i),"");
            }
            //压缩
            tree1.Compress("D:\ppp");
    
    
            HaffmanTree tree2=new HaffmanTree();
          //读写压缩编码
            tree2.read2("D:","ppp");
            
            LinkedList temp2=tree2.createTree2();
            
            for(int i=0;i<temp2.size();i++)
                tree2.getCode((HaffNode) temp2.get(i),"");
            //解压缩
            tree2.reCompress("C:\Users\666\abcd\zzzz.txt");
    

    结果:


  • 相关阅读:
    ab Apache HTTP server benchmarking tool
    压缩JS的类
    看电影学人生:《白银帝国》有感
    JavaScript 闭包
    微软为asp.net ajax和jquery创建了CDN
    JavaScript工具
    Mysql初始化root密码和允许远程访问
    一步一步学Ruby(二十一):文件操作2
    测试包含HttpContext.Current的代码
    What’s the difference between <system.web> and <system.webServer>?
  • 原文地址:https://www.cnblogs.com/acgacg/p/11909686.html
Copyright © 2020-2023  润新知