auto commit

This commit is contained in:
CyC2018
2020-11-17 00:32:18 +08:00
parent f5ad47b470
commit 7e61fc1360
380 changed files with 2371 additions and 46715 deletions

View File

@ -1,40 +1,42 @@
# 算法 - 符号表
<!-- GFM-TOC -->
* [前言](#前言)
* [初级实现](#初级实现)
* [1. 链表实现无序符号表](#1-链表实现无序符号表)
* [2. 二分查找实现序符号表](#2-二分查找实现序符号表)
* [二叉查找树](#二叉查找树)
* [1. get()](#1-get)
* [2. put()](#2-put)
* [3. 分析](#3-分析)
* [4. floor()](#4-floor)
* [5. rank()](#5-rank)
* [6. min()](#6-min)
* [7. deleteMin()](#7-deletemin)
* [8. delete()](#8-delete)
* [9. keys()](#9-keys)
* [10. 分析](#10-分析)
* [2-3 查找树](#2-3-查找树)
* [1. 插入操作](#1-插入操作)
* [2. 性质](#2-性质)
* [红黑树](#红黑树)
* [1. 左旋转](#1-左旋转)
* [2. 旋转](#2-旋转)
* [3. 颜色转换](#3-颜色转换)
* [4. 插入](#4-插入)
* [5. 分析](#5-分析)
* [散列表](#散列表)
* [1. 散列函数](#1-散列函数)
* [2. 拉链法](#2-拉链法)
* [3. 线性探测](#3-线性探测)
* [小结](#小结)
* [1. 符号表算法比较](#1-符号表算法比较)
* [2. Java 的符号表实现](#2-java-符号表实现)
* [3. 稀疏向量乘法](#3-稀疏向量乘法)
* [算法 - 符号表](#算法---符号表)
* [前言](#前言)
* [初级实现](#初级实现)
* [1. 链表实现序符号表](#1-链表实现序符号表)
* [2. 二分查找实现有序符号表](#2-二分查找实现有序符号表)
* [二叉查找树](#二叉查找树)
* [1. get()](#1-get)
* [2. put()](#2-put)
* [3. 分析](#3-分析)
* [4. floor()](#4-floor)
* [5. rank()](#5-rank)
* [6. min()](#6-min)
* [7. deleteMin()](#7-deletemin)
* [8. delete()](#8-delete)
* [9. keys()](#9-keys)
* [10. 分析](#10-分析)
* [2-3 查找树](#2-3-查找树)
* [1. 插入操作](#1-插入操作)
* [2. 性质](#2-性质)
* [红黑树](#红黑树)
* [1. 旋转](#1-旋转)
* [2. 右旋转](#2-右旋转)
* [3. 颜色转换](#3-颜色转换)
* [4. 插入](#4-插入)
* [5. 分析](#5-分析)
* [散列表](#散列表)
* [1. 散列函数](#1-散列函数)
* [2. 拉链](#2-拉链)
* [3. 线性探测法](#3-线性探测法)
* [小结](#小结)
* [1. 符号表算法比较](#1-符号表算法比较)
* [2. Java 的符号表实现](#2-java-的符号表实现)
* [3. 稀疏向量乘法](#3-稀疏向量乘法)
<!-- GFM-TOC -->
# 前言
## 前言
符号表Symbol Table是一种存储键值对的数据结构可以支持快速查找操作
@ -74,9 +76,9 @@ public interface OrderedST<Key extends Comparable<Key>, Value> {
}
```
# 初级实现
## 初级实现
## 1. 链表实现无序符号表
### 1. 链表实现无序符号表
```java
public class ListUnorderedST<Key, Value> implements UnorderedST<Key, Value> {
@ -151,7 +153,7 @@ public class ListUnorderedST<Key, Value> implements UnorderedST<Key, Value> {
}
```
## 2. 二分查找实现有序符号表
### 2. 二分查找实现有序符号表
使用一对平行数组一个存储键一个存储值
@ -241,7 +243,7 @@ public class BinarySearchOrderedST<Key extends Comparable<Key>, Value> implement
}
```
# 二叉查找树
## 二叉查找树
**二叉树** 是一个空链接或者是一个有左右两个链接的节点每个链接都指向一颗子二叉树
@ -296,7 +298,7 @@ public class BST<Key extends Comparable<Key>, Value> implements OrderedST<Key, V
为了方便绘图下文中二叉树的空链接不画出来
## 1. get()
### 1. get()
- 如果树是空的则查找未命中
- 如果被查找的键和根节点的键相等查找命中
@ -321,7 +323,7 @@ private Value get(Node x, Key key) {
}
```
## 2. put()
### 2. put()
当插入的键不存在于树中需要创建一个新节点并且更新上层节点的链接指向该节点使得该节点正确地链接到树中
@ -348,7 +350,7 @@ private Node put(Node x, Key key, Value value) {
}
```
## 3. 分析
### 3. 分析
二叉查找树的算法运行时间取决于树的形状而树的形状又取决于键被插入的先后顺序
@ -360,7 +362,7 @@ private Node put(Node x, Key key, Value value) {
<div align="center"> <img src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/5ea609cb-8ad4-4c4c-aee6-45a40a81794a.jpg" width="200"/> </div><br>
## 4. floor()
### 4. floor()
floor(key)小于等于键的最大键
@ -388,7 +390,7 @@ private Node floor(Node x, Key key) {
}
```
## 5. rank()
### 5. rank()
rank(key) 返回 key 的排名
@ -415,7 +417,7 @@ private int rank(Key key, Node x) {
}
```
## 6. min()
### 6. min()
```java
@Override
@ -432,7 +434,7 @@ private Node min(Node x) {
}
```
## 7. deleteMin()
### 7. deleteMin()
令指向最小节点的链接指向最小节点的右子树
@ -452,7 +454,7 @@ public Node deleteMin(Node x) {
}
```
## 8. delete()
### 8. delete()
- 如果待删除的节点只有一个子树 那么只需要让指向待删除节点的链接指向唯一的子树即可
- 否则让右子树的最小节点替换该节点
@ -486,7 +488,7 @@ private Node delete(Node x, Key key) {
}
```
## 9. keys()
### 9. keys()
利用二叉查找树中序遍历的结果为递增的特点
@ -512,17 +514,17 @@ private List<Key> keys(Node x, Key l, Key h) {
}
```
## 10. 分析
### 10. 分析
二叉查找树所有操作在最坏的情况下所需要的时间都和树的高度成正比
# 2-3 查找树
## 2-3 查找树
2-3 查找树引入了 2- 节点和 3- 节点目的是为了让树平衡一颗完美平衡的 2-3 查找树的所有空链接到根节点的距离应该是相同的
<div align="center"> <img src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/1097658b-c0e6-4821-be9b-25304726a11c.jpg" width="160px"/> </div><br>
## 1. 插入操作
### 1. 插入操作
插入操作和 BST 的插入操作有很大区别BST 的插入操作是先进行一次未命中的查找然后再将节点插入到对应的空链接上但是 2-3 查找树如果也这么做的话那么就会破坏了平衡性它是将新节点插入到叶子节点上
@ -536,13 +538,13 @@ private List<Key> keys(Node x, Key l, Key h) {
<div align="center"> <img src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/7002c01b-1ed5-475a-9e5f-5fc8a4cdbcc0.jpg" width="460"/> </div><br>
## 2. 性质
### 2. 性质
2-3 查找树插入操作的变换都是局部的除了相关的节点和链接之外不必修改或者检查树的其它部分而这些局部变换不会影响树的全局有序性和平衡性
2-3 查找树的查找和插入操作复杂度和插入顺序无关在最坏的情况下查找和插入操作访问的节点必然不超过 logN 含有 10 亿个节点的 2-3 查找树最多只需要访问 30 个节点就能进行任意的查找和插入操作
# 红黑树
## 红黑树
红黑树是 2-3 查找树但它不需要分别定义 2- 节点和 3- 节点而是在普通的二叉查找树之上为节点添加颜色指向一个节点的链接颜色如果为红色那么这个节点和上层节点表示的是一个 3- 节点而黑色则是普通链接
@ -571,7 +573,7 @@ public class RedBlackBST<Key extends Comparable<Key>, Value> extends BST<Key, Va
}
```
## 1. 左旋转
### 1. 左旋转
因为合法的红链接都为左链接如果出现右链接为红链接那么就需要进行左旋转操作
@ -590,7 +592,7 @@ public Node rotateLeft(Node h) {
}
```
## 2. 右旋转
### 2. 右旋转
进行右旋转是为了转换两个连续的左红链接这会在之后的插入过程中探讨
@ -609,7 +611,7 @@ public Node rotateRight(Node h) {
}
```
## 3. 颜色转换
### 3. 颜色转换
一个 4- 节点在红黑树中表现为一个节点的左右子节点都是红色的分裂 4- 节点除了需要将子节点的颜色由红变黑之外同时需要将父节点的颜色由黑变红 2-3 树的角度看就是将中间节点移到上层节点
@ -623,7 +625,7 @@ void flipColors(Node h) {
}
```
## 4. 插入
### 4. 插入
先将一个节点按二叉查找树的方法插入到正确位置然后再进行如下颜色操作
@ -670,19 +672,19 @@ private Node put(Node x, Key key, Value value) {
根节点一定为黑色因为根节点没有上层节点也就没有上层节点的左链接指向根节点flipColors() 有可能会使得根节点的颜色变为红色每当根节点由红色变成黑色时树的黑链接高度加 1.
## 5. 分析
### 5. 分析
一颗大小为 N 的红黑树的高度不会超过 2logN最坏的情况下是它所对应的 2-3 构成最左边的路径节点全部都是 3- 节点而其余都是 2- 节点
红黑树大多数的操作所需要的时间都是对数级别的
# 散列表
## 散列表
散列表类似于数组可以把散列表的散列值看成数组的索引值访问散列表和访问数组元素一样快速它可以在常数时间内实现查找和插入操作
由于无法通过散列值知道键的大小关系因此散列表无法实现有序性操作
## 1. 散列函数
### 1. 散列函数
对于一个大小为 M 的散列表散列函数能够把任意键转换为 [0, M-1] 内的正整数该正整数即为 hash
@ -748,21 +750,21 @@ public class Transaction {
}
```
## 2. 拉链法
### 2. 拉链法
拉链法使用链表来存储 hash 值相同的键从而解决冲突
查找需要分两步首先查找 Key 所在的链表然后在链表中顺序查找
对于 N 个键M 条链表 (N>M)如果哈希函数能够满足均匀性的条件每条链表的大小趋向于 N/M因此未命中的查找和插入操作所需要的比较次数为 \~N/M
对于 N 个键M 条链表 (N\>M)如果哈希函数能够满足均匀性的条件每条链表的大小趋向于 N/M因此未命中的查找和插入操作所需要的比较次数为 \~N/M
<div align="center"> <img src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/cbbfe06c-f0cb-47c4-bf7b-2780aebd98b2.png" width="330px"> </div><br>
## 3. 线性探测法
### 3. 线性探测法
线性探测法使用空位来解决冲突当冲突发生时向前探测一个空位来存储冲突的键
使用线性探测法数组的大小 M 应当大于键的个数 NM>N)
使用线性探测法数组的大小 M 应当大于键的个数 NM\>N)
<div align="center"> <img src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/0dbc4f7d-05c9-4aae-8065-7b7ea7e9709e.gif" width="350px"> </div><br>
@ -795,7 +797,7 @@ public class LinearProbingHashST<Key, Value> implements UnorderedST<Key, Value>
}
```
#### 3.1 查找
##### 3.1 查找
```java
public Value get(Key key) {
@ -807,7 +809,7 @@ public Value get(Key key) {
}
```
#### 3.2 插入
##### 3.2 插入
```java
public void put(Key key, Value value) {
@ -829,7 +831,7 @@ private void putInternal(Key key, Value value) {
}
```
#### 3.3 删除
##### 3.3 删除
删除操作应当将右侧所有相邻的键值对重新插入散列表中
@ -862,7 +864,7 @@ public void delete(Key key) {
}
```
#### 3.5 调整数组大小
##### 3.5 调整数组大小
线性探测法的成本取决于连续条目的长度连续条目也叫聚簇当聚簇很长时在查找和插入时也需要进行很多次探测例如下图中 2\~4 位置就是一个聚簇
@ -891,9 +893,9 @@ private void resize(int cap) {
}
```
# 小结
## 小结
## 1. 符号表算法比较
### 1. 符号表算法比较
| 算法 | 插入 | 查找 | 是否有序 |
| :---: | :---: | :---: | :---: |
@ -906,12 +908,12 @@ private void resize(int cap) {
应当优先考虑散列表当需要有序性操作时使用红黑树
## 2. Java 的符号表实现
### 2. Java 的符号表实现
- java.util.TreeMap红黑树
- java.util.HashMap拉链法的散列表
## 3. 稀疏向量乘法
### 3. 稀疏向量乘法
当向量为稀疏向量时可以使用符号表来存储向量中的非 0 索引和值使得乘法运算只需要对那些非 0 元素进行即可
@ -938,10 +940,3 @@ public class SparseVector {
}
}
```
<div align="center"><img width="320px" src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/githubio/公众号二维码-2.png"></img></div>