Java数据结构与算法

目录

赫夫曼编码字节数组

代码实现

 完整代码

 运行效果

测试Test 

 运行效果

 赫夫曼字节数组封装

实现代码

字节转二进制字符串

代码实现


赫夫曼编码字节数组

代码实现

	//编写一个方法,将字符串对应的byte[]数组,通过生成的赫夫曼编码表,
//返回一个赫夫曼编码压缩后的byte[]
	/*
	 * bytes 这时原始的字符串对应的byte[]
	 * huffmanCodes 生成的赫夫曼编码map
	 * 返回赫夫曼编码处理后的byte[]
	 * 举例:String content="I like like like java do you like a java";
=>byte[] contentBytes=content.getBytes();
	 * 返回的是字符串
	 * =>对应的byte[] huffmanCodeBytes,即8位对应一个byte,放入到huffmanCodeBytes
	 * huffmanCodeBytes[0]=10101000(补码)=>byte
	 * */
	private static byte[] zip(byte[] bytes,Map<Byte, String>huffmanCodes) {
		//1.利用huffmanCodes将bytes转成赫夫曼编码对应的字符串
		StringBuilder stringBuilder=new StringBuilder();
		//遍历bytes数组
		for (byte b:bytes) {
			stringBuilder.append(huffmanCodes.get(b));
		}
		//System.out.println("测试stringBuilder="+stringBuilder.toString());
		
		//将"1010100010111111110..."转成byte[]
		
		//统计返回byte[] huffmanCodeBytes长度
		
		int len;
		if (stringBuilder.length()%8==0) {
			len=stringBuilder.length()/8;
		}else {
			len=stringBuilder.length()/8+1;
		}
		//创建存储压缩后的byte数组
		byte[] huffmanCodeBytes=new byte[len];
		int index=0;
		for (int i = 0; i < stringBuilder.length(); i+=8) {
			String strByte;
			if (i+8>stringBuilder.length()) {//不够8位
				strByte=stringBuilder.substring(i);
			}else {
				strByte=stringBuilder.substring(i,i+8);
			}
			
			//将strByte转成一个byte,放入到huffmanCodeBytes
			huffmanCodeBytes[index]=(byte)Integer.parseInt(strByte,2);
			index++;
		}
		return huffmanCodeBytes;
	}
		//测试
		byte[] huffmanCodeBytes=zip(contentBytes, huffmanCodes);
		System.out.println("huffmanCodeBytes="+huffmanCodeBytes);
		
		//发送huffmanCodeBytes数组

 完整代码

package huffmancode;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class HuffmanCode {

	public static void main(String[] args) {
		String content="I like like like java do you like a java";
		byte[] contentBytes=content.getBytes();
		System.out.println(contentBytes.length);//40
		
		List<Node> nodes=getNodes(contentBytes);
		System.out.println("nodes"+nodes);
		
		//测试一把,创建的二叉树
		System.out.println("赫夫曼树");
		Node huffmanTreeRoot=createHuffmanTree(nodes);
		System.out.println("前序遍历");
		huffmanTreeRoot.preOrder();
		
		//测试一把是否生成了对应的赫夫编码
		Map<Byte, String> huffmanCodes=getCodes(huffmanTreeRoot);
		System.out.println("生成的赫夫曼编码表"+huffmanCodes);
		
		//测试
		byte[] huffmanCodeBytes=zip(contentBytes, huffmanCodes);
		System.out.println("huffmanCodeBytes="+huffmanCodeBytes);
		
		//发送huffmanCodeBytes数组
	}
	//编写一个方法,将字符串对应的byte[]数组,通过生成的赫夫曼编码表,返回一个赫夫曼编码压缩后的byte[]
	/*
	 * bytes 这时原始的字符串对应的byte[]
	 * huffmanCodes 生成的赫夫曼编码map
	 * 返回赫夫曼编码处理后的byte[]
	 * 举例:String content="I like like like java do you like a java";=>byte[] contentBytes=content.getBytes();
	 * 返回的是字符串
	 * =>对应的byte[] huffmanCodeBytes,即8位对应一个byte,放入到huffmanCodeBytes
	 * huffmanCodeBytes[0]=10101000(补码)=>byte
	 * */
	private static byte[] zip(byte[] bytes,Map<Byte, String>huffmanCodes) {
		//1.利用huffmanCodes将bytes转成赫夫曼编码对应的字符串
		StringBuilder stringBuilder=new StringBuilder();
		//遍历bytes数组
		for (byte b:bytes) {
			stringBuilder.append(huffmanCodes.get(b));
		}
		//System.out.println("测试stringBuilder="+stringBuilder.toString());
		
		//将"1010100010111111110..."转成byte[]
		
		//统计返回byte[] huffmanCodeBytes长度
		
		int len;
		if (stringBuilder.length()%8==0) {
			len=stringBuilder.length()/8;
		}else {
			len=stringBuilder.length()/8+1;
		}
		//创建存储压缩后的byte数组
		byte[] huffmanCodeBytes=new byte[len];
		int index=0;
		for (int i = 0; i < stringBuilder.length(); i+=8) {
			String strByte;
			if (i+8>stringBuilder.length()) {//不够8位
				strByte=stringBuilder.substring(i);
			}else {
				strByte=stringBuilder.substring(i,i+8);
			}
			
			//将strByte转成一个byte,放入到huffmanCodeBytes
			huffmanCodeBytes[index]=(byte)Integer.parseInt(strByte,2);
			index++;
		}
		return huffmanCodeBytes;
	}
	//生成赫夫曼树对应的赫夫曼编码
	//思路:
	//1.将赫夫曼编码存放在Map<Byte,String>形式
	//32->01 97->100 100->11000等等[形式]
	static Map<Byte,String>huffmanCodes=new HashMap<Byte, String>();
	//2.在生成赫夫曼编码表示中,需要去拼接路径,定义一个StringBuilder存储某个叶子结点的路径
	static StringBuilder stringBuilder=new StringBuilder();
	
	//为了调用方便,我们重载getCodes
	private static Map<Byte,String>getCodes(Node root){
		if (root==null) {
			return null;
		}
		//处理root的左子树
		getCodes(root.left,"0",stringBuilder);
		//处理root的右子树
		getCodes(root.right,"1",stringBuilder);
		return huffmanCodes;
	}
	/*
	 * 功能:将传入的node结点的所有结点的赫夫曼编码得到,并放入到huffmanCodes集合
	 * node 传入结点
	 * code 路径:左子节点是0,右子节点1
	 * stringBuilder  用于拼接路径
	 * */
	private static void getCodes(Node node,String code,StringBuilder stringBuilder) {
		StringBuilder stringBuilder2=new StringBuilder(stringBuilder);
		//将code加入到stringBuilder2
		stringBuilder2.append(code);
		if (node!=null) {//如果node==null不处理
			//判断当前node是叶子节点还是非叶子结点
			if (node.data==null) {//非叶子结点
				//递归处理
				//向右递归
				getCodes(node.left,"0", stringBuilder2);
				//向右递归
				getCodes(node.right, "1", stringBuilder2);
				
			}else {//说明是一个叶子节点
				//就表示找到某个叶子结点的最后
				huffmanCodes.put(node.data,stringBuilder2.toString());
			}
			
		}
	}
	//前序遍历的方法
	private static void preOrder(Node root) {
		if(root!=null) {
			root.preOrder();
		}else {
			System.out.println("赫夫曼树为空");
		}
	}
	private static List<Node>getNodes(byte[] bytes){
		//创建一个ArrayList
		ArrayList<Node> nodes=new ArrayList<Node>();
		
		//遍历bytes,统计每一个byte出现的次数->map[key,value]
		Map<Byte, Integer> counts=new HashMap<>();
		for (byte b:bytes) {
			Integer count=counts.get(b);
			if (count==null) {//Map还没有这个字符数据,第一次
				counts.put(b,1);
			}else {
				counts.put(b,count+1);
			}
		}
		//把每一个键值对转成一个Node对象,并加入到nodes集合
		//遍历map
		for (Map.Entry<Byte, Integer>entry:counts.entrySet()) {
			nodes.add(new Node(entry.getKey(),entry.getValue()));
		}
		return nodes;
	}
	//可以通过List创建对应的赫夫曼树
	private static Node createHuffmanTree(List<Node>nodes) {
		while (nodes.size()>1) {
			//排序,从小到大
			Collections.sort(nodes);
			//取出第一颗最小的二叉树
			Node leftNode=nodes.get(0);
			//取出第二颗最小的二叉树
			Node rightNode=nodes.get(1);
			//创建一颗新的二叉树,它的根结点没有data,只有权值
			Node parent=new Node(null,leftNode.weight+rightNode.weight);
			parent.left=leftNode;
			parent.right=rightNode;
			
			//将已经处理的两棵二叉树从nodes删除
			nodes.remove(leftNode);
			nodes.remove(rightNode);
			//将新的二叉树,加入到nodes
			nodes.add(parent);
		}
		//nodes最后的结点,就是赫夫曼树的根结点
		return nodes.get(0);
	}
}
//创建Node,待数据和权值
class Node implements Comparable<Node>{
	Byte data;//存放数据(字符)本身,比如'a'=>97 ''=>32
	int weight;//权值,表示字符出现的次数
	Node left;
	Node right;
	public Node(Byte data,int weight) {
		this.data=data;
		this.weight=weight;
	}
	@Override
	public int compareTo(Node o) {
		//从小到大排序
		return this.weight-o.weight;
	}
	public String toString() {
		return "Node[data="+data+"weight="+weight+"]";
	}
	//前序遍历
	public void preOrder() {
		System.out.println(this);
		if(this.left!=null) {
			this.left.preOrder();
		}
		if(this.right!=null) {
			this.right.preOrder();
		}
	}
}

 运行效果

测试Test 

package tree;

public class Test {

	@SuppressWarnings("unused")
	public static void main(String[] args) {
		String strByte="10101000";
		System.out.println((byte)Integer.parseInt(strByte,2));//-88
	}
}

 运行效果

 赫夫曼字节数组封装

实现代码

		//分布过程
		List<Node> nodes=getNodes(contentBytes);
		System.out.println("nodes"+nodes);
		
		//测试一把,创建的二叉树
		System.out.println("赫夫曼树");
		Node huffmanTreeRoot=createHuffmanTree(nodes);
		System.out.println("前序遍历");
		huffmanTreeRoot.preOrder();
		
		//测试一把是否生成了对应的赫夫编码
		Map<Byte, String> huffmanCodes=getCodes(huffmanTreeRoot);
		System.out.println("生成的赫夫曼编码表"+huffmanCodes);
		
		//测试
		byte[] huffmanCodeBytes=zip(contentBytes, huffmanCodes);
		System.out.println("huffmanCodeBytes="+huffmanCodeBytes);
		
		//发送huffmanCodeBytes数组
	//使用一个方法,将前面的方法封装起来,便于我们的调用
	/*
	 * bytes原始的字符串对应的字节数组
	 * 是经过赫夫曼编码处理后的字节数组(压缩后的数组)
	 * */
	private static byte[] huffmanZip(byte[] bytes) {
		List<Node> nodes=getNodes(bytes);
		//根据nodes创建的赫夫曼树
		Node huffmanTreeRoot=createHuffmanTree(nodes);
		//对应的赫夫曼编码(根据赫夫曼树)
		Map<Byte, String> huffmanCodes=getCodes(huffmanTreeRoot);
		//根据生成的赫夫曼编码,压缩得到压缩后的赫夫曼编码字节数组
		byte[] huffmanCodeBytes=zip(bytes, huffmanCodes);
		return huffmanCodeBytes;
	}

字节转二进制字符串

数据解压(使用赫夫曼编码解码)

使用赫夫曼编码来解码数据,具体要求是

1)前面我们得到了赫夫曼编码和对应的编码byte[]

2)现在要求使用赫夫曼编码,进行解码,又重新得到原来的字符串"

I like like like java do you like a java"

思路:解码过程,就是编码的一个逆向操作

代码实现

package huffmancode;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class HuffmanCode {

	public static void main(String[] args) {
		String content="i like like like java do you like a java";
		byte[] contentBytes=content.getBytes();
		System.out.println(contentBytes.length);//40
		
		byte[] huffmanCodesBytes=huffmanZip(contentBytes);
		System.out.println("压缩后的结果是:"+Arrays.toString(huffmanCodesBytes)+"长度="+huffmanCodesBytes.length);
		
		//测试一把byteToBitString方法
		//System.out.println("压缩后的结果是:"+Arrays.toString(huffmanCodesBytes)+"长度="+huffmanCodesBytes.length);
		byte[] sourceBytes=decode(huffmanCodes, huffmanCodesBytes);
		System.out.println("原来的字符串="+new String(sourceBytes));
		//如何将数据进行解压(解码)
		
		//分布过程
		/*List<Node> nodes=getNodes(contentBytes);
		System.out.println("nodes"+nodes);
		
		//测试一把,创建的二叉树
		System.out.println("赫夫曼树");
		Node huffmanTreeRoot=createHuffmanTree(nodes);
		System.out.println("前序遍历");
		huffmanTreeRoot.preOrder();
		
		//测试一把是否生成了对应的赫夫编码
		Map<Byte, String> huffmanCodes=getCodes(huffmanTreeRoot);
		System.out.println("生成的赫夫曼编码表"+huffmanCodes);
		
		//测试
		byte[] huffmanCodeBytes=zip(contentBytes, huffmanCodes);
		System.out.println("huffmanCodeBytes="+huffmanCodeBytes);
		
		//发送huffmanCodeBytes数组*/
	}
	
	//完成数据的解压
	//思路
	//1.将huffmanCodeBytes[]
	//重写先转成赫夫曼编码对应的二进制的字符串
	//2.赫夫曼编码对应的二进制的字符串=>对照赫夫曼编码=>I like like like java do you like a java
	
	//编写一个方法,完成对压缩数据的解码
	/*
	 * huffmanCodes 赫夫曼编码表map
	 * huffmanBytes 赫夫曼编码得到的字节数组
	 * 就是原来的字符串对应的数组
	 * */
	private static byte[] decode(Map<Byte, String>huffmanCodes,byte[] huffmanBytes) {
		
		//1.先得到huffmanBytes对应的二进制的字符串,形式
		StringBuilder stringBuilder=new StringBuilder();
		//将byte数组转成二进制的字符串
		for (int i = 0; i < huffmanBytes.length; i++) {
			byte b=huffmanBytes[i];
			//判断是不是最后一个字节
			boolean flag=(i==huffmanBytes.length-1);
			stringBuilder.append(byteToBitString(!flag, b));
		}
	//	System.out.println("赫夫曼字节数组对应的二进制字符串="+stringBuilder.toString());
	//	return null;
		//把字符串安装指定的赫夫曼编码进行解码
		//把赫夫曼编码表进行调换,因为反向查询a->100 100->a
		Map<String, Byte> map=new HashMap<String, Byte>();
		for (Map.Entry<Byte, String>entry:huffmanCodes.entrySet()) {
			map.put(entry.getValue(),entry.getKey());
		}
		
		//创建要给集合,存放byte
		List<Byte> list=new ArrayList<Byte>();
		//i可以理解成就是索引,扫描stringBuilder
		for (int i = 0; i < huffmanBytes.length; i++) {
			int count=1;
			boolean flag=true;
			Byte b=null;
			
			while (flag) {
				//取出一个'1''0'
				String key=stringBuilder.substring(i,i+count);//i不动,让count移动,指定匹配到一个字符
				b=map.get(key);
				if (b==null) {//说明没有匹配到
					count++;
				}else {
					//匹配到
					flag=false;
				}
			}
			list.add(b);
			i+=count;//i直接移动到count
		}
		//当for循环结束后,我们list中存放了所有的字符
		//把list中的数据放入到byte[]并返回
		byte b[]=new byte[list.size()];
		for (int i = 0; i < b.length; i++) {
			b[i]=list.get(i);
		}
		return b;
	}
	private static String byteToBitString(boolean flag,byte b) {
		//使用变量保存b
		int temp=b;//将b转成int
		//如果是正数我们还存在补高位
		
		temp|=256;//temp 1=>000
		String str=Integer.toBinaryString(temp);//返回的是temp对应的二进制的补码
		if (flag) {
			return str.substring(str.length()-8);
		}else {
			return str;
		}		
	}
	//使用一个方法,将前面的方法封装起来,便于我们的调用
	/*
	 * bytes原始的字符串对应的字节数组
	 * 是经过赫夫曼编码处理后的字节数组(压缩后的数组)
	 * */
	private static byte[] huffmanZip(byte[] bytes) {
		List<Node> nodes=getNodes(bytes);
		//根据nodes创建的赫夫曼树
		Node huffmanTreeRoot=createHuffmanTree(nodes);
		//对应的赫夫曼编码(根据赫夫曼树)
		Map<Byte, String> huffmanCodes=getCodes(huffmanTreeRoot);
		//根据生成的赫夫曼编码,压缩得到压缩后的赫夫曼编码字节数组
		byte[] huffmanCodeBytes=zip(bytes, huffmanCodes);
		return huffmanCodeBytes;
	}
	//编写一个方法,将字符串对应的byte[]数组,通过生成的赫夫曼编码表,返回一个赫夫曼编码压缩后的byte[]
	/*
	 * bytes 这时原始的字符串对应的byte[]
	 * huffmanCodes 生成的赫夫曼编码map
	 * 返回赫夫曼编码处理后的byte[]
	 * 举例:String content="I like like like java do you like a java";=>byte[] contentBytes=content.getBytes();
	 * 返回的是字符串
	 * =>对应的byte[] huffmanCodeBytes,即8位对应一个byte,放入到huffmanCodeBytes
	 * huffmanCodeBytes[0]=10101000(补码)=>byte
	 * */
	private static byte[] zip(byte[] bytes,Map<Byte, String>huffmanCodes) {
		//1.利用huffmanCodes将bytes转成赫夫曼编码对应的字符串
		StringBuilder stringBuilder=new StringBuilder();
		//遍历bytes数组
		for (byte b:bytes) {
			stringBuilder.append(huffmanCodes.get(b));
		}
		//System.out.println("测试stringBuilder="+stringBuilder.toString());
		
		//将"1010100010111111110..."转成byte[]
		
		//统计返回byte[] huffmanCodeBytes长度
		
		int len;
		if (stringBuilder.length()%8==0) {
			len=stringBuilder.length()/8;
		}else {
			len=stringBuilder.length()/8+1;
		}
		//创建存储压缩后的byte数组
		byte[] huffmanCodeBytes=new byte[len];
		int index=0;
		for (int i = 0; i < stringBuilder.length(); i+=8) {
			String strByte;
			if (i+8>stringBuilder.length()) {//不够8位
				strByte=stringBuilder.substring(i);
			}else {
				strByte=stringBuilder.substring(i,i+8);
			}
			
			//将strByte转成一个byte,放入到huffmanCodeBytes
			huffmanCodeBytes[index]=(byte)Integer.parseInt(strByte,2);
			index++;
		}
		return huffmanCodeBytes;
	}
	//生成赫夫曼树对应的赫夫曼编码
	//思路:
	//1.将赫夫曼编码存放在Map<Byte,String>形式
	//32->01 97->100 100->11000等等[形式]
	static Map<Byte,String>huffmanCodes=new HashMap<Byte, String>();
	//2.在生成赫夫曼编码表示中,需要去拼接路径,定义一个StringBuilder存储某个叶子结点的路径
	static StringBuilder stringBuilder=new StringBuilder();
	
	//为了调用方便,我们重载getCodes
	private static Map<Byte,String>getCodes(Node root){
		if (root==null) {
			return null;
		}
		//处理root的左子树
		getCodes(root.left,"0",stringBuilder);
		//处理root的右子树
		getCodes(root.right,"1",stringBuilder);
		return huffmanCodes;
	}
	/*
	 * 功能:将传入的node结点的所有结点的赫夫曼编码得到,并放入到huffmanCodes集合
	 * node 传入结点
	 * code 路径:左子节点是0,右子节点1
	 * stringBuilder  用于拼接路径
	 * */
	private static void getCodes(Node node,String code,StringBuilder stringBuilder) {
		StringBuilder stringBuilder2=new StringBuilder(stringBuilder);
		//将code加入到stringBuilder2
		stringBuilder2.append(code);
		if (node!=null) {//如果node==null不处理
			//判断当前node是叶子节点还是非叶子结点
			if (node.data==null) {//非叶子结点
				//递归处理
				//向右递归
				getCodes(node.left,"0", stringBuilder2);
				//向右递归
				getCodes(node.right, "1", stringBuilder2);
				
			}else {//说明是一个叶子节点
				//就表示找到某个叶子结点的最后
				huffmanCodes.put(node.data,stringBuilder2.toString());
			}
			
		}
	}
	//前序遍历的方法
	private static void preOrder(Node root) {
		if(root!=null) {
			root.preOrder();
		}else {
			System.out.println("赫夫曼树为空");
		}
	}
	private static List<Node>getNodes(byte[] bytes){
		//创建一个ArrayList
		ArrayList<Node> nodes=new ArrayList<Node>();
		
		//遍历bytes,统计每一个byte出现的次数->map[key,value]
		Map<Byte, Integer> counts=new HashMap<>();
		for (byte b:bytes) {
			Integer count=counts.get(b);
			if (count==null) {//Map还没有这个字符数据,第一次
				counts.put(b,1);
			}else {
				counts.put(b,count+1);
			}
		}
		//把每一个键值对转成一个Node对象,并加入到nodes集合
		//遍历map
		for (Map.Entry<Byte, Integer>entry:counts.entrySet()) {
			nodes.add(new Node(entry.getKey(),entry.getValue()));
		}
		return nodes;
	}
	//可以通过List创建对应的赫夫曼树
	private static Node createHuffmanTree(List<Node>nodes) {
		while (nodes.size()>1) {
			//排序,从小到大
			Collections.sort(nodes);
			//取出第一颗最小的二叉树
			Node leftNode=nodes.get(0);
			//取出第二颗最小的二叉树
			Node rightNode=nodes.get(1);
			//创建一颗新的二叉树,它的根结点没有data,只有权值
			Node parent=new Node(null,leftNode.weight+rightNode.weight);
			parent.left=leftNode;
			parent.right=rightNode;
			
			//将已经处理的两棵二叉树从nodes删除
			nodes.remove(leftNode);
			nodes.remove(rightNode);
			//将新的二叉树,加入到nodes
			nodes.add(parent);
		}
		//nodes最后的结点,就是赫夫曼树的根结点
		return nodes.get(0);
	}
}
//创建Node,待数据和权值
class Node implements Comparable<Node>{
	Byte data;//存放数据(字符)本身,比如'a'=>97 ''=>32
	int weight;//权值,表示字符出现的次数
	Node left;
	Node right;
	public Node(Byte data,int weight) {
		this.data=data;
		this.weight=weight;
	}
	@Override
	public int compareTo(Node o) {
		//从小到大排序
		return this.weight-o.weight;
	}
	public String toString() {
		return "Node[data="+data+"weight="+weight+"]";
	}
	//前序遍历
	public void preOrder() {
		System.out.println(this);
		if(this.left!=null) {
			this.left.preOrder();
		}
		if(this.right!=null) {
			this.right.preOrder();
		}
	}
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值