題目出處:https://leetcode.com/problems/the-skyline-problem/java
題目描述:node
輸入三元組
[Li, Ri, Hi]
,
python表明建築的左右座標,以及高度,構成圖A。
要求畫出天際線,如B圖所示,輸出爲
網絡[[x1,y1], [x2, y2], [x3, y3], ... ],一串二元組的集合,分別爲輪廓的左端點,x表明橫座標,y表明縱座標高度。
解題思路:
數據結構
思路一:函數
首先,看到這道題,最最簡單的思路是直接遍歷每一個x值,簡單粗暴正面剛,將每一個覆蓋x值的區間合併並求出最大值,從而畫出天際線,找到左端點。但這個思路顯然要跪,畢竟最早想到的90%都有問題,這個思路的空間複雜度爲O(m) ,時間複雜度爲O(nm)(m爲最大x值,n爲區間個數),因此放棄這個思路,繼續思考。學習
思路二:ui
一、經過簡單的分析,咱們能夠發現,對於天際線起做用的其實是建築的頂邊(就是上面那條邊,原諒我語文表達很差),而頂邊則是由左右端點肯定的,因此咱們只須要存儲每一個建築的左右端點。考慮如何存儲端點,應該將端點按照x座標值由小到大排序,同時若是x相等,對於左端點,按照高度由低到高;對於右端點,按照高度由高到低排序。考慮使用什麼數據結構存儲端點,因爲排序以及後面訪問端點時,須要知道是左端點仍是右端點。因此,咱們創建Node類,使用vector存儲點,同時重載sort函數進行排序(想到這裏,就以爲好麻煩有沒有(>﹏<))
spa
二、將端點排序儲存以後,咱們一個一個訪問端點。若是是左節點,就記錄高度信息,若是高度大於以前的高度,就說明這個節點是個拐點,記錄在ans(vector<pair<int, int>>類型)中;遇到右節點時,就須要將對應的左節點的高度刪掉。code
因而咱們須要存儲高度信息,並且還要實現按照大小排序的功能。到底用什麼數據結構呢?好蛋疼......set不能夠,可能會重複;vector沒有排序;經過查文檔,發現了priority_queue(好像老師上課講過,果真沒有好好聽課0.0),可是這個無法刪除,簡直B了狗了,可能還須要flag標記要刪除的信息,因而有須要map將flag映射到高度信息(果真是數據結構的題。。。。);繼續找,發現了multiset,就決定是你了!咱們用cur,pre分別表明當前及以前的最大高度,最終獲得ans,就是結果。
代碼以下:
class Solution { private: struct node { int x, y; string type; node(int _x, int _y, string _type) : x(_x), y(_y), type(_type) {} }; public: vector<pair<int, int>> getSkyline(vector<vector<int>>& buildings) { vector<node> height; for (int i = 0; i < buildings.size(); i++) { height.push_back(node(buildings[i][0], buildings[i][2], "LEFT")); height.push_back(node(buildings[i][1], buildings[i][2], "RIGHT")); } sort(height.begin(), height.end(),f); multiset<int> heap; heap.insert(0); vector<pair<int, int>> res; int pre = 0, cur = 0; for (int i = 0; i < height.size(); i++) { if (height[i].type == "LEFT") { heap.insert(height[i].y); } else { heap.erase(heap.find(height[i].y)); } cur = *heap.rbegin(); if (cur != pre) { res.push_back({height[i].x, cur}); pre = cur; } } return res; } static bool f(const node &a, const node &b) { if (a.x != b.x) return a.x < b.x; else if (a.type == "LEFT" && b.type == "LEFT") return a.y > b.y; else if (a.type == "RIGHT" && b.type == "RIGHT") return a.y < b.y; else return a.type == "LEFT"; } };
感受解釋的挺清楚的,就沒有加註釋(我不會告訴你實際上是由於我懶(¬_¬))。提交到OJ上,已經能夠AC。
原本呢,已經結束了,可是看了@bill_liu的博客,深受啓發,手動感謝@bill_liu,@GDP。借用朋神的思路,能夠將左節點的高度設爲負值加入到點的集合中,這樣就能夠區分左右結點,並且神奇的發現,這樣不須要重寫sort函數,也符合比較結果(將端點按照x座標值由小到大排序,同時若是x相等,對於左端點,按照高度由低到高;對於右端點,按照高度由高到低排序),這樣咱們不須要從新建Node類,只須要pair存儲點便可。
修正代碼:
class Solution { public: vector<pair<int, int>> getSkyline(vector<vector<int>>& buildings) { vector<pair<int, int>> height; for (int i = 0; i < buildings.size(); i++) { height.push_back({buildings[i][0], -buildings[i][2]}); height.push_back({buildings[i][1], buildings[i][2]}); } sort(height.begin(), height.end()); multiset<int> heap; heap.insert(0); vector<pair<int, int>> res; int pre = 0, cur = 0; for (int i = 0; i < height.size(); i++) { if (height[i].second < 0) { heap.insert(-height[i].second); } else { heap.erase(heap.find(height[i].second)); } cur = *heap.rbegin(); if (cur != pre) { res.push_back({height[i].first, cur}); pre = cur; } } return res; } };
代碼簡潔了許多,有沒有~
總結:
這個題確實很難,首先須要抽象出來線段,點,以及明確記錄的是什麼狀況下的點。還有就是選取合適的數據結構,包括使用正負區分左右端點。
嗯,就這樣了,(擼完大做業)有時間再想別的思路,晚安~~
同劉斌,爲什麼對於這道題,從OJ上的提交數據來看,用python,java等寫出來的運行速度比用C,C++寫出來的快得多?求大神解答
此博客中的內容均爲原創或來自網絡,不用作任何商業用途。歡迎與我交流學習,個人郵箱是lsa0924@163.com