快排的递归实现

news2024/11/21 2:27:38

快速排序是一种时间复杂度低,但会虽随着数组的顺序变化,因为其效率之高被称为快速排序,而

且其不稳定性也可以同过优化进行解决。

快速排序的实现有三种方法:

1.hoare版

其基本思想为:任取待排序元素序列中 的某元素作为基准值,按照该排序码将待排序集合分割成

两子序列,左子序列中所有元素均小于基准值右 子序列中所有元素均大于基准值,然后最左右

子序列重复该过程,直到所有元素都排列在相应位置上为止。

实现方法如图:

这幅图是将最左端的元素定为基准值, 先让基准值的另一边(right)开始走,找到比基准值小的

元素时停下,再让基准值(left)这一边开始走,找到比基准值大的元素停下,再将right和left对应

的元素进行交换,重复上述操作。如下图:

第一次交换:

第二次交换:

第二次交换后,right先走后left与right相遇将相遇的位置的元素与key的元素进行交换,此时

key的元素到了其排序好的最终元素的位置。 如下图:

这就是快速排序的单趟排序, 从上述图可以看出key将这个数组分为两部分,而左边和右边有可以

看为一个新的要排序的数组,而这又可以引用单趟排序,从而构成递归。

那么递归结束的条件是什么呢?是当(left == right)的时候吗?

当要拍序的数组本身有序的时候,我们会发现,可能会存在(left > right),所以递归结束的条件

应该为(left >= right)。

整体的代码如下:

//hoare法
int PartSort1(int* a, int left, int right)
{
	
	随机选k
	//int randi = left + (rand() % (right - left));
	//int key = left;
	//三数取中法
	int mid = FindMidNumi(a, left, right);
	if (mid != left)
	{
		Swap(&a[mid], &a[left]);
	}
	int key = left;

	while (left < right)
	{
		//右边找小
		while (left < right && a[right] >= a[key])
		{
			right--;
		}
		//左边找大
		while (left < right && a[left] <= a[key])
		{
			left++;
		}

		Swap(&a[right], &a[left]);
	}
	Swap(&a[key], &a[right]);
	key = right;

	return key;
}

void QuickSort(int* a, int left, int right)
{
	if (left >= right)
	{
		return;
	}
	
	int key = PartSort1(a, left, right);
	QuickSort(a, left, key - 1);
	QuickSort(a, key + 1, right);
	
}

2.挖坑法

此方法是hoare的一种变形,与上面的方法十分类似。

同样,我们把最左边的元素设为基准值,并将元素挖出来,用局部变量key将其存储如下图:

之后也是从右边先找到比key小的元素,并将此元素与坑位进行交换:

在从左边开始找比key大的元素,将其与坑位交换:

直到left与right相遇, 将key的值填回坑位。

从上述图可以看出key将这个数组分为两部分,而左边和右边有可以看为一个新的要排序的数组,

而这又可以引用单趟排序,从而构成递归。

 递归结束条件为:(left >= right)。

 代码如下:

//挖坑法
int PartSort2(int* a, int left, int right)
{
	//三数取中法
	int mid = FindMidNumi(a, left, right);
	if (mid != left)
	{
		Swap(&a[mid], &a[left]);
	}
	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 left, int right)
{
	if (left >= right)
	{
		return;
	}
	
	int key = PartSort2(a, left, right);
	QuickSort(a, left, key - 1);
	QuickSort(a, key + 1, right);
	
}

3.前后指针法:

前后指针法是快排的一种进阶方法:

分为两个指针:一个是当前位置(cur)一个是前一个位置(prev),其排序方法如下:

同样,我们把最左边的元素设为基准值

1,如果cur的值比key小,++prev,并将cur1的元素和prev的元素进行交换,++cur。

2,如果cur的值比key大,++cur。

3,当cur超过数组范围后,将prev的值和key交换。

从上述图可以看出key将这个数组分为两部分,而左边和右边有可以看为一个新的要排序的数组,

而这又可以引用单趟排序,从而构成递归。

 递归结束条件为:(left >= right)。

代码如下:

int PartSort3(int* a, int left, int right)
{
	// 三数取中法
	int mid = FindMidNumi(a, left, right);
	if (mid != left)
	{
		Swap(&a[mid], &a[left]);
	}
	int keyi = left;
	int prev = left;
	int cur = left + 1;
	/*while (cur <= right)
	{
		if (a[cur] < a[keyi])
		{
			++prev;
			Swap(&a[cur], &a[prev]);
			++cur;
		}
		else
		{
			++cur;
		}
	}*/
	//更简明,但是不好懂
	while (cur <= right)
	{
		if (a[cur] < a[keyi] && ++prev != cur)
		{
			Swap(&a[cur], &a[prev]);
		}
		cur++;
	}
	Swap(&a[prev], &a[keyi]);
	keyi = prev;

	return keyi;
}

void QuickSort(int* a, int left, int right)
{
	if (left >= right)
	{
		return;
	}
	
	int key = PartSort3(a, left, right);
	QuickSort(a, left, key - 1);
	QuickSort(a, key + 1, right);
	
}

我们刚开始提到时间复杂度随着数组的顺序变化,是因为当快速排序排有序的数组时,应为本身

就在最终位置,所以导致快排需要将所有元素都遍历一遍,从而使时间复杂度升高,而我们的解

决办法是三数取中法:

取数组最左,最右,中间三个元素来进行比较,取出最中间的元素,将其与最左边的元素进行交

换,从而使key的值在数组中处在一个居中的位置,代码如下(前面的代码都加了三数取中法):

//三数取中法
int FindMidNumi(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 right;
		}
		else
		{
			return left;
		}
	}
	else
	{
		if (a[mid] < a[right])
		{
			return mid;
		}
		else if (a[left] > a[right])
		{
			return left;
		}
		else
		{
			return right;
		}
	}
}

这样就可以让快排的时间复杂度稳定在O(n*logn)了,快排还是有些难度的,不过上述方法大家

可以只掌握一种,会写出快速排序即可。

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

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

相关文章

3、如何使用GDB来进行命令行debug

文章目录一、与前面的联系二、GDB的一些认识1、什么是gdb2、gdb作用3、gdb可实现的功能三、GDB常用的调试命令一、与前面的联系 对于前面说到的launch.json文件就是用于debug的配置文件&#xff0c;在前面的vscode中我们可以发现配置好launch.json文件之后进行调试&#xff0c…

攻防世界-web2(逆向加密算法)

打开链接是PHP源码 给了一串密文&#xff0c;并对这串密文进行了一系列操作加密&#xff0c;注释里说解密$miwen就是flag 在此我们先介绍一些PHP内置函数&#xff1a; strrev(string): 反转字符串 strlen(string): 返回字符串的长度 substr(string, start, length): 返回字符…

认识、使用C++vetor和array

目录 前言&#xff1a; 1.vector模板 1.1vector简介 1.2创建vector类对象 2.array模板 2.1array简介 2.2创建array类对象 3.比较中学习 4.怎么避免数组越界访问 前言&#xff1a; 指针的基础用法分了近三篇文章&#xff0c;结合数组、结构、共用体、字符串一起学习。相…

【Golang | http】使用http库完成一个简单的POST请求

引言 主要记录使用Golang实现一个POST请求所用到的小知识点 1、项目结构 客户端向服务端注册用户信息&#xff0c;服务端返回注册信息中的用户名 PS E:\goland-workspace\GolangLearning\http> tree /f 卷 文件 的文件夹 PATH 列表 卷序列号为 0C66-1433 E:. ├─client…

小样本学习FSL介绍

1 概念 小样本学习&#xff08;few-shot learning&#xff0c;FSL&#xff09;旨在从有限的标记实例&#xff08;通常只有几个&#xff09;中学习&#xff0c;并对新的、未见过的实例进行识别。 相比于传统的深度学习和机器学习方法&#xff0c;小样本学习能够更好地模拟人类的…

从C出发 22 --- 变量的作用域与生命期

问题 1 &#xff1a; 这样子定义一个不属于任何函数的变量正确吗? 问题 2 : 编译能通过吗? 问题 : 我们要打印的var 到底是 10 还是 100. 总结: 什么都不会输出&#xff0c;因为这里的 i ;是让局部变量的 i &#xff0c;程序会一直死循环 为什么都是 11&#xff0c;为什…

在构建个人想法时,使用哪个工具更好呢?Tana, AmpleNote 和 妙记多 Mojidoc的比较

笔记类 App 都很强调个人化&#xff0c;因为我们每个人会用不同的方法来做笔记、写日记。不过有一些框架可以帮助我们&#xff0c;比如子弹笔记&#xff08;Bullet Journal&#xff09;等。 Tana 和 Amplenote 都可以使用「标签」&#xff0c;尽管它们处理的方式、体验都大不相…

4.14~4.16学习总结

多线程&#xff1a; 同步代码块 格式&#xff1a;Synchronized(锁) { 操作共享数据的代码 } 特点1&#xff1a;锁默认打开&#xff0c;有一个线程进去了&#xff0c;锁自动关闭。 特点2&#xff1a;里面的代码全部执行完毕&#xff0c;线程处理&#xff0c;锁自动打开。 …

SaleSmartly(ss客服)怎么玩转Instagram自动化?

这段时间接触了不少粉丝&#xff0c;一直在说ins营销&#xff0c;说谁谁谁通过这个引流&#xff0c;结果爆了&#xff0c;那我们今天就来简单说一下。Instagram (IG) 是全球最大的照片和视频共享平台&#xff0c;拥有超过10亿的月活跃用户和 5 亿的日活跃Story用户。借助IG的强…

LNMP和论坛的搭建

系列文章目录 文章目录系列文章目录一、LNMP搭建1.承接上文搭建nginx服务2.Mysql数据库搭建3.安装配置 PHP 解析环境4.、部署 Discuz&#xff01;社区论坛 Web 应用总结一、LNMP搭建 1.承接上文搭建nginx服务 2.Mysql数据库搭建 1、安装Mysql环境依赖包 yum -y install \ n…

不限量免注册,极速体验AI助手

最近 ChatGPT 很火&#xff0c;火到每个人都想玩一把&#xff0c;由于受限&#xff0c;不是在搭梯子就是在搭梯子的路上&#xff0c;现在类 ChatGPT 产品&#xff0c;它终于来了。还是先简单秀一波操作&#xff1a;第一波&#xff1a;大数据记录中&#xff0c;涉及关键字快速检…

vue2路由(上)

路由的简介 什么是路由&#xff1f; 用生活上的例子&#xff0c;路由器上的接口对应一个主机。 而由key和values组成的映射关系就是路由 主要用于SPA单页面应用 就是根据你端口号后面的路径&#xff0c;看你有没有配置这个页面对应的组件&#xff0c;如果有&#xff0c;那么就…

批处理脚本用法总结

目录一、常用命令二、基本语法1. rem 和 ::2. echo 和 3. pause4. errorlevel5. title6. color7. goto 和 :三、常见用法1. 设置临时环境变量2. 启动CMD执行命令3. 打开环境变量窗口参考资料&#xff1a;批处理(Batch)&#xff0c;也称为批处理脚本。顾名思义&#xff0c;批处理…

零入门kubernetes网络实战-29->在同一个宿主机上基于虚拟网桥bridge链接不同网段的不同网络命名空间的通信方案

《零入门kubernetes网络实战》视频专栏地址 https://www.ixigua.com/7193641905282875942 本篇文章视频地址(稍后上传) 上一篇文章&#xff0c;我们分享了使用虚拟网桥来连接同网段的不同网络命名空间下的通信情况。 那么&#xff0c;本篇文章&#xff0c; 我们想测试一下&…

用ChatGPT快速阅读论文:3个步骤让你轻松阅读论文文档

引言 您是否曾经因为阅读论文而感到困难和无从下手&#xff1f;ChatDOC是一款专为您设计的人工智能工具&#xff0c;帮助您快速理解论文内容。通过上传文档&#xff0c;利用ChatGPT技术&#xff0c;您只需3个简单步骤&#xff0c;即可快速阅读论文&#xff0c;提高阅读效率。立…

[架构之路-162]-《软考-系统分析师》-3-作系统基本原理-进程管理

目录 前言&#xff1a; 3 . 1 操作系统概述 3.1.1 操作系统的类型 2 . 批处理系统 3 . 分时操作系统 4 . 网络操作系统 5 . 分布式操作系统 6 . 嵌入式操作系统 3.1.2 操作系统的软件结构 1 . 整体结构 2 . 层次结构 3 . 客户/服务器结构 4 . 面向对象结构 3 . 2…

一文总结 Shiro 实战教程

✅作者简介&#xff1a;2022年博客新星 第八。热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;Java Fans的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 &#x1f49e;当前专栏…

InstructGLM:基于ChatGLM-6B在指令数据集上进行微调

InstructGLM 基于ChatGLM-6BLoRA在指令数据集上进行微调 https://github.com/yanqiangmiffy/InstructGLM 本项目主要内容&#xff1a; &#x1f680; 2023/4/9 发布了基于100万条由BELLE项目生成的中文指令数据的Lora权重&#xff0c;具体可见output/belle/chatglm-lora.pt&a…

【Spring Boot】SpringBoot设计了哪些可拓展的机制?

文章目录前言SpringBoot核心源码拓展Initializer拓展监听器ApplicationListenerBeanFactory的后置处理器 & Bean的后置处理器AOP其他的拓展点前言 当我们引入注册中心的依赖&#xff0c;比如nacos的时候&#xff0c;当我们启动springboot&#xff0c;这个服务就会根据配置文…

【UE Sequencer系列】05-解决角色动画造成的位移问题

步骤 在上一篇博客制作的动画中&#xff0c;角色反击的动画部分会造成角色瞬移的问题&#xff0c;如下所示&#xff1a; 为了解决这个问题&#xff0c;我们可以做如下操作&#xff1a; 1.首先我的瞬移现象发生在第698帧到699帧的时候&#xff0c;我对第698帧的角色的transfor…