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());
            }
        }