java数据结构---赫夫曼树,编码,解码,文件压缩,解压

这个Java程序实现了文件的压缩和解压缩,利用赫夫曼编码进行数据编码。代码中包含了构建赫夫曼树、获取字符频率、生成赫夫曼编码表、压缩和解压缩文件等核心功能。程序还处理了从字节数组到字符串的转换,并提供了文件操作的相关方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

package com.jiahui.huffmanTree;

import java.io.*;
import java.util.*;

/**
 * @author shkstart
 * @create 2022-11-17 18:52
 */
public class HuffmanTree {
    public static void main(String args[]) throws IOException {
//        String str="i like like like java do you like a java";
//        Map<Byte, Integer> number = getNumber(str);
//
//        Node node = huffmanTree(number);
//        Map<Byte,String> huffmanCode=new HashMap<>();
//        getCodes(node.getLeft(),"0",new StringBuilder(),huffmanCode);
//        getCodes(node.getRight(),"1",new StringBuilder(),huffmanCode);
//
//        for (Map.Entry<Byte, String> i:huffmanCode.entrySet()){
//            System.out.println(i.getKey()+"--"+i.getValue());
//        }
//        byte[] zipHuffmanCode = zipHuffman(str, huffmanCode);
//        System.out.println(Arrays.toString(zipHuffmanCode));
//        byte b=1;
//        byte[] bytes = deCode(zipHuffmanCode, huffmanCode);
//        System.out.println(Arrays.toString(bytes));

//        byte[] bytes={65,66};
//        String s = new String(bytes);
//        System.out.println(s);



        File srcFile=new File("D:\\尚硅谷Java基础\\传入");
        try {
            srcFile.createNewFile();
        } catch (IOException e) {
            e.printStackTrace();
        }
        File dstFile=new File("D:\\尚硅谷Java基础\\输出");
        dstFile.createNewFile();
        FileOutputStream fos=new FileOutputStream(srcFile);
        fos.write("i like like like java do you like a java".getBytes());
        File file=new File("D:\\尚硅谷Java基础\\传入1");
        file.createNewFile();
        zipFile("D:\\尚硅谷Java基础\\传入","D:\\尚硅谷Java基础\\输出");
        unzipFile("D:\\尚硅谷Java基础\\输出","D:\\尚硅谷Java基础\\传入1");

//失败案例,试图压缩图片,然后下标越界,日后再看吧
//        File srcFile=new File("C:\\Users\\x'j'h'y'y'd's\\Desktop\\csdn\\ali.jpg");
//        try {
//            srcFile.createNewFile();
//        } catch (IOException e) {
//            e.printStackTrace();
//        }
//        File dstFile=new File("D:\\尚硅谷Java基础\\图片.jpg");
//        dstFile.createNewFile();
//        FileOutputStream fos=new FileOutputStream(srcFile);
//        fos.write("i like like like java do you like a java".getBytes());
//        File file=new File("D:\\尚硅谷Java基础\\图片1.jpg");
//        file.createNewFile();
//        zipFile("C:\\Users\\x'j'h'y'y'd's\\Desktop\\csdn\\ali.jpg","D:\\尚硅谷Java基础\\图片.jpg");
//        unzipFile("D:\\尚硅谷Java基础\\图片.jpg","D:\\尚硅谷Java基础\\图片1.jpg");
    }

    public static void preOrder(Node o){
        if (o!=null){
            o.preOrder();
        }else {
            System.out.println("是空树,不能遍历");
        }

    }
//第一版本(赫夫曼树版)
//    public static Node huffmanTree(int[] arry){
//        List<Node> Nodes=new ArrayList<>();
//
//        for (int i :arry){
//            Node temp =new Node(i);
//            Nodes.add(temp);
//        }
//
//        while (Nodes.size()>1){
//            Collections.sort(Nodes);
//            Node one =Nodes.get(0);
//            Node two=Nodes.get(1);
//
//            Node parent =new Node(one.getNumber()+two.getNumber());
//            parent.setLeft(one);
//            parent.setRight(two);
//
//            Nodes.remove(one);
//            Nodes.remove(two);//需要注意ArrayList类remove后,后一位的对象直接向前递进
//            Nodes.add(parent);
//
//        }
//
//        return Nodes.get(0);
//
//    }



    //将文件解压
    public static void unzipFile(String srcFile,String dstFile) {
        ObjectInputStream ois= null;
        FileOutputStream fos= null;
        try {
            ois = new ObjectInputStream(new FileInputStream(new File(srcFile)));
            fos = new FileOutputStream(new File(dstFile));

            byte[] bytes =(byte[]) ois.readObject();
            Map<Byte,String> huffmanCode=(Map<Byte,String> ) ois.readObject();
            byte[] bytes1 = unzipCode(bytes, huffmanCode);

            fos.write(bytes1);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } finally {
            try {
                if (fos!=null){
                    fos.close();
                }
                if (ois!=null){
                    ois.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }



    }

    //将文件压缩
    public static void zipFile(String srcFile,String dstFile)  {
        FileInputStream fis= null;
        ObjectOutputStream oos= null;
        try {
            fis = new FileInputStream(new File(srcFile));
            oos = new ObjectOutputStream(new FileOutputStream(new File(dstFile)));

            byte[] bytes=new byte[fis.available()];
            fis.read(bytes);

            Map<Byte, String> huffmanCode = getHuffmanCode(new String(bytes));
            byte[] bytes1 = zipCode(new String(bytes), huffmanCode);

            oos.writeObject(bytes1);
            oos.flush();

            oos.writeObject(huffmanCode);
            oos.flush();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (oos!=null){
                    oos.close();
                }

                if (fis!=null){
                    fis.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    //解压内容
    public static byte[] unzipCode(byte[] bytes,Map<Byte,String> huffmanCode){
        byte[] bytes1 = deCode(bytes, huffmanCode);
        return bytes1;
    }

    //运用赫夫曼编码表压缩内容
    public static byte[] zipCode(String str,Map<Byte,String> huffmanCode){
        byte[] bytes = zipHuffman(str, huffmanCode);  //压缩后得到的数组,一个byte代表一个字符
        return bytes;
    }

    //得到赫夫曼编码表
    public static Map<Byte,String> getHuffmanCode(String str){
        Map<Byte, Integer> number = getNumber(str);//得到每个字符出现的次数
        Node node = huffmanTree(number);//得到每个字符的huffman树
        Map<Byte,String> huffmanCode=new HashMap<>();//赫夫曼编码表
        getCodes(node.getLeft(),"0",new StringBuilder(),huffmanCode);//往左边走 将字符串转成huffman编码
        getCodes(node.getRight(),"1",new StringBuilder(),huffmanCode);//往右边走
        return huffmanCode;
    }


    //将得到的编码通过赫夫曼编码表转换成最初的string并将他们拼接起来返回,解码完成
    public static byte[] deCode(byte[] zipHuffmanCode ,Map<Byte,String> huffmanCode){
        StringBuilder stringBuilder=new StringBuilder();
        for (int i=0;i<zipHuffmanCode.length;i++){
            boolean isFlag=false;
            if (i==zipHuffmanCode.length-1){
                isFlag=true;
            }
            stringBuilder.append(byteToHuffmanString(zipHuffmanCode[i],isFlag));
        }
        Map<String ,Byte> map=new HashMap<>();
        for (Map.Entry<Byte,String> i: huffmanCode.entrySet()){
            map.put(i.getValue(),i.getKey());
        }
        String str=stringBuilder.toString();
        List<Byte> list=new ArrayList<>();
        for (int i=0;i<str.length();){
            String temp;
            int count=i+1;//长度
            while (true){
                temp=str.substring(i,count);
                if (map.containsKey(temp)){
                    break;
                }
                count++;
            }
            list.add(map.get(temp));
            i=count;
        }
        byte[] bytes=new byte[list.size()];

        for (int i=0;i<bytes.length;i++){
            bytes[i]=list.get(i);
        }
        return bytes;



    }



    //将byte转换为二进制字符串,也就是最开始的霍夫曼编码得到的编码
    //isFlag为true表示最后一位为正
    //需要考虑最后一个byte,如果是正数,不需要补码,直接加,如果是负数,说明当初转换的时候满了8位,没影响
    //但是最后一个byte是正数没有满8位
    public static String byteToHuffmanString(byte b,boolean isFlag){
        int temp=b;
        String s = Integer.toBinaryString(temp);
//            正数补完高位后,第九位为1,后面取后八位,不影响,而且八位全部取到,没有少
        if (isFlag){//最后一位为正,返回的不足8位
            return s;
        }
        temp|=256;
        s=Integer.toBinaryString(temp);
        s=s.substring(s.length()-8);//取后八位,得到的都是补码,但是当初编码的时候(byte)Interger.paeInt(string)
        // 就是把字符串当成二进制的补码,因此不影响,得到的s就是我们需要的编码
        return s;
    }




    //将字符串转成HuffmanCode编码的字节,也就是将正常字符串通过Huffmancode进行压缩
    public static byte[] zipHuffman(String str,Map<Byte,String> huffmanCode){
        byte[] contentByte=str.getBytes();
        StringBuilder stringBuilder=new StringBuilder();
        for (byte i:contentByte){
            stringBuilder.append(huffmanCode.get(i));
        }
        int len =0;
        if (stringBuilder.length()%8==0){
            len=stringBuilder.length()/8;
        }else {
            len=stringBuilder.length()/8+1;
        }
        byte[] zipHuffmanCode=new byte[len];
//        System.out.println(stringBuilder.length());  测试,看看压缩后的字符串长度是否符合
//        System.out.println(stringBuilder);
        int index =0;
        for (int i=0;i<stringBuilder.length();i+=8){
            String temp="";
            if (i+8>stringBuilder.length()){
                 temp=stringBuilder.substring(i);
            }else {
                 temp=stringBuilder.substring(i,i+8);
            }

            byte b=(byte) Integer.parseInt(temp,2);//该方法将传入的temp字符串代表的二进制当成补码使用

            zipHuffmanCode[index]=b;
            index++;
        }

        return zipHuffmanCode;
    }



    //得到每个字符的出现次数
    public static Map<Byte,Integer> getNumber(String str){
        Map<Byte,Integer> numberMap=new HashMap<>() ;
        byte[] array=str.getBytes();
        for (byte i:array){
            if (!numberMap.containsKey(i)){
                numberMap.put( i,1);
            }else {
                numberMap.put(i,numberMap.get(i)+1);
            }
        }
        return numberMap;

    }
//    第二版本赫夫曼树(赫夫曼编码版)
    public static Node huffmanTree(Map<Byte,Integer> numberMap){
        Set<Map.Entry<Byte, Integer>> entries = numberMap.entrySet();
        List<Node> nodes=new ArrayList<>();
        for (Map.Entry<Byte,Integer>  entry:entries){
            Byte ch = entry.getKey();
            int i=entry.getValue();
            Node node=new Node(i,ch);
            nodes.add(node);
        }

        while (nodes.size()>1){
            Collections.sort(nodes);
            Node one =nodes.get(0);
            Node two=nodes.get(1);

            Node parent =new Node(one.getNumber()+two.getNumber());
            parent.setLeft(one);
            parent.setRight(two);

            nodes.remove(one);
            nodes.remove(two);//需要注意ArrayList类remove后,后一位的对象直接向前递进
            nodes.add(parent);

        }
        return nodes.get(0);
    }
//每个字符对应的赫夫曼编码

    /**
     *
     * @param node 字符对应的huffman树
     * @param code  当前走的路径,左还是右
     * @param stringBuilder 走过的路径
     * @param huffmanCode  最后得到的每个字符转换成的赫夫曼编码对应的集合,byte代表字符,string代表赫夫曼编码
     */
    public static void getCodes(Node node,String code,StringBuilder stringBuilder,Map<Byte,String> huffmanCode){
        StringBuilder stringBuilder1=new StringBuilder(stringBuilder);
        stringBuilder1.append(code);
        if (node!=null){
            if (node.getData()!=null){
                huffmanCode.put(node.getData(),stringBuilder1.toString());
            }else {
                getCodes(node.getLeft(),"0",stringBuilder1,huffmanCode);
                getCodes(node.getRight(),"1",stringBuilder1,huffmanCode);
            }
        }


    }

}
class Node implements Comparable<Node>{
    private int number;
    private Byte data;
    private Node left;
    private Node right;

    public Node(int number, byte data){
        this.number=number;
        this.data=data;
    }

    public Byte getData() {
        return data;
    }

    public void setData(Byte data) {
        this.data = data;
    }

    public void preOrder(){
        System.out.println(this.getNumber());
        if (this.left!=null){
            this.left.preOrder();
        }
        if (this.right!=null){
            this.right.preOrder();
        }
    }



    public Node(int number) {
        this.number = number;
    }

    public int getNumber() {
        return number;
    }

    public void setNumber(int number) {
        this.number = number;
    }

    public Node getLeft() {
        return left;
    }

    public void setLeft(Node left) {
        this.left = left;
    }

    public Node getRight() {
        return right;
    }

    public void setRight(Node right) {
        this.right = right;
    }

    @Override
    public String toString() {
        return "Node{" +
                "number=" + number +
                ", data=" + data +
                '}';
    }

    @Override
    public int compareTo(Node o) {
        return this.number-o.number;
    }
}

这是个大工程,严格算时间感觉写了一个多礼拜,每天能抽出来写的时间并不多,还是太菜了。

遇到的一些相关问题都标在注释里面了,希望对大家有些帮助。


1.byte b=(byte) Integer.parseInt(temp,2);
该方法将传入的temp字符串代表的二进制当成补码使用,因为计算机底层都是补码形式。

2.int temp=b;

String s = Integer.toBinaryString(temp);

该方法转变的负数得到的位数远远大于8位,但是后八位的形式是数字temp的二进制的补码形式。

因此与256做一次或运算或者不做也可以,直接subString取后八位即可。

该方法得到的正数,由于正数第8位(即符号位)为0,所以会直接取到最后一个1的位置,剩下的省略,因此得到的不足8位,此时如果想得到8位,与256做一次或运算然后取后8位,舍弃第九位即可。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值