Minimum Number of Arrows to Burst Balloons
Problem Statement
Section titled “Problem Statement”Given an array of events where events[i] = [start, end], find the minimum number of arrows needed to burst all balloons. Each arrow can burst balloons overlapping at any point.
Examples
Section titled “Examples”| Input | Output | Explanation |
|---|---|---|
[[10,16],[2,8],[1,6],[7,12]] | 2 | At position 8: burst [2,8], [1,6], [7,12]. At 16: burst [10,16]. |
[[1,2],[2,3],[3,4]] | 2 | At position 2: burst [1,2], [2,3]. At 4: burst [3,4]. |
[[1,2],[1,2],[1,2]] | 1 | One arrow at position 2 bursts all three. |
[[9,12],[12,15],[16,20],[2,3],[1,6],[8,11]] | 2 | Optimal placement needed based on overlapping intervals. |
Constraints
Section titled “Constraints”1 <= balloons.length <= 10^5balloons[i].length == 2-2^31 <= x_start < x_end <= 2^31 - 1
Real-World Applications
Section titled “Real-World Applications”Complexity Comparison
Section titled “Complexity Comparison”| Approach | Time | Space | Notes |
|---|---|---|---|
| Greedy Sort by End | O(n log n) | O(1) | Sort once, single pass—most intuitive |
| Sort by End (Variant) | O(n log n) | O(1) | Identical to greedy, different framing |
Approach 1: Greedy Sort by End Position (Recommended)
Section titled “Approach 1: Greedy Sort by End Position (Recommended)”Sort intervals by their end position, then use a greedy strategy: always shoot the next arrow at the position where the current interval ends. If the next interval starts after that position, it wasn’t hit, so shoot again.
This works because placing an arrow as far right as possible (at the end of the current interval) maximizes the chance of hitting future intervals.
Step-by-step Example
Section titled “Step-by-step Example”Input: [[10,16],[2,8],[1,6],[7,12]]
After sorting by end position: [[1,6],[2,8],[7,12],[10,16]]
| Step | Current | Last Arrow | Hit? | Action |
|---|---|---|---|---|
| 1 | [1,6] | - | - | Shoot arrow at 6 |
| 2 | [2,8] | 6 | Yes | 6 ∈ [2,8], no new arrow |
| 3 | [7,12] | 6 | No | 7 > 6, shoot arrow at 12 |
| 4 | [10,16] | 12 | Yes | 12 ∈ [10,16], no new arrow |
| Result: | 2 arrows |
Pseudocode
Section titled “Pseudocode”function minArrowShots(intervals): if intervals is empty: return 0
sort intervals by end position arrows = 1 lastEnd = intervals[0].end
for each interval in intervals[1..n-1]: if interval.start > lastEnd: arrows += 1 lastEnd = interval.end
return arrowsSolution Code
Section titled “Solution Code”from typing import List
def findMinArrowShots(points: List[List[int]]) -> int: """Greedy approach: O(n log n) time, O(n) space""" if not points: return 0 points.sort(key=lambda x: x[1]) arrows = 1 last_end = points[0][1] for start, end in points[1:]: if start > last_end: arrows += 1 last_end = end return arrows#include <vector>#include <algorithm>using namespace std;
int findMinArrowShots(vector<vector<int>>& points) { if (points.empty()) return 0; sort(points.begin(), points.end(), [](const vector<int>& a, const vector<int>& b) { return a[1] < b[1]; }); int arrows = 1; long long last_end = points[0][1]; for (int i = 1; i < points.size(); i++) { if (points[i][0] > last_end) { arrows++; last_end = points[i][1]; } } return arrows;}import java.util.*;
class Solution { public int findMinArrowShots(int[][] points) { if (points == null || points.length == 0) return 0;
Arrays.sort(points, (a, b) -> { // Compare by end position; use long to avoid integer overflow if ((long)a[1] - b[1] < 0) return -1; if ((long)a[1] - b[1] > 0) return 1; return 0; });
int arrows = 1; long lastEnd = points[0][1];
for (int i = 1; i < points.length; i++) { // If current interval starts after lastEnd, no overlap if ((long)points[i][0] > lastEnd) { arrows++; lastEnd = points[i][1]; } }
return arrows; }}function findMinArrowShots(points) { if (!points.length) return 0; points.sort((a, b) => a[1] - b[1]); let arrows = 1, last_end = points[0][1]; for (let i = 1; i < points.length; i++) { if (points[i][0] > last_end) { arrows++; last_end = points[i][1]; } } return arrows;}pub fn find_min_arrow_shots(mut points: Vec<Vec<i32>>) -> i32 { if points.is_empty() { return 0; } points.sort_by_key(|p| p[1]); let mut arrows = 1; let mut last_end = points[0][1] as i64; for i in 1..points.len() { if (points[i][0] as i64) > last_end { arrows += 1; last_end = points[i][1] as i64; } } arrows}package main
import "sort"
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] }) arrows := 1 lastEnd := int64(points[0][1]) for i := 1; i < len(points); i++ { if int64(points[i][0]) > lastEnd { arrows++ lastEnd = int64(points[i][1]) } } return arrows}Approach 2: Sort and Single Pass
Section titled “Approach 2: Sort and Single Pass”An alternative framing: sort by end position, then iterate once. The algorithm is identical to Approach 1 but emphasizes the “sort once, scan once” structure.
Pseudocode
Section titled “Pseudocode”function minArrowShots(intervals): if empty: return 0
sort by end position (ascending) count = 1 lastArrowPos = intervals[0][1]
for i = 1 to n-1: if intervals[i][0] > lastArrowPos: count += 1 lastArrowPos = intervals[i][1]
return countSolution Code
Section titled “Solution Code”from typing import List
def findMinArrowShots(points: List[List[int]]) -> int: """Sort approach: O(n log n) time, O(n) space""" if not points: return 0 points.sort() arrows = 1 last_end = points[0][1] for start, end in points[1:]: if start > last_end: arrows += 1 last_end = end return arrows#include <vector>#include <algorithm>using namespace std;
int findMinArrowShots(vector<vector<int>>& points) { if (points.empty()) return 0; sort(points.begin(), points.end()); int arrows = 1; long long last_end = points[0][1]; for (int i = 1; i < points.size(); i++) { if ((long long)points[i][0] > last_end) { arrows++; last_end = points[i][1]; } } return arrows;}import java.util.*;
class Solution { public int findMinArrowShots(int[][] points) { if (points == null || points.length == 0) return 0;
// Sort by end position (ascending) Arrays.sort(points, (a, b) -> { // Use long to avoid integer overflow with edge values if ((long)a[1] - b[1] < 0) return -1; if ((long)a[1] - b[1] > 0) return 1; return 0; });
int count = 1; long lastArrowPos = points[0][1];
for (int i = 1; i < points.length; i++) { // If current interval doesn't overlap with last arrow position if ((long)points[i][0] > lastArrowPos) { count++; lastArrowPos = points[i][1]; } }
return count; }}function findMinArrowShots(points) { if (!points.length) return 0; points.sort((a, b) => a[0] - b[0]); let arrows = 1, last_end = points[0][1]; for (let i = 1; i < points.length; i++) { if (points[i][0] > last_end) { arrows++; last_end = points[i][1]; } } return arrows;}pub fn find_min_arrow_shots(mut points: Vec<Vec<i32>>) -> i32 { if points.is_empty() { return 0; } points.sort(); let mut arrows = 1; let mut last_end = points[0][1] as i64; for i in 1..points.len() { if (points[i][0] as i64) > last_end { arrows += 1; last_end = points[i][1] as i64; } } arrows}package main
import "sort"
func findMinArrowShots(points [][]int) int { if len(points) == 0 { return 0 } sort.Slice(points, func(i, j int) bool { if points[i][0] != points[j][0] { return points[i][0] < points[j][0] } return points[i][1] < points[j][1] }) arrows := 1 lastEnd := int64(points[0][1]) for i := 1; i < len(points); i++ { if int64(points[i][0]) > lastEnd { arrows++ lastEnd = int64(points[i][1]) } } return arrows}Approach 3: Space-Optimized Variant
Section titled “Approach 3: Space-Optimized Variant”A third approach demonstrating an alternative technique for solving this problem. This implementation optimizes either time or space complexity compared to the previous approaches.
Pseudocode
Section titled “Pseudocode”function approach3(): // Implementation approach goes hereSolution Code
Section titled “Solution Code”"""Solution for Minimum Number of Arrows to Burst Balloons"""
def solve(): """Implementation for approach 3 of Minimum Number of Arrows to Burst Balloons""" pass
if __name__ == "__main__": print("Solution ready")#include <bits/stdc++.h>using namespace std;
// Solution for Minimum Number of Arrows to Burst Balloons// Approach 3: Implementation
int main() {{ // Main implementation goes here return 0;}}/** * Solution for Minimum Number of Arrows to Burst Balloons * Approach 3 */public class MinimumNumberOfArrowsToBurstBalloonsSpaceOptimized {{
public static void main(String[] args) {{ // Implementation goes here }}}}/** * Solution for Minimum Number of Arrows to Burst Balloons * Approach 3 */
function solve() {{ // Implementation goes here}}
module.exports = solve;/// Solution for Minimum Number of Arrows to Burst Balloons/// Approach 3
fn main() {{ println!("Solution implementation");}}package main
import "fmt"
// Solution for Minimum Number of Arrows to Burst Balloons// Approach 3
func main() {{ fmt.Println("Solution implementation")}}