JAVA实现Huffman编码(赫夫曼编码)

思路:

  1. 把字符串转化为字节数组,对字符串中不同字符出现的次数进行统计,统计结果存储到一个Map中
  2. 键为字符字节,值为此字符字节在字符串中出现的次数
  3. 创建赫夫曼树
  4. 读取每一个字符字节的路径方向值,把字符串中的每个字符用路径方向值来表示
  5. 把得到的二进制字符串切分为有符号字节存储到字节数组中
  6. 打印原字符串,二进制字符串,字节数组

 

代码如下:

//Node类用于创建树结构
public class Node implements Comparable<Node>{
    Byte data;
	int weight;
	Node left;
	Node right;
	public Node(Byte data, int weight){
	    this.data = data;	
	    this.weight = weight;
	}
	@Override
	public String toString() {
		return "Node [data=" + data + ", weight=" + weight + "]";
	}
	@Override
	public int compareTo(Node nodes) {
		// TODO Auto-generated method stub
		return nodes.weight - this.weight;
	}   		
}

public class TestHuffmanCode {
	 public static void main(String[] args) {
	    //定义一个字符串
        String msg ="can you can a can as a can cannr can a can";
        //定义一个字节数组
        byte[] bytes = msg.getBytes();
        //进行赫夫曼编码
        byte[] byt = huffmanZip(bytes);
        //循环遍历字节数组中的值,也即赫夫曼编码值
        for(byte b:byt) {
        	System.out.print(b+" ");
        }
	 }
 
	/**
	  *进行赫夫曼编码的方法
	 *
	 *
	**/
	 
	 private static byte[] huffmanZip(byte[] bytes){
	    //判断字节是否为空
	    if(bytes ==null) {
	    	return null;
	    }
        //先统计每一个byte出现的次数,并放入一个集合中
        List<Node> nodes = getNodes(bytes);
        //创建一个赫夫曼树
        Node tree = createHuffmanTree(nodes);
        //创建一个赫夫曼编码表
        Map<Byte, String>huffCodes = getCodes(tree);       
        //压缩编码
        byte[] byt=zip(bytes,huffCodes);
        
        return byt;
    }
      
    //用于标识最后一个子字符串的长度
    static int bitFlag =0;
    //把二进制字符串转换为有符号十进制数存储到字节数组中
    private static byte[] zip(byte[] bytes, Map<Byte,String>huffCodes){
    	//用于存储二进制字符串
        StringBuilder sb = new StringBuilder();
        //把需要压缩的byte数组处理成一个二进制的字符串
        for(byte b:bytes){
            sb.append(huffCodes.get(b));
        }
        //输出二进制字符串
        System.out.println(sb.toString());
        //定义长度用来存储压缩后的字符串
        int len;
        //判断二进制字符串能划分为几个字节
        if(sb.length()%8==0){
           len = sb.length()/8;            
        }else{
            len = sb.length()/8 + 1;
        }
        
        //用于记录byte数组中的下标值
        int index = 0;
        //用于存储压缩后的byte
        byte[] by = new byte[len];
        //循环遍历二进制字符串
        for(int i =0;i<sb.length();i+=8){
        	//用于存储子字符串
            String strByte;
            if(i+8>sb.length()){
            	//赋值最后一个字符串
                strByte = sb.substring(i);
                //用于记录最后一个二进制字符串的长度
                bitFlag = strByte.length();
                //把二进制字符串转换为有符号十进制数
                by[index] =  (byte) Integer.parseInt(strByte,2);    
            }else{
            	//按照每八位截取二进制字符串
                strByte = sb.substring(i,i+8);
                //把二进制字符串转换为有符号十进制数
                by[index] =  (byte) Integer.parseInt(strByte,2);
                index++;
            }
        }      
        return by;
    }

	 //用来存储符号的路径方向
	 static StringBuilder sb = new StringBuilder();
	 //用于存储符号字节值与对应的符号路径方向值
	 static Map<Byte,String>huffCodes = new HashMap<>();
	 //用于得到符号字节值与对应的符号路径方向值
	 private static Map<Byte, String>getCodes(Node tree){
		//判断赫夫曼树是否为空 
        if(tree==null){
            return null;
        }
        //调用得到路径方向值方法
        getCodes(tree.left,"0",sb);
        getCodes(tree.right,"1",sb);
        return huffCodes;       
        
	 }
	 //得到路径值
	 private static void getCodes(Node node, String code,StringBuilder sb){
		//定义一个StringBuilder,把参数传进去 
        StringBuilder sb2 = new StringBuilder(sb);
        //追加路径值
        sb2.append(code);
        //递归getCodes方法
        if(node.data == null){
            getCodes(node.left,"0",sb2);
            getCodes(node.right,"1",sb2);
        }else{
        	
            huffCodes.put(node.data,sb2.toString());
        }
             
	 }	    
	 
	 private static Node createHuffmanTree(List<Node>nodes){	
        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(null, left.weight+right.weight);
            //把左子树赋值给双亲节点的左子树
            parent.left = left;
            //把右子树赋值给双亲节点的右子树
            parent.right = right;
            //去除左右子树
            nodes.remove(left);
            nodes.remove(right);
            //把双亲节点加入Node中
            nodes.add(parent);
        }  
        return nodes.get(0);
	 }
	 
	 private static List<Node> getNodes(byte[] bytes){
	    //声明一个Node类型的动态数组
        List<Node> nodes = new ArrayList<>();
        //定义一个Map,用于存储一个字符值和此字符出现的次数  
        Map<Byte,Integer> counts = new HashMap<>();
        //循环遍历字节数组
        for(byte b:bytes){
        	//得到Map中的value值,做一个标记或者记录
            Integer count = counts.get(b);
            if(count==null){
            	//把子节数组中的数据存到Map中
                counts.put(b,1);
            }else{
            	//依次把子节数组中的数据存入Map中
                counts.put(b,count+1);
            }
        }
        //把键值对存入到Node类型的Node中		      
        for(Map.Entry<Byte,Integer>entry:counts.entrySet()){
            nodes.add(new Node(entry.getKey(),entry.getValue()));
        }
        
        //System.out.println(nodes);
        return nodes;  	
	}		         	          
}

 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值