返回

Array.prototype.sort 的排序原理以及优缺点

前端

Array.prototype.sort 的内部原理

Array.prototype.sort() 方法对数组中的元素进行排序,默认情况下按照元素的 Unicode 编码值进行升序排序。但我们也可以通过提供一个比较函数来指定自定义的排序规则。

当我们调用 Array.prototype.sort() 方法时,它会执行以下步骤:

  1. 如果未提供比较函数,则使用默认的比较函数。默认比较函数将两个元素的 Unicode 编码值进行比较,并返回一个整数:

    • 如果第一个元素的 Unicode 编码值小于第二个元素的 Unicode 编码值,则返回 -1。
    • 如果第一个元素的 Unicode 编码值等于第二个元素的 Unicode 编码值,则返回 0。
    • 如果第一个元素的 Unicode 编码值大于第二个元素的 Unicode 编码值,则返回 1。
  2. 如果提供了比较函数,则使用该比较函数将两个元素进行比较,并返回一个整数:

    • 如果比较函数返回负数,则第一个元素排在第二个元素之前。
    • 如果比较函数返回 0,则两个元素的顺序不变。
    • 如果比较函数返回正数,则第二个元素排在第一个元素之前。
  3. 重复步骤 1 或步骤 2,直到数组中的所有元素都已排序。

Array.prototype.sort 所采用的排序算法

Array.prototype.sort() 方法通常使用快速排序算法来对数组进行排序。快速排序是一种分治排序算法,它将数组划分为较小的子数组,然后递归地对每个子数组进行排序,最后将排序后的子数组合并成一个排序后的数组。

快速排序的时间复杂度为 O(n log n)(平均情况)或 O(n^2)(最坏情况),空间复杂度为 O(log n)(平均情况)或 O(n)(最坏情况)。

比较函数的使用方式

我们可以通过提供一个比较函数来指定自定义的排序规则。比较函数是一个函数,它接收两个参数,并返回一个整数:

  • 如果第一个元素应该排在第二个元素之前,则返回负数。
  • 如果第一个元素应该与第二个元素保持相同顺序,则返回 0。
  • 如果第二个元素应该排在第一个元素之前,则返回正数。

例如,以下比较函数将数组中的数字按降序排列:

function compareNumbersDescending(a, b) {
  return b - a;
}

const numbers = [1, 3, 2, 5, 4];
numbers.sort(compareNumbersDescending);
console.log(numbers); // [5, 4, 3, 2, 1]

排序的稳定性

Array.prototype.sort() 方法是稳定的,这意味着对于两个相等元素,它们的相对顺序在排序后保持不变。

例如,以下数组包含两个相等元素 "2":

const numbers = [1, 2, 3, 2, 5, 4];

如果我们使用默认的比较函数对数组进行排序,则结果如下:

numbers.sort();
console.log(numbers); // [1, 2, 2, 3, 4, 5]

可以看到,两个相等元素 "2" 保持了它们的相对顺序。

时间复杂度和空间复杂度

Array.prototype.sort() 方法的时间复杂度为 O(n log n)(平均情况)或 O(n^2)(最坏情况),空间复杂度为 O(log n)(平均情况)或 O(n)(最坏情况)。

  • 时间复杂度:

    • 平均情况下,Array.prototype.sort() 方法的时间复杂度为 O(n log n)。这是因为快速排序算法在平均情况下具有 O(n log n) 的时间复杂度。
    • 最坏情况下,Array.prototype.sort() 方法的时间复杂度为 O(n^2)。这是因为当数组已经排序好或逆序时,快速排序算法会退化成冒泡排序,而冒泡排序的时间复杂度为 O(n^2)。
  • 空间复杂度:

    • 平均情况下,Array.prototype.sort() 方法的空间复杂度为 O(log n)。这是因为快速排序算法在平均情况下具有 O(log n) 的空间复杂度。
    • 最坏情况下,Array.prototype.sort() 方法的空间复杂度为 O(n)。这是因为当数组已经排序好或逆序时,快速排序算法会退化成冒泡排序,而冒泡排序的空间复杂度为 O(n)。