赫夫曼编码优点✨✨✨
1.相较于原始编码,使用了无损压缩
2.每一个字符的对应编码都不会是其他任何字符的编码前缀,不会造成二义性
赫夫曼编码构建思路✨✨✨
1.统计句子中各字符出现的个数
2.以字符出现次数作为权值,构建赫夫曼树
3.根据赫夫曼树,给每个字符规定编码:向左的路径为0,向右的路径为1
以下是未封装版本(略微麻烦)
package DataStructure;
import java.util.*;
/**
* Created with IntelliJ IDEA.
* Description:
* User: 86178
* Date: 2024-03-07
* Time: 15:32
*/
public class Test {
static StringBuilder path=new StringBuilder();
public static void main(String[] args) {
String str="i like like like java do you like a java";
//1 将一个字符串转换为int数组
int[] arr=new int[str.length()];
for(int i=0;i<str.length();i++){
arr[i]=str.charAt(i);
}
//2 将一个int数组转换为nodes
List<Node> nodes=getNodes(arr);
Node root=createHuffman(nodes);
//前序遍历
preOrder(root);
//3 获取到赫夫曼树中每一个叶子节点的哈夫曼编码 保存到全局变量huffmanCodes中(Map类型)
getCodes(root);
//4 已知每个字符对应的哈夫曼编码 将其转换为byte数组
byte[] ret=zip(arr,huffmanCodes);
for (byte b : ret) {
System.out.print(b+" ");
}
System.out.println();
System.out.println(huffmanCodes);
}
//将句子转换为对应的哈夫曼代码
public static byte[] zip(int[] arr,Map<Integer,String> huffmanCodes){
//通过拼接 获取赫夫曼代码
StringBuilder str=new StringBuilder();
for (int s : arr) {
str.append(huffmanCodes.get(s));
}
//1 统计返回的赫夫曼编码地长度
int len=0;
if(str.length()%8==0){
//说明刚刚好是八的整数倍
len=str.length()/8;
}else{
len=str.length()/8+1;
}
//创建压缩后的存储数组
byte[] huffmanCodeBytes=new byte[len];
int index=0;
for(int i=0;i<str.length();i+=8){
String strByte;
if(i+8<str.length()){
strByte=str.substring(i,i+8);
}else{
strByte=str.substring(i,str.length());
}
//下一步 将其放入到数组中
huffmanCodeBytes[index]=(byte)Integer.parseInt(strByte,2);
index++;
}
return huffmanCodeBytes;
}
public static void preOrder(Node root){
if(root==null){
System.out.println("节点为空");
return;
}else{
root.preOrder();
}
}
public static List<Node> getNodes(int[] arr){
List<Node> nodes=new ArrayList<>();
Map<Integer,Integer> cnt=new HashMap<>();
for (int i : arr) {
Integer count=cnt.get(i);
if(count==null){
//没有这个字符 第一次
cnt.put(i,1);
}else{
//在原有的基础上加 1
cnt.put(i,count+1);
}
}
//遍历Map
for(Map.Entry<Integer,Integer> entry: cnt.entrySet()){
Node newNode=new Node(entry.getKey(),entry.getValue());
nodes.add(newNode);
}
return nodes;
}
public static Node createHuffman(List<Node> nodes){
while (nodes.size()>1){
Collections.sort(nodes);
Node left=nodes.get(0);
Node right=nodes.get(1);
Node parent=new Node(null,left.weight+right.weight);
parent.left=left;
parent.right=right;
nodes.remove(left);
nodes.remove(right);
nodes.add(parent);
}
return nodes.get(0);
}
//todo 由赫夫曼树生成赫夫曼编码
/*todo 1.将每个字符转换为的哈夫曼编码存放在Map<Integer,String> 中,
其中String为路径 类似于“0110” 左为0,右为1
2.在生成各叶子节点的哈夫曼编码的时候,需要定义一个StringBuilder 存储叶子节点的路径
*/
static Map<Integer,String> huffmanCodes=new HashMap<Integer,String>();
//todo {32=01, 97=100, 100=11000, 117=11001, 101=1110, 118=11011, 105=101, 121=11010, 106=0010, 107=1111, 108=000, 111=0011}
//将传入的node节点的赫夫曼编码得到 并且 存储到huffmanCodes中
public static void getCodes(Node node,String code,StringBuilder path){
StringBuilder road=new StringBuilder(path);
road.append(code);
if(node!=null){//如果node==null 不需要进行处理
if(node.data!=null){
huffmanCodes.put(node.data,road.toString());
return ;
}else{
//说明是非叶子节点 继续递归
getCodes(node.left,"0",road);
getCodes(node.right,"1",road);
}
}
}
//对上面代码进行重载 使调用更加简单
public static void getCodes(Node node){
if(node==null){
System.out.println("节点为空");
return;
}else{
getCodes(node,"",path);
}
}
}
class Node implements Comparable<Node>{
public Integer data;//字符对应ASCII
public int weight;//次数
public Node left;
public Node right;
public Node(Integer data,int weight){
//构造器
this.data=data;
this.weight=weight;
}
//前序遍历
public void preOrder(){
System.out.println(this);
if(this.left!=null) this.left.preOrder();
if(this.right!=null) this.right.preOrder();
}
@Override
public int compareTo(Node o){
return this.weight-o.weight;
}
@Override
public String toString(){
return "Node [ data="+data+" weight="+weight+" ]";
}
}
以下是封装版本
package DataStructure;
import java.util.*;
/**
* Created with IntelliJ IDEA.
* Description:
* User: 86178
* Date: 2024-03-07
* Time: 15:32
*/
public class Test {
static StringBuilder path=new StringBuilder();
public static void main(String[] args) {
String str="i like like like java do you like a java";
//todo 1 将一个字符串转换为int数组
int[] arr=new int[str.length()];
for(int i=0;i<str.length();i++){
arr[i]=str.charAt(i);
}
byte[] huffmanBytes=huffmanZip(arr);
System.out.println("经过赫夫曼编码压缩后的byte数组为:");
for (byte huffmanByte : huffmanBytes) {
System.out.print(huffmanByte+" ");
}
}
//进行封装
public static byte[] huffmanZip(int[] arr){
//todo 2 将一个int数组转换为nodes
List<Node> nodes=getNodes(arr);
Node root=createHuffman(nodes);
//todo 3 获取到赫夫曼树中每一个叶子节点的哈夫曼编码 保存到全局变量huffmanCodes中(Map类型)
getCodes(root);
//todo 4 已知每个字符对应的哈夫曼编码 将其转换为byte数组
byte[] ret=zip(arr,huffmanCodes);
return ret;
}
//将句子转换为对应的哈夫曼代码
public static byte[] zip(int[] arr,Map<Integer,String> huffmanCodes){
//通过拼接 获取赫夫曼代码
StringBuilder str=new StringBuilder();
for (int s : arr) {
str.append(huffmanCodes.get(s));
}
//1 统计返回的赫夫曼编码地长度
int len=0;
if(str.length()%8==0){
//说明刚刚好是八的整数倍
len=str.length()/8;
}else{
len=str.length()/8+1;
}
//创建压缩后的存储数组
byte[] huffmanCodeBytes=new byte[len];
int index=0;
for(int i=0;i<str.length();i+=8){
String strByte;
if(i+8<str.length()){
strByte=str.substring(i,i+8);
}else{
strByte=str.substring(i,str.length());
}
//下一步 将其放入到数组中
huffmanCodeBytes[index]=(byte)Integer.parseInt(strByte,2);
index++;
}
return huffmanCodeBytes;
}
public static void preOrder(Node root){
if(root==null){
System.out.println("节点为空");
return;
}else{
root.preOrder();
}
}
public static List<Node> getNodes(int[] arr){
List<Node> nodes=new ArrayList<>();
Map<Integer,Integer> cnt=new HashMap<>();
for (int i : arr) {
Integer count=cnt.get(i);
if(count==null){
//没有这个字符 第一次
cnt.put(i,1);
}else{
//在原有的基础上加 1
cnt.put(i,count+1);
}
}
//遍历Map
for(Map.Entry<Integer,Integer> entry: cnt.entrySet()){
Node newNode=new Node(entry.getKey(),entry.getValue());
nodes.add(newNode);
}
return nodes;
}
public static Node createHuffman(List<Node> nodes){
while (nodes.size()>1){
Collections.sort(nodes);
Node left=nodes.get(0);
Node right=nodes.get(1);
Node parent=new Node(null,left.weight+right.weight);
parent.left=left;
parent.right=right;
nodes.remove(left);
nodes.remove(right);
nodes.add(parent);
}
return nodes.get(0);
}
//todo 由赫夫曼树生成赫夫曼编码
/*todo 1.将每个字符转换为的哈夫曼编码存放在Map<Integer,String> 中,
其中String为路径 类似于“0110” 左为0,右为1
2.在生成各叶子节点的哈夫曼编码的时候,需要定义一个StringBuilder 存储叶子节点的路径
*/
static Map<Integer,String> huffmanCodes=new HashMap<Integer,String>();
//todo {32=01, 97=100, 100=11000, 117=11001, 101=1110, 118=11011, 105=101, 121=11010, 106=0010, 107=1111, 108=000, 111=0011}
//将传入的node节点的赫夫曼编码得到 并且 存储到huffmanCodes中
public static void getCodes(Node node,String code,StringBuilder path){
StringBuilder road=new StringBuilder(path);
road.append(code);
if(node!=null){//如果node==null 不需要进行处理
if(node.data!=null){
huffmanCodes.put(node.data,road.toString());
return ;
}else{
//说明是非叶子节点 继续递归
getCodes(node.left,"0",road);
getCodes(node.right,"1",road);
}
}
}
//对上面代码进行重载 使调用更加简单
public static void getCodes(Node node){
if(node==null){
System.out.println("节点为空");
return;
}else{
getCodes(node,"",path);
}
}
}
class Node implements Comparable<Node>{
public Integer data;//字符对应ASCII
public int weight;//次数
public Node left;
public Node right;
public Node(Integer data,int weight){
//构造器
this.data=data;
this.weight=weight;
}
//前序遍历
public void preOrder(){
System.out.println(this);
if(this.left!=null) this.left.preOrder();
if(this.right!=null) this.right.preOrder();
}
@Override
public int compareTo(Node o){
return this.weight-o.weight;
}
@Override
public String toString(){
return "Node [ data="+data+" weight="+weight+" ]";
}
}