原题网址:https://leetcode.com/problems/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:
Given nums = [5, 2, 6, 1] 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]
.
方法一:穷举计数,时间复杂度O(n^2),超时、无法通过。
public class Solution {
public List countSmaller(int[] nums) {
int[] smaller = new int[nums.length];
for(int i=0; i nums[j]) smaller[i] ++;
}
}
List results = new ArrayList<>(smaller.length);
for(int i=0; i
方法二:从右到左扫描,应用二分法将当前数据插入到前面已排好序的数组中,二分法查找的时间复杂度为O(logn),但由于需要插入数据,所以每个数据查找和插入的总时间是O(nlogn),总体的时间复杂度O(n^2*logn),但勉强能够通过测试。
public class Solution {
public List countSmaller(int[] nums) {
int[] smaller = new int[nums.length];
for(int i=nums.length-2; i>=0; i--) {
int left = i+1;
int right = nums.length-1;
while (left<=right) {
int m = (left+right)/2;
if (nums[i] > nums[m]) right = m - 1;
else left = m + 1;
}
smaller[i] = nums.length - left;
int temp = nums[i];
for(int j=i; j results = new ArrayList<>(nums.length);
for(int i=0; i
方法三:应用合并排序。如果我们对数组进行排序,那么对于某个特定的数据,其后面的逆序数等于在排序过程中需要移动到该数前面的个数。时间复杂度O(nlogn)。
我们定义数组pos[i] = j,表示第排名i个数据的元素下标是j。
public class Solution {
private void sort(int[] nums, int[] smaller, int[] pos, int from, int to) {
if (from >= to) return;
int m = (from + to) / 2;
sort(nums, smaller, pos, from, m);
sort(nums, smaller, pos, m+1, to);
int[] merged = new int[to-from+1];
int i=from, j=m+1, k=0, jump = 0;
while (i<=m || j<=to) {
if (i>m) {
jump ++;
merged[k++] = pos[j++];
} else if (j>to) {
smaller[pos[i]] += jump;
merged[k++] = pos[i++];
} else if (nums[pos[i]] <= nums[pos[j]]) {
smaller[pos[i]] += jump;
merged[k++] = pos[i++];
} else {
jump ++;
merged[k++] = pos[j++];
}
}
for(int p=0; p countSmaller(int[] nums) {
int[] smaller = new int[nums.length];
int[] pos =new int[nums.length];
for(int i=0; i result = new ArrayList<>(nums.length);
for(int i=0; i
另一种实现形式:
public class Solution {
private void sort(int[] nums, int[] pos, int[] counts, int from, int to) {
if (from+1>=to) return;
int m=(from+to)/2;
sort(nums, pos, counts, from, m);
sort(nums, pos, counts, m, to);
int[] merged = new int[to-from];
int smaller = 0;
for(int i=from, j=m, k=0; k=m) {
merged[k] = pos[j++];
} else if (j>=to) {
counts[pos[i]] += smaller;
merged[k] = pos[i++];
} else if (nums[pos[i]] <= nums[pos[j]]) {
counts[pos[i]] += smaller;
merged[k] = pos[i++];
} else {
smaller ++;
merged[k] = pos[j++];
}
}
for(int i=0; i countSmaller(int[] nums) {
int[] counts = new int[nums.length];
int[] pos = new int[nums.length];
for(int i=0; i results = new ArrayList<>(nums.length);
for(int count : counts) results.add(count);
return results;
}
}
方法四:使用BST进行统计。时间复杂度O(nlogn)。可以先对数组排序,以便使BST均衡?
public class Solution {
TreeNode root;
private int smaller(TreeNode current, int val) {
current.size ++;
if (current.val < val) {
if (current.right == null) current.right = new TreeNode(val);
return current.size - 1 - current.right.size + smaller(current.right, val);
} else if (current.val > val) {
if (current.left == null) current.left = new TreeNode(val);
return smaller(current.left, val);
} else {
return current.left == null? 0 : current.left.size;
}
}
public List countSmaller(int[] nums) {
List result = new ArrayList<>(nums.length);
int[] smaller = new int[nums.length];
if (nums == null || nums.length == 0) return result;
root = new TreeNode(nums[nums.length-1]);
for(int i=nums.length-1; i>=0; i--) {
smaller[i] = smaller(root, nums[i]);
}
for(int i=0; i
事实上,二叉搜索树可以用数组实现:
public class Solution {
private void update(int[] tree, int[] smaller, int value) {
int i=0, j=tree.length-1;
while (i<=j) {
int m = (i+j)/2;
if (value < tree[m]) {
smaller[m] ++;
j = m - 1;
} else {
i = m + 1;
}
}
}
private int smaller(int[] tree, int[] smaller, int value) {
int sum = 0;
int i=0, j=tree.length-1;
while (i<=j) {
int m = (i+j)/2;
if (tree[m] <= value) {
sum += smaller[m];
i = m + 1;
} else {
j = m - 1;
}
}
return sum;
}
public List countSmaller(int[] nums) {
int[] tree = Arrays.copyOf(nums, nums.length);
Arrays.sort(tree);
int[] smaller = new int[nums.length];
int[] count = new int[nums.length];
for(int i=nums.length-1; i>=0; i--) {
count[i] = smaller(tree, smaller, nums[i]);
update(tree, smaller, nums[i]);
}
List results = new ArrayList<>(nums.length);
for(int i=0; i
甚至再进一步简化:
public class Solution {
private int smaller(int[] tree, int[] smaller, int value) {
int sum = 0;
int i=0, j=tree.length-1;
while (i<=j) {
int m = (i+j)/2;
if (tree[m] <= value) {
sum += smaller[m];
i = m + 1;
} else {
smaller[m] ++;
j = m - 1;
}
}
return sum;
}
public List countSmaller(int[] nums) {
int[] tree = Arrays.copyOf(nums, nums.length);
Arrays.sort(tree);
int[] smaller = new int[nums.length];
int[] count = new int[nums.length];
for(int i=nums.length-1; i>=0; i--) {
count[i] = smaller(tree, smaller, nums[i]);
}
List results = new ArrayList<>(nums.length);
for(int i=0; i
上面判断条件中,tree[m] <= value修改为tree[m] < value会更加make sense:
public class Solution {
private int smaller(int[] tree, int[] count, int value) {
int sum = 0;
int i = 0, j = tree.length - 1;
while (i <= j) {
int m = (i + j) / 2;
if (tree[m] < value) {
sum += count[m];
i = m + 1;
} else {
count[m]++;
j = m - 1;
}
}
return sum;
}
public List countSmaller(int[] nums) {
int[] tree = Arrays.copyOf(nums, nums.length);
Arrays.sort(tree);
int[] count = new int[nums.length];
Integer[] result = new Integer[nums.length];
for(int i = nums.length - 1; i >= 0; i--) {
result[i] = smaller(tree, count, nums[i]);
}
return Arrays.asList(result);
}
}
public class Solution {
private int smaller(int[] tree, int[] count, int value) {
int sum = 0;
int i = 0, j = tree.length - 1;
while (i <= j) {
int m = (i + j) / 2;
if (tree[m] < value) {
sum += count[m];
i = m + 1;
} else {
count[m]++;
j = m - 1;
}
}
return sum;
}
public List countSmaller(int[] nums) {
Set unique = new HashSet<>();
for(int num : nums) unique.add(num);
int[] tree = new int[unique.size()];
int pos = 0;
for(int num : unique) tree[pos++] = num;
Arrays.sort(tree);
int[] count = new int[nums.length];
Integer[] result = new Integer[nums.length];
for(int i = nums.length - 1; i >= 0; i--) {
result[i] = smaller(tree, count, nums[i]);
}
return Arrays.asList(result);
}
}
public class Solution {
private int smaller(Node node, int val) {
node.count ++;
if (node.min == node.max) return 0;
int m = (node.min + node.max) / 2;
if (m < val) {
if (node.right == null) node.right = new Node(m+1, node.max);
return node.count - 1 - node.right.count + smaller(node.right, val);
} else if (m > val) {
if (node.min == m) return 0;
if (node.left == null) node.left = new Node(node.min, m-1);
return smaller(node.left, val);
} else {
if (node.left == null) return 0;
return node.left.count;
}
}
public List countSmaller(int[] nums) {
int min = Integer.MAX_VALUE;
int max = Integer.MIN_VALUE;
for(int num: nums) {
min = Math.min(min, num);
max = Math.max(max, num);
}
Node root = new Node(min, max);
int[] smaller = new int[nums.length];
for(int i=nums.length-1; i>=0; i--) {
smaller[i] = smaller(root, nums[i]);
}
List result = new ArrayList<>(nums.length);
for(int i=0; i
注意,关于分段树此处可能有概念误解,分段树应该是按照名次进行分段,即等同于树状数组,以后再具体检查修正。
方法六:使用树状数组,要点是按名次进行计数。时间复杂度O(nlogn)。
public class Solution {
private void sort(int[] nums, int[] pos, int from, int to) {
if (from>=to) return;
int m = (from+to)/2;
sort(nums, pos, from, m);
sort(nums, pos, m+1, to);
int[] merged = new int[to-from+1];
int i=from, j=m+1, p=0;
while (i<=m || j<=to) {
if (i>m) {
merged[p++] = pos[j++];
} else if (j>to) {
merged[p++] = pos[i++];
} else if (nums[pos[i]] <= nums[pos[j]]) {
merged[p++] = pos[i++];
} else {
merged[p++] = pos[j++];
}
}
for(int k=0; k0) {
count += sum[s];
s -= (s & -s);
}
return count;
}
private void update(int[] sum, int s) {
while (s countSmaller(int[] nums) {
int[] pos = new int[nums.length];
for(int i=0; i=0; i--) {
smaller[i] = count(sum, seq[i]-1);
update(sum, seq[i]);
}
List result = new ArrayList<>(nums.length);
for(int i=0; i