经典排序算法

选择排序

849589-20171015224719590-1433219824
public static void selectionSort(int[] arr) {
  if (arr == null || arr.length < 2) {
    return;
  }

  // 找出 0 ~ n-1 中的最小值的索引
  // 找出 1 ~ n-1 中的最小值的索引
  // 找出 2 ~ n-1 中的最小值的索引
  // 找出 i ~ n-1 中的最小值的索引
  for (int i = 0; i < arr.length; i++) {
    int minValueIndex = i;
    for (int j = i + 1; j < arr.length; j++) {
      minValueIndex = arr[j] < arr[minValueIndex] ? j : minValueIndex;
    }
    // 交换值,将每次最小都放入前面
    swap(arr, i, minValueIndex);
  }
}

private static void swap(int[] arr, int i, int j) {
  int iV = arr[i];
  arr[i] = arr[j];
  arr[j] = iV;
}

@Test
public void testSelectionSort() {
  int[] arr = new int[]{1, 3, 5, 3, 2, 9, 2, 5};
  selectionSort(arr);
  System.out.println(Arrays.toString(arr));
}

时间复杂度:

冒泡排序

9916080-f0605d250bd43468

依次比较相邻的数,并交换,最后将最大的数冒泡到最后,这样循环 n-1 次后,即可排序所有数。

时间复杂度:

  1. 假设数组长度为 n,交换方式是最坏的情况,每次都要交换

  2. 那么交换次数为: n, n-1, n-2, ... 3, 2, 1

  3. 根据等差数列公式,Sn=n*a1+n(n-1)d/2 或者 Sn=n(a1+an)/2,可以得到数据量级n与交换次数之间的关系为 (1/2)*(n^2) -(1/2 -a1)n

  4. 保留最高次项,故冒泡排序的时间复杂度为O(n^2)

插入排序

插入排序之所以叫插入排序,是因为他的排序过程类似扑克游戏斗地主的摸牌的排序过程。

849589-20171015225645277-1151100000

时间复杂度:

归并排序

img

归并排序是时间复杂度为O(n*logn)

递归实现

  1. 让数组arr的L到R位置上有序

  2. 将数组拆分为两半,确认中间位置 (L+R)/2 = M

  3. 让 L到M, M+1到R范围内有序

  4. 左右部分排序成功后,准备一个R-L长度的数组空间,同时遍历左右空间,每次取出一个元素,比较两个两个数字的大小。谁小将谁放额外数组空间中,这样就完成了结果的归并

  5. 就这样这样依次对半拆分排序,直到不能再拆分,再依次归并,即可完成归并排序

复杂度:

也可以使用for循环替代while:


非递归实现

  1. 递归是将数组拆分为两半,而这种方式则是将元素分为固定的k组,整体思想是相同的

  2. k=1:循环数组,因为数组在[0,0]、[1,1]、[2,2]、[3,3]...范围内有序,合并有序数组,让数组在[0,1]、[2,3]、[4,5]...范围内有序

  3. k=2:循环数组,因为数组在[0,1]、[2,3]、[4,5]、[6,7]...范围内有序,合并有序数组,让数组在[0,3]、[4,7]、[8,11]...范围内有序

  4. k=4:循环数组,因为数组在[0,3]、[4,7]、[8,11]...范围内有序,合并有序数组,让数组在[0,7]、[8,15]、[16,23]...范围内有序

  5. k=?:循环数组,因为数组在[i, i+k-1]范围上有序,合并有序数组,让数组在 [i + 2k-1]范围内有序

  6. 如此循环数组,直到k>=arr.length时,即完成了数组的排序

  7. 注意边界不能超过数组的长度

  8. 如此循环,将2个有序的小组,合并为4个有序的小组

  9. 此时让 k=4,按照上述流程继续循环,将4个7有序的小组合并为8个有序的小组

  10. 如此反复,

代码如下:

时间复杂度:

为什么选择、冒泡、插入排序O(n^2)的时间复杂度劣于归并排序O(N * logN)?

因为前者在比较时,将所有的数依次比较,很多比较行为并没有交换,造成了很多无效的操作。

小和问题

假设有个数组, [6, 3, 4, 6, 7]

  1. 第0个元素左侧比该元素小的值的总和为0

  2. 第1个元素左侧比该元素小的值的总和为0

  3. 第2个元素左侧比该元素小的值的总和为3

  4. 第3个元素左侧比该元素小的值的总和为3+4=7

  5. 第4个元素左侧比该元素小的值的总和为6+3+4+6=19

  6. 故最小和为 3 + 7 + 19 = 29

要求复杂度为 O(N*logN)

快速排序

快排的步骤:

  1. 以数组中最后一个数字P为边界,将数组内的小于等于P的数放在左侧,大于P的数放右侧

  2. 当前数 <= P,当前数在 <= 区,当前数与下一个数交换,<= 区扩容1,当前数指针向后移动一次

  3. 当前数 > P,当前数指针向后移动

最后更新于

这有帮助吗?