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

C++算法恢复训练之快速排序

快速排序(Quick Sort)是一种基于分治思想的排序算法,它通过将待排序数组分成两个子数组,其中一个子数组的所有元素都比另一个子数组的元素小,然后对这两个子数组递归地进行排序,最终将整个数组排序。快速排序是一种原地排序算法,其时间复杂度为 O ( n l o g n ) O(nlogn) O(nlogn)

下面是使用 C++ 实现的 经典 快速排序算法:

#include <vector>
#include <iostream>
using namespace std;int partitionSimple(vector<int>& array, int left, int right)
{if (left >= right){return -1;}// Use the value of index `right` as the pivotconst int pivot = array[right];int lessBound = left - 1;for (int i = left; i < right; i++){// If the current element is not more than then pivot value,// then swap it with the less part's next value, and make the less part add 1if (array[i] <= pivot){swap(array[i], array[++lessBound]);}}// At last, swap the pivot with the next element of less partswap(array[lessBound + 1], array[right]);return lessBound + 1;
}void quickSortSimple(vector<int>& array, int left, int right)
{if (left >= right){return;}const int pivotIndex = partitionSimple(array, left, right);quickSortSimple(array, left, pivotIndex - 1);quickSortSimple(array, pivotIndex + 1, right);
}void quickSort(vector<int>& array)
{quickSortSimple(array, 0, array.size() - 1);
}

经典快速排序的实现过程可以分为两个步骤:

  • 分割子问题:选取一个元素作为基准值(pivot),将待排序数组分成两个子数组,一个子数组中的元素都小于等于基准值,另一个子数组中的元素都大于等于基准值。
  • 合并解决方案:对两个子数组分别进行快速排序(递归),最后将两个已排序的子数组合并成一个有序数组。

递归的部分其实比较好理解和实现,那么现在可以将问题简化为:给定一个数组,和一个基准值,如何将小于等于基准值的元素放在数组左边,将大于基准值的元素放在数组右边?

我的实现思路是利用一个小于等于pivot值的边界的索引这样一个概念变量,对应代码中的lessBound,它对应的元素及其左边部分都小于等于pivot值。在随后的数组遍历过程中,如果遍历的元素满足这样的条件,则将该元素与lessbound的后一位对调,然后将lessbound的范围扩大一位。核心思路类似快慢指针:即lessbound扮演的是慢指针,i扮演的是快指针。

最后,数组已经被分成了两个子数组,其中一个子数组中的元素都小于等于基准值,另一个子数组中的元素都大于基准值。然后分别对这两个子数组进行递归。

快速排序的时间复杂度为 O ( n l o g n ) O(nlogn) O(nlogn),其中 n n n 是待排序数组的长度。快速排序每次将待排序数组分成两个子数组,其中一个子数组中的元素都小于等于基准值,另一个子数组中的元素都大于等于基准值。

由于快速排序每次都将待排序数组分成两个子数组,因此递归树的高度为 l o g n logn logn。每个节点所处理的子问题的规模最大为 n n n,因此快速排序的时间复杂度为 O ( n l o g n ) O(nlogn) O(nlogn)

需要注意的是,在最坏情况下,快速排序的时间复杂度为 O ( n 2 ) O(n^2) O(n2),此时待排序数组已经有序或者接近有序,且每次选取的基准值都是数组中的最小或最大元素。为了避免最坏情况的发生,可以采用以下优化措施:

  • 随机选取基准值。
  • 三数取中法(Median-of-three partitioning):从子数组的左端、右端和中间位置分别选取一个元素,选择它们的中间值作为基准值。

除此以外,从算法本身出发,经典快排是利用某个值作为基准值,其算法实质在于一个周期内确定这个pivot的下标索引。从这点考虑,可以考虑在这个周期内将所有与pivot相等的值的位置都敲定,在递归时就不考虑这一批数。

C++相应的实现:


vector<int> partitionOptimized(vector<int>& array, int left, int right)
{if (left >= right){return {-1, -1};}int pivot = array[right];int lessBound = left - 1, moreBound = right;int i = left;while (i < moreBound){if (array[i] == pivot){i++;}else if (array[i] < pivot){swap(array[++lessBound], array[i++]);}else{// array[i] > pivotswap(array[--moreBound], array[i]);}}swap(array[right], array[moreBound++]);return {lessBound, moreBound};
}void quickSortOptimized(vector<int>& array, int left, int right)
{if (left >= right){return;}vector<int> bounds = partitionOptimized(array, left, right);quickSortOptimized(array, left, bounds[0]);quickSortOptimized(array, bounds[1], right);
}void quickSort(vector<int>& array)
{quickSortOptimized(array, 0, array.size() - 1);
}

新的算法的最显著的不同之处在于,partition的返回值是一个数组,保存了小于pivot的边界和大于pivot的边界,他们也是新一轮递归的依据。在计算这两个边界时(partition内),需要将一个数组拆分为:小于pivot的部分,等于pivot的部分,以及大于pivot的部分。此时主要是利用三个指针,分别指向小于pivot的部分的边界,大于pivot的部分的边界,以及当前遍历元素。如果当前元素小于pivot,则与之前的思路类似,将当前元素与小于边界的下一位调换,小于的边界扩大一位,继续下一个元素遍历;如果当前元素等于pivot,继续下一个元素遍历,其他不变;如果当前元素大于pivot,则需要将当前元素与大于边界的下一位进行调换,大于的边界减小一位,注意此时仍然需要调查被调换元素的大小,即不继续一个元素的遍历。

当然,虽说是优化,但是这个思路也仅仅是在数组中有重复元素时会比经典快排稍微快一些,本质上算法复杂度并没有发生改变,也没有改变快排依赖数组状况的问题。

利用随机取基准值的方法确实可以令这个问题得到改善,但是取随机数本身也是需要一定的指令,其产生的消耗也是一个需要考虑和权衡的问题。

相关文章:

C++算法恢复训练之快速排序

快速排序&#xff08;Quick Sort&#xff09;是一种基于分治思想的排序算法&#xff0c;它通过将待排序数组分成两个子数组&#xff0c;其中一个子数组的所有元素都比另一个子数组的元素小&#xff0c;然后对这两个子数组递归地进行排序&#xff0c;最终将整个数组排序。快速排…...

事务的特性

四大特性 原子性&#xff08;atomicity&#xff09; 事务的一系列操作&#xff0c;要么所有操作所有都成功&#xff0c;要么一个操作都不做 一致性&#xff08;consistency&#xff09; 指数据的规则,在事务前/后应保持一致&#xff0c;事务的原子性保证了一致性 隔离性&a…...

Python 计算三角形的面积、Python 阶乘实例

Python 计算三角形的面积 以下实例为通过用户输入三角形三边长度&#xff0c;并计算三角形的面积&#xff1a; # -*- coding: UTF-8 -*-# Filename : test.py # author by : www.w3cschool.cna float(input(输入三角形第一边长: )) b float(input(输入三角形第二边长: )) c …...

C++入门教程||C++ 重载运算符和重载函数||C++ 多态

C 重载运算符和重载函数 C 重载运算符和重载函数 C 允许在同一作用域中的某个函数和运算符指定多个定义&#xff0c;分别称为函数重载和运算符重载。 重载声明是指一个与之前已经在该作用域内声明过的函数或方法具有相同名称的声明&#xff0c;但是它们的参数列表和定义&…...

docker+docker-compose+nginx前后端分离项目部署

文章目录 1.安装docker1.1 基于centos的安装1.2 基于ubuntu 2.配置国内加速器2.1 配置阿里云加速器&#x1f340; 找到相应页面&#x1f340; 创建 docker 目录&#x1f340; 创建 daemon.json 文件&#x1f340; 重新加载服务配置文件&#x1f340; 重启 docker 引擎 2.2 配置…...

基于PCA与LDA的数据降维实践

基于PCA与LDA的数据降维实践 描述 数据降维&#xff08;Dimension Reduction&#xff09;是降低数据冗余、消除噪音数据的干扰、提取有效特征、提升模型的效率和准确性的有效途径&#xff0c; PCA&#xff08;主成分分析&#xff09;和LDA&#xff08;线性判别分析&#xff0…...

【Hello Network】网络编程套接字(一)

作者&#xff1a;小萌新 专栏&#xff1a;网络 作者简介&#xff1a;大二学生 希望能和大家一起进步 本篇博客简介&#xff1a;简单介绍网络的基础概念 网络编程套接字&#xff08;一&#xff09; 预备知识源ip和目的ip端口号TCP和UDP协议网络中的字节序 socket编程接口socket常…...

【计算机网络】学习笔记:第二章 物理层(五千字详细配图)【王道考研】

创作不易&#xff0c;本篇文章如果帮助到了你&#xff0c;还请点赞支持一下♡>&#x16966;<)!! 主页专栏有更多知识&#xff0c;如有疑问欢迎大家指正讨论&#xff0c;共同进步&#xff01; 给大家跳段街舞感谢支持&#xff01;ጿ ኈ ቼ ዽ ጿ ኈ ቼ ዽ ጿ ኈ ቼ ዽ ጿ…...

直流有刷电机的电路分析

这里写目录标题 H桥改进后的电路L298N原理图野火的电机驱动板MOS管野火的原理图 H桥 当 Q1 和 Q4 导通时&#xff0c;电流将经过 Q1 从左往右流过电机&#xff0c;在经过 Q4 流到电源负极&#xff0c;这时图中电机可以顺时针转动。 当 Q3 和 Q2 导通时&#xff0c;电流将经过 Q…...

使用PowerShell自动部署ASP.NetCore程序到IIS

asp.net core 安装asp.net core sdk https://dotnet.microsoft.com/en-us/download/dotnet/3.1 创建asp.net core项目 dotnet new webapi运行项目 访问https://localhost:5001/WeatherForecast iis配置 安装iis 以管理员身份运行powershell Enable-WindowsOptiona…...

Elasticsearch:保留字段名称

作为 Elasticsearch 用户&#xff0c;我们从许多不同的位置收集数据。 我们使用 Logstash、Beats 和其他工具来抓取数据并将它们发送到 Elasticsearch。 有时&#xff0c;我们无法控制数据本身&#xff0c;我们需要管理数据的结构&#xff0c;甚至需要在摄取数据时处理字段名称…...

Qt 套接字类(QTcpSocket和QUdpSocket)解密:迈向 Qt 网络编程之巅

Qt 套接字类解密&#xff1a;迈向 Qt 网络编程之巅 一、套接字类简介&#xff08;Introduction to Socket Classes&#xff09;# 套接字类的作用&#xff08;Role of Socket Classes&#xff09;Qt 中常见套接字类概述&#xff08;Overview of Common Socket Classes in Qt&…...

Python视频编辑库:MoviePy

MoviePy MoviePy是一个关于视频编辑的python库,主要包括:剪辑,嵌入拼接,标题插入,视频合成(又名非线性编辑),视频处理,和自定制效果。可以看gallery中的一些实例来了解用法。MoviePy可以读写所有的音频和视频格式,包括GIF,通过python2.7+和python3可以跨平台运行于window/M…...

课程3:ASP.NET Core 身份验证 - Cookie

课程简介目录 🚀前言一、.Net Core 身份验证简介二、开启Cookie身份验证三、添加登录接口3.1 添加登录Dto3.2 添加登录接口Login3.3 获取用户信息接口,添加身份验证四、获取用户信息接口测试4.1 测试获取用户信息接口4.2 登录4.3 再次测试:获取用户信息接口4.4 其他浏览器测…...

Visual Studio 2022如何安装和使用MSDN

我是荔园微风&#xff0c;作为一名在IT界整整25年的老兵&#xff0c;在后台收到提问&#xff0c;问我Visual Studio 2022如何安装和使用MSDN&#xff0c;这个我之前也没有在这个版本上装过MSDN&#xff0c;我之前是在Visual Studio 2017版上装过MSDN&#xff0c;那既然有人问了…...

82.qt qml-2D粒子系统、粒子方向、粒子项(一)

由于粒子系统相关的类比较多, 所以本章参考自QmlBook in chinese的粒子章节配合学习: 由于QmlBook in chinese翻译过来的文字有些比较难理解,所以本章在它的基础上做些个人理解,建议学习的小伙伴最好配合QmlBook in chinese一起学习。 1.介绍 粒子模拟的核心是粒子系统(Partic…...

引用的底层原理(汇编指令),引用与指针的联系与区别

TIPS 2. 3. 4. 引用的底层本质 在语法层面上的话&#xff0c;这个引用是不开空间的&#xff0c;相当于是对一个变量进行一个取别名的这么一个操作。在底层实现上实际是有空间的&#xff0c;因为引用是按照指针方式来实现的。然而如果你从底层的角度去看的话&#xff0c;因…...

磁盘的移臂调度算法

1、概要 访问磁盘&#xff0c;首先要找到数据&#xff0c;但机械硬盘并不是直接电子读取&#xff0c;是需要移动磁头到相应的数据块上才能读取的&#xff0c;即需要磁头移动到目标柱面(磁道)&#xff0c;然后磁片旋转使磁头能访问到相应扇区&#xff0c;进而读取到数据。 根据访…...

软考第六章 网络互连与互联网

网络互连与互联网 1.网络互连设备 组成因特网的各个网络叫做子网&#xff0c;用于连接子网的设备叫做中间系统。它的主要作用是协调各个网络的工作&#xff0c;使得跨网络的通信得以实现。 网络互连设备可以根据它们工作的协议层进行分类&#xff1a; 中继器&#xff1a;工…...

C6678-缓存和内存

C6678-缓存和内存 全局内存映射扩展内存控制器&#xff08;XMC&#xff09;-MPAX内存保护与地址扩展使用例程缓存 全局内存映射 扩展内存控制器&#xff08;XMC&#xff09;-MPAX内存保护与地址扩展 每个C66x核心都具有相同大小的L1和L2缓存&#xff0c;并且可配置为普通内存使…...

益达App:5分钟打造你的跨平台全能媒体聚合神器

益达App&#xff1a;5分钟打造你的跨平台全能媒体聚合神器 【免费下载链接】yidaRule 益达规则仓库 项目地址: https://gitcode.com/gh_mirrors/yi/yidaRule 还在为手机里装满了各种视频、音频、阅读App而烦恼吗&#xff1f;每天在不同应用间切换&#xff0c;只为找到想…...

开源STK插件模块大全:提升你的空天地一体化仿真效率

开源STK插件模块大全&#xff1a;提升空天地一体化仿真效率的实战指南 如果你已经熟悉STK的基础操作&#xff0c;却还在为复杂的星座仿真流程和有限的分析功能而头疼&#xff0c;那么开源插件模块将成为你的效率倍增器。本文将带你深入探索那些被专业用户私藏的工具箱&#xff…...

深入解析SAC算法:从最大熵原理到机器人控制实践

1. SAC算法为什么值得关注 第一次听说SAC(Soft Actor-Critic)算法时&#xff0c;我和大多数强化学习新手一样困惑&#xff1a;为什么这个算法能在机器人控制领域迅速走红&#xff1f;直到在机械臂抓取项目中亲自尝试后&#xff0c;我才真正理解它的独特价值。 SAC最吸引人的特点…...

企业必看:致远OA密码重置漏洞修复指南(附官方补丁下载与安装教程)

致远OA密码重置漏洞全面修复指南&#xff1a;从补丁部署到安全加固 1. 漏洞背景与影响范围 近期致远OA协同办公平台曝出的密码重置漏洞&#xff0c;已成为企业IT安全团队亟需应对的高危风险。该漏洞允许攻击者在仅获取用户名的情况下&#xff0c;通过构造特定HTTP请求绕过短信…...

手把手教你给RK3588开发板添加RTL8188EUS USB无线网卡驱动(附完整配置流程)

RK3588开发板实战&#xff1a;RTL8188EUS无线网卡驱动移植全指南 在嵌入式开发领域&#xff0c;为特定硬件平台添加第三方外设驱动是开发者常遇到的挑战。本文将详细介绍如何在Rockchip RK3588开发板上为RTL8188EUS USB无线网卡移植驱动&#xff0c;从环境准备到功能验证&#…...

隐私优先方案:OpenClaw本地化部署Qwen3.5-9B处理敏感财报分析

隐私优先方案&#xff1a;OpenClaw本地化部署Qwen3.5-9B处理敏感财报分析 1. 为什么金融从业者需要本地化AI方案 作为一名长期关注金融科技自动化的从业者&#xff0c;我深刻理解处理财报数据时的隐私焦虑。去年尝试使用某云端AI服务分析客户财报时&#xff0c;系统突然弹出&…...

一体机-显控终端 国产化嵌入式处理板卡 产品规格说明书

一、产品概述MB-FT24A02是一款专为工业嵌入式、车载人机交互、国产化终端替代等场景设计的全国产化高性能处理板卡&#xff0c;采用紧凑型PCB设计&#xff0c;核心搭载飞腾FT-2000/4国产处理器&#xff0c;搭配飞腾X100专用国产桥片&#xff0c;构建全链路自主可控硬件平台&…...

ZeroOmega终极指南:3分钟掌握智能代理规则配置

ZeroOmega终极指南&#xff1a;3分钟掌握智能代理规则配置 【免费下载链接】ZeroOmega Manage and switch between multiple proxies quickly & easily. 项目地址: https://gitcode.com/gh_mirrors/ze/ZeroOmega 还在为网络代理切换而烦恼吗&#xff1f;每次访问不同…...

Python农业图像识别精度为何卡在92.3%?揭秘3个被90%开发者忽略的标注陷阱与突破路径

第一章&#xff1a;Python农业图像识别精度为何卡在92.3%&#xff1f;在多个田间部署的玉米病害识别模型中&#xff0c;验证集准确率稳定收敛于92.3%&#xff0c;进一步调参或增加训练轮次均未突破该阈值。深入分析发现&#xff0c;该瓶颈并非源于模型容量不足&#xff0c;而是…...

14 年 Java 老码农,重启 CSDN:从 2012 到 2026,我的技术成长与重启之路

图&#xff1a;我的 CSDN 主页&#xff0c;2012 年 8 月 13 日注册&#xff0c;2014 年分享的第一篇 SSH 框架相关文章。 14 年过去&#xff0c;从青涩的 Java 工具类到现在的 DevOps 科研 AI&#xff0c;账号尘封多年&#xff0c;今天正式重启。 一、2012–2026&#xff1a;…...