常见排序列表
中文名称 | 英文名称 | 平均时间复杂度 | 最坏时间复杂度 | 最好时间复杂度 | 空间复杂度 | 稳定性 |
---|---|---|---|---|---|---|
选择排序 | 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 + " ");
}
}
}
递归:调用自身,但需要留出跳出去的条件,每次调用都会栈分配栈帧,然后栈帧在函数完成后依次消失。