Hard
题目描述
给你一个下标从 0 开始的二维整数数组 flowers,其中 flowers[i] = [starti, endi] 表示第 i 朵花会在 starti 时刻开始盛开,在 endi 时刻结束盛开(包含边界)。同时给你一个下标从 0 开始、大小为 n 的整数数组 people,其中 people[i] 是第 i 个人来看花的时刻。
请你返回一个大小为 n 的整数数组 answer,其中 answer[i] 是第 i 个人到达时刻,正处于盛开状态的花朵数目。
示例 1:
输入:flowers = [[1,6],[3,7],[9,12],[4,13]], people = [2,3,7,11]
输出:[1,2,2,2]
解释:
上图展示了每朵花的盛开时间和每个人的到达时间。
对每个人,我们返回他们到达时盛开的花朵数量。
示例 2:
输入:flowers = [[1,10],[3,3]], people = [3,3,2]
输出:[2,2,1]
解释:
上图展示了每朵花的盛开时间和每个人的到达时间。
对每个人,我们返回他们到达时盛开的花朵数量。
提示:
1 <= flowers.length <= 5 * 10^4flowers[i].length == 21 <= starti <= endi <= 10^91 <= people.length <= 5 * 10^41 <= people[i] <= 10^9
解题思路
解题思路
这道题的核心思想是:在时刻 t,盛开的花朵数量 = 已经开始盛开的花朵数量 - 已经结束盛开的花朵数量。
方法一:差分数组(推荐) 利用差分思想,在开花时间点 +1,在结束时间的下一个时间点 -1。然后通过前缀和还原每个时间点的花朵数量。由于时间范围很大,需要进行坐标压缩。
方法二:二分查找 分别提取所有花朵的开始时间和结束时间并排序。对于每个人的到达时间,通过二分查找:
- 找到已经开始盛开的花朵数量
- 找到已经结束盛开的花朵数量
- 两者相减得到当前盛开的花朵数量
这里采用二分查找的方法,时间复杂度更优且实现简单。
核心步骤:
- 分别提取并排序所有花朵的开始时间和结束时间
- 对每个人的到达时间,使用
upper_bound计算已开始和已结束的花朵数量 - 相减得到当前盛开的花朵数量
代码实现
class Solution {
public:
vector<int> fullBloomFlowers(vector<vector<int>>& flowers, vector<int>& people) {
vector<int> starts, ends;
// 提取开始和结束时间
for (auto& flower : flowers) {
starts.push_back(flower[0]);
ends.push_back(flower[1]);
}
// 排序
sort(starts.begin(), starts.end());
sort(ends.begin(), ends.end());
vector<int> result;
for (int time : people) {
// 已经开始盛开的花朵数量
int started = upper_bound(starts.begin(), starts.end(), time) - starts.begin();
// 已经结束盛开的花朵数量
int ended = upper_bound(ends.begin(), ends.end(), time - 1) - ends.begin();
result.push_back(started - ended);
}
return result;
}
};
class Solution:
def fullBloomFlowers(self, flowers: List[List[int]], people: List[int]) -> List[int]:
import bisect
# 提取开始和结束时间
starts = [flower[0] for flower in flowers]
ends = [flower[1] for flower in flowers]
# 排序
starts.sort()
ends.sort()
result = []
for time in people:
# 已经开始盛开的花朵数量
started = bisect.bisect_right(starts, time)
# 已经结束盛开的花朵数量
ended = bisect.bisect_right(ends, time - 1)
result.append(started - ended)
return result
public class Solution {
public int[] FullBloomFlowers(int[][] flowers, int[] people) {
List<int> starts = new List<int>();
List<int> ends = new List<int>();
// 提取开始和结束时间
foreach (var flower in flowers) {
starts.Add(flower[0]);
ends.Add(flower[1]);
}
// 排序
starts.Sort();
ends.Sort();
int[] result = new int[people.Length];
for (int i = 0; i < people.Length; i++) {
int time = people[i];
// 已经开始盛开的花朵数量
int started = UpperBound(starts, time);
// 已经结束盛开的花朵数量
int ended = UpperBound(ends, time - 1);
result[i] = started - ended;
}
return result;
}
private int UpperBound(List<int> arr, int target) {
int left = 0, right = arr.Count;
while (left < right) {
int mid = left + (right - left) / 2;
if (arr[mid] <= target) {
left = mid + 1;
} else {
right = mid;
}
}
return left;
}
}
var fullBloomFlowers = function(flowers, people) {
// 提取开始和结束时间
const starts = flowers.map(flower => flower[0]);
const ends = flowers.map(flower => flower[1]);
// 排序
starts.sort((a, b) => a - b);
ends.sort((a, b) => a - b);
const result = [];
for (const time of people) {
// 已经开始盛开的花朵数量
const started = upperBound(starts, time);
// 已经结束盛开的花朵数量
const ended = upperBound(ends, time - 1);
result.push(started - ended);
}
return result;
function upperBound(arr, target) {
let left = 0, right = arr.length;
while (left < right) {
const mid = Math.floor((left + right) / 2);
if (arr[mid] <= target) {
left = mid + 1;
} else {
right = mid;
}
}
return left;
}
};
复杂度分析
| 复杂度类型 | 复杂度 |
|---|---|
| 时间复杂度 | O(n log n + m log n) |
| 空间复杂度 | O(n) |
其中 n 是 flowers 数组的长度,m 是 people 数组的长度。
- 排序需要 O(n log n)
- 每次二分查找需要 O(log n),总共 m 次查询
- 空间复杂度主要用于存储开始和结束时间数组
相关题目
. Meeting Rooms II (Medium)