哈夫曼树

哈夫曼树的构造过程

首先生成一颗哈夫曼树,每次生成过程中选取频率最少的两个节点,生成一个新节点作为它们的父节点,并且新节点的频率为两个节点的和。选取频率最少的原因是,生成过程使得先选取的节点位于树的更低层,那么需要的编码长度更长,频率更少可以使得总编码长度更少。

生成编码时,从根节点出发,向左遍历则添加二进制位 0,向右则添加二进制位 1,直到遍历到叶子节点,叶子节点代表的字符的编码就是这个路径编码。

举例:

  • 给定了四个结点a,b,c,d,权值分别为7,5,2,4。
    image.png
  • 找出现有权值中最小的两个,2 和 4 ,相应的结点 c 和 d 构建一个新的二叉树,树根的权值为 2 + 4 = 6,同时将原有权值中的 2 和 4 删掉,将新的权值 6 加入。
    image.png
  • 重复之前的步骤。image.png
  • 所有的结点构建成了一个全新的二叉树,这就是哈夫曼树。
    image.png

代码

  1. public class Huffman {
  2. private class Node implements Comparable<Node> {
  3. char ch;
  4. int freq;
  5. boolean isLeaf;
  6. Node left, right;
  7. public Node(char ch, int freq) {
  8. this.ch = ch;
  9. this.freq = freq;
  10. isLeaf = true;
  11. }
  12. public Node(Node left, Node right, int freq) {
  13. this.left = left;
  14. this.right = right;
  15. this.freq = freq;
  16. isLeaf = false;
  17. }
  18. @Override
  19. public int compareTo(Node o) {
  20. return this.freq - o.freq;
  21. }
  22. }
  23. public Map<Character, String> encode(Map<Character, Integer> frequencyForChar) {
  24. PriorityQueue<Node> priorityQueue = new PriorityQueue<>();
  25. for (Character c : frequencyForChar.keySet()) {
  26. priorityQueue.add(new Node(c, frequencyForChar.get(c)));
  27. }
  28. while (priorityQueue.size() != 1) {
  29. Node node1 = priorityQueue.poll();
  30. Node node2 = priorityQueue.poll();
  31. priorityQueue.add(new Node(node1, node2, node1.freq + node2.freq));
  32. }
  33. return encode(priorityQueue.poll());
  34. }
  35. private Map<Character, String> encode(Node root) {
  36. Map<Character, String> encodingForChar = new HashMap<>();
  37. encode(root, "", encodingForChar);
  38. return encodingForChar;
  39. }
  40. private void encode(Node node, String encoding, Map<Character, String> encodingForChar) {
  41. if (node.isLeaf) {
  42. encodingForChar.put(node.ch, encoding);
  43. return;
  44. }
  45. encode(node.left, encoding + '0', encodingForChar);
  46. encode(node.right, encoding + '1', encodingForChar);
  47. }
  48. }