面试题51:数组中的逆序对

题目:

在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数。例如,在数组{7,5,6,4}中,一共存在5个逆序对,分别是(7,6)、(7,5)、(7,4)、(6,4)和(5.4)。

分析:

第一反应是顺序扫描整个数组,每扫描到一个数字,比较这个数字和它后面的数字的大小,如果后面的数字比它小,就构成一个逆序对。这种方法时间复杂度是O(n²)。

逆序数的计算可以结合归并排序来统计。回忆归并排序的过程:将一个数组平均分成左右两部分,对于左右部分,继续分隔成左右两部分,直到每部分的长度为1。此时,因为每部分只有一个元素,所以可以看做每部分都是有序的,接下来,对相邻的两部分进行合并,在合并的过程中,统计逆序数的个数。假设第一个部分是6789,第二个部分是1234,合并这两个部分,第一部分下标指向数字6,第二部分下标指向数字1,此时6>1,按照归并排序的要求,需要将小的那个数放入临时数组,让小的那部分下标后移,再继续下面的操作,我们需要在这里加上一步,当前部分的值大于后部分的值时候,就出现了逆序对,比如6>1,我们知道第一部分是有序的,又因为6>1,于是7>1,8>1,9>1也成立,所以根据6>1得出的逆序对的数量是4个(mid-left+1),继续往后看,第一部分下标指向6,第二部分下标指向2,6>2,同理7,8,9也大于2,用同样的方法mid-left+1,继续累加逆序对。直到p1<=mid && p2<=right不成立了,说明有一边已经遍历完了。此时,将没有遍历完的部分,依次放入临时数组即可。最后将临时数组里的数据覆盖到原数组中即可。

此时的时间复杂度是O(nlogn)。

解法:

package com.wsy;

public class Main {
    public static int[] array;
    public static int count;

    public static void main(String[] args) {
        array = new int[]{7, 5, 6, 4};
        int length = array.length;
        mergeSort(0, length - 1);
        System.out.println("逆序对个数为:" + count);
    }

    public static void mergeSort(int left, int right) {
        if (left < right) {
            int mid = (left + right) >> 1;
            mergeSort(left, mid);
            mergeSort(mid + 1, right);
            merge(left, mid, right);
        }
    }

    public static void merge(int left, int mid, int right) {
        int p1 = left, p2 = mid + 1, p3 = 0;
        int[] temp = new int[right - left + 1];
        while (p1 <= mid && p2 <= right) {
            if (array[p1] < array[p2]) {
                temp[p3++] = array[p1++];
            } else {
                count += mid - p1 + 1;
                temp[p3++] = array[p2++];
            }
        }
        while (p1 <= mid) {
            temp[p3++] = array[p1++];
        }
        while (p2 <= right) {
            temp[p3++] = array[p2++];
        }
        for (int i = left; i <= right; i++) {
            array[i] = temp[i - left];
        }
    }
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值