diff --git a/notes/算法.md b/notes/算法.md index 18366255..207621ce 100644 --- a/notes/算法.md +++ b/notes/算法.md @@ -31,6 +31,9 @@ * [红黑树](#红黑树) * [散列表](#散列表) * [小结](#小结) +* [七、其它](#七其它) + * [汉诺塔](#汉诺塔) + * [哈夫曼编码](#哈夫曼编码) * [参考资料](#参考资料) @@ -2032,6 +2035,131 @@ public class SparseVector { } ``` +# 七、其它 + +## 汉诺塔 + +这是一个经典的递归问题,分为三步求解: + +1. 将 n-1 个圆盘从 from -> buffer +2. 将 1 个圆盘从 from -> to +3. 将 n-1 个圆盘从 buffer -> to + +如果只有一个圆盘,那么只需要进行一次移动操作,从上面的移动步骤可以知道,n 圆盘需要移动 (n-1)+1+(n-1) = 2n-1 次。 + +

+ +

+ +

+ +

+ +```java +public class Hanoi { + public static void move(int n, String from, String buffer, String to) { + if (n == 1) { + System.out.println("from " + from + " to " + to); + return; + } + move(n - 1, from, to, buffer); + move(1, from, buffer, to); + move(n - 1, buffer, from, to); + } + + public static void main(String[] args) { + Hanoi.move(3, "H1", "H2", "H3"); + } +} +``` + +```html +from H1 to H3 +from H1 to H2 +from H3 to H2 +from H1 to H3 +from H2 to H1 +from H2 to H3 +from H1 to H3 +``` + +## 哈夫曼编码 + +哈夫曼编码根据数据出现的频率对数据进行编码,从而压缩原始数据。 + +例如对于文本文件,其中各种字符出现的次数如下: + +- a : 10 +- b : 20 +- c : 40 +- d : 80 + +可以将每种字符转换成二进制编码,例如将 a 转换为 00,b 转换为 01,c 转换为 10,d 转换为 11。这是最简单的一种编码方式,没有考虑各个字符的权值(出现频率)。而哈夫曼编码能让出现频率最大的字符编码最短,从而保证最终的编码长度最短。 + +首先生成一颗哈夫曼树,每次生成过程中选取频率最少的两个节点,生成一个新节点作为它们的父节点,并且新节点的频率为两个节点的和。选取频率最少的原因是,生成过程使得先选取的节点在树的最底层,那么需要的编码长度更长,频率更少可以使得总编码长度更少。 + +生成编码时,从根节点出发,向左遍历则添加二进制位 0,向右则添加二进制位 1,直到遍历到根节点,根节点代表的字符的编码就是这个路径编码。 + +

+ +```java +public class Huffman { + + private class Node implements Comparable { + char ch; + int freq; + boolean isLeaf; + Node left, right; + + public Node(char ch, int freq) { + this.ch = ch; + this.freq = freq; + isLeaf = true; + } + + public Node(Node left, Node right, int freq) { + this.left = left; + this.right = right; + this.freq = freq; + isLeaf = false; + } + + @Override + public int compareTo(Node o) { + return this.freq - o.freq; + } + } + + public Map encode(Map frequencyForChar) { + PriorityQueue priorityQueue = new PriorityQueue<>(); + for (Character c : frequencyForChar.keySet()) { + priorityQueue.add(new Node(c, frequencyForChar.get(c))); + } + while (priorityQueue.size() != 1) { + Node node1 = priorityQueue.poll(); + Node node2 = priorityQueue.poll(); + priorityQueue.add(new Node(node1, node2, node1.freq + node2.freq)); + } + return encode(priorityQueue.poll()); + } + + private Map encode(Node root) { + Map encodingForChar = new HashMap<>(); + encode(root, "", encodingForChar); + return encodingForChar; + } + + private void encode(Node node, String encoding, Map encodingForChar) { + if (node.isLeaf) { + encodingForChar.put(node.ch, encoding); + return; + } + encode(node.left, encoding + '0', encodingForChar); + encode(node.right, encoding + '1', encodingForChar); + } +} +``` + # 参考资料 - Sedgewick, Robert, and Kevin Wayne. _Algorithms_. Addison-Wesley Professional, 2011. diff --git a/pics/1c4e8185-8153-46b6-bd5a-288b15feeae6.png b/pics/1c4e8185-8153-46b6-bd5a-288b15feeae6.png new file mode 100644 index 00000000..35e992f7 Binary files /dev/null and b/pics/1c4e8185-8153-46b6-bd5a-288b15feeae6.png differ diff --git a/pics/2861e923-4862-4526-881c-15529279d49c.png b/pics/2861e923-4862-4526-881c-15529279d49c.png new file mode 100644 index 00000000..6cc26c26 Binary files /dev/null and b/pics/2861e923-4862-4526-881c-15529279d49c.png differ diff --git a/pics/3ff4f00a-2321-48fd-95f4-ce6001332151.png b/pics/3ff4f00a-2321-48fd-95f4-ce6001332151.png new file mode 100644 index 00000000..266a4687 Binary files /dev/null and b/pics/3ff4f00a-2321-48fd-95f4-ce6001332151.png differ diff --git a/pics/54f1e052-0596-4b5e-833c-e80d75bf3f9b.png b/pics/54f1e052-0596-4b5e-833c-e80d75bf3f9b.png new file mode 100644 index 00000000..ad60a7e0 Binary files /dev/null and b/pics/54f1e052-0596-4b5e-833c-e80d75bf3f9b.png differ diff --git a/pics/8587132a-021d-4f1f-a8ec-5a9daa7157a7.png b/pics/8587132a-021d-4f1f-a8ec-5a9daa7157a7.png new file mode 100644 index 00000000..f77ff346 Binary files /dev/null and b/pics/8587132a-021d-4f1f-a8ec-5a9daa7157a7.png differ