[LeetCode] 1365. How Many Numbers Are Smaller Than the Current Number 有多少小于当前数字的数字
Given the array nums
, for each nums[i]
find out how many numbers in the array are smaller than it. That is, for each nums[i]
you have to count the number of valid j's
such that j != i
and nums[j] < nums[i]
.
Return the answer in an array.
Example 1:
Input: nums = [8,1,2,2,3]
Output: [4,0,1,1,3]
Explanation:
For nums[0]=8 there exist four smaller numbers than it (1, 2, 2 and 3).
For nums[1]=1 does not exist any smaller number than it.
For nums[2]=2 there exist one smaller number than it (1).
For nums[3]=2 there exist one smaller number than it (1).
For nums[4]=3 there exist three smaller numbers than it (1, 2 and 2).
Example 2:
Input: nums = [6,5,4,8]
Output: [2,1,0,3]
Example 3:
Input: nums = [7,7,7,7]
Output: [0,0,0,0]
Constraints:
2 <= nums.length <= 500
0 <= nums[i] <= 100
这道题说是给了一个数组 nums,让对于数组中的每一个数字,统计出数组中有多少个数小于该数字,并组成一个数组返回。参见题目中的例子不难理解题意,虽然说这是一道 Easy 的题,但是博主还是不想用 brute force 的方法,因为对于每个数字都遍历一遍数字统计小的实在是太直接了,完全不用什么思考,而且平方级的时间复杂度,也不知道 OJ 能不能放行。这道题比较好的解题思路是用时间换空间,建立每个数字与其出现次数之间的映射,同时按照数字大小排序,那么这里用 TreeMap 这个数据结构就比较合适了。这样按顺序遍历映射对儿,比当前映射对儿的数字小的数字个数就是前面所有映射对儿的值的总和,就拿题目中的例子1来举例吧,建立的映射对儿是:
1 -> 1
2 -> 2
3 -> 1
8 -> 1
这里定义个变量 cnt 表示当前已经累加的映射值,初始化为0。当遍历到第一个映射对儿的数字1时,当前的 cnt 值就是比1小的数字个数,为0,是对的,然后此时 cnt 要加上当前映射值1。再遍历到下一个映射对儿数字2,此时 cnt 是比2小的数字个数,为1,是对的,然后此时 cnt 要加上当前映射值2。以此类推,就可以知道比每个数字小的数字个数了,为了方便的保存结果,这里用一个 HashMap 来建立数字和比其小的数字个数之间的映射,最终组成返回数组 res 的时候就直接到这个 HashMap 中去取就可以了,参见代码如下:
解法一:
class Solution {
public:
vector<int> smallerNumbersThanCurrent(vector<int>& nums) {
int cnt = 0;
vector<int> res;
map<int, int> cntMap;
unordered_map<int, int> m;
for (int num : nums) {
++cntMap[num];
}
for (auto a : cntMap) {
m[a.first] = cnt;
cnt += a.second;
}
for (int num : nums) {
res.push_back(m[num]);
}
return res;
}
};
当然我们也可以不用 TreeMap 和 HashMap,因为题目限定了数字的大小不超过 100,所以可以用一个大小为 101 的数字来代替 TreeMap,统计每个数字出现的个数。接下来是建立累加和数组,这样位置i上的数字 cnt[i] 就表示不小于数字i的数字个数,所以小于数字i的数字个数就是 cnt[i-1],有了累加和数字,就可以很方便的生成返回数组 res 了,参见代码如下:
解法二:
class Solution {
public:
vector<int> smallerNumbersThanCurrent(vector<int>& nums) {
int n = nums.size();
vector<int> res(n);
vector<int> cnt(101);
for (int num : nums) {
++cnt[num];
}
for (int i = 1; i <= 100; ++i) {
cnt[i] += cnt[i - 1];
}
for (int i = 0; i < n; ++i) {
if (nums[i] == 0) continue;
res[i] = cnt[nums[i] - 1];
}
return res;
}
};
再来看一种利用二分搜索法的解法,关于二分搜索法可以参见博主之前的总结帖 LeetCode Binary Search Summary 二分搜索法小结,先将原数组排序,这样对于每个数字,查找第一个不小于该数字的位置,其坐标就是数组中小于该数字的个数,还是用例子1,来举例,排序后为 [1,2,2,3,8],对于数字3来说,第一个不小于3的数字的位置是3,同时也表示有3个数字比数字3小,参见代码如下:
解法三:
class Solution {
public:
vector<int> smallerNumbersThanCurrent(vector<int>& nums) {
vector<int> res, t = nums;
sort(t.begin(), t.end());
for (int num : nums) {
res.push_back(binarySearch(t, num));
}
return res;
}
int binarySearch(vector<int>& nums, int num) {
int left = 0, right = nums.size();
while (left < right) {
int mid = left + (right - left) / 2;
if (nums[mid] < num) left = mid + 1;
else right = mid;
}
return right;
}
};
当然我们也可以利用 C++ 的自带函数 lower_bound 来进行二分搜索,这样就比较偷懒啦~
解法四:
class Solution {
public:
vector<int> smallerNumbersThanCurrent(vector<int>& nums) {
vector<int> res, t = nums;
sort(t.begin(), t.end());
for (int num : nums) {
int cnt = lower_bound(t.begin(), t.end(), num) - t.begin();
res.push_back(cnt);
}
return res;
}
};
Github 同步地址:
https://github.com/grandyang/leetcode/issues/1365
类似题目:
Count of Smaller Numbers After Self
Longest Subsequence With Limited Sum
参考资料:
https://leetcode.com/problems/how-many-numbers-are-smaller-than-the-current-number