diff --git a/DIRECTORY.md b/DIRECTORY.md index 104584cc0358..5c9cba5a461a 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -269,6 +269,7 @@ * [Index 2D Array In 1D](data_structures/arrays/index_2d_array_in_1d.py) * [Kth Largest Element](data_structures/arrays/kth_largest_element.py) * [Median Two Array](data_structures/arrays/median_two_array.py) + * [Merge Intervals](data_structures/arrays/merge_intervals.py) * [Merge Sorted](data_structures/arrays/merge_sorted.py) * [Monotonic Array](data_structures/arrays/monotonic_array.py) * [Pairs With Given Sum](data_structures/arrays/pairs_with_given_sum.py) diff --git a/data_structures/arrays/merge_intervals.py b/data_structures/arrays/merge_intervals.py new file mode 100644 index 000000000000..c4c41ae1f016 --- /dev/null +++ b/data_structures/arrays/merge_intervals.py @@ -0,0 +1,64 @@ +def merge_intervals(intervals: list[list[int]]) -> list[list[int]]: + """ + Merge all overlapping intervals. + + Each interval is represented as a list of two integers [start, end]. + The function merges overlapping intervals and returns a list of + non-overlapping intervals sorted by start time. + + Parameters: + intervals (list[list[int]]): A list of intervals. + + Returns: + list[list[int]]: A list of merged non-overlapping intervals. + + Edge Cases Handled: + - Empty list: returns [] + - Single interval: returns the interval itself + - Intervals already sorted or unsorted + - Fully overlapping intervals + - Invalid intervals (e.g., [[]] or intervals not having exactly + 2 integers) raise ValueError + + Examples: + >>> merge_intervals([[1, 3], [2, 6], [8, 10], [15, 18]]) + [[1, 6], [8, 10], [15, 18]] + >>> merge_intervals([[1, 4], [4, 5]]) + [[1, 5]] + >>> merge_intervals([[6, 8], [1, 3], [2, 4]]) + [[1, 4], [6, 8]] + >>> merge_intervals([]) + [] + >>> merge_intervals([[1, 4]]) + [[1, 4]] + + Time Complexity: + O(n log n) - sorting the intervals, where n is the number of intervals. + + Space Complexity: + O(n) - storing the merged intervals. + """ + + if not intervals: + return [] + + for interval in intervals: + msg = f"Each interval must have exactly 2 integers, got {interval}" + if len(interval) != 2: + raise ValueError(msg) + + # Sort intervals based on the start time + # Sort a copy so the caller's list (and its inner lists) are never mutated + sorted_intervals = sorted(intervals, key=lambda interval: interval[0]) + + merged: list[list[int]] = [sorted_intervals[0][:]] + + for current in sorted_intervals[1:]: + last = merged[-1] + + # If current interval overlaps with the last merged interval + if current[0] <= last[1]: + last[1] = max(last[1], current[1]) + else: + merged.append(current[:]) + return merged