算法——计算数组中的逆序对

在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数。
输入: [7,5,6,4]
输出: 5
链接: leetcode.

解题思路:利用归并排序的思想。在归并排序中,需要通过两个指针比较前后两个元素的大小,当前元素大于后元素时,这就是一个有效的逆序对。同时,如果某一前元素大于后元素,则前段元素区间中,该元素的后面元素也会大于后段区间中该后元素,所以,也需要将这一部分逆序对计算上。
这样做会不会产生重复的逆序对计算呢?答案是不会的,每次比较的两个相等长度的段中的元素,随着递归,前面计算的段已经被合并,段的长度会增长,之后比较的元素一定不会再被重复比较,这样就不会产生重复计算。

下面提供了两种做法,还是推荐递归做法,这样没用考虑很多边界问题。

  1. 递归做法
class Solution {
    int res = 0;
    public int reversePairs(int[] nums) {
        merge(nums, 0, nums.length - 1);

        return res;
    }

    public void merge(int[] nums, int l, int r) {
        if(l >= r) return;

        int mid = l + r >> 1;
        merge(nums, l, mid);
        merge(nums, mid + 1, r);

        int i = l, j = mid + 1;
        List<Integer> temp = new ArrayList<>();

        while(i <= mid && j <= r) {
            if(nums[i] <= nums[j]) {
                temp.add(nums[i]);
                i++;
            } else {
            	// 逆序对计算,数量应当是i及i到mid之间的元素数量
            	// 因为i大于j位置的元素,那i之后的元素也一定大于j位置的元素
                res += mid - i + 1;
                temp.add(nums[j]);
                j++;
            }
        }

        while(j <= r) {
            temp.add(nums[j]);
            j++;
        }

        while(i <= mid) {
            temp.add(nums[i]);
            i++;
        }

        i = l;

        while(i <= r) {
            nums[i] = temp.get(i - l);
            i++;
        }

    }
}
  1. 非递归
class Solution {
    public int reversePairs(int[] nums) {
        int n = nums.length;
        if(n == 0 || n == 1) return 0;
        int res = 0;

        for(int len = 1; len < n; len *= 2) {
            for(int i = 0; i < n; i += 2 * len) {
                List<Integer> temp = new ArrayList<>();
                int l = i, r = i + len;
                int mid = i + len;
                while(l < mid && r < i + len * 2 && r < n) {
                    if(nums[l] > nums[r]) {
                        res += mid - l;
                        temp.add(nums[r]);
                        r++;
                    } else{
                        temp.add(nums[l]);
                        l++;
                    }
                }

                while(l < mid && l < n) {
                    temp.add(nums[l++]);
                }

                while(r < i + len * 2 && r < n) {
                    temp.add(nums[r++]);
                }

                for(int j = 0; j < temp.size(); j++) {
                    nums[j + i] = temp.get(j);
                }
            }
        }

        return res;
    }
}
posted @ 2020-11-04 19:37  lippon  阅读(107)  评论(0编辑  收藏  举报