返回

7 大常用排序算法深度解析:从代码到时间、空间复杂度,一一掌握!

前端

7 种 JavaScript 排序算法:特性、优势和示例

排序算法是任何编程工具箱的重要组成部分,它们允许我们根据特定的顺序排列数据。在 JavaScript 中,有多种排序算法可供选择,每种算法都有其独特的特性和优势。本文将深入探讨 7 种常用的 JavaScript 排序算法,为您提供全面了解它们的特性、时间复杂度和代码示例。

1. 冒泡排序

冒泡排序是一种简单的排序算法,它通过比较相邻元素并交换它们的位置来工作。它一遍又一遍地遍历数组,直到所有元素按序排列。虽然冒泡排序易于实现,但它对大型数据集效率低下。

时间复杂度:O(n^2)
空间复杂度:O(1)

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

  return array;
}

2. 快速排序

快速排序是一种高效的排序算法,它通过选择一个枢轴元素将数组分成两部分,然后递归地对这两部分进行排序。快速排序的时间复杂度通常为 O(n log n),但对于某些输入,它可能退化为 O(n^2)。

时间复杂度:O(n log n) 平均,O(n^2) 最坏情况
空间复杂度:O(log n)

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

  const pivot = array[Math.floor(array.length / 2)];
  const left = [];
  const right = [];

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

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

3. 选择排序

选择排序是一种简单的排序算法,它通过找到数组中最小的元素并将其放在第一个位置,然后找到数组中第二小的元素并将其放在第二个位置,以此类推,来工作。选择排序比冒泡排序稍微高效,但仍然不适合大型数据集。

时间复杂度:O(n^2)
空间复杂度:O(1)

function selectionSort(array) {
  for (let i = 0; i < array.length; i++) {
    let minIndex = i;
    for (let j = i + 1; j < array.length; j++) {
      if (array[j] < array[minIndex]) {
        minIndex = j;
      }
    }

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

  return array;
}

4. 希尔排序

希尔排序是一种改进的插入排序,它通过将数组分成更小的子数组并对每个子数组进行插入排序来工作。希尔排序通常比插入排序快,但仍比快速排序慢。

时间复杂度:O(n log n) 最佳情况下,O(n^2) 最坏情况下
空间复杂度:O(1)

function shellSort(array) {
  const gaps = [5, 3, 1];

  for (const gap of gaps) {
    for (let i = gap; i < array.length; i++) {
      const current = array[i];
      let j = i - gap;
      while (j >= 0 && array[j] > current) {
        array[j + gap] = array[j];
        j -= gap;
      }
      array[j + gap] = current;
    }
  }

  return array;
}

5. 插入排序

插入排序是一种简单的排序算法,它通过将数组中的元素逐个插入到正确的位置来工作。插入排序对于小数据集非常高效,但对于大型数据集来说速度较慢。

时间复杂度:O(n^2)
空间复杂度:O(1)

function insertionSort(array) {
  for (let i = 1; i < array.length; i++) {
    const current = array[i];
    let j = i - 1;
    while (j >= 0 && array[j] > current) {
      array[j + 1] = array[j];
      j--;
    }
    array[j + 1] = current;
  }

  return array;
}

6. 归并排序

归并排序是一种稳定的排序算法,它通过将数组分成更小的子数组并对每个子数组进行排序,然后合并这些子数组来工作。归并排序的时间复杂度始终为 O(n log n),使其成为大型数据集的理想选择。

时间复杂度:O(n log n)
空间复杂度:O(n)

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

  const middle = Math.floor(array.length / 2);
  const left = mergeSort(array.slice(0, middle));
  const right = mergeSort(array.slice(middle));

  return merge(left, right);
}

function merge(left, right) {
  const merged = [];
  let leftIndex = 0;
  let rightIndex = 0;

  while (leftIndex < left.length && rightIndex < right.length) {
    if (left[leftIndex] <= right[rightIndex]) {
      merged.push(left[leftIndex]);
      leftIndex++;
    } else {
      merged.push(right[rightIndex]);
      rightIndex++;
    }
  }

  return [...merged, ...left.slice(leftIndex), ...right.slice(rightIndex)];
}

7. 堆排序

堆排序是一种不稳定的排序算法,它通过将数组构建成一个二叉堆,然后从堆中依次删除元素来工作。堆排序的时间复杂度始终为 O(n log n),使其成为大型数据集的另一种选择。

时间复杂度:O(n log n)
空间复杂度:O(1)

function heapSort(array) {
  buildMaxHeap(array);

  for (let i = array.length - 1; i > 0; i--) {
    [array[0], array[i]] = [array[i], array[0]];
    maxHeapify(array, 0, i);
  }

  return array;
}

function buildMaxHeap(array) {
  for (let i = Math.floor(array.length / 2) - 1; i >= 0; i--) {
    maxHeapify(array, i, array.length);
  }
}

function maxHeapify(array, index, heapSize) {
  const left = 2 * index + 1;
  const right = 2 * index + 2;
  let largest = index;

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

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

  if (largest !== index) {
    [array[index], array[largest]] = [array[largest], array[index]];
    maxHeapify(array, largest, heapSize);
  }
}

结论

在本文中,我们探讨了 7 种常用的 JavaScript 排序算法:冒泡排序、快速排序、选择排序、希尔排序、插入排序、归并排序和堆排序。每种算法都有其独特的优点和缺点,在选择特定算法时考虑数据规模、排序速度和稳定性要求非常重要。通过了解这些排序算法的工作原理、时间复杂度和代码示例,您可以为您的 JavaScript 项目选择最合适的排序解决方案。

常见问题解答

1. 哪种排序算法最适合大型数据集?
对于大型数据集,归并排序和堆排序是时间复杂度最优(O(n log n))的算法。

2. 哪种排序算法是稳定的?
归并排序是稳定的算法,这意味着它保持相等元素的原始顺序。

3. 哪种排序算法最简单实现?
冒泡排序和选择排序是最简单的算法,它们易于理解和实现。

4. 哪种排序算法最通用?