初看感觉蛮简单,但是实现过程中就犯迷糊了,主要是针对重复的元素不知道咋简单的写代码处理得到小于该重复数字的个数,然后看了卡哥的讲解,给了很好的思路:
这个思路和y总讲 01背包问题 的时候对二维dp优化为一维dp的思路大相径庭,很奇妙!
给出自己在看了卡哥思路后尝试写的代码:
class Solution {
public:
vector<int> smallerNumbersThanCurrent(vector<int>& nums) {
vector<int> ans, tmp;
tmp = nums;
int sn [110] = {};
sort(nums.begin(), nums.end());
for (int i = nums.size() - 1; i >= 0; i--)
sn[nums[i]] = i;
for (auto c : tmp)
ans.push_back(sn[c]);
return ans;
}
};
也给出卡哥的代码:
class Solution {
public:
vector<int> smallerNumbersThanCurrent(vector<int>& nums) {
vector<int> vec = nums;
sort(vec.begin(), vec.end()); // 从小到大排序之后,元素下标就是小于当前数字的数字
int hash[101];
for (int i = vec.size() - 1; i >= 0; i--) { // 从后向前,记录 vec[i] 对应的下标
hash[vec[i]] = i;
}
// 此时hash里保存的每一个元素数值 对应的 小于这个数值的个数
for (int i = 0; i < nums.size(); i++) {
vec[i] = hash[nums[i]];
}
return vec;
}
};
不过看了官方题解后,官方的方法三很妙:
代码如下:
class Solution {
public:
vector<int> smallerNumbersThanCurrent(vector<int>& nums) {
vector<int> cnt(101, 0);
int n = nums.size();
for (int i = 0; i < n; i++) {
cnt[nums[i]]++;
}
for (int i = 1; i <= 100; i++) {
cnt[i] += cnt[i - 1];
}
vector<int> ret;
for (int i = 0; i < n; i++) {
ret.push_back(nums[i] == 0 ? 0: cnt[nums[i]-1]);
}
return ret;
}
};
这样做就不用排序了 (没有了sort(nums.begin(), nums.end());
),而排序需要 O(NlogN) 的时间,所以时间复杂度改善了。
然后需要注意一定要写成ret.push_back(nums[i] == 0 ? 0: cnt[nums[i]-1]);
而不能写成ret.push_back(cnt[nums[i]-1]);
,为什么?
假设当nums[i]为0时,那么cnt[nums[i]-1]
的写法就报错了(因为变成了cnt[-1]
)。