链接:
视频 | 手撕九大经典排序算法,看我就够了! - 力扣(LeetCode)的文章 - 知乎
复杂度
很多高效排序算法的代价是 nlogn,难道这是排序算法的极限了吗? - 曾加的回答 - 知乎
排序算法
冒泡排序
基础版本: 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16import Java.util.Arrays;
public static int[] BubbleSort(int[] sourceArray) {
int[] arr = Arrays.copyOf(sourceArray, sourceArray.length);
int temp;
for (int i = 0; i < arr.length; i++) {
for (int j = 0; j < arr.length - i - 1; j++) {
if (arr[j + 1] < arr[j]) {
temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
return arr;
}
优化版本: 如果第i
个循环,从第0
个到第arr.length-1-i
都顺序,则可以提前结束 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20public static int[] BubbleSort(int[] sourceArray) {
int[] arr = Arrays.copyOf(sourceArray, sourceArray.length);
int temp;
boolean flag;
for (int i = 0; i < arr.length; i++) {
flag = true; // 默认存在逆序对,会发生交换
for (int j = 0; j < arr.length - i - 1; j++) {
if (arr[j + 1] < arr[j]) {
flag = false;
temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
if (flag)
break; //如果全部顺序就提前结束外循环
}
return arr;
}
选择排序
每一步找出未排序序列中的最小值 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15public static int[] SelectionSort(int[] sourceArray){
int[] arr = Arrays.copyOf(sourceArray, sourceArray.length);
for (int i = 0; i < arr.length; i++){
int argmin = i;
for (int j = i; j < arr.length; j++){
if (arr[j] < arr[argmin]){
argmin = j;
}
}
int temp = arr[i];
arr[i] = arr[argmin];
arr[argmin] = temp;
}
return arr;
}
插入排序
把下一个元素插入到有序序列的合适位置 1
2
3
4
5
6
7
8
9
10
11
12
13
14public static int[] InsertSort(int[] sourceArray){
int[] arr = Arrays.copyOf(sourceArray, sourceArray.length);
for (int i = 1; i < arr.length; i++){
int j = i;
int temp = arr[i];
while (j - 1 >= 0 && arr[j - 1] > temp){
arr[j] = arr[j-1];
j--;
}
arr[j] = temp;
}
return arr;
}
希尔排序
优化版的插入排序 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15public static int[] ShellSort(int[] sourceArray) {
int[] arr = Arrays.copyOf(sourceArray, sourceArray.length);
for (int gap = arr.length >> 1; gap >= 1; gap >>= 1) {
for (int i = gap; i < arr.length; i++) {
int temp = arr[i];
int j = i;
while (j - gap >= 0 && arr[j - gap] > temp) {
arr[j] = arr[j - gap];
j -= gap;
}
arr[j] = temp;
}
}
return arr;
}
归并排序
递归,如果用原位迭代空间复杂度为o(1)但时间复杂度到o(n2) 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42public static int[] MergeSort(int[] sourceArray) {
int[] arr = Arrays.copyOf(sourceArray, sourceArray.length);
if (arr.length < 2)
return arr;
int middle = arr.length >> 1;
int[] left = Arrays.copyOfRange(arr, 0, middle);
int[] right = Arrays.copyOfRange(arr, middle, arr.length);
return merge(MergeSort(left), MergeSort(right));
}
static int[] merge(int[] left, int[] right) {// 两个有序序列
int[] result = new int[left.length + right.length];
int i = 0;
while (left.length > 0 && right.length > 0) {
if (left[0] <= right[0]) {
result[i] = left[0];
i++;
left = Arrays.copyOfRange(left, 1, left.length);
} else {
result[i] = right[0];
i++;
right = Arrays.copyOfRange(right, 1, right.length);
}
}
while (left.length > 0) {
result[i] = left[0];
i++;
left = Arrays.copyOfRange(left, 1, left.length);
}
while (right.length > 0) {
result[i] = right[0];
i++;
right = Arrays.copyOfRange(right, 1, right.length);
}
return result;
}
快速排序
- 快速排序是对冒泡排序的一种改进。
- 时间复杂度并不固定,如果在最坏情况下(元素刚好是反向的)速度比较慢,达到 O(n^2)(和选择排序一个效率),但是如果在比较理想的情况下时间复杂度 O(nlogn)。
- 从数列中挑出一个元素,称为 “基准”(pivot);
- 重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。
- 如果基准值
pivot
在最左,从i=index=pivot+1
开始,每次遇到比arr[index]
小的元素,就swap(arr, i, index); index++
。index
记录分隔符向右移动的情况,但此时pivot的位置还未改变,直到最后再swap(arr), pivot, index-1
。 - 在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作;
- 递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序;
1 | public static int[] QuickSort(int[] sourceArray) { |
堆排序
- 堆:相较于完全二叉树,所有父节点的值大于(小于)子节点
- 最大堆的最大元素值出现在根结点(堆顶)
- 稳定性:不稳定
- 完全二叉树、二叉堆属于数据结构。
1 | static void heapify(int[] arr, int i, int len){ |
计数排序
1 | static int[] CountingSort(int[] sourceArray){ |
桶排序
- 设置固定数量的空桶。
- 把数据放到对应的桶中
- 对每个不为空的桶中数据进行排序
- 拼接不为空的桶中数据,得到结果
1 | public class BucketSort { |
基数排序
- 首先通过
getMaxValue
方法得出最大数 - 通过
getDigitCount
方法得出最大数的数位数,也就是全arr的最大digits数 getDigitValue
方法可以给出给定数的某数位的值- 从低位(个位)开始,新建0-9十个空桶,按照arr中每个数的个位放入桶中,然后再依次放回到arr中
- 再进入下一位,新建十个空桶,重复操作
- 直到遍历MaxDigit
1 | import java.util.Arrays; |