Code Templates
Master interval scheduling patterns with these battle-tested code templates. Each template includes implementations in JavaScript, Python, Java, C++, Go, and Ruby with detailed explanations.
The “Main” Template: Activity Selection (Earliest Finish Time)
Problem: Select maximum number of non-overlapping intervals, choosing earliest finishing intervals first.
This template directly solves problems like Meeting Rooms and Non-overlapping Intervals.
function activitySelection(intervals) {
// Sort by finish time
intervals.sort((a, b) => a.end - b.end);
const result = [];
let lastEnd = -Infinity;
for (const interval of intervals) {
if (interval.start >= lastEnd) {
result.push(interval);
lastEnd = interval.end;
}
}
return result;
}def activity_selection(intervals):
# Sort by finish time
intervals.sort(key=lambda x: x[1])
result = []
last_end = float('-inf')
for interval in intervals:
if interval[0] >= last_end:
result.append(interval)
last_end = interval[1]
return resultpublic class Solution {
public List<int[]> activitySelection(int[][] intervals) {
// Sort by finish time
Arrays.sort(intervals, Comparator.comparingInt(a -> a[1]));
List<int[]> result = new ArrayList<>();
int lastEnd = Integer.MIN_VALUE;
for (int[] interval : intervals) {
if (interval[0] >= lastEnd) {
result.add(interval);
lastEnd = interval[1];
}
}
return result;
}
}vector<vector<int>> activitySelection(vector<vector<int>>& intervals) {
// Sort by finish time
sort(intervals.begin(), intervals.end(),
[](const vector<int>& a, const vector<int>& b) {
return a[1] < b[1];
});
vector<vector<int>> result;
int lastEnd = INT_MIN;
for (const auto& interval : intervals) {
if (interval[0] >= lastEnd) {
result.push_back(interval);
lastEnd = interval[1];
}
}
return result;
}func activitySelection(intervals [][]int) [][]int {
// Sort by finish time
sort.Slice(intervals, func(i, j int) bool {
return intervals[i][1] < intervals[j][1]
})
result := make([][]int, 0)
lastEnd := math.MinInt32
for _, interval := range intervals {
if interval[0] >= lastEnd {
result = append(result, interval)
lastEnd = interval[1]
}
}
return result
}def activity_selection(intervals)
# Sort by finish time
intervals.sort! { |a, b| a[1] <=> b[1] }
result = []
last_end = -Float::INFINITY
intervals.each do |interval|
if interval[0] >= last_end
result << interval
last_end = interval[1]
end
end
result
endCode Breakdown
Key Variables
lastEnd: Tracks the end time of the last selected intervalresult: Stores the selected non-overlapping intervalsintervals.sort(): Critical sorting by end time for greedy selection
Visual Algorithm Flow
Input: [1,4], [2,5], [6,8], [7,9]
Step 1: Sort by end time
Sorted: [1,4], [2,5], [6,8], [7,9]
Step 2: Greedy selection
lastEnd = -∞
Process [1,4]: 1 >= -∞ ✓ → Select [1,4], lastEnd = 4
Process [2,5]: 2 < 4 ✗ → Skip [2,5]
Process [6,8]: 6 >= 4 ✓ → Select [6,8], lastEnd = 8
Process [7,9]: 7 < 8 ✗ → Skip [7,9]
Result: [1,4], [6,8]Critical Sections
- Initialization: Set
lastEndto negative infinity to accept first interval - Expansion: Check if current interval can be added without overlap
- Contraction: Update
lastEndwhen interval is selected
Variations
Merge Intervals
For problems requiring interval merging:
Input: [1,3], [2,6], [8,10], [15,18]
Step 1: Sort by start time
Sorted: [1,3], [2,6], [8,10], [15,18]
Step 2: Merge overlapping intervals
result = [[1,3]]
Process [2,6]: 2 <= 3 ✓ → Merge → result = [[1,6]]
Process [8,10]: 8 > 6 ✗ → Add new → result = [[1,6], [8,10]]
Process [15,18]: 15 > 10 ✗ → Add new → result = [[1,6], [8,10], [15,18]]
Result: [1,6], [8,10], [15,18]function merge(intervals) {
if (intervals.length === 0) return [];
intervals.sort((a, b) => a.start - b.start);
const result = [intervals[0]];
for (let i = 1; i < intervals.length; i++) {
const current = result[result.length - 1];
const next = intervals[i];
if (current.end >= next.start) {
current.end = Math.max(current.end, next.end);
} else {
result.push(next);
}
}
return result;
}def merge(intervals):
if not intervals:
return []
intervals.sort(key=lambda x: x[0])
result = [intervals[0]]
for i in range(1, len(intervals)):
current = result[-1]
next_interval = intervals[i]
if current[1] >= next_interval[0]:
current[1] = max(current[1], next_interval[1])
else:
result.append(next_interval)
return resultpublic int[][] merge(int[][] intervals) {
if (intervals.length == 0) return new int[0][0];
Arrays.sort(intervals, Comparator.comparingInt(a -> a[0]));
List<int[]> result = new ArrayList<>();
result.add(intervals[0]);
for (int i = 1; i < intervals.length; i++) {
int[] current = result.get(result.size() - 1);
int[] next = intervals[i];
if (current[1] >= next[0]) {
current[1] = Math.max(current[1], next[1]);
} else {
result.add(next);
}
}
return result.toArray(new int[result.size()][]);
}vector<vector<int>> merge(vector<vector<int>>& intervals) {
if (intervals.empty()) return {};
sort(intervals.begin(), intervals.end());
vector<vector<int>> result = {intervals[0]};
for (size_t i = 1; i < intervals.size(); ++i) {
vector<int>& current = result.back();
const vector<int>& next = intervals[i];
if (current[1] >= next[0]) {
current[1] = max(current[1], next[1]);
} else {
result.push_back(next);
}
}
return result;
}func merge(intervals [][]int) [][]int {
if len(intervals) == 0 {
return [][]int{}
}
sort.Slice(intervals, func(i, j int) bool {
return intervals[i][0] < intervals[j][0]
})
result := [][]int{intervals[0]}
for i := 1; i < len(intervals); i++ {
current := result[len(result)-1]
next := intervals[i]
if current[1] >= next[0] {
current[1] = max(current[1], next[1])
result[len(result)-1] = current
} else {
result = append(result, next)
}
}
return result
}def merge(intervals)
return [] if intervals.empty?
intervals.sort! { |a, b| a[0] <=> b[0] }
result = [intervals[0]]
(1...intervals.length).each do |i|
current = result[-1]
next_interval = intervals[i]
if current[1] >= next_interval[0]
current[1] = [current[1], next_interval[1]].max
else
result << next_interval
end
end
result
endMeeting Rooms II (Minimum Resources)
For resource allocation problems:
Input: [0,30], [5,10], [15,20]
Step 1: Separate start and end times
starts: [0, 5, 15]
ends: [10, 20, 30]
Step 2: Two-pointer approach
rooms = 0, endIdx = 0
Process start 0: 0 < 10 ✓ → rooms = 1
Process start 5: 5 < 10 ✓ → rooms = 2
Process start 15: 15 >= 10 ✗ → endIdx = 1, rooms = 2
Process start 20: 20 >= 20 ✗ → endIdx = 2, rooms = 2
Result: 2 rooms neededfunction minMeetingRooms(intervals) {
const starts = intervals.map(i => i.start).sort((a, b) => a - b);
const ends = intervals.map(i => i.end).sort((a, b) => a - b);
let rooms = 0;
let endIdx = 0;
for (let start of starts) {
if (start < ends[endIdx]) {
rooms++;
} else {
endIdx++;
}
}
return rooms;
}def min_meeting_rooms(intervals):
starts = sorted(i[0] for i in intervals)
ends = sorted(i[1] for i in intervals)
rooms = 0
end_idx = 0
for start in starts:
if start < ends[end_idx]:
rooms += 1
else:
end_idx += 1
return roomspublic int minMeetingRooms(int[][] intervals) {
int[] starts = Arrays.stream(intervals).mapToInt(i -> i[0]).sorted().toArray();
int[] ends = Arrays.stream(intervals).mapToInt(i -> i[1]).sorted().toArray();
int rooms = 0;
int endIdx = 0;
for (int start : starts) {
if (start < ends[endIdx]) {
rooms++;
} else {
endIdx++;
}
}
return rooms;
}int minMeetingRooms(vector<vector<int>>& intervals) {
vector<int> starts, ends;
for (const auto& interval : intervals) {
starts.push_back(interval[0]);
ends.push_back(interval[1]);
}
sort(starts.begin(), starts.end());
sort(ends.begin(), ends.end());
int rooms = 0;
size_t endIdx = 0;
for (int start : starts) {
if (start < ends[endIdx]) {
rooms++;
} else {
endIdx++;
}
}
return rooms;
}func minMeetingRooms(intervals [][]int) int {
starts := make([]int, len(intervals))
ends := make([]int, len(intervals))
for i, interval := range intervals {
starts[i] = interval[0]
ends[i] = interval[1]
}
sort.Ints(starts)
sort.Ints(ends)
rooms := 0
endIdx := 0
for _, start := range starts {
if start < ends[endIdx] {
rooms++
} else {
endIdx++
}
}
return rooms
}def min_meeting_rooms(intervals)
starts = intervals.map { |i| i[0] }.sort
ends = intervals.map { |i| i[1] }.sort
rooms = 0
end_idx = 0
starts.each do |start|
if start < ends[end_idx]
rooms += 1
else
end_idx += 1
end
end
rooms
endPractice
Ready to apply these templates? Check out our practice problems to reinforce your understanding and master interval scheduling algorithms.
Pro Tip: Always sort intervals by end time for greedy algorithms and by start time for merging operations. This choice is crucial for optimal solutions.