归并排序的主要操作:
- 分解待排序的n个元素的序列成各具n/2个元素的子序列;
- 使用归并排序递归地排序成两个子序列;
- 合并两个已排好序的子序列产生的已排好序的答案。
这完全符合分治模式在每层递归的三个步骤:
- 分解原问题为若干子问题,这些子问题是原问题的规模较小的实例;
- 解决这些子问题,递归的求解各子问题。子问题若规模足够小,则直接求解;
- 合并这些子问题的解成原问题的解。
下面使用归并排序的算法过程来感受分而治之的思想:
一、分解
排序的分解很简单,每次将原序列分解成两部分,再分别对两部分进行递归排序。
二、解决
递归基的确定:当排序的序列长度为1的时候,递归“开始回升”,因为长度为1的每个序列都是排好序的,不需要任何操作。
有了递归基依次调用确定就可以了。
三、合并
这是整个归并排序中最关键的操作。其思想很简单:
假设有两堆牌面向上的牌 和
,每堆都已排序,最小的牌在顶上。将两堆牌上较小的一张
移入到输出堆
中,一直重复这个步骤到
或
有一堆牌被移完为止。
过程图(来自百度)
『代码』
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int n;
int arr[200],b[200];
//对arr[l..mid]和arr[mid..r]进行归并
void merge(int l,int mid,int r ){
int i = l,j = mid+1;
int cnt = 0;
while(i <= mid || j <= r){
if(j > r || (i <= mid && arr[i] < arr[j]) )
//两个条件分别是 1.右边数组已经没有元素,左边还有元素 2.两边都有元素,并且左边的小于右边
b[cnt++] = arr[i++];
else
b[cnt++] = arr[j++];
}
for(int i = 0,k = l ; i < cnt; i++){
arr[k++] = b[i];
}
}
void Merge_sort(int l,int r) {
if(l >= r)
return ;
int mid = l + (r-l)/2;
Merge_sort(l,mid);//对左半部分进行递归
Merge_sort(mid+1,r); //对右半部份进行递归
merge(l,mid,r);
}
int main() {
scanf("%d",&n);
for(int i = 0 ; i < n; i++) {
scanf("%d",arr+i);
}
Merge_sort(0,n-1);
for(int i = 0 ; i < n; i++)
printf("%d ",arr[i]);
return 0;
}