# Non-overlapping Intervals

**1. Clarify the problem:**
Before starting, let's ask some clarifying questions to ensure we fully understand the problem requirements:

- Are the intervals given in sorted order or can they be in any order?
- Can intervals overlap partially or only completely?
- Can the start and end points of intervals be negative?

**2. Analyze the problem:**
Let's break down the problem to better understand its components:

- Input: A collection of intervals represented as pairs of start and end points.
- Output: The minimum number of intervals to remove to make the rest of the intervals non-overlapping.
- Constraints: The number of intervals can range from 0 to 1000. The start and end points of intervals are integers.

**3. Design an algorithm:**
To solve this problem efficiently, we can follow these steps:

- Sort the intervals based on their end points in ascending order.
- Initialize a variable
`end`

with the end point of the first interval. - Iterate through the remaining intervals and check if the start point of the current interval is less than
`end`

.- If it is, increment a counter
`count`

and skip to the next interval. - If it is not, update
`end`

to the end point of the current interval.

- If it is, increment a counter
- Return the value of
`count`

.

**4. Explain your approach:**
We will sort the intervals based on their end points because by doing so, we can ensure that the first interval with the smallest end point is included in the solution. Then, we iterate through the intervals from the second one onwards and check if the start point of the current interval overlaps with the previous interval's end point. If it does, we increment the count and skip to the next interval. If it doesn't overlap, we update the end point to the current interval's end point and continue.

**5. Write clean and readable code:**

Here's an implementation of the algorithm in Python:

python

```
def eraseOverlapIntervals(intervals):
if len(intervals) == 0:
return 0
intervals.sort(key=lambda x: x[1])
end = intervals[0][1]
count = 0
for i in range(1, len(intervals)):
if intervals[i][0] < end:
count += 1
else:
end = intervals[i][1]
return count
```

**6. Test your code:**
Now, let's test the code with some example test cases to verify its correctness:

python

```
# Test case 1
intervals = [[1, 2], [2, 3], [3, 4], [1, 3]]
print(eraseOverlapIntervals(intervals)) # Output: 1
# Test case 2
intervals = [[1, 2], [1, 2], [1, 2]]
print(eraseOverlapIntervals(intervals)) # Output: 2
# Test case 3
intervals = [[1, 2], [2, 3]]
print(eraseOverlapIntervals(intervals)) # Output: 0
# Test case 4
intervals = [[1, 100], [11, 22], [1, 11], [2, 12]]
print(eraseOverlapIntervals(intervals)) # Output: 2
```

**7. Optimize if necessary:**
The given solution already has a time complexity of O(n log n) due to the sorting step. This is the most optimal approach for this problem.

**8. Handle error cases:**
In the code implementation, we handle the case where the input collection is empty by checking its length and returning 0 as there are no intervals to remove.

**9. Discuss complexity analysis:**

- Time complexity: The time complexity of the solution is dominated by the sorting step, which has a complexity of O(n log n), where n is the number of intervals. The subsequent iteration through the sorted intervals takes O(n), resulting in an overall time complexity of O(n log n).
- Space complexity: The space complexity is O(1) as we only use a constant amount of extra space for the variables
`end`

and`count`

. The sorting is performed in-place, so it doesn't contribute to the space complexity.