Set Intersection Size At Least Two

Hard Greedy

Problem

Given a list of intervals, find the minimum size of a set such that every interval contains at least two integers from that set.

  • 1 ≤ intervals.length ≤ 3000
  • intervals[i].length == 2
  • 0 ≤ starti < endi ≤ 10⁸

Example

Input: intervals = [[1,3],[1,4],[2,5],[3,5]]
Output: 3

Think of this as efficiently 'pinning' tasks on a timeline. We need 2 pins per task. 1. The algorithm sorts intervals by End Time Ascending (and Start Time Descending for ties). Sorted order: `[[1,3], [1,4], [3,5], [2,5]]`. 2. Processing `[1,3]`: We have no pins. We need 2. The greedy choice is to place them as late as possible to maximize overlap with future tasks. We place pins at `2` and `3`. (Set: `{2, 3}`). 3. Processing `[1,4]`: Our pins `{2, 3}` are both inside. Requirement met. Do nothing. 4. Processing `[3,5]`: Only pin `3` is inside. We need 1 more. Place it at the very end, `5`. (Set: `{2, 3, 5}`). 5. Processing `[2,5]`: Pins `{3, 5}` are inside. Requirement met. Result: 3.

Approach

Straightforward Solution

A brute-force approach would attempt to generate all subsets of integers within the range of the intervals and check if they satisfy the '2-point' condition. Given the range can go up to 10^8, this is computationally impossible.

Core Observation

To minimize the total points used, every point added must serve the maximum number of future intervals. Since intervals are processed from left to right (based on end time), the optimal position for any new point is the rightmost possible position (the end of the current interval), as this extends its 'reach' furthest into subsequent intervals.

Path to Optimal

The key recognition signals are 'intervals', 'minimum size', and 'overlap'. These indicate a Greedy approach based on sorting. The standard 'Activity Selection' pattern sorts by End Time. However, a naive sort fails on nested intervals like `[1, 5]` and `[2, 5]`. If we process `[1, 5]` first, we might pick points `{4, 5}` which satisfies both, OR `{1, 5}` which fails `[2, 5]`. The insight is to always process the *stricter* (shorter) constraint first. By sorting `[2, 5]` before `[1, 5]` (Start Time Descending tie-breaker), we ensure that satisfying the inner interval automatically satisfies the outer one.

Optimal Approach

1. Sort intervals by End Time Ascending. Tie-break with Start Time Descending. 2. Maintain variables `p1` and `p2` tracking the two largest points added so far. 3. Iterate through the sorted intervals. For each interval, check overlap with `p1` and `p2`. If 2 points overlap, continue. If 1 point overlaps, add `end`. If 0 points overlap, add `end-1` and `end`. Update `p1` and `p2` accordingly.

Time

O(N log N)

Sorting the intervals takes O(N log N). The subsequent pass checks each interval exactly once, performing O(1) operations per check.

Space

O(1)

O(1) auxiliary space. We only track the last two points (`p1`, `p2`) and a counter. We do not need to store the full set of points to determine the answer.

Pattern Spotlight

Greedy Algorithms (Interval Sorting)

When greedy decisions on intervals depend on overlaps, sort by End Time to linearize the timeline. Crucially, if End Times match, sort by Start Time *Descending* to process the most restrictive (shortest) intervals first.

Solution

1class Solution:
2 def intersectionSizeTwo(self, intervals: List[List[int]]) -> int:
3 intervals.sort(key=lambda x: (x[1], -x[0]))
4
5 p1, p2 = -1, -1
6 count = 0
7
8 for start, end in intervals:
9 if p1 >= start and p2 >= start:
10 continue
11
12 if p2 >= start:
13 count += 1
14 p1 = p2
15 p2 = end
16 else:
17 count += 2
18 p1 = end - 1
19 p2 = end
20
21 return count

Step-by-Step Solution

1

Sort Intervals by 'Earliest Finish, Latest Start'

3intervals.sort(key=lambda x: (x[1], -x[0]))

Objective

To order the intervals such that we handle the most constrained deadlines first.

Key Insight

Sorting by End Time (`x[1]`) allows us to make a greedy decision that is locally optimal (satisfying the current interval) and globally optimal (extending furthest right). The secondary sort by Start Time Descending (`-x[0]`) is the critical fix for **nested intervals**: it ensures that if interval A is inside interval B and they end together, we process the *inner* (A) first. Satisfying the stricter inner interval automatically satisfies the outer one.

Interview Quick-Check

Core Logic

The sort key `(x[1], -x[0])` orders by deadline, while prioritizing shorter/stricter intervals when deadlines clash.

Common Pitfalls & Bugs

Forgetting the negative sign on `x[0]` will cause the algorithm to fail on nested intervals like `[1,5]` and `[2,5]`.

2

Initialize State Trackers

5p1, p2 = -1, -1
6count = 0

Objective

To initialize the variables that track the locations of the two most recently placed points.

Key Insight

We don't need to store a generic array of points. We only care about the 'frontier'—the rightmost points that might overlap with the next interval. Initializing `p1` and `p2` to `-1` ensures they fall outside the range of any valid interval (since `start >= 0`), forcing the greedy logic to add points for the very first interval.

Interview Quick-Check

State & Boundaries

p1 and p2 act as a sliding window of 'active' points. p1 is the second-to-last point, p2 is the last point.

3

Execute Greedy Coverage Strategy

8for start, end in intervals:
9 if p1 >= start and p2 >= start:
10 continue
12 if p2 >= start:
13 count += 1
14 p1 = p2
15 p2 = end
16 else:
17 count += 2
18 p1 = end - 1
19 p2 = end

Objective

To iterate through the sorted intervals and add the minimum number of points required to satisfy the 2-point overlap constraint.

Key Insight

The greedy choice property applies here: if we need to add points, we should always add them at the *very end* of the current interval (`end` and `end-1`). This places them as far right as possible, maximizing the probability that they will also lie inside the *next* interval (which, due to sorting, starts later). We handle three cases: completely covered (do nothing), partially covered (add 1), or uncovered (add 2).

Interview Quick-Check

Core Logic

We prioritize point placement at `end` and `end-1`. This maximizes the 'reach' into future intervals.

Complexity

This linear scan works because the sorting guarantees we never need to look back at previous intervals.

State & Boundaries

When updating `p2` to a new value, the old `p2` becomes `p1`. Maintaining this order is crucial for the logic `p1 < p2`.

4

Return Total Set Size

21return count

Objective

To return the accumulated count of points added.

Key Insight

The `count` variable holds the minimum set size. Because we only added points when absolutely forced to (by the 2-point constraint) and always placed them optimally (furthest right), the result is guaranteed to be minimal.

Line Analysis

This solution has 6 Critical lines interviewers watch for.

Line 3 Critical
intervals.sort(key=lambda x: (x[1], -x[0]))

Sort intervals by end time ascending, then start time descending.

This specific sort order is the engine of the solution; it linearizes the problem by deadline and handles nested intervals by processing the strictest constraint first.

Line 15 Critical
p2 = end

Place the new point at the very end of the current interval.

This is the greedy choice that guarantees optimality; picking the rightmost possible point maximizes the chance of overlapping with the next interval.

Line 18 Critical
p1 = end - 1

Place the first new point at `end - 1`.

We pick `end - 1` instead of `start` because it is further right, offering better overlap potential while remaining distinct from `end`.

Line 19 Critical
p2 = end

Place the second new point at `end`.

Just as in the 1-point case, placing a point at the absolute boundary maximizes its utility for future deadlines.

Line 12 Critical
if p2 >= start:

Check if the interval is partially covered (contains only p2).

This condition identifies the case where the previous 'last' point is inside, but the 'second to last' fell short, requiring exactly one new point.

Line 14 Critical
p1 = p2

Shift the last point (p2) to become the second-to-last point (p1).

This maintains the invariant that `p1` and `p2` always track the two rightmost points in our set.

Line 9 Core
if p1 >= start and p2 >= start:

Check if the current interval is already fully covered by existing points.

If `p1` (the second-to-last point) is inside the interval, then `p2` (which is larger) is definitely inside too, satisfying the 2-point requirement.

Line 16 Core
else:

Handle the case where no existing points cover the interval.

If even `p2` is to the left of `start`, the interval is completely disjoint from our current set, forcing us to add 2 new points.

Line 5 Standard
p1, p2 = -1, -1

Initialize the last two chosen points to -1.

Setting these to -1 guarantees they do not satisfy the first interval (min start 0), correctly triggering the initial point placement logic.

Line 10 Standard
continue

Skip processing if the interval is already satisfied.

Greedy minimality requires us to add nothing if the constraints are already met.

Line 8 Standard
for start, end in intervals:

Iterate through each interval in the sorted list.

A single linear pass is sufficient after sorting because the 'future' intervals are guaranteed to end later.

Line 13 Standard
count += 1

Increment the count by 1.

We add exactly the minimum necessary points to satisfy the constraint.

Line 17 Standard
count += 2

Increment the count by 2.

We must add two distinct points to satisfy the problem constraint for this disjoint interval.

Line 21 Standard
return count

Return the calculated minimum set size.

After processing all intervals with optimal greedy choices, this count represents the global minimum.

Line 6 Basic
count = 0

Initialize the counter for the total points added.

This variable accumulates the answer as we make greedy decisions.

Test Your Understanding

Why is it sufficient to track only the last two points (`p1`, `p2`) instead of the entire set of chosen points?

Because we sort by End Time, any future interval must end *after* the current one. This implies its start time will likely be to the right of our older points. The only points with a chance of landing inside the next interval are the rightmost ones we just added.

Related Problems

Greedy pattern

Don't just read it. Drill it.

Reconstruct Set Intersection Size At Least Two from memory until it sticks. AlgoDrill blanks out key lines and makes you fill them back in, step by step.

Unlock the Set Intersection Size At Least Two drill

or drill a free problem