(手撕)快速排序 ----->c语言实现 +图解

news2024/11/26 13:53:00

目录

目录:

        1:快速排序的思想

        2:快速排序的三种形式

        3:快速排序的优化方法


    前言:快速排序是一种非常重要的排序我们需要掌握它,当然肯定也相比前面的那些排序有一定的难度,但是相信本篇文章会让你对快排有重新的理解,让快排变得不再那么难!!让我么一起加油吧!!

1:快速排序的思想

        在这里我们是用快速排序排升序!

        其实快速排序的思想与我们之前学过的二叉树的知识有点联系,快速排序一趟思想主要是遍历一遍数组,确定一个数的位置,我们假设这个位置是keyi,一般keyi的值为区间最左边的值,我们就是让在keyi左边数的值全部小于这个位置处的值,即小于a[keyi],在keyi位置右边的值全部大于a[keyi]。这样说可能还是有点抽象,我们借用图来理解一下这个思想。

        在这一趟快排结束后我们就在数组中确定了一个数的位置,且这个数的位置在之后的操作不需要管了。

快排的总思想:采用了分治的思想,就是对数组进行多次的排序,让小区间有序,最后整体就会有序了。如图

        总结:快速排序是采用了分治的思想,一趟快速排序能够确定一个数的位置,然后我们只需要将该位置处左边的数组有序,与右边有序,那么整体就会有序了。

        下面让我们来手撕一下快速排序的底层具体代码吧!!

  2:快速排序的三种方法

        说实话,可能我们在平时面试的时候可能只需要掌握一种情况就可以了,但是当我们在学习的时候我们就要增加自己的内功,多认识几种方法总会是好的~~

        这些代码我们已近加上了三数取中的优化方法,至于为啥可以看到第三个标题哦。

        1:hoare提出的

        我们先直接上代码,然后我们来一起手撕这个代码!

                

void QuickSort1(int* a, int left,int right)
{
	//[begin,end],控制区间用来递归
	int midi = GetMidi(a, left, right);
	Swap(&a[midi], &a[left]);
	if (left >= right)
		return;
	int begin = left;
	int end = right;
	int keyi = left;
	while (begin < end)
	{
		//右边找小
		while (begin < end && a[end] >= a[keyi])
		{
			end--;
		}

		//左边找大
		while (begin < end && a[begin] <= a[keyi])
		{
			begin++;
		}
		Swap(&a[begin], &a[end]);

	}
	Swap(&a[keyi], &a[begin]);
	
	QuickSort1(a, left,begin-1);
	QuickSort1(a, begin + 1,right);
}

hoare大佬的思想是这样子的:首先定义两个指针,一左一右  我们姑且就认为是begin与end吧!,先假设keyi的位置为left处的位置,然后让右指针先走找到比a[keyi]值小的,在左指针走找大于a[keyi]处的值,然后交换a[begin]与a[end],然后循环的寻找,直到begin与end相遇我们才认为我们的循环结束,最后在交换a[keyi]与a[begin]或者是a[end]因为在相遇位置处一定是begin==end的。

        代码要注意的点:第一个我们因该保留的是key位置的下标,不然在进行交换的时候我们不能将数组中的两个位置交换,他可能只是数组中的1个值与key变量进行交换。

        第二个就是我们在使用内部循环的时候要注意begin要小于end如果不小于的话,那可能我们的数组会形成越界,

        我们通过图来解析这个思路

如果你没看懂这个图的话,建议拿着代码在去动手画一下图。就应该能懂了。

        

2挖坑法

        老规矩还是先上代码:

        

void QuickSort3(int* a, int left, int right)
{
	int midi = GetMidi(a, left, right);
	Swap(&a[midi], &a[left]);
	//先将第一个坑给挖出来,并且值保留
	if (left >= right)
	{
		return;
	}

	int key = a[left];
	int hole = left;
	int end = right;
	int begin = left;
	while (begin < end)
	{
		//右边找小
		while (begin < end && a[end] >= key)
		{
			end--;
		}
		//将找到小的值,移动到坑处
		Swap(&a[hole], &a[end]);
		hole = end;
		//左边找大
	while (begin < end && a[begin] <= key)
		{
			begin++;
		}
		Swap(&a[hole], &a[begin]);
		hole = begin;

	}
	a[hole] = key;

	QuickSort3(a, left, hole - 1);
	QuickSort3(a, hole + 1, right);

}

挖坑法其实本质上只是hoare方法的一种优化,它是首先将数组中第一个数的值放到一个key变量的值,在图上就是将6放到key变量中,这样我们假设我们的数组形成了一个坑位,还是采用老方法,一左一右两个指针,但是与hoare方法不同的思想就在于:右边找小,找到小了我们就将这个值放到坑位上去,然后我们将坑位变成右边这个指针处,然后左指针开始找大,找到大的值后我们将它放入坑中,在将坑位变成左指针处,循环的进行下去,直到左指针==右指针,最后我们在将key放入坑位中,这样我们的一趟快速排序就完成了,我们在采用分治的思想递归坑的左边,递归坑的右边,当递归完成的时候我们的快速排序也就完成了.

   这里我们就用图来画一趟内部的快速排序!

             

 3双指针法

void QuickSort2(int* a, int left, int right)
{
	if (left >= right)
		return;
	int midi = GetMidi(a, left, right);
	Swap(&a[midi], &a[left]);

	
	int prev = left;
	int cur = prev + 1;
	int keyi = left;
	while (cur <=right)
	{
		//cur找小,找到小,先++prev然后在交换
		if (a[cur] < a[keyi])
		{
			++prev;
			Swap(&a[cur], &a[prev]);
		}
		cur++;
	}
	Swap(&a[keyi], &a[prev]);
	QuickSort2(a, left, prev - 1);
	QuickSort2(a, prev+1, right);

}

前后指针的一趟思想:

        我们先定义一个prev指针指向要分治数组的左边,在定义一个cur指针它的值等于prev指针+1,定义一个keyi为数组的左边的值,cur指针找小,当cur找到小的时候我们就先++prev指针,然后在与cur指针进行交换,当cur大于keyi的值时,我们就++cur就行,当cur大于数组最后一个值的下标时,我们的一趟循环就结束了。

        我们上一个动图来与该思路一起配合起来。

在这里我们就将快速排序的3种思想就讲解完毕了!

  如果还有不懂得小伙伴可以私信我哦!!

3:快速排序的优化

        我们知道,快速排序的时间复杂度最坏的情况下就是要排序数组是逆序的,此时快速排序的时间复杂度为O(N^2)的,为啥是这样呢?因为我们在这种情况下我们遍历一遍数组选择一个位置,所以总的来说就是O(N^2),但是在平常我们在使用的时候为什么我们说快速排序的时间复杂度是O(N*logN)呢,因为我们对三面三种情况下的代码都增加了三数取中的思想,这样能够是我们呢的快速排序形成分治的思路。防止了逆序的出现。

        代码如下:

        优化方法1:三数取中

        相信三数取中的代码的思想不是很难我们返回的是数组的下标哦,我们只需要两两进行比较就行了。

int GetMidi(int* a, int left, int right)
{
	int mid = (left + right) / 2;
	// left mid right
	if (a[left] < a[mid])
	{
		if (a[mid] < a[right])
		{
			return mid;
		}
		else if (a[left] > a[right])  // mid是最大值
		{
			return left;
		}
		else
		{
			return right;
		}
	}
	else // a[left] > a[mid]
	{
		if (a[mid] > a[right])
		{
			return mid;
		}
		else if (a[left] < a[right]) // mid是最小
		{
			return left;
		}
		else
		{
			return right;
		}
	}
}

优化方法2:减少递归的调用次数

        我们知道递归是在栈区进行的,而栈区的空间相对于其它区域的空间来说,它并不是很大的,所以当我们递归的高度过多的时候我们的函数就会出现栈溢出的bug。

        那我们如何优化?

        首先我们知道假设我们的数组非常大,我们对他进行快排,当子数组长度非常小的时候我们假设为10,那么这个子数组肯定相对于原来的数组来说,是有序的,这时候我们采用直接插入排序就可以优化我们的快速排序了,好像库里面也是这样实现的。

        那为什么我们要当结点个数小于10的时候就不递归了呢?如下分析

我们知道二叉树中最后一层的结点个数接近数组整个长度的一半,而倒数三层所加起来的结点的个数≈%87.5,所以我们就选用了10这个数字。

        总结优化思路:规定数组大小那些不需要进行优化与直接插入排序的使用。

        代码如下:

        

就将这条语句放入QuickSort中就行了
if (right - left + 1 < 10)
	{
		InsertSort(a + left, right- left + 1);
	}

到这里我们的快速排序就基本上手撕完毕了,感谢大家的观看,如果你觉得对你有用的话,可以点个赞哦!!

        

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

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

相关文章

vue3 + mark.js | 实现文字标注功能

页面效果 具体实现 新增 1、监听鼠标抬起事件&#xff0c;通过window.getSelection()方法获取鼠标用户选择的文本范围或光标的当前位置。2、通过 选中的文字长度是否大于0或window.getSelection().isCollapsed (返回一个布尔值用于描述选区的起始点和终止点是否位于一个位置&…

TensorFlow入门(三、TensorFlow模型的运行机制)

TensorFlow通过"图"和会话的方式分离了计算的定义和执行,即它的运行机制是"定义"与"运行"相分离的。从操作层面可以把它抽象成两种:模型构建和模型运行。 TensorFlow模型中的几个概念: ①张量(tensor):数据,即某一类型的多维数组 ②变量(Vari…

Wi-Fi直连分享:Android设备间的高速连接

Wi-Fi直连分享&#xff1a;Android设备间的高速连接 引言 随着无线局域网&#xff08;Wi-Fi&#xff09;的普及和发展&#xff0c;使用Wi-Fi直连技术&#xff08;P2P&#xff09;在没有中间接入点的情况下实现设备间直接互联成为可能。通过Wi-Fi直连&#xff0c;具备相应硬件…

链动2+1模式:如何用二级分销打造高效团队,实现销量突破

你是否想要拥有一个高效的团队&#xff0c;让你的销量快速提升&#xff1f;你是否厌倦了传统的多层级分销模式&#xff0c;觉得它太复杂、太难管理、太不合规&#xff1f;你是否想要找到一种简单、合理、合法的商业模式&#xff0c;让你的收益稳定、可持续、可复制&#xff1f;…

【深度学习实验】卷积神经网络(三):自定义二维卷积神经网络:步长和填充、输入输出通道

目录 一、实验介绍 二、实验环境 1. 配置虚拟环境 2. 库版本介绍 三、实验内容 0. 导入必要的工具包 1. 步长、填充 a. 二维互相关运算&#xff08;corr2d&#xff09; b. 二维卷积层类&#xff08;Conv2D&#xff09; c. 模型测试 d. 代码整合 2. 输入输出通道 a…

前缀树-Trie树

前缀树—Trie树&#xff0c;也叫作“单词查找树”、“字典树” 它属于多叉树结构&#xff0c;典型应用是用于统计&#xff0c;排序和保存大量的字符串&#xff08;但不仅限于字符串&#xff09;&#xff0c;所以经常被搜索引擎系统用于文本词频统计。它的优点是&#xff1a;利…

【前段基础入门之】=>玩转【CSS】开篇章!

目录 CSS 的简介&#xff1a;CSS的编写位置行内样式内部样式外部样式 样式表的优先级CSS语法规范&#xff1a; 总结&#xff1a; CSS 的简介&#xff1a; 层叠样式表&#xff08;Cascading Style Sheets&#xff0c;缩写为 CSS&#xff09;是一种样式表语言&#xff0c;用来描述…

前端项目练习(练习-007-typescript-02)

学习前&#xff0c;首先&#xff0c;创建一个web-007项目&#xff0c;内容和web-006一样。&#xff08;注意将package.json中的name改为web-007&#xff09; 前面的例子&#xff0c;我们使用了nodejswebpack&#xff0c;成功创建了包含html&#xff0c;ts&#xff0c;css三个文…

【.net core】使用nssm发布WEB项目

nssm下载地址&#xff1a;NSSM - the Non-Sucking Service Manager 配置方式 修改服务在nssm工具下输入命令:nssm edit jntyjr 其中 jntyjr为添加服务时设置的Service name nssm可以设置任何以参数启动的应用程序以服务形式启动,通过设置参数内容启动服务 以上配置等同于执行…

ReferenceError: primordials is not defined错误解决

问题场景&#xff1a; 从github上拉了一个项目&#xff0c;想要学习一下&#xff0c;在起服务的时候出现了这个问题。 造成的原因&#xff1a; gulp 与 node 版本起冲突。 1&#xff09;首先&#xff0c;安装 gulp&#xff0c;查看版本&#xff1b; npm install gulp -g g…

如何设计科研问卷?

问卷研究法的最大特点在于能在较短时间内调查很多研究对象取得大量的资料&#xff0c;并能对资料进行数量化处理&#xff0c;经济省时&#xff0c;因此是教育研究中使用频率较高、用途较广泛的一种研究方法。问卷研究法的关键在于设计一份信度、效度较高&#xff0c;内容合理的…

二维码怎么分解成链接?线上快速解码教学

怎么分解二维码呢&#xff1f;有些时候我们需要将二维码图片分解成链接使用&#xff0c;所以想要使用解码功能一般都需要通过二维码生成器工具来完成。那么如何在线将二维码分解成链接呢&#xff0c;可能有些小伙伴还不知道怎么操作&#xff0c;下面就给大家分享一下免费二维码…

较真儿学源码系列-PowerJob时间轮源码分析

PowerJob版本&#xff1a;4.3.2-main。 之前分析过PowerJob的启动流程源码&#xff0c;感兴趣的可以查看《较真儿学源码系列-PowerJob启动流程源码分析》 1 简介 试想一下&#xff0c;如果此时有一个需要延迟3s执行的任务&#xff0c;你会怎么实现呢&#xff1f;一种常规的思路…

洗地机哪个牌子好用又实惠?口碑最好的洗地机推荐

智能技术飞速发展的时代&#xff0c;扫地机器人这类智能家电其实也在顺应潮流和用户需求&#xff0c;不断更新迭代。暂且不说市面上现有多少个洗地机品牌&#xff0c;单单一个洗地机品牌旗下&#xff0c;其实每年都会有多个系列的新品亮相&#xff0c;我们面对的选择多了&#…

Python交叉验证实现

目录 <font colorblue size4 face"楷体">HoldOut 交叉验证<font colorred size4 face"楷体">K 折交叉验证<font colorblue size4 face"楷体">分层 K 折交叉验证<font colorblue size4 face"楷体">Leave P Out…

融云 CallPlus + X,通话场景一站式解决方案

融云近期上线的 CallPlus SDK&#xff0c;针对音视频呼叫场景单独设计后端服务 Call Server&#xff0c;信令延时低至 150ms&#xff0c;确保各端计时准确、一致&#xff1b;上线了音视频通话互转、灵活的多人通话、通话记录管理能力等功能。关注【融云全球互联网通信云】了解更…

掌动智能兼容性测试有哪些优势

兼容性测试为企业带来市场竞争优势&#xff0c;并提高用户满意度。在软件开发过程中&#xff0c;将兼容性测试作为一个重要的环节&#xff0c;将为企业的成功和用户满意度打下坚实的基础。那么&#xff0c;掌动智能兼容性测试的具体优势是什么?下面&#xff0c;就来看看具体介…

【面试题】说说你对 async和await 理解

前端面试题库 &#xff08;面试必备&#xff09; 推荐&#xff1a;★★★★★ 地址&#xff1a;前端面试题库 表妹一键制作自己的五星红旗国庆头像&#xff0c;超好看 async await详解 原理&#xff1a; async声明该函数是异步的&#xff0c;且该函数会返回一个…

比例导引详解(Proportional navigation guidance,PNG)-及Python程序

模型算法推导 比例导引是一种制导算法&#xff0c;其经典程度相当于控制器中的PID&#xff0c;在本文中&#xff0c;只对其二维平面的情况做分析&#xff0c;考虑一个拦截弹拦截机动目标&#xff08;固定目标相当于目标速度为0&#xff09;&#xff0c;其运动如下图所示&#…

变配电智能化系统:提高效率与安全性

随着科技的发展&#xff0c;电力系统正在逐步向智能化、数字化方向转型。变配电系统作为电力系统的重要组成部分&#xff0c;其智能化水平直接影响着电力系统的运行效率和稳定性。 一、系统概述 力安科技变配电智能化系统是一种采用先进技术&#xff0c;实现对变配电设…