求数组中逆序对的数量,时间复杂度 O(n * lg n)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/afei__/article/details/82951905

题目

这是《算法导论 第3版》第二章的第 4 个思考题。

原题为:

假设 A[ 1…n ] 是一个有 n 个不同数的数组。若 i < j 且 A[ i ] > A [ j ] ,则对偶 (i, j) 称为 A 的一个逆序对。

给出一个确定在 n 个元素的任何排列中逆序对数量的算法,时间复杂度 O(n * lg n)。

(提示:修改归并排序)

 

分析

首先有一种直观的解法,即依次遍历数组 A,找出元素 A[ i ] 后面存在几个小于它的数,统计总次数即可。然而这种解法虽然简单,但时间复杂度为 O(n^2),代码也很简单这里不作示例了。下面说收怎么使用归并排序的方法计算逆序对的数量。

在归并阶段存在多少逆序对时,总有:逆序对数量 = 左半部分逆序对 + 右半部分逆序对 + 合并部分逆序对

关键是怎么得到合并部分的逆序对数量,合并时,若 i 为左半数组的遍历索引,j 为右半数组的遍历索引,一旦发现 A [ i ] 比 A [ j ] 大,那么会有 ( mid – i ) 个逆序对产生。因为 A [ i + 1 ] , A [ i + 2 ] … A [ mid - 1 ] 都比 A [ j ] 大。其中 mid 是右半数组开始的索引。

 

解答

public class Main {
 
    public static void main(String[] args) {
        int[] arr = new int[] { 2, 3, 8, 6, 1 };
        int inversionCount = mergeSort(arr, 0, arr.length);
        printArray(arr);
        System.out.println("inversionCount: " + inversionCount);
    }
 
    public static int mergeSort(int[] arr, int start, int end) {
        int inversionCount = 0;
        int length = end - start;
        if (length > 1) { // 长度大于1才需要排序
            int mid = (start + end) / 2;
            inversionCount += mergeSort(arr, start, mid); // sort left
            inversionCount += mergeSort(arr, mid, end); // sort right
            inversionCount += merge(arr, start, mid, end);
        }
        return inversionCount;
    }
 
    public static int merge(int[] arr, int start, int mid, int end) {
        // check input
        if (arr == null || start < 0 || end > arr.length) {
            return 0;
        }
        int[] temp = new int[end - start];
        int inversionCount = 0;
        int i = start; // 左半部分索引
        int j = mid; // 右半部分索引
        int k = 0; // temp数组索引
        while (i < mid && j < end) {
            if (arr[i] <= arr[j]) {
                temp[k++] = arr[i++];
            } else {
                temp[k++] = arr[j++];
                // 一旦 arr[i] > arr[j],就会有 (mid - i) 个逆序对产生
                inversionCount += mid - i;
            }
        }
        if (i != mid) {
            System.arraycopy(arr, i, temp, k, mid - i);
        } 
        if (j != end){
            System.arraycopy(arr, j, temp, k, end - j);
        }
        System.arraycopy(temp, 0, arr, start, temp.length);
        return inversionCount;
    }
 
    public static void printArray(int[] arr) {
        for (int i = 0; i < arr.length; i++) {
            System.out.print(arr[i] + " ");
        }
        System.out.println();
    }
 
}

运行结果:

1 2 3 6 8 
inversionCount: 5

原数组为 [ 2, 3, 8, 6, 1 ],其逆序对有 (2, 1)、 (3, 1)、 (8, 1)、 (6, 1)、 (8, 6) 共 5 对。

符合我们运行结果。 

猜你喜欢

转载自blog.csdn.net/afei__/article/details/82951905
今日推荐