算法与数据结构实验6:逆序对(归并排序)
Description
在这个问题中,你需要分析一个对n个不同数排序的算法。该算法主要通过交换相邻数直到序列有序(升序)。比如:对输入序列
9 1 0 5 4
经过一系列交换后变成有序序列
0 1 4 5 9
你的任务是计算将序列变成有序最少需要经过多少次交换。
Input
输入包含多组测试数据。每组第一个是整数n<500,000,表示输入序列的长度,接下来是n行,每行有一个整数a[i](0≤a[i]≤999,999,999)。当n=0时表示结束。
Output
对每一组输入,输出该序列变成有序所需要交换的最少的次数。
Sample Input
5
9
1
0
5
4
3
1
2
3
0
Sample Output
6
0
解题思路:看到这个题按照题目意思“要通过交换相邻数直到序列有序(升序)”这是什么?冒泡排序?虽然冒泡排序就是基于这种思想,可是O(n^2)的时间
复杂度并不允许我这么搞,那该怎么办?
模拟原始做法:
对于数组:4 8 2 7 5 6 1 3
1 4 8 2 7 5 6 3------>6次
1 2 4 8 7 5 6 3------>2次
1 2 3 4 8 7 5 6------>5次
1 2 3 4 5 8 7 6------>2次
1 2 3 4 5 6 8 7------>2次
1 2 3 4 5 6 7 8------>1次
在模拟过程中,我们发现每次都是找到一个最小的然后移到最前面,但是除了这个最小的,其他数的相对次序并没有改变,所以我们可以将原始做法换一种表述方式:
找到最小的,统计它前面有多少个数比它大,然后加入结果,将这个最小的删去。如此反复。
这时我们就发现,其实原题就是求数列的逆序对的个数!归并排序,线段树,树状数组搞起来!!!!
我之前写过的关于逆序数求法的博客https://www.cnblogs.com/wkfvawl/p/9512861.html
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #define maxn 500010 5 #define ll long long int 6 using namespace std; 7 ll a[maxn]; 8 ll temp[maxn]; 9 ll sum; 10 void Merge(int l,int r,int m) 11 { 12 int i=l; 13 int j = m + 1; 14 int k = l; 15 while(i<=m&&j<=r) 16 { 17 if(a[i]>a[j]) 18 { 19 sum+=m-i+1;///剩下的没有进入临时空间的元素的个数 20 temp[k++]=a[j++]; 21 } 22 else 23 { 24 temp[k++]=a[i++]; 25 } 26 } 27 while(i<=m)///将剩余的元素存到数组中 28 { 29 temp[k++]=a[i++]; 30 } 31 while(j<=r) 32 { 33 temp[k++]=a[j++]; 34 } 35 for(i=l; i<=r; i++) 36 { 37 a[i]=temp[i]; 38 } 39 } 40 void mergesort(int l,int r) 41 { 42 if(l<r) 43 { 44 int m = (l + r) / 2; 45 mergesort(l,m);///左二分排序 46 mergesort(m+1,r);///右二分排序 47 Merge(l,r,m);///合并两个升序数组 48 } 49 } 50 int main() 51 { 52 int n,i; 53 while(scanf("%d",&n)!=EOF) 54 { 55 if(n==0) 56 { 57 break; 58 } 59 for(i=0; i<n; i++) 60 { 61 scanf("%lld",&a[i]); 62 } 63 sum=0; 64 mergesort(0,n-1); 65 printf("%lld ",sum); 66 } 67 return 0; 68 }