Java 算法之求逆序对

逆序对问题:
给定一个数组,如:[8, 8, 3, 0, 6, 8, 9, 3],前大后小的数据即为一个逆序对,例如[8,3]、[3,0],求出这一数组中的所有逆序对。

思路:暴力法:设置双重循环,数量标志,比较两两之间的大小,符合逆序对标准则数量标志+1,复杂度O(n^2),本例中不考虑这个方法。


       合并排序:合并排序是将一个数组均分,递归调用对子数组进行排序,然后将子数组合并,主要使用分治思想。在本题中,示例数组可分为:

[8,8,3,0]

[6,8,9,3]

分别排序之后为 

A: [0,3,8,8]

B: [3,6,9,8] 

设置i=0,j=0分别标志A、B数组,在合并的过程中,比较A[i]、B[j],若A[i]>B[j] 则将B[i]放入原数组,i++,同理将数据依次放入。 


解决逆序对的最关键的就是合并这一步,假设A[i]>B[i]了,那么A[i]之后的所有数据,都是大于B[i]的,此时设置一个标识,用于记录数据,返回这个数据,递归,最后就可以得到所有逆序对了,这一方法的时间复杂度为O(nlogn),在数组数据非常多的时候可以明显感受到它的效率。


以下是它的实现代码,其实就是在合并排序的基础上,记录一下逆序对个数:

 public static void DecTest(int a[]) {
        // System.out.println(Arrays.toString(a));
        System.out.println("逆序对个数:"+mergeSort(a));

    }

    private static int mergeSort(int a[]) {
        int count = 0;
        int n = a.length;
        if (n <= 1) {
            return 0;
        }
        int b[] = new int[n / 2];
        System.arraycopy(a, 0, b, 0, n / 2);
        int c[];
        if (n % 2 == 0) {
            c = new int[n / 2];
            System.arraycopy(a, n / 2, c, 0, n / 2);
        } else {
            c = new int[n / 2 + 1];
            System.arraycopy(a, n / 2, c, 0, n / 2 + 1);
        }

        count += mergeSort(b);
        count += mergeSort(c);
        count += merge(b, c, a);
        return count;
    }

    private static int merge(int b[], int c[], int a[]) {
        int count = 0;      //标志
        int i = 0, j = 0, k = 0;
        int p = b.length, q = c.length;
        while (i < p && j < q) {
            if (b[i] <= c[j]) {
                a[k] = b[i];
                i++;
            } else {
                a[k] = c[j];
                j++;
                count += p - i;     //记录逆序对个数
            }
            k++;
        }
        if (i == p) {
            for (; j < q; j++, k++) {
                a[k] = c[j];
            }
        } else if (j == q) {
            for (; i < p; i++, k++) {
                a[k] = b[i];
            }
        }
        return count;
    }

    public static void main(String args[]) {

        int a[];
        int n = (int) (Math.random() * 1000000 + 1);
        a = new int[n];
        for (int i = 0; i < n; i++) {
            a[i] = (int) (Math.random() * 10);
        }
        long t = System.currentTimeMillis();
        DecTest(a);
        System.out.print("耗时:");
        System.out.println(System.currentTimeMillis() - t);

    }

本例中测试了百万个数据,以下是运行结果:


百万个数据119mm运行完毕,速度是十分可观的。

猜你喜欢

转载自blog.csdn.net/yforyoung/article/details/80468421