排序(9)——归并排序非递归版

本文介绍了如何使用栈模拟非递归方式实现归并排序,特别关注了在处理大量数据的外部排序场景中遇到的越界问题以及时间复杂度分析。作者通过斐波那契数列的思想指导合并过程,展示了归并排序在内存和硬盘之间数据排序的应用。
摘要由CSDN通过智能技术生成

目录

基本思路

问题

 代码实现

时间复杂度

归并排序——外排序


前面快排的非递归版我们用了栈来实现,这里我们还能用栈实现吗?

快速排序可以用栈

  • 快速排序的递归:前序递归
  • 快速排序的非递归:借用栈
  • 快排没有最后回归的过程,所以栈空就意味着结束。

而归并排序不一样。

  • 归并排序的递归:后序递归
  • 归并排序的非递归:直接分解
  • 归并排序回归需要处理,而借助栈模拟非递归,根本没有回归这个过程。

借助栈模拟非递归,比较适合前序,后序需要复杂处理是不适合的。

基本思路

  • 利用斐波那契数列的思想
  • 顺着走
  • 一一合并,二二合并,四四合并... 
  • 每小组合并之后拷贝回原数组(❗不要在每大组合并完再去拷贝❗)
  • 设置一个gap表示归并每组的数据个数
  • 直接实行合并的过程。往回走

每一组

  • 设置【begin1,end1】和【begin2,end2】,控制两个区间的序列的合并
  • 注意第一组begin1=i end1=i+gap-1  gap表示归并每组的数据个数,最后-1是因为end1表示的是下标
  • 第二组是begin2=i+gap end2=i+2*gap-1
  • i+=2*gap

整体

  • 一一归,二二归,四四归通过一个循环进行控制,gap=gap*2
  • 结束条件:gap< n

问题

最后会有越界?   

  • 合并的组数不一定都是2的次方倍,越界问题。(可以尝试打印区间来查看越界问题)
  • 越界问题存在三种情况(begin1=i<n不会越界)
  1. end1>=n(后面两个肯定越界,第一序列存在数,第二序列不存在数)break;
  2. begin2>=n(end2肯定越界,第二序列不存在数)break;
  3. end2>=n(可能第二序列区间还存在数)要修改处理为end2 = n - 1;

 代码实现

void MergeSortNonR(int* a, int n)
{
	int* tmp = (int*)malloc(sizeof(int) * n);
	if (tmp == NULL)
	{
		perror("malloc fail");
		return;
	}

	int gap = 1;
	while (gap < n)
	{
		//printf("gap:%2d->", gap);
		for (size_t i = 0; i < n; i += 2 * gap)
		{
			int begin1 = i, end1 = i + gap - 1;
			int begin2 = i + gap, end2 = i + 2 * gap - 1;
			// [begin1, end1][begin2, end2] 归并
			//printf("[%2d,%2d][%2d, %2d] ", begin1, end1, begin2, end2);

			// 边界的处理
			if (end1 >= n || begin2 >= n)
			{
				break;
			}

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

			//printf("[%2d,%2d][%2d, %2d] ", begin1, end1, begin2, end2);

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

			while (begin1 <= end1)
			{
				tmp[j++] = a[begin1++];
			}

			while (begin2 <= end2)
			{
				tmp[j++] = a[begin2++];
			}

			memcpy(a + i, tmp + i, sizeof(int) * (end2 - i + 1));
		}

		//printf("\n");

		gap *= 2;
	}


	free(tmp);
}

时间复杂度

时间复杂度:O(N*logN) 

归并排序——外排序

  • 内部排序:数据元素全部放在内存中的排序。
  • 外部排序:数据元素太多不能同时放在内存中,根据排序过程的要求不能在内外存之间移动数据的排序。(磁盘)
  • 我们前面学过的排序都是内排序
  • 归并排序既是内排序,也是外排序。
  • 为什么数据要放到硬盘里面?
  • 内存和硬盘的区别?
  1. 内存容量小,速度快,价格贵,带电存储。
  2. 硬盘容量大,速度慢,价格便宜,不带电存储,永久存储
  • 大量的数据在文件中保存,如果用归并排序使其有序?

外排序视频

 

  • 7
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
MPI(Message Passing Interface)是一种用于并行计算的编程模型和库。归并排序是一种经典的排序算法,适合并行计算。 在MPI中,可以通过发送和接收消息来实现进程间的通信。下面是一个基于MPI的归并排序的伪代码: ```python def parallel_merge_sort(data): # 获取进程总数和当前进程编号 size = MPI.COMM_WORLD.Get_size() rank = MPI.COMM_WORLD.Get_rank() # 计算每个进程要处理的数据量 chunk_size = len(data) // size remainder = len(data) % size # 将数据分发到各个进程 if rank == 0: for i in range(size): if i < remainder: chunk = data[i * (chunk_size + 1):(i + 1) * (chunk_size + 1)] else: chunk = data[remainder + i * chunk_size:remainder + (i + 1) * chunk_size] MPI.COMM_WORLD.send(chunk, dest=i, tag=0) # 接收数据 chunk = MPI.COMM_WORLD.recv(source=0, tag=0) # 对本地数据进行排序 chunk.sort() # 归并排序 for step in range(size): # 计算要交换数据的进程编号 partner = (rank + step) % size # 发送和接收数据 sendbuf = chunk recvbuf = MPI.COMM_WORLD.recv(source=partner, tag=step) if rank < partner: sendtag = step recvtag = step + size else: sendtag = step + size recvtag = step MPI.COMM_WORLD.send(sendbuf, dest=partner, tag=sendtag) chunk = merge(chunk, recvbuf) # 将排序好的数据返回 if rank == 0: result = [] for i in range(size): chunk = MPI.COMM_WORLD.recv(source=i, tag=size) result.extend(chunk) return result else: MPI.COMM_WORLD.send(chunk, dest=0, tag=size) ``` 在这个算法中,首先将原始数据分发到各个进程,然后每个进程对本地数据进行排序,接着对每个步骤进行归并排序,并且使用MPI的send和recv函数进行交换数据。最后将排序好的数据返回到主进程。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值