452. 用最少数量的箭引爆气球
就是求交集的数量【1,5】【2,6】【8,12】
输入:points = [[10,16],[2,8],[1,6],[7,12]]
输出:2
解释:对于该样例,x = 6 可以射爆 [2,8],[1,6] 两个气球,以及 x = 11 射爆【10,16】【7,12】
- 时间复杂度:O(n_log_n),其中 n 是数组points 的长度。排序的时间复杂度为O(n_log_n),对所有气球进行遍历并计算答案的时间复杂度为 O(n),其在渐进意义下小于前者,因此可以忽略。
-
解题思路1
和其他合并区间类的题目套路一样, 都是贪心思想, 先排序, 然后遍历检查是否满足合并区间的条件
这里判断是否有交叉区间, 所以其实是计算已知区间的交集数量.
这里以[[10,16],[2,8],[1,6],[7,12]] 为例子: 先排序, 我是按区间开始位置排序, 排序后: [[1,6],[2,8],[7,12],[10,16]]
- 遍历计算交叉区间(待发射箭头),
- 待发射箭头的区间range = [1, 6], 需要的箭数量 arrows = 1;
- 区间[2, 8], 和带发射区间[1, 6]有交集: 更新发射区域为它们的交集 range = [2, 6]
- 区间[7, 12], 和待发射区间[2, 6]没有任何交集, 说明需要增加一个新的发射区域, 新的待发射区域range = [7, 12]
- 区间[10,16], 和待发射区域[7, 12]有交集, 待发射区域更新为[10, 12]
- 返回需要待发射区间的个数
//也是比较后区间
func findMinArrowShots(points [][]int) int {
sort.Slice(points, func(i, j int) bool {
return points[i][1] < points[j][1]
})
count := 0 // 弓箭数
i := 0 // 遍历的指针
for i < len(points) {
right := points[i][1] // 当前区间的右端
i++ // 考察下一个区间
for i < len(points) && points[i][0] <= right { // 避免i越界
i++ // 只要重合,i就推进,继续寻求与下一个的重合
}
count++ // 给重合的团伙来一箭
}
return count
}
解题思路2
用区间的尾部排序貌似效率会更好, 因为已经保证后面的区间右侧都是大于当前区间, 所以将发射点设置在右侧边界, 后面的区间只有左边界比它更靠左,则可以被一起处理掉
这里换个example: [[10,16],[2,5],[1,6],[7,12]] 为例子:
- 先排序, 按区间结束位置排序, 排序后: [[2,5],[1, 6],[7,12],[10,16]]
遍历计算交叉区间,
- 发射点初始化为pos = 5, 需要的箭数量 arrows = 1;
- 区间[1, 6], 1 是小于5的, 在点5射箭可以干掉这个区间
- 区间[7, 12], 在5的位置射箭无法打掉, 说明需要增加一个新的发射点, 新的待发射点pos = 12
区间[10,16], 10 < 12那么在12位置射箭可以干掉它
//从右值开始比较; 排序 + 贪心 + 重叠区间 func findMinArrowShots(points [][]int) int { if len(points) == 0 { return 0 } sort.Slice(points, func(i, j int) bool { return points[i][1] < points[j][1] }) maxRight := points[0][1] ans := 1 for _, p := range points { if p[0] > maxRight { maxRight = p[1] ans++ } } return ans }