Skip to content

基础排序

先来讲讲基础的排序算法。虽然都是基础的算法,但理解其中思路还是非常有必要的。

TIP

在本章里将的所有排序,都是对数组进行排序

1. 选择排序

选择排序实现步骤:

  • 找到数组中最小的那个元素
  • 将它和数组的第 1 个元素交换位置(如果第 1 个元素就是最小元素,那么就和自己交换)
  • 在剩下的元素中找到最小的那个元素
  • 将它与数组的第 2 个元素交换位置
  • ......

如此往复,直到将整个数组排序。

因为算法是在不断地选择剩余元素之中的最小者,所以算法称为选择排序。还有另一个叫法,冒泡排序,因为每次都是将剩余元素之中的最小者 ”冒泡“ 似的排在了前面。

图1:选择排序

TIP

实现数组元素交换

js
function swap(arr, i, j) {
  const temp = arr[i];
  arr[i] = arr[j];
  arr[j] = temp;
}
js
function swap(arr, i, j) {
  const temp = arr[i];
  arr[i] = arr[j];
  arr[j] = temp;
}

// 选择排序
function SelectionSort(arr) {
  const l = arr.length;
  for (let i = 0; i < l; i++) {
    let min = i;
    for (let j = i + 1; j < l; j++) {
      // 查找剩余元素中最小的那一个所在的位置
      if (arr[j] < arr[min]) min = j;
    }
    // 将剩余元素中最小元素和当前位置元素进行交换
    swap(arr, i, min);
  }
}

算法时间复杂度为:在最差情形下共需遍历 (n-1) + (n-2) …+ 1 次,即 (n-1)n/2,算法时间复杂度 O(N2)

2. 插入排序

不知道大家在整理扑克的时候是怎样的,想来通常都是一张一张的,将每一张牌插入到其它已经有序的牌中的适当位置,直到将所有牌整理完成。

  • 将第 2 个元素正确地插入到前面已排序的元素中(此时就只有 1 个元素)

    这里的关键在于如何正确的插入,也很简单,就是从后往前依次与前一个元素进行比较

    • 如果元素小于前一个元素,交换,然后再进行下一轮比较
    • 如果大于或等于,那么就是正确位置了
  • 将第 3 个元素正确地插入到前面已排序的元素中(此时就有了 2 个元素)

  • ......

如此往复,直到将整个数组排序。

按照这样的思路进行排序,每一次都是将元素插入到已经有序的元素组中,因为,这种排序算法称为插入排序。

图2:插入排序

js
function swap(arr, i, j) {
  const temp = arr[i];
  arr[i] = arr[j];
  arr[j] = temp;
}

// 插入排序
function InsertSort(arr) {
  const l = arr.length;
  for (let i = 1; i < l; i++) {
    for (let j = i; j > 0; j--) {
      // 元素与前一个元素进行比较,小于就交换
      if (arr[j] < arr[j - 1]) swap(arr, j, j - 1);
      // 否者就是找到了正确的位置,退出循环
      else break;
    }
  }
}

3. 比较两种排序算法

现在已经实现了两种排序算法,那么到底那种排序算法更快呢?

结论:对于随机排序的无重复主键的数组,插入排序和选择排序的运行时间都是平方级别的,两者之比应该是一个较小的常数。

4. 小结

基础排序还有一种,就是希尔排序,主要思想是使得数组中任意间隔为 h 的元素都是有序的。

虽然这两种算法的时间复杂度都是 O(N2),但是作为排序算法的基础,我们还是很有必要学一学的。

Released under the MIT License.