|
| 1 | +class Solution { |
| 2 | + private Map<int[], List<int[]>> graph; |
| 3 | + private Map<Integer, List<int[]>> nodesInComp; |
| 4 | + private Set<int[]> visited; |
| 5 | + |
| 6 | + // return whether two intervals overlap (inclusive) |
| 7 | + private boolean overlap(int[] a, int[] b) { |
| 8 | + return a[0] <= b[1] && b[0] <= a[1]; |
| 9 | + } |
| 10 | + |
| 11 | + // build a graph where an undirected edge between intervals u and v exists |
| 12 | + // iff u and v overlap. |
| 13 | + private void buildGraph(int[][] intervals) { |
| 14 | + graph = new HashMap<>(); |
| 15 | + for (int[] interval : intervals) { |
| 16 | + graph.put(interval, new LinkedList<>()); |
| 17 | + } |
| 18 | + |
| 19 | + for (int[] interval1 : intervals) { |
| 20 | + for (int[] interval2 : intervals) { |
| 21 | + if (overlap(interval1, interval2)) { |
| 22 | + graph.get(interval1).add(interval2); |
| 23 | + graph.get(interval2).add(interval1); |
| 24 | + } |
| 25 | + } |
| 26 | + } |
| 27 | + } |
| 28 | + |
| 29 | + // merges all of the nodes in this connected component into one interval. |
| 30 | + private int[] mergeNodes(List<int[]> nodes) { |
| 31 | + int minStart = nodes.get(0)[0]; |
| 32 | + for (int[] node : nodes) { |
| 33 | + minStart = Math.min(minStart, node[0]); |
| 34 | + } |
| 35 | + |
| 36 | + int maxEnd = nodes.get(0)[1]; |
| 37 | + for (int[] node : nodes) { |
| 38 | + maxEnd = Math.max(maxEnd, node[1]); |
| 39 | + } |
| 40 | + |
| 41 | + return new int[] {minStart, maxEnd}; |
| 42 | + } |
| 43 | + |
| 44 | + // use depth-first search to mark all nodes in the same connected component |
| 45 | + // with the same integer. |
| 46 | + private void markComponentDFS(int[] start, int compNumber) { |
| 47 | + Stack<int[]> stack = new Stack<>(); |
| 48 | + stack.add(start); |
| 49 | + |
| 50 | + while (!stack.isEmpty()) { |
| 51 | + int[] node = stack.pop(); |
| 52 | + if (!visited.contains(node)) { |
| 53 | + visited.add(node); |
| 54 | + |
| 55 | + if (nodesInComp.get(compNumber) == null) { |
| 56 | + nodesInComp.put(compNumber, new LinkedList<>()); |
| 57 | + } |
| 58 | + nodesInComp.get(compNumber).add(node); |
| 59 | + |
| 60 | + for (int[] child : graph.get(node)) { |
| 61 | + stack.add(child); |
| 62 | + } |
| 63 | + } |
| 64 | + } |
| 65 | + } |
| 66 | + |
| 67 | + // gets the connected components of the interval overlap graph. |
| 68 | + private void buildComponents(int[][] intervals) { |
| 69 | + nodesInComp = new HashMap<>(); |
| 70 | + visited = new HashSet<>(); |
| 71 | + int compNumber = 0; |
| 72 | + |
| 73 | + for (int[] interval : intervals) { |
| 74 | + if (!visited.contains(interval)) { |
| 75 | + markComponentDFS(interval, compNumber); |
| 76 | + compNumber++; |
| 77 | + } |
| 78 | + } |
| 79 | + } |
| 80 | + |
| 81 | + public int[][] merge(int[][] intervals) { |
| 82 | + buildGraph(intervals); |
| 83 | + buildComponents(intervals); |
| 84 | + |
| 85 | + // for each component, merge all intervals into one interval. |
| 86 | + List<int[]> merged = new LinkedList<>(); |
| 87 | + for (int comp = 0; comp < nodesInComp.size(); comp++) { |
| 88 | + merged.add(mergeNodes(nodesInComp.get(comp))); |
| 89 | + } |
| 90 | + |
| 91 | + return merged.toArray(new int[merged.size()][]); |
| 92 | + } |
| 93 | +} |
0 commit comments