[算法入门]--十分钟弄懂归并排序
目录
一、问题(合并子功能的实现):
想要弄懂归并排序,首先要弄懂这样一个问题:
1.给定两个有序数组,如何将这两个数组合并到一个数组中去,同时还要保证这个新的数组是有序的呢?(图片来自别的博主)
2.根据这个过程,我们可以设计一个代码来进行合并的操作,具体思路如下:
1、使用双指针分别指向两个数组的开始
2、开辟一个新的辅助数组空间
3、将指针数值小的元素拷贝到辅助空间,该指针后移
4、必然会出现两种情况,即其中一个指针会先越界
5、判断指针的越界情况,将未越界的剩余元素全部移到辅助空间
我们可以发现,能这样做的首要前提是两个待处理数组都是有序的。我们每次都将最小的数字移入空间,那么最后剩下的一批肯定比已经移入的元素要大,可以自行画图移动来进行理解。
3.好,那我们现在来设计对应的代码来实现合并的操作:
void merge(vector<int>& arr, int L, int M, int R) { int i = L, j = M + 1; vector<int> tem; //定义临时数组存放排序好的数据 while (i <= M && j <= R) { if (arr[i] <= arr[j]) tem.push_back(arr[i++]); //先拷贝再自增 简化代码 else tem.push_back(arr[j++]); } while (i <= M) tem.push_back(arr[i++]); //如果是j越界了,i还么放完,剩下的全塞进去 //这两个while只会中其中一个 while (j <= R) tem.push_back(arr[j++]); int t = 0, p = L; //t指向tem,p指向原数组 while (p <= R) arr[p++] = tem[t++]; //覆盖元素组[L,R]区间上的元素 }
在这个函数中,我们的L,M,R规定了这两个和并数组的界限:
[L,M]是第一个数组的下标范围,[M+1,R]是第二个数组的下标范围
二、递归思路
递归设计:我们每次将数组同过传入递归传入的参数范围,将数组平分。
返回条件:当(L+R)传入的参数左右范围是同一个值说明此时已经不能再往下拆分,停止递归。
回溯:调用合并函数,将每次将下一层递归处理好的子数组进行合并(merge操作)。
三、全流程完整代码(递归函数+合并函数)
#include <vector> //用于动态数组 #include <iostream> #include <ctime> //用于生成随机数种子 #include <cstdlib> //用于rand() using namespace std; int randint(int a, int b) { return rand() % (b - a + 1) + a; } void initArr(vector<int>& arr, int N) { for (int i = 0; i < N; i++) arr.push_back(randint(1, 100)); //生成数值1到100范围的长度为N数组 } void merge(vector<int>& arr, int L, int M, int R) { int i = L, j = M + 1; vector<int> tem; //定义临时数组存放排序好的数据 while (i <= M && j <= R) { if (arr[i] <= arr[j]) tem.push_back(arr[i++]); //先拷贝再自增 简化代码 else tem.push_back(arr[j++]); } while (i <= M) tem.push_back(arr[i++]); //如果是j越界了,i还么放完,剩下的全塞进去 //这两个while只会中其中一个 while (j <= R) tem.push_back(arr[j++]); int t = 0, p = L; //t指向tem,p指向原数组 while (p <= R) arr[p++] = tem[t++]; //覆盖元素组[L,R]区间上的元素 } void mergeSort(vector<int>& arr, int L, int R) { if (L == R) return; //L==R说明递归到最底部,这时开始回溯 int mid = L + ((R - L) >> 1); //等同与L + (R - L)/2 但是位运算更帅且更快~ //这里注意+优先级大于>> //之前我写一直出bug,上网一搜位运算优先级,有个大哥也是在写归并排序出现了这个同样的问题,说来真巧~ // https://blog.csdn.net/hhhenjoy/article/details/115479935参见这篇blog mergeSort(arr, L, mid); //递归左边 mergeSort(arr, mid + 1, R); //递归右边 //先递归到最底部再回溯然后merge归并起来 merge(arr, L, mid, R); //归并下一层的有序数组 } int main() { srand(time(0)); //生成随机数种子来随机生成数组 vector<int> arr; initArr(arr, 50); //调用初始化数组函数 vector<int>::iterator it = arr.begin(); //建立一个迭代器 cout << "未排序的数组:" << endl; for (; it < arr.end(); it++) cout << *it << ' ';cout << endl; //打印没有排序的数组 mergeSort(arr, 0, arr.size() - 1); //归并排序范围为整个数组 cout << "排好序的数组:" << endl; it = arr.begin(); //初始化迭代器让他回到第一个位置 for (; it < arr.end(); it++) cout << *it << ' ';cout << endl; //打印排好序的数组 }
四、代码效果
五、总结与学习建议
1、归并排序,实际上是递归算法的一种实现,巧妙地利用了递归栈存函数,从而延时调用的性质。
2、在学习归并之前(快排也是一个原理),最好是先弄懂递归调用的底层原理,然后能够独立设计出merge(合并部分)算法,这时再来看归并会通透很多。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧