auto commit
This commit is contained in:
@ -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>
|
||||
|
Reference in New Issue
Block a user