資料結構Project 報告
資料結構Project 報告
毒液哥 Fudan University
問題描述與定義
維護一個二維平面上的資料結構,要求支援:
- 插入一個點
- 刪除一個點
- 插入一個多邊形
- 刪除一個多邊形
- 詢問一個點在哪些多邊形內部
- 詢問一個多邊形內部有哪些點
演算法設計流程
樸素演算法
若給定一個點和一個多邊形,我們知道可以使用射線法判斷該點是否在該多邊形中。
那麼很容易地,我們得到一個樸素的演算法:用vector記錄每個插入;用unordered_map
記錄每個元素是否刪除;對於每個詢問點,暴力查詢插入過的未刪除的多邊形,用射線法判斷其是否在該多邊形中。
改進1-線段樹優化
若一個點在一個邊數為 的多邊形中,則它在x軸上的投影(以下用投影代替)一定在多邊形的投影的內部。射線法的複雜度為 ,若能用不多於 的時間排除一個投影不在多邊形投影中的點,演算法的時間效能可以提升。
使用線段樹維護,將一個點或多邊形根據其在的投影插入到線段樹中。詢問操作時先線上段樹中查詢到所有投影被包含的點,再分別射線法判斷。
由於線段樹只能維護整數座標,考慮將原座標放大 倍後取整。
改進2-將多邊形拆分成若干條線段
若要判斷一個點是否在一個邊數為 的凸多邊形中,射線法的複雜度為 。但一條射向y軸負方向的射線(以下用射線代替),只會經過該凸多邊形的一條邊,而射線法卻會判斷多邊形所有的邊。
若以一點為起點的射線,經過了多邊形的某條邊,則該點的投影一定在該邊的投影內部。
改進上一個演算法,將多邊形拆分成若干條邊(每條邊要儲存多邊形編號),根據其所在的投影插入到線段樹中。
每次查詢一個多邊形時,用一個unordered_map
記錄每個點穿過了該多邊形幾次,將該多邊形每條邊根據其投影線上段樹上對點進行查詢。
查詢點同理,用一個unordered_map
記錄每個多邊形被該點穿過了幾次,將該點根據其投影線上段樹上對每條線段進行查詢。
改進3-對原座標進行適當縮放
改進2演算法在本機執行時間約1分鐘,經過除錯發現,插入操作佔用約55秒。
考慮降低插入操作的用時。在改進2中,座標的範圍在經過縮放操作後達到2 ^ 60
級別,這會導致線段樹層數達到60層,且線段樹節點必須用long long
型別儲存。為了縮小座標範圍,我們直接將原座標取整。這會導致改進2中的性質——若以一點為起點的射線,經過了多邊形的某條邊,則該點的投影一定在該邊的投影內部——退化。由“投影內部”退化為“投影內部及附近”。因此,插入部分的效能會提高,但查詢部分的效能會降低。若想繼續提高插入部分效能,可以將座標繼續縮小,反之若想提高查詢部分效能,則要將座標放大。