经典算法问题: 逆序对(用归并排序的思想解决)

问题:给定一个长度为n的排列,求其逆序对数

输入格式:
       输入一个数组

输出格式:
       输出逆序对总数
  
样例输入:
        [1,2,4,3]

样例输出:
          1
数据规模:
          n<=100000

解题思路:

    首先,在解决这个问题之前,先了解一下什么叫逆序对

逆序对:数学术语,设 A 为一个有 n 个数字的有序集 (n>1),其中所有数字各不相同。
如果存在正整数 i, j 使得 1 ≤ i < j ≤ n 而且 A[i] > A[j],则 <A[i], A[j]> 这个有序对称为 A 的一个逆序对,也称作逆序数。

    简单来说,就是在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。

例如:
[1,2,4,3]
其中的逆序对只有(4,3)
所以return 1

在例如:
数组 [2, 4, 1, 3, 5] 中
有 3 个逆序对 (2, 1), (4, 1), (4, 3)
则 return 3 

    知道了逆序对是什么,就很好的求解了,只需要要左边的大于右边的即可。

    那么,用归并的思想来说,在每一次拆分合并的过程中,可以比较左边的数组和右边的两个数组,在组成新的数组中,每次右边的数组加入新数组,那么就是一组逆序对(因为左边的大于右边的)。

不知道的归并排序的小伙伴可以看看我另一篇文章
经典排序之一:归并排序

例如:nums=[2,9,5,4]

其过程如图:
在这里插入图片描述

由上图可明显的看出,在每次归并的过程中当右边的数组加入新数组的时候,那么此时的逆序对个就要相加。

注意:这里的相加不是简单的加1,而是加上左边数组的长度减去当前下标。因为左边最小的数子已经大于右边,那么,左边数组的所有数必然大于右边的数,每一个都是一个逆序对。

然后,在每次递归调用将其逆序对加起来,就是所求的总逆序对数。

这就是归并排序的思想,很简单,只需要增加一行代码即可。

代码我放下下面,小伙伴们可以去看看我另一篇文章中归并排序的代码,两个一对比,相信你很快能理解。

题目代码:

public class CastleWalls {
    static long revers = 0; //一定要用long int相对于100000个数时候会溢出,返回一个负号

    private static void mergeSort(int[] list) {
        if (list.length > 1) {
            int[] fiestHalf = new int[list.length / 2];
            System.arraycopy(list, 0, fiestHalf, 0, list.length / 2);
            mergeSort(fiestHalf);
            int[] secondHalf = new int[list.length - list.length / 2];
            System.arraycopy(list, list.length / 2, secondHalf, 0, list.length - list.length / 2);
            mergeSort(secondHalf);
            merge(fiestHalf, secondHalf, list);
        }

    }

    private static void merge(int[] list1, int[] list2, int[] list3) {
        int current1 = 0;
        int current2 = 0;
        int current3 = 0;
        while (current1 < list1.length && current2 < list2.length) {
            if (list1[current1] <= list2[current2]) { ///必须小于等于,当两个比较相等,其后面就不用比较,将左边的放入数组,
                // 然后右边的继续比较左边的,校友,则为逆序数,将其放入新数组
                list3[current3++] = list1[current1++];
            } else {
                list3[current3++] = list2[current2++];
                revers += (list1.length - current1); //如果左边最小的数已经大于右边。那么左边的所有数都大于右边,那么就组成了list1.lenth-current1个逆序对

            }
        }

        while (current1 < list1.length) {
            list3[current3++] = list1[current1++];
        }

//            for (int i = current1; i < list1.length; i++) {
//                list3[current3++] = list1[i];
//            }

        while (current2 < list2.length) {
            list3[current3++] = list2[current2++];
        }

//            for (int i = current2; i < list2.length; i++) {
//                list3[current3++] = list2[i];
//            }
    }


    public static void main(String[] args) {

//可根据输入N 来输入数组规模
//        Scanner input = new Scanner(System.in);
//        int lenth = input.nextInt();
//        int [] num = new int[lenth];
//        for (int i = 0; i < num.length; i++) {
//            num[i]=input.nextInt() ;
//        }
//        mergeSort(num);
//        System.out.println(revers);

		//测试数组
        int num[] = {3, 8, 6, 11, 2, 7, 11};
        mergeSort(num);
        System.out.println(Arrays.toString(num));
        System.out.println(revers);
    }
}

测试结果:
[2, 3, 6, 7, 8, 11, 11]
7

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

风儿吹吹吹

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值