哈夫曼编码实践纪录

课程:《程序设计与数据结构》
班级:1823
姓名:华罗晗
学号:20182308
实验教师:王志强
实验日期:(课后实践)
必修/选修:必修

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个英文字母的文件,统计每个字符出现的概率,根据计算的概率构造一颗哈夫曼树。
并完成对英文文件的编码和解码。
要求:

  • 准备一个包含26个英文字母的英文文件(可以不包含标点符号等),统计各个字符的概率
  • 构造哈夫曼树
  • 对英文文件进行编码,输出一个编码后的文件
  • 对编码文件进行解码,输出一个解码后的文件
  • 撰写博客记录实验的设计和实现过程,并将源代码传到码云
  • 把实验结果截图上传到云班课

2. 实验思路、过程及结果

1、百度上找到一个符合要求的文段。
2、构造哈夫曼树,首先哈弗曼树是一种树,所以必须要制造结点,这是Node代码里面的一部分方法。

public HuffmanNode(T name, double length)
    {
        this.name = name;
        this.length = length;
        code = "";
    }

    public T getName()
    {
        return name;
    }

    public void setName(T name)
    {
        this.name = name;
    }

    public double getLength()
    {
        return length;
    }

    public void setLength(double length)
    {
        this.length = length;
    }

    public HuffmanNode<T> getLeft()
    {
        return left;
    }

    public void setLeft(HuffmanNode<T> left)
    {
        this.left = left;
    }

    public HuffmanNode<T> getRight()
    {
        return right;
    }

    public void setRight(HuffmanNode<T> right)
    {
        this.right = right;
    }

    public String getCode()
    {
        return code;
    }

    public void setCode(String str)
    {
        code = str;
    }

    @Override
    public String toString()
    {
        return "name:"+this.name+";length:"+this.length+";编码为: "+this.code;
    }

    @Override
    public int compareTo(HuffmanNode<T> other)
    {
        if(other.getLength() > this.getLength())
            return 1;
        if(other.getLength() < this.getLength())
            return -1;
        return 0;
    }

这是哈弗曼树的结点类中的一些方法,哈弗曼树构造的本体中还有一些别的构造树的方法,大体与之前打的树差不多。
这里列举出建树部分的代码:

public  HuffmanNode<T> createTree(List<HuffmanNode<T>> nodes)
    {
        while (nodes.size() > 1)
        {
            Collections.sort(nodes);
            HuffmanNode<T> left = nodes.get(nodes.size() - 2);
            left.setCode(0 + "");
            HuffmanNode<T> right = nodes.get(nodes.size() - 1);
            right.setCode(1 + "");
            HuffmanNode<T> parent = new HuffmanNode<T>(null, left.getLength() + right.getLength());
            parent.setLeft(left);
            parent.setRight(right);
            nodes.remove(left);
            nodes.remove(right);
            nodes.add(parent);
        }
        return nodes.get(0);
    }

    public 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();
            if (node.getLeft() != null)
                node.getLeft().setCode(node.getCode() + "0");
            if (node.getRight() != null)
                node.getRight().setCode(node.getCode() + "1");

            if (node.getLeft() != null)
            {
                queue.offer(node.getLeft());
            }

            if (node.getRight() != null)
            {
                queue.offer(node.getRight());
            }
        }
        return list;
    }

3、对英文文件进行编码,首先需要创建一个文件、并且进行读入,这里沿用I/O相关的内容。这一部分的代码就跳过啦,把这个文件中的英文转换成String格式的变量、进行01加密。再新建两个文件将目标变量res(编码后)和String2(解码后)写入。同时再对String进行操作,计算一下出现概率之类的变量。

        int temp = 0;
        for(int e = 0;e<inlist.size();e++)
        {
            if(inlist.get(e).getName() != null){
                System.out.println(inlist.get(e).getName()+"的编码为"+ inlist.get(e).getCode()+" ");
                name[temp] = (String) inlist.get(e).getName();
                code[temp] = inlist.get(e).getCode();
                temp++;
            }
        }

        String res = "";
        for(int f = 0; f < sum; f++)
        {
            for(int j = 0;j<name.length;j++)
            {
                if(message.charAt(f) == name[j].charAt(0))
                    res += code[j];
            }
        }

        System.out.println("编码后:"+ res);

        List<String> putlist = new ArrayList<String>();
        for(int i = 0;i < res.length();i++)
            putlist.add(res.charAt(i)+"");
        String string1 = "";
        String string2 = "";
        for(int h = putlist.size(); h > 0; h--){
            string1 = string1 + putlist.get(0);
            putlist.remove(0);
            for(int i=0;i<code.length;i++){
                if (string1.equals(code[i])) {
                    string2 = string2+""+ name[i];
                    string1 = "";
                }
            }
        }
        System.out.println("解码后:" + string2);

4、读取部分放在了之后的错误抛出部分,在此就不把代码放出来了。

运行结果如下图所示:

3. 实验过程中遇到的问题和解决过程

  • 问题1:错误抛出问题。
  • 问题1解决方案:参看同学代码的时候,想到了抛出错误的问题。因此加入了以下的方法代码(同时对于两个新建文件的读入操作也在这里):
private static int getFileLineCount(File file) {
        int cnt = 0;
        InputStream is = null;
        try
        {
            is = new BufferedInputStream(new FileInputStream(file));
            byte[] c = new byte[1024];
            int readChars = 0;
            while ((readChars = is.read(c)) != -1)
            {
                for (int i = 0; i < readChars; ++i)
                {
                    if (c[i] == '\n')
                        ++cnt;
                }
            }
        } catch (Exception ex)
        {
            cnt = -1;
            ex.printStackTrace();
        }
        finally
        {
            try
            {
                is.close();
            }
            catch (Exception ex)
                {ex.printStackTrace();}
        }
        return cnt;
    }

参考资料

posted on 2019-11-22 23:18  20182308hlh  阅读(128)  评论(0编辑  收藏  举报

导航