Facebook面试题2 | The Building Outline

208 阅读5分钟

专栏 | 九章算法
网址 | www.jiuzhang.com

题目描述

根据城市建筑剪影图给出轮廓线坐标。

左图A是城市大楼的剪影图,右图B是轮廓线,红点便是要给出的轮廓线坐标。

Example:

图A中给出的大楼剪影坐标是:[ [2 9 10], [3 7 15], [5 12 12], [15 20 10], [19 24 8] ]

输出图B中的轮廓线坐标是:[ [2 10], [3 15], [7 12], [12 0], [15 10], [20 8], [24 0] ]

Input:

  1. 所有大楼都是在同一条水平线上(x轴)。
  2. 给出每栋大楼的坐标和高度 [Li, Ri, Hi], 其中 Li 和 Ri分别是大楼的左右两点坐标,Hi 是大楼的高度。0 <= Li, Ri <= INT_MAX, 0 < Hi <= INT_MAX, 并且 Li < Ri。
  3. 输入数据已经按照大楼的左坐标 Li 排序。

Output:

  1. 输出格式 [ [Xi, Yi] ],要求按照 Xi 排序。
  2. 只输出轮廓线图中的转折点。比如有这样两座大楼相重叠:[ [2, 6, 5], [3, 7, 5] ],只需输出 [ [2, 5], [7, 0] ]。

解题思路分析

  1. 暴力解法:由于输入已经按照大楼位置排好序了,所以我们可以直接获取每栋大楼的高度?但如果大楼A太矮小,被大楼B挡住了,那么大楼A的高度是不会出现在答案里的,所以这个方法不可行。那我们试试把区间拆开来,用扫描线的方法?似乎可行:按顺序扫一遍 x 轴上的每个 Li、Ri,然后判断当前这个大楼棱角要不要输出。时间复杂度是 O(n)。不过问题在于“判断当前坐标要否输出”,你不得不枚举每一种可能的情况(比如大楼A是不是在大楼B的阴影下啦,两座大楼高度是否一样啦,等等),然后写一大堆奇丑无比的 if-elif-else。姑且当你能枚举完所有情况(也没几种可能的情况?图样图森破~)并正确写完这一坨 if-else,但这样不仅耗时,而且代码的可读性可维护性都极差。

  2. 改进:我们真需要枚举每一种可能的情况吗?其实不用。由题意可知,当扫到某个大楼的坐标点 Li 时,对应的轮廓线高度并不一定是 Hi,比如这栋大楼太矮,被其他大楼挡住了,那么此时轮廓线的高度是其他大楼的高度。所以对于每一个 x 轴上的位置 Xi,对应的 Yi 是当前所有可能的大楼(就是所有满足 Li <= Xi < Ri 的大楼)里的最高高度。而且,并不是每一个 (Xi, Yi) 都需要输出,比如两座大厦重叠 Y[i] == Y[i-1],此时在轮廓线上并无区别,那么 (Xi, Yi) 便不需要输出。

  3. 如何获取当前所有可能的大楼高度里最高的那一个?这个问题等价于:如何获取一个动态变化的整数数组里的最大值。因为当扫过大楼的 Li 后就要将大楼高度 Hi 加入候选,而当扫过 Ri 后 Hi 便该从候选里剔除,所以这个整数数组是动态变化的。

a. 数组 + 排序:根据 Xi 的位置维护数组里的数值(扫过 Li 的时候添加新的 Hi,扫过 Ri 的时候删除对应的 Hi),对数组进行排序,数组的最后一个元素便是最大值,复杂度O(nlogn),加上外层循环每个 Xi,总复杂度是 O(n^2*logn)。

b. 优先队列(Priority Queue,在此题里就是大根堆):扫过 Li 的时候往优先队列里添加新的 Hi 和对应的 Ri (Hi, Ri),扫过 Ri 的时候不做任何处理。对于每个 Xi,先把当前队列顶部的所有过期高度(就是对于队列里的 (Hj, Rj),如果 Rj <= Xi,那么该高度就是过期高度)处理掉,然后再取队列顶部的元素,便是当前所有可能高度里最高的那一个。堆的插入、删除操作复杂度都是 O(logn),取堆顶元素复杂度是 O(1),所以复杂度是 O(logn),加上外层循环,总复杂度是 O(nlogn)。

参考代码

Building Outline 参考程序

面试官角度分析

这道题相对比较难,要想到扫描线+堆的方式才能够拿到offer.

如果能够会hashheap来优化堆的话,那么可以拿到strong hire.

相关LintCode练习

Number Of Airplanes In The Sky

Building Outline


推荐阅读



欢迎关注我的微信公众号:九章算法(ninechapter)。
精英程序员交流社区,定期发布面试题、面试技巧、求职信息等

九章算法,IT教育领域的深耕者
九章算法,IT教育领域的深耕者