[C/C++]排序算法 快速排序 (递归与非递归)

目录

🚩概念:

🚩实现:

⚡1.hoare

⚡2.挖坑法

⚡3.双指针法

🚩快速排序递归实现

🚩快速排序非递归实现


🚩概念:

          通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据比另一部分的所有数据要小,再按这种方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,使整个数据变成有序序列。 

🌟排序过程:

1.在数组中确定一个关键值

2.将小于关键值的数排到其左边,将大于关键值的数排到其右边,此时关键数在数组中的位置就排好了

3.在左边的数据和右边的数据分别找一个关键值,通过排序使小于关键值的数排到其左边,大于关键值的数排到其右边...

4.重复上述操作,可以通过递归与非递归实现


快速排序的关键是写好步骤二的单趟排序,实现这个功能有三种版本

  1. hoare
  2. 挖坑法
  3. 双指针法

🚩实现:

⚡1.hoare

        将数组第一个元素定位关键值,定义begin和end指针,先让end从后往前找到比关键值小的数,begin从前往后找比关键值大的数,然后交换两数,直到 begin==end,再让关键值和begin所指的元素交换,最后返回关键值所在位置,便于后续进行递归或非递归操作(一定要先从后往前找小,原因下文解释)

动态展示:

void swap(int* a, int* b)
{
	int tmp = *a;
	*a = *b;
	*b = tmp;
}

//hoare
int PartSort1(int* a, int begin, int end)
{
	int left = begin, right = end;
	int keyi = begin;
	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[left], &a[keyi]);
	return left;
}

2.挖坑法

        首先将关键值定为数组第一个元素,并将坑位定为begin,先让end从后往前找到比关键值小的数,将这个数放到坑位,并更新坑位,再让begin从前往后找比关键值大的数,将这个数放到坑位,并更新坑位,直到 begin==end,再让关键值和坑位的元素交换,最后返回关键值所在位置

//挖坑法
int PartSort2(int* a, int begin, int end)
{
	int mid = GetMid(a, begin, end);
	swap(&a[begin], &a[mid]);

	int key = a[begin];
	int hole = begin;
	while (begin < end)
	{
		//右边找小,填入坑内,更新坑位
		while (begin<end && a[end]>=key)
		{
			--end;
		}
		a[hole] = a[end];
		hole = end;
		//左边找大,填入坑内,更新坑位
		while (begin<end && a[begin]<=key)
		{
			++begin;
		}
		a[hole] = a[begin];
		hole = begin;
	}
	a[hole] = key;
	return hole;
}

⚡3.双指针法

        将数组第一个元素定为关键值,定义两个指针prev和cur,先让prev指向数组的第一个元素,cur指向prev的下一个元素,cur的作用是找比关键值小的元素,若cur所指元素不小于关键值则cur++,直到cur所值元素小于关键值,此时,prev和cur之间的元素都是大于关键值的元素,若prev+1不是cur的话就可以让prev++所指元素与cur所指元素交换了,直到cur指向数组的最后一个元素

这里可能会有人出现问题:

1.为什么要判断 prev++!=cur

[解释]:如果prev+1为cur的话,那交换prev++和cur所指元素那就是一个元素之间的交换了,没有意义

2.为什么要交换prev++所指元素

[解释]:因为prev和cur之间的元素都为大于关键值的元素,prev++就可以让prev指向大于关键值的元素,而cur所指的是小于关键值的元素,这样一交换小的数就去前面了,大的数就去后面了

//双指针法
int PartSort3(int* a, int begin, int end)
{
	int mid = GetMid(a, begin, end);
	swap(&a[begin], &a[mid]);

	int key = begin;
	int prev = begin;
	int cur = prev + 1;
	while (cur <= end)
	{
		if (a[cur] < a[key] && ++prev != cur)
		{
			swap(&a[prev], &a[cur]);
		}
		cur++;
	}
	swap(&a[prev], &a[key]);
	return prev;
}

🚩快速排序递归实现

小优化:

        上述三个方法都是快速排序的单趟排序,但是上述排序还有一个小缺陷,因为三个方法都是固定第一个元素为关键值的,如果数组为有序的,那么从后往前找小就要遍历整个数组,效率会很小,所以通常会再写一个找中间值的函数:在数组开头结尾和中间三个数中找出一个大小在中间的数,并让这个数和数组第一个数交换,这样就会减少上述情况的发生

int GetMid(int* a, int begin, int end)
{
	int mid = (begin + end) / 2;
	if (a[begin] > a[mid])
	{
		if (a[mid] > a[end])
			return mid;
		else if (a[end] > a[begin])
			return end;
		else
			return begin;
	}
	else
	{
		if (a[begin] > a[end])
			return begin;
		else if (a[end] > a[mid])
			return mid;
		else
			return end;
	}
}


void swap(int* a, int* b)
{
	int tmp = *a;
	*a = *b;
	*b = tmp;
}

//hoare
int PartSort1(int* a, int begin, int end)
{
	int mid = GetMid(a, begin, end);
	swap(&a[begin], &a[mid]);

	int left = begin, right = end;
	int keyi = begin;
	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[left], &a[keyi]);
	return left;
}

//挖坑法
int PartSort2(int* a, int begin, int end)
{
	int mid = GetMid(a, begin, end);
	swap(&a[begin], &a[mid]);

	int key = a[begin];
	int hole = begin;
	while (begin < end)
	{
		//右边找小,填入坑内,更新坑位
		while (begin<end && a[end]>=key)
		{
			--end;
		}
		a[hole] = a[end];
		hole = end;
		//左边找大,填入坑内,更新坑位
		while (begin<end && a[begin]<=key)
		{
			++begin;
		}
		a[hole] = a[begin];
		hole = begin;
	}
	a[hole] = key;
	return hole;
}
//双指针法
int PartSort3(int* a, int begin, int end)
{
	int mid = GetMid(a, begin, end);
	swap(&a[begin], &a[mid]);

	int key = begin;
	int prev = begin;
	int cur = prev + 1;
	while (cur <= end)
	{
		if (a[cur] < a[key] && ++prev != cur)
		{
			swap(&a[prev], &a[cur]);
		}
		cur++;
	}
	swap(&a[prev], &a[key]);
	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);
}

补充:

为什么开始一定要右边找小,并且为什么left和right相遇那个点一定小于关键值呢?

[解释]:

        L遇到R有两种情况

  • R遇到L: R从右往左找小,一直没找到小,直到遇到L,而L的点的值小于关键值(因为此时L的值是和上一轮R找的小的值)
  • L遇到R:R先从右找小,找到小停下来,L从左往右找打大没有找到遇到R,相遇点的值小于关键值

🚩快速排序非递归实现

        快速排序的递归实现,无非就是通过调用函数对数组的不同区间进行排序,而非递归我们也可以用栈实现,不是递归胜似递归.

实现思路:
1.创建一个栈,将数组的右边界下标和左边界下标依次入栈

2.循环弹出数组的左右边界下标,并对该区间进行单趟排序,确定关键值的下标,分为左右两个区间

3.若左区间元素个数大于一个,将左区间右边界下标和左边界下标依次入栈,右区间同理

4.重复操作步骤2 3直到栈为空


例如待排序数组为:

第一步:将右边界下标和左边界下标7和0入栈

第二步:将边界值弹出,将0给到begin,7给到end,进行单趟排序(单趟排序采用挖坑法)

排序完后,key将数组分为了左右两个区间,让右区间边界7和5入栈,左边界3和0入栈

第三步:取出0  3并对此区间单趟排序

此时,关键值又把区间分为了两个区间,右区间只有一个值,没必要入栈排序,只需要将左区间边界1 0入栈

再弹出0 1,对此区间单趟排序,此时左区间无元素,有区间只有一个元素,这样数组左边就全部排序完成了,再次出栈的话就该排序5和7的区间了,和左边类似

void QuickSortNonR(int* a, int begin, int end)
{
	ST s;
	STInit(&s);
	STPush(&s,end);
	STPush(&s,begin);

	while (!STEmpty(&s))
	{
		int left = STTop(&s);
		STPop(&s);
		int right = STTop(&s);
		STPop(&s);

		int key = PartSort1(a, left, right);

		if (left < key - 1)
		{
			STPush(&s, key - 1);
			STPush(&s, left);
		}

		if (right > key + 1)
		{
			STPush(&s, right);
			STPush(&s, key+1);
		}
	}
	STDestroy(&s);
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Spring中常见的BeanFactory后处理器

常见的BeanFacatory后处理器 先给出没有添加任何BeanFactory后处理器的测试代码 public class TestBeanFactoryPostProcessor {public static void main(String[] args) {GenericApplicationContext context new GenericApplicationContext();context.registerBean("co…

判断电话号码是否重复-excel

有时候重复的数据不需要或者很烦人&#xff0c;就需要采取措施&#xff0c;希望以下的方法能帮到你。 1.判断是否重复 方法一&#xff1a; 1&#xff09;针对第一个单元格输入等号&#xff0c;以及公式countif(查找记录数的范围&#xff0c;需要查找的单元格&#xff09; 2…

node.js express框架开发入门教程

文章目录 前言一、Express 生成器&#xff08;express-generator&#xff09;二、快速安装1.express框架express-generator生成器安装2.使用pug视图引擎创建项目,projectName 为项目名称自定义 三、安装热更新插件 nodemon四、目录结构1. public文件夹2.routes路由其他请求方式…

解密负载均衡:如何平衡系统负载(下)

&#x1f90d; 前端开发工程师&#xff08;主业&#xff09;、技术博主&#xff08;副业&#xff09;、已过CET6 &#x1f368; 阿珊和她的猫_CSDN个人主页 &#x1f560; 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 &#x1f35a; 蓝桥云课签约作者、已在蓝桥云…

连接GaussDB(DWS)报错:Invalid or unsupported by client SCRAM mechanisms

用postgres方式连接GaussDB(DWS)报错&#xff1a;Invalid or unsupported by client SCRAM mechanisms 报错内容 [2023-12-27 21:43:35] Invalid or unsupported by client SCRAM mechanisms org.postgresql.util.PSQLException: Invalid or unsupported by client SCRAM mec…

论文阅读《Rethinking Efficient Lane Detection via Curve Modeling》

目录 Abstract 1. Introduction 2. Related Work 3. BezierLaneNet 3.1. Overview 3.2. Feature Flip Fusion 3.3. End-to-end Fit of a Bezier Curve 4. Experiments 4.1. Datasets 4.2. Evalutaion Metics 4.3. Implementation Details 4.4. Comparisons 4.5. A…

Flink on K8S生产集群使用StreamPark管理

&#xff08;一&#xff09;直接部署&#xff08;手动测试用&#xff0c;不推荐&#xff09; Flink on Native Kubernetes 目前支持 Application 模式和 Session 模式&#xff0c;两者对比 Application 模式部署规避了 Session 模式的资源隔离问题、以及客户端资源消耗问题&am…

用好它们!没有你找不到的电子书

以前读书的时候很喜欢买纸质书籍 &#xff0c;但自从有了 iPad 和智能手机。我发现用智能设备看书似乎性价比更好&#xff0c;不仅可以随时随地阅读 而且还能省不少书的钱&#xff0c;因为电子书网上很多分享几个本人常用的电子书搜索工具。用好这几个工具99% 的书籍你都可以下…

PAT 乙级 1028 人口普查

解题思路&#xff1a;此题我想到俩种方法&#xff0c;一种是排序方法&#xff0c;一种是不排序的方法&#xff0c;首先都是看是否是有效年龄&#xff0c;然后一种是排序&#xff0c;另一种是维护最大值和最小值的变量&#xff0c;一定要注意如果有效数字是0那就只输出0就可以了…

新版ONENET的物联网环境调节系统(esp32+onenet+微信小程序)

新版ONENET的物联网环境调节系统&#xff08;esp32onenet微信小程序&#xff09; 好久没用onenet突然发现它大更新了&#xff0c;现在都是使用新版的物联网开放平台&#xff0c;只有老用户还有老版的多协议接入&#xff0c;新用户是没有的&#xff0c;所以我顺便更新一下新的开…

企业私有云容器化架构运维实战

什么是虚拟化: 虚拟化&#xff08;Virtualization&#xff09;技术最早出现在 20 世纪 60 年代的 IBM 大型机系统&#xff0c;在70年代的 System 370 系列中逐渐流行起来&#xff0c;这些机器通过一种叫虚拟机监控器&#xff08;Virtual Machine Monitor&#xff0c;VMM&#x…

会议室占用时间段 - 华为OD统一考试

OD统一考试 题解: Java / Python / C++ 题目描述 现有若干个会议,所有会议共享一个会议室,用数组表示各个会议的开始时间和结束时间, 格式为: [[会议1开始时间,会议1结束时间],[会议2开始时间,会议2结束时间]] 请计算会议室占用时间段。 输入描述 [[会议1开始时间,…

数据库——LAMP的搭建及MySQL基操

1.实验内容及原理 1. 在 Windows 系统中安装 VMWare 虚拟机&#xff0c;在 VMWare 中安装 Ubuntu 系统,并在 Ubuntu 中搭建 LAMP 实验环境。 2. 使用 MySQL 进行一些基本操作&#xff1a; &#xff08;1&#xff09;登录 MySQL&#xff0c;在 MySQL 中创建用户&#xff0c;并对…

关于增强监控以检测针对Outlook Online APT活动的动态情报

一、基本内容 2023年6月&#xff0c;联邦民事行政部门&#xff08;FCEB&#xff09;在其Microsoft 365&#xff08;M365&#xff09;云环境中发现了可疑活动。该机构迅速向Microsoft和网络安全和基础设施安全局&#xff08;CISA&#xff09;报告了此情况。经过深入调查&#x…

【架构】企业信息安全体系架构详解

企业信息安全体系架构来说,是包含技术、运维、管理3个层面。本文说的安全架构,主要集中讨论偏研发技术层面的安全架构。 安全性是软件研发技术体系,系统架构设计阶段的一个关键DFX能力,与可靠性、可服务性、性能等架构属性并列。由于安全性设计自身的特点,涉及到系统架构…

Zblog主题模板:zblog博客主题aymeleven

zblog主题模板&#xff1a;zblog博客主题aymeleven zblog博客主题aymeleven主要是以文字内容为主导&#xff0c;将页面的设计杂乱的图片和元素进行最小化或者去除&#xff0c;从而使整个页面更加简洁、清晰&#xff0c;突出信息的呈现。 下面介绍一下zblog主题模板:zblog博客主…

AI产品经理 - 技术课-要不要懂技术(上)

一、AI产品经理&#xff0c;要不要懂技术&#xff1a;笔者答案是肯定的 1.AI产品经理要懂产品方案 2.不懂算法&#xff0c;会遇到问题&#xff1a;没有核心竞争力&#xff0c;会边缘化

基于ssm的数据结构课程网络学习平台的设计与实现论文

数据结构课程网络学习平台 摘要 本文介绍了数据结构课程网络学习平台的开发全过程。通过分析企业对于数据结构课程网络学习平台的需求&#xff0c;创建了一个计算机管理数据结构课程网络学习平台的方案。文章介绍了数据结构课程网络学习平台的系统分析部分&#xff0c;包括可行…

【elk-day01】es和kibana搭建及验证---Mac-Docker

Mac系统使用Docker下载搭建和验证eskibana Docker下载安装es安装es验证kibana安装kibana验证 Docker下载安装 Docker Desktop官网安装下载地址 说明一下为什么要安装desktop版本的docker&#xff0c;因为docker作为工具使用&#xff0c;我们需要的是开箱即用&#xff0c;没有必…

【yolov5驾驶员和摩托车佩戴头盔的检测】

yolov5驾驶员和摩托车佩戴头盔的检测 数据集和模型yolov5驾驶员和摩托车佩戴头盔的检测yolov5驾驶员和摩托车佩戴头盔的检测可视化结果 数据集和模型 数据和模型下载&#xff1a; yolov5摩托车佩戴头盔和驾驶员检测模型 yolov5-6.0-helmat-mortor-1225.zipyolov3摩托车佩戴头…