【手撕排序2】快速排序

news2024/11/8 13:25:57


🍃 如果觉得本系列文章内容还不错,欢迎订阅🚩
🎊个人主页:小编的个人主页
🎀 🎉欢迎大家点赞👍收藏⭐文章
✌️ 🤞 🤟 🤘 🤙 👈 👉 👆 🖕 👇 ☝️ 👍

目录

  • 🐼前言
  • 🐼快速排序
  • 🐼hoare版本
  • 🐼挖坑版本
  • 🐼前后指针版本
  • 🐼文末

🐼前言

🌟在上一节我们实现了希尔排序,感受到了插入排序的魅力,如果感兴趣的小伙伴,可以阅读我的上一篇文章:> 希尔排序,这节小编将带领大家感受交换排序的美学。

🐼快速排序

🔍 快速排序:快速排序是一种二叉树结构的交换排序,思想是:任取排序序列中的数字为基准值,按照基准值将待排序列划分为左右两个子序列,保证左序列的值均小于基准值,右序列的值均大于基准值。然后左右子序列重复该过程,直到所有元素都排列在相应位置上为止
📋 总结:找基准值,划分左子序列,划分右子序列。每一轮排序都能定位一个基准值,这样每一次递归一个基准值就在相应位置上了。

🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟
快速排序主逻辑:

//快速排序
void QuickSort(int* arr, int left, int right)
{
	if (left >= right)
	{
		return;
	}
	int keyi = PartSort1(arr, left, right);//基准值
	QuickSort(arr, left,keyi-1);//左序列
	QuickSort(arr, keyi+1, right);//右序列
}

🌻代码解析

📋设置递归出口,如果左索引大于右索引,表示定位到所有基准值。接着,找基准值,保证左序列的值均小于基准值,右序列的值均大于基准值。keyi的位置已经排好,再递归左子树,(左序列:[left,keyi-1])再递归右子树(右序列[ keyi+1, right])
下面我们来实现找基准值,并让这个左序列的值均小于基准值,右序列的值均大于基准值这个步骤。

🐼hoare版本

🍐在上述 PartSort1⽤于按照基准值将区间[left,right)中的元素进行划分。我们先来实现hoare版本。
在hoare版本中,初始我们让基准值keyi就是第一个元素的下标,还需要定义两个指针left,right,一个指针right从右向左找比基准值小的或相等的元素,一个指针left需要从左向右找比基准值大的或相等元素。找到了两个元素,进行交换(并让left和right分别走一步,避免死循环),直到left>right,跳出循环。最后,让keyi处的值与右下标right的值交换。即arr[right]作为新的基准值,这样right作为基准值在该在的位置,也保证了左序列的值均小于基准值,右序列的值均大于基准值。
数组 {4,6,7,2,1,8,9,5,3 ,0}进行一轮:
在这里插入图片描述

hoare版本代码实现

// 快速排序hoare版本
int PartSort1(int* arr, int left, int right)
{
	int keyi = left;
	left++;//从keyi的下一个位置找
	while (left <= right)
	{
		//从右向左找比基准值小的
		while (left<=right && arr[right] > arr[keyi])
		{
			right--;
		}
		//从左向右找比基准值大的
		while (left <= right && arr[left] < arr[keyi])
		{
			left++;
		}
		//找到满足的right和left
		if (left <= right)
		{
			Swap(&arr[left++], &arr[right--]);
		}
	}
	//将基准值与右坐标交换
	Swap(&arr[keyi], &arr[right]);
	return right;
}

👀为什么在找基准值的最后(left>right),一定是arr[right]<=arr[keyi]
因为在遍历的过程中,left从左向右走,一定保证了比基准值小,当right走到left左边,一定比arr[keyi]小。
👀为什么在交换过程中,不是直接交换,交换完让right,left走一步?
在我们写的hoare排序版本中,我们right从右向左找比基准值小的或相等的元素,一个指针left需要从左向右找比基准值大的或相等元素。所以对于{2,2,2,2,2}这样的序列,left和right找到完,如果不更新,会造成死循环。
👀为什么left从左向右为什么要找到大于或等于的元素,为什么不直接找到大于的元素呢?
如果一定找到比基准值大或比基准值小的元素,那么对于一组升序的数字{1,2,3,4,5…}right从右向左找比基准值小的或相等的元素,直到找到了1,基准值1与1交换。然后基准值没有左序列,只有右序列,那么每次找基准值要排序的个数都少1。所以,如果数组原本就有序,或数组元素时间复杂度过高,就为O(N^2).
👀为什么当left和right相遇了,不跳出循环?
当left和right指针相遇时,并不立即跳出循环的原因是,即使两个指针相遇,它们指向的位置的元素可能与基准值不等,需要进一步比较和交换以达到排序的目的,比如如果left和right同时指向4,基准值为2,此时让2,4交换,显然错误,所以,right需要小于left。
🍂画图剖析:
排列数组为{4,1,6,7,3}
在这里插入图片描述
🍀测试结果:
在这里插入图片描述

🐼挖坑版本

🍐挖坑版本的还是需要需要创建两个指针leftright,其思想是:我们需要先事先挖好一个坑hole,假设坑位刚开始是起始位置。保存基准值key为起始坑的位置(key = arr[hole])。
🍐现在我们需要填坑,从右向左找一个比基准值小的数据,找到后立即放入左边坑中,当前位置变为新的"坑",然后从左向右找出比基准大的数据,找到后立即放入右边坑中,当前位置变为新的"坑",结束循环后将最开始存储的基准值放入当前的"坑"中返回当前"坑"下标(即基准值下标)。
挖坑版本代码实现:

// 快速排序挖坑法
int PartSort2(int* arr, int left, int right)
{
	int hole = left;
	int key = arr[hole];//保存起始洞值
	while (left < right)
	{
		//从右向左找到一个比基准值小的
		while (left<right && arr[right]>key)
		{
			right--;
		}
		//该值覆盖洞值,并更新洞
		arr[hole] = arr[right];
		hole = right;
		while (left<right && arr[left]<key)
		{
			left++;
		}
		arr[hole] = arr[left];
		hole = left;
	}
	arr[hole] = key;
	//返回基准值
	return hole;
}

🍂画图剖析:
在这里插入图片描述

🔍我们需要不断地从右向左找到比基准值小的,拿来填旧坑位,当前坑位就变成了新坑位,从左向右找到比基准值大的,拿来填旧坑位,当前坑位就变成了新坑位,最后,将最开始的基准值与当前坑位交换,当前坑位就是新基准值下标。这样从左向右找比基准值小的数据,放到前面。保证一轮循环下来。比基准值小的都在左边,比基准值大的都在右边。

🐼前后指针版本

🍐在前后指针中我们需要定义前后指针prev,cur。其思想是:让cur去前面探路,如果cur找到比基准值小的,然后prev先++,再将cur和prev所在位置交换。否则cur就一直向后找。最后,跳出循环,交换基准值和prev所在的数据。
🍐这样从左向右找比基准值小的数据,放到前面。保证一轮循环下来。比基准值小的都在左边,比基准值大的都在右边。
前后指针代码:

// 快速排序前后指针法
int PartSort3(int* arr, int left, int right)
{
	int prev = left, cur = prev + 1;
	int keyi = left;
	while (cur<=right)
	{
		if (arr[cur] < arr[keyi] && cur != ++prev)
		{
			Swap(&arr[cur], &arr[prev]);
		}
		cur++;
	}
	Swap(&arr[keyi], &arr[prev]);
	return prev;
}

🍂画图剖析:
在这里插入图片描述

👀为什么限制条件要加上cur != ++prev
因为当prev先走向下一个位置,此时刚好和cur重合,此时交换是无意义的,应该让cur继续移动。
👀为什么要先让prev++,再与cur交换
因为prev所在位置是已经调整好的数据,所以要先让prev先移动到下一个位置。

🐼文末

感谢你看到这里,如果觉得本篇文章对你有帮助,点个赞👍 吧,你的点赞就是我更新的最大动力 ⛅️🌈 ☀️

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2235850.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

OpenCV自学系列(1)——简介和GUI特征操作

与另一个计算机视觉系列相对应&#xff0c;本系列主要探索OpenCV的具体操作。 学习资源&#xff1a;官网教程 https://docs.opencv.org/4.x/d6/d00/tutorial_py_root.htmlhttps://docs.opencv.org/4.x/d6/d00/tutorial_py_root.html 所有源码均上传至仓库&#xff1a; http…

KION Group EDI 需求分析

梳理EDI需求资料 KION Group将EDI项目中需要的资料公开在其官网上&#xff0c;企业可以点击在 KION Group 官网下载 EDI需求资料 企业可以在以上网址中获取到如下资料&#xff1a; 1.KION Group EDI团队的联系信息以及EDI连接信息 KION Group EDI团队支持7*24小时的支持&am…

OpenDroneMap Webodm

OpenDroneMap & Webodm OpenDroneMap Webodm 开源无人机航拍系列图像及其它系列图像三维重建软件。很棒的开源无人机测绘软件OpenDroneMap,从航拍图像生成精确的地图、高程模型、3D 模型和点云。 应用领域 Mapping & Surveying 测绘和测量 从图像测量获得高精度的可…

计算机网络——网络层导论

转发是局部功能——数据平面 路由是全局的功能——控制平面 网卡 网卡&#xff0c;也称为网络适配器&#xff0c;是计算机硬件中的一种设备&#xff0c;主要负责在计算机和网络之间进行数据传输。 一、主要功能 1、数据传输&#xff1a; 发送数据时&#xff0c;网卡将计算机…

从0开始深度学习(26)——汇聚层/池化层

池化层通过减少特征图的尺寸来降低计算量和参数数量&#xff0c;同时增加模型的平移不变性和鲁棒性。汇聚层的主要优点之一是减轻卷积层对位置的过度敏感。 1 最大汇聚层、平均汇聚层 汇聚层和卷积核一样&#xff0c;是在输入图片上进行滑动计算&#xff0c;但是不同于卷积层的…

深度学习笔记10-多分类

多分类和softmax回归 在多分类问题中&#xff0c;一个样本会被划分到三个或更多的类别中&#xff0c;可以使用多个二分类模型或一个多分类模型&#xff0c;这两种方式解决多分类问题。 1.基于二分类模型的多分类 直接基于二分类模型解决多分类任务&#xff0c;对于多分类中的每…

接口测试面试题及答案(后续)

一、你们什么时候测试接口 一般有需求就会做&#xff0c;后台的接口开发好&#xff0c;就可以开始测。例外&#xff0c;如果增加了新需求&#xff0c;也要做接口测试&#xff0c;还有就是开发对后台的接口做了修改&#xff0c;交互逻辑发生变化&#xff0c;我们也要重新对接口…

【SpringCloud】Nacos微服务注册中心

微服务的注册中心 注册中心可以说是微服务架构中的"通讯录"&#xff0c;它记录了服务和服务地址的映射关系 。在分布式架构中&#xff0c; 服务会注册到这里&#xff0c;当服务需要调⽤其它服务时&#xff0c;就从这里找到服务的地址&#xff0c;进行调用。 注册中心…

服务器数据恢复—分区结构被破坏的reiserfs文件系统数据恢复案例

服务器数据恢复环境&#xff1a; 一台服务器中有一组由4块SAS硬盘组建的RAID5阵列&#xff0c;上层安装linux操作系统统。分区结构&#xff1a;boot分区LVM卷swap分区&#xff08;按照顺序&#xff09;&#xff0c;LVM卷中划分了一个reiserfs文件系统作为根分区。 服务器故障…

Linux下的WatchDog

看门狗&#x1f415; 看门狗简介 看门狗定时器&#xff08;Watchdog Timer&#xff09;是一种定时器&#xff0c;用于检测系统是否正常运行。如果系统在规定时间内没有向看门狗定时器发送复位信号&#xff0c;看门狗定时器就会产生复位信号&#xff0c;使系统复位。看门狗定时…

机器学习(五)——支持向量机SVM(支持向量、间隔、正则化参数C、误差容忍度ε、核函数、软间隔、SVR、回归分类源码)

目录 关于1 间隔与支持向量2 对偶问题3 核函数4 软间隔与正则化5 支持向量回归6 核方法X 案例代码X.1 分类任务X.1.1 源码X.1.2 数据集&#xff08;鸢尾花数据集&#xff09;X.1.3 模型效果 X.2 回归任务X.2.1 源码X.2.2 数据集&#xff08;加州房价数据&#xff09;X.2.3 模型…

25国考照片处理器使用流程图解❗

1、打开“国家公务员局”网站&#xff0c;进入2025公务员专题&#xff0c;找到考生考务入口 2、点击下载地址 3、这几个下载链接都可以 4、下载压缩包 5、解压后先看“使用说明”&#xff0c;再找到“照片处理工具”双击。 6、双击后会进入这样的界面&#xff0c;点击&…

几个docker可用的镜像源

几个docker可用的镜像源 &#x1f490;The Begin&#x1f490;点点关注&#xff0c;收藏不迷路&#x1f490; sudo rm -rf /etc/docker/daemon.json sudo mkdir -p /etc/dockersudo tee /etc/docker/daemon.json <<-EOF {"registry-mirrors": ["https://d…

Vue 项目中为何选择 TSX 而非传统 .vue 文件

近年来&#xff0c;Vue 项目中使用 TSX&#xff08;TypeScript JSX&#xff09;的写法逐渐增多&#xff0c;尤其在 TypeScript 项目中。 1. TSX 与 Vue 的结合背景 1、Vue 3 和 TypeScript Vue 3 从设计之初便更好地支持 TypeScript。Vue 3 使用了 TypeScript 重写核心&…

【SpringBoot】18 上传文件到数据库(Thymeleaf + MySQL)

Git仓库 https://gitee.com/Lin_DH/system 介绍 使用 Thymeleaf 写的页面&#xff0c;将&#xff08;txt、jpg、png&#xff09;格式文件上传到 MySQL 数据库中。 依赖 pom.xml <!-- https://mvnrepository.com/artifact/com.mysql/mysql-connector-j --><depende…

[VUE]框架网页开发1 本地开发环境安装

前言 其实你不要看我的文章比较长&#xff0c;但是他就是很长&#xff01;步骤其实很简单&#xff0c;主要是为新手加了很多解释&#xff01; 步骤一&#xff1a;下载并安装 Node.js 访问 Node.js 官网&#xff1a; Node.js — Download Node.js 下载 Windows 64 位版本&…

canal1.1.7使用canal-adapter进行mysql同步数据

重要的事情说前面&#xff0c;canal1.1.8需要jdk11以上&#xff0c;大家自行选择&#xff0c;我这由于项目原因只能使用1.1.7兼容版的 文章参考地址&#xff1a; canal 使用详解_canal使用-CSDN博客 使用canal.deployer-1.1.7和canal.adapter-1.1.7实现mysql数据同步_mysql更…

403 Request Entity Too Lager(请求体太大啦)

昨天收到 QA 的生产报障&#xff0c;说是测试环境的附件上传功能报了 403 的错误&#xff0c;错误信息&#xff1a;403 Request Entity Too Lager。我尝试复现问题&#xff0c;发现传个几兆的文件都费劲啊&#xff0c;一传一个失败。不用说&#xff0c;项目用到 ng 代理&#x…

【VScode】如何在VSCode中配置Python开发环境:从零开始的完整指南

文章目录 前言软件准备软件安装1. 安装Python2. 检查Python是否安装成功3. 安装第三方包4. 安装VSCode 配置VSCode1. 安装Python插件2. 创建项目&#xff0c;配置工作区域3. 编写Python文件4. 配置Python编译设置5. 使用代码格式化工具yapf 更多文章结尾 前言 在当今的编程世界…

SQL,力扣题目571, 给定数字的频率查询中位数

一、力扣链接 LeetCode_571 二、题目描述 Numbers 表&#xff1a; ------------------- | Column Name | Type | ------------------- | num | int | | frequency | int | ------------------- num 是这张表的主键(具有唯一值的列)。 这张表的每一行表示某个数…