当前位置: 首页 > 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;并且可配置为普通内存使…...

【免费下载】 JIRA用户操作指南(详细版)

JIRA用户操作指南&#xff08;详细版&#xff09; 【下载地址】JIRA用户操作指南详细版 JIRA用户操作指南&#xff08;详细版&#xff09;欢迎使用JIRA用户操作指南&#xff0c;本指南旨在帮助您全面理解并高效地使用JIRA这一强大的问题跟踪与项目管理工具 项目地址: https:/…...

【亲测免费】 UPX脱壳机资源下载

UPX脱壳机资源下载 【下载地址】UPX脱壳机资源下载 UPX脱壳机资源下载本仓库提供了一个名为“upx脱壳机”的资源文件下载 项目地址: https://gitcode.com/open-source-toolkit/3cfe1 本仓库提供了一个名为“upx脱壳机”的资源文件下载。该资源文件是一个名为“HA_UPXShe…...

英雄联盟R3nzSkin换肤工具:3分钟实现安全免费的全皮肤体验

英雄联盟R3nzSkin换肤工具&#xff1a;3分钟实现安全免费的全皮肤体验 【免费下载链接】R3nzSkin Skin changer for League of Legends (LOL) 项目地址: https://gitcode.com/gh_mirrors/r3n/R3nzSkin R3nzSkin是一款专为英雄联盟玩家设计的开源内存换肤工具&#xff0c…...

MTKClient实战手册:联发科芯片调试的5个专业技巧解决常见问题

MTKClient实战手册&#xff1a;联发科芯片调试的5个专业技巧解决常见问题 【免费下载链接】mtkclient MTK reverse engineering and flash tool 项目地址: https://gitcode.com/gh_mirrors/mt/mtkclient 当你的联发科设备遇到无法连接、分区读写失败或固件提取困难时&am…...

超导量子处理器校准技术:频率分配与门优化

1. 超导量子处理器校准技术概述超导量子处理器校准是量子计算硬件实现中的关键环节&#xff0c;其核心目标是通过系统化的参数优化和误差抑制&#xff0c;确保量子比特能够可靠地执行高保真度的量子门操作。在Zuchongzhi 3.1处理器的研发过程中&#xff0c;我们成功集成了105个…...

Haneke最佳实践:10个技巧让你的图片缓存更高效

Haneke最佳实践&#xff1a;10个技巧让你的图片缓存更高效 【免费下载链接】Haneke A lightweight zero-config image cache for iOS, in Objective-C. 项目地址: https://gitcode.com/gh_mirrors/ha/Haneke Haneke是一款适用于iOS平台的轻量级零配置图片缓存库&#xf…...

第二章:Compose入门—声明式UI编程

第二章&#xff1a;Compose 入门 — 声明式 UI 编程 Compose 的核心理念&#xff1a;用 Kotlin 代码声明 UI&#xff0c;而不是用 XML 布局文件。 2.1 传统 View 系统 vs Compose 对比项传统 View 系统Jetpack ComposeUI 描述XML 布局文件Kotlin 代码状态更新findViewById 手…...

从‘密码长度’到‘任意代码执行’:手把手复现攻防世界int_overflow靶场(附Python3 EXP)

从密码长度到系统控制&#xff1a;整数溢出漏洞实战攻防全解析 在网络安全领域&#xff0c;整数溢出漏洞往往因其隐蔽性而被开发者忽视&#xff0c;却可能成为攻击者打开系统大门的金钥匙。本文将带您深入一个典型场景&#xff1a;如何通过精心构造的密码输入&#xff0c;从简单…...

瑞芯微(EASY EAI)RV1126B TF卡电路

1. TF卡电路RV1126B核心板集成了1个SDMMC控制器和1个SDIO控制器&#xff0c;均可支持SDIO3.0协议&#xff0c;以及MMC V4.51协议。4线的数据总线宽度支持SDR104模式&#xff0c;速率达到200MHz。SDMMC控制器是由PMIC单独供电&#xff0c;可以动态的在1.8V和3.3V之间调节&#x…...

PyCharm 运行 FastAPI 接口请求阻塞?竟是后台多进程残留导致

问题描述在 PyCharm 中启动 FastAPI 项目进程后&#xff0c;使用 Postman 发起接口请求出现明显阻塞现象&#xff0c;不仅请求迟迟无法得到响应&#xff0c;项目控制台也完全接收不到任何请求日志&#xff0c;接口调用彻底失效。 问题根源分析日常开发中习惯性直接关闭运行终端…...