逆序数个数 (Inversion Counting) - Merge and Sort

来源:互联网 发布:oracle的端口 编辑:程序博客网 时间:2024/05/11 03:59

普林斯顿的算法课质量很赞,这次作业中涉及到一个"逆序数"的知识,正好在之前学习mergesort时有一道课后提供的面试题与之相关,于是试着实现了。

原题链接:http://www.practice.geeksforgeeks.org/problem-page.php?pid=558

Given an array, find inversion count of array.

Inversion Count for an array indicates – how far (or close) the array is from being sorted. If array is already sorted then inversion count is 0. If array is sorted in reverse order that inversion count is the maximum. 
Formally speaking, two elements a[i] and a[j] form an inversion if a[i] > a[j] and i < j.
The sequence 2, 4, 1, 3, 5 has three inversions (2, 1), (4, 1), (4, 3).

Input:

The first line of input contains an integer T denoting the number of test cases.
The first line of each test case is N,N is the size of array.
The second line of each test case contains N input C[i].

Output:

Print the inversion count of array.

Constraints:

1 ≤ T ≤ 100
1 ≤ N ≤ 100
1 ≤ C ≤ 500

Example:

Input:
1
5
2 4 1 3 5

Output:
3

这道题的精髓在于对mergesort的理解,我们只需要对merge做一点微小的调整就能在O(NlgN)的时间内完成逆序数对数统计。

在一次merge中,数组的指标i是从前到后的index,j是从后到前的index,i和j不能越过数组一半的地方,这是由于数组是对半分好,前后两半排好序再merge,这是mergesort典型的模式。在设置好计数器为0后,在一次sort递归中我们需要做的只有两件事,第一,把计数器cnt加上sort前半部分、sort后半部分得到的逆序数加起来,第二,把cnt加上merge过程中找到的逆序数个数。

在一次merge中,每次从存储原来数组的辅助数组aux向要排好序的数组a添加元素时不外乎4个情况,

aux[i]不比aux[j]大,也就是目前前半部分最小值不比后半部分最小值大,把aux[i]添加到a[],不产生新的逆序对

aux[i]更大,这时从中间位置开始往左一直到aux[i],所有的数都比目前后半部分的最小值aux[j]更大,产生相应个数的逆序对

i已用尽(增加至超过一半)那么肯定是aux[j],

j已用尽(减少过半)那么肯定是aux[i],这两个情况下添加元素都不会导致新的逆序数,因为已经在第二种情况中被考虑过了

其余代码均于原本的mergesort如出一辙

import java.util.Scanner;class InversionCount {    private static int mergeAndCount(int[] a, int[] aux, int lo, int mid, int hi) {        for (int k = lo; k <= hi; k++)            aux[k] = a[k];        // assume that a[lo:mid] and a[mid+1:hi] are sorted        int cnt = 0; // counter of inversions        int i = lo, j = mid + 1;        for (int k = lo; k <= hi; k++) {            if (i > mid) { // i is exhausted                a[k] = aux[j++];            }            else if (j > hi) { // j is exhausted                a[k] = aux[i++];            }            else if (aux[j] < aux[i]) {                cnt += mid + 1 - i;                a[k] = aux[j++];            }            else {                a[k] = aux[i++];            }        }        return cnt;    }    private static int sortAndCount(int[] a, int[] aux, int lo, int hi) {        if (hi <= lo) return 0;        int mid = (lo + hi) / 2;        int cnt = 0;        cnt += sortAndCount(a, aux, lo, mid);        cnt += sortAndCount(a, aux, mid + 1, hi);        cnt += mergeAndCount(a, aux, lo, mid, hi);        return cnt;    }    public static int inversionCount(int[] a) {        int[] aux = new int[a.length];        return sortAndCount(a, aux, 0, a.length - 1);    }    public static void main (String[] args) {        Scanner scan = new Scanner(System.in);        int T = scan.nextInt();        while (T-- > 0) {            int N = scan.nextInt();            int[] a = new int[N];            for (int i = 0; i < N; i++) {                a[i] = scan.nextInt();            }            System.out.println(inversionCount(a));        }    }}



0 0
原创粉丝点击