Minimum Number of Arrows to Burst Balloons
Problem Statement
You are given an array points where points[i] = [xstart, xend] represents the horizontal diameter of one balloon. An arrow shot vertically at coordinate x bursts every balloon whose interval contains x. Return the minimum number of arrows needed to burst all balloons.
Input
An integer matrix points, where each row is the inclusive horizontal range [xstart, xend] covered by one balloon.
Output
An integer: the minimum number of vertical arrows required to burst every balloon.
Constraints
- •
1 <= points.length <= 10^5 - •
points[i].length == 2 - •
-2^31 <= xstart < xend <= 2^31 - 1
Examples
Example 1
points = [[10,16],[2,8],[1,6],[7,12]]
2Example 2
points = [[1,2],[3,4],[5,6],[7,8]]
4Example 3
points = [[1,2],[2,3],[3,4],[4,5]]
2Learning Objectives
- Recognise balloon bursting as choosing points that stab intervals.
- Sort intervals by end coordinate to make the safest earliest arrow choice.
- Prove why shooting at the first ending balloon does not lose optimality.
- Use overflow-safe comparators when endpoints can be near integer limits.
Intuition
Pattern Recognition
The signal is minimum arrows, burst all balloons, and each balloon is an interval on the x-axis. This is not a merge problem; we do not need the union of ranges. We need the fewest points such that every interval contains at least one chosen point. That is the interval stabbing pattern.
The trap is sorting by start and committing too early. The interval that ends first creates the tightest deadline: if we do not shoot by its end, it can never be burst later. So we sort by end, shoot at that end, and let the same arrow cover every later balloon whose start is still at or before that coordinate.
Why greedy works / proof sketch
Look at the remaining balloon with the smallest end r. Any valid solution must place some arrow inside it, at a coordinate x <= r. If that arrow also bursts another remaining balloon, that other balloon has start <= x <= r and end >= r because r is the smallest end among remaining intervals. Therefore moving the arrow to exactly r still bursts every balloon the old arrow burst. There is an optimal solution whose first arrow is at r, so the greedy choice is safe. After removing all balloons hit by that arrow, the same argument applies to the suffix.
Common mistakes
- ×Sorting by start time and shooting at the first start, which can choose an arrow too far left.
- ×Using subtractive comparator arithmetic and overflowing on extreme endpoints.
- ×Starting a new arrow when **start == arrowX**, even though endpoints are inclusive.
- ×Updating the arrow position while processing an overlapping balloon; the arrow should stay at the earliest end already chosen.
Algorithm Explanation
Key idea
Sort balloons by their right endpoint. The first balloon in that order must be hit no later than its end, so shoot an arrow exactly there. Every following balloon with start <= arrowX is also burst by that arrow. The first balloon with start > arrowX cannot be hit by the current arrow, so it starts a new group and we shoot at its end. Use Integer.compare(a[1], b[1]) for the sort because coordinates can be near Integer.MAX_VALUE or Integer.MIN_VALUE.
Interval walkthrough
Use points = [[10,16],[2,8],[1,6],[7,12]]. Sorted by end, the number line order is [1,6], [2,8], [7,12], [10,16]. Draw the first two as overlapping over coordinate 6: 1 ---- 6 ---- 8. Shoot at 6, so both [1,6] and [2,8] disappear. The next start is 7, which is to the right of 6, so draw the next cluster 7 ---- 12 ---- 16. Shoot at 12, and both [7,12] and [10,16] are hit. Two clusters mean two arrows.
Algorithm
- Sort points by end coordinate ascending with Integer.compare.
- Initialise arrows = 1 and arrowX to the end of the first sorted balloon.
- Scan the remaining balloons in sorted order.
- If points[i][0] <= arrowX, the current arrow bursts this balloon, so skip it.
- If points[i][0] > arrowX, shoot a new arrow at points[i][1] and increment arrows.
- Return arrows after the scan.
Solutions
Solution: Sort by end and shoot greedily
Use this canonical solution whenever the problem asks for the fewest points, arrows, or markers needed to cover intervals.
Sort by each balloon end coordinate. The first ending balloon forces the next arrow position, and placing the arrow at that end keeps it as far right as possible while still hitting the forced balloon.
Step-by-step
- Sort points by end ascending using Integer.compare.
- Place the first arrow at the first sorted balloon end.
- For each next balloon, compare its start with the current arrow coordinate.
- If the start is less than or equal to the arrow coordinate, the balloon is already burst.
- Otherwise, count a new arrow and place it at this balloon end.
- Return the number of arrows placed.
O(n log n)
O(n)
Sorting dominates the scan. Java sorts object arrays with temporary storage; the greedy scan itself is O(1) extra space.
Java implementation
Dry Run
Sample input
points = [[10,16],[2,8],[1,6],[7,12]]. After sorting by end: [[1,6],[2,8],[7,12],[10,16]].
| sorted balloon | arrow before | test | action | arrows |
|---|---|---|---|---|
| [1,6] | none | first balloon | shoot at 6 | 1 |
| [2,8] | 6 | 2 <= 6 | same arrow bursts it | 1 |
| [7,12] | 6 | 7 > 6 | shoot new arrow at 12 | 2 |
| [10,16] | 12 | 10 <= 12 | same arrow bursts it | 2 |
Every skipped balloon contains the current arrow coordinate. The scan creates exactly two non-overlapping arrow groups, so the answer is 2.
Interview Tips
State the greedy invariant clearly: arrowX is the end of the earliest-ending unburst balloon, and all skipped balloons contain that coordinate. Mention endpoint inclusivity, because start == arrowX is still a burst. Also call out the comparator: use Integer.compare(a[1], b[1]), never subtraction, because the problem allows extreme integer coordinates.
Likely follow-ups
- How would you return the actual arrow coordinates instead of only the count?
- What changes if balloon endpoints are open intervals instead of inclusive intervals?
- How would you solve it if balloons arrived as a stream and you could not sort first?
- How would the strategy change if each arrow had a width instead of being a single coordinate?
Similar Problems
Key Takeaways
- Minimum arrows is an interval stabbing problem, not a merge-output problem.
- Sorting by end exposes the earliest deadline among remaining balloons.
- Shooting at that end is safe by an exchange argument and maximises reuse of the arrow.
- Inclusive endpoints mean **start <= arrowX** is covered by the current arrow.