1.实现哈夫曼树
数的带权路径:所有的叶子结点的带权路径之后,记为WPL,权值越大的结点离根结点越近的二叉树才是最优二叉树。WPL最小的就是哈夫曼树
需要注意的点:1.对于集合中数据进行排序,使用Collections.sort()方法进行排序。
2.对于前序遍历的书写。
思路:
(1) byte[] contentBytes = content.getBytes() 得到“I like like like java do you like a java ”对应的byte[]数组.
(2) class Node implements Comparable<Node》 创建相应的结点:Node{data{存放数据},weight(权值),left和right}
(3) List<Node》 getNodes(byte[] bytes) 将byte数组传入,Node节点放到list,形式[Node[data=97,weight=5]],[Node[data=32,weight=9]],。。。。。注意:这里统计次数的方法,还有将键值对转换成Node对象,并将每个Node结点都放入到List集合中去。
(4)private static Node createHuffmanTree(List<Node nodes 编写一个方法,构建哈夫曼树。
* @Author Mr.Wu
* @Date 2019/7/22 10:40
* @Version 1.0
**/
public class HuffmanCode {
public static void main(String[] args) {
String content="i like like like java do you like a java";
byte[] contentBytes = content.getBytes();
List<Node> nodes = getNodes(contentBytes);
System.out.println("nodes"+nodes);
//测试一把,创建的二叉树
System.out.println("哈夫曼树");
Node huffmanTreeRoot = createHuffmanTree(nodes);
System.out.println("前序遍历");
huffmanTreeRoot.preOrder();
//测试一把是否生成了对应的哈夫曼编码
Map<Byte, String> huffmanCodes = getCodes(huffmanTreeRoot);
System.out.println("生成的哈夫曼编码表"+huffmanCodes);
/**
*
* @param bytes 接收字节数组
*
* @return ,将准备构建哈夫曼树的Node节点放到list,形式[Node[data=97,weight=5]],
* [Node[data=32,weight=9]]
*/
private static List<Node> getNodes(byte[] bytes){
//1.创建一个ArrayList
ArrayList<Node> nodes = new ArrayList<>();
//遍历bytes,统计每一个byte出现的次数->map[key,value]
HashMap<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);
}
}
//把每一个键值对转换成一个Node对象,并加入到nodes集合
//遍历map
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);
Node parent = new Node(null ,leftNode.weight+rightNode.weight);
parent.left =leftNode;
parent.right =rightNode;
nodes.remove(leftNode);
nodes.remove(rightNode);
nodes.add(parent);
}
return nodes.get(0);
}
}
//创建Node,待数据和权值
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();
}
}
}
}
2.实现哈夫曼编码
思路:在哈夫曼树的基础上实现哈夫曼编码,现在的所有叶子结点就是需要编码的数。
注意:getCodes方法中传入stringBuilder:是考虑递归的时候,需要把前面的值也一起加上。
//生成哈夫曼树对应的编码
//思路:1.将哈夫曼表存放在Map<Byte,string>形式
static Map<Byte,String> huffmanCodes = new HashMap<Byte,String>();
//2.在生成哈夫曼编码表时,需要去拼接路径,定义一个StringBuilder,存储某个叶子结点的路径。
static StringBuilder stringBuilder1 = new StringBuilder();
private static Map<Byte,String> getCodes(Node root){
if(root==null){
return null;
}
//处理root的左子树
getCodes(root.left,"0",stringBuilder1);
//处理root的右子树
getCodes(root.right,"1",stringBuilder1);
return huffmanCodes;
}
/**
* 功能:将传入的node结点的所有叶子结点 的哈夫曼树得到,并放入到huffmancodes集合中。
* @param node :传入结点
* @param code :左子结点是0,右子结点是1
* @param stringBuilder :用于拼接。
*/
private static void getCodes(Node node,String code,StringBuilder stringBuilder){
StringBuilder stringBuilder2 = new StringBuilder(stringBuilder); //这里传入stringBuilder:是考虑递归的时候,需要把前面的值也一起加上。
stringBuilder2.append(code);
if(node!=null){
if(node.data==null){
//递归处理
//向左递归
getCodes(node.left,"0",stringBuilder2);
//向右递归
getCodes(node.right,"1",stringBuilder2);
}else{//说明是一个叶子结点
//就表示找到某个叶子结点的最后
huffmanCodes.put(node.data,stringBuilder2.toString());
}
}