位置: 文档库 > JavaScript > 整理常用的JS排序算法

整理常用的JS排序算法

ZenithBlade56 上传于 2022-12-20 23:09

在JavaScript开发中,排序算法是处理数据时不可或缺的基础技能。无论是前端展示数据、后端处理业务逻辑,还是算法竞赛或面试场景,掌握常见排序算法的实现原理和优化技巧都能显著提升代码效率。本文将系统梳理JavaScript中常用的排序算法,涵盖冒泡排序、选择排序、插入排序、快速排序、归并排序、堆排序等经典方法,通过代码实现、时间复杂度分析和适用场景对比,帮助开发者构建完整的排序知识体系。

一、基础排序算法:理解排序的本质

基础排序算法通常以直观的逻辑实现,适合小规模数据或教学场景。它们的核心思想是通过比较和交换元素位置达到有序状态。

1. 冒泡排序(Bubble Sort)

冒泡排序通过多次遍历数组,每次比较相邻元素,将较大的值"冒泡"到数组末尾。其时间复杂度为O(n²),空间复杂度为O(1),是稳定性排序算法。

function bubbleSort(arr) {
  const len = arr.length;
  for (let i = 0; i  arr[j + 1]) {
        [arr[j], arr[j + 1]] = [arr[j + 1], arr[j]]; // ES6解构交换
      }
    }
  }
  return arr;
}
// 优化版:增加标志位减少无效遍历
function optimizedBubbleSort(arr) {
  let swapped = true;
  for (let i = 0; i  arr[j + 1]) {
        [arr[j], arr[j + 1]] = [arr[j + 1], arr[j]];
        swapped = true;
      }
    }
  }
  return arr;
}

优化后的冒泡排序在最好情况下(已排序数组)时间复杂度降至O(n),但平均和最坏情况仍为O(n²)。

2. 选择排序(Selection Sort)

选择排序每次遍历找到未排序部分的最小值,与当前位置交换。其时间复杂度恒为O(n²),但交换次数少于冒泡排序。

function selectionSort(arr) {
  const len = arr.length;
  for (let i = 0; i 

选择排序的不稳定性体现在相同值的元素可能因交换改变相对顺序。

3. 插入排序(Insertion Sort)

插入排序将数组分为已排序和未排序两部分,逐个将未排序元素插入到已排序部分的正确位置。对近乎有序的数组效率极高。

function insertionSort(arr) {
  const len = arr.length;
  for (let i = 1; i = 0 && arr[j] > current) {
      arr[j + 1] = arr[j];
      j--;
    }
    arr[j + 1] = current;
  }
  return arr;
}

插入排序的时间复杂度为O(n²),但最好情况下(已排序)可达O(n),且空间复杂度为O(1)。

二、高效排序算法:分治思想的实践

高效排序算法通过分治策略将问题分解为子问题,显著降低时间复杂度,适合大规模数据排序。

1. 快速排序(Quick Sort)

快速排序采用"分而治之"策略,选择基准值(pivot)将数组分为小于和大于基准的两部分,递归排序子数组。平均时间复杂度为O(n log n),最坏情况为O(n²)。

function quickSort(arr) {
  if (arr.length = right) return;
  
  const pivotIndex = partition(arr, left, right);
  quickSortInPlace(arr, left, pivotIndex - 1);
  quickSortInPlace(arr, pivotIndex + 1, right);
  
  return arr;
}

function partition(arr, left, right) {
  const pivot = arr[right];
  let i = left;
  
  for (let j = left; j 

快速排序的效率高度依赖基准值的选择,三数取中法可优化最坏情况。

2. 归并排序(Merge Sort)

归并排序将数组递归分为两半,分别排序后合并。其时间复杂度稳定为O(n log n),但需要O(n)的额外空间。

function mergeSort(arr) {
  if (arr.length 

归并排序的稳定性使其适合处理包含复杂对象的数组。

3. 堆排序(Heap Sort)

堆排序利用二叉堆数据结构,先构建最大堆,然后反复取出堆顶元素并调整堆。时间复杂度为O(n log n),空间复杂度为O(1)。

function heapSort(arr) {
  const len = arr.length;
  
  // 构建最大堆
  for (let i = Math.floor(len / 2) - 1; i >= 0; i--) {
    heapify(arr, len, i);
  }
  
  // 逐个提取元素
  for (let i = len - 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;
  const left = 2 * i + 1;
  const right = 2 * i + 2;
  
  if (left  arr[largest]) {
    largest = left;
  }
  
  if (right  arr[largest]) {
    largest = right;
  }
  
  if (largest !== i) {
    [arr[i], arr[largest]] = [arr[largest], arr[i]];
    heapify(arr, n, largest);
  }
}

堆排序的不稳定性体现在相同值的元素可能因堆调整改变顺序。

三、特殊场景排序算法

1. 计数排序(Counting Sort)

计数排序适用于整数且范围较小的场景,通过统计每个元素的出现次数实现线性时间排序。

function countingSort(arr) {
  const max = Math.max(...arr);
  const min = Math.min(...arr);
  const count = Array(max - min + 1).fill(0);
  const output = Array(arr.length).fill(0);
  
  arr.forEach(num => count[num - min]++);
  
  for (let i = 1; i = 0; i--) {
    output[count[arr[i] - min] - 1] = arr[i];
    count[arr[i] - min]--;
  }
  
  return output;
}

计数排序的时间复杂度为O(n + k),其中k为数据范围。

2. 桶排序(Bucket Sort)

桶排序将数据分到有限数量的桶中,每个桶单独排序后合并。适用于均匀分布的浮点数。

function bucketSort(arr, bucketSize = 5) {
  if (arr.length === 0) return arr;
  
  let min = Math.min(...arr);
  let max = Math.max(...arr);
  
  const bucketCount = Math.floor((max - min) / bucketSize) + 1;
  const buckets = Array.from({ length: bucketCount }, () => []);
  
  arr.forEach(num => {
    const bucketIndex = Math.floor((num - min) / bucketSize);
    buckets[bucketIndex].push(num);
  });
  
  return buckets.flatMap(bucket => {
    if (bucket.length > 1) {
      return insertionSort(bucket); // 桶内使用插入排序
    }
    return bucket;
  });
}

四、JavaScript内置排序方法

JavaScript的Array.prototype.sort()方法默认按Unicode码点排序,可通过比较函数自定义排序逻辑。

const numbers = [3, 1, 4, 1, 5];
// 升序排序
numbers.sort((a, b) => a - b);
// 降序排序
numbers.sort((a, b) => b - a);
// 对象数组排序
const users = [{name: 'John', age: 25}, {name: 'Jane', age: 22}];
users.sort((a, b) => a.age - b.age);

不同浏览器对sort()的实现可能不同(如V8使用Timsort变种),但均保证O(n log n)的平均时间复杂度。

五、算法选择指南

选择排序算法需考虑数据规模、数据特征和性能要求:

  • 小规模数据(n :插入排序或选择排序
  • 近乎有序数据:插入排序
  • 大规模数据:快速排序或归并排序
  • 内存受限环境:堆排序
  • 整数小范围数据:计数排序
  • 稳定排序需求:归并排序或插入排序

六、性能优化技巧

  1. 对小规模子数组使用插入排序(快速排序优化)
  2. 三数取中法选择基准值(快速排序优化)
  3. 避免递归使用栈溢出(改用迭代实现)
  4. 利用TypedArray处理数值数据
  5. Web Worker多线程处理超大规模数据

关键词:JavaScript排序算法、冒泡排序、选择排序、插入排序、快速排序、归并排序、堆排序、计数排序、桶排序、时间复杂度、空间复杂度、稳定性排序

简介:本文系统梳理JavaScript中常用的排序算法,涵盖冒泡排序、选择排序、插入排序等基础算法,以及快速排序、归并排序、堆排序等高效算法,详细分析各算法的实现原理、时间复杂度、空间复杂度和稳定性,并提供代码实现和优化建议,帮助开发者根据不同场景选择最适合的排序方案。

《整理常用的JS排序算法.doc》
将本文的Word文档下载到电脑,方便收藏和打印
推荐度:
点击下载文档