返回

JS排序:效率最优,代码最美的九种排序算法详细解析

前端

前言

排序是数据结构算法中很常见的一种操作,在前端开发中也有不少应用场景,比如对数据进行排序、对数组进行排序、对对象进行排序等。

本文将介绍 JS 中常见的 9 种排序算法,包括快速排序、冒泡排序、选择排序、插入排序、希尔排序、堆排序、归并排序、计数排序和桶排序。我们通过对这些算法进行详细解析,让您不仅掌握排序算法,还能欣赏它们的优美代码。

一、快速排序

快速排序是一种分治排序算法,它是通过选取一个基准元素,然后将数组分成两部分,一部分包含所有小于基准元素的元素,另一部分包含所有大于基准元素的元素。然后分别对这两部分进行快速排序。这种方法的时间复杂度为 O(n log n)。

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

  let pivot = arr[0];
  let left = [];
  let right = [];

  for (let i = 1; i < arr.length; i++) {
    if (arr[i] < pivot) {
      left.push(arr[i]);
    } else {
      right.push(arr[i]);
    }
  }

  return quickSort(left).concat(pivot, quickSort(right));
}

二、冒泡排序

冒泡排序是一种简单的排序算法,它通过不断比较相邻元素,将较大的元素向后移动,较小的元素向前移动,直到数组有序。这种方法的时间复杂度为 O(n^2)。

function bubbleSort(arr) {
  for (let i = 0; i < arr.length - 1; i++) {
    for (let j = 0; j < arr.length - i - 1; j++) {
      if (arr[j] > arr[j + 1]) {
        [arr[j], arr[j + 1]] = [arr[j + 1], arr[j]];
      }
    }
  }

  return arr;
}

三、选择排序

选择排序是一种简单的排序算法,它通过不断找到数组中最小(或最大)的元素,然后将其放到数组的开头(或结尾),直到数组有序。这种方法的时间复杂度为 O(n^2)。

function selectionSort(arr) {
  for (let i = 0; i < arr.length - 1; i++) {
    let minIndex = i;

    for (let j = i + 1; j < arr.length; j++) {
      if (arr[j] < arr[minIndex]) {
        minIndex = j;
      }
    }

    [arr[i], arr[minIndex]] = [arr[minIndex], arr[i]];
  }

  return arr;
}

四、插入排序

插入排序是一种简单的排序算法,它通过不断将一个元素插入到已经排好序的子数组中,直到数组有序。这种方法的时间复杂度为 O(n^2)。

function insertionSort(arr) {
  for (let i = 1; i < arr.length; i++) {
    let key = arr[i];
    let j = i - 1;

    while (j >= 0 && arr[j] > key) {
      arr[j + 1] = arr[j];
      j--;
    }

    arr[j + 1] = key;
  }

  return arr;
}

五、希尔排序

希尔排序是一种改进的插入排序,它通过将数组分为多个子数组,然后分别对每个子数组进行插入排序,最后合并所有子数组。这种方法的时间复杂度为 O(n log n)。

function shellSort(arr) {
  let gap = Math.floor(arr.length / 2);

  while (gap > 0) {
    for (let i = gap; i < arr.length; i++) {
      let key = arr[i];
      let j = i - gap;

      while (j >= 0 && arr[j] > key) {
        arr[j + gap] = arr[j];
        j -= gap;
      }

      arr[j + gap] = key;
    }

    gap = Math.floor(gap / 2);
  }

  return arr;
}

六、堆排序

堆排序是一种基于堆数据结构的排序算法,它通过将数组构建成一个堆,然后不断从堆中取出最大的元素,直到数组有序。这种方法的时间复杂度为 O(n log n)。

function heapSort(arr) {
  let heapSize = arr.length;

  for (let i = Math.floor(heapSize / 2) - 1; i >= 0; i--) {
    heapify(arr, heapSize, i);
  }

  while (heapSize > 1) {
    [arr[0], arr[heapSize - 1]] = [arr[heapSize - 1], arr[0]];
    heapSize--;
    heapify(arr, heapSize, 0);
  }

  return arr;
}

function heapify(arr, heapSize, i) {
  let largest = i;
  let left = 2 * i + 1;
  let right = 2 * i + 2;

  if (left < heapSize && arr[left] > arr[largest]) {
    largest = left;
  }

  if (right < heapSize && arr[right] > arr[largest]) {
    largest = right;
  }

  if (largest !== i) {
    [arr[i], arr[largest]] = [arr[largest], arr[i]];
    heapify(arr, heapSize, largest);
  }
}

七、归并排序

归并排序是一种分治排序算法,它通过将数组分为两部分,分别对这两部分进行归并排序,然后合并两部分的结果。这种方法的时间复杂度为 O(n log n)。

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

  let mid = Math.floor(arr.length / 2);
  let left = mergeSort(arr.slice(0, mid));
  let right = mergeSort(arr.slice(mid));

  return merge(left, right);
}

function merge(left, right) {
  let merged = [];
  let i = 0;
  let j = 0;

  while (i < left.length && j < right.length) {
    if (left[i] < right[j]) {
      merged.push(left[i]);
      i++;
    } else {
      merged.push(right[j]);
      j++;
    }
  }

  while (i < left.length) {
    merged.push(left[i]);
    i++;
  }

  while (j < right.length) {
    merged.push(right[j]);
    j++;
  }

  return merged;
}

八、计数排序

计数排序是一种非比较排序算法,它通过计算每个元素出现的次数,然后根据这些次数将元素放到正确的位置。这种方法的时间复杂度为 O(n + k),其中 k 是数组中元素的最大值。

function countingSort(arr, k) {
  let n = arr.length;
  let output = new Array(n);
  let count = new Array(k + 1);

  for (let i = 0; i <= k; i++) {
    count[i] = 0;
  }

  for (let i = 0; i < n; i++) {
    count[arr[i]]++;
  }

  for (let i = 1; i <= k; i++) {
    count[