一、题目描述
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].
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.
二、题解
方法一:暴力(超时)
额…,
public List<Integer> countSmaller(int[] nums) {
List<Integer> list = new LinkedList<>();
for (int i = 0; i < nums.length; i++) {
int count = 0;
for (int j = i+1; j < nums.length; j++) {
if (nums[j] < nums[i])
count++;
}
list.add(count);
}
return list;
}
复杂度分析
- 时间复杂度:,
- 空间复杂度:,
方法二:二分
可以想象一下有 5 个人的按升高升序排列队伍进行报数,当报到 3 的时候,右边很容易得到右 2 个人。如果队伍无序则很麻烦。需要一个一个地向后数。
我们选择从右往左查询,每次查询到 1 个数时,我们将其插入到 sorted_list
中,怎么插入呢?我们选择的位置是在 sorted_list
中找到一个小于 nums[i]
的最大元素,我们将 nums[i]
插入到该元素的后面。
这样就能根据插入的位置判断有多少个元素比自己小了,又因为是从右往左枚举的,所以插入的位置代表了右边有多少个元素比自己小。
[1,3,6,1,2,3]
loop 3, output: [3] -> 3 左边有 0 个数,插在 0 位置
loop 2, output: [2,3] -> 2 左边有 0 个数,插在 0 位置
loop 1, output: [1,2,3] -> 1 左边有 0 个数,插在 0 位置
loop 6, output: [1,2,3,6] -> 6 左边有 3 个数,插在 3 位置
loop 3', output: [1,2,3',3,6] -> 3' 左边有 2 个数,插在 2 位置
loop 1', output: [1',1,2,3',3,6] -> 1' 左边有 0 个数,插在 0 位置
public List<Integer> countSmaller(int[] nums) {
List<Integer> list = new ArrayList<>();
Integer[] res = new Integer[nums.length];
for (int i = nums.length-1; i >= 0; i--) {
int l = 0, r = list.size();
while (l < r) {
int mid = (l + r) >>> 1;
if (nums[i] > list.get(mid)) {
l = mid + 1;
} else {
r = mid;
}
}
res[i] = l;
list.add(l, nums[i]);
}
return Arrays.asList(res);
}
复杂度分析
- 时间复杂度:,
- 空间复杂度:,