排序算法详解
冒泡排序
原理:重复遍历数组,比较相邻元素,如果顺序错误就交换,将最大元素逐渐"冒泡"到末尾。
function bubbleSort(arr) {
for (let i = 0; i < arr.length; i++) {
for (let j = 0; j < arr.length - 1 - i; j++) {
if (arr[j] > arr[j + 1]) {
[arr[j], arr[j + 1]] = [arr[j + 1], arr[j]];
}
}
}
return arr;
}
选择排序
原理:每次从未排序部分选择最小(或最大)元素,放到已排序序列的末尾。
function selectionSort(arr) {
for (let i = 0; i < arr.length; 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;
}
插入排序
原理:将数组分为已排序和未排序两部分,每次从未排序部分取出一个元素,插入到已排序部分的正确位置。
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;
}
希尔排序
原理:插入排序的改进版,通过比较相距一定间隔的元素来工作,逐渐减小间隔直到1。
function shellSort(arr) {
let gap = Math.floor(arr.length / 2);
while (gap > 0) {
for (let i = gap; i < arr.length; i++) {
let temp = arr[i];
let j = i;
while (j >= gap && arr[j - gap] > temp) {
arr[j] = arr[j - gap];
j -= gap;
}
arr[j] = temp;
}
gap = Math.floor(gap / 2);
}
return arr;
}
归并排序
原理:分治法,将数组递归分成两半,分别排序后再合并。
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) {
const result = [];
while (left.length && right.length) {
result.push(left[0] <= right[0] ? left.shift() : right.shift());
}
return result.concat(left, right);
}
快速排序
原理:分治法,选择一个基准元素,将小于基准的放左边,大于基准的放右边,递归排序左右部分。
function quickSort(arr) {
if (arr.length <= 1) return arr;
const pivot = arr[Math.floor(arr.length / 2)];
const left = [], right = [], equal = [];
for (let num of arr) {
if (num < pivot) left.push(num);
else if (num > pivot) right.push(num);
else equal.push(num);
}
return quickSort(left).concat(equal, quickSort(right));
}
堆排序
原理:利用堆这种数据结构,将数组构建成最大堆,然后反复取出堆顶元素。
function heapSort(arr) {
function heapify(arr, n, i) {
let largest = i;
const left = 2 * i + 1;
const 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);
}
}
for (let i = Math.floor(arr.length / 2) - 1; i >= 0; i--) {
heapify(arr, arr.length, i);
}
for (let i = arr.length - 1; i > 0; i--) {
[arr[0], arr[i]] = [arr[i], arr[0]];
heapify(arr, i, 0);
}
return arr;
}
计数排序
原理:统计每个元素出现的次数,然后按顺序重建数组。适用于整数且范围不大的情况。
function countingSort(arr) {
const max = Math.max(...arr);
const 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;
}
桶排序
原理:将元素分配到有限数量的桶中,每个桶单独排序,最后合并。
function bucketSort(arr, bucketSize = 5) {
if (arr.length === 0) return arr;
const min = Math.min(...arr);
const max = Math.max(...arr);
const bucketCount = Math.floor((max - min) / bucketSize) + 1;
const buckets = Array.from({ length: bucketCount }, () => []);
for (let num of arr) {
buckets[Math.floor((num - min) / bucketSize)].push(num);
}
arr.length = 0;
for (let bucket of buckets) {
insertionSort(bucket);
arr.push(...bucket);
}
return arr;
}
基数排序
原理:按位排序,从最低位到最高位依次排序,使用稳定的排序算法(通常用计数排序)。
function radixSort(arr) {
const max = Math.max(...arr);
const maxDigit = Math.floor(Math.log10(max)) + 1;
for (let digit = 0; digit < maxDigit; digit++) {
const buckets = Array.from({ length: 10 }, () => []);
const radix = 10 ** digit;
for (let num of arr) {
const bucketIndex = Math.floor(num / radix) % 10;
buckets[bucketIndex].push(num);
}
arr = [].concat(...buckets);
}
return arr;
}