笔试中最常见的几个排序算法!面试官:你不会?

377 阅读4分钟

前言

排序算法是笔试中的高频考点,掌握常见的排序算法不仅能帮助你在面试中脱颖而出,还能提升你的编程思维。本文用 JavaScript 实现常见的排序算法,并详细分析它们的时间复杂度和空间复杂度,助你轻松应对技术面试!


一、基础排序算法

1.1 冒泡排序(Bubble Sort)

实现流程

  1. 比较相邻元素,如果顺序错误就交换。
  2. 对每一对相邻元素重复步骤 1。
  3. 重复以上步骤,直到没有需要交换的元素。

JavaScript 实现

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

复杂度分析

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

1.2 选择排序(Selection Sort)

实现流程

  1. 找到数组中最小的元素,将其与第一个元素交换。
  2. 在剩余元素中找到最小的元素,与第二个元素交换。
  3. 重复以上步骤,直到排序完成。

JavaScript 实现

function selectionSort(arr) {
  let n = arr.length;
  for (let i = 0; i < n - 1; i++) {
    let minIndex = i;
    for (let j = i + 1; j < n; j++) {
      if (arr[j] < arr[minIndex]) {
        minIndex = j;
      }
    }
    [arr[i], arr[minIndex]] = [arr[minIndex], arr[i]]; // 交换
  }
  return arr;
}

复杂度分析

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

1.3 插入排序(Insertion Sort)

实现流程

  1. 从第二个元素开始,将其与前面的元素比较。
  2. 如果前面的元素更大,则将其向后移动。
  3. 重复以上步骤,直到找到合适的位置插入。

JavaScript 实现

function insertionSort(arr) {
  let n = arr.length;
  for (let i = 1; i < n; 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²)
  • 空间复杂度:O(1)

二、高级排序算法

2.1 快速排序(Quick Sort)

实现流程

  1. 选择一个基准元素(通常为第一个或最后一个元素)。
  2. 将数组分为两部分,小于基准的放在左边,大于基准的放在右边。
  3. 递归地对左右两部分进行排序。

JavaScript 实现

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), pivot, ...quickSort(right)];
}

复杂度分析

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

2.2 归并排序(Merge Sort)

实现流程

  1. 将数组分成两半,递归地对每一半进行排序。
  2. 将排序后的两半合并成一个有序数组。

JavaScript 实现

function mergeSort(arr) {
  if (arr.length <= 1) return arr;
  const mid = Math.floor(arr.length / 2);
  const left = mergeSort(arr.slice(0, mid));
  const right = mergeSort(arr.slice(mid));
  return merge(left, right);
}

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

复杂度分析

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

2.3 堆排序(Heap Sort)

实现流程

  1. 构建一个最大堆(或最小堆)。
  2. 将堆顶元素与最后一个元素交换,并调整堆。
  3. 重复以上步骤,直到堆为空。

JavaScript 实现

function heapSort(arr) {
  let n = arr.length;
  for (let i = Math.floor(n / 2) - 1; i >= 0; i--) {
    heapify(arr, n, i);
  }
  for (let i = n - 1; i > 0; i--) {
    [arr[0], arr[i]] = [arr[i], arr[0]]; // 交换
    heapify(arr, i, 0);
  }
  return arr;
}

function heapify(arr, n, i) {
  let largest = i;
  let left = 2 * i + 1;
  let right = 2 * i + 2;
  if (left < n && arr[left] > arr[largest]) {
    largest = left;
  }
  if (right < n && arr[right] > arr[largest]) {
    largest = right;
  }
  if (largest !== i) {
    [arr[i], arr[largest]] = [arr[largest], arr[i]]; // 交换
    heapify(arr, n, largest);
  }
}

复杂度分析

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

三、其他常见排序算法

3.1 希尔排序(Shell Sort)

实现流程

  1. 选择一个增量序列,将数组分为若干子序列。
  2. 对每个子序列进行插入排序。
  3. 逐步缩小增量,重复以上步骤。

JavaScript 实现

function shellSort(arr) {
  let n = arr.length;
  for (let gap = Math.floor(n / 2); gap > 0; gap = Math.floor(gap / 2)) {
    for (let i = gap; i < n; i++) {
      let temp = arr[i];
      let j;
      for (j = i; j >= gap && arr[j - gap] > temp; j -= gap) {
        arr[j] = arr[j - gap];
      }
      arr[j] = temp;
    }
  }
  return arr;
}

复杂度分析

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

3.2 计数排序(Counting Sort)

实现流程

  1. 统计数组中每个元素出现的次数。
  2. 根据统计结果将元素放回数组。

JavaScript 实现

function countingSort(arr) {
  let max = Math.max(...arr);
  let count = new Array(max + 1).fill(0);
  for (let num of arr) {
    count[num]++;
  }
  let index = 0;
  for (let i = 0; i <= max; i++) {
    while (count[i] > 0) {
      arr[index++] = i;
      count[i]--;
    }
  }
  return arr;
}

复杂度分析

  • 时间复杂度:O(n + k)(k 为最大值)
  • 空间复杂度:O(k)

四、总结

排序算法时间复杂度(平均)时间复杂度(最坏)空间复杂度
冒泡排序O(n²)O(n²)O(1)
选择排序O(n²)O(n²)O(1)
插入排序O(n²)O(n²)O(1)
快速排序O(n log n)O(n²)O(log n)
归并排序O(n log n)O(n log n)O(n)
堆排序O(n log n)O(n log n)O(1)
希尔排序O(n log² n)O(n²)O(1)
计数排序O(n + k)O(n + k)O(k)

掌握这些排序算法,不仅能帮助你在笔试和面试中脱颖而出,还能提升你的编程能力。赶紧收藏起来,动手实践吧!