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

十一,算法-快速排序

在前面的部分已经讲过冒泡、选择、插入排序三种排序算法,本章节讲述快速排序,快速排序属于递归算法,同时很多语言的内置排序算法使用的就是快速排序。

定义

快速排序算法结合了分区和递归,步骤如下:

  1. 对数组执行分区,分区后,基准值处于数组中的正确位置;
  2. 对分区后的每个子数组继续执行1、2两个步骤,即不断分区;
  3. 当某个子数组没有或者只有一个元素,则这就是递归的基础情形,不做任何操作。

分区

快速排序依赖一个概念-分区。还是以数组为例,选取数组中的某个值作为基准,以该基准为分界线,将小于基准的数值移动到基准左侧位置,将大于基准的数值移动到基准右侧位置,步骤如下:

  1. 选择数组最右侧数值为基准;
  2. 创建两个指针,一个指向数组第一个(最左侧)数值,此处暂且称为左侧指针,另一个指针指向数组最右侧数值(此处排除基准值所在的位置),此处暂且称为右侧指针;
  3. 左侧指针不断右移,直到遇到大于或等于基准的数值才停止;
  4. 右侧指针不断左移,直到遇到小于等于基准的数值或者数组的第一个元素才停止;
  5. 右侧指针停止后出现两种情况,如果左侧指针和右侧指针相遇或者交错,即左侧指针指向元素的数组索引等于或大于右侧指针指向的元素的数组索引,则继续执行步骤6;否则交换左侧指针和右侧指针指向的数值,然后跳转到步骤3继续执行;
  6. 交换基准和左侧指针指向的数值;

完成上述步骤后,可以保证基准值已经处于数组中的正确位置,但基准值左侧是数值和右侧的数值可能并不是按顺序排列的。代码如下:

	int partition(std::vector<int>& arr, int left, int right) {int pivotIndex{right};int pivot{arr[pivotIndex]};while (true) {while (arr[left] < pivot) {++left;}while (arr[right] > pivot) {++right;}if (left > right) {break;}else {std::swap(arr[left], arr[right]);++left;}}std::swap(arr[left], arr[right]);return left;}

递归

接下来就是对分区后的子数组进行递归,最后的快速排序实现如下:

	void quickSort(std::vector<int>& arr, int left, int right) {if (left >= right) return;int pivotIndex = partition(arr, left, right);quickSort(arr, left, pivotIndex - 1);quickSort(arr, pivotIndex + 1, right);}

时间复杂度

快速排序包含两类主要操作:比较和交换。以一次分区为例,数组中所有数值都需要和基准进行比较,为N;但不是每次比较都需要交换数值,最多需要N/2次交换。这是一次交换的时间步骤数,再乘以分区次数,即为快速排序的时间复杂度,但进行多少次分区是无法确定的,因为数组的原始排序不可知,假设数组是平均情况(有顺序有逆序,差不多各一半),则统计下来快速排序的时间负责大概为O(Nlog(N))。对比之前的插入排序,平均情况下快速排序还是比插入排序的O(N^{2})快很多。

快速选择

日常会遇到类似的算法题,即从数组中选出第十小或者第五大的数。一种解决方案是排序整个数组,再读取对应索引的数值,这种方法在平均情况下最快为O(Nlog(N))。另一种解决方案是部分使用快速排序的方法,可以称之为快速选择。快速选择即是将要找的数值索引与基准值索引做比较,这样每次都只需要对基准一侧的子数组进行递归,另一侧数组不用做额外处理,这种情况下快速选择的时间复杂度接近线性时间复杂度,即O(N),代码如下:ku

	int quickSelect(int queryValueIndex, std::vector<int>& arr, int left, int right) {if (left >= right) return;int pivotIndex = partition(arr, left, right);if(queryValueIndex < pivotIndex ){quickSelect(arr, left, pivotIndex - 1);}else if(queryValueIndex > pivotIndex){quickSelect(arr, pivotIndex + 1, right);}else{return arr[pivotIndex ];}}

快速排序和快速选择都是递归算法,虽然实现过程没有那么直观,但效率确实排序算法中最好的。至此,关于递归的部分就基本结束。一部分数据结构中使用了递归操作,如二叉查找树的前、中、后需查找等,这部分将在后续的文章中继续介绍。

http://www.dtcms.com/a/330778.html

相关文章:

  • 大带宽服务器具体是指什么?
  • 十分钟学会一个算法 —— 快速排序
  • 【03】VMware安装麒麟操作系统kylin10sp3
  • Docker运行python项目:使用Docker成功启动FastAPI应用
  • vue3+leaflet案例:告警系统GIS一张图(附源码下载)
  • Mybatis实现页面增删改查
  • 服务器的定义-哈尔滨云前沿
  • [机器学习]07-基于多层感知机的鸢尾花数据集分类
  • Effective Java笔记:要在公有类而非公有域中使用访问方法
  • 解决Maven编译时JAVA_HOME配置错误问题:从报错到根治的完整方案
  • 自动驾驶与人形机器人的技术分水岭
  • springboot博客实战笔记02
  • React.memo、useMemo 和 React.PureComponent的区别
  • 智慧城市SaaS平台/专项管理系统
  • 板子识别出来的所有端点号等信息
  • C++中的链式操作原理与应用(三):专注于异步操作延的C++开源库 continuable
  • 决策树 >> 随机森林
  • 智慧工地从工具叠加到全要素重构的核心引擎
  • Claude Code频繁出错怎么办?深入架构层面的故障排除指南
  • 【Linux学习|黑马笔记|Day4】IP地址、主机名、网络请求、下载、端口、进程管理、主机状态监控、环境变量、文件的上传和下载、压缩和解压
  • 【论文阅读】基于表面肌电信号的下肢多关节运动估计:一种深度卷积神经网络方法
  • [小练习]生成54张扑克牌,洗牌。
  • 解决 VSCode 运行 Python 时 ModuleNotFoundError: No module named ‘open_webui‘ 问题
  • 三角洲知识点
  • CI/CD流水线搭建流程
  • 药房发药的“时间密码”:同步时钟用药安全?
  • 抗辐照CANFD通信芯片在高安全领域国产化替代的研究
  • CMake进阶: externalproject_add用于在构建阶段下载、配置、构建和安装外部项目
  • 常见的Jmeter压测问题
  • 飞算 JavaAI 云原生实践:基于 Docker 与 K8s 的自动化部署架构解析