基础排序算法-快排的非递归和归并的具体实现

2023-10-27

目录

快排的非递归实现:

归并排序:

归并排序的非递归实现:

内、外排序 


上文:7大排序算法-- 堆排 快速排序 --精解_luck++的博客-CSDN博客_堆排序 快速排序

快排的非递归实现:

我们知道快排的实现效率很高 但是它还是有个弊端 就是我们本身栈这个空间不大  所以一旦我们的递归太深 这个空间就不够用了 所以我们在一些情况下得 把快排改成分递归得形式

要改成非递归得形式得用到 数据结构中的 栈来实现 

我先把代码放出来 大家先看看:

int PartSort3(int* a, int left, int right)
{
	int tmp = CheckMidKey(a, (right - left + 1) >> 1, left, right);//改变选keyi的方式
	swap(a + left, a + tmp);

	int prev, cur;
	int keyi = left;

	prev = left;
	cur = left + 1;
	while (cur <= right)
	{

		if (a[cur] <= a[keyi] && a[++prev] != a[cur])//附加的判断条件 意思是我的prev和cur中
		{                                            //间交换得有值才让
			swap(a + prev, a + cur);
		}
		cur++;
	}
	swap(a + keyi, a + prev);
	return prev;
}

void QuickSortNonR(int* a, int left, int right)//快排的非递归实现
{
	St tmp;
	StackInit(&tmp);

	StackPush(&tmp, left);
	StackPush(&tmp, right);

	while (!StackIsEmpty(&tmp))
	{
		right = StackTop(&tmp);
		StackPop(&tmp);
		left = StackTop(&tmp);
		StackPop(&tmp);

		int keyi = PartSort3(a, left, right);//排完会把keyi位置的值的正确位置排好

		if (left < keyi - 1)// 等于的时候只有一个数据就不用排了
		{
			StackPush(&tmp, left);
			StackPush(&tmp, keyi - 1);
		}

		if (right > keyi + 1)
		{
			StackPush(&tmp, keyi + 1);
			StackPush(&tmp, right);
		}
	}

	StackDestory(&tmp);
}

 在我们使用栈实现之前 我们要知道栈是后进先出的 也就是栈是始终在数据的尾端进行操作的无论是入数据还是出数据

我们整体的思路是 

① 我们利用 栈 把 表示区间的两个数字 存储 到栈的这种数据结构中 

② 每次要使用时就把这段区间 拿出来使用(拿出来后还要把它删掉) 毕竟我们找keyi的函数的 参数 就是一个区间

③ 使用完 我们根据这个区间选出的 keyi 把它的左右区间 入到栈中 区间只有一个数据不入 区间不存在也是

④ 反复循环 直到栈里面没有区间了

 后面的操作都是一样的直到 我们的栈中没有数据了 

大区间->选keyi->小区间->选keyi->小小区间。。。。

当不再入区间后区间后就会 就轮到出 这个区间前面的区间 这样我们不就模拟了一个递归返回的操作了嘛 

大家看整体是不是和我们递归的操作是一样的内 我们只是用栈来暂时存储我们的区间的数字就可以实现递归的效果!!

实际上按照这样的思想来写的话其实用对列的数据结构来实现的话也是可以的  这不过这个时一层一层(区间逐渐分下来的一种结构)遍历下来的

归并排序:

归并排序其实就和 它的名字一样 是把两组数据归并成一组有序的数据 但有个前提是我们的两组数据的是有序的才行 (后面会说到是为什么 大家也可以先想一想) 时间复杂度:O(N*logN) 空间复杂度:O(N) 稳定性:稳定

我们先看归并排序的思路:

① 我们对两组设置对应的 用于遍历数据的 变量 

② 两组数据从头开始比较 哪一边小 那么这个这边的这个数就要放到我们预先开辟的数组里面(如果是链表的结构 直接插到比他小的数就可以了 如果没有 那么它就做第一个数据) 放完这个对应的变量 ++

这个是 两组数据 不有序 的情况: 

 这个就是 归并排序 的 思想 可是给我们的通常是一组无序的数据 如果只是单单把这组数据分成两部分 那这两部分我们又不能保证他们是有序的

所以我们可以把他们分割成 单个数为一组 不就可以了嘛 毕竟一个数肯定是有序的不是嘛

 现在大家看完思路后 来实现我们的代码吧 (因为我们要开辟一个数组 而我们的分割操作是要用到递归的 所以只写一个函数的话 那每递归一次就会开辟一次 所以我们还要新建一个子函数)

void _MergeSort(int* a, int left, int right, int* tmp)//归并排序
{
	if (left >= right) return;//只有一个数 区间不存在 返回

	int mid = (left + right) >> 1;
	_MergeSort(a, left, mid, tmp);
	_MergeSort(a, mid + 1, right, tmp);

}

void MergeSort(int* a, int n)
{
	int* tmp = (int*)malloc(sizeof(int) * n);
	_MergeSort(a, 0, n - 1, tmp);
}

这个是我们写的 分割部分 的函数 注意这里我们的区间设置是:

[left,mid] [mid+1,right]

因为我的编译器默认是向下取整的 所以我们的右半区间 应该是 mid+1 

    int begin1 = left, end1 = mid, begin2 = mid + 1, end2 = right;
	int index = left;
	while (begin1 <= end1 && begin2 <= end2)
	{
		if (a[begin1] > a[begin2]) tmp[index++] = a[begin2++];
		else tmp[index++] = a[begin1++];
	}

	while (begin1 <= end1) tmp[index++] = a[begin1++];
	while (begin2 <= end2) tmp[index++] = a[begin2++];
	memmove(a + left, tmp + left, sizeof(int) * (right - left + 1));

    因为很有可能我们在归并的时候我们有一组数据已经排完了 这个时候就要停下来了 因为还有剩的这一组数据就可以直接加到我们最终归并的数组里面去了        

    因为我们剩下的一组数据的第一个数据肯定是大于已经排完的那组中所有数据的(我们是小的先移进去大的留下来)而这个时候我们之前说过要保证我们我们两组数据是有序的 所以直接放进去的剩下的数据是一定比之前数据都大的数 且是有序的!!!

归并排序的非递归实现:

现在我们要实现归并的非递归,那我们要怎样把他们分成一个一个为一组呢,其实我们可以换个思路,又不是一定要把它分组,我们自己设置组不就好了嘛,我们想让它多少个为一组就让它多少个为一组,就好像是这样

 

 我们的一组的个数只要超过总个数就停下来,所以是不是用两个循环就可以搞定,大家来看我们的代码:

    int* tmp = (int*)malloc(sizeof(int) * n);
	assert(tmp);
	int gap = 1;
	while (gap < n)
	{
		for (int i = 0; i < n; i += 2 * gap)//换到下一个要归并的两组数据
		{
			int begin1 = i, end1 = i + gap - 1, begin2 = i + gap, end2 = i + 2 * gap - 1;

			int index = i;
			while (begin1 <= end1 && begin2 <= end2)
			{
				if (a[begin1] > a[begin2]) tmp[index++] = a[begin2++];
				else tmp[index++] = a[begin1++];
			}

			while (begin1 <= end1) tmp[index++] = a[begin1++];
			while (begin2 <= end2) tmp[index++] = a[begin2++];

		}
		memcpy(a, tmp, sizeof(int) * n);//我们排完要把排完的再拷回去进行下一层的排序
		gap *= 2;
	}

 但是我们现在的代码并不能很好排序任何数据  因为这里面还涉及我们的边界问题  我们每次gap都是成倍增长  我们划分的区间也是跟随gap变化  所以我们的区间是很有可能越界的   包括到那些不是数组里面的数据

            if (end1 >= n) end1 = n - 1;
			if (begin2 >= n)
			{
				begin2 = n;
				end2 = n - 1;
			}
			if (end2 >= n) end2 = n - 1;

 1.第一个边界处理:这里虽然我们的end1大于end了,那我们的begin2肯定也是大于n的,我们的第二个边界处理是为了让第二个区间不存时不进入第一个while循环,我们虽不会进入第一个循环,这里我们后面不是还有两个while循环嘛(这两个循环是处理剩余数据的),但是这里它会进去啊,进去一访问不就越界了嘛

2.第二个边界处理:这里的begin1大于了n,那我们的第二个区间就不存在了,后面的所有while循环都不能参加了,所以我们自己改变begin2和end2的大小,begin2>end2,这样我们的区间不存在,后面的循环也就没它的戏了

3.第三个边界同样也是为了防止最后那两个循环,不能让它访问的时候造成越界的问题

非递归和递归的空间复杂度和时间复杂度是一样的 时间复杂度:O(N*logN) 空间复杂度:O(N)

内、外排序 

我们之前学的这几个排序都是可以时内排序,内排序的意思就是在内存中进行排序,外排序就是在磁盘中进行排序(这个时候也就意味着我们的数据很大了)

我们的内排序也更快,相对的外排序就会慢一些;内存中的数据可以使用下标随机访问,但磁盘里的数据不可以,我们访问磁盘数据的形式是文件,我们使用那些文件操作函数的时候,都是串行访问的

 如果我们想要排文件里面的数据,就要把文件里面的数据大小切分成几个适合内存大小的几个小文件,把这几个小文件中的数据放到内存中去排序(别使用归并,会额外开一个空间),再把排序完的数据拿回来,再在磁盘中对这几个小文件进行归并,合成一个大的有序的文件


谢谢大家看到这里,预祝大家都能收到自己心仪大厂的offer!!! 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

基础排序算法-快排的非递归和归并的具体实现 的相关文章

  • 高效的使用top

    为什么80 的码农都做不了架构师 gt gt gt 对桌面用户来说 监视系统资源使用是一项重要的工作 通过这项工作 我们可以找到系统瓶颈所在 针对性的进行系统优化 识别内存泄露等 问题是 我们应该用什么软件 以及如果针对我们的需求使用它 在
  • Jedis使用教程详解

    目录 一 前言 二 基本使用 三 Jedis连接池 四 连接池参数 五 哨兵模式 六 集群模式 七 Springboot当中使用Jedis 八 Springboot源码分析 一 前言 Jedis是Redis的一款Java语言的开源客户端连接
  • 分库分表实战(10):新的挑战 — 千万级数据优化之垂直拆分

    V xin ruyuanhadeng获得600 页原创精品文章汇总PDF 前 言 读写分离方案上线后 订单sql查询时间再一次稳定在了300ms以下 此时对数据的增删改操作会走主库 而读请求会走从库 通过读写分离大大提升了数据读的处理能力
  • vue顶部菜单加左侧菜单_vue动态路由+侧边栏菜单之侧边栏菜单

    直接放我侧边栏组件代码 相关代码在vue动态路由 侧边栏菜单之动态路由 参考略作修改即可 新建目录Sidebar Sidebar gt index vue background color 304156 text color fff act
  • 阿里工程师修养之:技术三板斧:关于技术规划、管理、架构的思考的概述

    技术三板斧 前言 一 关于技术规划三板斧 二 关于技术管理三板斧 三 关于技术架构三板斧 四 关于赛车 赛道 赛手三段论 五 关于点线面体的思考 前言 实践需要理论的指导 理论从实践中来 作为技术工程师 要不断地从事件中反思经验 总结规律
  • sqldeveloper安装

    1 安装 下载地址 解压之后 运行目录下面的文件即可 运行界面如下 sqldeveloper是基于jdbc的 所以需要创建连接 打开SQL工作表 工具 gt SQL工作表 或者使用快捷键Alt F10 选择连接 2 连接Oracle数据库及
  • xshell 7使用密钥证书登录CentOS 7.9

    xshell 7证书登录CentOS 1 打开xshell 点击 文件 新建 2 连接成功后点击 工具 用户密钥管理者 点击 生成 密钥类型默认RSA 然后下一步 生成密钥后点击属性 然后把密钥复制下来 也可保存为文件 此时回到系统用户目录

随机推荐

  • Linux Host is not allowed to connect to this MySQL server解决方法

    先说说这个错误 其实就是我们的MySQL不允许远程登录 所以远程登录失败了 解决方法如下 在装有MySQL的机器上登录MySQL mysql u root p密码 执行use mysql 执行update user set host whe
  • Java教程(JAVA、分布式、微服务)

    Java语言教程 http www codingdict com tutorials Java教程 http www runoob com java java multithreading html Spring Cloud教程 http
  • java设计模式--[行为模式]--状态模式[state pattern]

    一 狀態模式 允許一個對象在其內部狀態改變時改變它的行為 這個對象看起來似乎修改了它的類 看起來 狀態模式好像是神通廣大 居然可以修改自身的類 二 狀態模式包括三個角色 1 環境 環境是一個類 該類含有抽象狀態聲明的變量 可以引用任何具體狀
  • 电脑正常登录QQ微信,但浏览器无法打开网页,这个你一定要学会!

    电脑能正常登录微信 QQ 但是浏览器无法打开网页的情况时有发生 掌握这三个方法 就能轻松解决问题 NO 01 检查电脑DNS是否正常 首先按Win R 输入CMD 回车 输入ping baidu com 回车 网络正常情况有回复 有 来自x
  • element table组件实现保留横向滚动条,去除纵向滚动条

    实现仅去除纵向滚动条效果 项目开发中 有这样一个需求 实现表格内容自动滚动 去掉纵向滚动条 代码如下所示 v deep webkit scrollbar width 0 height 0 这种写法确实实现了去掉了纵向滚动条的效果 不过对于我
  • 华为OD机试真题(Java),根据员工出勤信息,判断本次是否能获得出勤奖(100%通过+复盘思路)

    一 题目描述 公司用一个字符串来标识员工的出勤信息 absent 缺勤 late 迟到 leaveearly 早退 present 正常上班 现需根据员工出勤信息 判断本次是否能获得出勤奖 能获得出勤奖的条件如下 缺勤不超过1次 没有连续的
  • 活动安排算法

    问题描述 设有n个活动 每个活动要求使用统一资源 每个活动i都有起始时间s和一个结束时间f 活动1执行完成后活动2也可以完全执行 则活动1与活动2相容 活动安排问题就是要在所给的活动集合中选出最大的相容活动子集合 活动结束时间以升序排列 算
  • 面试官:说说常见的排序算法有哪些?区别?

    一 是什么 排序是程序开发中非常常见的操作 对一组任意的数据元素经过排序操作后 就可以把他们变成一组一定规则排序的有序序列 排序算法属于算法中的一种 而且是覆盖范围极小的一种 彻底掌握排序算法对程序开发是有很大的帮助的 对于排序算法的好坏衡
  • shell编程-case语句中遇到问题

    bin bash echo Hit a key then hit return read Keypress case Keypress in A Z echo Uppercase letter a z echo Lowercase lett
  • 设置QLineEidt部件输入时自动切换到英文输入法(无法输入中文)

    在输入密码时会通过输入法显示密码 只需设置一下LineEdit部件属性即可 setAttribute Qt WA InputMethodEnabled false 设置账号输入框点击时无法输入中文 使用ui的QLineEdit对象名调用 如
  • 基于SpringBoot开源项目JeeSite的持续交互介绍

    一 实战项目介绍 JeeSite 基于Spring Boot 2 0 数据存储MySQL 语言 Java 规模大小 适中 适合初学者 源码地址 https gitee com thinkgem jeesite4 本次项目演练地址 https
  • Deepin20-R7000开启显示器扩展

    联想R7000的Deepin20系统开启显示器扩展 深度论坛里我的问题贴 基本信息 机器配置 联想拯救者R7000 2020 CPU AMD R7 4800h 独显 NVIDIA 1650 出现的问题 系统安装时勾选了闭源的NVIDIA驱动
  • Qt开发记录5——Qt错误提示系列

    目录 Qt错误提示 1 multiple definition of MainWindow MainWindow QWidget 2 error invalid use of incomplete type class Ui MainWin
  • 开始学习鸟哥的Linux私房菜-基础篇(第五章)

    现在开始学习鸟哥的Linux私房菜基础篇 下面结合自己专业的学习和对这本书的理解 我从第五章开始学习 下面是我做的笔记 5 1 使用者与群组 分清用户 用户组与其他人 下面是文件的权限和对文件权限的修改 Linux文件权限概念 在Linux
  • Java-1.10

    题目描述 假设一个人45分30秒跑了14千米 编写程序 显示他以每小时多少英里为单位的平均速度 1英里约等于1 6千米 代码 public class Speed public static void main String args do
  • 矩阵简述

    矩阵加法 C ij A ij B ij 矩阵数乘 将该数与每一个元素相乘 矩阵乘法 设A大小为n m B大小为m p 则A和B的乘积得到的矩阵大小为n p 其中每一项 AB ij sum k 1 m A ik B kj 矩阵乘法不满足交换律
  • docker安装配置goland

    第一步 确保已经安装好docker 第二步 拉取最新的go版本 docker pull golang 第三步 查询镜像 展示所有的镜像 docker images 第四步 启动容器 docker run itd p 8081 8081 v
  • 慧荣SM3271AD芯片U盘量产

    优化方式可以默认的 容量优先 设置2个盘 一个可以引导的光驱 一个存储用的磁盘 开始刷机 看看系统 上认到的磁盘状态 测试用U盘光驱引导启动 顺利完成 量产工具 地址 https download csdn net download ive
  • Java实现excel导出功能的几种方法——poi、easyExcel、easypoi、jxl

    推荐使用easyExcel 简单好用 对于稍微复杂一点的表格 个人建议用jxl easypoi 以下代码中包含的操作 合并单元格 设置字体格式 加粗 字体大小 颜色 设置单元格格式 居中 边框 背景颜色 填充数据 一 jxl jxl jxl
  • 基础排序算法-快排的非递归和归并的具体实现

    目录 快排的非递归实现 归并排序 归并排序的非递归实现 内 外排序 上文 7大排序算法 堆排 快速排序 精解 luck 的博客 CSDN博客 堆排序 快速排序 快排的非递归实现 我们知道快排的实现效率很高 但是它还是有个弊端 就是我们本身栈