zoukankan      html  css  js  c++  java
  • [LeetCode 493] Reverse Pairs

    Given an array nums, we call (i, j) an important reverse pair if i < j and nums[i] > 2*nums[j].

    You need to return the number of important reverse pairs in the given array.

    Example1:

    Input: [1,3,2,3,1]
    Output: 2
    

     

    Example2:

    Input: [2,4,3,5,1]
    Output: 3
    

     

    Note:

    1. The length of the given array will not exceed 50,000.
    2. All the numbers in the input array are in the range of 32-bit integer.

    Solution 1. Binary Indexed Tree with binary search or coordinate compression, O(N * logN)

    The keys of a binary indexed tree are all the possible number values. So the high level idea of using a BIT here is that we process each nums[i] from left to right, check the count of > 2 * nums[i] and update ans, then update BIT with nums[i] appearing one more time. 

    Memory Limitation: The input length is up to 5 * 10^4, but there is no constraint on each input array number. It can be as big as Integer.MAX_VALUE or as small as Integer.MIN_VALUE. But we know there can be at most 5 * 10^4 unique numbers and will use this to get around the memory limitation here. 

    The first approach is to use binary search: get a copy of nums, sort it, and create a BIT of size nums.length + 1 as there will be at most nums.length unique numbers. The relative order of nums[i] is matched to BIT's key in the following manner:

    1. nums[i] -> binary search to find the leftmost nums[i]' index J and use J + 1 to update; Guanranteed to find a match; 

    2. nums[i] * 2 + 1 -> binary search to find the leftmost index K such that nums[K] >= nums[i] * 2 + 1;  May not exist;

    class BinaryIndexedTree {
        int[] ft;
        BinaryIndexedTree(int n) {
            ft = new int[n];
        }
        int rangeSum(int r) {
            int sum = 0;
            for(; r > 0; r -= (r & (-r))) {
                sum += ft[r];
            }
            return sum;
        }
        void update(int k, int v) {
            for(; k < ft.length; k += (k & (-k))) {
                ft[k] += v;
            }
        }
    }
    class Solution {
        public int reversePairs(int[] nums) {
            int n = nums.length, ans = 0;
            int[] a = Arrays.copyOf(nums, nums.length);
            Arrays.sort(a);
            BinaryIndexedTree bit = new BinaryIndexedTree(n + 1);
            for(int i = 0; i < n; i++) {
                int idx = index(a, 2L * nums[i] + 1);
                if(idx <= n) {
                    ans += bit.rangeSum(n) - bit.rangeSum(idx);
                }
                bit.update(index(a, nums[i]) + 1, 1);
            }
            return ans;
        }
        //find the leftmost index i such that a[i] >= target
        private int index(int[] a, long target) {
            int l = 0, r = a.length - 1;
            while(l < r - 1) {
                int mid = l + (r - l) / 2;
                if(a[mid] >= target) {
                    r = mid;
                }
                else {
                    l = mid + 1;
                }
            }
            if(a[l] >= target) {
                return l;
            }
            else if(a[r] >= target) {
                return r;
            }
            return a.length + 1;
        }
    }

    The second approach is to use coordinate compression:  Combine all nums[i] * 2 along with nums[i] and sort the combined values. For each unqiue value, assign an index to it. The rest logic is pretty much the same with the binary search approach.

    class BinaryIndexedTree {
        int[] ft;
        BinaryIndexedTree(int n) {
            ft = new int[n];
        }
        int rangeSum(int r) {
            int sum = 0;
            for(; r > 0; r -= (r & (-r))) {
                sum += ft[r];
            }
            return sum;
        }
        void update(int k, int v) {
            for(; k < ft.length; k += (k & (-k))) {
                ft[k] += v;
            }
        }
    }
    class Solution {
        public int reversePairs(int[] nums) {
            Map<Long, Integer> compressMap = coordCompress(nums);
            int n = compressMap.size(), ans = 0;
            BinaryIndexedTree bit = new BinaryIndexedTree(n + 1);
            for(int i = 0; i < nums.length; i++) {
                ans += bit.rangeSum(n) - bit.rangeSum(compressMap.get(2L * nums[i]));
                bit.update(compressMap.get(1L * nums[i]), 1);
            }
            return ans;
        }
        private Map<Long, Integer> coordCompress(int[] a) {
            Map<Long, Integer> map = new HashMap<>();
            long[] b = new long[a.length * 2];
            int j = 0;
            for(int i = 0; i < a.length; i++) {
                b[j] = a[i];
                j++;
                b[j] = 2L * a[i];
                j++;
            }
            Arrays.sort(b);
            int idx = 1;
            for(long v : b) {
                if(!map.containsKey(v)) {
                    map.put(v, idx);
                    idx++;
                }
            }
            return map;
        }
    }

     Solution 2. Divide and Conquer, Merge Sort based approach, O(N * logN)

    If we divide nums into two subarrays left and right of equal length, then the final answer is the answer of left and right + the count where the 1st element is from left and the 2nd element is from right.  If after solving the subproblems on left and right, these two subarrays are also in sorted order, then we can count the cross pair in linear time by using two pointers. This can be embedded into merge sort.

    class Solution {
        public int reversePairs(int[] nums) {
            return mergeSort(nums, new int[nums.length], 0, nums.length - 1);
        }
        private int mergeSort(int[] a, int[] aux, int l, int r) {
            if(l >= r) return 0;
            int mid = l + (r - l) / 2;
            int cnt = mergeSort(a, aux, l, mid) + mergeSort(a, aux, mid + 1, r);
            for(int i = l; i <= r; i++) {
                aux[i] = a[i];
            }
            int k1 = l, k2 = mid + 1;
            while(k1 <= mid && k2 <= r) {
                if(aux[k1] > 2L * aux[k2]) {
                    cnt += (mid - k1 + 1);
                    k2++;
                }
                else {
                    k1++;
                }
            }
            k1 = l;
            k2 = mid + 1;
            for(int i = l; i <= r; i++) {
                if(k1 <= mid && k2 <= r) {
                    if(aux[k1] <= aux[k2]) {
                        a[i] = aux[k1];
                        k1++;
                    }
                    else {
                        a[i] = aux[k2];
                        k2++;
                    }
                }
                else if(k1 <= mid) {
                    a[i] = aux[k1];
                    k1++;
                }
                else {
                    a[i] = aux[k2];
                    k2++;
                }
            }
            return cnt;
        }
    }

    Related Problems

    [LeetCode 315] Count of Smaller Numbers After Self

  • 相关阅读:
    Git轻松入门3:远程仓库篇
    Git轻松入门2:分支篇
    Git轻松入门1:本地仓库篇
    通俗易懂的解释:什么是API
    小白都看得懂的Javadoc使用教程
    尾调用与尾递归
    要理解递归就要先理解递归:手把手教你写递归
    不复杂的空间复杂度
    不复杂的时间复杂度
    Java程序执行过程及内存机制
  • 原文地址:https://www.cnblogs.com/lz87/p/11846144.html
Copyright © 2011-2022 走看看