当前位置: 首页 > news >正文

归并排序(非递归)

前面说完了递归,虽然递归容易理解,思路清晰,但是造成了时间和空间上的浪费。那为了追求效率,就可以将递归改为迭代,进一步提高代码性能。直接上代码:
#include <stdio.h>
#include <malloc.h>

#define MAXSIZE 10000
#define N 9
typedef struct {
	int r[MAXSIZE + 1];
	int length;
}SqList;

/*将SR[i..m]和SR[m+1..n]归并排序为TR[i..n]*/ 
void Merge(int SR[], int TR[], int i, int m, int n) {
	int j;
	int k;
	int l;
	
	for(j = m+1, k = i; i <= m && j <= n; k++) {	//将SR中的数据有小到大写入TR 
		if(SR[i] < SR[j]) {
			TR[k] = SR[i++];
		} else {
			TR[k] = SR[j++];
		}
	}
	
	if(i <= m) {	//将剩余的SR[i..m]复制到TR 
		for(l = 0; l <= m-i; l++) {
			TR[k+l] = SR[i+l];
		}
	}
	
	if(j <= n) {	//将剩余的SR[j..n]复制到TR 
		for(l = 0; l <= n-j; l++) {
			TR[k+l] = SR[j+l];
		}
	}
}

void MergePass(int SR[], int TR[], int s, int n) {
	int i = 1;
	int j;
	while(i <= n-2*s+1) {
		Merge(SR, TR, i, i+s-1, i+2*s-1);	//两两归并
		i = i+2*s; 
	}
	if(i < n-s+1) {		//归并最后的两个序列 
		Merge(SR, TR, i, i+s-1, n);
	} else {		//若最后只剩下单个子序列 
		for(j = i; j <= n; j++) {
			TR[j] = SR[j];
		}
	}
} 

void MergeSort(SqList *L) {
	int* TR = (int*)malloc(L->length * sizeof(int));
	
	int k = 1;
	while(k < L->length) {
		MergePass(L->r, TR, k, L->length);
		k = 2*k;						//子序列长度加倍 
		MergePass(TR, L->r, k, L->length);
		k = 2*k;						//子序列长度加倍
	}
} 

/*打印顺序表*/
void print(SqList L) {
	int i;
	for(i = 1; i < L.length; i++) {
		printf("%d,", L.r[i]);
	}
	printf("%d\n", L.r[i]);
}

int main() {
	int i;
	int d[N]={50,10,90,30,70,40,80,60,20};
	
	SqList L;
	for(i = 0; i < N; i++) {
		L.r[i+1] = d[i];
	}
	L.length = N;
	
	printf("排序前:\n");
    print(L);
    printf("归并排序(非递归):\n");
	MergeSort(&L);
	print(L);
	
	return 0;
}

MergeSort():

MergeSort()函数中while循环的目的是不断地归并有序序列,k值是由1->2->4->8->16变化的。k=1时,MergePass()函数是将原来无序的数组两两归并入TR。k=2时,MergePass()函数将TR中已经两两归并的有序序列再次归并回数组L.r中,k=4至k=16重复此过程。

由这段代码可以看出,非递归的方法更加清楚直接,从最小的直至归并完成。不像递归算法,需要先拆分递归,再归并退出递归。

MergePass()函数:

此函数中while循环就是为了两两归并。s=1,n-2*s+1=8,此时我们会想到,明明有9个元素呀,为什么是1-8?因为我们的目的是两两归并,最终9条数据肯定会剩下来,无法归并。一直循环,直至第七个和第八个数据完成归并。下来,就需要处理剩下的元素了,直接加入到TR数组末尾。当MergeSort()函数再次调用MergePass()函数时,s=2,此时i就是以4为增量进行循环了,就是将两个有两个数据的有序序列合并成四个数据的有序序列,最终再将剩下的第九个数据插入到TR数组末尾。

复杂度分析:

非递归的方法避免了递归时深度为log₂n的栈空间,空间只是用到申请归并临时用的TR数组上,因此空间复杂度为O(n)。时间复杂度也有所提升,因此,应尽量考虑非递归算法

相关文章:

  • Java中final、finally和finalize的区别
  • 归并排序(自然分组)
  • STM32概述
  • 软件工程学科的诞生
  • 软件开发过程模型综述
  • 算法设计题--数组元素换位
  • 数组换位问题-比较容易理解的解法
  • C语言形参与实参的复习
  • MySQL入门学习总结
  • 计算机存储体系简介
  • Linux基本命令个人总结
  • Makefile的简单写法总结
  • 不带头节点链表--用面向对象的思想实现
  • 经典算法--约瑟夫环问题的三种解法
  • 堆栈的实现与应用--工具
  • 深入了解以太坊
  • 【前端学习】-粗谈选择器
  • 【跃迁之路】【444天】程序员高效学习方法论探索系列(实验阶段201-2018.04.25)...
  • 2017 前端面试准备 - 收藏集 - 掘金
  • Git学习与使用心得(1)—— 初始化
  • hadoop入门学习教程--DKHadoop完整安装步骤
  • iOS编译提示和导航提示
  • leetcode386. Lexicographical Numbers
  • Linux快速复制或删除大量小文件
  • PAT A1092
  • PV统计优化设计
  • Spring Cloud Alibaba迁移指南(一):一行代码从 Hystrix 迁移到 Sentinel
  • 高度不固定时垂直居中
  • 排序算法学习笔记
  • 前端面试之闭包
  • 如何利用MongoDB打造TOP榜小程序
  • 数据库写操作弃用“SELECT ... FOR UPDATE”解决方案
  • ​软考-高级-信息系统项目管理师教程 第四版【第23章-组织通用管理-思维导图】​
  • # .NET Framework中使用命名管道进行进程间通信
  • #我与Java虚拟机的故事#连载03:面试过的百度,滴滴,快手都问了这些问题
  • (8)Linux使用C语言读取proc/stat等cpu使用数据
  • (二)什么是Vite——Vite 和 Webpack 区别(冷启动)
  • (附源码)springboot猪场管理系统 毕业设计 160901
  • (附源码)计算机毕业设计SSM智能化管理的仓库管理
  • (三)centos7案例实战—vmware虚拟机硬盘挂载与卸载
  • (四)Linux Shell编程——输入输出重定向
  • .NET DataGridView数据绑定说明
  • .NET/C# 利用 Walterlv.WeakEvents 高性能地定义和使用弱事件
  • .NET/C# 中设置当发生某个特定异常时进入断点(不借助 Visual Studio 的纯代码实现)
  • [ C++ ] STL priority_queue(优先级队列)使用及其底层模拟实现,容器适配器,deque(双端队列)原理了解
  • []FET-430SIM508 研究日志 11.3.31
  • []使用 Tortoise SVN 创建 Externals 外部引用目录
  • [AI]文心一言出圈的同时,NLP处理下的ChatGPT-4.5最新资讯
  • [ai笔记9] openAI Sora技术文档引用文献汇总
  • [Android]使用Git将项目提交到GitHub
  • [C#]winform部署yolov5-onnx模型
  • [C/C++]数据结构 栈和队列()
  • [CDOJ 1343] 卿学姐失恋了
  • [Docker]十.Docker Swarm讲解
  • [go 反射] 进阶