常见排序算法之快速排序

news2024/11/19 20:18:13

       快速排序是Hoare于1962年提出的一种二叉树结构的交换排序方法。

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

void QuickSort(int array[], int left, int right)
{
     if(right - left <= 1)
         return; 
     int div = partion(array, left, right);
     QuickSort(array, left, div); 
     QuickSort(array, div+1, right);
}

       上述为快速排序递归实现的主框架,发现与二叉树前序遍历规则非常像,我们在写递归框架时可想想二叉树前序遍历规则即可快速写出来,后序只需分析如何按照基准值来对区间中数据进行划分的方式即可。

将区间按照基准值划分为左右两半部分的常见方式有:

        1.hoare版本

        2.挖坑法

        3.前后指针版本

下面将介绍三种方法,在此之前我们现需写一个三数取中代码:

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

一、hoare版本

具体步骤

  1. 先选取数组左边的第一个数作为key,定义一个左下标left指向最左边的数,定义一个右下标right指向最右边的数。
  2. 然后让right先往左遍历,找到第一个比key大的值,让left后向右遍历,找到第一个比key小的值。
  3. 这时,左边的值都比key小,右边的值都比key大,交换left和right指向的值。
  4. 这样一趟排序就结束了,key就在了它应该在的位置上。重复以上步骤,直到整个序列有序。

核心代码

int PartSort1(int* a, int left, int right)
{
	int midi = GetMidi(a, left, right);
	Swap(&a[left], &a[midi]);
	int keyi = left;
	while (left < right)
	{
		while (left < right && a[right] >= a[keyi])
		{
			--right;
		}
		while (left < right && a[left] <= a[keyi])
		{
			++left;
		}
		Swap(&a[left], &a[right]);
	}
	Swap(&a[keyi], &a[left]);
	return left;
}

void QuickSort(int* a, int begin, int end)
{
	if (begin >= end)
		return;

	int keyi = PartSort1(a, begin, end);
	QuickSort(a, begin, keyi - 1);
	QuickSort(a, keyi + 1, end);
}

void PrintArray(int* a, int n)
{
	for (int i = 0; i < n; i++)
	{
		printf("%d ", a[i]);
	}
	printf("\n");
}

void TestQuickSort()
{
	int a[] = { 6,1,2,7,9,3,4,5,10,8 };
	QuickSort(a, 0, sizeof(a) / sizeof(int) - 1);
	PrintArray(a, sizeof(a) / sizeof(int));
}

int main()
{
    TestQuickSort();
	return 0;
}

实验结果


二、挖坑法

具体步骤

       首先设置两个指针,一个指向数组的起始位置,一个指向数组的末尾位置。从末尾位置开始,向前遍历,找到第一个小于基准元素的元素,并将其填入起始位置的坑中。然后从起始位置开始,向后遍历,找到第一个大于基准元素的元素,并将其填入上一步所挖的坑中。重复上述步骤,直到起始位置和末尾位置相遇。此时,将基准元素填入最后一个坑中,这样就完成了一次分区操作。最后对分区后的左右两个子数组,分别递归地进行上述步骤。

核心代码

int PartSort2(int* a, int left, int right)
{
	int midi = GetMidi(a, left, right);
	Swap(&a[left], &a[midi]);
	int key = a[left];
	int hole = left;
	while (left < right)
	{
		while (left < right && a[right] >= key)
		{
			--right;
		}
		a[hole] = a[right];
		hole = right;
		while (left < right && a[left] <= key)
		{
			++left;
		}
		a[hole] = a[left];
		hole = left;
	}
	a[hole] = key;
	return hole;
}

void QuickSort(int* a, int begin, int end)
{
	if (begin >= end)
		return;

	int keyi = PartSort2(a, begin, end);
	QuickSort(a, begin, keyi - 1);
	QuickSort(a, keyi + 1, end);
}

void PrintArray(int* a, int n)
{
	for (int i = 0; i < n; i++)
	{
		printf("%d ", a[i]);
	}
	printf("\n");
}

void TestQuickSort()
{
	int a[] = { 6,1,2,7,9,3,4,5,10,8 };
	QuickSort(a, 0, sizeof(a) / sizeof(int) - 1);
	PrintArray(a, sizeof(a) / sizeof(int));
}

int main()
{
    TestQuickSort();
	return 0;
}

实验结果


三、前后指针版本

具体步骤

  1. 选择枢轴元素:在数组中选择一个元素作为枢轴,一般选择第一个元素或最后一个元素。

  2. 初始化左右指针:左指针指向数组的第一个元素,右指针指向数组的最后一个元素。

  3. 划分过程:从前往后遍历数组,当找到一个比枢轴大的元素时,将左指针向右移动一位;从后往前遍历数组,当找到一个比枢轴小的元素时,将右指针向左移动一位。当左指针大于等于右指针时,说明已经找到正确的位置,将枢轴与左指针所在位置的元素交换。

  4. 递归处理:将枢轴左边的部分作为新的子数组,递归调用快速排序函数;将枢轴右边的部分作为新的子数组,递归调用快速排序函数。

核心代码 

int PartSort3(int* a, int left, int right)
{
	int midi = GetMidi(a, left, right);
	Swap(&a[left], &a[midi]);
	int prev = left;
	int cur = prev + 1;
	int keyi = left;
	while (cur <= right)
	{
		if (a[cur] < a[keyi] && ++prev != cur)
		{
			Swap(&a[prev], &a[cur]);
		}
		++cur;
	}
	Swap(&a[prev], &a[keyi]);
	return prev;
}

void QuickSort(int* a, int begin, int end)
{
	if (begin >= end)
		return;

	int keyi = PartSort3(a, begin, end);
	QuickSort(a, begin, keyi - 1);
	QuickSort(a, keyi + 1, end);
}

void PrintArray(int* a, int n)
{
	for (int i = 0; i < n; i++)
	{
		printf("%d ", a[i]);
	}
	printf("\n");
}

void TestQuickSort()
{
	int a[] = { 6,1,2,7,9,3,4,5,10,8 };
	QuickSort(a, 0, sizeof(a) / sizeof(int) - 1);
	PrintArray(a, sizeof(a) / sizeof(int));
}

int main()
{
    TestQuickSort();
	return 0;
}

实验结果


四、非递归版本

       快速排序的非递归版本是递归版本的一种优化,主要是通过将递归过程转化为循环来实现。基本思路是将数组分为两部分,一部分比基准值小,另一部分比基准值大,然后对这两部分分别进行快速排序。这个过程可以通过循环来实现,而不是通过递归调用函数自身。

具体步骤

       首先从数组中挑选一个元素作为基准值,然后将所有小于基准值的元素移动到基准值的左边,将所有大于基准值的元素移动到基准值的右边。接下来,对基准值左右两边的子数组分别进行相同的操作,直到数组完全有序。

核心代码

void QuickSortNonR(int* a, int left, int right)
{
Stack st;
StackInit(&st);
StackPush(&st, left);
StackPush(&st, right);
while (StackEmpty(&st) != 0)
{
 right = StackTop(&st);
 StackPop(&st);
 left = StackTop(&st);
 StackPop(&st);
 
 if(right - left <= 1)
 continue;
 int div = PartSort1(a, left, right);
 // 以基准值为分割点,形成左右两部分:[left, div) 和 [div+1, right)
 StackPush(&st, div+1);
 StackPush(&st, right);
 
 StackPush(&st, left);
 StackPush(&st, div);
}
 
 StackDestroy(&s);
}

       感兴趣的同学可以自行练习。


五、快速排序特性总结

        1.快速排序整体的综合性能和使用场景都是比较好的,所以才敢叫快速排序

        2.时间复杂度:O(N*logN)

        3.空间复杂度:O(logN)

        4.稳定性:不稳定


结语:快速排序的相关分享到这里就结束了,希望对大家的学习会有帮助,如果大家有什么问题或者不同的见解,欢迎大家的留言~~~

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

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

相关文章

Flutter 实战:构建跨平台应用

文章目录 一、简介二、开发环境搭建三、实战案例&#xff1a;开发一个简单的天气应用1. 项目创建2. 界面设计3. 数据获取4. 实现数据获取和处理5. 界面展示6. 添加动态效果和交互7. 添加网络错误处理8. 添加刷新功能9. 添加定位功能10. 添加通知功能11. 添加数据持久化功能 《F…

【Unity细节】Failed importing package???Unity导包失败?

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! 本文由 秩沅 原创 &#x1f636;‍&#x1f32b;️收录于专栏&#xff1a;unity细节和bug &#x1f636;‍&#x1f32b;️优质专栏 ⭐【…

计算机服务器中了mallox勒索病毒怎么解决,勒索病毒解密,数据恢复

企业的计算机服务器为企业的数据存储提供了极大便利&#xff0c;也让企业的生产运行效率得到了极大提升&#xff0c;但是网络数据安全威胁随着技术的不断发展也不断增加。近期&#xff0c;云天数据恢复中心接到很多企业的求助&#xff0c;企业的计算机服务器遭到了mallox勒索病…

图神经网络 (GNN)

目录 一、GNN介绍1.1引入1.1.1图的介绍1.1.2怎样将内容表示成图1.1.4图神经网络是在做什么 1.2基本概念 二、GNN流程2.1聚合2.2更新2.2.1一次GNN操作 2.3循环2.3.1多层GNN操作2.3.2能做什么 三、GNN算法原理3.1数据3.2变量定义3.3GNN算法3.3.1Forward3.3.2Backward 四、GNN优势…

Unity 调用自己封装好的DLL库

因为做项目时会用到很多重复的方法&#xff0c;每次都重新写有点浪费时间&#xff0c;就可以将这些方法封装成DLL类库&#xff0c;用的时候直接引入调用就行。 首先在VS里面创建类库文件 注&#xff1a;.NET Framework要选3.5以下 然后定义好命名空间名字和类名就可以写自己要…

互联网按摩预约小程序开发;

随着移动互联网的普及&#xff0c;越来越多的人开始通过手机预约按摩服务。按摩预约小程序是一种方便快捷的预约方式&#xff0c;可以让用户随时随地预约按摩服务。那么&#xff0c;按摩预约小程序的开发周期要多久&#xff1f;按摩预约小程序的功能有哪些呢&#xff1f;本文将…

振南技术干货集:研发版本乱到“妈不认”? Git!(1)

注解目录 1、关于 Git 1.1Git 今生 (Git 和 Linux 的生父都是 Linus&#xff0c;振南给你讲讲当初关于 Git 的爱恨情愁&#xff0c;其背后其实是开源与闭源两左阵营的明争暗斗。) 1.2Git的爆发 (Git 超越时代的分布式思想。振南再给你讲讲旧金山三个年轻人创办 GitHub&…

ABAQUS分析步笔记

定义原则&#xff1a; 每个step的所有边界条件&#xff0c;载荷条件累加构成本step的仿真效果&#xff1b; step2需要在step1的状态基础上进行载荷运动等限定时&#xff0c;需要确保在step2中传递了step1的想要保留的特征&#xff0c;如&#xff1a; 1、BC-1 这里的BC-1的固…

2024最新fl studio 21.2.0.3842中文版完整下载

FL Studio 21.2.0.3842中文版完整下载是最好的音乐开发和制作软件也称为水果音乐软件。它是最受欢迎的工作室&#xff0c;因为它包含了一个主要的听觉工作场所。2024最新fl studioFL Studio 21版有不同的功能&#xff0c;如它包含图形和音乐音序器&#xff0c;帮助您使完美的配…

JRebel热部署——效率提升100倍(程序员工具必备)

1. 下载JRebel 2.激活程序 这里推荐一个免费获取jrebel激活服务器地址和激活邮箱的地址:点击进入 进入网站之后就可以获取到激活链接和邮箱 点击进入激活 复制过去激活就可以 然后就可以看到激活成功了 3.如何使用 代码修改后&#xff0c;直接CtrlShitF9 即可重新启动 4…

阿里云服务器怎么样?阿里云服务器优势、价格及常见问题介绍

阿里云&#xff08;Alibaba Cloud&#xff09;是阿里巴巴集团旗下的云计算服务提供商&#xff0c;其提供的云服务器&#xff08;ECS&#xff09;是其核心服务之一。在云计算市场中&#xff0c;阿里云服务器备受用户的青睐&#xff0c;那么&#xff0c;阿里云服务器究竟怎么样呢…

基于SSM的房屋租售信息管理系统的设计与实现

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;采用JSP技术开发 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#x…

“基于RflySim平台飞控底层算法开发”系列专题培训 (第三期)

>> RflySim平台系列专题培训 RflySim平台是一个生态系统或工具链&#xff08;官网&#xff1a;https://doc.rflysim.com&#xff09;&#xff0c;发起于北航可靠飞行控制研究组&#xff0c;主要用于遵循基于模型设计的思想进行无人系统的控制和安全测试。本平台选择MATL…

合成数据加速机器视觉学习

虽然机器学习在基于视觉的自动化中的应用正在增长&#xff0c;但许多行业都面临着挑战&#xff0c;并难以在其计算机视觉应用中实施它。这在很大程度上是由于需要收集许多图像&#xff0c;以及与准确注释这些图像中的不同产品相关的挑战。 该领域的最新趋势之一是利用合成数据…

51单片机PCF8591数字电压表数码管显示设计( proteus仿真+程序+设计报告+讲解视频)

PCF8591数字电压表数码管显示 1.主要功能&#xff1a;讲解视频&#xff1a;2.仿真3. 程序代码4. 设计报告5. 设计资料内容清单&&下载链接资料下载链接&#xff08;可点击&#xff09;&#xff1a; 51单片机PCF8591数字电压表数码管设计( proteus仿真程序设计报告讲解视…

C++以数组作为参数,传递数组地址

文章目录 函数如何使用指针来处理数组将数组作为参数意味着什么数组名和指针对应是好的吗&#xff1f; 参考资料 函数如何使用指针来处理数组 在大多数情况下&#xff0c;C和 C 语言一样&#xff0c;也将数组名视为指针。 C将数组名解释为其第一个元素的地址: cookies &…

YOLOv8 Ultralytics:使用Ultralytics框架训练RT-DETR实时目标检测模型

YOLOv8 Ultralytics&#xff1a;使用Ultralytics框架训练RT-DETR实时目标检测模型 前言相关介绍前提条件实验环境安装环境项目地址LinuxWindows 制作自己的数据集训练自己的数据集创建自己数据集的yaml文件football.yaml文件内容 进行训练进行验证进行预测 数据集获取参考文献 …

NAS 扩容简明指南:使用各种外设给 NAS 们扩容

说起来有趣&#xff0c;NAS 除了“不同设备共享存储”这个功能之外&#xff0c;最重要的功能就是为设备扩容&#xff0c;但是 NAS 自己的存储容量不够了&#xff0c;又该如何。 ​这篇文章分享下我目前使用外设给 NAS 扩容的思路&#xff0c;如何以相对低的成本来获取更大的容…

IPV6网络技术详细介绍

无状态和有状态并不是相互对立的&#xff0c;他们可以同时存在&#xff0c;也就是一张网卡上可以同时出现通过RA生成的IP以及通过DHCPv6获得的IP。 从图中可以看到&#xff0c;顺序为&#xff1a; 1、Stateless自动配置“链路本地地址”2、Stateless自动配置“全球地址”&…

mysql基础 --子查询

文章目录 子查询 子查询 一个查询语句&#xff0c;嵌套在另一个查询语句内部&#xff1b;子查询先执行&#xff0c;其结果被外层主查询使用&#xff1b;子查询放入括号内&#xff1b;子查询放在比较条件的右侧&#xff1b;子查询返回一条&#xff0c;为单行子查询&#xff1b;…