前言
排序算法是笔试中的高频考点,掌握常见的排序算法不仅能帮助你在面试中脱颖而出,还能提升你的编程思维。本文用 JavaScript 实现常见的排序算法,并详细分析它们的时间复杂度和空间复杂度,助你轻松应对技术面试!
一、基础排序算法
1.1 冒泡排序(Bubble Sort)
实现流程:
- 比较相邻元素,如果顺序错误就交换。
- 对每一对相邻元素重复步骤 1。
- 重复以上步骤,直到没有需要交换的元素。
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)
实现流程:
- 找到数组中最小的元素,将其与第一个元素交换。
- 在剩余元素中找到最小的元素,与第二个元素交换。
- 重复以上步骤,直到排序完成。
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)
实现流程:
- 从第二个元素开始,将其与前面的元素比较。
- 如果前面的元素更大,则将其向后移动。
- 重复以上步骤,直到找到合适的位置插入。
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)
实现流程:
- 选择一个基准元素(通常为第一个或最后一个元素)。
- 将数组分为两部分,小于基准的放在左边,大于基准的放在右边。
- 递归地对左右两部分进行排序。
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)
实现流程:
- 将数组分成两半,递归地对每一半进行排序。
- 将排序后的两半合并成一个有序数组。
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)
实现流程:
- 构建一个最大堆(或最小堆)。
- 将堆顶元素与最后一个元素交换,并调整堆。
- 重复以上步骤,直到堆为空。
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)
实现流程:
- 选择一个增量序列,将数组分为若干子序列。
- 对每个子序列进行插入排序。
- 逐步缩小增量,重复以上步骤。
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)
实现流程:
- 统计数组中每个元素出现的次数。
- 根据统计结果将元素放回数组。
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) |
掌握这些排序算法,不仅能帮助你在笔试和面试中脱颖而出,还能提升你的编程能力。赶紧收藏起来,动手实践吧!