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:
- The length of the given array will not exceed
50,000
. - 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