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

  • 相关阅读:
    Ida动态修改android程序的内存数据和寄存器数值,绕过so文件的判断语句
    Ida双开定位android so文件
    IDA调试android so文件.init_array和JNI_OnLoad
    超EASY 五步实现Eclipse ASN.1 SDK和插件安装
    记录重装系统的艰苦奋斗历程
    音标
    有道建昆老师~Reading Comprehensive
    Linux之普通用户用sudo建立文件和root用户建立的区别
    名句
    20200307(13)
  • 原文地址:https://www.cnblogs.com/lz87/p/11846144.html
Copyright © 2011-2022 走看看