【差分数组】【二分查找】【数组】【2023-09-28】
2251. 花期内花的数目
每朵花都有自己的花期,有些花的花期会有重叠,也就是说某些时刻会有多朵花同时开放,你需要找出在某些时刻花开的数量。
解题的思路其实很明确,即确定某个时刻花开的数目即可。朴素的想法就是枚举的花,根据所有花的花期更新所有的时间点的花开数目。这种方法的最坏时间复杂度为 O(n^2)
, n n n 为数组 flowers
的长度,由于本题的数据规模达到 1 0 5 10^5 105,因此朴素方法一定超时。
我们维护一个有序哈希表 cnts
,表示某个时刻花开的数量(有序哈希表根据时间节点升序排序),我们枚举数组 flowers
中的每朵花 flower
:
++cnts[flower[0]]
;--cnts[flowers[1] + 1]
。我们对 flowers
中的每朵花都进行以上操作来更新 cnts
。
为什么这样更新 cnts
?因为这样,我们累加 cnts
的某个时刻的花开数量,即可得到某个时刻的所有花开数量。
我们以上更新的只是两种时刻的花开数量,即花开的时刻和花谢的下一个时刻,其他时刻的花开数量没有更新。因为我们只需要关注 “观赏者” 赏花的时刻,因此将 cnts
中没有覆盖到的观赏者赏花的时刻的花开数量进行更新。通过以下代码进行更新:
for (auto preson : presons) {
cnts[x]; // cnts 中没有键 x,就 cnts[x] = 0;否则不进行任何操作
}
然后,我们更新 cnts
:
int t = 0;
for(auto &cnt : cnts){
cnt.second = (t += cnt.second);
}
t
表示当前花开的数目,这里我们利用的是差分数组的性质累加得到某个时刻花开的数目。
最后,输出对应时刻的花开数目到答案数组 res
中,并返回。
图解 cnts
的更新
现在以 示例 1 为例,来对以上的思路进行示例分析。
(1)flowers = [[1,6],[3,7],[9,12],[4,13]], people = [2,3,7,11]
;
(2)遍历数组 flowers
,更新得到的 cnts
如下图所示:
(3)将 cnts
中没有覆盖到的观赏者赏花的时刻的花开数量进行更新:
(4)最后累加更新 cnts
。
实现代码
class Solution {
public:
vector<int> fullBloomFlowers(vector<vector<int>>& flowers, vector<int>& persons) {
map<int, int> cnts; // 统计每个时刻有几朵花开
for(auto &it : flowers){
++cnts[it[0]];
--cnts[it[1]+1];
}
for(auto x : persons){
cnts[x]; // 没有 cnts[x] 就设置 cnts[x] = 0;否则不用管
}
int t = 0;
for(auto &cnt : cnts){
cnt.second = (t += cnt.second);
}
vector<int> ret;
for(auto &x : persons){
ret.push_back(cnts[x]);
}
return ret;
}
};
复杂度分析
时间复杂度: O ( n l o g n + m l o g m ) O(nlogn+mlogm) O(nlogn+mlogm), n n n 为数组 flowers
的长度。cnts
是有序哈希表,每次插入的时间为 O ( l o g n ) O(logn) O(logn),一共插入 n
个元素,构建有序集合的时间为 O ( n l o g n ) O(nlogn) O(nlogn)。 m m m 为数组 presons
的长度,插入 m
个元素到有序哈希表中的时间复杂度为 O ( m l o g m ) O(mlogm) O(mlogm)。
空间复杂度: O ( n + m ) O(n+m) O(n+m)。
设:第 i
个人到达的时间为 presons[i]
,在 presons[i]
时间点之前的花开数目为 x
,在 presons[i]
时间点之前的花谢的数目为 y
。则,presons[i]
时间点还处于开花状态的花数目为 x - y
。
x
即为 start <= presons[i]
的花朵数目,y
即为 end < presons[i]
的花朵数目。
根据以上的分析,我们可以单独统计起始时间 start
和 结束时间 end
,利用二分查找快速查找结果:
start
和 end
进行升序排序;x
,利用二分查找到 e n d i < = p e r s o n s [ i ] end_i <= persons[i] endi<=persons[i] 的花朵数目 y
,则第 i
个人可以看到的花朵数目为 x - y
;实现代码
class Solution {
public:
vector<int> fullBloomFlowers(vector<vector<int>>& flowers, vector<int>& people) {
vector<int> start, end;
for (auto flower : flowers) {
start.push_back(flower[0]);
end.push_back(flower[1]);
}
sort(start.begin(), start.end());
sort(end.begin(), end.end());
int m = people.size();
vector<int> res(m);
for(int i = 0; i < n; ++i) {
int x = upper_bound(start.begin(), start.end(), people[i]) - start.begin();
int y = lower_bound(end.begin(), end.end(), people[i]) - end.begin();
res[i] = x - y;
}
return res;
}
};
复杂度分析
时间复杂度: O ( ( n + m ) l o g n ) O((n+m)logn) O((n+m)logn), n n n 为数组 flowers
的长度, m m m 为数组 presons
的长度。对 start
,end
排序的时间复杂度为 O ( n l o g n ) O(nlogn) O(nlogn);对每个人进行二分查找的时间为 O ( l o g n ) O(logn) O(logn),有 m
个人,因此二分查找总时间复杂度为 O ( m l o g n ) O(mlogn) O(mlogn);所以总的时间复杂度为 O ( ( n + m ) l o g n ) O((n+m)logn) O((n+m)logn)。
空间复杂度: O ( n ) O(n) O(n),使用的额外空间为 start
和 end
数组,这两个数组排序使用的额外空间为 O ( l o g n ) O(logn) O(logn),因此总的空间复杂度为 O ( n ) + O ( l o g n ) = O ( n ) O(n) + O(logn) = O(n) O(n)+O(logn)=O(n)。
如果文章内容有任何错误或者您对文章有任何疑问,欢迎私信博主或者在评论区指出 。
如果大家有更优的时间、空间复杂度方法,欢迎评论区交流。
最后,感谢您的阅读,如果感到有所收获的话可以给博主点一个 哦。