哈夫曼编码
一、使用哈夫曼编码对字节数组进行压缩
1、创建哈夫曼树
节点
class NodeNo implements Comparable<NodeNo>{
Byte data; //存放数据
int weight; //权值
NodeNo left; //左子节点
NodeNo right; //右子节点
public NodeNo(Byte data, int weight) {
this.data = data;
this.weight = weight;
}
@Override
public int compareTo(NodeNo o) {
return this.weight - o.weight;
}
//前序遍历
public void pre(NodeNo node) {
if(node==null) {
return;
}
System.out.print(node.weight + " ");
pre(node.left);
pre(node.right);
}
}
//创建哈夫曼树
public static NodeNo create(byte[] bytes) {
//创建一个列表
List<NodeNo> list = new ArrayList<NodeNo>();
//用于记录每一个节点的权值
Map<Byte, Integer> counts = new HashMap<>();
//计算权值
for(byte b : bytes) {
Integer count = counts.get(b);
if(count==null) {
counts.put(b, 1);
} else {
counts.put(b, count+1);
}
}
//将数据存放到列表中
Set<Byte> set = counts.keySet();
for(Byte b : set) {
list.add(new NodeNo(b, counts.get(b)));
}
//创建哈夫曼树
while(list.size() > 1) { //遍历列表
//对列表进行排序
Collections.sort(list);
//取出前两个值
NodeNo temp1 = list.get(0);
NodeNo temp2 = list.get(1);
//将两个数进行相加得到父节点
NodeNo parent = new NodeNo(null, temp1.weight + temp2.weight);
parent.left = temp1;
parent.right = temp2;
//从列表中删除最小的两个节点
list.remove(temp1);
list.remove(temp2);
//将新节点加入到列表中
list.add(parent);
}
return list.get(0);
}
2、获取每个叶子节点的哈夫曼编码
//获取每个叶子节点的哈夫曼编码
public static Map<Byte, String> getHaffmanCodes(NodeNo node, String code) {
if(node.left==null && node.right==null) {
map.put(node.data, code);
return map;
}
if(node.left!=null) {
getHaffmanCodes(node.left, code + "0");
}
if(node.right!=null) {
getHaffmanCodes(node.right, code + "1");
}
return map;
}
3、获取压缩之后的字节数组
//获取压缩之后的字节数组
public static byte[] getHaffmanCodeBytes(byte bytes[]) {
//字符串,将编码拼串
String str = new String();
for(Byte b : bytes) { //从哈夫曼编码中获取二进制编码
str += map.get(b);
}
//System.out.println(str);
//每个字节占8为
//获取字节数组的长度
int len = (str.length() + 7) / 8;
byte[] haffmanByts = new byte[len];
int j = 0;
for(int i=0; i<str.length(); i+=8) { //每8位占一个字节
String temp;
if(i+8 < str.length()) { //截取字符串
temp = str.substring(i, i+8);
} else {
temp = str.substring(i);
}
haffmanByts[j++] = (byte)Integer.parseInt(temp, 2);
}
return haffmanByts;
}
4、整合压缩过程
//生成哈夫曼编码
static Map<Byte, String> map = null;
//编码
public static byte[] getCodes(byte[] bytes) {
//重新初始化map集合
map = null;
map = new HashMap<>();
NodeNo node = create(bytes); //创建哈夫曼树
getHaffmanCodes(node, ""); //获取哈夫曼编码
return getHaffmanCodeBytes(bytes); //得到压缩的字节数组
}
二、解码
1、将字节转化位一个二进制字符串
//将字节转化位一个二进制字符串
//flag用来标记是否需要补高位,如果需要补高位,值为true,如果是最后一个字节,无需不高位
public static String byteToBitString(boolean flag, byte b) {
int temp = b;
if(flag) {
//补高位,按位或
temp |= 256;
}
//返回temp的二进制补码
String str = Integer.toBinaryString(temp);
if(flag) {
str = str.substring(str.length() - 8);
}
return str;
}
2、对压缩的字节数组进行解码得到原始的字节数组
//对压缩的数据进行解码
public static byte[] decode(byte[] haffmanByte) {
String bitStr = "";
//利用字节数组得到二进制字符串
for(int i=0; i<haffmanByte.length; i++) {
//判断是不是最后一个字节,获取二进制补码
bitStr += byteToBitString(!(i==haffmanByte.length-1), haffmanByte[i]);
}
//将二进制编码表进行倒转
Map<String, Byte> codeToString = new HashMap<>();
for(Map.Entry<Byte, String> entry: map.entrySet()) {
codeToString.put(entry.getValue(), entry.getKey());
}
//将解码之后的字符二进制编码存放在列表中
List<Byte> list = new ArrayList<>();
for(int i=0; i<bitStr.length();) {
int count = 1;
boolean flag =true;
Byte b = null;
while(flag) {
//字符串匹配
String temp = bitStr.substring(i, i+count);
b = codeToString.get(temp);
if(b!=null) {
flag = false;
} else {
count++;
}
}
list.add(b);
i += count;
}
byte[] b = new byte[list.size()];
int i = 0;
for(Byte t : list) {
b[i++] = t;
}
//String s = new String(b);
//System.out.println(s.length());
return b;
}
三、文件压缩
//对文件进行压缩
public static void zipFile(String file, String toSave) {
FileInputStream is = null;
OutputStream os = null;
ObjectOutputStream oos = null;
try {
//初始化三个文件流
is = new FileInputStream(file);
//读取文件的字节数组
byte[] b = new byte[is.available()];
is.read(b);
//使用哈夫曼编码进行压缩
byte[] res = getCodes(b);
os = new FileOutputStream(toSave);
oos = new ObjectOutputStream(os);
//将编码后的字节数组以及哈夫曼编码写入压缩文件中
oos.writeObject(res);
oos.writeObject(map);
} catch(Exception e) {
System.out.println("文件压缩出错");
} finally {
//关闭各种流
try {
is.close();
oos.close();
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
四、文件解压
//解压文件
public static void unZipFile(String zipFile, String dstFile) {
//定义文件输入流
InputStream is = null;
//定义一个对象输入流
ObjectInputStream ois = null;
//定义文件的输出流
OutputStream os = null;
try {
//创建文件输入流
is = new FileInputStream(zipFile);
//创建一个和 is 关联的对象输入流
ois = new ObjectInputStream(is);
//读取 byte 数组 huffmanBytes
byte[] huffmanBytes = (byte[])ois.readObject();
//读取赫夫曼编码表
map = (Map<Byte,String>)ois.readObject();
//解码
byte[] bytes = decode(huffmanBytes);
//将 bytes 数组写入到目标文件
os = new FileOutputStream(dstFile);
//写数据到 dstFile 文件
os.write(bytes);
} catch (Exception e) {
// TODO: handle exception
System.out.println(e.getMessage());
} finally {
try {
os.close();
ois.close();
is.close();
} catch (Exception e2) {
// TODO: handle exception
System.out.println(e2.getMessage());
}
}
}
五、全部代码
public class HaffmanNo {
public static void main(String[] args) {
byte[] b = "i like like like java do you like a java".getBytes();
//输出压缩前大小
// System.out.println(b.length);
//压缩后
// NodeNo no = NodeHaffmanNo.create(b);
// no.pre(no);
// System.out.println("---------------------------------------------------------------------------------------");
// System.out.println(NodeHaffmanNo.getHaffmanCodes(no, ""));
// System.out.println("---------------------------------------------------------------------------------------");
// byte[] b1 = NodeHaffmanNo.getHaffmanCodeBytes(b);
// System.out.println(Arrays.toString(b1));
// System.out.println("解码后------------------------------------------------------------");
// System.out.println(NodeHaffmanNo.decode(NodeHaffmanNo.map, b1));
// byte[] codes = NodeHaffmanNo.getCodes(b);
// System.out.println(Arrays.toString(codes));
// System.out.println(new String(NodeHaffmanNo.decode(codes)));
NodeHaffmanNo.zipFile("C:\\Users\\10507\\Desktop\\实验用图\\1.jpg", "D:\\b.zip");
//NodeHaffmanNo.unZipFile("D:\\a.zip", "D:\\1.jpg");
System.out.println("压缩完成");
}
}
class NodeHaffmanNo {
//生成哈夫曼编码
static Map<Byte, String> map = null;
//编码
public static byte[] getCodes(byte[] bytes) {
//重新初始化map集合
map = null;
map = new HashMap<>();
NodeNo node = create(bytes); //创建哈夫曼树
getHaffmanCodes(node, ""); //获取哈夫曼编码
return getHaffmanCodeBytes(bytes); //得到压缩的字节数组
}
//创建哈夫曼树
public static NodeNo create(byte[] bytes) {
//创建一个列表
List<NodeNo> list = new ArrayList<NodeNo>();
//用于记录每一个节点的权值
Map<Byte, Integer> counts = new HashMap<>();
//计算权值
for(byte b : bytes) {
Integer count = counts.get(b);
if(count==null) {
counts.put(b, 1);
} else {
counts.put(b, count+1);
}
}
//将数据存放到列表中
Set<Byte> set = counts.keySet();
for(Byte b : set) {
list.add(new NodeNo(b, counts.get(b)));
}
//创建哈夫曼树
while(list.size() > 1) { //遍历列表
//对列表进行排序
Collections.sort(list);
//取出前两个值
NodeNo temp1 = list.get(0);
NodeNo temp2 = list.get(1);
//将两个数进行相加得到父节点
NodeNo parent = new NodeNo(null, temp1.weight + temp2.weight);
parent.left = temp1;
parent.right = temp2;
//从列表中删除最小的两个节点
list.remove(temp1);
list.remove(temp2);
//将新节点加入到列表中
list.add(parent);
}
return list.get(0);
}
//获取每个叶子节点的哈夫曼编码
public static Map<Byte, String> getHaffmanCodes(NodeNo node, String code) {
if(node.left==null && node.right==null) {
map.put(node.data, code);
return map;
}
if(node.left!=null) {
getHaffmanCodes(node.left, code + "0");
}
if(node.right!=null) {
getHaffmanCodes(node.right, code + "1");
}
return map;
}
//获取压缩之后的字节数组
public static byte[] getHaffmanCodeBytes(byte bytes[]) {
//字符串,将编码拼串
String str = new String();
for(Byte b : bytes) { //从哈夫曼编码中获取二进制编码
str += map.get(b);
}
//System.out.println(str);
//每个字节占8为
//获取字节数组的长度
int len = (str.length() + 7) / 8;
byte[] haffmanByts = new byte[len];
int j = 0;
for(int i=0; i<str.length(); i+=8) { //每8位占一个字节
String temp;
if(i+8 < str.length()) { //截取字符串
temp = str.substring(i, i+8);
} else {
temp = str.substring(i);
}
haffmanByts[j++] = (byte)Integer.parseInt(temp, 2);
}
return haffmanByts;
}
//将字节转化位一个二进制字符串
//flag用来标记是否需要补高位,如果需要补高位,值为true,如果是最后一个字节,无需不高位
public static String byteToBitString(boolean flag, byte b) {
int temp = b;
if(flag) {
//补高位,按位或
temp |= 256;
}
//返回temp的二进制补码
String str = Integer.toBinaryString(temp);
if(flag) {
str = str.substring(str.length() - 8);
}
return str;
}
//对压缩的数据进行解码
public static byte[] decode(byte[] haffmanByte) {
String bitStr = "";
//利用字节数组得到二进制字符串
for(int i=0; i<haffmanByte.length; i++) {
//判断是不是最后一个字节,获取二进制补码
bitStr += byteToBitString(!(i==haffmanByte.length-1), haffmanByte[i]);
}
//将二进制编码表进行倒转
Map<String, Byte> codeToString = new HashMap<>();
for(Map.Entry<Byte, String> entry: map.entrySet()) {
codeToString.put(entry.getValue(), entry.getKey());
}
//将解码之后的字符二进制编码存放在列表中
List<Byte> list = new ArrayList<>();
for(int i=0; i<bitStr.length();) {
int count = 1;
boolean flag =true;
Byte b = null;
while(flag) {
//字符串匹配
String temp = bitStr.substring(i, i+count);
b = codeToString.get(temp);
if(b!=null) {
flag = false;
} else {
count++;
}
}
list.add(b);
i += count;
}
byte[] b = new byte[list.size()];
int i = 0;
for(Byte t : list) {
b[i++] = t;
}
//String s = new String(b);
//System.out.println(s.length());
return b;
}
//对文件进行压缩
public static void zipFile(String file, String toSave) {
FileInputStream is = null;
OutputStream os = null;
ObjectOutputStream oos = null;
try {
//初始化三个文件流
is = new FileInputStream(file);
//读取文件的字节数组
byte[] b = new byte[is.available()];
is.read(b);
//使用哈夫曼编码进行压缩
byte[] res = getCodes(b);
os = new FileOutputStream(toSave);
oos = new ObjectOutputStream(os);
//将编码后的字节数组以及哈夫曼编码写入压缩文件中
oos.writeObject(res);
oos.writeObject(map);
} catch(Exception e) {
System.out.println("文件压缩出错");
} finally {
//关闭各种流
try {
is.close();
oos.close();
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
//解压文件
public static void unZipFile(String zipFile, String dstFile) {
//定义文件输入流
InputStream is = null;
//定义一个对象输入流
ObjectInputStream ois = null;
//定义文件的输出流
OutputStream os = null;
try {
//创建文件输入流
is = new FileInputStream(zipFile);
//创建一个和 is 关联的对象输入流
ois = new ObjectInputStream(is);
//读取 byte 数组 huffmanBytes
byte[] huffmanBytes = (byte[])ois.readObject();
//读取赫夫曼编码表
map = (Map<Byte,String>)ois.readObject();
//解码
byte[] bytes = decode(huffmanBytes);
//将 bytes 数组写入到目标文件
os = new FileOutputStream(dstFile);
//写数据到 dstFile 文件
os.write(bytes);
} catch (Exception e) {
// TODO: handle exception
System.out.println(e.getMessage());
} finally {
try {
os.close();
ois.close();
is.close();
} catch (Exception e2) {
// TODO: handle exception
System.out.println(e2.getMessage());
}
}
}
}
class NodeNo implements Comparable<NodeNo>{
Byte data; //存放数据
int weight; //权值
NodeNo left; //左子节点
NodeNo right; //右子节点
public NodeNo(Byte data, int weight) {
this.data = data;
this.weight = weight;
}
@Override
public int compareTo(NodeNo o) {
return this.weight - o.weight;
}
//前序遍历
public void pre(NodeNo node) {
if(node==null) {
return;
}
System.out.print(node.weight + " ");
pre(node.left);
pre(node.right);
}
}