当前位置: 首页 > news >正文

谈谈快速排序算法,其时间复杂度和空间复杂度是多少?

一、快速排序算法原理

快速排序(QuickSort)是一种基于分治策略的高效排序算法,核心思想是通过选取基准值(pivot)将数组拆分为两个子数组,递归排序后合并。具体步骤如下:

  1. 基准选择:从数组中选一个元素作为基准(如中间元素、首尾元素或随机元素)。
  2. 分区操作:重新排列数组,所有比基准小的元素放在左侧,大的放在右侧(等于可放任意一侧)。
  3. 递归排序:对左右子数组递归执行上述操作,直到子数组长度为1或0。
// 基础快速排序实现(以中间元素为基准)
function quickSort(arr) {
  if (arr.length <= 1) return arr; // 终止条件:空数组或单元素

  const pivotIndex = Math.floor(arr.length / 2); // 选中间索引为基准
  const pivot = arr.splice(pivotIndex, 1)[0]; // 取出基准值(原数组被修改)
  const left = [];
  const right = [];

  // 分区操作:比基准小的放左,大的放右
  for (const num of arr) {
    num < pivot ? left.push(num) : right.push(num);
  }

  // 递归排序并合并
  return [...quickSort(left), pivot, ...quickSort(right)];
}

二、时间复杂度与空间复杂度
  • 时间复杂度
    • 平均情况O(n log n)。每次分区操作将数组分成两半,递归深度为log n,每层遍历n次。
    • 最坏情况O(n²)。当数组已排序且每次选第一个元素为基准,导致每次分区仅减少一个元素。
  • 空间复杂度
    • 平均情况O(log n)。递归调用栈的深度。
    • 最坏情况O(n)。递归深度退化为线性。

三、优化方向与使用建议
1. ​基准选择优化

避免最坏情况的关键是合理选择基准:

  • 三数取中法:取头、中、尾三个元素的中位数作为基准。
  • 随机基准:随机选择索引降低有序数组的影响。
// 优化:三数取中法选择基准
function getPivotIndex(arr, left, right) {
  const mid = Math.floor((left + right) / 2);
  // 比较三个值,返回中间值的索引
  if (arr[left] > arr[mid]) [arr[left], arr[mid]] = [arr[mid], arr[left]];
  if (arr[left] > arr[right]) [arr[left], arr[right]] = [arr[right], arr[left]];
  if (arr[mid] > arr[right]) [arr[mid], arr[right]] = [arr[right], arr[mid]];
  return mid; // 中间值作为基准
}
2. ​小数组切换插入排序

当子数组较小时(如长度 < 10),插入排序更高效:

function quickSortOptimized(arr, left = 0, right = arr.length - 1) {
  if (right - left < 10) {
    // 插入排序优化小数组
    for (let i = left + 1; i <= right; i++) {
      const key = arr[i];
      let j = i - 1;
      while (j >= left && arr[j] > key) {
        arr[j + 1] = arr[j];
        j--;
      }
      arr[j + 1] = key;
    }
    return;
  }

  const pivotIndex = partition(arr, left, right);
  quickSortOptimized(arr, left, pivotIndex - 1);
  quickSortOptimized(arr, pivotIndex + 1, right);
}
3. ​避免递归栈溢出
  • 尾递归优化:确保递归调用是最后一步操作(部分引擎支持)。
  • 迭代实现:用栈模拟递归过程。
// 迭代实现快速排序(避免栈溢出)
function quickSortIterative(arr) {
  const stack = [{ left: 0, right: arr.length - 1 }];

  while (stack.length) {
    const { left, right } = stack.pop();
    if (left >= right) continue;

    const pivotIndex = partition(arr, left, right);
    stack.push({ left, right: pivotIndex - 1 });
    stack.push({ left: pivotIndex + 1, right });
  }
  return arr;
}

四、实际开发注意事项
1. ​处理大量重复元素

当数组中重复元素较多时,基础分区可能导致性能下降。使用三向切分优化:

function quickSort3Way(arr, left = 0, right = arr.length - 1) {
  if (left >= right) return;

  let lt = left; // 小于基准的右边界
  let gt = right; // 大于基准的左边界
  const pivot = arr[left];
  let i = left;

  while (i <= gt) {
    if (arr[i] < pivot) {
      [arr[i], arr[lt]] = [arr[lt], arr[i]];
      lt++;
      i++;
    } else if (arr[i] > pivot) {
      [arr[i], arr[gt]] = [arr[gt], arr[i]];
      gt--;
    } else {
      i++;
    }
  }

  quickSort3Way(arr, left, lt - 1);
  quickSort3Way(arr, gt + 1, right);
}
2. ​稳定性问题

快速排序是不稳定排序​(相同元素可能交换位置)。若业务需要稳定性(如多字段排序),应选择归并排序。

3. ​性能权衡
  • 大数据量:优先选择快速排序(平均性能最优)。
  • 内存敏感场景:堆排序(O(1)空间复杂度)更合适。

五、总结与建议
  • 适用场景:快速排序适合内存充足、数据量大且对稳定性无要求的场景(如前端处理本地大规模数据排序)。
  • 避免踩坑
    • 避免直接对已排序数组使用固定基准(如首元素)。
    • 处理海量数据时,优先测试递归深度限制,必要时改用迭代版本。
  • 工程实践:大多数语言内置排序(如JS的Array.sort())已做优化(混合使用快排、插入排序等),无特殊需求建议直接使用内置方法。

相关文章:

  • 【Office办公】【Excel】VLOOKUP函数-高速查找指定匹配数据,可合并2个表格
  • Bevformer环境配置
  • 数据处理专题(六)
  • SQL、mySQL与SQLite简单理解
  • 2025.4.1英语听力
  • MobPush用户分群功能全新升级,精准触达每一位用户
  • C++---RAII模式
  • 碳化硅 MOSFET三相逆变电路损耗新算法
  • JavaScript元素尺寸与位置
  • NLP高频面试题(二十九)——大模型解码常见参数解析
  • Spark(13)HDFS概述
  • Flutter渲染优化之RepaintBoundary
  • MacOS安装软件及运行时,提示“已损坏无法打开”的解决方案
  • 自定义汇编语言(Custom Assembly Language) 和 Unix Git
  • 中级:Maven面试题精讲
  • Deepseek API+Python 测试用例一键生成与导出 V1.0.6(加入分块策略,返回更完整可靠)
  • Profibus DP 转 ModbusTCP 网关:工业自动化的桥梁
  • 自制AirTag定位器成品使用教程(支持安卓/鸿蒙/iOS/PC等所有系统查看定位器的位置)
  • theos工具来编译xcode的swiftUI项目为ipa文件
  • 【11408】考研英语长难句解析策略:三步断开与简化法,快速提升阅读得分
  • 练手网站开发/seo技术培训学校
  • 全国疫情最新消息各省排名/上海网站seo快速排名
  • 推广app网站/正规手游代理平台有哪些
  • 淘客网站怎么与pid/邯郸网站优化公司
  • 炫酷的个人网站/免费的网络推广平台
  • 开办网站需要什么手续/电商网页