前言:为什么要学习排序?
排序并不是一个华而不实的学术题目。它像厨房里的切菜技巧,看似简单却决定了后续步骤的效率。掌握基本的排序算法,不仅能读懂别人的代码,也能在面试和性能优化时得心应手。
学习排序的过程,就是把抽象的“有序”概念分解为一系列易于理解的小动作:比较、移动、交换。把一个乱序的数组想象成杂乱的书架:
有些算法会一个一个挑出合适的书放到左边(选择排序),
有些会把新书插进已有的整齐一摞(插入排序),
有些则从左到右不断让更厚的书排到右侧(冒泡排序)。
快速入门:JavaScript 原生 sort
在实际项目中,最快的方法通常是使用语言自带的排序方法。JavaScript 的 Array.prototype.sort 接受一个比较函数,能处理数值与自定义排序:
// 数值排序示例
const arr = [3, 5, 2, 1, 4]
arr.sort((a, b) => b - a)
console.log(arr) // [5,4,3,2,1]
arr.sort() 默认按字符串比较,因此对数值要传比较函数。
冒泡排序:像水中气泡一样上浮
冒泡排序是最直观的排序方法:不断相邻比较,把较大的元素像气泡一样逐步“冒”到右侧。
想象一锅正在加热的水,越大的气泡越容易往上浮,每一次相邻比较,就像一次上浮尝试,最终最大的气泡会被不断推到最上层。
const arr = [5, 3, 2, 1, 4]
function bubbleSort(arr) {
const len = arr.length
for (let n = 0; n < len; n++) {
let swapped = false
for (let i = 0; i < len - 1 - n; i++) {
if (arr[i] > arr[i + 1]) {
[arr[i], arr[i + 1]] = [arr[i + 1], arr[i]]
swapped = true
}
}
if (!swapped) return arr
}
return arr
}
console.log(bubbleSort(arr))
复杂度:平均与最坏 O(n^2),最好 O(n)(若加入提前退出)。
适用:教学与理解细节,或几乎有序的小数组。
选择排序:像挑苹果一样挑最小值
选择排序每一轮从未排序区间挑出最小(或最大)的元素,放到已排序区间末尾。相比冒泡,交换次数更少——每轮最多一次。
将未排序的项目看作一篮苹果,每次挑出最完美的放到一旁排成序列。
const arr = [5, 3, 2, 1, 4]
function selectSort(arr) {
const len = arr.length
for (let i = 0; i < len - 1; i++) {
let minIndex = i
for (let j = i + 1; j < len; j++) {
if (arr[j] < arr[minIndex]) minIndex = j
}
if (minIndex !== i) {
[arr[i], arr[minIndex]] = [arr[minIndex], arr[i]]
}
}
return arr
}
console.log(selectSort(arr))
复杂度:始终 O(n^2),与初始有序性无关。
优缺点:实现简单且交换次数少,适合写入代价高的场景,但总体比较次数仍然很多,不适合大数据集。
插入排序:像打牌整理手牌
插入排序通过维护一个已排序序列,把下一个元素插入到合适的位置。对于小规模或接近有序的数据,效率非常好。
整理手牌时,你会把新摸到的牌从右向左比较并插入到正确位置。
const arr = [5, 3, 2, 1, 4]
function insertSort(arr) {
const len = arr.length
for (let i = 1; i < len; i++) {
const temp = arr[i]
let j = i - 1
while (j >= 0 && arr[j] > temp) {
arr[j + 1] = arr[j]
j--
}
arr[j + 1] = temp
}
return arr
}
console.log(insertSort(arr))
复杂度:平均 / 最坏 O(n^2),最好 O(n)(几乎有序时)。
适用:小数组、部分有序数组;常作为更复杂排序在小规模子问题时的终止条件。
最后
排序算法并不复杂,但它们很好地体现了算法思想的差异:
有的通过不断交换让元素“浮”到正确位置,有的每一轮都明确地选择一个最优解,也有的则在已有秩序中小心地插入新元素。
在学习这些算法的过程中,比结果更重要的是理解它们各自的行为方式——比较发生在什么时候、元素是如何移动的、为什么某些算法在接近有序时会更快。这种对过程的理解,会在之后学习更复杂的算法(如快速排序、归并排序)时持续发挥作用。
对我来说,手写这些排序算法不仅是一次练习,更是一次把“抽象规则”转化为“可执行步骤”的过程。希望这篇文章,也能帮你在排序这条路上建立起属于自己的直觉。