java树
线索化树
package tree;
public class treadbinarytree {
public static void main(String[] args) {
//测试中序线索二叉树的功能
HeroNode root = new HeroNode(1, "Tom");
HeroNode node2 = new HeroNode(3, "jack");
HeroNode node3 = new HeroNode(6, "smith");
HeroNode node4 = new HeroNode(8, "mary");
HeroNode node5 = new HeroNode(10, "king");
HeroNode node6 = new HeroNode(14, "dim");
root.setLeft(node2);
root.setRight(node3);
node2.setLeft(node4);
node2.setRight(node5);
node3.setLeft(node6);
//测试线索化
BinaryTree binaryTree = new BinaryTree();
binaryTree.setRoot(root);
binaryTree.threadedNodes();
//以10号节点为例
HeroNode leftNode = node5.getLeft();
HeroNode rightNode = node5.getRight();
System.out.println("10号节点的前驱节点是=" + leftNode);
System.out.println("10号节点的后继节点是=" + rightNode);
//线索化的方式遍历线索化二叉树
System.out.println("线索化的方式遍历线索化二叉树");
binaryTree.threadedList();//8->3->10->1->14->6
}
}
//定义ThreadedBinaryTree实现了线索化功能的二叉树
class BinaryTree {
private HeroNode root;
//再递归进行线索化时,这个pre总是保留前一个节点
private HeroNode pre = null;
public void setRoot(HeroNode root) {
this.root = root;
}
//重载
public void threadedNodes() {
this.threadedNodes(root);
}
//遍历线索化二叉树的方法
public void threadedList() {
//定义临时存储变量node
HeroNode node = root;
while (node != null) {
//循环的找到leftType==1的节点,第一个找到的应该是8
while (node.getLeftType() == 0) {
node = node.getLeft();
}
System.out.println(node);
//如果当前节点的右指针指向的是后继节点,就一直输出
while (node.getRightType() == 1) {
node = node.getRight();
System.out.println(node);
}
//替换这个遍历的节点
node = node.getRight();
}
}
//建立中序线索化二叉树方法
/**
* @param node 当前需要线索化的节点
*/
public void threadedNodes(HeroNode node) {
//如果当前节点为空
if (node == null) {
return;
}
//(1)先线索化左子树
threadedNodes(node.getLeft());
//(2)线索化当前节点[有难度]
/*先处理当前节点的前驱节点*/
/*以8节点为例:8.left = null;8.letfType = 1*/
if (node.getLeft() == null) {
//让当前节点的左指针指向前驱节点
node.setLeft(pre);
//并且修改当前节点的左指针的类型,指向前驱节点
node.setLeftType(1);
}
/*再处理后继节点*/
if (pre != null && pre.getRight() == null) {
pre.setRight(node);
pre.setRightType(1);
}
//每处理一个节点后,让当前节点是下一个节点的前驱节点
pre = node;
//(3)再线索化右子树
threadedNodes(node.getRight());
}
}
//创建HeroNode
class HeroNode {
private int no;
private String name;
private HeroNode left;
private HeroNode right;
//New
//1. if(leftType==0),表示指向左子树,if(leftType==1)责表示指向前驱节点
//2. if(rightType==0),表示指向右子树,if(righType==1)则表示指向后继节点
private int leftType;
private int rightType;
public int getLeftType() {
return leftType;
}
public int getRightType() {
return rightType;
}
public void setLeftType(int leftType) {
this.leftType = leftType;
}
public void setRightType(int rightType) {
this.rightType = rightType;
}
public HeroNode(int no, String name) {
this.no = no;
this.name = name;
}
public int getNo() {
return no;
}
public void setNo(int no) {
this.no = no;
}
public HeroNode getLeft() {
return left;
}
public void setLeft(HeroNode left) {
this.left = left;
}
public HeroNode getRight() {
return right;
}
public void setRight(HeroNode right) {
this.right = right;
}
@Override
public String toString() {
return "HeroNode[no=" + no + ", name=" + name + "]";
}
}
二叉树
package DataStructures.Tree;
public class BinaryTreeDemo {
public static void main(String[] args) {
//先创建一颗二叉树
BinaryTree binaryTree = new BinaryTree();
//创先需要的节点
HeroNode root = new HeroNode(1, "宋江");
HeroNode node2 = new HeroNode(2, "吴用");
HeroNode node3 = new HeroNode(3, "卢俊义");
HeroNode node4 = new HeroNode(4, "林冲");
HeroNode node5 = new HeroNode(5, "关胜");
//说明:我们先手动创建二叉树,后面使用递归创建二叉树
root.setLeft(node2);
root.setRight(node3);
node3.setRight(node4);
node3.setLeft(node5);
binaryTree.setRoot(root);
/* //测试:
System.out.println("前序遍历");//1->2->3->5->4
binaryTree.preOrder();
//测试:
System.out.println("中序遍历");//2->1->5->3->4
binaryTree.infixOrder();
//测试:
System.out.println("后序遍历");//2->5->4->3->1
binaryTree.postOrder();
*/
/* //前序遍历查找
System.out.println("前序遍历方式~~~~");
HeroNode resNode_pre = binaryTree.preOrderSearch(5);
if (resNode_pre != null) {
System.out.printf("找到了,信息为no=%d name=%s\n", resNode_pre.getNo(), resNode_pre.getName());
} else {
System.out.printf("没有找到no = %d的英雄\n", 5);
}
//中序遍历查找——3次
System.out.println("中序遍历方式!!!!");
HeroNode resNode_infix = binaryTree.infixOrderSearch(5);
if (resNode_infix != null) {
System.out.printf("找到了,信息为no=%d name=%s\n", resNode_infix.getNo(), resNode_infix.getName());
} else {
System.out.printf("没有找到no = %d的英雄\n", 5);
}
//后序遍历查找——2次
System.out.println("后序遍历方式····");
HeroNode resNode_Post = binaryTree.postOrderSearch(5);
if (resNode_Post != null) {
System.out.printf("找到了,信息为no=%d name=%s\n", resNode_Post.getNo(), resNode_Post.getName());
} else {
System.out.printf("没有找到no = %d的英雄\n", 5);
}
*/
//测试删除
System.out.println("删除前,前序遍历");
binaryTree.preOrder();//1->2->3->5->4
//binaryTree.delNode(5);//删除子叶
binaryTree.delNode(3);//删除子树
System.out.println("删除后,前序遍历");
//binaryTree.preOrder();//1->2->3->4
binaryTree.preOrder();//1->2
}
}
//定义BinaryTree二叉树
class BinaryTree {
private HeroNode root;
public void setRoot(HeroNode root) {
this.root = root;
}
//删除节点
public void delNode(int no){
if (root!=null){
//需要判断root是否是要删除的节点
if (root.getNo()==no){
root=null;
}else {
//递归删除
root.delNode(no);
}
}else {
System.out.println("空树,不能删除");
}
}
//前序遍历
public void preOrder() {
if (this.root != null) {
this.root.preOrder();
} else {
System.out.println("二叉树为空,无法遍历");
}
}
//中序遍历
public void infixOrder() {
if (this.root != null) {
this.root.infixOrder();
} else {
System.out.println("二叉树为空,无法遍历");
}
}
//后序遍历
public void postOrder() {
if (this.root != null) {
this.root.postOrder();
} else {
System.out.println("二叉树为空,无法遍历");
}
}
//前序查找
public HeroNode preOrderSearch(int no) {
if (root != null) {
return root.preOrderSearch(no);
} else {
return null;
}
}
//中序查找
public HeroNode infixOrderSearch(int no) {
if (root != null) {
return root.infixOrderSearch(no);
} else {
return null;
}
}
//后序查找
public HeroNode postOrderSearch(int no) {
if (root != null) {
return root.postOrderSearch(no);
} else {
return null;
}
}
}
//先创建HeroNode结点
class HeroNode {
private int no;
private String name;
private HeroNode left;
private HeroNode right;
public HeroNode(int no, String name) {
this.no = no;
this.name = name;
}
public int getNo() {
return no;
}
public void setNo(int no) {
this.no = no;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public HeroNode getLeft() {
return left;
}
public void setLeft(HeroNode left) {
this.left = left;
}
public HeroNode getRight() {
return right;
}
public void setRight(HeroNode right) {
this.right = right;
}
@Override
public String toString() {
return "HeroNode[no=" + no + ", name=" + name + "]";
}
//递归删除节点
//1.如果删除叶子节点,就直接删除
//2.如果删除非叶子节点,就删除该子树
public void delNode(int no) {
//判断该节点的左右是否需要删除
if (this.left != null && this.left.no == no) {
this.left = null;
return;
}
if (this.right != null && this.right.no == no) {
this.right = null;
return;
}
//上两步没完成任务,开始递归寻找
if (this.left != null) {
this.left.delNode(no);
}
if (this.right != null) {
this.right.delNode(no);
}
}
// 1. 前序遍历
public void preOrder() {
//先输出父节点
System.out.println(this);
//向左子树前序遍历递归
if (this.left != null) {
this.left.preOrder();
}
//向右子树前序遍历递归
if (this.right != null) {
this.right.preOrder();
}
}
// 2. 中序遍历
public void infixOrder() {
//向左子树中序遍历递归
if (this.left != null) {
this.left.infixOrder();
}
System.out.println(this);
//向右子树中序遍历递归
if (this.right != null) {
this.right.infixOrder();
}
}
// 3. 后序遍历
public void postOrder() {
//向左子树后序遍历递归i
if (this.left != null) {
this.left.postOrder();
}
if (this.right != null) {
this.right.postOrder();
}
System.out.println(this);
}
// 1. 前序遍历查找
/**
* @param no 查找no
* @return 如果找到就返回该Node,如果没有找到就返回null
*/
public HeroNode preOrderSearch(int no) {
System.out.println("进入前序遍历查找~~~");
//自己
if (this.no == no) {
return this;
}
//向左
HeroNode resNode = null;
if (this.left != null) {
resNode = this.left.preOrderSearch(no);
}
if (resNode != null) {
return resNode;
}
//向右
if (this.right != null) {
resNode = this.right.preOrderSearch(no);
}
return resNode;
}
// 2. 中序遍历查找
public HeroNode infixOrderSearch(int no) {
//向左
HeroNode resNode = null;
if (this.left != null) {
resNode = this.left.infixOrderSearch(no);
}
if (resNode != null) {
return resNode;
}
//自己
System.out.println("进入中序查找");
if (this.no == no) {
return this;
}
//向右
if (this.right != null) {
resNode = this.right.infixOrderSearch(no);
}
return resNode;
}
// 3. 后序遍历查找
public HeroNode postOrderSearch(int no) {
HeroNode resNode = null;
//向左
if (this.left != null) {
resNode = this.left.postOrderSearch(no);
}
if (resNode != null) {
return resNode;
}
//向右
if (this.right != null) {
resNode = this.right.postOrderSearch(no);
}
if (resNode != null) {
return resNode;
}
//自己
System.out.println("后序遍历");
if (this.no == no) {
return this;
}
return resNode;
}
}
顺序树
package Tree;
/**
* 顺序存储二叉树
* 数组是顺序存储的,但是用二叉树的前序遍历完成对数组元素的遍历
* 其实就相当于用数组来存储二叉树的数据
*/
public class ArrBinaryTreeDemo {
public static void main(String[] args) {
int[] arr = {1, 2, 3, 4, 5, 6, 7};
ArrBinaryTree arrBinaryTree = new ArrBinaryTree(arr);
arrBinaryTree.preOrder();
}
}
class ArrBinaryTree {
private int[] arr;//用来存储节点的数组
public ArrBinaryTree(int[] arr) {
this.arr = arr;
}
//方法重载
public void preOrder() {
this.preOrder(0);
}
//前序遍历
public void preOrder(int n) {
//判断传入的数组是不是空
if (arr.length == 0 || arr == null) {
System.out.println("数组为空,无法遍历");
return;
}
System.out.println(arr[n]);//输入当前节点
//向左前序遍历
if ((2 * n + 1) < arr.length) {
preOrder(2 * n + 1);
}
//向右进行前序遍历
if ((2 * n + 2) < arr.length) {
preOrder(2 * n + 2);
}
}
}
哈希表
package hashcode;
import java.util.Scanner;
public class hashcode {
public static void main(String[] args) {
//创建哈希表
HashTable hashTable = new HashTable(7);
//写一个简单的菜单
String key = "";
Scanner scanner = new Scanner(System.in);
while (true){
System.out.println("add:添加雇员");
System.out.println("list:显示雇员");
System.out.println("find:查找雇员");
System.out.println("exit:退出系统");
System.out.println("del:删除雇员");
key = scanner.next();
switch (key){
case "add":
System.out.println("输入id");
int id = scanner.nextInt();
System.out.println("输入名字");
String name = scanner.next();
Emp emp = new Emp(id,name);
hashTable.add(emp);
break;
case "list":
hashTable.list();
break;
case "find":
System.out.println("请输入要查找的id");
id = scanner.nextInt();
hashTable.findEmpById(id);
break;
case "del":
System.out.println("请输入要删除的id");
id = scanner.nextInt();
hashTable.delEmp(id);
break;
case "exit":
scanner.close();
System.exit(0);
break;
default:
break;
}
}
}
}
//创建HashTable 管理多条链表
class HashTable {
private EmpLinkedList empLinkedListArray[];
private int size;
//构造器
public HashTable(int size) {
this.size = size;
empLinkedListArray = new EmpLinkedList[size];
//留坑=> 这时不要忘记分别初始化每一个链表
for (int i=0;i<size;i++){
empLinkedListArray[i] = new EmpLinkedList();
}
}
//添加雇员
public void add(Emp emp) {
//根据员工的id得到该员工应该添加到哪条链表
//所以要去编写一个散列函数↓
int empLinkedListNO = hashFun(emp.id);
//将emp添加到对应的链表中
empLinkedListArray[empLinkedListNO].add(emp);
}
//遍历所有的链表,遍历hashtabl
public void list() {
for (int i = 0; i < size; i++) {
empLinkedListArray[i].list(i);
}
}
//根据输入的id查找雇员
public void findEmpById(int id){
//使用散列函数确定哪条链表查找
int empLinkedListNo = hashFun(id);
Emp emp = empLinkedListArray[empLinkedListNo].findEmpById(id);
if(emp!=null){
//找到
System.out.printf("在第%d条链表中找到雇员 id = %d\n",(empLinkedListNo+1),id);
}else {
System.out.println("在哈希表中没有找到该雇员");
}
}
//删除输入的id对应的雇员信息
public void delEmp(int id){
int empLinkedListNo = hashFun(id);
empLinkedListArray[empLinkedListNo].del(id);
}
//编写散列函数,使用一个简单的取模法
public int hashFun(int id) {
return id % size;
}
}
//表示一个雇员
class Emp {
public int id;
public String name;
public Emp next;
public Emp(int id, String name) {
super();
this.id = id;
this.name = name;
}
}
//创建一个EmpLinkedList,表示链表
class EmpLinkedList {
//头指针,执行第一个Emp,因此我们这个链表的head是直接指向第一个Emp
private Emp head;//默认为null
//添加雇员到链表
//说明
//1.假定添加雇员就是链表最后
public void add(Emp emp) {
//如果是添加第一个雇员
if (head == null) {
head = emp;
return;
}
//出现了!链表必出现的辅助指针
Emp curEmp = head;
while (true) {
if (curEmp.next == null) {
break;
}
curEmp = curEmp.next;
}
//退出时,直接将emp加入链表
curEmp.next = emp;
}
//遍历链表的雇员信息
public void list(int no) {
if (head == null) {
System.out.println("第"+(no+1)+"条链表为空");
return;
}
System.out.println("第"+(no+1)+"条链表信息为:");
Emp curEmp = head;
while (true) {
System.out.printf(" =>id=%d name=%s\t", curEmp.id, curEmp.name);
if (curEmp.next == null) {
break;
}
curEmp = curEmp.next;
}
System.out.println();
}
//根据id查找雇员
//如果查找到就返回Emp,如果没有就返回null
public Emp findEmpById(int id){
//判断链表是否为空
if(head==null){
System.out.println("链表空");
return null;
}
Emp curEmp = head;
while (true){
if(curEmp.id==id){
break;
}
//退出条件
if (curEmp.next==null){
//no find
curEmp = null;
break;
}
curEmp = curEmp.next;
}
return curEmp;
}
public void del(int id){
if(head==null){
System.out.println("链表为空,无法删除");
return;
}
if (head.id==id){
if(head.next==null){
head=null;
}else {
head = head.next;
}
return;
}
Emp curEmp = head;
boolean flag = false;
while (true){
if(curEmp.next==null){
break;
}
if (curEmp.next.id==id){
flag=true;
break;
}
curEmp = curEmp.next;
}
if (flag){
curEmp.next = curEmp.next.next;
}else {
System.out.println("要删除的节点不存在");
}
}
}
赫夫曼树
package tree;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class hafumantree {
public static void main(String[] args) {
int arr[] = {13, 7, 8, 3, 29, 6, 1};
Node root = createHuffmanTree(arr);
preOreder(root);//67->29->38->15->7->8->……->13
}
//前序遍历的方法
public static void preOreder(Node root) {
if (root != null) {
root.preOrder();
} else {
System.out.println("空树");
}
}
//创建赫夫曼树
public static Node createHuffmanTree(int arr[]) {
//一:遍历arr数组
//二:将arr的没有元素构建成一个NOde
//三:将Node放入到ArrayList中
List<Node> nodes = new ArrayList<Node>();
for (int value : arr) {
nodes.add(new Node(value));
}
while (nodes.size() > 1) {
//排序,从小到大
Collections.sort(nodes);
System.out.println("nodes" + nodes);
//(1)取出权值最小的两个二叉树
Node leftNode = nodes.get(0);
//(2)取出权值第二最小的两个二叉树
Node rightNode = nodes.get(1);
//(3)构建一颗新的二叉树
Node parent = new Node(leftNode.value + rightNode.value);
parent.left = leftNode;
parent.right = rightNode;
//(4)从ArrayList中删除处理过的二叉树
nodes.remove(leftNode);
nodes.remove(rightNode);
//(5)加入新的节点
nodes.add(parent);
}
//返回赫夫曼树的root节点
return nodes.get(0);
}
}
//节点
//为了让Node支持排序Collection
//让NOde实现Comparable借口
class Node implements Comparable<Node> {
int value;//权
Node left;
Node right;
//前序遍历,用来测试的
public void preOrder() {
System.out.println(this);
if (this.left != null) {
this.left.preOrder();
}
if (this.right != null) {
this.right.preOrder();
}
}
public Node(int value) {
this.value = value;
}
@Override
public String toString() {
return "Node{" + "value=" + value + '}';
}
@Override
public int compareTo(Node o) {
//表示从小到大排序
return this.value - o.value;
}
}
赫夫曼编码
package tree;
import java.util.*;
public class hafumantreecode {
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<HuffmanNode> nodes = getHuffmanNodes(contentBytes);
System.out.println("nodes" + nodes);
//测试创建二叉树
System.out.println("赫夫曼树");
HuffmanNode HuffmanTreeRoot = creatHuffmanTree(nodes);
System.out.println("前序遍历");
HuffmanTreeRoot.preOreder();
//测试一把是否赫夫曼编码
//getCodes(HuffmanTreeRoot,"",stringBuilder);
Map<Byte,String> huffmanCodes = getCodes(HuffmanTreeRoot);
System.out.println("~生成的赫夫曼编码表\n"+huffmanCodes);
}
//生成赫夫曼树对应的赫夫曼编码里
//思路
//(1)将赫夫曼编码表放在Map<Byte,String>
// 这个Map就跟Python的字典一样,{[key:value] [key:value]……}
static Map<Byte,String> huffmanCodes = new HashMap<Byte, String>();
//(2)在生成赫夫曼编码表时,需要去拼接路径,所以定义一个StringBuilder 存储某个叶子节点的路径
static StringBuilder stringBuilder = new StringBuilder();
//Ps:为了调用方便,我们重载一下getCodes
private static Map<Byte,String> getCodes(HuffmanNode root){
if (root==null){
return null;
}
//处理root的左子树
getCodes(root.left,"0",stringBuilder);
//处理root的右子树
getCodes(root.right,"1",stringBuilder);
return huffmanCodes;
}
/**
* //(3)方法
* 功能:得到传入的node节点的所有赫夫曼编码,并存放到huffmanCodes集合中
* @param node 传入的节点(默认从root)
* @param code 该节点的路径(左节点0和右节点1)
* @param stringBuilder 拼接路径
*/
private static void getCodes(HuffmanNode node,String code,StringBuilder stringBuilder){
StringBuilder stringBuilder2 = new StringBuilder(stringBuilder);
//将传入的code加入到StringBuile2中
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());
}
}
}
/**
* @param bytes 接受的数组
* @return 返回的就是List形式:[Node[date=97,weight =5],Node[date=32,weight=9]]……
*/
private static List<HuffmanNode> getHuffmanNodes(byte bytes[]) {
//1.创建ArrayList
ArrayList<HuffmanNode> nodes = new ArrayList<HuffmanNode>();
//2.遍历bytes,统计每个byte出现的次数->map
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);
}
}
//把每个键值对转成HuffmanNode对象,并加入nodes中
//遍历Map
for (Map.Entry<Byte, Integer> entry : counts.entrySet()) {
nodes.add(new HuffmanNode(entry.getKey(), entry.getValue()));
}
return nodes;
}
//通过上面的List,常见对应的赫夫曼树
private static HuffmanNode creatHuffmanTree(List<HuffmanNode> nodes) {
while (nodes.size() > 1) {
Collections.sort(nodes);
HuffmanNode leftNode = nodes.get(0);
HuffmanNode rightNode = nodes.get(1);
//没有data只有权值weight
HuffmanNode parent = new HuffmanNode(null, leftNode.weight + rightNode.weight);
parent.left = leftNode;
parent.right = rightNode;
nodes.remove(leftNode);
nodes.remove(rightNode);
nodes.add(parent);
}
return nodes.get(0);
}
//前序遍历
private static void preOreder(HuffmanNode root) {
if (root != null) {
root.preOreder();
} else {
System.out.println("空树");
}
}
}
//创建Node
class HuffmanNode implements Comparable<HuffmanNode> {
Byte data;//存放数据本身'a'->97 ' '->32
int weight;//权值,字符出现的次数
HuffmanNode left;
HuffmanNode right;
public HuffmanNode(Byte data, int weight) {
this.data = data;
this.weight = weight;
}
@Override//表示从小到达排序
public int compareTo(HuffmanNode huffmanNode) {
return this.weight - huffmanNode.weight;
}
@Override
public String toString() {
return "HuffmanNode{" + "data=" + data + ", weight=" + weight + '}';
}
public void preOreder() {
System.out.println(this);
if (this.left != null) {
this.left.preOreder();
}
if (this.right != null) {
this.right.preOreder();
}
}
}
赫夫曼解码
package tree;
import java.util.*;
import javax.print.DocFlavor;
public class hafuman {
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));
System.out.println("压缩后的长度是:" + huffmanCodesBytes.length);
//分步过程
/*
//测试
List<HuffmanNode> nodes = getHuffmanNodes(contentBytes);
System.out.println("nodes" + nodes);
//测试创建赫夫曼树
System.out.println("赫夫曼树");
HuffmanNode HuffmanTreeRoot = creatHuffmanTree(nodes);
System.out.println("前序遍历");
HuffmanTreeRoot.preOreder();
//测试一把是否赫夫曼编码
//getCodes(HuffmanTreeRoot,"",stringBuilder);
Map<Byte, String> huffmanCodes = getCodes(HuffmanTreeRoot);
System.out.println("~生成的赫夫曼编码表\n" + huffmanCodes);
//测试
byte[] huffmanCodeBytes = zip(contentBytes, huffmanCodes);
System.out.println("huffmanCodeBytes"+Arrays.toString(huffmanCodeBytes)); // 成功被压缩了
//发送huffmanCodeBytes数组
*/
//解码过程
//测试一下
byte sourceBytes[] = decode(huffmanCodes,huffmanCodesBytes);
System.out.println("原来的字符串"+new String(sourceBytes));
}
/*完成数据的解压*/
/*思路
* 1. 将huffmanCodeBytes [-88, -65, -56, -65, -56, -65, -55, 77, -57, 6, -24, -14, -117, -4, -60, -90, 28]
* 重新转成由赫夫曼编码组成的二进制字符串
* 2. 将赫夫曼编码对应的字符串,对应着赫夫曼编码组成原始的字符串*/
/**
* 第二步,将第一步返回的字符串转化成原始的字符串
*
* @param huffmanCodes 赫夫曼编码表,记录了每个字母对应的补码
* @param huffmanBytes 赫夫曼编码得到的字节数组
* @return 原始字符串
*/
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(byteToBiteString(!flag,b));
}
// System.out.println("赫夫曼字节数组对应的二进制字符串"+ stringBuilder.toString());
// 把字符串按照指定的赫夫曼编码表及逆行解码
// 把赫夫曼编码表进行调换,因为反向查询 a(97) -> 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());
}
//System.out.println("map"+map);
//创建一个集合,存放byte
List<Byte> list = new ArrayList<>();
for (int i = 0; i<stringBuilder.length(); ){
int count = 1;
boolean flag = true;
Byte b = null;
while (flag){
//取出一段字符,进行匹配
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;
}
/**
* 第一步:将byte转成二进制的字符串
* 可以参考java韩顺平二进制的分析
*
* @param b
* @param flag 标识是否需要补高位,如果是true表示需要补高位,如果是false表示不补,如果是最后一个字节就不用补高位
* @return 是这个b对应的二进制的字符串,注意是按照补码返回,记得编码的时候也是按照补码的方式编码的
*/
private static String byteToBiteString(boolean flag, byte b) {
//Byte方法中没有转成二进制的方法,所以使用Integer
//因此需要一个变量保存b
int temp = b; // 将b转成int
//如果是正数,我们还存在补高位的问题
if (flag) {
temp |= 256; //按位与,256-> 1 0000 0000 | 0000 0001 =》1 0000 0001
}
String str = Integer.toBinaryString(temp);
if (flag) {
// str是int类型的补码,补码很长不需要那么长,所以需要截取str.substring
return str.substring(str.length() - 8);
} else {
return str;
}
}
//写一个方法,将前面的方法封装起来,便于我们的调用
/**
* @param bytes 原始的字符串对应的字节数组
* @return 是经过赫夫曼编码处理后的字节数组,也就是压缩后的数组
*/
private static byte[] huffmanZip(byte[] bytes) {
// 1.数组转列表
List<HuffmanNode> nodes = getHuffmanNodes(bytes);
// 2. 根据nodes创建赫夫曼树
HuffmanNode HuffmanTreeRoot = creatHuffmanTree(nodes);
// 3. 根据赫夫曼树创建生成对应的赫夫曼编码
Map<Byte, String> huffmanCodes = getCodes(HuffmanTreeRoot);
// 4. 根据生成的赫夫曼编码生成压缩后的赫夫曼编码字节数组
byte[] huffmanCodeBytes = zip(bytes, huffmanCodes);
// 5. 将压缩后的字节进行返回
return huffmanCodeBytes;
}
//编写一个方法,将一个字符串对应的byte[]数组,通过赫夫曼编码转码
/**
* @param bytes 原始的字符串对应的byte[]
* @param huffmanCodes 生成的赫夫曼编码Map
* @return 返回赫夫曼编码处理后的byte数组
* 举例: String content = "i like like like java do you like a java";=》 byte contentBytes[] = content.getBytes();
* 返回的是:各个字符对应的编码组成的字符串,然后八位一组变成字符放到byte[] huffmanCodeBytes中
*/
private static byte[] zip(byte[] bytes, Map<Byte, String> huffmanCodes) {
//1. 先利用赫夫曼编码表将传进来的bytes[]转成赫夫曼编码组成的字符串
StringBuilder stringBuilder = new StringBuilder();
//遍历byte数组
for (byte b : bytes) {
stringBuilder.append(huffmanCodes.get(b));
}
//System.out.println("测试stringBuilder = "+stringBuilder.toString());
//将”10101000101。。。"字符串转成byte[]
//统计返回byte[]huffmanCode长度
//以下内容一句话搞定 int len = (stringBuilder.leng()+7)/8
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;//记录是第几个byte
for (int i = 0; i < stringBuilder.length(); i += 8) {
//每8位对应一个byte,所以步长是+8
String strByte;
if (i + 8 > stringBuilder.length()) {
//不够八位
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>
// 这个Map就跟Python的字典一样,{[key:value] [key:value]……}
static Map<Byte, String> huffmanCodes = new HashMap<Byte, String>();
//(2)在生成赫夫曼编码表时,需要去拼接路径,所以定义一个StringBuilder 存储某个叶子节点的路径
static StringBuilder stringBuilder = new StringBuilder();
//Ps:为了调用方便,我们重载一下getCodes
private static Map<Byte, String> getCodes(HuffmanNode root) {
if (root == null) {
return null;
}
//处理root的左子树
getCodes(root.left, "0", stringBuilder);
//处理root的右子树
getCodes(root.right, "1", stringBuilder);
return huffmanCodes;
}
/**
* //(3)方法
* 功能:得到传入的node节点的所有赫夫曼编码,并存放到huffmanCodes集合中
*
* @param node 传入的节点(默认从root)
* @param code 该节点的路径(左节点0和右节点1)
* @param stringBuilder 拼接路径
*/
private static void getCodes(HuffmanNode node, String code, StringBuilder stringBuilder) {
StringBuilder stringBuilder2 = new StringBuilder(stringBuilder);
//将传入的code加入到StringBuile2中
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());
}
}
}
/**
* @param bytes 接受的数组
* @return 返回的就是List形式:[Node[date=97,weight =5],Node[date=32,weight=9]]……
*/
private static List<HuffmanNode> getHuffmanNodes(byte bytes[]) {
//1.创建ArrayList
ArrayList<HuffmanNode> nodes = new ArrayList<HuffmanNode>();
//2.遍历bytes,统计每个byte出现的次数->map
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);
}
}
//把每个键值对转成HuffmanNode对象,并加入nodes中
//遍历Map
for (Map.Entry<Byte, Integer> entry : counts.entrySet()) {
nodes.add(new HuffmanNode(entry.getKey(), entry.getValue()));
}
return nodes;
}
//通过上面的List,常见对应的赫夫曼树
private static HuffmanNode creatHuffmanTree(List<HuffmanNode> nodes) {
while (nodes.size() > 1) {
Collections.sort(nodes);
HuffmanNode leftNode = nodes.get(0);
HuffmanNode rightNode = nodes.get(1);
//没有data只有权值weight
HuffmanNode parent = new HuffmanNode(null, leftNode.weight + rightNode.weight);
parent.left = leftNode;
parent.right = rightNode;
nodes.remove(leftNode);
nodes.remove(rightNode);
nodes.add(parent);
}
return nodes.get(0);
}
//前序遍历
private static void preOreder(HuffmanNode root) {
if (root != null) {
root.preOreder();
} else {
System.out.println("空树");
}
}
}
//创建Node
class HuffmanNode implements Comparable<HuffmanNode> {
Byte data;//存放数据本身'a'->97 ' '->32
int weight;//权值,字符出现的次数
HuffmanNode left;
HuffmanNode right;
public HuffmanNode(Byte data, int weight) {
this.data = data;
this.weight = weight;
}
@Override//表示从小到达排序
public int compareTo(HuffmanNode huffmanNode) {
return this.weight - huffmanNode.weight;
}
@Override
public String toString() {
return "HuffmanNode{" + "data=" + data + ", weight=" + weight + '}';
}
public void preOreder() {
System.out.println(this);
if (this.left != null) {
this.left.preOreder();
}
if (this.right != null) {
this.right.preOreder();
}
}
}
赫夫曼解码压缩
package tree;
import java.io.*;
import java.util.*;
/**
* Huffman编码以及文件解压缩
*/
//定义结点
class Node implements Comparable<Node>{
Byte data;//数据
int weight;//权值
Node leftNode;
Node rightNode;
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 o) {
return this.weight - o.weight;
}
//前序遍历
public void preOrder(){
System.out.println(this);
if (this.leftNode != null){
this.leftNode.preOrder();
}
if (this.rightNode != null){
this.rightNode.preOrder();
}
}
}
public class HuffmanCode {
/**
* 统计字符个数并存入到nodes列表
* @param bytes 字节接收数组
* @return 结点的List形式
*/
public static List<Node> getNodes(byte[] bytes){
//创建一个ArrayList
ArrayList<Node> nodes = new ArrayList<>();
//遍历bytes,统计每一个byte出现的次数
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);
}
}
//遍历Map,把每一个键值对转换成一个Node并加入到nodes集合
for (Map.Entry<Byte,Integer> entry: counts.entrySet()){
nodes.add(new Node(entry.getKey(),entry.getValue()));
}
return nodes;
}
/**
* 生成Huffman树
* @param nodes 结点的List形式
* @return Huffman树
*/
public static Node createHuffmanTree(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.leftNode = left;
parent.rightNode = right;
//从列表删除已使用的结点
nodes.remove(left);
nodes.remove(right);
//将新节点加入列表
nodes.add(parent);
}
return nodes.get(0);
}
/**
* 前序遍历
* @param root Huffman树根节点
*/
public static void preOrder(Node root){
if (root != null){
root.preOrder();
}else {
System.out.println("空树");
}
}
//将Huffman编码存放在Map<Byte,String>形式
static Map<Byte,String> huffmanCodes = new HashMap<Byte,String>();
//定义一个StringBuilder存储某个叶子结点
static StringBuilder stringBuilder = new StringBuilder();
//重载getCodes
public static Map<Byte,String> getCodes(Node root){
if (root == null){
return null;
}
//处理root的左子树
getCodes(root.leftNode,"0",stringBuilder);
//处理root的右子树
getCodes(root.rightNode,"1",stringBuilder);
return huffmanCodes;
}
/**
* 计算node的所有叶子结点的Huffman编码,并放入到huffmanCodes
* @param node 传入的结点
* @param code 左边为0,右边为1
* @param stringBuilder 用于拼接路径
*/
public static void getCodes(Node node,String code,StringBuilder stringBuilder){
StringBuilder inStringBuilder = new StringBuilder(stringBuilder);
//将code加入到inStringBuilder
inStringBuilder.append(code);
if (node != null) {//如果当前结点为空不处理
if (node.data == null){//当前结点为非叶子结点
//向左递归
getCodes(node.leftNode,"0",inStringBuilder);
//向右递归
getCodes(node.rightNode,"1",inStringBuilder);
}else {//当前结点为叶子结点
huffmanCodes.put(node.data,inStringBuilder.toString());
}
}
}
/**
* 将字符串对应的byte[]传入,返回一个经过生成的Huffman编码压缩后的byte[]
* @param bytes 原始的字符串生成的byte[]
* @param huffmanCodes 生成的编码
* @return 压缩之后的byte[]
*/
public static byte[] zip(byte[] bytes,Map<Byte,String> huffmanCodes){
//1.利用huffmanCodes将bytes转成huffman对应的字符串 zipHuffmanCodeBytes
StringBuilder stringBuilder = new StringBuilder();
//遍历bytes数组
for (byte b: bytes){
stringBuilder.append(huffmanCodes.get(b));
}
//2.统计返回的huffmanCodeBytes长度
int len;
if (stringBuilder.length() % 8 == 0) {
len = stringBuilder.length() /8;
}else {
len = stringBuilder.length() /8 + 1 ;
}
//3.创建存储压缩后的新Byte数组
byte[] zipHuffmanCodeBytes = new byte[len];
int index = 0;//记录是第几个byte
for (int i = 0; i < stringBuilder.length(); i += 8) {//每8位对应一个byte,步长为8
String strByte;
if (i+8 > stringBuilder.length()) {//不够8位
strByte = stringBuilder.substring(i);
}else {
strByte = stringBuilder.substring(i,i+8);
}
//将strByte转换成一个byte,放入到新的byte数组中
zipHuffmanCodeBytes[index] = (byte) Integer.parseInt(strByte,2);
index++;
}
return zipHuffmanCodeBytes;
}
/**
* 将前面的方法封装起来
* @param bytes 原始的字节数组
* @return 压缩之后的编码数组
*/
public static byte[] huffmanZip(byte[] bytes){
List<Node> nodes = getNodes(bytes);//获得Node类型的列表
Node huffmanTreeRoot = createHuffmanTree(nodes);//创建Huffman树,获得huffman树的根节点
Map<Byte, String> huffmanCodes = getCodes(huffmanTreeRoot);//获得Huffman编码
return zip(bytes, huffmanCodes);//压缩
}
/**
* 将一个byte转成对应的二进制字符串
* @param flag 是否要补位的标志 true为需要补高位,false不用,最后一个字节不用补高位
* @param b 传入的byte
* @return byte对应的二进制字符串
*/
public static String byteToBitString(boolean flag,byte b){
int temp = b;//使用变量保存byte并转换成int类型
//如果是正数我们还需要补高位
if (flag){
temp |= 256;//按位与256
}
String str = Integer.toBinaryString(temp);//返回的是temp的二进制补码
if (flag){
return str.substring(str.length()-8);//截取后八位
}else {
return str;
}
}
/**
* 完成对压缩数据的解码
* @param huffmanCodes Huffman编码表
* @param zipHuffmanBytes 压缩完成的byte数组
* @return 未压缩的byte数组
*/
public static byte[] decode(Map<Byte,String> huffmanCodes, byte[] zipHuffmanBytes){
//1、先得到对应的二进制编码
StringBuilder stringBuilder = new StringBuilder();
//将byte数组转换成二进制字符串
for (int i = 0; i < zipHuffmanBytes.length; i++) {
byte b = zipHuffmanBytes[i];
//判断是不是最后一个字节
boolean flag = (i == zipHuffmanBytes.length - 1);
stringBuilder.append(byteToBitString(!flag,b));
}
//2、根据Huffman编码进行反编译
//调换Huffman表
Map<String,Byte> map = new HashMap<String, Byte>();
for (Map.Entry<Byte,String> entry : huffmanCodes.entrySet()){
map.put(entry.getValue(),entry.getKey());
}
//3.创建要给的集合,存放byte
ArrayList<Byte> list = new ArrayList<>();
//扫描stringBuilder提取字符
for (int i = 0; i < stringBuilder.length();) {
int count = 1;//小的计数器
boolean flag = true;
Byte b = null;
while (flag){
//count递增取出key
String key = stringBuilder.substring(i, i + count);//i不动,count递增,直到匹配到一个字符
b = map.get(key);//回到Huffman表里面查找
if (b == null){//没找打
count++;
}else {
flag = false;
}
}
list.add(b);//加入到集合
i += count;//i直接跳到count
}
//4.把list里面的内容添加到byte数组中返回
byte[] b = new byte[list.size()];
for (int i = 0; i < b.length; i++) {
b[i] = list.get(i);
}
return b;
}
/**
* 将一个文件进行压缩
* @param srcFile 目标文件路径
* @param dstFile 输出文件路径
*/
public static void zipFile(String srcFile,String dstFile){
//创建文件的输入流
FileInputStream is = null;
//创建文件的输出流和对象输出流
FileOutputStream os = null;
ObjectOutputStream oos = null;
try{
is = new FileInputStream(srcFile);
//创建与源文件大小一样的byte[]
byte[] b = new byte[is.available()];
//读取文件
is.read(b);
//压缩
byte[] huffmanZip = huffmanZip(b);
//输出流存放文件
os = new FileOutputStream(dstFile);
oos = new ObjectOutputStream(os);
//把压缩后的字节数组写入文件
oos.writeObject(huffmanZip);
//以对象流的方式写入Huffman编码
oos.writeObject(huffmanCodes);
} catch (IOException e) {
System.out.println(e.getMessage());
}finally {
try {
is.close();
os.close();
oos.close();
} catch (IOException e) {
System.out.println(e.getMessage());
}
}
}
/**
* 将一个文件进行解压
* @param srcFile 目标文件路径
* @param dstFile 输出文件路径
*/
public static void unZipFile(String srcFile, String dstFile){
//创建文件输入流
InputStream is = null;
//创建文件输出流
OutputStream os = null;
//创建文件输入流对象
ObjectInputStream ois = null;
try {
is = new FileInputStream(srcFile);
ois = new ObjectInputStream(is);
//读取压缩后的字节数组
byte[] zipHuffmanBytes = (byte[]) ois.readObject();
//读取Huffman表
Map<Byte,String> huffmanCodes = (Map<Byte, String>)ois.readObject();
//解码
byte[] result = decode(huffmanCodes, zipHuffmanBytes);
//将结果写入文件
os = new FileOutputStream(dstFile);
os.write(result);
} catch (Exception e) {
System.out.println(e.getMessage());
}finally {
try {
os.close();
ois.close();
is.close();
} catch (IOException e) {
System.out.println(e.getMessage());
}
}
}
public static void main(String[] args) {
/*String content = "i like like like java do you like a java";
System.out.println("原始数据: " + content);
byte[] huffmanZip = huffmanZip(content.getBytes());
System.out.println("压缩之后的数据: " + Arrays.toString(huffmanZip));
byte[] decodeHuffman = decode(huffmanCodes, huffmanZip);
System.out.println("解压之后的数据: "+ new String(decodeHuffman));*/
//测试压缩文件
//String srcFile = "C://Users//liuchang//Desktop//src.bmp";
//String dstFile = "D://dst.zip";
//zipFile(srcFile,dstFile);
//System.out.println("成功");
//测试解压文件
String srcFile = "D://dst.zip";
String dstFile = "D://dst2.bmp";
unZipFile(srcFile,dstFile);
System.out.println("成功");
}
}