常见排序列表
| 中文名称 | 英文名称 | 平均时间复杂度 | 最坏时间复杂度 | 最好时间复杂度 | 空间复杂度 | 稳定性 |
|---|---|---|---|---|---|---|
| 选择排序 | Selection | n2 | n2 | n2 | 1 | 不稳 |
| 冒泡排序 | Bubble | n2 | n2 | n | 1 | 稳 |
| 插入排序 | Insertion | n2 | n2 | n | 1 | 稳 |
| 堆排序 | heap | n log2 n | nlog2n | n log2 n | 1 | 不稳 |
| 希尔排序 | Shell | n1.3 | n2 | n | 1 | 不稳 |
| 归并排序 | Merge | n log2 n | n log2n | n log2 n | n | 稳 |
| 快速排序 | Quick | n log2 n | n2 | n log2 n | log2 n | 不稳 |
| 桶排序 | Bucket | n + k | n2 | n | n + k | 稳 |
| 计数排序 | Counting | n + k | n + k | n + k | n + k | 稳 |
| 基数排序 | Radix | n * k | n * k | n * k | n + k | 稳 |
排序算法
比较类排序
交换排序
冒泡排序
从第一个数开始,比较相邻的两个数,前一个比后一个大,则交换两个数位置
// 冒泡排序public class BubbleSort {public static void main(String[] args) {int[] a = {6,3,1,4,5,8,7,9,2};sort(a);print(a);}static void sort(int[] arr) {for (int i = arr.length - 1; i > 0; i--) {findMax(arr,i);}}static void findMax(int[] arr, int max) {for (int i = 0; i < max; i++) {if (arr[i + 1] < arr[i]) {swap(arr, i, i + 1);}}}static void swap(int[] arr, int i, int j) {int temp = arr[i];arr[i] = arr[j];arr[j] = temp;}static void print(int[] arr) {for (int i = 0; i < arr.length ; i++) {System.out.print( arr[i] + " ");}}}
这个算法仍然有优化空间,在数组为[1,2,3,4,5,6,7,8,9]时将时间复杂度降低为O(n)
时间复杂度
- 消耗时间最多的应该是这两个循环嵌套,则为 O(n²)
static void sort(int[] arr) {for (int i = arr.length - 1; i > 0; i--) {findMax(arr,i);}}static void findMax(int[] arr, int max) {for (int i = 0; i < max; i++) {if (arr[i + 1] < arr[i]) {swap(arr, i, i + 1);}}}
空间复杂度
O(1)
最坏时间复杂度
O(n²)
最好时间复杂度
O(n)
稳定性
两两进行比较,铁定是稳定的。
快速排序
//快速排序public class QuickSort {public static void main(String[] args) {int[] arr = {2,5,7,8,9,11,34,6,3,64,4,23,54,1,10};sort(arr);}static void sort(int[] arr) {int random = new Random().nextInt(arr.length);System.out.println("随机数:" + random);for (int i = 0; i < arr.length; i++) {if (arr[i] < arr[random]) {}}}static void print(int[] arr) {for (int i = 0; i < arr.length; i++) {System.out.print(arr[i] + " ");}}}
插入排序
简单插入排序
从后往前,将小的插到大的前面,将后面的整体后移。
// 插入排序public class InsertionSort {public static void main(String[] args) {int[] a = {6,3,1,4,5,8,7,9,2};sort(a);print(a);}static void sort(int[] arr) {// 选择第i个数字for (int i = 1; i < arr.length; i++) {compare(arr,i);}}static void compare (int[] arr, int point) {// 将指定数字插到比自己大的数字前面去for (int i = point; i > 0 && arr[i] < arr[i-1]; i--) {swap(arr,i,i-1);}}static void compare2 (int[] arr, int point) {int temp = arr[point];for (int i = point; i > 0; i--) {if (arr[i] < arr[i-1]) {arr[i - 1] = arr[i];}}arr[point]=temp;}static void swap(int[] arr, int i, int j) {int temp = arr[i];arr[i] = arr[j];arr[j] = temp;}static void print(int[] arr) {for (int i = 0; i < arr.length ; i++) {System.out.print( arr[i] + " ");}}}
时间复杂度
O(n²)
空间复杂度
O(n)
最坏时间复杂度
O(n²)
最好时间复杂度
O(n)
稳定性
稳定
- 冒泡
- 基本不用,太慢
- 选择:
- 基本不用,不稳
- 插入排序:
- 样本小且基本有序的时候效率比较高
希尔排序
改进的插入排序,选择固定的数字,依次减少,每次以该数字为间隔进行排序
// 希尔排序public class ShellSort {public static void main(String[] args) {int[] arr = {2,5,7,8,9,11,34,6,3,64,4,23,54,1,10};sort(arr);print(arr);}public static void sort(int[] arr) {int h = 1;while (h <= arr.length / 3) {h = 3*h +1;}for (int gap = h; gap > 0; gap = (gap-1)/3) {for (int i = gap; i < arr.length; i++) {for (int j = i; j > gap - 1; j -= gap) {if (arr[j - gap] > arr[j]) {swap(arr, j - gap, j);}}}}}static void swap(int[] arr, int i, int j) {int temp = arr[i];arr[i] = arr[j];arr[j] = temp;}static void print(int[] arr) {for (int num : arr) {System.out.print(num + " ");}}}
选择排序
从第一个开始和后面的依次比较,将每次最小的放到前面,使数组从小到大排列
简单选择排序
// 选择排序public class SelectionSort {public static void main(String[] args) {int[] arr = {6,3,1,4,5,8,7,9,2};sort(arr);print(arr);}static void sort(int[] arr) {int[] array = arr;for (int i = 0; i < array.length - 1; i++) {int minPos = i;for (int j = i + 1; j < array.length; j++) {minPos = array[j] < array[minPos] ? j : minPos;}swap(array,i,minPos);}}static void swap(int[] arr, int i, int j) {int temp = arr[i];arr[i] = arr[j];arr[j] = temp;}static void print(int[] array) {for(int num : array) {System.out.print(num + " ");}}}class SelectionSort2 {public static void main(String[] args) {int[] arr = {6,3,1,4,5,8,7,9,2};int minPos = 1, maxPos = arr.length-1;for (int i = 0; i < (arr.length)/2; i++){for (int j = 0; j < arr.length; j++) {minPos = arr[j] < arr[minPos] ? j : minPos;}int templ = arr[i];arr[i] = arr[minPos];arr[minPos] = templ;for (int k = arr.length-1; k >= 0; k--) {maxPos = arr[k] > arr[maxPos] ? k : maxPos;}int tempr = arr[arr.length-1];arr[arr.length-1] = arr[maxPos];arr[maxPos] = tempr;System.out.println("mixPos:" + minPos + "==="+ "maxPos:" + maxPos + "==");for (int num : arr) {System.out.print(num + " ");}System.out.println();}}}
时间复杂度
public class SelectionSort {public static void main(String[] args) {// 每个都需要进行初始化,不计入算法时间int[] array = {6,3,1,4,5,8,7,9,2};// O(1) O(n) O(n)for (int i = 0; i < array.length - 1; i++) {// O(n)int minPos = i;// O(n) O(n²) O(n²)for (int j = i + 1; j < array.length; j++) {minPos = array[j] < array[minPos] ? j : minPos;// (n-1)+(n-2)+……+1 = (n*(n-1))/2 = n²/2 - n/2 + T(常数)(舍去常数,低位项) = O(n²)}// 不计入算法时间System.out.println("minPos:" + minPos);// O(n)swap(array,i,minPos);// 不计入算法时间System.out.println("经过第" + i + "次循环,数组内容为:");// 不计入算法时间print(array);}}}
空间复杂度
- 需要用到的额外空间,临时变量之类的一直需要,而且用完即无,因此不需要考虑,可以考虑有无新数组之类的创建作为排序赋值、反转
消耗的空间也就是 O(1)
最好情况
至少也要O(n²)
最坏情况
最坏也是O(n²)
稳定性
不稳:两个相等的数会经过排序可能造成相对位置发生变化
验证算法一对数器
如何验算你的算法是否正确?
- 肉眼观察
- 产生足够多随机样本
- 用确定正确的算法计算样本结果
对比被验证算法的结果 ```java // 选择排序 public class SelectionSort { static void sort(int[] arr) {
// 每个都需要进行初始化,不计入算法时间int[] array = arr;/* int[] array = {6,3,1,4,5,8,7,9,2};*/// O(1) O(n) O(n)for (int i = 0; i < array.length - 1; i++) {// O(n)int minPos = i;// O(n) O(n²) O(n²)for (int j = i + 1; j < array.length; j++) {minPos = array[j] < array[minPos] ? j : minPos;// (n-1)+(n-2)+……+1 = (n*(n-1))/2 = n²/2 - n/2 + T(常数)(舍去常数,低位项) = O(n²)}// 不计入算法时间System.out.println("minPos:" + minPos);// O(n)swap(array,i,minPos);// 不计入算法时间System.out.println("经过第" + i + "次循环,数组内容为:");// 不计入算法时间print(array);}
}
static void swap(int[] arr, int i, int j) {
int temp = arr[i];arr[i] = arr[j];arr[j] = temp;
}
static void print(int array[]) {
for(int num : array) {System.out.print(num + " ");}
} }
// 对数器 class DataChecker { // 创建一个随机数组 static int[] generateRandomArray() { Random r = new Random();
int[] arr = new int[10];for (int i = 0; i < arr.length; i++) {arr[i] = r.nextInt(10);}return arr;}static void check() {// 拷贝一份,后续做比较int[] array = generateRandomArray();int[] array2 = new int[array.length];System.arraycopy(array,0,array2,0,array.length);Arrays.sort(array);SelectionSort.sort(array2);boolean same = true;for (int i = 0; i < array2.length; i++) {if (array[i] != array2[i]) {same = false;}}System.out.println(same == true ? "right" : "wrong");}public static void main(String[] args) {check();}
}
<a name="141c7256"></a>##### 堆排序<a name="95566613"></a>#### 归并排序<a name="fddb0f4c"></a>##### 二路归并排序** 两个已经排好的数组,用两个指针分别依次进行比较,将每次比较小的依次放进新的数组中,若一个数组已经空,则另一个数组直接放入新数组中。 **```java// 归并排序public class MergeSort {public static void main(String[] args) {int[] arr = {1,4,5,7,9,2,3,6,8};sort(arr,0,arr.length-1);print(arr);}public static void sort(int[] arr, int left, int right) {if (left == right) {return;}// 分成两半int mid = left + (right-left)/2;// 左部排序sort(arr,left,mid);// 右部排序sort(arr,mid+1,right);merge(arr,left,mid+1,right);}static void merge(int[] arr, int leftPrt, int rightPrt, int rightBound) {int mid = rightPrt - 1;int[] temp = new int[rightBound - leftPrt + 1];int i = leftPrt;int j = rightPrt;int k = 0;while (i <= mid && j <= rightBound) {temp[k++] = arr[i] <= arr[j] ? arr[i++] : arr[j++];}while (i <= mid) {temp[k++] = arr[i++];}while (j <= rightBound) {temp[k++] = arr[j++];}for (int m = 0; m < temp.length; m++) {arr[leftPrt + m] = temp[m];}}static void swap(int[] arr,int i,int j) {int temp = arr[i];arr[j] = arr[i];arr[i] = temp;}static void print(int[] arr) {for (int num : arr) {System.out.print(num + " ");}}}
递归:调用自身,但需要留出跳出去的条件,每次调用都会栈分配栈帧,然后栈帧在函数完成后依次消失。
