讀古今文學網 > 算法神探:一部谷歌首席工程師寫的CS小說 > 17 二叉搜索樹陷阱 >

17 二叉搜索樹陷阱

在距離Cloaks andMore店不到一個街區的地方,Frank注意到有一個女人在跟蹤他。即使十分惱怒,Frank也不得不承認她的跟蹤技術很好。她一直走在街對面,在Frank後面至少30英尺的地方,她還時不時地通過商店窗戶上的反光來觀察Frank。並且她穿了一件沒有任何特點的深綠色披風,這種顏色的披風在街上隨處可見。

Frank突然停下來,彎下腰,假裝自己在繫鞋帶。這是兩種最常見的識別跟蹤者的方法之一。另外一種則是突然向一個隨意的方向快步走去,看看有誰會跟上來。雖然可能後者更加有效,但繫鞋帶這個動作動靜更小,而且更重要的是,不需要人跑動。

跟蹤者向前走過了Frank,卻在前方十英尺的地方停了下來。她看起來彷彿在看一家玻璃透亮的商店裡賣的捲心菜。

Frank站了起來,轉身向反方向走去。過了半個街區,他橫穿馬路,憤怒的驢車駕駛員對他大喊大叫,Frank完全無視了他。Frank走到了馬路的另一邊,也就是跟蹤者行走的一邊。他隨即向一個小巷中走去。一走進小巷,Frank便停了下來,開始等待。

當跟蹤者匆忙跑過轉角的時候,她幾乎撞到了Frank身上。

「嗨,」Frank說道,「你為什麼要跟著我?」他盡量讓自己的語氣平淡一些,但這在平時都是不可能的,這次雖然他沒有尖叫,但語氣還是像低吼一般。

職業密探一般都會花很長時間來計劃如果被發現了該如何應對。他們有一大堆故事,用來在各種場合下圓場。他們甚至可以解釋自己為什麼會帶著竊聽裝置和假烏龜出現在皇家宮殿中。他們的夢想就是能在這種情況下通過謊言來順利過關。但現實一般不會如此順利——他們經常會在意想不到的時候被發現。就像現在,Frank就希望能出其不意地抓住她。

但跟蹤者異常專業,一點都沒有緊張或者驚訝的跡象。她臉上露出了一絲憤怒,隨後她丟下一顆煙霧彈,消失了。

就算她不丟煙霧彈,Frank也不可能追上她。在Frank反應過來準備去抓她的時候,她已經在街上跑遠了。Frank咒罵了一聲,穿過煙霧開始追她。

追了不到半個街區,Frank決定開始用深度優先搜索。這種情況下,密探一般都會盡快離開主路,試圖逃離被追者的視線。大多數時候這都是一個很好的策略,但在這塊幾乎沒有什麼岔路的街區卻明顯行不通。

Frank邊跑邊將身邊的街道想像成一個圖。岔路口和死路的盡頭是這個圖上的點,而連接它們的路則是連接一個點與另一個點的邊。

快速計算了一番後,Frank意識到在他落下太遠之前,他只來得及搜索五六條岔路。這也是用深度優先搜索來追人所固有的缺陷。

Frank搜索完前兩條街時一無所獲。其中最像犯罪行為的便是一群孩子們在牆上塗鴉,他們用一根燒焦的木棍在牆上寫了「遞歸之隊」和「遞歸永恆」。Frank繼續他的搜索。

又搜索過幾條死路後,Frank正想著放棄,此時他在泥地中找到了一串通向一個打開的下水道口的腳印。他一邊靠在牆上試圖調整自己的呼吸,一邊想著眼前的下水道肯定是她逃走的路。

Frank望向漆黑的下水道內,但什麼都看不見。他爬進下水道,落在了一個木製平台上。他一邊彎下腰,盡量讓自己暴露的面積變小,一邊巡視著這個房間。他所在的平台是固定在石牆上的,而它的下方是至少50英尺高的一間房子。唯一的一絲光是從頭頂的下水道口照進來的,像一個位於遠處的聚光燈一般。他看到那個密探從光照亮的橢圓形中跑過,跑向了對面的那堵牆。現在Frank已經被她甩掉很遠了。

Frank思考著該如何選擇。在他下面還有其他的平台,互相之間有20英尺的距離,並以鐵梯子連接著。當他注意到地板上嵌著的黃銅標籤時,他忍不住咒罵了一聲:他現在正站在一個二叉搜索梯的頂端。

二叉搜索梯最先是由Alena Branche(一位奇異藝術館館長)為了整理自己的藏畫而設計的。它們本質上就是一個巨大的二叉搜索樹——一種為了高效查找而設計的數據結構。整個結構像一棵巨大的上下顛倒的樹,而最上方的一個平台叫作根節點。從每一個平台向下都會分出最多兩個梯子,而每一個梯子則通向另一個子節點,也就是下一層的另一個平台。整個結構在向下的過程中不斷地分叉,使得整個結構中有著很多不同的路徑。

作為一個追求細節的狂熱分子,Alena原本是用這個結構中描繪的葉子數量來整理她的藏畫。她的整理方法很簡單:對於任何一個平台來說,其所存放的畫中描繪的葉子數量,一定多於其左下方梯子通向的平台(左子樹)中的畫中描繪的葉子數量,且一定少於其右下方梯子抵達的平台(右子樹)中的畫描繪的葉子數量。這樣你就可以從頂端開始選擇路徑找到含有特定葉子數量的畫。

不幸的是,這種二叉搜索梯在藝術領域一直都未曾流行起來。這也許是因為它們體積太大了,而且還需要人不停地爬上爬下。不過很快它們就被犯罪分子們利用了起來。二叉搜索梯陷阱這種危險的東西是初級巫師Katia Ladderfell在為Vinettee集團賣命的時候發明的。Katia在每一層上放了一個數字標籤,需要知道密碼才能安全地通過這棵樹。在設計這棵樹和放置標籤時,他延續了二叉搜索樹的特性——一個節點的左子樹中的數字一定會比當前數字小,而它的右子樹中的數字一定會比當前數字大。在這棵被變成武器的二叉搜索樹中,只有一條路是安全的,這條路便是通向最底層寫著密碼的那個節點的路徑。如果你知道密碼的話,你可以一層一層地從上到下去找這個數字。到達每一層時,你可以比較密碼和當前數字的大小,從而決定是往左下方還是往右下方走。因此,Vinettee集團的惡棍們只需要記住一個密碼就行了,而不需要記住一大串左右的選擇。由於這群惡棍們大多不太聰明,所以這一點對於Vinettee集團格外重要。

如果你不知道密碼而選擇了錯誤的梯子的話,你就會觸發一個機關。有時候這些機關是致命的,而有時候卻只是嚇你一下而已。一些常用的機關有:詛咒之梯、毒蜘蛛、從上面掉下的石頭、飛鏢和在空中搖晃的刀刃,還有時候選錯梯子的人會受到一個魔法咒語的辱罵,辱罵他的外表、氣味或智商等方面。

上一次Frank面對Vinettee集團的二叉搜索梯時,一個惡棍告密者告訴他密碼是10。那一次Frank得以悄悄地來到他們的藏身點,並抓住了三個Vinettee集團的人,只有Rebecca Vinettee逃掉了。

要是他知道這個陷阱現在的密碼的話,也許他還有機會抓住那個密探。

Frank頭腦中閃過了一串思緒。首先,Vinettee集團會不會重複利用密碼?他們的惡棍一般都很蠢,Frank不覺得他們可以記住很多數字。其次,由於現在已經沒有多少邪惡巫師,這個二叉搜索梯一定有些年頭了。在Exponentious巫師造反失敗後,那些邪惡巫師們要麼被改造了,要麼躲起來了,就連Katia Ladderfell都逃到了一個小鎮上,開了一個葉子農場。由於站在如此高的地方,Frank的腿已經開始有點發抖了。

Frank看了看腳下的根節點的平台上的標籤,上面寫著50。如果他想的沒錯,Vinettee集團用的還是同一個密碼的話,因為10小於50,所以他應該往左下方走。

Frank擔心地咒罵了幾聲,由左邊的梯子向下爬去。這一路十分順利:沒有蜘蛛,沒有刀刃,甚至連侮辱人的髒話都沒有。

在下一個平台上,Frank找到了一個寫著5的標籤。由於10大於5,他需要向右邊走下去。他越來越自信,一下跳到了右邊的梯子。看來有的時候把敵人想得蠢一些是沒錯的。

下一個平台離地面只有一層,也就是20英尺了。Frank看到標籤上寫著25,便立刻往左邊走去。

當他意識到有些不對時,他已經爬了一半了。他聽見了一聲吱吱的聲音,同時他左腳下的那根橫桿開始晃動了。他向下看時,正好看到橫桿撞上了附近的一截鐵棍,而他的左腳則被夾在了兩者之間。他驚訝地叫了出來,眼睜睜地看著那節鐵棍不斷地上下移動。而鐵棍每動一次,他的腳就又感到一陣疼痛,簡直就像是梯子在咬他的腳一樣,他甚至可以感受到腳下的金屬梯子在慢慢長出牙齒。

趁梯子還沒有開始咬他的手,Frank毫不猶豫地跳了下去。由於腳還在因為之前被咬而疼痛,他非常笨拙地落到了地上,前後趔趄了幾步後才穩住。

他不由自主地轉了一圈,隨即看向梯子下面的標籤,上面清楚地寫著10——他走的路是正確的。但隨即他注意到了附近的地板上用粉筆寫的一小行字:「不要使用。密碼改了。」Frank頓時繼續開始罵了起來。

警用算法導論:二叉搜索樹Ⅰ

節選自Drecker教授講義

二叉搜索樹是一個數據結構,它儲存信息的方式和二分搜索中使用信息的方式類似。樹中的每一個節點存放一個值,並且每個節點最多有兩個子節點:一個左子節點和一個右子節點。節點的位置根據其中存放的值的大小來定。所有左子節點和它的左子節點中存放的值都比當前節點中的值小;類似的,所有右子節點和它的右子節點中存放的值都比當前節點的值大。

要高效地在二叉搜索樹中查找一個值,可以從最上面的節點(根節點)開始往下查找,每查找一步就比較要找的值和當前節點的值的大小,以決定是該向左查找還是向右查找。如果要找的值比當前值小,我們就向左查找:

而如果要找的值比當前值大,我們就向右查找:

當我們找到要找的值,或者遇到一條死路的時候,搜索就結束了。如果我們遇到了一條死路,就可以確定我們要找的值在樹中並不存在。

如果對於每個節點,其左子樹中的節點數量都和其右子樹中的節點數量一致,我們就可以說這個二叉搜索樹是完全平衡的。在這種情況下,如果我們將樹中的節點數量翻倍,整棵樹的高度只會增加1。

這種搜索的計算量與目標值在樹中的深度是成正比的。位置越深,我們需要進行比較的次數就越多。