【数据结构】快速排序(4种方式实现)

news2025/1/10 17:11:08

前言:前面我们学习了几种相对比较简单的排序,今天我们要一起学习的是快速排序,我们将通过四种方式来模拟实现快排。

💖 博主CSDN主页:卫卫卫的个人主页 💞
👉 专栏分类:数据结构 👈
💯代码仓库:卫卫周大胖的学习日记💫
💪关注博主和博主一起学习!一起努力!
在这里插入图片描述


C语言算法-快速排序

什么是快速排序

任取待排序元素序列中的某元素作为基准值,按照该排序码将待排序集合分割成两子序列,左子序列中所有元素均小于基准值,右子序列中所有元素均大于基准值,然后最左右子序列重复该过程,直到所有元素都排列在相应位置上为止。

快速排序之hoare版

hoare思想

1.首先我们选定一个基准值,通常是数组中的第一个元素。
2. 定义俩个指针,一个left一个right分别在数组的最左边和最右边。
3. 我们让右指针先走,如果比我们定义的基准值小就停下来。
4. 右指针走完我们在让左指针走,如果比我们定义的基准值小也停下来。
5. 在俩个指针都停下来的时候把它们的值进行交换,以此反复循环直到俩个指针相遇,我们把基准值和它们的值进行交换。
6. 最终这一趟下来我们会得到如下图一样的数据,基准值左边的都比基准值小,右边的都比基准值大。
7. 我们把这一趟走完后在重新分为左右俩个部分的数据,在用此方法以此往复即可实现一个有序数组。

请添加图片描述
代码思路:刚刚我们用那个思想可以实现一次的排序过程,可是排完一次那剩下的怎么排呢?我们可以把这个问题拆成许多个小问题(如下图所示),因此我们可以采用递归的思想来实现它。
在这里插入图片描述


代码实现:

void QuickSort1(int* a,int begin, int end)//快速排序 -- hoare
{
	int right = end;
	int left = begin;
	int key = begin;
	if (begin >= end)
	{
		return;
	}
	while (left < right)
	{
		while (a[right] >= a[key] && right >left)
		{
			right--;
		}
		while (a[left] <= a[key] &&  right > left)
		{
			left++;
		}
		Swap(&a[right], &a[left]);
	}
	Swap(&a[key], &a[left]);
	key = left;
	QuickSort1(a, begin, left - 1);//左边
	QuickSort1(a, key+1, end);//右边
}

测试函数:

void Test_QuickSort1()
{
	int a[] = { 20,16,30,18,12,21,26,21 };
	QuickSort1(a, 0, sizeof(a) / sizeof(a[0]) - 1);
	PrintArray(a, sizeof(a) / sizeof(a[0]));

}

运行结果:
在这里插入图片描述


快速排序之挖坑法

挖坑思想:

  1. 首先我们和前面的hoare法一样,我们选定一个基准值,通常是数组中的第一个元素。
  2. 我们定义一个坑位(hole)放在基准值的位置的下面。
  3. 我们同理定义俩个指针leftright一个在数组的最左边一个在最右边。
  4. 不同的是,我们right指针找到比基准值小的值的时候,就直接和坑位所对应的值交换,并把right此时的位置置为hole
  5. 同理left也行动开始找值补坑,找到比基准值大的值将其放入坑位,置为新的坑。
  6. 以此往复,当leftright相遇的时候,在将基准值放入坑位。此时我们也会发现和hoare一样的排序结果,右边的值都比基准小,左边的都比其大。(如下图所示)
    请添加图片描述

代码实现:

void PartSort2(int* a,int begin ,int end)//快速排序 -- 挖坑法
{
	int right = end;
	int left = begin;
	int hole = left;

	int key = a[begin];
	if (begin >= end)
	{
		return;
	}
	while(left < right)
	{
		while(a[right] >= key && right > left)
		{
			right--;
		}
		a[hole] = a[right];
		hole = right;
		while (a[left] <= key && right > left)
		{
			left++;
		}
		a[hole] = a[left];
		hole = left;
	}
	a[hole] = key;
	QuickSort1(a, begin, hole - 1);//左边
	QuickSort1(a, hole + 1, end);//右边
}

测试函数:

void Test_PartSort2()
{
	int a[] = { 20,16,30,18,12,21,26,21 };
	PartSort2(a, 0, sizeof(a) / sizeof(a[0]) - 1);
	PrintArray(a, sizeof(a) / sizeof(a[0]));

}

运行结果:
在这里插入图片描述


快速排序之前后指针法

双指针思想

双指针法相较于前面俩种方式,是目前主流的一种写法,这里我们依然来看看单趟的走法。

  1. 这里我们依然和前面的俩种法一样,我们选定一个基准值,通常是数组中的第一个元素。
  2. 定义一个pre和一个cur指针,同理,我们让cur指针先走,如果遇到比基准值小的值我们就停下来,然后让pre指针往前走一步,我们再对其进行交换。
  3. 注意,在交换的时候我们pre指针和cur指针不能是处于同一位置。
  4. pre指针和cur指针相遇的时候,我们就让此次循环终止,在将基准值和此时的pre值进行交换即可(cur值可能已经走出边界所以不能是cur)。此时我们也会发现和hoare和挖坑法一样的排序结果,右边的值都比基准小,左边的都比其大。(如下图所示)
    请添加图片描述

代码实现

void PartSort3(int* a, int begin, int end)//双指针法   //cur遇到比key大的++cur    cur遇到比key小的值,++prev,交换pre和cur的位置的值, ++cur
{
	int pre = begin;
	int cur = pre+1;
	int key = begin;
	while (cur <= end && pre <= end)
	{
		if(a[cur] < a[key] && cur != ++pre)
		{
			Swap(&a[cur], &a[pre]);
		}
		++cur;
	}
	Swap(&a[key], &a[pre]);
	QuickSort1(a, begin, pre - 1);//左边
	QuickSort1(a, pre + 1, end);//右边
}

函数测试

void Test_PartSort3()
{
	int a[] = { 20,16,30,18,12,21,26,21 };
	PartSort3(a, 0, sizeof(a) / sizeof(a[0]) - 1);
	PrintArray(a, sizeof(a) / sizeof(a[0]));
}

运行结果:
在这里插入图片描述


快速排序之非递归排序

非递归思想

因为递归是在函数栈帧是在栈上开辟的,十分容易出现溢出的现象,为了防止这个问题,我们有一种非栈帧的方式来实现排序,就是通过非递归的方式来实现,即用栈(前面所学的数据结构中的栈)来模拟实现。

  1. 入栈一定得保证先左再右即我们先进去的是左区间,再进去的是右区间。
  2. 将每次入栈的数据进行单趟排序。
  3. 再将剩余的部分划分成 [left,key-1][key+1,right]
  4. 循环此操作直到栈中的数据全部为空即可(如果不理解可以看下图)。
    在这里插入图片描述

代码实现:

int PartSort4(int* a, int begin, int end)//双指针法   //cur遇到比key大的++cur    cur遇到比key小的值,++prev,交换pre和cur的位置的值, ++cur
{
	int pre = begin;
	int cur = pre + 1;
	int key = begin;
	while (cur <= end && pre <= end)
	{
		if (a[cur] < a[key] && cur != ++pre)
		{
			Swap(&a[cur], &a[pre]);
		}
		++cur;
	}
	Swap(&a[key], &a[pre]);
	key = pre;
	return key;
}

void QuickSortNonR(int* a, int left, int right)
{
	ST st;
	STInit(&st);
	STPush(&st, left);
	STPush(&st, right);
	while (!STEmpty(&st))
	{
		int end = STTop(&st);//右边
		STPop(&st);
		int begin = STTop(&st);//左边
		STPop(&st);
		int key = PartSort4(a, begin,end);//第一趟排序的区间
		//[left, key - 1] key [key + 1, right];
		if (key + 1 < end)//判断此时是左区间还是右区间
		{
			STPush(&st, key + 1);
			STPush(&st, end);
		}
		if (begin < key - 1)
		{
			STPush(&st, begin);
			STPush(&st, key - 1);
		}

	}
	STDestroy(&st);
}


测试函数:

void Test_QuickSortNonR()
{
	int a[] = { 20,16,30,18,12,21,26,21 };

	QuickSortNonR(a,0, sizeof(a) / sizeof(a[0]) - 1);
	PrintArray(a, sizeof(a) / sizeof(a[0]));

}

运行结果:
在这里插入图片描述


结语:今天的内容就到这里吧,谢谢各位的观看,如果有讲的不好的地方也请各位多多指出,作者每一条评论都会读的,谢谢各位。


🫵🫵🫵 这里提前祝各位新年快乐呀! 💞

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

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

相关文章

Impala4.x源码阅读笔记(三)——Impala如何管理Iceberg表元数据

前言 本文为笔者个人阅读Apache Impala源码时的笔记&#xff0c;仅代表我个人对代码的理解&#xff0c;个人水平有限&#xff0c;文章可能存在理解错误、遗漏或者过时之处。如果有任何错误或者有更好的见解&#xff0c;欢迎指正。 上一篇文章Impala4.x源码阅读笔记&#xff0…

函数式编程的妙用

前言 我们平常项目中维护的比较多的就是实体类中的数量问题&#xff0c;我们最常见的做法就是通过get方法读取旧数据&#xff0c;然后进行新数据的set 。这套方法相对来说是比较统一固定的&#xff0c;如果有多处地方使用&#xff0c;我们可以想着通过Function和BiConsumer的函…

防爆气象环境监测站设备的应用场所

TH-FBCQX2防爆气象环境监测站设备应用广泛&#xff0c;主要用于对各种危险品、易爆品等场所的气象环境进行实时监测和预警&#xff0c;保障安全生产和人员安全。 这些设备通常采用防爆设计&#xff0c;能够承受恶劣的环境条件&#xff0c;如高温、低温、潮湿、震动等&#xff0…

「亲测有效」ChatGPT Plus会员/GPT4开通方法 — 仅需支付宝或微信

这是我这两天找到的一个「只需要有支付宝或者微信」就可行的会员开通方法。 这个方法亲测有效&#xff0c;半个小时前给一个新的ChatGPT账号成功开通Plus会员&#xff0c; 并且只要有微信或支付宝就能成功支付 准备工作 首先我们准备好一个没有开通GPT4的ChatGPT账号&#xf…

Java学习——设计模式——创建型模式2

文章目录 创建型模式原型建造者模式扩展 创建型模式对比 创建型模式 关注点是如何创建对象&#xff0c;核心思想是要把对象创建和使用相分离&#xff0c;这样两者能相对独立地变换 包括&#xff1a; 1、工厂方法&#xff1a;Factory Method 2、抽象工厂&#xff1a;Abstarct Fa…

Factory Method工厂模式(对象创建)

Factory Method&#xff08;对象创建&#xff09; 链接&#xff1a;工厂模式实例代码 解析 目的 在软件系统中&#xff0c;经常面临着创建对象的工作&#xff1b;由于需求的变化&#xff0c;需要创建的对象的具体类型经常变化。 如何应对这种变化&#xff1f;如何绕过常规的…

什么是工厂方法模式,工厂方法模式解决了什么问题?

工厂方法模式是一种创建型设计模式&#xff0c;它定义了一个用于创建对象的接口&#xff0c;但将实际的实例化过程延迟到子类中。这样&#xff0c;客户端代码在不同的子类中实例化具体对象&#xff0c;而不是直接实例化具体类。工厂方法模式允许一个类的实例化延迟到其子类&…

词表示:语言与计算的桥梁

目录 前言1 什么是词表示2 独热表示3 上下文表示4 分布式表示结语 前言 在自然语言处理领域&#xff0c;词语的表示是一个基本挑战。将词语转换为计算机可以理解的符号&#xff0c;衡量词语之间的相似度&#xff0c;捕捉它们之间复杂的关系&#xff0c;是使机器能够理解和处理…

RTC实时时钟

简介 RTC时钟是一个独立的定时器&#xff0c;可以在后备电源不掉电的情况下一直运行。在对应的软件配置下一般可以做时钟日历功能。   RTC模块和时钟配置系统&#xff08;RCC_BDCR寄存器&#xff09;是在后备区域&#xff0c;即使系统复位或者待机唤醒后RTC的设置和时间都维持…

图片格式 WebP、JPEG、PNG、SVG 及转换

文章目录 图片格式 WebP、JPEG、PNG、SVG 及转换1. 图片格式1.1 WebP1.2 JPEG1.3 PNG1.4 SVG1.5 ... 2. 格式转换2.1 Python 批量转 WebP2.2 在线转换工具2.2.1 Shutterstock2.2.2 PicWish2.2.3 MyEdit2.2.4 Freeconvert2.2.5 iLoveIMG Reference 图片格式 WebP、JPEG、PNG、SV…

WPF+Halcon 培训项目实战(8):WPF+Halcon初次开发

前言 为了更好地去学习WPFHalcon&#xff0c;我决定去报个班学一下。原因无非是想换个工作。相关的教学视频来源于下方的Up主的提供的教程。这里只做笔记分享&#xff0c;想要源码或者教学视频可以和他联系一下。 相关链接 微软系列技术教程 WPF 年度公益课程 Halcon开发 CSD…

MySQL数据库性能优化中常用的方法是什么?

MySQL是目前广泛使用的关系型数据库系统&#xff0c;随着数据量的不断增加和业务需求的提升&#xff0c;MySQL数据库性能优化已经成为开发人员和DBA必须面对的一个重要问题。 查询语句是MySQL数据库中最常用的操作之一&#xff0c;也是造成性能问题的主要原因之一。以下是一些常…

SpringMVC之视图和RESTful

学习的最大理由是想摆脱平庸&#xff0c;早一天就多一份人生的精彩&#xff1b;迟一天就多一天平庸的困扰。各位小伙伴&#xff0c;如果您&#xff1a; 想系统/深入学习某技术知识点… 一个人摸索学习很难坚持&#xff0c;想组团高效学习… 想写博客但无从下手&#xff0c;急需…

CSI多普勒效应

CSI多普勒效应 一、定义二、应用三、计算方法1方法2STFT和DFT间的区别 一、定义 多普勒频移是指由于运动引起的信号频率的变化。当信号源相对于接收器运动时&#xff0c;由于多普勒效应&#xff0c;信号的频率会发生改变。多普勒频移可以通过以下公式表示&#xff1a; 二、应…

4.16 构建onnx结构模型-And

前言 构建onnx方式通常有两种&#xff1a; 1、通过代码转换成onnx结构&#xff0c;比如pytorch —> onnx 2、通过onnx 自定义结点&#xff0c;图&#xff0c;生成onnx结构 本文主要是简单学习和使用两种不同onnx结构&#xff0c; 下面以 And 结点进行分析 方式 方法一&…

轻松记录、修改收支,让财务一目了然!

收支明细管理是每位个人或企业都必须面对的财务任务&#xff0c;但如何准确记录并修改收支明细却常常让人感到困扰。为了帮助大家更好地管理财务&#xff0c;让你轻松掌握记录、修改收支的技巧&#xff0c;让财务状况一目了然&#xff01;方法如下&#xff1a; 第一步&#xf…

线上隐私保护的未来:分布式身份DID的潜力

在日益数字化的世界中&#xff0c;人们的生活越来越多地依赖于互联网&#xff0c;数字身份也因而变得越来越重要。根据法律规定&#xff0c;互联网应用需要确认用户的真实身份才能提供各种服务&#xff0c;而用户则希望在进行身份认证的同时能够尽量保护他们的个人隐私&#xf…

云手机快速发展的原因

云手机之所以迅速崛起&#xff0c;根本原因在于5G技术的广泛应用以及音视频技术的不断发展&#xff0c;这些因素共同推动了云手机的使用体验取得显著提升&#xff0c;引发了越来越多公司对云手机的深入研究。那么&#xff0c;为何云手机成为当前和未来的热门趋势呢&#xff1f;…

Linux管理LVM逻辑卷

目录 一、LVM逻辑卷介绍 1. 概述 2. LVM基本术语 2.1 PV&#xff08;Physical Volume&#xff0c;物理卷&#xff09; 2.2 VG (Volume Group&#xff0c;卷组&#xff09; 2.3 LV (Logical Volume&#xff0c;逻辑卷&#xff09; 3. 常用的磁盘命令 4. 查看系统信息的命…

创建您的第一个记忆卡片游戏

大家好&#xff01;今天&#xff0c;我们将一起探索如何用HTML、CSS和JavaScript创建一个有趣的记忆卡片游戏。我们的游戏规则很简单&#xff1a;用户需要找到一对一样的卡片。如果你是编程新手&#xff0c;不用担心&#xff0c;我会逐步引导你完成这个项目。 正文&#xff1a…