数据结构之排序【快速排序和归并排序的非递归代码实现及分析】

news2025/1/10 23:58:23

引言:

今天因为要写论文,所以现在有点迟了,并且此时是北京时间:2022/12/28/1:41 ,我发现晚睡我真的是专业的,当然睡觉我也是专业的,懂的都懂,现在有点迟加上天大寒,手指不可屈伸,所以我们的引言就这样啦!但是这个位置我还想要记录一下:今天我的搜狗输入法成功进入20万字了,电脑上自带的键盘都要给我敲烂了,我已经能听出来空格键的声音跟以前不一样了,但是还可以用,本来是打算在20万字之时就换一个键盘的,但是……一言难尽,所以暂时我们继续用电脑上的键盘,等我们到40万字的时候再换吧(想象一下,等40万字,可能……)。上篇博客我们讲到了快速排序的递归方法实现,今天我们就讲一下快速排序的非递归实现。

1.快排实现(非递归)

有的人会问,我们不是已经实现了快排吗?快排我已经学会了啊,为什么还要区分递归和非递归呢?
原因就是递归的缺陷,递归是有一定的缺陷的。

递归的缺陷:建立栈帧,效率低(但是这个对于递归基本不算是缺陷) ,在一些极端情况下,栈帧深度(因为调用一个函数就会创建一个栈帧)太深,会导致栈溢出(这个就是递归的真正的大缺陷)

所以我们就要想一些方法来解决这个缺陷,当然解决方法有很多,比如增加栈的空间,使它不会栈溢出,但是这好像是不可能的,所以最好的方法就是不使用递归(当然是在某些特定的情况下不使用)
所以我们就可以想到递归的替换方法,我相信大多数人第一时间都会想到的是用循环来替代递归,但是今天我们不使用循环来替代递归,今天我们使用数据结构中的栈来模拟递归的过程,当然前提是有一个栈,所以我们要先自己实现一个栈。

代码如下:

void Swap(int* child, int* parent)
{
	int tmp = *child;
	*child = *parent;
	*parent = tmp;

}
int PartQuickSort1(int* arr, int left, int right)
{
	int index = GetMidIndex(arr, left, right);
	Swap(&arr[left], &arr[index]);

	int begin = left;
	int end = right;
	int pivot = begin;
	int key = arr[begin];
	while (begin < end)
	{
		while (begin < end && arr[end] >= key)
		{
			end--;
		}
		arr[pivot] = arr[end];
		pivot = end;

		while (begin < end && arr[begin] <= key)
		{
			begin++;
		}
		arr[pivot] = arr[begin];
		pivot = begin;
	}

	pivot = begin;
	arr[pivot] = key;

	return pivot;
}

void QuickSortNonR(int* arr, int n)
{
	//此时因为这个需要有一个栈需要我们去拿以前文件中的栈的实现的代码
	//所以为了方便,我们把这个非递归的快排的实现给拿到外面去
	ST st;
	StackInit(&st);
	//栈实现的原理:栈里面的区间就是需要被单趟排序的
	StackPush(&st, n - 1);//根据栈的原理(后进先出),当我们想要先出我们的左,我们就不应该先入左,而是应该要先入右,此时的右也就是我们的n-1(传参传的是闭区间的话)
	StackPush(&st, 0);//根据栈的原理,并且同理的情况下,这边我们要出的是右,所以我们就应该要入左,也就是入0

	while (!StackEmpty(&st))//只要栈里面有数据就不结束,只有当我的栈里面的数据为空的时候才停止
	{
		//此时如上面两步,我已经把我的左子数组和右子数组给放进去了
		int left = StackTop(&st);//数组放进去之后,我就开始出数组中的元素(并且此时因为先进后出原理,所以此时我们栈中出的第一个元素就是我的要放在arr[0]的元素了)
		StackPopt(&st);//出完元素要删除掉,因为是栈,只有这样我们才可以出剩下的元素

		int right = StackTop(&st);
		StackPopt(&st);

		int keyIndex = PartQuickSort1(arr, left, right);
		//此时有了上面这个挖坑法的代码,我们此时就可以拿到中间的那个key了
		//所以我们此时就把这个数组给分为了左子区间和右子区间
		//[left,keyIndex-1][keyIndex][keyIndex+1,right]
		if (keyIndex + 1 < right)
		{
			//此时就是表示右半区间还存在,元素没有排序完,我们就还需要入栈
			StackPush(&st, right);
			StackPush(&st, keyIndex + 1);
		}
		if (left < keyIndex - 1)//这个表示此时的栈
		{
			//因为上述的代码left是会++的,所以只要left没有走到key的位置处,就是表示此时left中还有元素没有出完
			StackPush(&st, keyIndex-1);
			StackPush(&st, left);
		}
	}

	StackDestory(&st);

}

此时的void QuickSortNonR(int* arr, int n)这个代码中的内容就是不使用递归实现快排的代码,而int PartQuickSort1(int* arr, int left, int right)而这个函数就是一个普通的挖坑法获取key的代码而已,所以重点是void QuickSortNonR(int* arr, int n)这个函数中的内容,但是前提是我们要有一个数据结构中的栈,所以我们此时就还要实现一个栈出来(C语言的不好的地方,假如是C++或者Java就可以不用,因为它们有自己独立的栈可以供我们直接调用),所以关于栈的知识可以看我以前的博客,讲解的都是非常的到位的。

2.归并排序(非递归)

原理如图:
在这里插入图片描述

代码实现:

void MergeSortNonR(int* arr, int n)
{
	//归并的思想,我肯定是要有一个临时数组的
	int* tmp = (int*)malloc(sizeof(int) * n);
	int gap = 1;//表示每一组需要进行归并的数据的个数
	while (gap < n)
	{
		int i;
		for (i = 0; i < n; i += 2 * gap)//由图得到
		{
			//[i,i+gap-1] [i+gap,i+gap*2-1]
				//归并算法的代码实现:
			int begin1 = i;
			int end1 = i + gap - 1;
			int begin2 = i + gap;
			int end2 = i + gap * 2 - 1;
			int index = i;

			//此时因为数据的个数,不一定按照整数倍,所以在划分组的时候就可能导致越界,或者不存在
			//所以需要修正一下
			if (begin2 >= n)
			{
				begin2 = n + 1;
				end2 = n;
			}
			//end2越界也要修正一下
			if (end1 >= n)
			{
				end1 = n - 1;
			}
			//end2越界,需要修正后再归并
			if (end2 >= n)
			{
				end2 = n - 1;
			}

			while (begin1 <= end1 && begin2 <= end2)
			{
				if (arr[begin1] < arr[begin2])
				{
					tmp[index++] = arr[begin1++];
				}
				else
				{
					tmp[index++] = arr[begin2++];
				}
			}
			while (begin1 <= end1)
			{
				tmp[index++] = arr[begin1++];
			}
			while (begin2 <= end2)
			{
				tmp[index++] = arr[begin2++];
			}
		}
		//将新数组中的元素拷贝回原数组
		int j;
		for (j = 0; j < n; j++)
		{
			arr[j] = tmp[j];
		}

		gap *= 2;
	}

	free(tmp);
}

3.测试代码

在这里插入图片描述

时间有点急了,已经快3点多了,所以就这样吧!我要睡了。

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

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

相关文章

android之View的滑动

其实不管是哪种滑动方式&#xff0c;基本思想都是类似的&#xff1a;当点击事件传递到View时&#xff0c;系统记下触摸点的坐标&#xff0c;手指移动的时候&#xff0c;系统记下移动后的坐标&#xff0c;并计算出偏移量&#xff0c;并通过偏移量来修改View的坐标。 下面我们来…

黑客比程序员高在哪里?

黑客其实和一般的程序员一样&#xff0c;但是他们的关注点不一样。黑客关注的是如何破坏&#xff0c;通过这些有创造性的破坏来获取利益&#xff0c;展现自己的能力。而程序员关注的是如何创造&#xff0c;通过创造来获取利益&#xff0c;展现自己的能力。 就如同一个硬币的两…

CCF BDCI|算能赛题决赛选手说明论文-01

基于TPU平台实现人群密度估计 加速器队伍 黄显钧 个人名义参赛 中国-广东广州peterhuang0323qq.com 团队简介 加速器队伍队长&#xff1a;黄显钧&#xff0c;现任某科技公司的高级工程师&#xff0c;技术栈涉足嵌入式全栈开发&#xff0c;AI 开发等领域&#xff0c;对技术充满…

云桌面 Vscode 远程debug python

云桌面 Vscode 远程debug python1、进入云桌面2、下载VScode配套软件3、挂载本地磁盘4、安装软件4.1 安装VScode4.2 安装插件vsix文件4.3 在服务端安装vscode server5、VScode 配置6、远程调试6.1 python解释器选择6.2 设置debug7. mtu 配置Author: 沧海一阳1、进入云桌面 根据…

傻白入门芯片设计,盘点计算机体系结构顶会

目录 一、集成电路/半导体领域的三大顶会&#xff1a; &#xff08;1&#xff09;ISSCC &#xff08;2&#xff09;IEDM &#xff08;3&#xff09;VLSI 二、计算机体系结构四大顶会 &#xff08;1&#xff09;ISCA &#xff08;2&#xff09;HPCA &#xff08;3&#x…

42. 网络中的网络(NiN)

LeNet、AlexNet和VGG都有一个共同的设计模式&#xff1a;通过一系列的卷积层与汇聚层来提取空间结构特征&#xff1b;然后通过全连接层对特征的表征进行处理。 AlexNet和VGG对LeNet的改进主要在于如何扩大和加深这两个模块。 或者&#xff0c;可以想象在这个过程的早期使用全连…

利用GithubPage和Hexo搭建个人博客

title: 利用Github搭建个人博客 date: 2022-11-28 20:55:30 tags: [blogs] categories: Hexo 建立Git远程仓库 固定格式为&#xff1a;name.github.io ![]](https://img-blog.csdnimg.cn/fa9d7320d1cc422a8a79f2b41dd8458e.png) 开启Github Pages 设置github的token登陆 连接…

免费在线绘制高颜值,带填充的连贯堆叠柱状图

堆叠柱状图是我们日常工作中经常使用的一类图形。然而当分类较多时&#xff0c;堆叠柱状图看起来不是那么清晰&#xff0c;通过添加额外的连线&#xff0c;可以增加堆叠柱状图的颜值&#xff0c;给人一种连贯的感觉&#xff0c;并且能够更好地观察数据比例的变化。 图1. 堆叠柱…

1.8 异常 模块和包

文章目录了解异常异常的捕获方法为什么需要捕获异常捕获常规的异常捕获指定的异常捕获多个异常捕获所有的异常异常else异常的finally异常的传递Python模块模块的导入自定义模块测试模块\_all\_模块Python包了解异常 当我们的解释器运行时发生了一些没办法的操作&#xff0c;或…

初学Java web(十)

Filter和Listener 一.Filter 概念&#xff1a;Filter表示过滤器&#xff0c;是JavaWeb三大组件(Servlet、Filter、Listener)之一。 过滤器可以把对资源的请求拦截下来&#xff0c;从而实现一些特殊的功能。 过滤器一般完成一些通用的操作&#xff0c;比如&#xff1a;权限控…

新键盘到了,我的工作效率提升了数十倍

前言&#xff1a;快过年了&#xff0c;找到了一份满意的实习&#xff0c;正好旧的键盘坏掉了&#xff0c;最近入手了一款不错的机械键盘奖励自己。到货使用一段时间了&#xff0c;来一篇键盘开箱的博客做一个反馈。 新键盘使用感言&#xff1a;优良的键盘如图云上漫步&#xf…

白质中的BOLD信号激活检测问题

白质中BOLD信号的生理意义存在争议的两个主要原因&#xff1a; BOLD信号依赖于脑血流量CBF和脑血容量CBV&#xff0c;但是白质中的血流量和血容量比灰质中的少得多&#xff08;利用MRI估计的微血管数量&#xff1a;白质为10-192条/mm^2&#xff0c;灰质为99-761条/mm^2&#x…

【GUI界面】基于Python的WSG84三点定位系统(经纬度坐标与平面坐标转换法求解)

【GUI界面】基于Python的WSG84三点定位系统&#xff08;经纬度坐标与平面坐标转换法求解&#xff09; 方法汇总&#xff1a; blog.csdn.net/weixin_53403301/article/details/128441789 【精准三点定位求解汇总】利用Python或JavaScript高德地图开放平台实现精准三点定位&…

[开源工具]使用Fiddler简单计算QPS[新手开箱可用]

使用Fiddler简单计算QPS1.什么是QPS?2.怎么计算QPS?3.如何使用Fiddler得到一个API接口的QPS?3.1配置&#xff1a;打开Fiddler文件夹&#xff0c;点击Fiddler.exe运行fiddler进行配置4.如何得到本机的核心数?5.根据公式计算QPS?6.扩展计算单机可支撑PV(理论值)?1.什么是QP…

springboot中controller层接收参数,servers层调用mapper层,一条sql搞定排序

前言 很多小伙伴们在公司不管是测试C端产品还是B端产品&#xff0c;都会测到排序的业务需求&#xff1b;那么我们就会好奇排序是如何实现的呢&#xff1f;下面我们开始介绍代码的实现 数据库建表 我们需要创建一个书籍book表结构&#xff0c;如下图所示 CREATE TABLE book ( …

嵌入式C语言面向对象编程 --- 总结

什么是 C 语言面向对象? 在开始嵌入式 C 语言设计模式系列文章之前,先通过三篇文章讲述了如何使用 C 语言实现面向对象的三大特性,封装,继承,多态。 图片来源公众号:码农翻身 对于“面向对象”这个词语,相信很多软件工程师都不会感觉到陌生,并且很多软件工程师在刚开…

ABAP: 定义关键字的区别

问题&#xff1a;TYPE、LIKE、LIKE TABLE OF、LIKE LINE OF、TYPE TABLE OF 的区别&#xff1f; 1、TYPE 用于变量的类型定义&#xff0c;可以是表中预定义好的字段&#xff0c;也可以是C(字符)&#xff0c;F(浮点型)&#xff0c;I(整型)等。 例如&#xff1a; DATA: NAME TY…

Spring Cloud简介

一、什么是SpringCloud&#xff1f; Spring Cloud 是一系列框架的有序集合。 Spring Cloud 并没有重复制造轮子&#xff0c;它只是将目前各家公司开发的比较成熟、经得起实际考验的服务框架组合起来。 通过 Spring Boot 风格进行再封装屏蔽掉了复杂的配置和实现原理&#xff…

Python 图像边缘检测 | 利用 opencv 和 skimage 的 Canny 算法

文章目录一、简介二、opencv 实践三、skimage 实践CSDN 叶庭云&#xff1a;https://yetingyun.blog.csdn.net/ 一、简介 提取图片的边缘信息是底层数字图像处理的基本任务之一。边缘信息对进一步提取高层语义信息有很大的影响。大部分边缘检测算法都是上个世纪的了&#xff0c…

【Java基础】day11

day11 一、BIO、NIO、AIO 三种 IO 模型分别是什么&#xff1f; BIO &#xff08;Blocking I/O&#xff09;同步阻塞的 I/O 、NIO&#xff08;New/Non-blocking I/O&#xff09; 同步非阻塞的 I/O 、AIO&#xff08;Asynchronous I/O&#xff09; 异步非阻塞的 I/O 。这三种 I…