算法描述
运用了分治的思想,将一个数组分成几乎相等的两份,分别将两段中第一个最小的数拿出来放在一个临时数组中,直到全部取完。因为是递归的,所以每一段的数列都是排序好的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| void merge_sort(ll *A,ll *B,int x,int y) { if(y-x<=1)return; int mid=x+(y-x)/2; int p=x,q=mid,i=x; merge_sort(A,B,x,mid); merge_sort(A,B,mid,y); while(p<mid or q<y) { if(q>=y or (p<mid and A[p]<=A[q]))B[i++]=A[p++]; else B[i++]=A[q++],ans+=mid-p; } for(int i=x;i<y;i++)A[i]=B[i]; }
|
时间复杂度为 \(O(n\log n)\)。
例题
可以利用分治的过程处理一些问题,比如求数组的逆序对。
我们发现每次递归时左边的还没有入列的数都是大于右边的数的,此时统计左侧比右侧该数大的数的个数,即可统计到所有的逆序对。
洛谷P1908
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| #include<iostream> #include<cstdio> using namespace std; typedef long long ll; template <typename T> inline T read() { int w=0;T x=0;char c=getchar(); while(!isdigit(c))w|=c=='-',c=getchar(); while(isdigit(c))x=(x<<3)+(x<<1)+(c^48),c=getchar(); return w?-x:x; } const int maxn=5e5+10; int n; ll A[maxn],B[maxn],ans;
void merge_sort(ll *A,ll *B,int x,int y) { if(y-x<=1)return; int mid=x+(y-x)/2; int p=x,q=mid,i=x; merge_sort(A,B,x,mid); merge_sort(A,B,mid,y); while(p<mid or q<y) { if(q>=y or (p<mid and A[p]<=A[q]))B[i++]=A[p++]; else B[i++]=A[q++],ans+=mid-p; } for(int i=x;i<y;i++)A[i]=B[i]; }
int main() { n=read<int>(); for(int i=1;i<=n;i++)A[i]=read<ll>(); merge_sort(A,B,1,n+1); printf("%lld\n",ans); return 0; }
|