Comment on page

315.Count-of-Smaller-Numbers-After-Self

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:
Input: [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.

代码

Approach #0 Binary Indexed Tree

class Solution {
class FenwickTree {
private int[] sums;
public FenwickTree(int n) {
sums = new int[n + 1];
}
​
public void update(int i, int delta) {
while (i < sums.length) {
sums[i] += delta;
i += lowbit(i);
}
}
​
public int query(int i) {
int sum = 0;
while (i > 0) {
sum += sums[i];
i -= lowbit(i);
}
return sum;
}
​
private int lowbit(int x) {
return x & -x;
}
};
​
public List<Integer> countSmaller(int[] nums) {
int[] sorted = Arrays.copyOf(nums, nums.length);
Arrays.sort(sorted);
Map<Integer, Integer> ranks = new HashMap();
int rank = 0;
for (int i = 0; i < sorted.length; i++) {
if (i == 0 || sorted[i] != sorted[i - 1]) {
ranks.put(sorted[i], ++rank);
}
}
​
FenwickTree tree = new FenwickTree(ranks.size());
List<Integer> ans = new ArrayList<Integer>();
for (int i = nums.length - 1; i >= 0; i--) {
int sum = tree.query(ranks.get(nums[i]) - 1);
ans.add(sum);
tree.update(ranks.get(nums[i]), 1);
}
​
Collections.reverse(ans);
return ans;
}
}

Approach #1 BST

Every node will maintain a val sum recording the total of number on it's left bottom side, dup counts the duplication. For example, [3, 2, 2, 6, 1], from back to beginning,we would have:
1(0, 1)
\
6(3, 1)
/
2(0, 2)
\
3(0, 1)
class Solution {
class Node {
Node left, right;
int val, sum, dup = 1;
public Node(int v, int s) {
val = v;
sum = s;
}
}
​
public List<Integer> countSmaller(int[] nums) {
Integer[] ans = new Integer[nums.length];
Node root = null;
for (int i = nums.length - 1; i >= 0; i--) {
root = insert(nums[i], root, ans, i, 0);
}
return Arrays.asList(ans);
}
​
private Node insert(int num, Node node, Integer[] ans, int i, int preSum) {
if (node == null) {
node = new Node(num, 0);
ans[i] = preSum;
} else if (node.val == num) {
node.dup++;
ans[i] = preSum + node.sum;
} else if (num < node.val) { // left
node.sum++;
node.left = insert(num, node.left, ans, i, preSum);
} else { // right
// num > node.val
node.right = insert(num, node.right, ans, i, preSum + node.dup + node.sum);
}
​
return node;
}
}

Approach #2 Merge Sort

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.
class Solution {
public List<Integer> countSmaller(int[] nums) {
List<Integer> res = new ArrayList<Integer>();
int[] count = new int[nums.length];
​
int[] indexes = new int[nums.length];
for (int i = 0; i < nums.length; i++) {
indexes[i] = i;
}
mergesort(nums, indexes, 0, nums.length - 1, count);
​
for (int i = 0; i < count.length; i++) {
res.add(count[i]);
}
return res;
}
​
private void mergesort(int[] nums, int[] indexes, int start, int end, int[] count){
if (end <= start) return;
​
int mid = (start + end) / 2;
mergesort(nums, indexes, start, mid, count);
mergesort(nums, indexes, mid + 1, end, count);
​
merge(nums, indexes, start, end, count);
}
​
private void merge(int[] nums, int[] indexes, int start, int end, int[] count) {
int mid = (start + end) / 2;
int left_index = start;
int right_index = mid + 1;
int rightcount = 0;
int[] new_indexes = new int[end - start + 1];
​
int sort_index = 0;
while (left_index <= mid && right_index <= end) {
if (nums[indexes[right_index]] < nums[indexes[left_index]]) {
new_indexes[sort_index] = indexes[right_index];
rightcount++;
right_index++;
} else {
new_indexes[sort_index] = indexes[left_index];
count[indexes[left_index]] += rightcount;
left_index++;
}
sort_index++;
}
​
while (left_index <= mid){
new_indexes[sort_index] = indexes[left_index];
count[indexes[left_index]] += rightcount;
left_index++;
sort_index++;
}
​
while (right_index <= end){
new_indexes[sort_index] = indexes[right_index];
right_index++;
sort_index++;
}
​
for (int i = start; i <= end; i++){
indexes[i] = new_indexes[i - start]; // ?
}
}
}

Approach #3 Merge Sort

class Solution {
public List<Integer> countSmaller(int[] nums) {
int[] res = new int[nums.length];
int[] index = new int[nums.length];
for (int i = 0; i < res.length; i++) {
index[i] = i;
}
mergeSort(nums, index, 0, nums.length - 1, res);
List<Integer> list = new LinkedList<>();
for (int i: res) {
list.add(i);
}
return list;
}
​
private void mergeSort(int[] nums, int[] index, int l, int r, int[] res) {
if (l >= r) {
return;
}
int mid = (l + r) / 2;
mergeSort(nums, index, l, mid, res);
mergeSort(nums, index, mid + 1, r, res);
merge(nums, index, l, mid, mid + 1, r, res);
}
​
private void merge(int[] nums, int[] index, int l1, int r1, int l2, int r2, int[] res) {
int start = l1;
int[] tmp = new int[r2 - l1 + 1];
int count = 0;
int p = 0;
while (l1 <= r1 || l2 <= r2) {
if (l1 > r1) {
tmp[p++] = index[l2++];
} else if (l2 > r2) {
res[index[l1]] += count;
tmp[p++] = index[l1++];
} else if (nums[index[l1]] > nums[index[l2]]) {
tmp[p++] = index[l2++];
count++;
} else {
res[index[l1]] += count;
tmp[p++] = index[l1++];
}
}
for (int i = 0; i < tmp.length; i++) {
index[start+i] = tmp[i];
}
}
}

Approach #4 Segment tree

class Solution {
public List<Integer> countSmaller(int[] nums) {
if (nums == null || nums.length == 0) return new ArrayList<>();
int[] minMax = getMinMax(nums);
SegmentTreeNode root = new SegmentTreeNode(minMax[0], minMax[1]);
LinkedList<Integer> ans = new LinkedList<>();
for (int i = nums.length - 1; i >= 0; i--) {
//query
ans.addFirst(query(root, nums[i]));
//update tree;
update(root, nums[i]);
}
return ans;
}
private int query(SegmentTreeNode root, int num) {
if (root == null || root.start >= num) return 0;
if (num > root.end) return root.count;
return query(root.left, num) + query(root.right, num);
}
private void update(SegmentTreeNode root, int num) {
​
if (num < root.start || num > root.end) return;
root.count++;
if (root.start == root.end) return;
int mid = root.start + (root.end - root.start) / 2;
if (num <= mid) {
if (root.left == null) {
root.left = new SegmentTreeNode(root.start, mid);
}
update(root.left, num);
} else {
if (root.right == null) {
root.right = new SegmentTreeNode(mid + 1, root.end);
}
update(root.right, num);
}
}
​
private int[] getMinMax(int[] nums) {
int min = nums[0], max = nums[0];
for (int n : nums) {
min = min > n ? n : min;
max = max < n ? n : max;
}
return new int[]{min, max};
}
}
class SegmentTreeNode {
int start;
int end;
int count;
SegmentTreeNode left;
SegmentTreeNode right;
public SegmentTreeNode(int start, int end) {
this.start = start;
this.end = end;
this.count = 0;
}
}
public class Solution {
public List<Integer> countSmaller(int[] nums) {
int[] res = new int[nums.length];
List<Integer> list = new ArrayList<>();
for(int i = nums.length - 1; i >= 0; i--) {
res[i] = insert(list, nums[i]);
}
list.clear();
for(int i = 0 ; i < nums.length; i++) {
list.add(res[i]);
}
return list;
}
​
// binary insert
private int insert(List<Integer> list, int num) {
int l = 0;
int r = list.size() - 1;
while(l <= r) {
int mid = l + (r - l)/2;
int M = list.get(mid);
if(M >= num) {
r = mid - 1;
}else if(M < num) {
l = mid + 1;
}
}
list.add(l, num);
return l;
}
}