数组中的逆序对

题目描述

在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数P。并将P对1000000007取模的结果输出。 即输出P%1000000007

输入描述:

题目保证输入的数组中没有的相同的数字

数据范围:

对于%50的数据,size<=10^4

对于%75的数据,size<=10^5

对于%100的数据,size<=2*10^5

示例1

输入

1,2,3,4,5,6,7,0

输出

  7

题目链接

法一(借鉴):o(nlgn)。这题很容易想到的是o(n^2)的解决办法,就不说了,超时。利用归并分治的思想,将数组分成两部分,先分别计算左段和右段的逆序对(计算完成后就会变成有序的,这是归并排序中的分解),
再计算左段+右段的逆序对(这是归并排序中的归并,分别对两个数组从后往前进行遍历,如果第一个数组的末尾数值大于第二个的末尾数值,则此时可以有第二个数组长度那么多的逆序对存在,此时将大数移到临时数组中,
再继续进行遍历,重复进行)。代码如下(耗时700ms):
 1 public int InversePairs(int [] array) {
 2         int len = array.length;
 3         int[] tmp = new int[len];
 4         long res = InversePairsCore(array, 0, len - 1, tmp);
 5         return (int)(res % 1000000007);
 6     }
 7     public static long InversePairsCore(int[] array, int left, int right, int[] tmp) {
 8         long inver = 0;
 9         if(left < right) {
10             int mid = (left + right) / 2;
11             inver += InversePairsCore(array, left, mid, tmp);//找左半段的逆序对数目
12             inver += InversePairsCore(array, mid + 1, right, tmp);//找右半段的逆序对数目
13             inver += MergeArray(array, left, mid, right, tmp);//在找完左右半段逆序对以后两段数组有序,然后找两段之间的逆序对。最小的逆序段只有一个元素。
14         }
15         return inver;
16     }
17     public static long MergeArray(int[] array, int left, int mid, int right, int[] tmp) {
18         //i是左半段末尾下标,j是右半段末尾下标,k是临时数组末尾下标
19         int i = mid, j = right, k = 0;
20         long cnt = 0;
21         //设定两个指针ij分别指向两段有序数组的末尾元素,将大的那一个放入到临时数组中去。
22         while(i >= left && j > mid) {
23             if(array[i] > array[j]) {
24                 tmp[k++] = array[i--];//从临时数组的最后一个位置开始排序
25                 cnt += j - mid;//因为arry[mid+1...j...end]是有序的,如果arry[i]>arry[j],那么也大于arry[j]之前的元素,从a[mid+1...j]一共有j-(mid+1)+1=j-mid
26             }
27             else {
28                 tmp[k++] = array[j--];
29             }
30         }
31         //表示前半段数组中还有元素未放入临时数组
32         while(i >= left) {
33             tmp[k++] = array[i--];
34         }
35         while(j > mid) {
36             tmp[k++] = array[j--];
37         }
38         //将临时数组中的元素写回到原数组当中去。
39         for(i = 0; i < k; i++) {
40             array[right - i] = tmp[i];
41         }
42         return cnt;
43     }
View Code

法二(剑指offer标准):与上面的解法逻辑相同,只是代码方式不同。代码如下:

 1 public int InversePairs(int [] array) {
 2         int len = array.length;
 3         int[] tmp = new int[len];
 4         int res = InversePairsCore(array, 0, len - 1, tmp);
 5         return res;
 6     }
 7     private static int InversePairsCore(int[] array, int left, int right, int[] tmp) {
 8         if(left == right) {
 9             return 0;
10         }
11         int mid = (left + right) >> 1;
12         //统计左半段的逆序对
13         int leftCnt = InversePairsCore(array, left, mid, tmp) % 1000000007;
14         //统计右半段的逆序对
15         int rightCnt = InversePairsCore(array, mid + 1, right, tmp) % 1000000007;
16         //统计左半段+右半段的逆序对
17         int cnt = 0;
18         //标记左半段末尾下标
19         int i = mid;
20         //标记右半段末尾下标
21         int j = right;
22         //临时数组末尾下标
23         int k = right;
24         while(i >= left && j > mid) {
25             if(array[i] > array[j]) {
26                 cnt += j - mid;
27                 tmp[k--] = array[i--];
28                 if(cnt >= 1000000007) {
29                     cnt %= 1000000007;
30                 }
31             }
32             else {
33                 tmp[k--] = array[j--];
34             }
35         }
36         while(i >= left) {
37             tmp[k--] = array[i--];
38         }
39         while(j > mid) {
40             tmp[k--] = array[j--];
41         }
42         for(i = left; i <= right; i++) {
43             array[i] = tmp[i];
44         }
45         return (leftCnt + rightCnt + cnt) % 1000000007;
46     }
View Code
 
原文地址:https://www.cnblogs.com/cing/p/8690783.html