数据结构实验之排序五:归并求逆序数
Time Limit: 50MS
Memory Limit: 65536KB
Problem Description
对于数列a1,a2,a3…中的任意两个数ai,aj (i < j),如果ai > aj,那么我们就说这两个数构成了一个逆序对;在一个数列中逆序对的总数称之为逆序数,如数列 1 6 3 7 2 4 9中,(6,4)是一个逆序对,同样还有(3,2),(7,4),(6,2),(6,3)等等,你的任务是对给定的数列求出数列的逆序数。
Input
输入数据N(N <= 100000)表示数列中元素的个数,随后输入N个正整数,数字间以空格间隔。
Output
输出逆序数。
Example Input
10 10 9 8 7 6 5 4 3 2 1
Example Output
45#include <bits/stdc++.h> using namespace std; long long num=0; void merg(int a[],int l,int mid,int r) //归并 { int i=l,j=mid+1,k=0; //i为第一段开始,j为第二段开始 int *b=(int *)malloc((r-l+1)*sizeof(int)); //动态数组存储临时合并序列 while(i<=mid&&j<=r) //读取两段序列 { if(a[i]<=a[j]) //将两段中小的存入b[] b[k++]=a[i++]; else { b[k++]=a[j++]; num+=(mid-i+1); //若a[i]>a[j] 则之后的(mid-i+1)个数都大于a[j] } } while(i<=mid) b[k++]=a[i++]; //若第一段没完,并到b[] while(j<=r) b[k++]=a[j++]; //若第二段没完,并到b[] for(k=0,i=l; i<=r; i++,k++) //将b[]复制到a[] a[i]=b[k]; } void mergesort(int a[],int l,int r) //分解 { int mid; if(l<r) { mid=(l+r)/2; mergesort(a,l,mid); mergesort(a,mid+1,r); merg(a,l,mid,r); } } int a[100010]; int main() { int n,i; scanf("%d",&n); for(i=0; i<n; i++) scanf("%d",&a[i]); mergesort(a,0,n-1); printf("%lld\n",num); return 0; }
归并排序的基本思想
将待排序序列R[0...n-1]看成是n个长度为1的有序序列,将相邻的有序表成对归并,得到n/2个长度为2的有序表;
将这些有序序列再次归并,得到n/4个长度为4的有序序列;如此反复进行下去,最后得到一个长度为n的有序序列。
综上可知:
归并排序其实要做两件事:
(1)“分解”——将序列每次折半划分。
(2)“合并”——将划分后的序列段两两合并后排序。
我们先来考虑第二步,如何合并?
在每次合并过程中,都是对两个有序的序列段进行合并,然后排序。
这两个有序序列段分别为 R[low, mid] 和 R[mid+1, high]。
先将他们合并到一个局部的暂存数组R2中,带合并完成后再将R2复制回R中。
为了方便描述,我们称 R[low, mid] 第一段,R[mid+1, high] 为第二段。
每次从两个段中取出一个记录进行关键字的比较,将较小者放入R2中。最后将各段中余下的部分直接复制到R2中。
经过这样的过程,R2已经是一个有序的序列,再将其复制回R中,一次合并排序就完成