Geometric Search

幾何搜索

平衡搜索樹(BST)在幾何方面的應用,處理的內容變成幾何對象,像點,矩形。算法

先來看一維的狀況,一維的範圍搜索是後面的基礎,處理的對象是在一條線上的點。這是符號表的一個小拓展,多了範圍查找(range search) 和範圍計數(range count)即但願知道給定範圍上有哪些點或是有多少點。數據庫

用無序鏈表來實現的話,插入直接放開頭,可是範圍查找和範圍計數都須要正比於 N 的時間。用有序數組來實現的話,範圍計數的時間複雜度是 logN 級別,範圍查找是 R+logN 級別(範圍內有 R 個點),插入爲了維護有序性須要的時間也和 N 成正比。因此說,高效的實現得用 BSTs,插入和範圍計數的時間複雜度都是 logN,範圍查找的則是 R+logN。編程

範圍計數:數組

1d-range-count

範圍查找:性能

1d-range-search

line segment intersection

接着咱們到二維,設想有不少水平或垂直的線段,如今但願找到全部交點。atom

line-segment-intersection

遍歷檢查全部可能組合會是平方級別的複雜度,顯然是不可接受的,其實咱們能夠把問題轉成上面一維的狀況。spa

2d-sweep

方便起見,這邊的線段沒有重合啥七七八八的狀況。假想有一條垂直的線,從左掃到右,碰到水平線段的左端點就把該線段的 y 值放入 BST,碰到水平線段的右端點就把該線段的 y 值從 BST 中移除,碰到垂直線段就以該線段的上下端點爲區間在 BST 中作範圍計數。像上面的例圖,點 2 已經從 BST 中移除了,點 4 是第一個垂直線段,範圍查找發現有個點 1,也就發現了第一個交點。3d

kd trees

kd 樹是 BSTs 的拓展(k 個維度),能夠高效地處理空間中的點,十分靈活,在不少應用中頗有用。就算不是幾何問題,在數據庫中你可能想知道收入在 1m - 10m 且年齡在 40 - 50 歲的人有哪些,這種場景也好用。rest

如今的例子正式從一維拓展到二維,範圍查找和範圍計數從區間上升到矩形,即但願知道平面上有哪些點或是有多少點在查詢的矩形上。對象

2d-rectangle-range

一個可能的作法是把平面用網格劃分,比較理想情況下是這樣的:

2d-rectangle-range-grid-best

點的分佈比較均勻,每一個網格能夠對應一個鏈表來表示在其中的點。範圍查找的時候就檢查涉及到的網格,不用遍歷整個平面,從而提升搜索效率。

M * M 的網格,M 太大會浪費空間,過小每一個網格會有好多點,N 個點能夠考慮設爲 \(\sqrt []{N}\)。可是吧,對於幾何數據,彙集(clustering)是一個很常見的現象,無法均勻地分佈在網格上,好比像下面的地圖數據:

2d-rectangle-range-grid-problem

因此說,網格劃分不大適合咱們的應用場景,常常會有好多點在一格,否則開好多小格又會浪費空間。可是,空間劃分的想法是好的,咱們能夠用樹結構相似的遞歸把空間分紅兩半兩半。

2d-tree-anatomy

樹節點的鍵交替用 x,y 座標,左右孩子仍是原來那樣一個小一個大,在平面上來看就是點的上下或左右。

應用方面,舉了範圍查找和搜索最近鄰居兩個例子。

range search in a 2d tree

在 x 節點比較橫座標選左右,在 y 節點比較縱座標選擇上下,就是二分吧,一次砍掉一半搜索空間。

2d-tree-range-search

第一次點 1 不在查詢矩形裏,接着發現矩形在左邊,右子樹立刻整個不要;接着點 1 的左孩子點 3 也不在矩形裏,但 y 座標在區間裏因此上下都要搜索;點 4 和點 1 同樣,只要搜索左邊;如今找到點 5 在矩形裏,兩個孩子爲空結束搜索;繼續返回搜索點 3 的另外一邊,點 6 也只要搜索左邊,爲空結束,至此完成整個搜索過程。

典型狀況下,2d 樹的時間複雜度爲 R+logN (R 是在矩形中的點的個數),可是吧,最壞狀況下,即便樹是平衡的,複雜度也是 \(R+\sqrt []{N}\) (課程說相關證實超綱不提)。但是,對於不少實際應用來講,2d 樹易於實現並且也值得一用。

nearest neighbor search in a 2d tree

搜索過程相似,每次選擇查詢點所在的一邊,雖然最近的點也可能在另一邊,可是通常來講在同一邊的機率大,因此這裏採起這樣的選擇。

2d-tree-nearest-neighbor

一路找下來,更新離查詢點最近的距離和點。找到點 5 以後返回搜索點 4 的右邊,爲空繼續返回搜索點 6 的右邊,一樣爲空到了點 1 的右邊。這裏應該也能夠有是否剪枝的判斷,好像此次的編程做業就有這個,好比說如今點 5 到查詢點的距離明顯小於查詢點到點 1 所在垂直紅線的距離,那麼點 1 的右子樹就徹底能夠砍掉。

性能方面,典型狀況是 logN,最壞狀況是 N,我想了下,好比說點在圓上,而後查詢點在圓心這樣子。反正,通常來講,仍是很高效的。

interval search trees

原來一維上的研究對象是點,如今再來看看一維下的區間(線段),問題是查找和給定區間有交集的區間。

interval-search-anatomy

仍是用 BST 來表示,區間左端點爲鍵(這裏假定沒有重複的左端點),同時在節點裏保存後代最大的右端點。

interval-search-tree-anatomy

插入新的節點的時候,經過比較左端點來找到合適位置,而後看看路徑上最大右端點是否要更新。

查找的時候,節點有交集就直接返回(先說找到任一相交區間),否則就把查詢區間左端點和左孩子的最大右端點比較,前者比較大就能夠砍掉左子樹;後者比較大就須要查找左子樹。要注意的是,第二種狀況下,左子樹可能仍是沒有相交的區間,而後!這個時候的右子樹也是不用搜索的。

interval-search-tree-search-case2

要是查詢區間在左子樹沒有相交區間,右端點最大的那個區間的左端點 c 確定在前面,而右子樹的左端點又比左子樹的大,因此就像上圖那樣能夠不用找右邊了。

實現上,能夠用紅黑樹來保證對數級別的性能,找到全部相交區間的複雜度是 RlogN,就一共有 R 個相交的區間這樣。

retangle intersection

最後,把二維下的處理對象也升級爲矩形,但願知道平面上矩形相交的個數。課程說這個有實際的需求,大概是用來檢查電路板之類的,由於摩爾定律啊,平方級別的算法確定是不行的。

算法和檢查二維線段相交差很少,而後這邊假定 x,y 座標不重複。

retangle-intersection-sweep-line

相似的變成一維上的區間相交檢測,在 N 個矩形查找 R 個相交的複雜度是 NlogN + RlogN。

最後的最後,貼張概括:

geometric-search-summary

相關文章
相關標籤/搜索