package com.interview.geometry; import java.util.*; /** * Date 01/07/2016 * @author Tushar Roy * * Given skyline of a city merge the buildings * * Time complexity is O(nlogn) * Space complexity is O(n) * * References * https://leetcode.com/problems/the-skyline-problem/ * https://leetcode.com/discuss/67091/once-for-all-explanation-with-clean-java-code-nlog-time-space */ public class SkylineDrawing { /** * Represents either start or end of building */ static class BuildingPoint implements Comparable<BuildingPoint> { int x; boolean isStart; int height; @Override public int compareTo(BuildingPoint o) { //first compare by x. //If they are same then use this logic //if two starts are compared then higher height building should be picked first //if two ends are compared then lower height building should be picked first //if one start and end is compared then start should appear before end if (this.x != o.x) { return this.x - o.x; } else { return (this.isStart ? -this.height : this.height) - (o.isStart ? -o.height : o.height); } } } public List<int[]> getSkyline(int[][] buildings) { //for all start and end of building put them into List of BuildingPoint BuildingPoint[] buildingPoints = new BuildingPoint[buildings.length*2]; int index = 0; for(int building[] : buildings) { buildingPoints[index] = new BuildingPoint(); buildingPoints[index].x = building[0]; buildingPoints[index].isStart = true; buildingPoints[index].height = building[2]; buildingPoints[index + 1] = new BuildingPoint(); buildingPoints[index + 1].x = building[1]; buildingPoints[index + 1].isStart = false; buildingPoints[index + 1].height = building[2]; index += 2; } Arrays.sort(buildingPoints); //using TreeMap because it gives log time performance. //PriorityQueue in java does not support remove(object) operation in log time. TreeMap<Integer, Integer> queue = new TreeMap<>(); //PriorityQueue<Integer> queue1 = new PriorityQueue<>(Collections.reverseOrder()); queue.put(0, 1); //queue1.add(0); int prevMaxHeight = 0; List<int[]> result = new ArrayList<>(); for(BuildingPoint buildingPoint : buildingPoints) { //if it is start of building then add the height to map. If height already exists then increment //the value if (buildingPoint.isStart) { queue.compute(buildingPoint.height, (key, value) -> { if (value != null) { return value + 1; } return 1; }); // queue1.add(cp.height); } else { //if it is end of building then decrement or remove the height from map. queue.compute(buildingPoint.height, (key, value) -> { if (value == 1) { return null; } return value - 1; }); // queue1.remove(cp.height); } //peek the current height after addition or removal of building x. int currentMaxHeight = queue.lastKey(); //int currentMaxHeight = queue1.peek(); //if height changes from previous height then this building x becomes critcal x. // So add it to the result. if (prevMaxHeight != currentMaxHeight) { result.add(new int[]{buildingPoint.x, currentMaxHeight}); prevMaxHeight = currentMaxHeight; } } return result; } public static void main(String args[]) { int [][] buildings = {{1,3,4},{3,4,4},{2,6,2},{8,11,4}, {7,9,3},{10,11,2}}; SkylineDrawing sd = new SkylineDrawing(); List<int[]> criticalPoints = sd.getSkyline(buildings); criticalPoints.forEach(cp -> System.out.println(cp[0] + " " + cp[1])); } }