Interval Intersection
What is this?
Two problems that both start from sorted intervals and go in opposite directions. The first reads an overlap out of two lists in a single pass. The second builds the smallest set of points touching every interval at least twice — a greedy that is short to write and needs an argument to defend.
💡 Fun fact: in the two-pointer pass, the decision of which pointer to advance has a one-line proof. Whichever interval ends first cannot possibly overlap anything later in the other list, because every later interval starts after this one's end. So it is finished, permanently, and advancing it can never skip an intersection. That single observation is what makes an
O(m + n)pass correct where a nested loop would beO(mn).
🔓 The 2 problems in this chapter are free. Sign in with Google or Microsoft to start solving.
The one-line idea: in both problems the interval that ends first is the one whose fate is already decided — so advance it, or serve it, and never look back.
1. Two pointers, one pass
i = j = 0
while i < len(A) and j < len(B):
lo, hi = max(A[i][0], B[j][0]), min(A[i][1], B[j][1])
if lo <= hi: out.append([lo, hi]) # non-empty overlap
if A[i][1] < B[j][1]: i += 1 # A[i] is finished
else: j += 1 # B[j] is finished
lo <= hi rather than lo < hi is deliberate: the endpoints are closed, so an overlap of a single point — [5,5] — is a real intersection and the problem wants it.
There is no merging and no sorting. Both inputs arrive sorted and internally disjoint, and the pass depends on exactly that.
2. Late placement, and a tie-break that matters
intervals.sort(key=lambda x: (x[1], -x[0])) # end ↑, start ↓
ans = []
for s, e in intervals:
if not ans or ans[-1] < s: # no chosen point inside
ans += [e - 1, e]
elif ans[-2] < s: # only the largest is inside
ans.append(e)
return len(ans)
Because intervals are processed by ascending end, ans stays sorted, so ans[-1] and ans[-2] are the two largest points chosen so far — the only two that could possibly fall inside the current interval. That is why counting "how many points already hit this interval" costs two comparisons rather than a scan.
Why place at e and e − 1? A point further right is inside every future interval that a point further left is inside, and possibly more. Placing as late as legality allows therefore never loses, which is the exchange argument the whole greedy rests on.
Why the descending start on ties? Among intervals sharing an end, the widest is processed first. Its two points then sit as far right as that interval allows, and the narrower intervals nested inside are likelier to already be covered.
3. A 30-second worked example (covering greedy)
intervals = [[1,3], [3,7], [8,9]] — already sorted by end.
[1,3] ans is empty → add 2 and 3 ans = [2, 3]
[3,7] ans[-1] = 3 ≥ 3, so one point is inside
ans[-2] = 2 < 3, so only one → add 7 ans = [2, 3, 7]
[8,9] ans[-1] = 7 < 8, none inside → add 8 and 9 ans = [2, 3, 7, 8, 9]
answer: 5
The interesting step is the middle one. [3,7] already contains the point 3, so it needs exactly one more — and putting it at 7 rather than at 4 is free here but decisive whenever another interval starts before 7 and ends after it.
4. Where you'll actually meet this
- Calendar systems. Intersecting two people's free/busy lists is the two-pointer pass verbatim.
- Media editing. Where two tracks' clip ranges overlap, computed without materialising either timeline.
- Monitoring. Correlating two incident windows across sorted event streams.
- Patrol and sensor placement. "Every window observed at least twice" is a genuine redundancy requirement.
- Database range queries. Intersecting sorted index ranges is the same single pass.
5. Problems in this chapter
▶ Interval List Intersections
All intersections of two sorted, disjoint interval lists. Emit [max(starts), min(ends)] when non-empty and advance whichever interval ends first.
Pattern: two pointers over sorted lists. Target: O(m + n) time, O(1) extra space.
▶ Set Intersection Size At Least Two
The smallest set of integers containing at least two from every interval. Sort by end ascending and start descending, then place points as late as each interval allows.
Pattern: greedy with an exchange argument. Target: O(n log n) time, O(1) extra space.
6. Common pitfalls 🚫
- Merging the lists first. They are already sorted and disjoint; merging adds work and loses the structure.
- Using
lo < hi. Endpoints are closed, so a single-point overlap counts. - Advancing both pointers after emitting, which skips intersections with the interval that has not finished.
- Sorting by start in the covering greedy. End ascending is what makes late placement safe.
- Dropping the descending start tie-break, which costs extra points when several intervals share an end.
- Placing points early. A later point dominates an earlier one for every future interval.
- Scanning the chosen set to count coverage, when only the two largest points can be inside.
7. Key takeaways
- The interval ending first is settled — that fact powers both algorithms.
[max(starts), min(ends)]is the intersection; inverted means empty.- Sorted input is the gift. Do not merge it away.
- Place greedy points as late as legality allows — later dominates earlier.
- The two largest chosen points are the only ones that can matter, which makes the coverage check
O(1). - A tie-break can be load-bearing. End ascending, start descending, and both are needed.
- Why interviewers like it: the code is short, so the entire evaluation is whether you can justify the sort key and the pointer move.
Order: Interval List Intersections → Set Intersection Size At Least Two.
Set Intersection Size At Least Two
Core idea: You must pick a set
Sof integers so that every interval contains at least two of them, andSshould be as small as possible. The winning move is to sort intervals by their right endpoint ascending (ties by left endpoint descending) and sweep left to right, always placing new points as far right as the current interval allows (at its end,bandb-1). Why far right? Because a high point sits inside the most future intervals — and since we process intervals in order of increasing end, every point you've already chosen is<= b_prev <= b_current, so any earlier point that is>= ais automatically inside the current interval. That collapses the whole "is this point inside?" question to a single comparison against the interval's left edge. Track just the two most recently chosen pointsp1 <= p2(the two largest). For each interval[a, b]: ifa <= p1, both are already inside — add nothing; else ifa <= p2, only one is inside — add one new point atb; else neither is inside — add two new points atb-1andb. That's it. O(n log n) for the sort, then a single linear pass.
The problem, rephrased
You're placing inspection checkpoints along a single highway (the integer number line). Regulators have handed you a list of road segments, each given as [start, end]. The rule: every segment must contain at least two checkpoints. Checkpoints are expensive, so you want to install the fewest total checkpoints that satisfy all segments at once. A single checkpoint can serve many segments if it falls inside all of them — so you want to place them cleverly, not segment-by-segment.
Formally: given an array intervals where intervals[i] = [a_i, b_i] represents the inclusive interval [a_i, b_i], find the minimum size of a set S of integers such that each interval contains at least two integers from S. ("Contains two from S" means at least two distinct integers x, y in S with a_i <= x, y <= b_i.)
This is LeetCode 757 — Set Intersection Size At Least Two.
| Input | Means | Output |
|---|---|---|
intervals = [[1,3],[1,4],[2,5],[3,5]] |
Four overlapping segments | 3 (e.g. S = {2, 3, 4}) |
intervals = [[1,2],[2,3],[2,4],[4,5]] |
Tight, partly disjoint segments | 5 (e.g. S = {1, 2, 3, 4, 5}) |
intervals = [[1,2]] |
One segment of width 1 | 2 (the only two integers, {1, 2}) |
The phrase "at least two" is the entire difficulty. The classic "stab every interval with one point" problem (minimum number of arrows / activity selection) is a clean greedy. Bumping the requirement from one to two means a single chosen point is no longer enough, and the bookkeeping of which two points are still live is what makes this a hard greedy.
Sign in to continue reading
The rest of this lesson is available with a free account. Signing in with Google or Microsoft is free.
Sign in to read the full lesson