赫夫曼编码
1.给定一个字符串统计其中的字符的出现的次数,返回一个list<node>集合 node类有askll码和他的权值(出现次数)
2.然后根据这个list<node>集合,构造一个哈夫曼树.
3.得到赫夫曼编码
package day20;
import java.nio.charset.StandardCharsets;
import java.util.*;
public class huffManCode {
// 赫夫曼编码
// 给定一个字符串统计其中的字符的出现的次数,返回一个list<node>集合 node类有askll码和他的权值(出现次数)
// 然后根据这个list<node>集合,构造一个哈夫曼树.
// 得到赫夫曼编码
public static void main(String[] args) {
String str =" i like like java do you like java";
byte[] strBytes = str.getBytes(); //这个集合包含了字符串的所有字符的askll码
// System.out.println(str.length()); //34
System.out.println(Arrays.toString(strBytes));
List<Node> nodes = getNode(strBytes);
System.out.println(nodes);
// 测试
System.out.println("赫夫曼树");
Node huffmantreeroot = createhuffmantree(nodes);
System.out.println("前序遍历");
huffmantreeroot.preorder();
//测试是否生成哈夫曼编码
Map<Byte, String> codes = getCodes(huffmantreeroot);
System.out.println("生成的哈夫曼编码表为"+codes);
}
//生成赫夫曼树对应的赫夫曼编码
// 思路:
// 1、将赫夫曼编码表存放在map中,Map<Byte,String>
// 32-01 97-100 100-11000
static Map<Byte,String> huffmancode =new HashMap<Byte,String>();
//2.遍历赫夫曼编码表需要拼接路径,定义一个stringbuilder存储某个叶子节点的路径。
static StringBuilder stringBuilder =new StringBuilder();
// stringBulider为一个可变字符串容器。
// 为了调用方便
private static Map<Byte,String> getCodes(Node root){
if (root==null ){
return null;
}
// 处理root的右子树
getCodes(root.left,"0",stringBuilder);
getCodes(root.right,"1",stringBuilder);
return huffmancode;
}
/**
* 将传入的node节点的所有叶子节点的赫夫曼编码得到,存放到haffmancode集合
* @param node 传入节点
* @param code 路径 左子节点为0,右子节点为1.
* @param stringBuilder 是用于拼接路径
*/
private static void getCodes(Node node,String code,StringBuilder stringBuilder){
StringBuilder stringBuilder2 = new StringBuilder(stringBuilder);
// 将code加入stirngbulider2
stringBuilder2.append(code);
if (node!=null){
// 判断当前node是否叶子节点
if (node.data==null){
// 非叶子节点
// 递归左
getCodes(node.left,"0",stringBuilder2);
// 向右递归
getCodes(node.right,"1",stringBuilder2);
}else {
// node不为空 ,说明为叶子节点
// 就表示找到了叶子节点的最后
huffmancode.put(node.data,stringBuilder2.toString());
}
}
}
//前序遍历
public static void preorder(Node root){
if (root!=null){
root.preorder();
}else {
System.out.println("为空");
}
}
//此方法获得了string的字符和所对应的次数。
private static List<Node> getNode(byte[] bytes){ // bytes传入的为所有字符的askll码集合
// 创建arrlist
ArrayList<Node> nodes = new ArrayList<>();
// 遍历bytes, 统计每个byte出现的次数 map[key是他的askll码的值 ,value是他出现的次数];
Map<Byte, Integer> counts = new HashMap<>();
for (byte b:bytes){ //遍历bytes ,以b为单位
Integer count =counts.get(b);//
// map的get方法:通过key获得value.
if (count ==null){ //说明b第一次找到
counts.put(b,1); //将他设为1
}else {
counts.put(b,count+1); //给他加1
}
}
// for结束map就已经完成了统计
// 把每一个键值对转为node
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 parentnode =new Node(null, leftnode.weight+ rightnode.weight);
parentnode.left=leftnode;
parentnode.right=rightnode;
// 将处理过的两颗二叉树移除
nodes.remove(leftnode);
nodes.remove(rightnode);
// 加入新的二叉树
nodes.add(parentnode);
}
// 最后返回的为跟节点
return nodes.get(0);
}
}
class Node implements Comparable<Node>{
Byte data;//存放字符本身 ‘a’=97;
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;
}
@Override
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();
}
}
}