归并排序总结

归并排序

  归并排序(Merge Sort)是利用归并的思想实现的排序方法,该算法采用经典的分治(divide-and-conquer)策略将问题分成一些小的问题然后递归求解,即分而治之。
  归并排序每次会将输入序列分割成左右两部分,再分别对这两部分进行归并排序。如此一来在每次递归的时候,问题规模都会变为原来的二分之一。最后将完成排序的两部分进行合并为一个有序序列。
  我笔试的时候递归基写歪了QAQ。递归函数中的递归基忘记写了,写到合并函数里面去了。这次面试怕是凉凉,归并排序都能写歪来,淦。
  自己写完归并排序模板可以到LeetCode找一个排序题测试一下。飞机票:https://leetcode-cn.com/problems/sort-an-array/submissions/

//首先我们需要一个排序入口
void merge_sort(vector<int>& arr){
	merge(arr, 0, arr.size());
}

//由于每次我们都会对序列进行归并排序,如果传引用,就需要我们每次调用都开辟新的数组。
//为了减少传参的花销,我们使用下标来表示这当前序列的长度
void merge(vector<int>& arr, int left, int right){
	//递归基:当left大于right-1时结束
	//此时序列只有一个元素 [l, l+1) 或没有元素 [l, l)
	if(left >= right-1)
		return;
	//将当前序列分为两部分,分别对这两部分进行递归排序
	int mid = (left+right)>>1;	//求出分割点
	merge(arr, left, mid);	//对左序列[l, mid)进行归并排序。结果:左序列有序。
	merge(arr, mid, right);	//对右序列[mid, r)进行归并排序。结果:右序列有序。
	merge_core(arr, left, mid, right);	//合并s两个有序序列,实现排序。
}

//那么问题来了:merge_core如何合并两个有序序列呢?
//合并左序列[left, mid) 和 [mid, right) 均为左闭右开区间
void merge_core(vector<int>& arr, int left, int mid, int right){
	//因为合并两个有序序列很难进行原地合并,因此我们用一个新数组辅助
	//左序列[l, mid)和右序列[mid, right)总共有right-left+1个元素
	vector<int> tmp(right-left+1, 0);	
	int idx = 0;	//下标指向中间数组tmp
	int l = left;	//下标指向左序列[left, mid)
	int r = mid;	//下标指向右序列[mid, right)
	//当左右序列都没被取完时
	while(l<mid && r<right){
		//将左序列和右序列中指向的元素较小值取出放入中间数组tmp,并且相应的下标移动。
		tmp[idx++] = arr[l]<=arr[r] ? arr[l++] : arr[r++];
	}
	//此时某个序列已经被全部放入中间数组,剩下的序列可以直接放在tmp后面
	while(l<mid){
		tmp[idx++] = arr[l++];
	}
	while(r<right){
		tmp[idx++] = arr[r++];
	}
	//此时两个序列已经合并完成排序了,存放在中间数组tmp中,我们需要将它放回原数组
	for(int i=left; i<right; i++){
		arr[i] = tmp[i-left];
	}
}
  • 需要注意的点
    • 递归分割中的递归基不要遗漏。
    • 递归函数中是先对子序列归并,再合并两个有序序列。
    • 递归分割时把特殊情况(空序列,单个序列)排除了,合并时没有特殊情况。
posted @ 2019-12-10 21:05  azhao_blogs  阅读(454)  评论(0编辑  收藏  举报