算法 - 其它.md 5.5 KB
Newer Older
C
CyC2018 已提交
1
<!-- GFM-TOC -->
C
CyC2018 已提交
2 3
* [汉诺塔](#汉诺塔)
* [哈夫曼编码](#哈夫曼编码)
C
CyC2018 已提交
4
<!-- GFM-TOC -->
C
CyC2018 已提交
5 6


C
CyC2018 已提交
7 8
# 汉诺塔

C
CyC2018 已提交
9
<div align="center"> <img src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/69d6c38d-1dec-4f72-ae60-60dbc10e9d15.png" width="300"/> </div><br>
C
CyC2018 已提交
10 11

有三个柱子,分别为 from、buffer、to。需要将 from 上的圆盘全部移动到 to 上,并且要保证小圆盘始终在大圆盘上。
C
CyC2018 已提交
12 13 14

这是一个经典的递归问题,分为三步求解:

C
CyC2018 已提交
15
① 将 n-1 个圆盘从 from -> buffer
C
CyC2018 已提交
16

C
CyC2018 已提交
17
<div align="center"> <img src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/f9240aa1-8d48-4959-b28a-7ca45c3e4d91.png" width="300"/> </div><br>
C
CyC2018 已提交
18

C
CyC2018 已提交
19
② 将 1 个圆盘从 from -> to
C
CyC2018 已提交
20

C
CyC2018 已提交
21
<div align="center"> <img src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/f579cab0-3d49-4d00-8e14-e9e1669d0f9f.png" width="300"/> </div><br>
C
CyC2018 已提交
22

C
CyC2018 已提交
23
③ 将 n-1 个圆盘从 buffer -> to
C
CyC2018 已提交
24

C
CyC2018 已提交
25
<div align="center"> <img src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/d02f74dd-8e33-4f3c-bf29-53203a06695a.png" width="300"/> </div><br>
C
CyC2018 已提交
26 27 28

如果只有一个圆盘,那么只需要进行一次移动操作。

C
CyC2018 已提交
29
从上面的讨论可以知道,a<sub>n</sub> = 2 * a<sub>n-1</sub> + 1,显然 a<sub>n</sub> = 2<sup>n</sup> - 1,n 个圆盘需要移动 2<sup>n</sup> - 1 次。
C
CyC2018 已提交
30 31

```java
C
CyC2018 已提交
32 33 34 35 36 37 38 39 40 41 42 43 44 45
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");
    }
C
CyC2018 已提交
46 47 48 49
}
```

```html
C
CyC2018 已提交
50 51 52 53 54 55 56
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
C
CyC2018 已提交
57 58
```

C
CyC2018 已提交
59
# 哈夫曼编码
C
CyC2018 已提交
60 61 62 63 64

根据数据出现的频率对数据进行编码,从而压缩原始数据。

例如对于一个文本文件,其中各种字符出现的次数如下:

C
CyC2018 已提交
65 66 67 68
- a : 10
- b : 20
- c : 40
- d : 80
C
CyC2018 已提交
69

C
CyC2018 已提交
70
可以将每种字符转换成二进制编码,例如将 a 转换为 00,b 转换为 01,c 转换为 10,d 转换为 11。这是最简单的一种编码方式,没有考虑各个字符的权值(出现频率)。而哈夫曼编码采用了贪心策略,使出现频率最高的字符的编码最短,从而保证整体的编码长度最短。
C
CyC2018 已提交
71 72 73

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

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

C
CyC2018 已提交
76
<div align="center"> <img src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/8edc5164-810b-4cc5-bda8-2a2c98556377.jpg" width="300"/> </div><br>
C
CyC2018 已提交
77 78

```java
C
CyC2018 已提交
79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132
public class Huffman {

    private class Node implements Comparable<Node> {
        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<Character, String> encode(Map<Character, Integer> frequencyForChar) {
        PriorityQueue<Node> 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<Character, String> encode(Node root) {
        Map<Character, String> encodingForChar = new HashMap<>();
        encode(root, "", encodingForChar);
        return encodingForChar;
    }

    private void encode(Node node, String encoding, Map<Character, String> encodingForChar) {
        if (node.isLeaf) {
            encodingForChar.put(node.ch, encoding);
            return;
        }
        encode(node.left, encoding + '0', encodingForChar);
        encode(node.right, encoding + '1', encodingForChar);
    }
C
CyC2018 已提交
133 134
}
```
C
CyC2018 已提交
135 136 137 138




C
CyC2018 已提交
139 140 141 142 143 144
# 微信公众号


微信公众号 CyC2018 提供了该项目的离线阅读版本,后台回复 "下载" 即可领取。也提供了一份技术面试复习大纲,不仅系统整理了面试知识点,而且标注了各个知识点的重要程度,从而帮你理清多而杂的面试知识点,后台回复 "大纲" 即可领取。我基本是按照这个大纲来进行复习的,对我拿到了 BAT 头条等 Offer 起到很大的帮助。你们完全可以和我一样根据大纲上列的知识点来进行复习,就不用看很多不重要的内容,也可以知道哪些内容很重要从而多安排一些复习时间。


C
CyC2018 已提交
145
<img width="580px" src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/other/公众号海报2.png"></img>