【排序】——2.快速排序法(含优化)

快速排序法

递归法
霍尔版本(左右指针法)
1.思路
1、选出一个key,一般是最左边或是最右边的。
2、定义一个begin和一个end,begin从左向右走,end从右向左走。(需要注意的是:若选择最左边的数据作为key,则需要end先走;若选择最右边的数据作为key,则需要bengin先走)。
3、在走的过程中,若end遇到小于key的数,则停下,begin开始走,直到begin遇到一个大于key的数时,将begin和right的内容交换,end再次开始走,如此进行下去,直到begin和end最终相遇,此时将相遇点的内容与key交换即可。(选取最左边的值作为key)
4.此时key的左边都是小于key的数,key的右边都是大于key的数
5.将key的左序列和右序列再次进行这种单趟排序,如此反复操作下去,直到左右序列只有一个数据,或是左右序列不存在时,便停止操作,此时此部分已有序
📝tip1

- 常选左为key,那右边先走。
📝优化一:选key的常见三种方法
-
1.三数取中(比较推荐)
- 三数取中 left mid right
- 大小居中的值,也就是不是最大也不是最小的
-
2.随机取一个
-
3.取中间位置
//关于选kevi——三数取中,作为kevi(比较好)
int Getmid(int* arr, int left, int right)
{//方法1:三数取中//left mid right//找到三者——中间值的下标,返回int mid = (left + right) / 2;if (arr[left] < arr[mid]){if (arr[mid] < arr[right]){return mid;}else if(arr[right]<arr[mid]){return right;}else{return left;}}else //此时已经arr[left]>arr[mid]{if (arr[right] < arr[mid]){return mid;}else if (arr[left] < arr[right]){return left;}else{return right;}}
}
//方法2:随机取一个(还行)
int GetRand(int* arr, int left, int right)
{srand((size_t)time(NULL));return rand() % (right-left+1)+left;
}
//方法3:取中间位置
int GetMid(int* arr, int left, int right)
{return (left + right) / 2;
}
📝优化二:小区间优化
优化小数组的交换,就是为了解决大才小用问题
原因:对于很小和部分有序的数组,快排不如插排好。当待排序序列的长度分割到一定大小后,继续分割的效率比插入排序要差,此时可以使用插排而不是快排
截止范围:待排序序列长度N = 10,虽然在5~20之间任一截止范围都有可能产生类似的结果,这种做法也避免了一些有害的退化情形。
if (right - left + 1 < 10)
{InsertSort(a + left, right - left + 1); //使用插入排序return;
}
2.图解
单趟动图:

3.代码
代码如下:
//hoare(霍尔)版本(左右指针法)
#include<stdio.h>
void Swap(int* p1, int* p2)
{int tmp = *p1;*p1 = *p2;*p2 = tmp;
}
void QuickSort(int* arr,int left,int right)
{//递归终止条件if (left >= right)return;//记录一下,因为后面要变化int begin=left;int end=right;//选左边为keyint mid = Getmid(arr, left, right); //三数取中Swap(&arr[mid],&arr[left]); //把找到的,移到最左边int kevi = left;while (left < right){//右先走// 注意:left<right//right,找小while (left < right &&arr[right] >= arr[kevi]){right--;}//left找大while (left < right && arr[left]<= arr[kevi]){left++;}//交换(大和小的)Swap(&arr[left], &arr[right]);}//交换key和相遇点Swap(&arr[kevi], &arr[right]);kevi = right; //更新kevi//递归// 右边大于kevi 左边小于kevi//此时[begin,kevi) kevi [kevi+1,end]QuickSort(arr, begin,kevi-1);QuickSort(arr, kevi+1,end);
}
运行结果:

挖坑法
1.思路
- 选key,左边为key(坑)
- 右边先走,找小(小于key),找到把值放key(坑里),此时right变新坑
- 左边后走,找大(大于key),找到把值放key(坑里),此时left变新坑。
- 一直相遇,把一开始key的值放相遇点
- 递归下去
2.图解

3.代码
//挖坑法
void QuickSort_keng(int* arr,int begin,int end)
{//递归结束if (begin >= end)return;//int left = begin;int right = end;int mid = Getmid(arr, left, right); //三数取中Swap(&arr[mid], &arr[left]); //换过来int kevi = arr[left]; //设置开始的坑位,保存key的值int keng = left; //记录坑位的位置while (left < right){//right 找小while (left<right&&arr[right] >= kevi){right--;}Swap(&arr[right], &arr[keng]); //交换,right位置变成新的坑位keng = right;//left找大while (left < right && arr[left] <= kevi){left++;}Swap(&arr[left], &arr[keng]); //交换,left位置变成新的坑位keng = left; }//最终把kevi放keng位置(相遇点)arr[keng] = kevi;//递归QuickSort_keng(arr,begin,keng-1);QuickSort_keng(arr, keng+1, end);}

前后指针法
1.思路
- 1、选出一个key,一般是最左边或是最右边的。
- 2、起始时,prev指针指向序列开头,cur指针指向prev+1(前后指针)。
- 3、若cur指向的内容小于key,则prev先向后移动一位,然后交换prev和cur指针指向的内容,然后cur指针++;
- 4、若cur指向的内容大于key,则cur指针直接++。如此进行下去,直到cur到达end位置,此时将key和++prev指针指向的内容交换即可。
经过一次单趟排序,最终也能使得key左边的数据全部都小于key,key右边的数据全部都大于key。
然后也还是将key的左序列和右序列再次进行这种单趟排序,如此反复操作下去,直到左右序列只有一个数据,或是左右序列不存在时,便停止操作
2.图解

3.代码
void Swap(int* p1, int* p2)
{int tmp = *p1;*p1 = *p2;*p2 = tmp;
}
//前后指针版本
void QuickSort_prev(int* arr, int begin, int end)
{//递归结束if (begin >= end) return;记录区间范围int left =begin;int right = end;int kevi = left; //记录key的下标//前后指针int prev =left;int cur = left + 1;while (cur <= right){if (arr[cur] < arr[kevi] && ++prev != cur){//把小的交换到前面Swap(&arr[cur],&arr[prev]);}cur++;}//cur越界了,交换kevi和prevSwap(&arr[kevi], &arr[prev]);kevi = prev;//递归QuickSort_prev(arr, left, kevi - 1);QuickSort_prev(arr, kevi + 1, right);}
非递归法
1.思路
-
使用stack来存储待处理的区间,先入栈的是右端点,然后是左端点。
-
在while循环中,只要栈不为空,就继续处理。
-
每次从栈中弹出两个元素,分别代表当前处理区间的左右端点。
-
执行一趟快速排序的划分操作,使用kevi作为基准元素的索引,prev用于记录小于基准元素的最后一个元素的位置。
-
在while循环中,cur遍历当前区间,如果发现比基准元素小的元素,则将其与prev位置的元素交换。
-
划分完成后,将基准元素与prev位置的元素交换,确保基准元素位于中间位置。
-
检查新的区间是否需要继续排序,如果需要,则将新的区间端点压入栈中。
-
重复步骤2-7,直到栈为空,这意味着所有元素都已经被排序。
2.图解

3.代码
这里得单趟,我套用了前面 前后指针的方法
//非递归
void QuickSort_None(int* arr, int begin, int end)
{stack<int> st; //栈//先入右,后入左st.push(end);st.push(begin);//不为空while (!st.empty()){//出栈区间,开始一趟int left = st.top();st.pop();int right = st.top();st.pop();//走单趟int kevi = left;int prev = left;int cur = left+1;while (cur <= right){//把小的换到前面if (arr[cur] < arr[kevi] && ++prev != cur){Swap(&arr[prev], &arr[cur]);}++cur;}//把kevi换到相遇点Swap(&arr[kevi],&arr[prev]);kevi = prev;//入栈新区间-先右后左// [begin,kevi-1] kevi [kevi+1,end]if (kevi + 1 < right){st.push(end); //先右后左st.push(kevi+1);}if (left<kevi-1){st.push(kevi-1); // 先右后左st.push(left);}}
}
算法性能

1.时间复杂度

2.空间复杂度

3.稳定性
不稳定
下面2 2 1 的例子,两个2相对位置发生变化





相关文章:
【排序】——2.快速排序法(含优化)
快速排序法 递归法 霍尔版本(左右指针法) 1.思路 1、选出一个key,一般是最左边或是最右边的。 2、定义一个begin和一个end,begin从左向右走,end从右向左走。(需要注意的是:若选择最左边的数据作为key,则…...
AnaTraf | 网络分析系统:高效IT运维工具
目录 什么是网络分析系统? 网络分析系统的核心功能 二、网络分析系统在IT运维中的重要性 案例分析:如何快速应对网络拥塞 技巧分享:如何使用网络分析系统优化带宽 网络分析系统的部署与最佳实践 确定监控范围与关键设备 分析结果的可…...
踩坑日记:线上接口超时问题排查
1.背景: 上线后,功能测试. 进入小程序页面发现很慢,耗时超过5秒,打开skywalking发现大量接口耗时都很高. 2.top命令 服务器top命令查看cpu资源发现占用并不高 3.mysql查看sql运行情况 # 当前运行的所有事务 select * from information_schema.innodb_trx; 1 | …...
C语言中的段错误(Segmentation Fault):底层原理及解决方法
引言 在C语言编程中,“段错误”(通常由操作系统信号 SIGSEGV 触发)是一种常见的异常情况,它表明程序试图访问不受保护的内存区域。本文将深入探讨段错误的原因、底层原理、常见情况以及如何调试和解决这类错误。 段错误的定义 …...
1.两数之和 暴力枚举和暴力搜索法
1. 两数之和 已解答 简单 相关标签 相关企业 提示 给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。 你可以假设每种输入只会对应一个答案,并且你不能使用两次相…...
你的收入达到了缴纳个人所得税的标准了吗?
在现代社会,个人所得税作为一种重要的税收形式,已经渗透到了我们每个人的日常生活中。它不仅关乎国家的财政收入,更与每个纳税人的切身利益息息相关。那么,你是否真正了解个人所得税的缴纳标准、计算方法以及相关的税收优惠政策呢…...
【C++贪心】2086. 喂食仓鼠的最小食物桶数|1622
本文涉及知识点 C贪心 LeetCode2086. 喂食仓鼠的最小食物桶数 给你一个下标从 0 开始的字符串 hamsters ,其中 hamsters[i] 要么是: ‘H’ 表示有一个仓鼠在下标 i ,或者’.’ 表示下标 i 是空的。 你将要在空的位置上添加一定数量的食物桶…...
notepad++中实现代码整体缩进和退格
我 | 在这里 ⭐ 全栈开发攻城狮、全网10W粉丝、2022博客之星后端领域Top1、专家博主。 🎓擅长 指导毕设 | 论文指导 | 系统开发 | 毕业答辩 | 系统讲解等。已指导60位同学顺利毕业 ✈️个人公众号:乡下小哥编程。回复 Java全套视频教程 或 前端全套视频教…...
如何调整配置请款单上的立账条件
顾问配置的立账条件取的是供应商档案里面的参数。与实际需求是不相匹配的。采购员商谈的立账条件经常是变化的。 措施:修改模板中立几账条件的OQL语句。 如下: select UFIDA::U9::AP::APBill::APBillHead.APBillLines.AccrueTerm.Name as 立账条件_名…...
骨传导耳机精选:2024最佳骨传导耳机有哪些?分享骨传导耳机top5
随着健康意识的普及,越来越多的人开始注重运动健身,并将音乐作为运动时的重要伴侣。然而,传统耳机在运动时易脱落且不易清洁的问题,给健身爱好者们带来了不少困扰。幸运的是,骨传导耳机的出现为这一问题提供了解决方案…...
for循环与webAPI练习题
爱太容易了,让爱维持才是最困难的部分 文章目录 for循环练习题webAPI练习题 for循环练习题 练习1:计算1-100的和 let sum 0for (let i 1; i < 100; i) {sum i}console.log(sum)练习2:将1-100之间所有是6的倍数的数字输出到控制台 for …...
FLUX | 轻松掌握FLUX.1 LoRA本地训练秘籍!
在数字艺术和创意领域,FLUX以其独特的虚实结合技术,已经成为艺术家和设计师们手中的利器。今天,我们激动地宣布,FLUX推出了一款全新的FLUX.1版本,它将LoRA本地训练技术完美融合,为用户提供了更加便捷和高效…...
LeetCode 每日一题 最小元素和最大元素的最小平均值
最小元素和最大元素的最小平均值 你有一个初始为空的浮点数数组 averages。另给你一个包含 n 个整数的数组 nums,其中 n 为偶数。 你需要重复以下步骤 n / 2 次: 从 nums 中移除 最小 的元素 minElement 和 最大 的元素 maxElement。 将 (minElement ma…...
PHP学习记录-编辑器推荐和本地环境的安装
文章目录 一,编辑器首推VSCode1,vscode2,PHPStorm 二,PHP环境搭建1,下载安装2,使用phpstudy创建站点3,答疑解惑 一,编辑器首推VSCode 1,vscode 对于PHP新手来说&#x…...
嵌套div导致子区域margin失效问题解决
嵌套div导致子区域margin失效问题解决 现象原因解决方法 现象 <div class"prev"></div> <div class"parent"><div class"child"></div><div class"child"></div> </div> <div cl…...
搭建app业务的服务器优势类型用途等
APP服务器的服务类型有哪些 APP服务器主要包括API服务器、数据库服务器、Web服务器等。API服务器可以提供登录、注册、查询、更新等各种API服务,为APP提供更方便的功能;数据库服务器可以存储APP数据,访问更快、更安全;Web服务器可…...
基于Springboot+Vue的个性化推荐影院(含源码数据库)
1.开发环境 开发系统:Windows10/11 架构模式:MVC/前后端分离 JDK版本: Java JDK1.8 开发工具:IDEA 数据库版本: mysql5.7或8.0 数据库可视化工具: navicat 服务器: SpringBoot自带 apache tomcat 主要技术: Java,Springboot,mybatis,mysql,vue 2.视频演示地址 3.功能 这个系…...
SpringMVC后台控制端校验-表单验证深度分析与实战优化
前言 在实战开发中,数据校验也是十分重要的环节之一,数据校验大体分为三部分: 前端校验后端校验数据库校验 本文讲解如何在后端控制端进行表单校验的工作 案例实现 在进行项目开发的时候,前端(jquery-validate),后端,数据库都要进行相关的数据…...
Codeforces Round 770 (Div. 2)
比赛链接:Dashboard - Codeforces Round 770 (Div. 2) - Codeforces A. Reverse and Concatenate 题意: 思路: 假设 s "abba" 经过1次操作后 -> "abbaabba" s "abcd" 经过一次操作后 -> "abcd…...
ProteinMPNN中蛋白质特征提取
函数 featurize 的主要作用是将一批蛋白质序列和结构信息转化为深度学习模型可以接受的特征矩阵。它在处理蛋白质多链结构(即多个链的蛋白质复合体)时,考虑了可见链和被掩码链的区分。 代码: import torch import numpy as np import csv import time import os import r…...
在软件开发中正确使用MySQL日期时间类型的深度解析
在日常软件开发场景中,时间信息的存储是底层且核心的需求。从金融交易的精确记账时间、用户操作的行为日志,到供应链系统的物流节点时间戳,时间数据的准确性直接决定业务逻辑的可靠性。MySQL作为主流关系型数据库,其日期时间类型的…...
XCTF-web-easyupload
试了试php,php7,pht,phtml等,都没有用 尝试.user.ini 抓包修改将.user.ini修改为jpg图片 在上传一个123.jpg 用蚁剑连接,得到flag...
利用ngx_stream_return_module构建简易 TCP/UDP 响应网关
一、模块概述 ngx_stream_return_module 提供了一个极简的指令: return <value>;在收到客户端连接后,立即将 <value> 写回并关闭连接。<value> 支持内嵌文本和内置变量(如 $time_iso8601、$remote_addr 等)&a…...
Cinnamon修改面板小工具图标
Cinnamon开始菜单-CSDN博客 设置模块都是做好的,比GNOME简单得多! 在 applet.js 里增加 const Settings imports.ui.settings;this.settings new Settings.AppletSettings(this, HTYMenusonichy, instance_id); this.settings.bind(menu-icon, menu…...
Keil 中设置 STM32 Flash 和 RAM 地址详解
文章目录 Keil 中设置 STM32 Flash 和 RAM 地址详解一、Flash 和 RAM 配置界面(Target 选项卡)1. IROM1(用于配置 Flash)2. IRAM1(用于配置 RAM)二、链接器设置界面(Linker 选项卡)1. 勾选“Use Memory Layout from Target Dialog”2. 查看链接器参数(如果没有勾选上面…...
第一篇:Agent2Agent (A2A) 协议——协作式人工智能的黎明
AI 领域的快速发展正在催生一个新时代,智能代理(agents)不再是孤立的个体,而是能够像一个数字团队一样协作。然而,当前 AI 生态系统的碎片化阻碍了这一愿景的实现,导致了“AI 巴别塔问题”——不同代理之间…...
【Web 进阶篇】优雅的接口设计:统一响应、全局异常处理与参数校验
系列回顾: 在上一篇中,我们成功地为应用集成了数据库,并使用 Spring Data JPA 实现了基本的 CRUD API。我们的应用现在能“记忆”数据了!但是,如果你仔细审视那些 API,会发现它们还很“粗糙”:有…...
相机Camera日志分析之三十一:高通Camx HAL十种流程基础分析关键字汇总(后续持续更新中)
【关注我,后续持续新增专题博文,谢谢!!!】 上一篇我们讲了:有对最普通的场景进行各个日志注释讲解,但相机场景太多,日志差异也巨大。后面将展示各种场景下的日志。 通过notepad++打开场景下的日志,通过下列分类关键字搜索,即可清晰的分析不同场景的相机运行流程差异…...
在WSL2的Ubuntu镜像中安装Docker
Docker官网链接: https://docs.docker.com/engine/install/ubuntu/ 1、运行以下命令卸载所有冲突的软件包: for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do sudo apt-get remove $pkg; done2、设置Docker…...
Spring数据访问模块设计
前面我们已经完成了IoC和web模块的设计,聪明的码友立马就知道了,该到数据访问模块了,要不就这俩玩个6啊,查库势在必行,至此,它来了。 一、核心设计理念 1、痛点在哪 应用离不开数据(数据库、No…...
