前端算法: 各种排序 (冒泡排序,选择排序,插入排序...)

5 阅读3分钟

排序算法详解

冒泡排序

原理:重复遍历数组,比较相邻元素,如果顺序错误就交换,将最大元素逐渐"冒泡"到末尾。

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;
}