1. 程式人生 > >【Algorithms公開課學習筆記10】 符號表part3——平衡搜尋樹的應用

【Algorithms公開課學習筆記10】 符號表part3——平衡搜尋樹的應用

Geometric Application of BSTs 平衡搜尋樹的幾何應用

0. 前言

在前面的文章中,我們分析了符號表的許多基本操作,包括:查詢、插入、刪除等。現在我們新增兩個操作:範圍查詢(range search)範圍統計(range count)。這兩個操作中資料的SQL查詢語句很常見。

1. 一維範圍搜尋

基本概念

一維查詢的幾何解釋如下:(結合下圖)

  • key是作為一維線段上的點
  • 查詢/統計包含在一維區間上的點(一維區間最大就是一維線段)

這就是範圍查詢和範圍統計的應用範圍,因此,符號表可以用來實現一維查詢。

基本操作

在前面的文章中也分析過,通過無序連結串列和有序陣列來實現符號表存在效能問題,而通過二叉搜尋樹(BST)來實現才能滿足 ~lgN的效能要求。

  • 無序連結串列:插入很快,但查詢需要遍歷連結串列
  • 有序陣列:由於插入需要確定次序導致很慢,但查詢可以使用二分法使得效能達到lgN

因此,我們將採用BST來實現一維查詢。

一維統計

//統計lo和hi之間的節點個數
public int size(Key lo, Key hi){
    //rank返每個節點的有序序列的排位
    if (contains(hi)) return rank(hi) - rank(lo) + 1;
    else return rank(hi) - rank(lo);
}

效能:查詢到lo的路徑+查詢到hi的路徑 ~lgN。

一維查詢

一維查詢是查詢在lo和hi之間的所有所有節點。

  • 遞迴地查詢左子樹的所有key
  • 查詢當前節點的的key
  • 遞迴地查詢右子樹的所有key(儲存滿足條件的key)

效能:查詢到lo的路徑+查詢到hi的路徑+匹配的節點數 ~lgN+R (R就是匹配的節點數)

2. 線段相交

線段相交就是在一個二維平面內,查詢所有相交的線段。傳統的方法就是兩兩對比,確定是否相交,但這種方法需要平方級的時間要求。下面介紹一個高效能的方法。

掃描法

掃描法的基本原理:在二維平面內,用一條垂直的線從左到右進行掃描。x座標作為觸發事件,事件如下:

  • 對水平的線段,當遇到線段的左端點時,將y座標插入二叉搜尋樹中
  • 對水平的線段,當遇到線段的右端點時,將y座標從二叉搜尋樹中刪除
  • 對垂直的線段,當遇到端點時,將線段的上下端點作為查詢範圍,進行二叉搜尋樹的範圍查詢,查詢的結果就是與該線段相交的其他線段(結合下圖)

通過掃描法,就可以將二維的線段相交的問題轉換成一維查詢的問題。在N條線段存在R個相交點的場景下,時間效能表現為 ~NlgN+R

3. kd樹

分析kd樹首先要明白二維正交範圍查詢(2-d orthogonal range search)的背景和基本概念。

二維正交範圍查詢

二維正交範圍查詢的幾何解釋為:(結合下圖)

  • 可以作為二維平面空間上的點
  • 查詢/統計包含在二維矩形內的點(二維矩形在二維平面空間內)

網格實現法

網格實現法是二維正交範圍查詢的最佳實現方法,網格實現法的描述如下:(結合下圖)

  • 將二維平面空間分割成 M*M 個網格
  • 對每一個網格都建立一個列表,來存在落在該網格的節點
  • 使用二維陣列來直接索引相關的網格方塊
  • 插入:根據新節點的座標索引到閘道器,再插入到網格對應的列表
  • 範圍查詢:只查詢與二維矩形相重疊的閘道器方塊對應的節點列表即可

空間效能:M*M+N(M表示網格,N表示節點) 時間效能:平均每個網格的查詢時間為 1+ N/M^2

因此,對M的選取是非常關鍵的。M太大網格就會小,導致浪費空間;M太小網格就會大,導致太多節點擠在同一個網格。理論上,最佳為M = √N。

kd樹

在網格實現法中,存在一個不可忽視的問題——節點聚簇。當節點聚簇的時候,像上述那樣均分網格就會導致節點擠在一小部分網格中(最終導致該網格對應的列表過長)。此時,需要一種合理地切分網格的方法,就是接下來要重點分析的kd樹法。

kd樹法就是遞迴地將空間分成兩部分。與BST不同的是,kd樹需要交替地使用x座標和y座標作為key。使用x座標為key時就垂直切分,使用y座標作為key時就水平切分。

範圍查詢

  • 檢查節點是否在矩形內
  • 根據矩形的位置,判斷往哪個方位繼續查詢

在下圖中,節點1不在矩形內,根據位置需要查詢左子節點3,忽略右子樹全部節點。接著發現矩形橫跨了節點3的分割線,所以節點3的左右節點均要查詢。以此類推。

時間效能:平均情況下是R+lgN,最壞情況下是R+√N

最鄰近節點

查詢與查詢節點最近的節點

  • 計算節點與待查節點的距離l
  • 分別計算左右子節點與待查節點的距離,如果出現小於l的則往該子節點路徑走(有可能左右子節點都符合條件),如果大於l的就忽略

時間效能:平均情況下是lgN,最壞情況時N

kd樹也可以推廣到三維立體空間中,本文不作詳述。

4. 區間搜尋樹interval search trees

基本概念

在一維區間查詢中,有一個很重要的操作就是一維區間相交查詢:給定一個區間(lo, hi),查找出於該區間相交的其他區間。(結合下圖)

所謂的區間查詢樹,就是樹的每一個節點存的是一個區間:(結合下圖)

  • 使用左端點作為節點的key(故比較物件是左端點)
  • 每一個節點要儲存以其為根的子樹中的最大key值(包括節點本身)

基本操作

插入

以插入(lo, hi)為例

  • 左端點lo作為key值,比較key值後確定其位置,然後插入
  • 更新每一個節點所儲存的最大key值

查詢

查詢是指查找出與給定節點相交的其他節點,以查詢節點(lo, hi)為例

  • 如果節點相交,則返回
  • 如果左子樹為空,往右邊走
  • 如果左子樹最大key值小於lo,往右邊走
  • 其他情況往左邊走
//根節點
Node x = root;
while (x != null){
    if (x.interval.intersects(lo, hi)) return x.interval;//存在相交,返回
    else if (x.left == null) x = x.right;//如果左子樹為空
    else if (x.left.max < lo) x = x.right;//如果左子樹最大key值小於lo
    else x = x.left;
}
return null

這麼比較是因為有以下特殊情況:

  • 如果往右邊走,則表示一定不會在左邊相交
  • 如果往左邊走,仍有可能會在右邊相交,或者兩邊都不相交

為了保證時間效能,可以採用紅黑樹來實現區間搜尋樹,這樣就可以將時間效能保持在 ~lgN的水平,本文不作詳述。

5. 矩形相交

矩形相交是在一個二維平面內,查詢所有相交的矩形。傳統的方法就是兩兩對比,確定是否相交。但這種做法需要平方級的時間要求,因此,使用掃描法可以保證時間效能。

在二維平面內使用一條垂直的線段從左到右掃描,矩形的x座標觸發事件,事件包括:(結合下圖)

  • 當遇到矩形的左邊界時,將y座標區間插入區間搜尋樹中;在插入之前,先對待插入的座標區間做區間查詢,以確定相交情況。
  • 當遇到矩形的由邊界時,將y座標區間從區間搜尋樹中刪除

通過掃描法,就可以將二維的矩形相交的問題轉換成區間查詢的問題。在N條線段存在R個相交點的場景下,時間效能表現為 ~NlogN+RlogN

小結