前端涨薪功法: 深入解析归并排序和快速排序

327 阅读2分钟

归并排序是一种高效、稳定的排序算法,它的基本原理是分治法(Divide and Conquer)。这意味着它将一个大问题分解成几个较小的问题,然后分别解决这些小问题,最后将所有小问题的解决方案合并成最终解决方案。

下面是归并排序的基本步骤:

  1. 将数组分成两半。
  2. 对每一半进行归并排序。
  3. 合并两个已排序的数组。

R.gif

我们将从一个简单的示例开始,然后逐步深入到算法实现的细节。

示例:

假设我们有一个整数数组 [5, 3, 8, 4, 2]。我们可以将这个问题分解成以下步骤:

  1. 将数组分成两半:[5, 3][8, 4, 2]
  2. 对每一半进行归并排序:
    • [5, 3] 进行归并排序:得到 [3, 5]
    • [8, 4, 2] 进行归并排序:得到 [2, 4, 8]
  3. 合并已排序的数组:[3, 5][2, 4, 8] 合并为 [2, 3, 4, 5, 8]

现在让我们开始实现这个算法。

实现:

首先,我们需要一个函数来分割数组。这个函数接受一个数组和两个索引作为参数,返回从第一个索引到第二个索引(包括)的元素组成的子数组。

function splitArray(array, start, end) {
  return array.slice(start, end + 1);
}

接下来,我们需要一个函数来合并两个已排序的数组。这个函数会从两个数组中选择最小的元素,然后将它们连接在一起。

function merge(left, right) {
  let result = [];
  let leftIndex = 0;
  let rightIndex = 0;
  
  while (leftIndex < left.length && rightIndex < right.length) {
    if (left[leftIndex] < right[rightIndex]) {
      result.push(left[leftIndex]);
      leftIndex++;
    } else {
      result.push(right[rightIndex]);
      rightIndex++;
    }
  }
  
  return result.concat(left.slice(leftIndex)).concat(right.slice(rightIndex));
}

最后,我们可以实现归并排序函数。这个函数接受一个数组作为输入,返回一个新的已排序数组。

function mergeSort(array) {
  if (array.length <= 1) {
    return array;
  }
  
  const middle = Math.floor(array.length / 2);
  const left = splitArray(array, 0, middle - 1);
  const right = splitArray(array, middle, array.length - 1);
  
  return merge(mergeSort(left), mergeSort(right));
}

现在我们可以使用 mergeSort 函数对数组进行排序:

const unsortedArray = [10,6,7,13,9,4,2]; 
const sortedArray = mergeSort(unsortedArray); 
console.log(sortedArray); // 输出:[1,2,3,4,6,7,9,10]

image.png

归并排序是一种非常有用的排序算法,因为它是稳定的(相同的元素保持原来的顺序)且时间复杂度为 O(n log n),在很多情况下都是一个很好的选择。

快速排序

快速排序基于大小的比较,分成较大区域和较小区域,可以进一步提升排序算法性能

具体实现如下

const quickSort = (arr) => {

  if (arr.length <= 1) {
    return arr;
  }

  const pivotIndex = Math.floor(Math.random() * arr.length);
  const pivot = arr[pivotIndex];
  const left = [];
  const right = [];

  for (let i = 0; i < arr.length; i++) {
    if (i === pivotIndex) {
      continue;
    }

    if (arr[i] < pivot) {
      left.push(arr[i]);
    } else {
      right.push(arr[i]);
    }
  }

  return [...quickSort(left), pivot, ...quickSort(right)];
}

测试一下性能

const arr = [];
for (let i = 0; i < 10000; i++) {
  arr.push(Math.random() * 10000);
}

const start = performance.now();
quickSort(arr);
const end = performance.now();

console.log(`Execution time: ${end - start} milliseconds`);

10000条数据排序,仅用了 20ms,还是很快的

image.png

算法复杂度分析,最大的缺点不稳定

image.png