面试题51.数组中的逆序对-详解归并排序java实现

题目

在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数。

示例 1:输入: [7,5,6,4]
输出: 5

限制:0 <= 数组长度 <= 50000

思路一:

暴力解题:依题意,我们可以用双层for循环累计求和。

java代码

class Solution {
    public int reversePairs(int[] nums){
    	int count = 0;
	for(int i=0;i<nums.length-1;i++){
		for(int j = i+1;j<nums.length;j++){
			if(nums[i]>nums[j]){
				count++;
			}
		}
	}
	return count;
    }

}

这是一道面试题,答案不可能这么简单。运行后会发现,当数据过大,会超时。
此种方法被pass掉。

思路二:

为了解决这个问题,我们需要设想这样一个场景。
这道题是要我们输出逆序数组对,那么我们怎样实现这个方式呢,答案是采用分而治之的思想,既然它要我们找逆序数对,我们不妨采用排序算法在排序过程中统计逆序数对。

归并排序实现逆序数对的统计

首先,我们需要了解什么是归并排序,它的思想是什么。

归并排序是一种典型的分治策略。
它包含这样三个步骤:
分解: 待排序的区间为 l, r,令 m = (l+r)/2,我们把 l, r 分成 [l, m ]和 [m + 1, r],采用递归不断分解。
解决: 使用归并排序递归地排序两个子序列
合并: 把两个已经排好序的子序列 [l, m] 和 [m + 1, r] 合并起来

这里借用网上的图来给大家分析:
首先我们需要采用递归来把数组给划分成最小单元。然后排序进行合并。

在这里插入图片描述

这里我们对拆分的数组进行合并,原理是开辟一个新的数组,将分散的2个有序数组进行比较并存入新数组中,最后返回这个新数组。

扫描二维码关注公众号,回复: 11346751 查看本文章

在这里插入图片描述

了解了归并排序,我们就可以在此基础上对我们这一题进行求解。由图我们可以看出,我们在合并的过程中进行统计逆序数对。只要第一个数组的元素大于第二个数组的元素,我们就可以进行统计。
统计公式为:count = mid-i+1

java代码:

  public int reversePairs(int[] nums) {
        int ret = 0;
        int len = nums.length;
        //判空操作
        if(len<2){
            return ret;
        }
        return mergeSort(nums, 0, nums.length - 1);
    }

    private int mergeSort(int[] nums, int left, int right) {
        if (left >= right) {
            return 0;
        }
        //拆分
        int mid = (left+right)/2;
        int leftPairs = mergeSort(nums, left, mid);
        int rightPairs = mergeSort(nums, mid + 1, right);

        //归并
        int crossPairs = merge(nums, left, mid, right);
        //将归并的所有结果汇总返回
        return  leftPairs+ rightPairs + crossPairs;
    }

    private int merge(int[] nums, int left, int mid, int right) {
        //指向第一个数组指针
        int i = left;
        //指向第二个数组指针
        int j = mid + 1;

        int k = 0;
        //计数
        int count = 0;
        //开辟新数组存放合并数组
        int res[] = new int[right - left + 1];
        //开始合并
        while (i <= mid && j <= right) {
            //如果j位置小于i位置,那么j位置小于i位置后所有的左半边的数
            if (nums[i] > nums[j]) {
                count += mid - i + 1;
                res[k++] = nums[j++];
            }else{
                res[k++] = nums[i++];
            }
//            res[k++] = nums[i] <= nums[j] ? nums[i++] : nums[j++];
        }
        //合并剩下的数组
        while (i <= mid) {
            res[k++] = nums[i++];
        }
        while (j <= right) {
            res[k++] = nums[j++];
        }
        //将值赋值回原数组
        for (int m = 0; m < res.length; m++) {
            nums[left + m] = res[m];
        }
        return count;
    }

如若对此题还未理解,欢迎私信博主。或者是参考力扣官方题解:https://leetcode-cn.com/problems/shu-zu-zhong-de-ni-xu-dui-lcof/solution/shu-zu-zhong-de-ni-xu-dui-by-leetcode-solution/

归并算法是一个经典算法,博主提醒小伙伴们务必掌握!

猜你喜欢

转载自blog.csdn.net/qq_43431171/article/details/105725518