​​快速排序(四)——挖坑法,前后指针法与非递归

news2024/6/30 0:09:47

fe594ea5bf754ddbb223a54d8fb1e7bc.gif

目录

​一.前言

二.挖坑法

三.前后指针法

四.递归优化

五.非递归

六.结语


 

8fb442646f144d8daecdd2b61ec78ecd.png一.前言

本文我们接着上篇文章的重点快排,现在继续讲解对快排优化的挖坑法,前后指针法以及非递归方法,下面是上篇文章快排链接:https://mp.csdn.net/mp_blog/creation/editor/135719674。

码字不易,希望大家多多支持我呀!(三连+关注,你是我滴神!)

二.挖坑法

其实挖坑法很简单,因为它只是在hoare的基础上优化了一部分,就比如不用去纠结为什么key在右边时,右边先走。这里因为坑设定在第一位,所以自然要从右边找数去填坑。

因此这里面的循环逻辑就变得很简单,右找小(填左边坑),自己变成新坑,左找大(填右边坑),自己变新坑,就这样以此类推最后一定会相遇,而这个相遇的位置一般就是我们的居中值(三数取中法),最后把key放入即可。

int PartSort2(int* a, int left, int right)
{
	int midi = GetMidi(a, left, right);
	Swap(&a[left], &a[midi]);
	//通过三数取中把位于最左边的居中数给key
	int key = a[left];
	//保存key值后,左边形成第一个坑位
	int hotel = left;
	//left与right不断相遇
	while (left < right)
	{
		//右边先走,找小;找到后把值放到左边坑位中,右边形成新的坑位
		while (left < right && a[right] >= key)
		{
			right--;
		}
		a[hotel] = a[right];
		hotel = right;
		//左边开始走,找大;找到后把值放到右边坑位中,左边形成新的坑位
		while (left < right && a[left] <= key)
		{
			left++;
		}
		a[hotel] = a[left];
		hotel = left;
	}
	//最后二者相遇,并且其中一个必定是坑位
	//将key放入坑位中
	a[hotel] = key;
	return hotel;


}

挖坑法由于有了前面hoare的铺垫,所以我们可以更简洁明了地写出代码,也更通俗易懂,但这些前提都是要理解hoare的核心,这样才能写出优化的挖坑法。

三.前后指针法

前后指针法写起来甚至比挖坑法更简单,因为它就只有一个核心点:

cur找比key小的数,prev++,交换prev与cur的值

而prev只有两者情况:

  1. 在cur没遇到比key大的值的时候,prev紧紧跟着cur(重合)
  2. 在cur遇到比key大的值的时候,prev在比key大的一组值的前面

这样就会出现一种情况,比key大的值会阻扰prev的前进,但只要cur再次找到比key小的值两者就会交换,prev就像是不断地推着比key大的值往后排。

3与7交换 

9与4交换

就这样持续到cur走出数组,这时可以看到prev的指向数是比key小的,最后prev与key进行交换。

写代码我们要注意一个地方,就是记得要在cur找小的时候添加一个条件如果该趟找不到小的已经越界了就及时退出,否则prev就会继续++,打乱了key的正确位置。

int PartSort3(int* a, int left, int right)
{
	int midi = GetMidi(a, left, right);
	Swap(&a[left], &a[midi]);
	int key = left;
	int prev = left;
	int cur = prev + 1;
	while (cur <= right)
	{
		//找小
		while (cur<=right&&a[cur] >= a[key])
		{
			cur++;
		}
		//当找不到小时
		if (cur > right)
		{
			break;
		}
		else
		{
			prev++;
			Swap(&a[prev], &a[cur]);
		}
	}
	Swap(&a[key], &a[prev]);
	return key;


}

还有一种写法,我们可以观察到cur无论是找小还是找不到小都会++,那不妨可以这样写:

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


}

注意!这两个优化的方法并不能提高快排的算法效率,只是思想上的优化,便于我们更好理解而已。

四.递归优化

我们再来处理优化递归的问题:

满二叉树最后一层节点会占总节点的50%,因为一共有2^h-1个节点,最后一层有2^(h-1)个

对比到快排的递归而言,为了让这10个数有序而走了那么多次递归有点不划算。

那如果10个数我们不用递归而采用直接插入法的话可以效率可以提高80%-90%,因为递归最后三层的消耗占据很多,在处理数量级小的情况下会很浪费。

代码部分: 

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

	}
	else
	{
		InsertSort(a + begin, end - begin + 1);
	}
}

 

这是最精妙的一步(小区间优化,小区间不再递归分割排序,降低递归次数),由于我们在不断递归的过程中那些后面被分割的许多个小组序列会占据大多数空间,所以我们统一划定一个标准,只要是小于10个的范围都直接插入法去解决,又因为我们划分的无数个子树都对应原数组不同的位置,那我们就分别用[begin,end]来规定它们,当我们用到直接插入排序时,只要在原数组地址加上begin就可以对应到自己的位置。

五.非递归

非递归的关键就是能够控制与保存区间——借助栈

我们先把数组的范围录入栈中

找到key的下标后把0-9取出录入我们的右区间6-9 

再把左区间0-4录入

之所以先入右再入左是因为这样可以先把左区间给取出来(栈的特点),然后下一次循环栈不为空则取出左区间走,0-4走完单趟选出key后继续分割

0-4分割成0-1与3-4,我们再把它们分别录入栈中。 

再把0-1取出来选出key为1后分割成0-0与2-1(2-1说明这里已经没有数了,也可以参考范围[key+1,end],)而这两个区间也不用继续入栈了(没啥好分的了)。

 我们可以发现这就是递归的过程,只不过我们需要用栈以非递归的形式实现

接着我们取出3-4进行分割,然后按照区间规则它分出的两区间不用再入栈了。

就这样以此类推直到栈为空结束。

void QuickSortNonR(int* a, int begin, int end)
{
	ST st;
	STInit(&st);
	//入栈,先入右,如9
	STPush(&st, end);
	//再入左,如0
	STPush(&st, begin);
	while (!STEmpty(&st))
	{
		//准备出栈,获取栈顶元素,如0
		int left = STTop(&st);
		//出栈,如0
		STPop(&st);
		//准备出栈,获取栈顶元素,如9
		int right = STTop(&st);
		//出栈,如9
		STPop(&st);
		//通过获取的元素构成范围来寻找key,【0-9】
		int key = PartSort3(a, left, right);
		//[left,key-1]key[key+1,right]
		//对范围如【0-9】进行分割,先入栈右区间再入栈左区间
        //准备入6-9
		//判断区间合理性,等于或大于不能放
		if (key + 1 < right)
		{
			STPush(&st, right);//如入9
			STPush(&st, key+1);//如入6
		}//6-9已经录入
		//同理把0-5录入
		if (left < key - 1)
		{
			STPush(&st, key-1);//如入5
			STPush(&st, left);//如入0
		}
        //现在第一层结束后栈顶由上到下就是0 5  6 9
		//至此由第一层分割的范围录入结束,后面就是判断栈里是否为空(还有没有范围)
		//如果栈不为空则继续执行这个循环,直到为空的时候,非递归也就完成了。

	}
}

 栈相关代码:对于栈功能还不熟悉的友友也可以移步我的这篇文章学习一下:https://mp.csdn.net/mp_blog/creation/editor/133249808

#include "Stack.h"

void STInit(ST* ps)
{
	assert(ps);
	ps->a = NULL;
	ps->capacity = 0;
	ps->top = 0;
}

void STDestroy(ST* ps)
{
	assert(ps);

	free(ps->a);
	ps->a = NULL;
	ps->top = ps->capacity = 0;
}

void STPush(ST* ps, STDataType x)
{
	assert(ps);
	// 11:40
	if (ps->top == ps->capacity)
	{
		int newCapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
		STDataType* tmp = (STDataType*)realloc(ps->a, sizeof(STDataType) * newCapacity);
		if (tmp == NULL)
		{
			perror("realloc fail");
			exit(-1);
		}

		ps->a = tmp;
		ps->capacity = newCapacity;
	}

	ps->a[ps->top] = x;
	ps->top++;
}

void STPop(ST* ps)
{
	assert(ps);

	// 
	assert(ps->top > 0);

	--ps->top;
}

STDataType STTop(ST* ps)
{
	assert(ps);

	// 
	assert(ps->top > 0);

	return ps->a[ps->top - 1];
}

int STSize(ST* ps)
{
	assert(ps);

	return ps->top;
}

bool STEmpty(ST* ps)
{
	assert(ps);

	return ps->top == 0;
}

4b12323f94834afd9ec146a3c10df229.jpeg六.结语

本文整体上是对快排进行的优化,让它的性能更加强大,另外也帮助我们在掌握好递归的同时也能运用好非递归。最后感谢大家的观看,友友们能够学习到新的知识是额滴荣幸,期待我们下次相见~

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

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

相关文章

如何用AirServer进行手机投屏?,Airserver 永久激活注册码

AirServer一款投屏神器&#xff0c;可以帮你轻松地将iPhone、iPad投屏到Mac。是不是经常看到游戏主播用AirServer投屏&#xff1f;此外&#xff0c;AirServer也是视频Up主必备工具之一&#xff01;用来录制演示教程不错。除了实现单个手机投屏到电脑或荧幕。如果你有多画面投屏…

Python - 【Socket】消息粘包处理demo(一)

一. 前言 在网络编程中&#xff0c;粘包是指TCP协议在传输过程中&#xff0c;多条数据被合并成一条数据发送或者一条数据被拆分成多个数据发送的现象。 二. 粘包问题的常规处理方法&#xff1a; 使用固定长度的包头 可以在发送数据前先发送一个固定长度的包头&#xff0c;包…

实现单链表的增删改查

实现单链表的增删改查的功能&#xff1a;头部插入删除/尾部插入删除&#xff0c;查找&#xff0c;在指定位置之前插入数据&#xff0c;删除pos节点&#xff0c;在指定位置之后插入数据&#xff0c;删除pos之后的节点&#xff0c;销毁链表。 SListNode.h #pragma once #includ…

美,英,法,德、意大利和西班牙的geojson,以及区域json

美&#xff0c;英&#xff0c;法&#xff0c;德、意大利和西班牙的geojson文件 json地址 https://pan.baidu.com/s/1nio1bV_j-jAEVqgEHXWsNw?pwdqwer#list/path/GEOJSON 感谢大佬提供的 大佬连接 大佬的知乎原地址 国内geojson获取工具地址 http://da![在这里插入图片描述](h…

【wu-framework-parent 1.2.2-JDK17-SNAPSHOT 新版本中的 ACW】

版本: 1.2.2-JDK17-SNAPSHOT 项目地址&#xff1a;https://gitee.com/wujiawei1207537021/wu-framework-parent/tree/master/wu-smart-intergration/wu-smart-acw 演示地址&#xff1a;http://124.222.48.62:30193/wu-smart-acw-ui/index.html admin/admin docker启动 dock…

智能控制箱解决方案

一、建设背景 在数字时代的浪潮中&#xff0c;智能化的管理和操作已经成为各行业的共同追求。唯众物联网智能控制箱&#xff0c;是一款功能性强、使用安装简单的智能网络传输、温湿度监测、门锁控制综合系统。系统由I/O网络模块、传感器、管理后端、移动端APP组成&#xff0c;辅…

docker-compose搭建redis哨兵模式

文件存放如下图&#xff1a; docker-compose.yml文件内容如下&#xff1a; version: 3.3 services:master:image: redis:3.2.12restart: alwayscontainer_name: redis-mastercommand: redis-server /usr/local/redis/conf/redis.confports:- 6380:6380volumes:- /root/redis/…

【源码】医院绩效管理系统,针对医、护、技、药、管不同岗位,可设置不同的核算方法、核算参数

医院绩效管理系统源码 医院绩效管理系统以国家医院绩效管理考核政策法规为依据&#xff0c;结合医院管理实践&#xff0c;以经济管理指标为核心&#xff0c;医疗质量、安全、效率、效益管理为重点&#xff0c;特别强调持续改进&#xff08;PDCA&#xff09;管理理念。实现医院绩…

从专线到云桥通SD-WAN企业组网开通速度对比

云桥通SD-WAN企业组网和专线是企业在建立和管理广域网连接时广泛采用的两种技术和解决方案。 云桥通SD-WAN企业组网基于软件定义网络技术&#xff0c;通过引入虚拟化、智能路由和网络功能虚拟化等技术&#xff0c;实现对多种网络连接的集中管理和控制。云桥通SD-WAN企业组网能够…

Cyber RT 服务通信

场景&#xff1a; 用户乘坐无人出租车过程中&#xff0c;可能临时需要切换目的地&#xff0c;用户可以通过车机系统完成修改&#xff0c;路径规划模块需要根据新的目的地信息重新规划路径&#xff0c;并反馈修正后的结果给用户&#xff0c;那么用户的修正请求数据与修正结果是如…

LINUX服务之YUM仓库

1. YUM概述 YUM基于RPM包构建的软件更新机制 可以自动解决依赖关系 所有软件包由集中的YUM软件仓库提供 YUM支持软件源 搭建yum支持的的软件源主要有以下三种&#xff1a; 本地yum&#xff1a;file&#xff1a;//… 网络yum&#xff0c;又分为HTTP服务器&#xff1a;http…

【开源项目】经典开源项目数字孪生智慧楼宇,分享revit数据

智慧楼宇IBMS可视化运营平台&#xff0c;一个集综合态势、能耗管理、智慧安防和设备运维于一体的智慧管理中心。飞渡科技数字孪生平台集结构、系统、服务、管理及它们之间的最优化组合&#xff0c;使冰冷的混凝土结构演变为智慧化、高效率以及安全性更强的生活和工作空间。 在综…

深度解析Oladance、韶音、南卡开放式耳机:选购指南与天花板级推荐

​随着开放式耳机在日常生活中越来越受欢迎&#xff0c;许多品牌纷纷降低材料品质以迎合大众需求&#xff0c;导致耳机的性能和音质严重下滑。这让消费者在选择优质开放式耳机时感到困惑。作为一名专业的耳机评测人员&#xff0c;我近期对多款热门开放式耳机进行了深入的测评&a…

postman测试导入文件

01 上传文件参数 1.选择请求方式 选择post请求方式&#xff0c;输入请求地址 2.填写Headers Key&#xff1a;Content-Type &#xff1b; Value&#xff1a;multipart/form-data 如下图 3.填写body 选择form-data&#xff0c;key选择file类型后value会出现按钮&#xff0…

2024三掌柜赠书活动第四期: Next.js实战,构建现代化的可扩展Web应用

目录 摘要前言Next.js简介关于《 Next.js实战》实战示例最佳实践和进阶应用编辑推荐内容简介作者简介图书目录书中前言/序言《Next.js实战》全书速览结束语 摘要&#xff1a;本文将介绍Next.js&#xff0c;一个流行的React框架&#xff0c;以及如何在实际项目中使用Next.js构…

vue中封装使用svg图片

vue中封装使用svg图片 svg的优点 &#xff1a; 可以无损放大或缩小&#xff0c;支持多种滤镜和效果&#xff0c;易于修改和编辑 封装组件更加方便使用 1、封装SvgIcon组件 <template><svg :class"svgClass" aria-hidden"true"><use :xli…

Pytorch自动求导机制

PyTorch框架可以帮我们计算好反向传播&#xff0c;需要求导的&#xff0c;可以手动定义 示例&#xff1a; #先构造一个随机的三行四列矩阵&#xff0c;两种方法都可以#方法1 x torch.randn(3,4,requires_gradTrue) x#方法2 x torch.randn(3,4) x.requires_gradTrue x #再构…

路由器配置虚拟服务器

文章目录 路由器配置虚拟服务器1.前言2.配置流程2.1 进入路由器的登录页面2.2 找到端口映射功能2.3 添加虚拟服务器2.4 查找路由器的动态IP2.5 SSH连接 路由器配置虚拟服务器 1.前言 局域网下面连接着路由器&#xff0c;路由器下面连接着服务器&#xff0c;我们自己的电脑想要…

专业Python集成开发 -- Wing Pro 9

Wing Pro 9是一款功能强大的Python集成开发环境&#xff08;IDE&#xff09;&#xff0c;专为专业开发人员设计。它提供了丰富的功能和工具&#xff0c;旨在提高Python开发人员的生产力。 Wing Pro 9的核心特点包括高级代码编辑器、调试器、重构工具、版本控制等。高级代码编辑…

buffer pool和查询缓存的区别

在学习buffer pool的时候我产生了疑问&#xff0c;buffer pool和查询缓存是一个东西吗&#xff1f; 结论&#xff1a;不是一回事。 buffer pool buffer pool我之前介绍过&#xff0c;它的出现是为了提高查找效率&#xff0c;缓存磁盘上的数据页。 buffer pool虽说是内存中的一…