CS-Notes/notes/算法 - 其它.md
2020-11-17 00:32:18 +08:00

130 lines
4.7 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 算法 - 其它
## 汉诺塔
<div align="center"> <img src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/69d6c38d-1dec-4f72-ae60-60dbc10e9d15.png" width="300"/> </div><br>
有三个柱子分别为 frombufferto需要将 from 上的圆盘全部移动到 to 并且要保证小圆盘始终在大圆盘上
这是一个经典的递归问题分为三步求解
n-1 个圆盘从 from -\> buffer
<div align="center"> <img src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/f9240aa1-8d48-4959-b28a-7ca45c3e4d91.png" width="300"/> </div><br>
1 个圆盘从 from -\> to
<div align="center"> <img src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/f579cab0-3d49-4d00-8e14-e9e1669d0f9f.png" width="300"/> </div><br>
n-1 个圆盘从 buffer -\> to
<div align="center"> <img src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/d02f74dd-8e33-4f3c-bf29-53203a06695a.png" width="300"/> </div><br>
如果只有一个圆盘那么只需要进行一次移动操作
从上面的讨论可以知道a<sub>n</sub> = 2 * a<sub>n-1</sub> + 1显然 a<sub>n</sub> = 2<sup>n</sup> - 1n 个圆盘需要移动 2<sup>n</sup> - 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 转换为 00b 转换为 01c 转换为 10d 转换为 11这是最简单的一种编码方式没有考虑各个字符的权值出现频率而哈夫曼编码采用了贪心策略使出现频率最高的字符的编码最短从而保证整体的编码长度最短
首先生成一颗哈夫曼树每次生成过程中选取频率最少的两个节点生成一个新节点作为它们的父节点并且新节点的频率为两个节点的和选取频率最少的原因是生成过程使得先选取的节点位于树的更低层那么需要的编码长度更长频率更少可以使得总编码长度更少
生成编码时从根节点出发向左遍历则添加二进制位 0向右则添加二进制位 1直到遍历到叶子节点叶子节点代表的字符的编码就是这个路径编码
<div align="center"> <img src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/8edc5164-810b-4cc5-bda8-2a2c98556377.jpg" width="300"/> </div><br>
```java
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);
}
}
```