1.归并排序(同时求逆序数)
代码是POJ - 2299的代码,归并思想是二分的思想,从其正中间(与快排不一样)分开两端,将两边分别排序,然后归并,归并由于两边都是扫一遍就可以实现,所以是O(N),由于区间是不断二分,所以总共有O(logN)层,这样粗略地一分析,总的复杂度就是O(NlogN),有一个小小的细节,由于我们觉得从大到小排序,比较方便求逆序数,所以我们要使归并的两段都是从大到小排序的。这里有一个疑问,我能不能在填充tem数组的时候按从大到小填充,等到取出tem里面数据的时候从小到大往原数组里面放呢?其实是不行的,因为,如果从小到大放的话,那么这一段序列递归到上一层的时候,是假定从大到小排序,并使用基于从大到小的算法进行新的归并,也就是说前后要保持排序的一致性,不过如果不求逆序数,那当然按照从小到大排序也不错,不过就算我们按照从大到小排序了,反正知道长度n,倒着输出就好了
代码:
#include<iostream> using namespace std; typedef long long ll; const int num=500010; int a[num],tem[num]; ll sum; void Merge(int a[],int s,int mid,int e,int tem[]){ int p=0,pi=s,pj=mid+1; while(pi<=mid&&pj<=e){ if(a[pi]<a[pj]){ tem[p++]=a[pj++]; } else if(a[pi]>=a[pj]){ tem[p++]=a[pi++];sum+=(e-pj+1); } } while(pi<=mid)tem[p++]=a[pi++]; while(pj<=e)tem[p++]=a[pj++]; for(int i=0;i<e-s+1;i++){ a[s+i]=tem[i]; } } void MergeSort(int a[],int s,int e,int tem[]){ if(s<e){ int mid=s+(e-s)/2; MergeSort(a,s,mid,tem); MergeSort(a,mid+1,e,tem); Merge(a,s,mid,e,tem); } } int main(){ int n;cin>>n; while(n!=0){ sum=0; for(int i=0;i<n;i++){ cin>>a[i]; } MergeSort(a,0,n-1,tem); cout<<sum<<endl; cin>>n; } return 0; }2.快速排序