Ai
1 Star 1 Fork 1

xcc/structures-and-algorithm

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
该仓库未声明开源许可证文件(LICENSE),使用请关注具体项目描述及其代码上游依赖。
克隆/下载
HuffmanCode.java 12.77 KB
一键复制 编辑 原始数据 按行查看 历史
xcc 提交于 2020-12-20 14:55 +08:00 . 添加文件的压缩和解压
package com.xcc.dataStructures.demo10_huffman;
import java.io.*;
import java.util.*;
/**
* 赫夫曼编码
*
* @author xiaocheng
* @date 2020/12/18 9:23
*/
public class HuffmanCode {
public static void main(String[] args) {
//压缩文件
String srcFile = "D:\\input\\59fc1fa95d771.jpg";
String destFile = "D:\\input\\59fc1fa95d771.zip";
zipFile(srcFile, destFile);
System.out.println("压缩完成");
String zipFile = "D:\\input\\59fc1fa95d771.zip";
String unzipFile = "D:\\input\\59fc1fa95d7711.jpg";
unzipFile(zipFile,unzipFile);
System.out.println("解压成功");
/*//压缩字符串
String str = "i like like like java do you like a java 12";
byte[] contentBytes = str.getBytes();
byte[] zipBytes = zip(contentBytes);
System.out.println(Arrays.toString(zipBytes));
byte[] sourceBytes = unzip(zipBytes, huffmanCodeMap);
System.out.println(new String(sourceBytes));*/
}
/**
* 解压文件
*
* @param srcFile 压缩之后的文件
* @param destFile 解压后的文件
*/
private static void unzipFile(String srcFile, String destFile) {
InputStream is = null;
ObjectInputStream ois = null;
OutputStream os = null;
try {
is = new FileInputStream(srcFile);
ois = new ObjectInputStream(is);
//读取压缩之后的 文件 和 对应的赫夫曼编码
byte[] zipBytes = (byte[]) ois.readObject();
Map<Byte, String> huffmanMap = (Map<Byte, String>) ois.readObject();
//解压
byte[] srcBytes = unzip(zipBytes, huffmanMap);
//输出
os = new FileOutputStream(destFile);
os.write(srcBytes);
} catch (Exception e) {
e.printStackTrace();
}finally {
try {
os.close();
ois.close();
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 压缩文件
*
* @param srcFile 原始文件路径
* @param destFile 压缩后文件路径
*/
private static void zipFile(String srcFile, String destFile) {
InputStream is = null;
OutputStream os = null;
//对象输出流
ObjectOutputStream oos = null;
try {
is = new FileInputStream(srcFile);
//创建原始字节数组
byte[] bytes = new byte[is.available()];
//读取文件
is.read(bytes);
//压缩
byte[] zip = zip(bytes);
os = new FileOutputStream(destFile);
//关联
oos = new ObjectOutputStream(os);
//将压缩之后的数组 和 赫夫曼编码对应关系 写出
oos.writeObject(zip);
oos.writeObject(huffmanCodeMap);
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
oos.close();
os.close();
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 转换成原始的字符串
*/
private static byte[] unzip(byte[] bytes, Map<Byte, String> huffmanMap) {
//先将数组转换为对应的赫夫曼编码的10010..组成的字符串
String huffmancode = decodeBytesToHuffmancode(bytes);
// System.out.println(huffmancode);
//将赫夫曼编码转换为对应的字符串
return decodeHuffmancodeToSourceStr(huffmancode, huffmanMap);
}
/**
* 将赫夫曼编码进行解码为原始字节数组
*
* @param huffmancode 赫夫曼编码
* @param huffmanMap 记录原始字节与编码的map集合
* @return 原始字符串
*/
private static byte[] decodeHuffmancodeToSourceStr(String huffmancode, Map<Byte, String> huffmanMap) {
//将map反转。原来是32->010,98->001... 转换为001->98,010->32...
Map<String, Byte> map = new HashMap<>();
for (Map.Entry<Byte, String> entry : huffmanMap.entrySet()) {
map.put(entry.getValue(), entry.getKey());
}
//原始字节集合
List<Byte> byteList = new ArrayList<>();
for (int i = 0; i < huffmancode.length(); ) {
StringBuilder stringBuilder = new StringBuilder();
//将huffman编码组成的字符村1000100011...,进行遍历。
//当指针指向一定的值存储在反转红藕的map集合中时,停止
while (map.get(stringBuilder.toString()) == null) {
if (i >= huffmancode.length()) {
break;
}
stringBuilder.append(huffmancode.charAt(i));
i++;
}
//获取每个字符,并组装成对应的字符串
Byte aByte = map.get(stringBuilder.toString());
byteList.add(aByte);
}
byte[] bytes = new byte[byteList.size()];
for (int i = 0; i < byteList.size(); i++) {
bytes[i] = byteList.get(i);
}
return bytes;
}
/**
* 将压缩后的数组转换为赫夫曼编码
*
* @param bytes 压缩后的字节数组
* @return 赫夫曼编码
*/
private static String decodeBytesToHuffmancode(byte[] bytes) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < bytes.length; i++) {
byte b = bytes[i];
//将当前byte转换为二进制字符串
String binaryString = Integer.toBinaryString(b);
//判断当前二进制是否长度大于8,如果大于8则截取
if (binaryString.length() > 8) {
sb.append(binaryString.substring(binaryString.length() - 8));
} else {
//判断是否为倒数第二个字节,如果为倒数第二个字节 则其二进制的长度应该为倒数第一个数的长度。
if (i == bytes.length - 2) {
byte last = bytes[i + 1];
int length = binaryString.length();
StringBuilder builder = new StringBuilder();
//将少于倒数第一个数值长度 的二进制 前面用0 补齐
for (int j = 0; j < last - length; j++) {
builder.append(0);
}
builder.append(binaryString);
sb.append(builder);
break;
} else {
//如果不是则补齐前面的0
int length = binaryString.length();
StringBuilder builder = new StringBuilder();
for (int j = 0; j < 8 - length; j++) {
builder.append(0);
}
builder.append(binaryString);
sb.append(builder);
}
}
}
return sb.toString();
}
/**
* 整合 : 将原始的数据转换为赫夫曼数组
*/
private static byte[] zip(byte[] contentBytes) {
//第一步:构建node结点集合,将每个字符(byte)出现的次数统计并封装到list集合中
List<Node> nodes = getNodes(contentBytes);
//第二步:构建赫夫曼树
Node root = creatHuffmanTree(nodes);
//第三步:获取赫夫曼树中每个结点对应的字符(byte),出现对应的路径。其中key为字符(byte),value为路径(0,1组装)
Map<Byte, String> nodePath = getNodePath(root);
//第四步:根据字符路径集合,获取赫夫曼编码的字符串
String huffmanCode = createHuffmanCode(contentBytes, nodePath);
// System.out.println(huffmanCode);
//第五步:将赫夫曼编码字符串,转换为压缩后的赫夫曼数组
return convertToHuffmanBytes(huffmanCode);
}
/**
* 将赫夫曼编码数据转换为压缩之后的数组
*
* @param code 赫夫曼编码的字符串
* @return 赫夫曼压缩数组
*/
private static byte[] convertToHuffmanBytes(String code) {
//获取转换后数组长度
int length = (code.length() + 7) / 8;
byte[] bytes = new byte[length + 1];
for (int i = 0, index = 0; i < code.length(); i += 8, index++) {
if (code.length() > i + 8) {
//将2进制转成10进制 并强转为byte
bytes[index] = (byte) Integer.parseInt(code.substring(i, i + 8), 2);
} else {
//最后一个byte存储 倒数第二个字节的位数(防止出现0110->6->110) 出现位数少1的情况 最后无法匹配而报错
String substring = code.substring(i);
bytes[index] = (byte) Integer.parseInt(substring, 2);
bytes[index + 1] = (byte) substring.length();
}
}
return bytes;
}
/**
* 获取赫夫曼编码的字符串
*/
private static String createHuffmanCode(byte[] contentBytes, Map<Byte, String> nodePath) {
StringBuilder stringBuilder = new StringBuilder();
for (byte key : contentBytes) {
stringBuilder.append(nodePath.get(key));
}
return stringBuilder.toString();
}
/**
* 根据根节点获取对应的编码集合
*/
private static Map<Byte, String> getNodePath(Node node) {
return getNodePath(node, "", new StringBuilder());
}
static Map<Byte, String> huffmanCodeMap = new HashMap<>();
/**
* 获取对应的节点和对应的编码
*
* @param node 需要获取的节点
* @param code 当前节点相对上一个节点的编码
* @param stringBuilder 父节点的路径
* @return map集合:key为字符byte,value为路径
*/
private static Map<Byte, String> getNodePath(Node node, String code, StringBuilder stringBuilder) {
StringBuilder sb = new StringBuilder(stringBuilder);
sb.append(code);
if (node != null) {
//非叶子结点
if (node.data == null) {
//左子树
getNodePath(node.left, "0", sb);
//右子数
getNodePath(node.right, "1", sb);
} else {
//叶子结点
huffmanCodeMap.put(node.data, sb.toString());
}
}
return huffmanCodeMap;
}
/**
* 获取节点集合
*/
private static List<Node> getNodes(byte[] contentBytes) {
//先统计bytes中每个字节的次数
Map<Byte, Integer> map = new HashMap<>();
for (byte key : contentBytes) {
map.put(key, map.get(key) == null ? 1 : map.get(key) + 1);
}
List<Node> list = new ArrayList<>();
for (Map.Entry<Byte, Integer> entry : map.entrySet()) {
list.add(new Node(entry.getKey(), entry.getValue()));
}
return list;
}
/**
* 生成赫夫曼树
*/
private static Node creatHuffmanTree(List<Node> nodes) {
while (nodes.size() > 1) {
Collections.sort(nodes);
Node leftNode = nodes.get(0);
Node rightNode = nodes.get(1);
Node parent = new Node(null, leftNode.count + rightNode.count);
parent.left = leftNode;
parent.right = rightNode;
nodes.remove(leftNode);
nodes.remove(rightNode);
nodes.add(parent);
}
return nodes.get(0);
}
public static void preOrder(Node root) {
if (root == null) {
System.out.println("当前二叉树为空,不能遍历!");
return;
}
root.preOrder();
}
/**
* 结点
*/
static class Node implements Comparable<Node> {
//字节 i l i ...
Byte data;
//出现的次数
Integer count;
//左子树
Node left;
//右子数
Node right;
//前序遍历
public void preOrder() {
// System.out.println((this.data == null?null:(char)Integer.parseInt(this.data.toString()))+"\t"+this.count);
System.out.println(this);
if (this.left != null) {
this.left.preOrder();
}
if (this.right != null) {
this.right.preOrder();
}
}
public Node(Byte data, Integer count) {
this.data = data;
this.count = count;
}
@Override
public String toString() {
return "Node[" +
"data=" + data +
", count=" + count +
']';
}
@Override
public int compareTo(Node o) {
return this.count - o.count;
}
}
}
Loading...
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
1
https://gitee.com/xiaocheng0902/structures-and-algorithm.git
git@gitee.com:xiaocheng0902/structures-and-algorithm.git
xiaocheng0902
structures-and-algorithm
structures-and-algorithm
master

搜索帮助