This commit is contained in:
CyC2018
2018-12-18 23:01:48 +08:00
parent c6fe1de49a
commit 8c7aa37313
1239 changed files with 33534 additions and 408 deletions

View File

@ -293,7 +293,7 @@ public abstract class Sort<T extends Comparable<T>> {
选择排序需要 \~N<sup>2</sup>/2 次比较和 \~N 次交换,它的运行时间与输入无关,这个特点使得它对一个已经排序的数组也需要这么多的比较和交换操作。
<div align="center"> <img src="../pics//37e79a32-95a9-4503-bdb1-159527e628b8.png" width="250"/> </div><br>
<div align="center"> <img src="pics/37e79a32-95a9-4503-bdb1-159527e628b8.png" width="250"/> </div><br>
```java
public class Selection<T extends Comparable<T>> extends Sort<T> {
@ -322,7 +322,7 @@ public class Selection<T extends Comparable<T>> extends Sort<T> {
以下演示了在一轮循环中,将最大的元素 5 上浮到最右侧。
<div align="center"> <img src="../pics//1a2f2998-d0da-41c8-8222-1fd95083a66b.png" width="250"/> </div><br>
<div align="center"> <img src="pics/1a2f2998-d0da-41c8-8222-1fd95083a66b.png" width="250"/> </div><br>
```java
public class Bubble<T extends Comparable<T>> extends Sort<T> {
@ -358,7 +358,7 @@ public class Bubble<T extends Comparable<T>> extends Sort<T> {
以下演示了在一轮循环中,将元素 2 插入到左侧已经排序的数组中。
<div align="center"> <img src="../pics//2a8e1442-2381-4439-a83f-0312c8678b1f.png" width="250"/> </div><br>
<div align="center"> <img src="pics/2a8e1442-2381-4439-a83f-0312c8678b1f.png" width="250"/> </div><br>
```java
public class Insertion<T extends Comparable<T>> extends Sort<T> {
@ -383,7 +383,7 @@ public class Insertion<T extends Comparable<T>> extends Sort<T> {
希尔排序使用插入排序对间隔 h 的序列进行排序。通过不断减小 h最后令 h=1就可以使得整个数组是有序的。
<div align="center"> <img src="../pics//0157d362-98dd-4e51-ac26-00ecb76beb3e.png" width="500"/> </div><br>
<div align="center"> <img src="pics/0157d362-98dd-4e51-ac26-00ecb76beb3e.png" width="500"/> </div><br>
```java
public class Shell<T extends Comparable<T>> extends Sort<T> {
@ -417,7 +417,7 @@ public class Shell<T extends Comparable<T>> extends Sort<T> {
归并排序的思想是将数组分成两部分,分别进行排序,然后归并起来。
<div align="center"> <img src="../pics//220790c6-4377-4a2e-8686-58398afc8a18.png" width="350"/> </div><br>
<div align="center"> <img src="pics/220790c6-4377-4a2e-8686-58398afc8a18.png" width="350"/> </div><br>
### 1. 归并方法
@ -512,7 +512,7 @@ public class Down2UpMergeSort<T extends Comparable<T>> extends MergeSort<T> {
- 归并排序将数组分为两个子数组分别排序,并将有序的子数组归并使得整个数组排序;
- 快速排序通过一个切分元素将数组分为两个子数组,左子数组小于等于切分元素,右子数组大于等于切分元素,将这两个子数组排序也就将整个数组排序了。
<div align="center"> <img src="../pics//f8047846-efd4-42be-b6b7-27a7c4998b51.png" width="500"/> </div><br>
<div align="center"> <img src="pics/f8047846-efd4-42be-b6b7-27a7c4998b51.png" width="500"/> </div><br>
```java
public class QuickSort<T extends Comparable<T>> extends Sort<T> {
@ -543,7 +543,7 @@ public class QuickSort<T extends Comparable<T>> extends Sort<T> {
取 a[l] 作为切分元素,然后从数组的左端向右扫描直到找到第一个大于等于它的元素,再从数组的右端向左扫描找到第一个小于它的元素,交换这两个元素。不断进行这个过程,就可以保证左指针 i 的左侧元素都不大于切分元素,右指针 j 的右侧元素都不小于切分元素。当两个指针相遇时,将切分元素 a[l] 和 a[j] 交换位置。
<div align="center"> <img src="../pics//766aedd0-1b00-4065-aa2b-7d31138df84f.png" width="400"/> </div><br>
<div align="center"> <img src="pics/766aedd0-1b00-4065-aa2b-7d31138df84f.png" width="400"/> </div><br>
```java
private int partition(T[] nums, int l, int h) {
@ -647,7 +647,7 @@ public T select(T[] nums, int k) {
堆可以用数组来表示,这是因为堆是完全二叉树,而完全二叉树很容易就存储在数组中。位置 k 的节点的父节点位置为 k/2而它的两个子节点的位置分别为 2k 和 2k+1。这里不使用数组索引为 0 的位置,是为了更清晰地描述节点的位置关系。
<div align="center"> <img src="../pics//f3080f83-6239-459b-8e9c-03b6641f7815.png" width="200"/> </div><br>
<div align="center"> <img src="pics/f3080f83-6239-459b-8e9c-03b6641f7815.png" width="200"/> </div><br>
```java
public class Heap<T extends Comparable<T>> {
@ -683,7 +683,7 @@ public class Heap<T extends Comparable<T>> {
在堆中,当一个节点比父节点大,那么需要交换这个两个节点。交换后还可能比它新的父节点大,因此需要不断地进行比较和交换操作,把这种操作称为上浮。
<div align="center"> <img src="../pics//33ac2b23-cb85-4e99-bc41-b7b7199fad1c.png" width="400"/> </div><br>
<div align="center"> <img src="pics/33ac2b23-cb85-4e99-bc41-b7b7199fad1c.png" width="400"/> </div><br>
```java
private void swim(int k) {
@ -696,7 +696,7 @@ private void swim(int k) {
类似地,当一个节点比子节点来得小,也需要不断地向下进行比较和交换操作,把这种操作称为下沉。一个节点如果有两个子节点,应当与两个子节点中最大那个节点进行交换。
<div align="center"> <img src="../pics//72f0ff69-138d-4e54-b7ac-ebe025d978dc.png" width="400"/> </div><br>
<div align="center"> <img src="pics/72f0ff69-138d-4e54-b7ac-ebe025d978dc.png" width="400"/> </div><br>
```java
private void sink(int k) {
@ -745,15 +745,15 @@ public T delMax() {
无序数组建立堆最直接的方法是从左到右遍历数组进行上浮操作。一个更高效的方法是从右至左进行下沉操作,如果一个节点的两个节点都已经是堆有序,那么进行下沉操作可以使得这个节点为根节点的堆有序。叶子节点不需要进行下沉操作,可以忽略叶子节点的元素,因此只需要遍历一半的元素即可。
<div align="center"> <img src="../pics//b84ba6fb-312b-4e69-8c77-fb6eb6fb38d4.png" width="300"/> </div><br>
<div align="center"> <img src="pics/b84ba6fb-312b-4e69-8c77-fb6eb6fb38d4.png" width="300"/> </div><br>
#### 5.2 交换堆顶元素与最后一个元素
交换之后需要进行下沉操作维持堆的有序状态。
<div align="center"> <img src="../pics//51fb761d-8ce0-4472-92ff-2f227ac7888a.png" width="270"/> </div><br>
<div align="center"> <img src="pics/51fb761d-8ce0-4472-92ff-2f227ac7888a.png" width="270"/> </div><br>
<div align="center"> <img src="../pics//b20a3466-44b4-445e-87c7-dd4fb9ef44b2.png" width="350"/> </div><br>
<div align="center"> <img src="pics/b20a3466-44b4-445e-87c7-dd4fb9ef44b2.png" width="350"/> </div><br>
```java
public class HeapSort<T extends Comparable<T>> extends Sort<T> {
@ -825,7 +825,7 @@ Java 主要排序方法为 java.util.Arrays.sort(),对于原始数据类型使
用于解决动态连通性问题,能动态连接两个点,并且判断两个点是否连通。
<div align="center"> <img src="../pics//9d0a637c-6a8f-4f5a-99b9-fdcfa26793ff.png" width="400"/> </div><br>
<div align="center"> <img src="pics/9d0a637c-6a8f-4f5a-99b9-fdcfa26793ff.png" width="400"/> </div><br>
| 方法 | 描述 |
| :---: | :---: |
@ -864,7 +864,7 @@ public abstract class UF {
但是 union 操作代价却很高,需要将其中一个连通分量中的所有节点 id 值都修改为另一个节点的 id 值。
<div align="center"> <img src="../pics//8f0cc500-5994-4c7a-91a9-62885d658662.png" width="350"/> </div><br>
<div align="center"> <img src="pics/8f0cc500-5994-4c7a-91a9-62885d658662.png" width="350"/> </div><br>
```java
public class QuickFindUF extends UF {
@ -904,7 +904,7 @@ public class QuickFindUF extends UF {
但是 find 操作开销很大,因为同一个连通分量的节点 id 值不同id 值只是用来指向另一个节点。因此需要一直向上查找操作,直到找到最上层的节点。
<div align="center"> <img src="../pics//5d4a5181-65fb-4bf2-a9c6-899cab534b44.png" width="350"/> </div><br>
<div align="center"> <img src="pics/5d4a5181-65fb-4bf2-a9c6-899cab534b44.png" width="350"/> </div><br>
```java
public class QuickUnionUF extends UF {
@ -937,7 +937,7 @@ public class QuickUnionUF extends UF {
这种方法可以快速进行 union 操作,但是 find 操作和树高成正比,最坏的情况下树的高度为节点的数目。
<div align="center"> <img src="../pics//bfbb11e2-d208-4efa-b97b-24cd40467cd8.png" width="150"/> </div><br>
<div align="center"> <img src="pics/bfbb11e2-d208-4efa-b97b-24cd40467cd8.png" width="150"/> </div><br>
## 加权 Quick Union
@ -945,7 +945,7 @@ public class QuickUnionUF extends UF {
理论研究证明,加权 quick-union 算法构造的树深度最多不超过 logN。
<div align="center"> <img src="../pics//a4c17d43-fa5e-4935-b74e-147e7f7e782c.png" width="200"/> </div><br>
<div align="center"> <img src="pics/a4c17d43-fa5e-4935-b74e-147e7f7e782c.png" width="200"/> </div><br>
```java
public class WeightedQuickUnionUF extends UF {
@ -1534,15 +1534,15 @@ public class BinarySearchOrderedST<Key extends Comparable<Key>, Value> implement
**二叉树** 是一个空链接,或者是一个有左右两个链接的节点,每个链接都指向一颗子二叉树。
<div align="center"> <img src="../pics//f9f9f993-8ece-4da7-b848-af9b438fad76.png" width="200"/> </div><br>
<div align="center"> <img src="pics/f9f9f993-8ece-4da7-b848-af9b438fad76.png" width="200"/> </div><br>
**二叉查找树** BST是一颗二叉树并且每个节点的值都大于等于其左子树中的所有节点的值而小于等于右子树的所有节点的值。
<div align="center"> <img src="../pics//8ae4550b-f0cb-4e4d-9e2b-c550538bf230.png" width="200"/> </div><br>
<div align="center"> <img src="pics/8ae4550b-f0cb-4e4d-9e2b-c550538bf230.png" width="200"/> </div><br>
BST 有一个重要性质,就是它的中序遍历结果递增排序。
<div align="center"> <img src="../pics//fbe54203-c005-48f0-8883-b05e564a3173.png" width="200"/> </div><br>
<div align="center"> <img src="pics/fbe54203-c005-48f0-8883-b05e564a3173.png" width="200"/> </div><br>
基本数据结构:
@ -1616,7 +1616,7 @@ private Value get(Node x, Key key) {
当插入的键不存在于树中,需要创建一个新节点,并且更新上层节点的链接指向该节点,使得该节点正确地链接到树中。
<div align="center"> <img src="../pics//107a6a2b-f15b-4cad-bced-b7fb95258c9c.png" width="200"/> </div><br>
<div align="center"> <img src="pics/107a6a2b-f15b-4cad-bced-b7fb95258c9c.png" width="200"/> </div><br>
```java
@Override
@ -1645,11 +1645,11 @@ private Node put(Node x, Key key, Value value) {
最好的情况下树是完全平衡的,每条空链接和根节点的距离都为 logN。
<div align="center"> <img src="../pics//4d741402-344d-4b7c-be01-e57184bcad0e.png" width="200"/> </div><br>
<div align="center"> <img src="pics/4d741402-344d-4b7c-be01-e57184bcad0e.png" width="200"/> </div><br>
在最坏的情况下,树的高度为 N。
<div align="center"> <img src="../pics//be7dca03-12ec-456b-8b54-b1b3161f5531.png" width="200"/> </div><br>
<div align="center"> <img src="pics/be7dca03-12ec-456b-8b54-b1b3161f5531.png" width="200"/> </div><br>
### 4. floor()
@ -1727,7 +1727,7 @@ private Node min(Node x) {
令指向最小节点的链接指向最小节点的右子树。
<div align="center"> <img src="../pics//dd15a984-e977-4644-b127-708cddb8ca99.png" width="500"/> </div><br>
<div align="center"> <img src="pics/dd15a984-e977-4644-b127-708cddb8ca99.png" width="500"/> </div><br>
```java
public void deleteMin() {
@ -1748,7 +1748,7 @@ public Node deleteMin(Node x) {
- 如果待删除的节点只有一个子树, 那么只需要让指向待删除节点的链接指向唯一的子树即可;
- 否则,让右子树的最小节点替换该节点。
<div align="center"> <img src="../pics//fa568fac-ac58-48dd-a9bb-23b3065bf2dc.png" width="400"/> </div><br>
<div align="center"> <img src="pics/fa568fac-ac58-48dd-a9bb-23b3065bf2dc.png" width="400"/> </div><br>
```java
public void delete(Key key) {
@ -1811,7 +1811,7 @@ private List<Key> keys(Node x, Key l, Key h) {
2-3 查找树引入了 2- 节点和 3- 节点,目的是为了让树平衡。一颗完美平衡的 2-3 查找树的所有空链接到根节点的距离应该是相同的。
<div align="center"> <img src="../pics//ff396233-1bb1-4e74-8bc2-d7c90146f5dd.png" width="250"/> </div><br>
<div align="center"> <img src="pics/ff396233-1bb1-4e74-8bc2-d7c90146f5dd.png" width="250"/> </div><br>
### 1. 插入操作
@ -1821,11 +1821,11 @@ private List<Key> keys(Node x, Key l, Key h) {
- 如果插入到 2- 节点上,那么直接将新节点和原来的节点组成 3- 节点即可。
<div align="center"> <img src="../pics//7f38a583-2f2e-4738-97af-510e6fb403a7.png" width="400"/> </div><br>
<div align="center"> <img src="pics/7f38a583-2f2e-4738-97af-510e6fb403a7.png" width="400"/> </div><br>
- 如果是插入到 3- 节点上,就会产生一个临时 4- 节点时,需要将 4- 节点分裂成 3 个 2- 节点,并将中间的 2- 节点移到上层节点中。如果上移操作继续产生临时 4- 节点则一直进行分裂上移,直到不存在临时 4- 节点。
<div align="center"> <img src="../pics//ef280699-da36-4b38-9735-9b048a3c7fe0.png" width="500"/> </div><br>
<div align="center"> <img src="pics/ef280699-da36-4b38-9735-9b048a3c7fe0.png" width="500"/> </div><br>
### 2. 性质
@ -1837,7 +1837,7 @@ private List<Key> keys(Node x, Key l, Key h) {
红黑树是 2-3 查找树,但它不需要分别定义 2- 节点和 3- 节点,而是在普通的二叉查找树之上,为节点添加颜色。指向一个节点的链接颜色如果为红色,那么这个节点和上层节点表示的是一个 3- 节点,而黑色则是普通链接。
<div align="center"> <img src="../pics//4f48e806-f90b-4c09-a55f-ac0cd641c047.png" width="250"/> </div><br>
<div align="center"> <img src="pics/4f48e806-f90b-4c09-a55f-ac0cd641c047.png" width="250"/> </div><br>
红黑树具有以下性质:
@ -1846,7 +1846,7 @@ private List<Key> keys(Node x, Key l, Key h) {
画红黑树时可以将红链接画平。
<div align="center"> <img src="../pics//3086c248-b552-499e-b101-9cffe5c2773e.png" width="300"/> </div><br>
<div align="center"> <img src="pics/3086c248-b552-499e-b101-9cffe5c2773e.png" width="300"/> </div><br>
```java
public class RedBlackBST<Key extends Comparable<Key>, Value> extends BST<Key, Value> {
@ -1866,7 +1866,7 @@ public class RedBlackBST<Key extends Comparable<Key>, Value> extends BST<Key, Va
因为合法的红链接都为左链接,如果出现右链接为红链接,那么就需要进行左旋转操作。
<div align="center"> <img src="../pics//9110c1a0-8a54-4145-a814-2477d0128114.png" width="450"/> </div><br>
<div align="center"> <img src="pics/9110c1a0-8a54-4145-a814-2477d0128114.png" width="450"/> </div><br>
```java
public Node rotateLeft(Node h) {
@ -1885,7 +1885,7 @@ public Node rotateLeft(Node h) {
进行右旋转是为了转换两个连续的左红链接,这会在之后的插入过程中探讨。
<div align="center"> <img src="../pics//e2f0d889-2330-424c-8193-198edebecff7.png" width="450"/> </div><br>
<div align="center"> <img src="pics/e2f0d889-2330-424c-8193-198edebecff7.png" width="450"/> </div><br>
```java
public Node rotateRight(Node h) {
@ -1903,7 +1903,7 @@ public Node rotateRight(Node h) {
一个 4- 节点在红黑树中表现为一个节点的左右子节点都是红色的。分裂 4- 节点除了需要将子节点的颜色由红变黑之外,同时需要将父节点的颜色由黑变红,从 2-3 树的角度看就是将中间节点移到上层节点。
<div align="center"> <img src="../pics//af4639f9-af54-4400-aaf5-4e261d96ace7.png" width="300"/> </div><br>
<div align="center"> <img src="pics/af4639f9-af54-4400-aaf5-4e261d96ace7.png" width="300"/> </div><br>
```java
void flipColors(Node h) {
@ -1921,7 +1921,7 @@ void flipColors(Node h) {
- 如果左子节点是红色的,而且左子节点的左子节点也是红色的,进行右旋转;
- 如果左右子节点均为红色的,进行颜色转换。
<div align="center"> <img src="../pics//08427d38-8df1-49a1-8990-e0ce5ee36ca2.png" width="400"/> </div><br>
<div align="center"> <img src="pics/08427d38-8df1-49a1-8990-e0ce5ee36ca2.png" width="400"/> </div><br>
```java
@Override
@ -2046,7 +2046,7 @@ public class Transaction {
对于 N 个键M 条链表 (N>M),如果哈希函数能够满足均匀性的条件,每条链表的大小趋向于 N/M因此未命中的查找和插入操作所需要的比较次数为 \~N/M。
<div align="center"> <img src="../pics//b4252c85-6fb0-4995-9a68-a1a5925fbdb1.png" width="300"/> </div><br>
<div align="center"> <img src="pics/b4252c85-6fb0-4995-9a68-a1a5925fbdb1.png" width="300"/> </div><br>
### 3. 线性探测法
@ -2054,7 +2054,7 @@ public class Transaction {
使用线性探测法,数组的大小 M 应当大于键的个数 NM>N)。
<div align="center"> <img src="../pics//dbb8516d-37ba-4e2c-b26b-eefd7de21b45.png" width="400"/> </div><br>
<div align="center"> <img src="pics/dbb8516d-37ba-4e2c-b26b-eefd7de21b45.png" width="400"/> </div><br>
```java
public class LinearProbingHashST<Key, Value> implements UnorderedST<Key, Value> {
@ -2155,7 +2155,7 @@ public void delete(Key key) {
线性探测法的成本取决于连续条目的长度,连续条目也叫聚簇。当聚簇很长时,在查找和插入时也需要进行很多次探测。例如下图中 2\~5 位置就是一个聚簇。
<div align="center"> <img src="../pics//386cd64f-7a9d-40e6-8c55-22b90ee2d258.png" width="400"/> </div><br>
<div align="center"> <img src="pics/386cd64f-7a9d-40e6-8c55-22b90ee2d258.png" width="400"/> </div><br>
α = N/Mα 称为使用率。理论证明,当 α 小于 1/2 时探测的预计次数只在 1.5 到 2.5 之间。为了保证散列表的性能,应当调整数组的大小,使得 α 在 [1/4, 1/2] 之间。
@ -2231,21 +2231,21 @@ public class SparseVector {
## 汉诺塔
<div align="center"> <img src="../pics//54f1e052-0596-4b5e-833c-e80d75bf3f9b.png" width="300"/> </div><br>
<div align="center"> <img src="pics/54f1e052-0596-4b5e-833c-e80d75bf3f9b.png" width="300"/> </div><br>
这是一个经典的递归问题,分为三步求解:
① 将 n-1 个圆盘从 from -> buffer
<div align="center"> <img src="../pics//8587132a-021d-4f1f-a8ec-5a9daa7157a7.png" width="300"/> </div><br>
<div align="center"> <img src="pics/8587132a-021d-4f1f-a8ec-5a9daa7157a7.png" width="300"/> </div><br>
② 将 1 个圆盘从 from -> to
<div align="center"> <img src="../pics//2861e923-4862-4526-881c-15529279d49c.png" width="300"/> </div><br>
<div align="center"> <img src="pics/2861e923-4862-4526-881c-15529279d49c.png" width="300"/> </div><br>
③ 将 n-1 个圆盘从 buffer -> to
<div align="center"> <img src="../pics//1c4e8185-8153-46b6-bd5a-288b15feeae6.png" width="300"/> </div><br>
<div align="center"> <img src="pics/1c4e8185-8153-46b6-bd5a-288b15feeae6.png" width="300"/> </div><br>
如果只有一个圆盘,那么只需要进行一次移动操作。
@ -2296,7 +2296,7 @@ from H1 to H3
生成编码时,从根节点出发,向左遍历则添加二进制位 0向右则添加二进制位 1直到遍历到叶子节点叶子节点代表的字符的编码就是这个路径编码。
<div align="center"> <img src="../pics//3ff4f00a-2321-48fd-95f4-ce6001332151.png" width="400"/> </div><br>
<div align="center"> <img src="pics/3ff4f00a-2321-48fd-95f4-ce6001332151.png" width="400"/> </div><br>
```java
public class Huffman {