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,4 +1,5 @@
# 约定
# 算法 - 排序
## 约定
待排序的元素需要实现 Java Comparable 接口该接口有 compareTo() 方法可以用它来判断两个元素的大小关系
@ -23,7 +24,7 @@ public abstract class Sort<T extends Comparable<T>> {
}
```
# 选择排序
## 选择排序
从数组中选择最小元素将它与数组的第一个元素交换位置再从数组剩下的元素中选择出最小的元素将它与数组的第二个元素交换位置不断进行这样的操作直到将整个数组排序
@ -50,7 +51,7 @@ public class Selection<T extends Comparable<T>> extends Sort<T> {
}
```
# 冒泡排序
## 冒泡排序
从左到右不断交换相邻逆序的元素在一轮的循环之后可以让未排序的最大元素上浮到右侧
@ -78,7 +79,7 @@ public class Bubble<T extends Comparable<T>> extends Sort<T> {
}
```
# 插入排序
## 插入排序
每次都将当前元素插入到左侧已经排序的数组中使得插入之后左侧数组依然有序
@ -107,7 +108,7 @@ public class Insertion<T extends Comparable<T>> extends Sort<T> {
}
```
# 希尔排序
## 希尔排序
对于大规模的数组插入排序很慢因为它只能交换相邻的元素每次只能将逆序数量减少 1希尔排序的出现就是为了解决插入排序的这种局限性它通过交换不相邻的元素每次可以将逆序数量减少大于 1
@ -143,13 +144,13 @@ public class Shell<T extends Comparable<T>> extends Sort<T> {
希尔排序的运行时间达不到平方级别使用递增序列 1, 4, 13, 40, ... 的希尔排序所需要的比较次数不会超过 N 的若干倍乘于递增序列的长度后面介绍的高级排序算法只会比希尔排序快两倍左右
# 归并排序
## 归并排序
归并排序的思想是将数组分成两部分分别进行排序然后归并起来
<div align="center"> <img src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/ec840967-d127-4da3-b6bb-186996c56746.png" width="300px"> </div><br>
## 1. 归并方法
### 1. 归并方法
归并方法将数组中两个已经排序的部分归并成一个
@ -185,7 +186,7 @@ public abstract class MergeSort<T extends Comparable<T>> extends Sort<T> {
}
```
## 2. 自顶向下归并排序
### 2. 自顶向下归并排序
将一个大数组分成两个小数组去求解
@ -213,7 +214,7 @@ public class Up2DownMergeSort<T extends Comparable<T>> extends MergeSort<T> {
```
## 3. 自底向上归并排序
### 3. 自底向上归并排序
先归并那些微型数组然后成对归并得到的微型数组
@ -236,9 +237,9 @@ public class Down2UpMergeSort<T extends Comparable<T>> extends MergeSort<T> {
```
# 快速排序
## 快速排序
## 1. 基本算法
### 1. 基本算法
- 归并排序将数组分为两个子数组分别排序并将有序的子数组归并使得整个数组排序
- 快速排序通过一个切分元素将数组分为两个子数组左子数组小于等于切分元素右子数组大于等于切分元素将这两个子数组排序也就将整个数组排序了
@ -270,7 +271,7 @@ public class QuickSort<T extends Comparable<T>> extends Sort<T> {
}
```
## 2. 切分
### 2. 切分
a[l] 作为切分元素然后从数组的左端向右扫描直到找到第一个大于等于它的元素再从数组的右端向左扫描找到第一个小于它的元素交换这两个元素不断进行这个过程就可以保证左指针 i 的左侧元素都不大于切分元素右指针 j 的右侧元素都不小于切分元素当两个指针相遇时将切分元素 a[l] a[j] 交换位置
@ -292,7 +293,7 @@ private int partition(T[] nums, int l, int h) {
}
```
## 3. 性能分析
### 3. 性能分析
快速排序是原地排序不需要辅助数组但是递归调用需要辅助栈
@ -300,17 +301,17 @@ private int partition(T[] nums, int l, int h) {
最坏的情况下第一次从最小的元素切分第二次从第二小的元素切分如此这般因此最坏的情况下需要比较 N<sup>2</sup>/2为了防止数组最开始就是有序的在进行快速排序时需要随机打乱数组
## 4. 算法改进
### 4. 算法改进
#### 4.1 切换到插入排序
##### 4.1 切换到插入排序
因为快速排序在小数组中也会递归调用自己对于小数组插入排序比快速排序的性能更好因此在小数组中可以切换到插入排序
#### 4.2 三数取中
##### 4.2 三数取中
最好的情况下是每次都能取数组的中位数作为切分元素但是计算中位数的代价很高一种折中方法是取 3 个元素并将大小居中的元素作为切分元素
#### 4.3 三向切分
##### 4.3 三向切分
对于有大量重复元素的数组可以将数组切分为三部分分别对应小于等于和大于切分元素
@ -342,7 +343,7 @@ public class ThreeWayQuickSort<T extends Comparable<T>> extends QuickSort<T> {
}
```
## 5. 基于切分的快速选择算法
### 5. 基于切分的快速选择算法
快速排序的 partition() 方法会返回一个整数 j 使得 a[l..j-1] 小于等于 a[j] a[j+1..h] 大于等于 a[j]此时 a[j] 就是数组的第 j 大元素
@ -370,9 +371,9 @@ public T select(T[] nums, int k) {
}
```
# 堆排序
## 堆排序
## 1.
### 1.
堆中某个节点的值总是大于等于或小于等于其子节点的值并且堆是一颗完全二叉树
@ -410,7 +411,7 @@ public class Heap<T extends Comparable<T>> {
}
```
## 2. 上浮和下沉
### 2. 上浮和下沉
在堆中当一个节点比父节点大那么需要交换这个两个节点交换后还可能比它新的父节点大因此需要不断地进行比较和交换操作把这种操作称为上浮
@ -443,7 +444,7 @@ private void sink(int k) {
}
```
## 3. 插入元素
### 3. 插入元素
将新元素放到数组末尾然后上浮到合适的位置
@ -454,7 +455,7 @@ public void insert(Comparable v) {
}
```
## 4. 删除最大元素
### 4. 删除最大元素
从数组顶端删除最大的元素并将数组的最后一个元素放到顶端并让这个元素下沉到合适的位置
@ -468,17 +469,17 @@ public T delMax() {
}
```
## 5. 堆排序
### 5. 堆排序
把最大元素和当前堆中数组的最后一个元素交换位置并且不删除它那么就可以得到一个从尾到头的递减序列从正向来看就是一个递增序列这就是堆排序
#### 5.1 构建堆
##### 5.1 构建堆
无序数组建立堆最直接的方法是从左到右遍历数组进行上浮操作一个更高效的方法是从右至左进行下沉操作如果一个节点的两个节点都已经是堆有序那么进行下沉操作可以使得这个节点为根节点的堆有序叶子节点不需要进行下沉操作可以忽略叶子节点的元素因此只需要遍历一半的元素即可
<div align="center"> <img src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/c2ca8dd2-8d00-4a3e-bece-db7849ac9cfd.gif" width="210px"> </div><br>
#### 5.2 交换堆顶元素与最后一个元素
##### 5.2 交换堆顶元素与最后一个元素
交换之后需要进行下沉操作维持堆的有序状态
@ -519,7 +520,7 @@ public class HeapSort<T extends Comparable<T>> extends Sort<T> {
}
```
## 6. 分析
### 6. 分析
一个堆的高度为 logN因此在堆中插入元素和删除最大元素的复杂度都为 logN
@ -529,9 +530,9 @@ public class HeapSort<T extends Comparable<T>> extends Sort<T> {
现代操作系统很少使用堆排序因为它无法利用局部性原理进行缓存也就是数组元素很少和相邻的元素进行比较和交换
# 小结
## 小结
## 1. 排序算法的比较
### 1. 排序算法的比较
| 算法 | 稳定性 | 时间复杂度 | 空间复杂度 | 备注 |
| :---: | :---: |:---: | :---: | :---: |
@ -548,13 +549,6 @@ public class HeapSort<T extends Comparable<T>> extends Sort<T> {
使用三向切分快速排序实际应用中可能出现的某些分布的输入能够达到线性级别而其它排序算法仍然需要线性对数时间
## 2. Java 的排序算法实现
### 2. Java 的排序算法实现
Java 主要排序方法为 java.util.Arrays.sort()对于原始数据类型使用三向切分的快速排序对于引用类型使用归并排序
<div align="center"><img width="320px" src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/githubio/公众号二维码-2.png"></img></div>