[算法入门]--十分钟弄懂归并排序

目录

一、问题(合并子功能的实现):

二、递归思路

三、全流程完整代码(递归函数+合并函数)

四、代码效果

五、总结与学习建议


一、问题(合并子功能的实现):

想要弄懂归并排序,首先要弄懂这样一个问题:

        1.给定两个有序数组,如何将这两个数组合并到一个数组中去,同时还要保证这个新的数组是有序的呢?(图片来自别的博主)

c598f55dfb804711b3dc0fed5f2dc84b.png


        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;	//打印排好序的数组 	 
}

四、代码效果

3a43af2051af40abb7a31006e701ed47.png


五、总结与学习建议

        1、归并排序,实际上是递归算法的一种实现,巧妙地利用了递归栈存函数,从而延时调用的性质。

        2、在学习归并之前(快排也是一个原理),最好是先弄懂递归调用的底层原理,然后能够独立设计出merge(合并部分)算法,这时再来看归并会通透很多。

  • 10
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值