逆序对数

2018/08/27 Offer 共 1344 字,约 4 分钟

逆序对数(inverse number) 给一列数a1, a2, . . . , an,求它的逆序对数,即有多少 个有序对i, j使得i < j但a[i] > a[j]。最简单的方法是二重循环,时间复杂度为O(n2)。 如果利用归并排序,我们可以轻松做到这一点。划分和递归求解部分完全一致,只有 合并部分需要修改:需要加上跨越两边的逆序对。考虑右表中的任意元素k。当k从右 表中删除时,左表剩下的所有元素x都与k构成(x; k)逆序对,因此应该在总答案中加上 左表此时的元素个数。

下面的程序中,左表初始为[l;m]而右表为[m + 1; r],两个表各有一个指针i和j, 因此在任意时刻左表为[i;m]而右表为[j; r]。如果右表为空或者两表都不空且a[i] · a[j]时,元素i从左表中移出;否则元素j从右表中移出,并累加左表元素个数m ¡ ¡j + 1。

这个好像是错的呀,while条件应该是||

void sort (int l , int r)
{
    if(l >= r) return 0;
    int m = ( l + r) > > 1;
    sort (l , m);
    sort (m+1 , r);
    // merge [l,m] and [m+1,r]
    int i = l , j = m+1 , c = l;
    while (i <= m && j <= r)
        if(j > r || ( i <= m && a[i] <= a[j ]) )
        t[c ++] = a[i ++];
        else
        { t[c ++] = a[j ++]; cnt += m– i + 1; }
    // copy back
    for(i = l; i <= r; i ++) a[i ] = t[i];
}

只要把cnt += m - i + 1;这一句去掉,就是标准的归并排序。时间复杂度也 是O(n log n),比普通的O(n2)算法快很多。需要注意的是附加空间t数组是­(n)的。


public class Solution {
    private int[] array=null;
    private int[] t = null;
    private int cnt=0;
    private void sort(int l,int r){
        if(l>=r) return;
        int m=(l+r)>>1;
        sort(l, m);
        sort(m + 1, r);
        int i=l,j=m+1,c=l;
        while (i<=m||j<=r){
            if(j>r||i<=m&&array[i]<=array[j]){
                t[c++]=array[i++];
            }else{
                t[c++]=array[j++];
                cnt=(cnt+m-i+1)%1000000007;
            }

        }
        for(i=l;i<=r;i++){
            array[i]=t[i];
        }
    }
    public int InversePairs(int [] array) {
        this.array=array;
        t=new int[array.length];
        sort(0,array.length-1);
        return cnt;
    }

    public static void main(String[] args) {
        int[] a=new int[]{1,2,3,4,5,6,7,0};
        int res = new Solution().InversePairs(a);
        System.out.println(res);
    }
}

Search

    Table of Contents