常见排序列表

中文名称 英文名称 平均时间复杂度 最坏时间复杂度 最好时间复杂度 空间复杂度 稳定性
选择排序 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

排序算法

比较类排序

交换排序

冒泡排序

从第一个数开始,比较相邻的两个数,前一个比后一个大,则交换两个数位置

  1. // 冒泡排序
  2. public class BubbleSort {
  3. public static void main(String[] args) {
  4. int[] a = {6,3,1,4,5,8,7,9,2};
  5. sort(a);
  6. print(a);
  7. }
  8. static void sort(int[] arr) {
  9. for (int i = arr.length - 1; i > 0; i--) {
  10. findMax(arr,i);
  11. }
  12. }
  13. static void findMax(int[] arr, int max) {
  14. for (int i = 0; i < max; i++) {
  15. if (arr[i + 1] < arr[i]) {
  16. swap(arr, i, i + 1);
  17. }
  18. }
  19. }
  20. static void swap(int[] arr, int i, int j) {
  21. int temp = arr[i];
  22. arr[i] = arr[j];
  23. arr[j] = temp;
  24. }
  25. static void print(int[] arr) {
  26. for (int i = 0; i < arr.length ; i++) {
  27. System.out.print( arr[i] + " ");
  28. }
  29. }
  30. }

这个算法仍然有优化空间,在数组为[1,2,3,4,5,6,7,8,9]时将时间复杂度降低为O(n)

时间复杂度

  • 消耗时间最多的应该是这两个循环嵌套,则为 O(n²)
  1. static void sort(int[] arr) {
  2. for (int i = arr.length - 1; i > 0; i--) {
  3. findMax(arr,i);
  4. }
  5. }
  6. static void findMax(int[] arr, int max) {
  7. for (int i = 0; i < max; i++) {
  8. if (arr[i + 1] < arr[i]) {
  9. swap(arr, i, i + 1);
  10. }
  11. }
  12. }

空间复杂度
O(1)

最坏时间复杂度
O(n²)

最好时间复杂度
O(n)

稳定性
两两进行比较,铁定是稳定的。

快速排序
  1. //快速排序
  2. public class QuickSort {
  3. public static void main(String[] args) {
  4. int[] arr = {2,5,7,8,9,11,34,6,3,64,4,23,54,1,10};
  5. sort(arr);
  6. }
  7. static void sort(int[] arr) {
  8. int random = new Random().nextInt(arr.length);
  9. System.out.println("随机数:" + random);
  10. for (int i = 0; i < arr.length; i++) {
  11. if (arr[i] < arr[random]) {
  12. }
  13. }
  14. }
  15. static void print(int[] arr) {
  16. for (int i = 0; i < arr.length; i++) {
  17. System.out.print(arr[i] + " ");
  18. }
  19. }
  20. }

插入排序

简单插入排序

从后往前,将小的插到大的前面,将后面的整体后移。

  1. // 插入排序
  2. public class InsertionSort {
  3. public static void main(String[] args) {
  4. int[] a = {6,3,1,4,5,8,7,9,2};
  5. sort(a);
  6. print(a);
  7. }
  8. static void sort(int[] arr) {
  9. // 选择第i个数字
  10. for (int i = 1; i < arr.length; i++) {
  11. compare(arr,i);
  12. }
  13. }
  14. static void compare (int[] arr, int point) {
  15. // 将指定数字插到比自己大的数字前面去
  16. for (int i = point; i > 0 && arr[i] < arr[i-1]; i--) {
  17. swap(arr,i,i-1);
  18. }
  19. }
  20. static void compare2 (int[] arr, int point) {
  21. int temp = arr[point];
  22. for (int i = point; i > 0; i--) {
  23. if (arr[i] < arr[i-1]) {
  24. arr[i - 1] = arr[i];
  25. }
  26. }
  27. arr[point]=temp;
  28. }
  29. static void swap(int[] arr, int i, int j) {
  30. int temp = arr[i];
  31. arr[i] = arr[j];
  32. arr[j] = temp;
  33. }
  34. static void print(int[] arr) {
  35. for (int i = 0; i < arr.length ; i++) {
  36. System.out.print( arr[i] + " ");
  37. }
  38. }
  39. }

时间复杂度
O(n²)

空间复杂度
O(n)

最坏时间复杂度
O(n²)

最好时间复杂度
O(n)

稳定性
稳定

  • 冒泡
    • 基本不用,太慢
  • 选择:
    • 基本不用,不稳
  • 插入排序:
    • 样本小且基本有序的时候效率比较高

希尔排序

改进的插入排序,选择固定的数字,依次减少,每次以该数字为间隔进行排序

  1. // 希尔排序
  2. public class ShellSort {
  3. public static void main(String[] args) {
  4. int[] arr = {2,5,7,8,9,11,34,6,3,64,4,23,54,1,10};
  5. sort(arr);
  6. print(arr);
  7. }
  8. public static void sort(int[] arr) {
  9. int h = 1;
  10. while (h <= arr.length / 3) {
  11. h = 3*h +1;
  12. }
  13. for (int gap = h; gap > 0; gap = (gap-1)/3) {
  14. for (int i = gap; i < arr.length; i++) {
  15. for (int j = i; j > gap - 1; j -= gap) {
  16. if (arr[j - gap] > arr[j]) {
  17. swap(arr, j - gap, j);
  18. }
  19. }
  20. }
  21. }
  22. }
  23. static void swap(int[] arr, int i, int j) {
  24. int temp = arr[i];
  25. arr[i] = arr[j];
  26. arr[j] = temp;
  27. }
  28. static void print(int[] arr) {
  29. for (int num : arr) {
  30. System.out.print(num + " ");
  31. }
  32. }
  33. }

选择排序

从第一个开始和后面的依次比较,将每次最小的放到前面,使数组从小到大排列

简单选择排序
  1. // 选择排序
  2. public class SelectionSort {
  3. public static void main(String[] args) {
  4. int[] arr = {6,3,1,4,5,8,7,9,2};
  5. sort(arr);
  6. print(arr);
  7. }
  8. static void sort(int[] arr) {
  9. int[] array = arr;
  10. for (int i = 0; i < array.length - 1; i++) {
  11. int minPos = i;
  12. for (int j = i + 1; j < array.length; j++) {
  13. minPos = array[j] < array[minPos] ? j : minPos;
  14. }
  15. swap(array,i,minPos);
  16. }
  17. }
  18. static void swap(int[] arr, int i, int j) {
  19. int temp = arr[i];
  20. arr[i] = arr[j];
  21. arr[j] = temp;
  22. }
  23. static void print(int[] array) {
  24. for(int num : array) {
  25. System.out.print(num + " ");
  26. }
  27. }
  28. }
  29. class SelectionSort2 {
  30. public static void main(String[] args) {
  31. int[] arr = {6,3,1,4,5,8,7,9,2};
  32. int minPos = 1, maxPos = arr.length-1;
  33. for (int i = 0; i < (arr.length)/2; i++){
  34. for (int j = 0; j < arr.length; j++) {
  35. minPos = arr[j] < arr[minPos] ? j : minPos;
  36. }
  37. int templ = arr[i];
  38. arr[i] = arr[minPos];
  39. arr[minPos] = templ;
  40. for (int k = arr.length-1; k >= 0; k--) {
  41. maxPos = arr[k] > arr[maxPos] ? k : maxPos;
  42. }
  43. int tempr = arr[arr.length-1];
  44. arr[arr.length-1] = arr[maxPos];
  45. arr[maxPos] = tempr;
  46. System.out.println("mixPos:" + minPos + "==="+ "maxPos:" + maxPos + "==");
  47. for (int num : arr) {
  48. System.out.print(num + " ");
  49. }
  50. System.out.println();
  51. }
  52. }
  53. }

时间复杂度

  1. public class SelectionSort {
  2. public static void main(String[] args) {
  3. // 每个都需要进行初始化,不计入算法时间
  4. int[] array = {6,3,1,4,5,8,7,9,2};
  5. // O(1) O(n) O(n)
  6. for (int i = 0; i < array.length - 1; i++) {
  7. // O(n)
  8. int minPos = i;
  9. // O(n) O(n²) O(n²)
  10. for (int j = i + 1; j < array.length; j++) {
  11. minPos = array[j] < array[minPos] ? j : minPos;
  12. // (n-1)+(n-2)+……+1 = (n*(n-1))/2 = n²/2 - n/2 + T(常数)(舍去常数,低位项) = O(n²)
  13. }
  14. // 不计入算法时间
  15. System.out.println("minPos:" + minPos);
  16. // O(n)
  17. swap(array,i,minPos);
  18. // 不计入算法时间
  19. System.out.println("经过第" + i + "次循环,数组内容为:");
  20. // 不计入算法时间
  21. print(array);
  22. }
  23. }
  24. }

空间复杂度

  • 需要用到的额外空间,临时变量之类的一直需要,而且用完即无,因此不需要考虑,可以考虑有无新数组之类的创建作为排序赋值、反转

消耗的空间也就是 O(1)

最好情况
至少也要O(n²)

最坏情况
最坏也是O(n²)

稳定性
不稳:两个相等的数会经过排序可能造成相对位置发生变化

验证算法一对数器

如何验算你的算法是否正确?

  1. 肉眼观察
  2. 产生足够多随机样本
  3. 用确定正确的算法计算样本结果
  4. 对比被验证算法的结果 ```java // 选择排序 public class SelectionSort { static void sort(int[] arr) {

    1. // 每个都需要进行初始化,不计入算法时间
    2. int[] array = arr;
    3. /* int[] array = {6,3,1,4,5,8,7,9,2};*/
    4. // O(1) O(n) O(n)
    5. for (int i = 0; i < array.length - 1; i++) {
    6. // O(n)
    7. int minPos = i;
    8. // O(n) O(n²) O(n²)
    9. for (int j = i + 1; j < array.length; j++) {
    10. minPos = array[j] < array[minPos] ? j : minPos;
    11. // (n-1)+(n-2)+……+1 = (n*(n-1))/2 = n²/2 - n/2 + T(常数)(舍去常数,低位项) = O(n²)
    12. }
    13. // 不计入算法时间
    14. System.out.println("minPos:" + minPos);
    15. // O(n)
    16. swap(array,i,minPos);
    17. // 不计入算法时间
    18. System.out.println("经过第" + i + "次循环,数组内容为:");
    19. // 不计入算法时间
    20. print(array);
    21. }

    }

    static void swap(int[] arr, int i, int j) {

    1. int temp = arr[i];
    2. arr[i] = arr[j];
    3. arr[j] = temp;

    }

    static void print(int array[]) {

    1. for(int num : array) {
    2. System.out.print(num + " ");
    3. }

    } }

// 对数器 class DataChecker { // 创建一个随机数组 static int[] generateRandomArray() { Random r = new Random();

  1. int[] arr = new int[10];
  2. for (int i = 0; i < arr.length; i++) {
  3. arr[i] = r.nextInt(10);
  4. }
  5. return arr;
  6. }
  7. static void check() {
  8. // 拷贝一份,后续做比较
  9. int[] array = generateRandomArray();
  10. int[] array2 = new int[array.length];
  11. System.arraycopy(array,0,array2,0,array.length);
  12. Arrays.sort(array);
  13. SelectionSort.sort(array2);
  14. boolean same = true;
  15. for (int i = 0; i < array2.length; i++) {
  16. if (array[i] != array2[i]) {
  17. same = false;
  18. }
  19. }
  20. System.out.println(same == true ? "right" : "wrong");
  21. }
  22. public static void main(String[] args) {
  23. check();
  24. }

}

  1. <a name="141c7256"></a>
  2. ##### 堆排序
  3. <a name="95566613"></a>
  4. #### 归并排序
  5. <a name="fddb0f4c"></a>
  6. ##### 二路归并排序
  7. ** 两个已经排好的数组,用两个指针分别依次进行比较,将每次比较小的依次放进新的数组中,若一个数组已经空,则另一个数组直接放入新数组中。 **
  8. ```java
  9. // 归并排序
  10. public class MergeSort {
  11. public static void main(String[] args) {
  12. int[] arr = {1,4,5,7,9,2,3,6,8};
  13. sort(arr,0,arr.length-1);
  14. print(arr);
  15. }
  16. public static void sort(int[] arr, int left, int right) {
  17. if (left == right) {
  18. return;
  19. }
  20. // 分成两半
  21. int mid = left + (right-left)/2;
  22. // 左部排序
  23. sort(arr,left,mid);
  24. // 右部排序
  25. sort(arr,mid+1,right);
  26. merge(arr,left,mid+1,right);
  27. }
  28. static void merge(int[] arr, int leftPrt, int rightPrt, int rightBound) {
  29. int mid = rightPrt - 1;
  30. int[] temp = new int[rightBound - leftPrt + 1];
  31. int i = leftPrt;
  32. int j = rightPrt;
  33. int k = 0;
  34. while (i <= mid && j <= rightBound) {
  35. temp[k++] = arr[i] <= arr[j] ? arr[i++] : arr[j++];
  36. }
  37. while (i <= mid) {
  38. temp[k++] = arr[i++];
  39. }
  40. while (j <= rightBound) {
  41. temp[k++] = arr[j++];
  42. }
  43. for (int m = 0; m < temp.length; m++) {
  44. arr[leftPrt + m] = temp[m];
  45. }
  46. }
  47. static void swap(int[] arr,int i,int j) {
  48. int temp = arr[i];
  49. arr[j] = arr[i];
  50. arr[i] = temp;
  51. }
  52. static void print(int[] arr) {
  53. for (int num : arr) {
  54. System.out.print(num + " ");
  55. }
  56. }
  57. }

递归:调用自身,但需要留出跳出去的条件,每次调用都会栈分配栈帧,然后栈帧在函数完成后依次消失。

多路归并排序

非比较类排序

计数排序

桶排序

基数排序