快速排序(非递归)以及归并排序的递归与非递归

news2024/11/25 2:34:00

快速排序的非递归算法:

递归次数太多的缺陷:极端情况下(栈帧深度太深)会导致栈溢出,即使程序代码正确(递归的深度足够深时,空间不足,就会导致栈溢出),因此在实际应用中通常情况下是利用非递归算法实现。

递归改成非递归:1.直接改循环(简单)2.借助数据结构栈模拟递归过程(复杂)

首先要建立一个栈,直接引用之前建立过的栈即可,用栈来模拟左右递归的过程,由于栈是先进的后出,为了先排左边的,就得先将被分割后的右边下标存入栈中,当左边的下标被提取完之后,说明左边已经有序,接着取出右边的下标进行单趟排序即可。以此类推:

快速排序非递归代码:

void QuickSortNoneR(int* a, int n)
{
	ST st;
	StackInit(&st);
	StackPush(&st, n - 1);
	StackPush(&st, 0);
	while (!StackEmpty(&st))
	{
		int left = StackTop(&st);
		StackPop(&st);
		int right = StackTop(&st);
		StackPop(&st);
		int keyIndex = PartSort1(a, left, right);
		//[left,keyIndex-1] keyIndex [keyIndex+1,right]
		if (keyIndex + 1 < right)
		{
			StackPush(&st, right);
			StackPush(&st, keyIndex + 1);
		}
		if (keyIndex - 1 > left)
		{
			StackPush(&st, keyIndex - 1);
			StackPush(&st, left);
		}
	}
	StackDestory(&st);
}

Stack.h

#pragma once
#include<stdio.h>
#include<stdbool.h>
#include<assert.h>
#include<stdlib.h>
typedef int STDataType;
typedef struct Stack
{
	STDataType* a;
	int top;//栈顶
	int capacity;
}ST;
//初始化
void StackInit(ST* ps);
//销毁栈
void StackDestory(ST* ps);
//栈顶插入数据(入栈)
void StackPush(ST* ps,STDataType x);
//出栈
void StackPop(ST* ps);
STDataType StackTop(ST* ps);
int StackSize(ST* ps);
bool StackEmpty(ST* ps);

 Stack.c

#define _CRT_SECURE_NO_WARNINGS
#include"Stack.h"
//初始化
void StackInit(ST* ps)
{
	assert(ps);
	ps->a = (STDataType*)malloc(sizeof(STDataType) * 4);
	if (ps->a == NULL)
	{
		printf("malloc fail\n");
		exit(-1);//不是正常结束
	}
	ps->capacity = 4;
	ps->top = 0;//意味着top指向的是栈顶元素的下一位,若为-1,则top指向的是栈顶元素
}
//销毁栈
void StackDestory(ST* ps)
{
	assert(ps);
	free(ps->a);
	ps->a = NULL;
	ps->capacity = ps->top = 0; 
}
//栈顶插入数据(入栈)
void StackPush(ST* ps, STDataType x)
{
	assert(ps);
	if (ps->top == ps->capacity)
	{
		STDataType* tmp = (STDataType*)realloc(ps->a, ps->capacity * 2 * sizeof(STDataType));
		if (tmp == NULL)
		{
			printf("realloc fail\n");
			exit(-1);//不是正常结束
		}
		else
		{
			ps->a = tmp;
			ps->capacity *= 2;
		}
	}
	ps->a[ps->top] = x;
	ps->top++;
}
//出栈
void StackPop(ST* ps)
{
	assert(ps);
	assert(ps->top > 0);//栈空了,调用pop,直接中止程序报错
	ps->top--;
}
//返回栈顶元素
STDataType StackTop(ST* ps)
{
	assert(ps);
	assert(ps->top > 0);
	return ps -> a[ps->top - 1];
}
//返回栈中数据的数量
int StackSize(ST* ps)
{
	assert(ps);
	return ps->top;
}
//判断是否为空
bool StackEmpty(ST* ps)
{
	assert(ps);
	return ps->top == 0;
}

我对于快速排序的非递归说的不太详细,大家想要多理解的话点这里

快速排序 非递归icon-default.png?t=N7T8https://blog.csdn.net/qq_41890240/article/details/124110735,这个大大写的非常详细,这里我就不细说啦!膜拜膜拜!

 

归并排序的递归: 

归并排序与之前练习过的合并两个有序数组相似,将两个有序数组合并为同一个有序数组,最终的有序数组为临时创建的数组,归并排序的前提条件是分开的左右两个数据有序,由于随机给的数据不太可能是左右两边都有序,此时我们就可以想到将左右两边的数组不断地分组,知道一个数据为一组,可以看成是有序的,不断拆分的过程就是不断递归的过程。

接下来的问题就是如何将左右两个有序数据合并为一个有序的数组。

从两个数组的第一个元素开始比较,把小的放在新数组的第一个位置上,小的数组索引++,再接着与另一个数组的第一个元素比较,仍是把小的放在新数组上,以此类推即可。

这里的难点是递归的理解,递归中止的条件是,数组被分成了只有一个数据的数组,即左右下标相同,一次递归结束后(左边)只有一个数据,会进入上一层递归进行下一步操作,即找到右边的一个数据,接着进行有序数组的合并,接着将在临时数组已经排好序的两个数据拷贝到原数组中即可。

代码如下:

//归并排序
void _MergeSort(int* a, int left, int right,int* tmp)
{
	if (left >= right)
		return;
	int mid = (left + right) >> 1;
	//假设[left,mid][mid+1,right]有序,就可以使用归并排序
	_MergeSort(a, left, mid, tmp);
	_MergeSort(a, mid + 1, right, tmp);
	int begin1 = left, end1 = mid;
	int begin2 = mid + 1, end2 = right;
	int index = left;
	while (begin1 <= end1 && begin2 <= end2)
	{
		if(a[begin1]<a[begin2])
		{
			tmp[index++] = a[begin1++];
		}
		else 
		{
			tmp[index++] = a[begin2++];
		}
	}
	while (begin1 <= end1)
	{
		tmp[index++] = a[begin1++];
	}
	while (begin2 <= end2)
	{
		tmp[index++] = a[begin2++];
	}
	//拷贝
	for (int i = left; i <= right; i++)
	{
		a[i] = tmp[i];
	}
}
void MergeSort(int* a, int n)
{
	int* tmp = (int*)malloc(sizeof(int) * n);
	_MergeSort(a, 0, n - 1, tmp);
	free(tmp);
}

归并排序的非递归算法:

先设置一个gap为1,gap为要排序的每个数组(被分为若干份)的个数,以下图为例,将6和3看成一组,将其进行有序数组的排序,放在一个新数组中,后面的数依次排序并拷贝到新数组中,一轮排序完成后,将新数组的数拷贝到原数组中,接着将gap变为2,即3 6,5 2两个数组排序成2 3 5 6拷贝至新数组中,以此类推,,最终合并为一个1有序数组。

代码如下:

void MergeSortNonR(int* a, int n)
{
	int* tmp = (int*)malloc(sizeof(int) * n);
	int gap = 1;
	while (gap < n)
	{
		for (int i = 0; i < n; i += 2 * gap)
		{
			int begin1 = i, end1 = i + gap - 1;
			int begin2 = i + gap, end2 = i + 2 * gap - 1;
			int index = i;
			while (begin1 <= end1 && begin2 <= end2)
			{
				if (a[begin1] < a[begin2])
				{
					tmp[index++] = a[begin1++];
				}
				else
				{
					tmp[index++] = a[begin2++];
				}
			}
			while (begin1 <= end1)
			{
				tmp[index++] = a[begin1++];
			}
			while (begin2 <= end2)
			{
				tmp[index++] = a[begin2++];
			}
		}
		//拷贝
		for (int j = 0; j < n; j++)
		{
			a[j] = tmp[j];
		}
		gap *= 2;
	}
	free(tmp);
}

 里面对于两个有序数组合并为一个有序数组问题与归并排序思想相同,排序部分的代码也与递归相同,只是将递归变成循环而已。但是这个代码有一个缺陷,就是如果当数组个数为奇数,最终总会有一个不能像其他数据一样有与其相同个数的数组排序如下:

由于gap每次都*2,也有可能右半区间数据个数不够,因此需要适当修正一下。

 当然,左半区间也可能出现个数不够的情况,这时左半部分就不会进行排序,并不会拷贝到新数组中,这时将新数组中的数据拷贝到原数组最后几个左区间就会是随机值,因此将新数组的数据拷贝到原数组中时,需要将排过序的数组拷贝至原数组中。

//归并过程中右半区间可能不存在
if (begin2 >= n)
	break;
// 归并区间右半部分区间算多了,修正一下
if (end2 >= n)
	end2 = n - 1;

拷贝:

//拷贝
			for (int j = i; j < i + end2; j++)
			{
				a[j] = tmp[j];
			}

修改后的代码:

void MergeSortNonR(int* a, int n)
{
	int* tmp = (int*)malloc(sizeof(int) * n);
	int gap = 1;
	while (gap < n)
	{
		for (int i = 0; i < n; i += 2 * gap)
		{
			int begin1 = i, end1 = i + gap - 1;
			int begin2 = i + gap, end2 = i + 2 * gap - 1;
			//归并过程中右半区间可能不存在
			if (begin2 >= n)
				break;
			// 归并区间右半部分区间算多了,修正一下
			if (end2 >= n)
				end2 = n - 1;
			int index = i;
			while (begin1 <= end1 && begin2 <= end2)
			{
				if (a[begin1] < a[begin2])
				{
					tmp[index++] = a[begin1++];
				}
				else
				{
					tmp[index++] = a[begin2++];
				}
			}
			while (begin1 <= end1)
			{
				tmp[index++] = a[begin1++];
			}
			while (begin2 <= end2)
			{
				tmp[index++] = a[begin2++];
			}
			//拷贝
			for (int j = i; j <= end2; j++)
			{
				a[j] = tmp[j];
			}
		}
		
		
		gap *= 2;
	}
	free(tmp);
}

这样子无论数组元素个数为多少都可以排序成功啦!

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

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

相关文章

hbase用shell命令新建表报错ERROR: KeeperErrorCode = NoNode for /hbase/master

或者HMster开启后几秒消失问题解决 报错如图&#xff1a; 首先jps命令查看当前运行的内容有没有HMaster,如果没有&#xff0c;开启一下hbase,稍微等一会儿&#xff0c;再看一下HMaster,如果仍和下图一样没有&#xff0c;就基本找到问题了 本人问题原因&#xff1a;hbase-site…

【深入使用】PHP的PDO 基本使用

前言&#xff1a; PDO&#xff1a;数据库抽象层 简介&#xff1a;PDO扩展为PHP访问数据库定义了一个轻量级的、一致性的接口&#xff0c;PDO解决了数据库连接不统一的问题。是PHP 5新加入的一个重大功能 【为什么要使用PDO】&#xff1f; PDO是PHP5新加入的一个重大功能&a…

hab_virtio hypervisor 虚拟化

Linux的 I / O 虚拟化 Virtio 框架 简而言之&#xff0c;virtio是半虚拟化管理程序中设备上的抽象层。virtio由Rusty Russell开发以支持他自己的虚拟化解决方案lguest。本文从准虚拟化和仿真设备的介绍开始&#xff0c;然后探讨的细节virtio。重点是virtio2.6.30内核发行版中的…

提前预判和确认再做 现货白银投资的两种思路

在现货白银投资中&#xff0c;对于交易的步骤长期有两种看法。一种是提前预判行情并提前布局。另外一种是等待行情启动再做布局。这种两种方法要怎么选呢&#xff1f;笔者将从自己的角度出发&#xff0c;对这个问题进行讨论。 我们来看一下前一种的投资者&#xff0c;他们喜欢提…

vulnhub-Tre(cms渗透)

靶机和kali都使用net网络&#xff0c;方便探测主机获取ip1.靶机探测 使用fping扫描net网段 靶机ip&#xff1a;192.168.66.130 2.端口扫描 扫描发现该靶机三个端口&#xff0c;ssh&#xff0c;还有两个web&#xff0c;使用的中间件也是不一样的&#xff0c;一个是apache&…

TortoiseGit通过SSH连接配置,生成SSH密钥方法

生成SSH密钥&#xff1a; Win环境下命令(git ssh key是可以自定义命名的)&#xff1a; ssh-keygen -t ed25519 -C "git ssh key" && start "" "C:\Windows\notepad.exe" "C:\Users\%username%\.ssh\id_ed25519.pub" 打开cm…

指针---你真的会使用指针吗?

指针作为C语言中的一个部分&#xff0c;可以说指针是C语言的核心&#xff0c;那么它的难度肯定是不言而喻的&#xff0c;总是能把人给绕得找不到方向。 今天我就好好的说一说指针这个东西。 1、何为指针&#xff1f; 指针是C语言中用来存放地址的一个变量类型。我们可以将指针看…

图像增强2023最新经典顶会论文和代码合集,附开源数据集下载

图像增强是图像处理领域长期的、不可回避的研究课题&#xff0c;也是活跃在各大顶会中的热门研究方向。其应用范围广泛涉及医学影像分析、安全监控、智能交通、目标检测与识别、遥感影像处理等领域&#xff0c;具有很高的实用性&#xff0c;因此每年的相关论文数量也比较多。 …

2023/12/20 work

1. 使用select完成TCP客户端程序 服务端&#xff1a;#include <stdio.h> #include <string.h> #include <stdlib.h> #include <myhead.h>#define PORT 9999 //端口号 #define IP "192.168.125.12" //IP地址int ma…

C++实现位图

目录 一、什么是位图 二、位图类 1.参数及构造函数 2.set函数设置为1&#xff08;代表存在&#xff09; 3.reset函数设置为0&#xff08;代表不存在&#xff09; 4.test函数查看状态&#xff08;0还是1&#xff09; 三、位图的变形 一、什么是位图 位图这个词汇比较少见…

一行代码修复100vh bug

你知道奇怪的移动视口错误&#xff08;也称为100vh bug&#xff09;吗&#xff1f;或者如何以正确的方式创建全屏块&#xff1f; 一、100vh bug 什么是移动视口错误&#xff1f; 你是否曾经在网页上创建过全屏元素&#xff1f;只需添加一行 CSS 并不难&#xff1a; .my-page {h…

PAT 乙级 1023 组个最小数

1023 组个最小数 分数 20 作者 CAO, Peng 单位 Google 给定数字 0-9 各若干个。你可以以任意顺序排列这些数字&#xff0c;但必须全部使用。目标是使得最后得到的数尽可能小&#xff08;注意 0 不能做首位&#xff09;。例如&#xff1a;给定两个 0&#xff0c;两个 1&#xff…

【教程】cocos2dx资源加密混淆方案详解

1,加密,采用blowfish或其他 2,自定是32个字符的混淆code 3,对文件做blowfish加密,入口文件加密前将混淆code按约定格式(自定义的文件头或文件尾部)写入到文件 4,遍历资源目录,对每个文件做md5混淆,混淆原始串“相对路径”“文件名”混淆code, 文件改名并且移动到资源目录根…

Python开发GUI常用库PyQt6和PySide6介绍之二:设计师(Designer)

Python开发GUI常用库PyQt6和PySide6介绍之二&#xff1a;设计师&#xff08;Designer&#xff09; PySide6和PyQt6都有自己的设计师&#xff08;Designer&#xff09;&#xff0c;用于可视化地设计和布局GUI应用程序的界面。这些设计师提供了丰富的工具和功能&#xff0c;使开…

acwing-蓝桥杯C++ AB组辅导课Day2-递归习题+递推+二分

感谢梦翔老哥的蓝桥杯C AB组辅导课~ 递归习题&#xff1a; 1.递归实现组合型枚举 题意&#xff1a; 题目要求输出组合枚举&#xff0c;与排列不同&#xff0c;排列具有顺序之分&#xff0c;对于组合来说&#xff0c;是没有顺序之分的&#xff0c;所以[1,2,3]和[3,2,1]被看成同…

灰盒测试简要学习指南!

在本文中&#xff0c;我们将了解什么是灰盒测试、以及为什么要使用它&#xff0c;以及它的优缺点。 在软件测试中&#xff0c;灰盒测试是一种有用的技术&#xff0c;可以确保发布的软件是高性能的、安全的并满足预期用户的需求。这是一种从外部测试应用程序同时跟踪其内部操作…

【【迭代16次的CORDIC算法-verilog实现】】

迭代16次的CORDIC算法-verilog实现 -32位迭代16次verilog代码实现 CORDIC.v module cordic32#(parameter DATA_WIDTH 8d32 , // we set data widthparameter PIPELINE 5d16 // Optimize waveform)(input …

数字化时代的智能支持:亚马逊云科技轻量应用服务器技术领先

轻量应用服务器是一种简化运维、门槛低的弹性服务器&#xff0c;它的"轻"主要体现在几个方面&#xff1a;开箱即用、应用优质、上手简洁、投入划算、运维简便以及稳定可靠。相较于普通的云服务器&#xff0c;轻量应用服务器简化了云服务的操作难度、使用和管理流程&a…

全链路压测之分布式架构/SkyWalking链路追踪/中间件

最近刷题&#xff0c;学习了些压测的知识&#xff0c;大多是在小破站上的笔记&#xff0c;仅供大家参考~ 一、分布式微服务架构 微服务&#xff1a;多个系统之间相互调用 全链路&#xff1a;简单理解&#xff0c;就是一个系统调用另一个系统 二、SkyWalking链路追踪平台 链路…

阿里云经济型、通用算力型、计算型、通用型、内存型云服务器最新活动报价

阿里云作为国内领先的云计算服务提供商&#xff0c;提供了多种规格的云服务器供用户选择。为了满足不同用户的需求&#xff0c;阿里云推出了经济型、通用算力型、计算型、通用型和内存型等不同类型的云服务器。下面将详细介绍这些云服务器的最新活动报价。 一、阿里云特惠云服…