1. 程式人生 > >資料結構Project 報告

資料結構Project 報告

資料結構Project 報告

毒液哥 Fudan University

問題描述與定義

維護一個二維平面上的資料結構,要求支援:

  1. 插入一個點
  2. 刪除一個點
  3. 插入一個多邊形
  4. 刪除一個多邊形
  5. 詢問一個點在哪些多邊形內部
  6. 詢問一個多邊形內部有哪些點

演算法設計流程

樸素演算法

若給定一個點和一個多邊形,我們知道可以使用射線法判斷該點是否在該多邊形中。

那麼很容易地,我們得到一個樸素的演算法:用vector記錄每個插入;用unordered_map記錄每個元素是否刪除;對於每個詢問點,暴力查詢插入過的未刪除的多邊形,用射線法判斷其是否在該多邊形中。

改進1-線段樹優化

若一個點在一個邊數為 n n 的多邊形中,則它在x軸上的投影(以下用投影代替)一定在多邊形的投影的內部。射線法的複雜度為 O ( n )

O(n) ,若能用不多於 O ( n ) O(n) 的時間排除一個投影不在多邊形投影中的點,演算法的時間效能可以提升。

使用線段樹維護,將一個點或多邊形根據其在的投影插入到線段樹中。詢問操作時先線上段樹中查詢到所有投影被包含的點,再分別射線法判斷。

由於線段樹只能維護整數座標,考慮將原座標放大 1 e 11 1e11 倍後取整。

改進2-將多邊形拆分成若干條線段

若要判斷一個點是否在一個邊數為 n n 的凸多邊形中,射線法的複雜度為 O ( n ) O(n) 。但一條射向y軸負方向的射線(以下用射線代替),只會經過該凸多邊形的一條邊,而射線法卻會判斷多邊形所有的邊。

若以一點為起點的射線,經過了多邊形的某條邊,則該點的投影一定在該邊的投影內部。

改進上一個演算法,將多邊形拆分成若干條邊(每條邊要儲存多邊形編號),根據其所在的投影插入到線段樹中。

每次查詢一個多邊形時,用一個unordered_map記錄每個點穿過了該多邊形幾次,將該多邊形每條邊根據其投影線上段樹上對點進行查詢。

查詢點同理,用一個unordered_map記錄每個多邊形被該點穿過了幾次,將該點根據其投影線上段樹上對每條線段進行查詢。

改進3-對原座標進行適當縮放

改進2演算法在本機執行時間約1分鐘,經過除錯發現,插入操作佔用約55秒。

考慮降低插入操作的用時。在改進2中,座標的範圍在經過縮放操作後達到2 ^ 60級別,這會導致線段樹層數達到60層,且線段樹節點必須用long long型別儲存。為了縮小座標範圍,我們直接將原座標取整。這會導致改進2中的性質——若以一點為起點的射線,經過了多邊形的某條邊,則該點的投影一定在該邊的投影內部——退化。由“投影內部”退化為“投影內部及附近”。因此,插入部分的效能會提高,但查詢部分的效能會降低。若想繼續提高插入部分效能,可以將座標繼續縮小,反之若想提高查詢部分效能,則要將座標放大。