一支弓箭可以沿着 x 轴从不同点完全垂直地射出。在坐标 x 处射出一支箭,若有一个气球的直径的开始和结束坐标为 xstart,xend, 且满足 xstart ≤ x ≤ xend,则该气球会被引爆。可以射出的弓箭的数量没有限制。 弓箭一旦被射出之后,可以无限地前进。我们想找到使得所有气球全部被引爆,所需的弓箭的最小数量。
输入:points = [[10,16],[2,8],[1,6],[7,12]]
输出:2
解释:对于该样例,x = 6 可以射爆 [2,8],[1,6] 两个气球,以及 x = 11 射爆另外两个气球
题解:先排序,然后遍历检查是否满足合并区间的条件,判断是否有交叉区间,计算已知区间的交集数量。
排序后结果:【【1,6】,【2,8】,【7,12】,【10,16】】
1.待发射箭头的区间range=【1,6】,需要的箭的数量arrow=1;
2.区间【2,8】和待发射区间【1,6】有交集,更新发射区域为它们的交集range为【2,6】
3.区间【7,12】和待发射区间【2,6】没有任何交集,说明需要增加一个新的发射区域,新的发射区域range=【7,12】
4.区间【10,16】和待发射区域【7,12】有交集,待发射区域更新为【10,12】
代码如下:
class Solution {
public:
int findMinArrowShots(vector<vector<int>>& points) {
// 对于empty或只有一个区间, return;
if (points.size() <= 1)
return points.size();
// 按照区间开始位置排序
sort(points.begin(), points.end(),
[](const vector<int> &a, const vector<int> b) {
return a[0] == b[0] ? a[1] <= b[1] : a[0] < b[0];
});
int arrows = 1;
// 初始化待发射区为[points[0][0], points[0][1]] ;
vector<int> range = {points[0][0], points[0][1]};
for (int i = 1; i < points.size(); i++) {
auto curr = points[i];
// 当前区间和待发射区间有交集, 更新交叉区间
if (curr[0] <= range[1]) {
range[0] = max(range[0], curr[0]);
range[1] = min(range[1], curr[1]);
} else {
// 没有交集, 增加箭头数量, 将待发射区间设置为当前区间
++arrows;
range[0] = curr[0];
range[1] = curr[1];
}
}
return arrows;
}
};
第二种思路:
用区间的尾部排序效率更高,因为已经保证后面区间的区间右侧都是大于当前区间的,所以将发射点设置在右侧边界,当后面的区间左边界比它更靠左时,则可以一起被处理掉。
这里换个example: [[10,16],[2,5],[1,6],[7,12]] 为例子:
先排序, 按区间结束位置排序, 排序后: [[2,5],[1, 6],[7,12],[10,16]]
遍历计算交叉区间,
1.发射点初始化为pos = 5, 需要的箭数量 arrows = 1;
2.区间[1, 6], 1 是小于5的, 在点5射箭可以干掉这个区间
3.区间[7, 12], 在5的位置射箭无法打掉, 说明需要增加一个新的发射点, 新的待发射点pos = 12
4.区间[10,16], 10 < 12那么在12位置射箭可以干掉它
代码如下:
class Solution {
public:
int findMinArrowShots(vector<vector<int>>& points) {
if (points.size() <= 1) {
return points.size();
}
sort(points.begin(), points.end(), [](const vector<int>& a, const vector<int>& b) {
return a[1] < b[1];
});
// 发射点设置为区间最右侧的点
int pos = points[0][1];
int arrows = 1;
for (int i = 1; i < points.size(); i++) {
auto curr = points[i];
if (curr[0] > pos) {
pos = curr[1];
++arrows;
}
}
return arrows;
}
};