import java.util.ArrayList;
import java.util.List;
/**
* 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].
* <p>
* Example:
* <p>
* Given nums = [5, 2, 6, 1]
* <p>
* 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.
* Return the array [2, 1, 1, 0].
* <p>
* Tags: Divide and Conquer, Binary Indexed Tree, Segment Tree, Binary Search Tree
* Similar Problems: (H) Count of Range Sum
* <p>
* Created by kiyan on 6/2/16.
*/
public class CountOfSmallerNumbersAfterSelf {
public static void main(String[] args) {
// int[] input = {5, 2, 6, 1};
int[] input = {1, 2, 3, 4};
CountOfSmallerNumbersAfterSelf c = new CountOfSmallerNumbersAfterSelf();
List<Integer> result = c.countSmaller(input);
System.out.println(result);
}
/**
* The smaller numbers on the right of a number are exactly those that jump from its right to its left during a
* stable sort. So I do mergesort with added tracking of those right-to-left jumps.
*/
public List<Integer> countSmaller(int[] nums) {
NumWithIndex[] array = new NumWithIndex[nums.length];
for (int i = 0; i < nums.length; i++) {
array[i] = new NumWithIndex(nums[i], i);
}
return MergeSort.sort(array);
}
static class NumWithIndex {
int num;
int index;
public NumWithIndex(int num, int index) {
this.num = num;
this.index = index;
}
}
static class MergeSort {
public static List<Integer> sort(NumWithIndex[] nums) {
List<Integer> smaller = new ArrayList<>();
for (int i = 0; i < nums.length; i++) {
smaller.add(0);
}
System.out.println(smaller.size());
sort(nums, 0, nums.length - 1, smaller);
return smaller;
}
private static void sort(NumWithIndex[] nums, int start, int end, List<Integer> smaller) {
if (start < end) {
int mid = start + (end - start) / 2;
sort(nums, start, mid, smaller);
sort(nums, mid + 1, end, smaller);
merge(nums, start, mid, end, smaller);
}
}
private static void merge(NumWithIndex[] nums, int start, int mid, int end, List<Integer> smaller) {
NumWithIndex[] copy = new NumWithIndex[nums.length];
for (int i = start; i <= end; i++) {
copy[i] = nums[i];
}
int low = start;
int high = mid + 1;
int currentIdx = low;
while (low <= mid && high <= end) {
if (copy[low].num <= copy[high].num) {
// how many elements moved from right to left
smaller.set(copy[low].index, smaller.get(copy[low].index) + (high - mid - 1));
nums[currentIdx++] = copy[low++];
} else {
nums[currentIdx++] = copy[high++];
}
}
for (int i = low; i <= mid; i++) {
// how many elements moved from right to left
smaller.set(copy[i].index, smaller.get(copy[i].index) + (high - mid - 1));
nums[currentIdx++] = copy[i];
}
}
}
}