Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 89 additions & 0 deletions problems/1288-remove-covered-intervals/analysis_daily_20260706.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
# 1288. Remove Covered Intervals

[LeetCode Link](https://leetcode.com/problems/remove-covered-intervals/)

Difficulty: Medium
Topics: Array, Sorting
Acceptance Rate: 57.0%

## Hints

### Hint 1

An interval is "covered" only in relation to *another* interval. Comparing every
pair is O(n²) and workable given the small constraints, but there is a cleaner
angle. When a problem asks you to reason about which intervals contain which,
think about what happens if you first put them in a predictable order. Which
sorting key would let you decide "covered or not" by looking only at what you've
already seen?

### Hint 2

Sort the intervals by their **start** value in ascending order. After sorting, if
interval `A` comes before interval `B`, then `A.start <= B.start`. That already
satisfies the first half of the coverage condition (`c <= a`). Now coverage
reduces to a single comparison on the **end** values. As you sweep left to right,
what single number do you need to remember to know whether the current interval is
swallowed by something earlier?

### Hint 3

Track the maximum end value seen so far. As you walk the sorted list, the current
interval is covered when its end is `<= maxEnd`; otherwise it survives and you
extend `maxEnd`. The one trap: two intervals sharing the same start. If `[1,4]`
and `[1,10]` are compared, `[1,4]` is covered by `[1,10]`, but a naive start-only
sort might place `[1,4]` first and wrongly count it. Fix this with the tie-break:
when starts are equal, sort by end in **descending** order so the longer interval
is processed first and correctly "eats" the shorter one.

## Approach

Sorting turns an all-pairs comparison into a single linear sweep.

1. **Sort** the intervals by start ascending. On ties (equal starts), sort by end
descending. This guarantees that for any interval we look at, every previously
seen interval has a start less than or equal to the current start, and among
equal starts the widest interval is seen first.

2. **Sweep** through the sorted list, keeping `maxEnd`, the largest end value among
all intervals processed so far. For each interval `[l, r]`:
- If `r <= maxEnd`, then some earlier interval starts no later (guaranteed by
the sort) and ends no earlier, so `[l, r]` is covered — skip it.
- Otherwise `[l, r]` is not covered; increment the surviving count and update
`maxEnd = r`.

3. Return the count of surviving intervals.

**Why the tie-break matters:** Consider `[[1,4],[1,10]]`. Both start at 1. If we
processed `[1,4]` first, `maxEnd` becomes 4, then `[1,10]` has end `10 > 4` and is
counted — giving 2. But `[1,4]` is actually covered by `[1,10]`, so the answer is
1. Sorting equal starts by end descending processes `[1,10]` first (`maxEnd = 10`),
then `[1,4]` has `4 <= 10` and is correctly discarded.

**Walkthrough on Example 1** `[[1,4],[3,6],[2,8]]`:
- Sorted (start asc, end desc): `[[1,4],[2,8],[3,6]]`.
- `[1,4]`: `4 > 0`, survives. count = 1, maxEnd = 4.
- `[2,8]`: `8 > 4`, survives. count = 2, maxEnd = 8.
- `[3,6]`: `6 <= 8`, covered. count stays 2.
- Result: **2**. ✅

## Complexity Analysis

Time Complexity: O(n log n) — dominated by the sort; the sweep is O(n).
Space Complexity: O(1) extra (or O(log n)–O(n) depending on the sort's internal
allocation), since we sort in place and keep only a couple of scalars.

## Edge Cases

- **Single interval:** `[[a, b]]` can never be covered by another — the answer is
always 1. The sweep handles this naturally.
- **Equal starts:** e.g. `[[1,4],[1,10]]`. Without the end-descending tie-break the
shorter interval is miscounted. This is the single most important case to get
right.
- **Identical/nested chains:** e.g. `[[1,2],[1,4],[1,6],[1,8]]` all share a start;
only the widest survives, so the answer is 1.
- **No coverage at all:** disjoint or partially overlapping intervals like
`[[1,2],[3,4],[5,6]]` — none is covered, so every interval counts.
- **Interval that shares an end but has a later start:** e.g. `[[2,8],[3,8]]`;
`[3,8]` is covered because `2 <= 3` and `8 <= 8`. The `<=` comparison (not `<`)
is essential here.
50 changes: 50 additions & 0 deletions problems/1288-remove-covered-intervals/problem.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
---
number: "1288"
frontend_id: "1288"
title: "Remove Covered Intervals"
slug: "remove-covered-intervals"
difficulty: "Medium"
topics:
- "Array"
- "Sorting"
acceptance_rate: 5696.8
is_premium: false
created_at: "2026-07-06T04:49:33.821768+00:00"
fetched_at: "2026-07-06T04:49:33.821768+00:00"
link: "https://leetcode.com/problems/remove-covered-intervals/"
date: "2026-07-06"
---

# 1288. Remove Covered Intervals

Given an array `intervals` where `intervals[i] = [li, ri]` represent the interval `[li, ri)`, remove all intervals that are covered by another interval in the list.

The interval `[a, b)` is covered by the interval `[c, d)` if and only if `c <= a` and `b <= d`.

Return _the number of remaining intervals_.



**Example 1:**


**Input:** intervals = [[1,4],[3,6],[2,8]]
**Output:** 2
**Explanation:** Interval [3,6] is covered by [2,8], therefore it is removed.


**Example 2:**


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




**Constraints:**

* `1 <= intervals.length <= 1000`
* `intervals[i].length == 2`
* `0 <= li < ri <= 105`
* All the given intervals are **unique**.
35 changes: 35 additions & 0 deletions problems/1288-remove-covered-intervals/solution_daily_20260706.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package main

import "sort"

// removeCoveredIntervals counts how many intervals remain after removing every
// interval that is fully covered by another.
//
// Approach: sort by start ascending, and on ties by end descending. After this
// sort, any earlier interval has a start <= the current start, so coverage
// depends only on the end value. Sweeping left to right while tracking the
// largest end seen (maxEnd), an interval is covered exactly when its end is
// <= maxEnd; otherwise it survives and extends maxEnd.
//
// Time: O(n log n) for the sort. Space: O(1) extra.
func removeCoveredIntervals(intervals [][]int) int {
sort.Slice(intervals, func(i, j int) bool {
if intervals[i][0] != intervals[j][0] {
return intervals[i][0] < intervals[j][0]
}
// Equal starts: put the wider interval (larger end) first so it can
// cover the narrower ones that follow.
return intervals[i][1] > intervals[j][1]
})

count := 0
maxEnd := 0
for _, iv := range intervals {
if iv[1] > maxEnd {
// Not covered by anything seen so far.
count++
maxEnd = iv[1]
}
}
return count
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package main

import "testing"

func TestRemoveCoveredIntervals(t *testing.T) {
tests := []struct {
name string
intervals [][]int
expected int
}{
{
name: "example 1: [3,6] covered by [2,8]",
intervals: [][]int{{1, 4}, {3, 6}, {2, 8}},
expected: 2,
},
{
name: "example 2: [2,3] covered by [1,4]",
intervals: [][]int{{1, 4}, {2, 3}},
expected: 1,
},
{
name: "edge case: single interval is never covered",
intervals: [][]int{{5, 9}},
expected: 1,
},
{
name: "edge case: equal starts, shorter is covered",
intervals: [][]int{{1, 4}, {1, 10}},
expected: 1,
},
{
name: "edge case: nested chain sharing a start",
intervals: [][]int{{1, 2}, {1, 4}, {1, 6}, {1, 8}},
expected: 1,
},
{
name: "edge case: disjoint intervals, none covered",
intervals: [][]int{{1, 2}, {3, 4}, {5, 6}},
expected: 3,
},
{
name: "edge case: shared end with later start is covered",
intervals: [][]int{{2, 8}, {3, 8}},
expected: 1,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := removeCoveredIntervals(tt.intervals)
if result != tt.expected {
t.Errorf("removeCoveredIntervals(%v) = %d, want %d", tt.intervals, result, tt.expected)
}
})
}
}