news 2026/2/12 16:59:47

快排(非递归)和归并的实现

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
快排(非递归)和归并的实现
1、快速排序(非递归)
思路

这里实现的是深度优先遍历(DFS),我们使用栈来模拟实现

*所以我们利用栈的先进后出的特点,在模拟实现递归的时候先将右边的压栈,再将左边的压栈

每访问完一个数据就按照这个顺序将它的左右两边的栈压进去,然后访问栈顶

实现

//这里应该加一个指向栈的链接

voidQuickSortNoRec(int*arr,intleft,intright){//先将右边的数据存进去,读的时候就可先读左边的了stack st1;st1.StackPush(right);st1.StackPush(left);while(!st1.Isempty()){//读取左右区间intbegin=st1.StackPop();intend=st1.StackPop();//进行排序intkey=QuickPart1(arr,begin,end);//先将右边的数据存进去if(key+1<end){st1.StackPush(end);st1.StackPush(key+1);}if(begin<key-1){st1.StackPush(key-1);st1.StackPush(begin);}}}

我这里写的栈是不标准的,我将Pop和Top和到一起了

2、归并排序(递归)
思路

***归并排序很像我们之前做的那个将两个有序数组合成一个有序数组

他就像是每一次进入函数后先判断是不是有序的,然后多次分割,知道小块有序,才开始往回返,对父数组进行排序

***实际上像是一个后序遍历

![[Pasted image 20251223194437.png]]

![[归并排序.gif]]

实现
void_MergeSort_(int*arr,int*temp,intleft,intright){if(left==right)return;//这里我们为啥不先写一个判断条件来判断这个数组是不是有序的呢//因为我们在归并的时候无非就是将整个数组分为两半,再遍历一遍,我们这里就没必要脱裤子放屁了intmid=(left+right)/2;_MergeSort_(arr,temp,left,mid);_MergeSort_(arr,temp,mid+1,right);intbegin1=left,end1=mid;intbegin2=mid+1,end2=right;inti=0;while(begin1<=end1&&begin2<=end2){if(arr[begin1]<arr[begin2])temp[i++]=arr[begin1++];elsetemp[i++]=arr[begin2++];}while(begin1<=end1){temp[i++]=arr[begin1++];}while(begin2<=end2){temp[i++]=arr[begin2++];}memcpy((arr+left),temp,(right-left+1)*sizeof(int));}voidMergeSort(int*arr,intn){//我们这里在原数组里直接malloc数组,但是我们不直接使用这个函数递归//因为我们如果直接使用原数组递归的话,将会malloc很多次,这是很浪费的int*temp=(int*)malloc(sizeof(int)*n);_MergeSort_(arr,temp,0,n-1);free(temp);}
时间复杂度O(N*logN)每一层遍历一遍是遍历了N个,相当于是遍历了logN层
空间复杂度O(N)创建了N个大小的新空间(temp数组)
void_MergeSort_(int*arr,int*temp,intleft,intright){if(left==right)return;//这里我们为啥不先写一个判断条件来判断这个数组是不是有序的呢//因为我们在归并的时候无非就是将整个数组分为两半,再遍历一遍,我们这里就没必要脱裤子放屁了intmid=(left+right)/2;_MergeSort_(arr,temp,left,mid);_MergeSort_(arr,temp,mid+1,right);intbegin1=left,end1=mid;intbegin2=mid+1,end2=right;inti=left;while(begin1<=end1&&begin2<=end2){if(arr[begin1]<arr[begin2])temp[i++]=arr[begin1++];elsetemp[i++]=arr[begin2++];}while(begin1<=end1){temp[i++]=arr[begin1++];}while(begin2<=end2){temp[i++]=arr[begin2++];}memcpy((arr+left),(temp+left),(right-left+1)*sizeof(int));}

这是修改版,修改了i的起始位置,从left开始依次将数据填入temp,最后从arr+left的位置将数据拷贝回去

易踩的坑

![[Pasted image 20251223201214.png]]
我们在计算中间值的时候如果直接/2就会丢失数据(1),所以在相邻的偶数和偶数加一的情境下会出现死循环
![[Pasted image 20251223201657.png]]
这是就可以了

这里实际上是巧妙的避开了

3、归并排序(非递归)
思路

使用的是循环,思路是将递归的思路反过来,一次对两组数据进行排序

一次排两组
![[Pasted image 20251223213049.png]]

intgap=1;for(inti=0;i<n;i+=2*gap){intbegin1=i,end1=i+gap-1;intbegin2=i+gap,end2=i+2*gap-1;//......}

这里的外层for循环是用来找每一次排序的头指针的

这里的gap就是每一组的数据个数

这里又出bug了
在这里[[2025 12 23 bug]]

这是可以对2的次方倍进行排序的版本

voidMergeSortNoRec(int*arr,intn){int*temp=(int*)malloc(sizeof(int)*n);if(nullptr==temp){perror("malloc fail");return;}intgap=1;while(gap<n){for(inti=0;i<n;i+=2*gap)//气笑了,少加了个等于号{//这是个大坑,忘记做备份了intj=i;intbegin1=j,end1=j+gap-1;intbegin2=j+gap,end2=j+2*gap-1;printf("[%d,%d] [%d,%d] ",begin1,end1,begin2,end2);while(begin1<=end1&&begin2<=end2){if(arr[begin1]<arr[begin2])temp[j++]=arr[begin1++];elsetemp[j++]=arr[begin2++];}while(begin1<=end1){temp[j++]=arr[begin1++];}while(begin2<=end2){temp[j++]=arr[begin2++];}memcpy((arr+i),(temp+i),(end2-i+1)*sizeof(int));}gap*=2;printf("\n");}}

这个程序还是有问题的,我们来改一下

这里并没有对2的n次以外的数据做出考量,是会越界的

![[Pasted image 20251223223119.png]]
我们在第一次排的数据肯定是没问题的

这里就可以看出我们从第二次开始就开始越界了

![[Pasted image 20251223215713.png]]

分析得出:

后两种情况在这个循环中就不用归并了,直接跳到下一个(因为此时前面的已经归并过了

第一种情况还是要归并的,但是要将end2改为n-1(这里的n是闭区间)

if(begin2>=n)break;if(end2>=n)end2=n-1;

最终代码

voidMergeSortNoRec(int*arr,intn){int*temp=(int*)malloc(sizeof(int)*n);if(nullptr==temp){perror("malloc fail");return;}intgap=1;while(gap<n){for(inti=0;i<n;i+=2*gap)//气笑了,少加了个等于号{//这是个大坑,忘记做备份了intj=i;intbegin1=j,end1=j+gap-1;intbegin2=j+gap,end2=j+2*gap-1;if(begin2>=n)break;if(end2>=n)end2=n-1;printf("[%d,%d] [%d,%d] ",begin1,end1,begin2,end2);while(begin1<=end1&&begin2<=end2){if(arr[begin1]<arr[begin2])temp[j++]=arr[begin1++];elsetemp[j++]=arr[begin2++];}while(begin1<=end1){temp[j++]=arr[begin1++];}while(begin2<=end2){temp[j++]=arr[begin2++];}memcpy((arr+i),(temp+i),(end2-i+1)*sizeof(int));}gap*=2;printf("\n");}}
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/2/9 17:21:54

计算机毕业设计springboot快递仓库管理系统 基于Spring Boot的快递仓储信息化管理系统设计与实现 Spring Boot框架下的快递仓库智能管理系统开发

计算机毕业设计springboot快递仓库管理系统502yr9 &#xff08;配套有源码 程序 mysql数据库 论文&#xff09; 本套源码可以在文本联xi,先看具体系统功能演示视频领取&#xff0c;可分享源码参考。 随着电子商务的蓬勃发展&#xff0c;快递行业迎来了前所未有的增长机遇。然而…

作者头像 李华
网站建设 2026/2/8 18:26:33

计算机毕业设计springboot农村住宅房屋信息管理应用系统 基于Spring Boot的农村住宅信息管理系统设计与实现 Spring Boot框架下的农村房屋信息管理平台开发

计算机毕业设计springboot农村住宅房屋信息管理应用系统7t1319&#xff08;配套有源码 程序 mysql数据库 论文&#xff09; 本套源码可以在文本联xi,先看具体系统功能演示视频领取&#xff0c;可分享源码参考。随着信息技术的飞速发展&#xff0c;农村地区的信息化建设也在逐步…

作者头像 李华
网站建设 2026/2/7 8:31:24

CAS号:2221948-98-5,Bis-PEG21-NHS,化学结构与基本性质

CAS号&#xff1a;2221948-98-5&#xff0c;Bis-PEG21-NHS&#xff0c;化学结构与基本性质Bis-PEG21-NHS 是一种功能性化学交联试剂&#xff0c;其结构特点为两端带有 N-羟基琥珀酰亚胺&#xff08;NHS&#xff09;活化酯&#xff0c;中间由由 21 个乙二醇单元组成的聚乙二醇&a…

作者头像 李华
网站建设 2026/2/7 5:35:59

Bis-Mal-amido-PEG11,2962831-02-1,主要用途与应用领域

Bis-Mal-amido-PEG11&#xff0c;2962831-02-1&#xff0c;主要用途与应用领域Bis-Mal-amido-PEG11 是一种双功能交联试剂&#xff0c;其结构特点为两端带有马来酰亚胺&#xff08;maleimide&#xff09;官能团&#xff0c;中间通过 11 个乙二醇单元&#xff08;PEG11&#xff…

作者头像 李华
网站建设 2026/2/12 11:44:28

Open-AutoGLM部署避坑大全(90%新手都会犯的3大错误)

第一章&#xff1a;Open-AutoGLM部署概述Open-AutoGLM 是一个面向自动化任务的开源大语言模型推理框架&#xff0c;专为高效部署和低延迟响应设计。其核心优势在于支持多后端引擎&#xff08;如 vLLM、HuggingFace Transformers&#xff09;与动态批处理机制&#xff0c;适用于…

作者头像 李华
网站建设 2026/2/9 21:03:06

Open-AutoGLM性能提升300%的秘密,90%的开发者还不知道

第一章&#xff1a;Open-AutoGLM性能提升300%的秘密&#xff0c;90%的开发者还不知道许多开发者在使用 Open-AutoGLM 时仅停留在默认配置层面&#xff0c;却不知通过底层优化策略可实现高达300%的推理吞吐提升。其核心秘密在于模型执行图的动态剪枝与内存复用机制的深度协同。动…

作者头像 李华