导读:本期聚焦于坚哥创作的《js如何实现数组快速排序?3种快速排序算法实现方案分享》,敬请观看详情。快速排序是前端面试和实际开发中最常被问到的排序算法之一。本文围绕JavaScript实现数组快速排序展开,详细讲解三种不同的实现方案,包括基于额外空间的简单实现、原地分区的经典实现以及三路快排优化方案。文章从快速排序的分治思想讲起,逐步分析每种写法的核心逻辑、代码细节和性能差异,并对比不同方案在时间复杂度、空间占用上的表现。无论是准备面试还是优化项目中的排序逻辑,读完本文都能对js快速排序有更扎实的理解,掌握适合不同场景的实现方式。

快速排序是一种基于分治思想的经典排序算法,平均时间复杂度为O(n log n),在大多数场景下比冒泡排序、插入排序要快得多。在JavaScript中实现快速排序有多种思路,有的写法简单易懂,有的则更贴近算法本质、性能更优。本文分享三种常见的实现方案,并逐一分析它们的优缺点和适用场景。

js如何实现数组快速排序?3种快速排序算法实现方案分享

方案一:基于额外空间的简易实现

这是最常见、也最容易理解的写法,尤其适合初学者和面试白板题。核心思路是:选取数组中间或任意一个元素作为基准值,然后遍历数组,把小于基准的元素放进左数组,大于等于基准的元素放进右数组,最后对左右两个子数组递归执行同样的操作,再把结果拼接起来。

这种写法的优点是逻辑清晰,不会出现下标越界的错误,代码可读性非常高。缺点也显而易见:每一层递归都会创建新的数组,空间开销较大,在数据量很大时内存占用明显增加,并不适合生产环境中的大数据排序。

function quickSort(arr) {
  // 递归终止条件:数组长度小于等于1时直接返回
  if (arr.length <= 1) return arr;
  const left = [];
  const right = [];
  // 取中间元素作为基准值
  const pivot = arr[Math.floor(arr.length / 2)];
  for (let i = 0; i < arr.length; i++) {
    if (arr[i] < pivot) {
      left.push(arr[i]);
    } else if (arr[i] > pivot) {
      right.push(arr[i]);
    }
  }
  // 递归排序左右子数组并合并
  return quickSort(left).concat([pivot], quickSort(right));
}
console.log(quickSort([5, 3, 8, 1, 9, 2]));

需要注意的是,上面代码里对等于基准值的元素既不放进左数组也不放进右数组,相当于自动跳过了重复元素,这在有大量重复数据时反而能减少递归次数,算是一个小的优化点。如果想保留重复元素,也可以把等于基准的元素单独归入其中一边。

方案二:原地分区经典实现

真正意义上的快速排序应该做到原地排序,也就是不借助额外数组,直接在原数组上通过交换元素完成分区。经典的做法是使用Lomuto分区方案:选取数组最后一个元素作为基准,维护一个分区指针,遍历过程中把小于基准的元素交换到分区左侧,遍历结束后把基准放到正确位置,返回基准的下标,然后对左右两部分递归处理。

这种实现的空间复杂度只有递归调用栈的O(log n),性能明显优于方案一。理解的关键在于分区指针的含义:它始终指向小于基准区域的后一个位置,每当遇到比基准小的元素,就把它交换进来并让指针右移。

function partition(arr, low, high) {
  const pivot = arr[high]; // 以最后一个元素为基准
  let i = low - 1; // 小于基准区域的边界
  for (let j = low; j < high; j++) {
    if (arr[j] < pivot) {
      i++;
      // 把小于基准的元素交换到左侧区域
      [arr[i], arr[j]] = [arr[j], arr[i]];
    }
  }
  // 把基准放到正确位置
  [arr[i + 1], arr[high]] = [arr[high], arr[i + 1]];
  return i + 1;
}

function quickSortInPlace(arr, low = 0, high = arr.length - 1) {
  if (low < high) {
    const p = partition(arr, low, high);
    quickSortInPlace(arr, low, p - 1);
    quickSortInPlace(arr, p + 1, high);
  }
  return arr;
}
const data = [6, 2, 7, 1, 3, 8, 4];
quickSortInPlace(data);
console.log(data); // [1, 2, 3, 4, 6, 7, 8]

经典实现也有它的隐患:如果每次选中的基准恰好是最大值或最小值,分区会极度不平衡,递归深度退化为n层,时间复杂度退化为O(n²)。比如对一个已经有序的数组排序时,固定取最后元素作基准就会触发最坏情况。实际使用中可以通过随机选取基准,或者采用三数取中法(取首、中、尾三个元素的中位数作基准)来规避这个问题。

方案三:三路快排应对重复元素

当数组中存在大量重复元素时,前两种方案的分区效果都会打折扣,因为等于基准的值会被集中到某一边继续参与递归。三路快排的思路是把数组划分成三个区域:小于基准、等于基准、大于基准。等于基准的区域在一次分区后就已经就位,不再参与后续递归,因此在重复数据多的场景下效率大幅提升。

三路分区的实现需要三个指针:lt指向小于区域的右边界,gt指向大于区域的左边界,i是当前遍历位置。遍历中根据当前元素与基准的大小关系,分别执行不同的交换操作,直到i与gt相遇为止。

function quickSort3Way(arr, low = 0, high = arr.length - 1) {
  if (low >= high) return arr;
  const pivot = arr[low];
  let lt = low;      // 小于基准区域的右边界
  let gt = high;     // 大于基准区域的左边界
  let i = low + 1;   // 当前遍历位置
  while (i <= gt) {
    if (arr[i] < pivot) {
      [arr[lt], arr[i]] = [arr[i], arr[lt]];
      lt++;
      i++;
    } else if (arr[i] > pivot) {
      [arr[i], arr[gt]] = [arr[gt], arr[i]];
      gt--;
      // i不动,交换过来的元素下一轮再判断
    } else {
      i++;
    }
  }
  quickSort3Way(arr, low, lt - 1);
  quickSort3Way(arr, gt + 1, high);
  return arr;
}
console.log(quickSort3Way([4, 2, 4, 3, 4, 1, 4, 5]));

三路快排在全部元素都相同的情况下,一次分区就能完成排序,时间复杂度接近O(n),这是前两种方案无法做到的。它的代价是普通场景下交换次数略多、代码复杂度稍高,属于典型的以场景换性能的优化手段。

三种方案的对比与选择建议

把三种方案放在一起对比,可以从时间复杂度、空间复杂度和适用场景三个维度来衡量。方案一空间开销最大,但代码最简单,适合学习算法思想和应对简单面试题;方案二是标准实现,空间和时间表现均衡,适合一般规模的排序需求;方案三则专门针对重复元素多的数据做了优化,在特定场景下有明显优势。

方案时间复杂度空间复杂度适用场景
简易实现O(n log n)O(n)学习理解、小数据量
原地分区O(n log n)O(log n)通用场景、生产环境
三路快排O(n log n)O(log n)大量重复元素

还有一点值得提醒:JavaScript引擎内置的Array.prototype.sort方法在V8中已经采用了TimSort混合排序算法,对短数组使用插入排序、长数组使用归并思想,稳定性和性能都经过充分优化。在实际项目中,除非有特殊需求或明确知道数据特征,一般直接使用sort方法就够了,自己实现快速排序更多是为了面试准备和理解算法原理。如果想深入性能调优,可以自行写个基准测试,用不同规模和分布的数据对比这几种实现的耗时,会收获更直观的体会。

js快速排序数组排序排序算法修改时间:2026-09-14 08:27:17

免责声明:已尽一切努力确保本网站所含信息的准确性。网站作品多为原创整理与精心创作,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们进行处理Email:chomcom@qq.com。
引用或转载本作品时,请注明当前出处:https://www.ipipp.com/html/20260914/56570.html,基于非商业用途的前提下,欢迎转载或二创本作品。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。