归并排序
排序的概念
排序是将一批(组)任意次序的记录重新排列成按关键字有序的记录序列的过程。
注意:首先需要打破僵化思维,排序的关键字可以指定多个,’有序‘的概念也是可以指定的,不只有递增或递减,排序的元素也不只有数字,任何可区分的元素都可以进行排序,因此排序算法会频繁的出现在各类算法中,不同的排序算法带来的思想常常是解决不同问题的突破点。
关键概念
归并:是指将两个或两个以上的有序序列合并成一个有序序列。
这里考虑一下一次归并的时间复杂度:
朴素考虑:如果我们将两个有序序列拼接在一起,然后得到的新序列进行一次排序,根据不同的排序算法时间复杂度可能是O(nlogn)~O(n^2)
当然这不可能是实现归并排序的基础,我们要提高效率就需要充分利用两个序列有序的条件。
高效考虑:在两个序列中找到一个最小的时间复杂度度是O(1),因为这个最小不是a序列的最小就是b序列的最小
继续思考:我们一直进行以上操作,就可以用O(n)的时间复杂度进行一次归并,归并算法就是以这个高效的操作为基础的。
算法思路
我们了解了归并,如何利用归并排序呢?
很自然我们想到了,对许多有序序列进行归并,就可以得到一个有序的总序列。但最初的有序是什么呢?
我们可以认为所有的排序元素就是“有序的”,因为一个元素没有逆序对。
那有很多不同的归并策略,哪种最高效呢?
我们首先考虑一个极端情况,我们有n个元素,那么就有了n个长度1的有序序列,一个一个的归并将所有的序列连接起来,那么就需要对n个元素进行n次取最小,时间复杂度是O(n2)。
归并的效率是O(n),那么总复杂度就取决于归并的次数,如何使效率最小呢?二分
这样只用进行logn次归并,由此得到了算法思路。
代码实现
递归实现,非递归也能实现,可以自己试试
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
void merge(int a[],int l,int r,int mid)//一次归并,通过左右中三个点划分数组
{
int aux[r-l+1],i,j,k;
for(k=l;k<=r;k++)
aux[k-l]=a[k];//记录原来的序列,因为新序列会覆盖掉a
i=l;//i指向第一个序列的第一项
j=mid+1;//j指向第二个序列的第一项
for(k=l;k<=r;k++){
if(i>mid){//第一个序列没了,只能是第二个序列最小
a[k]=aux[j-l];
j++;
}
else if(j>r){//第二个序列没了
a[k]=aux[i-l];
i++;
}
else if(aux[i-l]>aux[j-l]){//第一个序列头比第二个序列头小
a[k]=aux[j-l];
j++;
}
else{//反之
a[k]=aux[i-l];
i++;
}
}
}
void merge_sort(int a[],int l,int r){//函数最终效果是通过归并排序使l到r这段序列有序
if(l>=r)
return ;//元素本身有序
int mid=(l+r)/2;
//在进行归并前需要两端序列有序,所以需要先对两段序列排序
merge_sort(a,l,mid);
merge_sort(a,mid+1,r);
merge(a,l,r,mid); //归并
}
int main(){
int a[105],n,i;
scanf("%d",&n);//序列元素个数
for(i=0;i<n;i++)
scanf("%d",&a[i]);//序列
merge_sort(a,0,n-1);//开始归并
for(i=0;i<n;i++)
printf("%d ",a[i]);
return 0;
}