# [LeetCode 315] Count of Smaller Numbers After Self

You are given an integer array nums and you have to return a new counts array. The counts array has the property where counts[i] is the number of smaller elements to the right of nums[i].

Example 1:

Input: nums = [5,2,6,1]
Output: [2,1,1,0]
Explanation:
To the right of 5 there are 2 smaller elements (2 and 1).
To the right of 2 there is only 1 smaller element (1).
To the right of 6 there is 1 smaller element (1).
To the right of 1 there is 0 smaller element.


Example 2:

Input: nums = [-1]
Output: [0]


Example 3:

Input: nums = [-1,-1]
Output: [0,0]


Constraints:

• 1 <= nums.length <= 10^5
• -10^4 <= nums[i] <= 10^4

Brute force solution takes O(N^2) time.

Solution 1. Count smaller numbers during merge sort, O(N * logN)

In merge sort, when we are doing the merge for subarray [L, R], each time we pick a number A[i] from the left half, we can check how many numbers from the right half have been picked. This count is the number of smaller values of A[i] on its right side. After the merging for [L, R] is done, it is in sorted order, which means when we merge even larger subarrays, [L,R] will be on the same side, either left half or right half but not both. This way we'll never double count. So for A[i], we can first save its index information for the purpose of updating the final count. Then do a merge sort on A, the total number of right half numbers that get picked before A[i] is its answer.

class Pair {
int v;
int idx;
Pair(int v, int idx) {
this.v = v;
this.idx = idx;
}
}
class Solution {
private Integer[] ans;
public List<Integer> countSmaller(int[] nums) {
ans = new Integer[nums.length];
Arrays.fill(ans, 0);
Pair[] pairs = convertToPairs(nums);
mergeSort(pairs, new Pair[nums.length], 0, nums.length - 1);
return Arrays.asList(ans);
}
private Pair[] convertToPairs(int[] nums) {
Pair[] pairs = new Pair[nums.length];
for(int i = 0; i < nums.length; i++) {
pairs[i] = new Pair(nums[i], i);
}
return pairs;
}
private void mergeSort(Pair[] pairs, Pair[] aux, int left, int right) {
if(left < right) {
int mid = left + (right - left) / 2;
mergeSort(pairs, aux, left, mid);
mergeSort(pairs, aux, mid + 1, right);
for(int i = left; i <= right; i++) {
aux[i] = pairs[i];
}
int j = left, k = mid + 1;
for(int i = left; i <= right; i++) {
if(j <= mid && k <= right) {
if(aux[j].v <= aux[k].v) {
pairs[i] = aux[j];
ans[aux[j].idx] += (k - mid - 1);
j++;
}
else {
pairs[i] = aux[k];
k++;
}
}
else if(j <= mid) {
pairs[i] = aux[j];
ans[aux[j].idx] += (k - mid - 1);
j++;
}
else {
pairs[i] = aux[k];
k++;
}
}
}
}
}

Solution 2. Binary Indexed Tree, O(N * log(maxV))

Create a binary indexed tree that covers the full range of all possible nums[i] values. Here let's shift the entire array by 10^4 + 1 so the minimum value is 1.

Then starting from right to left, do a sum range query on all smaller values than nums[i], this is the answer for nums[i]. Then update frequency count at key nums[i] + 10001.

class BinaryIndexedTree {
int[] ft;
BinaryIndexedTree(int n) {
ft = new int[n];
}
void update(int k, int v) {
for(; k < ft.length; k += (k & (-k))) {
ft[k] += v;
}
}
int rangeSumQuery(int r) {
int sum = 0;
for(; r > 0; r -= (r & (-r))) {
sum += ft[r];
}
return sum;
}
}
class Solution {
public List<Integer> countSmaller(int[] nums) {
BinaryIndexedTree bit = new BinaryIndexedTree(20002);
List<Integer> ans = new ArrayList<>();
for(int i = nums.length - 1; i >= 0; i--) {
bit.update(nums[i] + 10001, 1);
}
Collections.reverse(ans);
return ans;
}
}

Solution 3. Segment Tree, O(N * logN), same idea with binary indexed tree.

class SegmentTree {
SegmentTree lChild, rChild;
int leftMost, rightMost, sum;

SegmentTree(int[] a, int leftMost, int rightMost) {
this.leftMost = leftMost;
this.rightMost = rightMost;
if(leftMost == rightMost) sum = a[leftMost];
else {
int mid = leftMost + (rightMost - leftMost) / 2;
lChild = new SegmentTree(a, leftMost, mid);
rChild = new SegmentTree(a, mid + 1, rightMost);
recalc();
}
}

void recalc() {
if(leftMost != rightMost) {
sum = lChild.sum + rChild.sum;
}
}

void update(int idx, int v) {
if(leftMost == rightMost) {
sum += v;
return;
}
if(idx <= lChild.rightMost) lChild.update(idx, v);
else rChild.update(idx, v);
recalc();
}

int rangeSum(int l, int r) {
if(l > rightMost || r < leftMost) return 0;
else if(l <= leftMost && r >= rightMost) return sum;
return lChild.rangeSum(l, r) + rChild.rangeSum(l, r);
}
}
class Solution {
public List<Integer> countSmaller(int[] nums) {
List<Integer> ans = new ArrayList<>();
int[] a = new int[20002];
SegmentTree st = new SegmentTree(a, 0, a.length - 1);
for(int i = nums.length - 1; i >= 0; i--) {
ans.add(st.rangeSum(0, nums[i] + 10001 - 1));
st.update(nums[i] + 10001, 1);
}
Collections.reverse(ans);
return ans;
}
}

Related Problems

[LeetCode 493] Reverse Pairs

posted @ 2021-02-18 02:41  Review->Improve  阅读(563)  评论(0编辑  收藏  举报