# Merge overlapping intervals

Given N intervals S = {E_{1},E_{2},…..E_{n}} with each E_{i} has start time `s`

and end time _{i}`e`

. Some of these intervals can be overlapping, Just to clarify, E_{i}_{i} and E_{j} overlap when *start time* of E_{j} i.e `s`

is less than _{j}*end time* of E_{i} i.e `e`

. For example, [(1,3),(2,4),(5,8), (6,9)] should transform into [(1, 4),(5,9)] has interval (1,3) and (2,4) overlap and interval (5,8) and (6,9) also overlap._{i}

### Merge overlapping intervals : Thought process

As we always do, first try to come up with brute force solution, given enough time and space and money, how would you solve this?

Natural course is to take i^{th} interval and compare start time of all jth intervals with end time of i^{th}, if the start time of jth interval is less than the end time of i^{th} event, then you can merge two intervals. What should be end time for merged interval then? It should be maximum of end times of two merged intervals.

What will be time complexity of this approach? We are not using any additional space, however, worst case time complexity is O(n2). Can we do better?

What are two times we are comparing in brute force solution? It’s the start time of one interval with the end time of another. If we arrange input in a specific order, can we reduce processing some entries?

If we sort all intervals based on their start time, `s`

< _{i}`s`

<_{i+1}` s`

. Also, interval is always forward looking, _{i+2}`e`

> _{i}`s`

, _{i}`e`

> _{i+1}`s`

and so on._{i+1}

If `s`

is greater _{i}`e`

, then _{i-1}`s`

will be greater than _{i+1}`e`

, so no need to compare _{i-1}`s`

with _{i+1}`e`

, that is no need to go beyond immediate previous interval for any interval E_{i-1}_{i}. If s_{i} is less than `e`

, update _{i-1}`e`

with maximum of _{i-1}`e`

and _{i-1}`e`

and move to E_{i}_{i+1}.

Notice that we need last interval E_{i-1} to decide if to merge new interval into previous one or keep it as standalone. A stack is the best data structure to use. The algorithm will look like:

- Consider interval E
_{i}. - If stack is empty, push E
_{i}to stack. - If stack is not empty, then pop interval at top of stack call it E
_{i-1}. - Compare
`s`

, start time of E_{i}_{i}with`e`

, end time of E_{i-1}_{i-1}. - If
`s`

less than_{i}`e`

, update_{i-1}`e`

as max(_{i-1}`e`

,_{i-1}`e`

), as in maximum of end times of two intervals and push back E_{i}_{i-1}on to stack. - Else push E
_{i}on to stack. - Continue till all events are considered.
- At the end of processing, stack will contain all merged interval.

Let’s take an example and see how this algorithm works. We have following intervals and we have to merge overlapping intervals.

First of all, sort all interval based on their start time.

Create a stack, start with the first interval, since the stack is empty, we will push the first event on to the stack.

After pushing the first event, the problem state looks like this

Take the second interval, start time (2) of the second interval is less than the end time of the previous event on the stack (3), hence, find the maximum of end times of these two intervals and update the last interval with that end time and push back on to the stack.

Look at the third interval, the start time of it is greater than the end time of interval on top of the stack, just push interval on to the stack.

Last interval, this time, the start time of the new interval is less than the end time of interval on top of the stack.

Find the maximum of end times of two intervals and update the previous interval with that end time and push it back on to stack.

At this point, when there is no more interval remaining, stack contains all merged overlapping intervals.

## Merge overlapping intervals : Implementation

package com.company; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.Stack; /** * Created by sangar on 8.4.18. */ public class OverlappingIntervals { public static ArrayList<Interval> mergeOverlappingIntervals(ArrayList<Interval> intervals){ ArrayList<Interval> mergedIntervals = new ArrayList<>(); Stack<Interval> s = new Stack(); //Sort the ArrayList of interval based on start time. Collections.sort(intervals, Comparator.comparing(p -> p.getStartTime())); for(Interval currentInterval : intervals){ if(s.empty())s.push(currentInterval); else { Interval previousInterval = s.pop(); if(previousInterval.getEndTime() > currentInterval.getStartTime()){ /* If current interval's start time is less than end time of previous interval, find max of end times of two intervals and push new interval on to stack. */ int endTime = Integer.max(previousInterval.getEndTime(), currentInterval.getEndTime()); /* Notice that we have created new interval and did not update the old one This concept is called as immutability of class */ s.push(new Interval(previousInterval.getStartTime(), endTime)); } else{ s.push(previousInterval); s.push(currentInterval); } } } while(!s.empty()){ mergedIntervals.add(s.pop()); } return mergedIntervals; } public static void main(String[] args) { ArrayList<Interval> intervals = new ArrayList<>(); intervals.add(new Interval(1,3)); intervals.add(new Interval(2,4)); intervals.add(new Interval(5,8)); intervals.add(new Interval(6,9)); ArrayList<Interval> mergedIntervals = mergeOverlappingIntervals(intervals); for (Interval interval : mergedIntervals){ System.out.print("(" + interval.getStartTime() +"," + interval.getEndTime() + ")"); } } }

Complexity of algorithm to ** merge overlapping intervals** will be

`O(n log N)`

due to sorting with `O(n)`

extra space for stack and then copying into the list to return also takes `O(n)`

space.There is another way to implement the same function without using the stack, here we use the fact that ArrayList in Java is implemented using the array as the base and getting an element at a particular index should be `O(1)`

operation. The code looks more or less the same, however, there is no traversal of the stack at the end to create the list to return.

public List<Interval> mergeOptimized(List<Interval> intervals) { if(intervals.size() == 0) return intervals; Collections.sort(intervals, (Interval a, Interval b) -> a.getStartTime() - b.getStartTime()); List<Interval> mergedIntervals = new ArrayList<Interval>(); for(Interval interval : intervals){ /*If the merged list is empty add the interval to it or check if the last interval in merged list overlaps /*Remember the get function on ArrayList is O(1) operation because Arraylists in Java are backed by arrays */ if(mergedIntervals.isEmpty() || mergedIntervals.get(mergedIntervals.size()-1).getEndTime() < interval.getStartTime() ){ mergedIntervals.add(interval); } else { int lastEndTime = Math.max( mergedIntervals.get(mergedIntervals.size()-1).getEndTime(), interval.getEndTime() ); mergedIntervals.get(mergedIntervals.size()-1).setEndTime(lastEndTime); } } return mergedIntervals; }

You can use the above snippet of code to submit for this leetcode problem and it should be accepted.

Please share if there is something missing or wrong. Also, please reach out to us at communications@algorithmsandme.com if you want to contribute to the website and help others to learn by sharing your knowledge. If you are preparing for an interview and need some coaching to prepare for it, please sign up for the free session with us.