逆序对和逆序数

   2011-11-27 20:10:14

  算法导论第2章里就提出逆序对的思想(设A[1..n]是一个包含n个不同数的数组,如果在i<j的情况下,有A[i]>A[j],则(i,j)就称为A中的一个逆序对),这里要强调n个数是不同的。我们的目的是要确定n个元素的任何排列中逆序对的数目。

  冒泡排序本质上体现逆序对的思想。不过由于在时间数量级上的巨大差异,用冒泡排序来计数逆序对的对数会随着n的增大而增大,我们可以估算出最坏情况下逆序对的数目是n*(n-1)/2。故从性能上考虑,冒泡排序不适合大数据量得求解。

  其实,合并排序就是一种天然地求解逆序对对数的算法。若最初不考虑是否想到用合并排序的思想。我们假设一个数组A[1..n],利用分治的方法把它分成两部分,称为左数组A1和右数组A2,那么A[1..n]的逆序对对数=左数组A1的逆序对对数+右数组A2的逆序对对数+合并A1、A2时A1数组里比A2数组里的数大的数目。

  这里我们例举PKU-2299题为例,该题典型在于它充分利用合并排序求逆序对数目的思想。代码如下:

 1 #include<stdio.h>
2 #include<stdlib.h>
3 #define N 500005
4
5 int a[N];
6 int b[N];
7 //why using longlong, as count may be larger than n * (n - 1)/2
8 long long count;

9
10 void merge(int l, int mid, int r)
11 {
12 int i = 0, j = l, k = mid + 1;
13 while (j <= mid && k <= r){
14 if (a[j] > a[k]){
15 printf("(%d %d)\n", a[k], a[j]);
16 b[i++] = a[k++];
17 count += mid - j + 1;//因为此时左数组有mid - j + 1个数比右数组大
18 }else{

19 b[i++] = a[j++];
20 }
21 }
22 while (j <= mid)
23 b[i++] = a[j++];
24 while (k <= r)
25 b[i++] = a[k++];
26 for (j = 0; j < i; j++)
27 a[l + j] = b[j];
28 }
29 void mergeSort(int l, int r)
30 {
31 if (l < r){
32 int mid = (l + r)/2;
33 mergeSort(l, mid);
34 mergeSort(mid + 1, r);
35 merge(l, mid, r);
36 }
37 }
38
39 int main(void)
40 {
41 int n, i;
42 while ((scanf("%d", &n) != EOF) && n != 0){
43 count = 0;
44 memset(a, 0, sizeof(a));
45 memset(b, 0, sizeof(b));
46 for (i = 0; i < n; i++){
47 scanf("%d", &a[i]);
48 }
49 mergeSort(0, n - 1);
50 printf("%lld\n", count);
51 }
52 }



posted @ 2011-11-27 20:12  liftBug  阅读(1108)  评论(0编辑  收藏  举报