今晚看到演算法引論關於二分搜尋的相關問題,想起了當年看程式設計珠璣的「無處不在的二分搜尋」那章,記得作者說過能完全寫對二分搜尋的程式設計師寥寥無幾,當時自己也寫了下,確實不容易寫,主要的難點在於寫對,大致的框架可能大家都非常熟悉,但是裡面的下標怎麼確定是正確的呢?不對的下標很有可能造成死迴圈。不過,演算法引論所推崇的數學歸納法的思想還是很普適的,反應在程式上就是先寫n=1的情況,再寫歸納階段的**,這樣的方法用在寫二分搜尋感覺很有效,例如書中最普通的二分搜尋**如下:
int binarysearch(int* a, int l, int r, int z)else
else
}}
這裡對程式稍微做了點修改,但是大意是一樣的,可以看到,程式書寫的邏輯上,先處理n=1的情況,即l==r,這時候只有1個元素,對於1個元素的判斷是trivial的,然後在else裡面,算出middle並遞迴判斷,這裡是上取整,那麼為什麼要上取整?
要明白這個問題就是要理解,如果下取整會出現什麼情況?死迴圈!讓我們來驗證下,假如某個時刻l=r-1,且a[l]<=z,這個時候就是死迴圈的時候,因為每次middle都會等於l(下取整)而a[l]<=z所以會走else那個分支,又繼續遞迴l(此時middle是等於l的)和r,從而一直死迴圈下去。
仔細分析上面的情況不難發現,關鍵的地方在middle是下取整的情況,如果下取整會出現乙個關鍵的問題就是l有可能等於middle,我們如果抓住這個問題去分析,就會很容易發現在else分支會出現死迴圈。下面用同樣的方法分析下上取整,如果上取整的話,middle則可能會等於r,如果a[middle]>z,會直接導致r等於l,即在下次進入n=1的判斷;如果a[middle]<=z,則同樣會有middle等於r進入n=1的分支,這也證明了這個程式一定會在n=1的時候退出。其實在下取整和上取整發生不同的地方就是臨界的位置,這也是容易造成死迴圈的時候。
通過以上分析,我們總結下如何快速判斷乙個二分搜尋程式是否會出現死迴圈:
其實,程式設計珠璣中也介紹了程式驗證學的方法,即assert,這個方法也是很好的一種方法,特別的是寫短小程式的時候。
用這個方法,我發現圖6.3中,二叉搜尋的特殊下標問題中的程式是錯誤的,即middle的取法應該是下取整,書上是上取整,我用程式跑書的例子,果然華麗的堆疊溢位了,看來這種方法還是挺有作用的,不知道有人和我有同樣的疑問嗎,勘誤上並沒有說程式的問題。
[update] 最近做leetjob 的online judegment又遇到類似的,問題,繼續總結,二分查詢可以在有序陣列中找任意滿足某些特定條件的乙個數,先根據條件,確定middle比較的形式,之後就可以確定是用上取整還是下取整,上取整,上限j必須要變,下取整,下限i必須要變,例如第乙個找某個數x使得x>=target,那麼我們就要寫a[middle](完)
寫好正確的二分搜尋
今天再次解決乙個需要使用二分查詢的問題,再一次的,我又沒有一次過寫對.為什麼我說 又 抓狂了,似乎開始有一些 二分查詢恐懼症 為了以後能夠一次將這個基本的演算法寫對,我決定再仔細研究一下.我之前有寫過乙個二分查詢的演算法,在 這裡,這一次再以這個問題為例來說明.我今早寫下的錯誤 類似於下面的樣子 i...
5 9 二分搜尋樹的順序性
二分搜尋樹當做查詢表的一種實現。我們使用二分搜尋樹的目的是通過查詢 key 馬上得到 value。二分搜尋樹還能回答哪些問題呢?這些問題都和順序相關。minimum,maximum successor,predecessor 這兩個元素在二分搜尋樹的 key 中必須存在 floor 地板 ceil ...
策略性跳躍的 二分搜尋
以下是我結合 挑戰程式設計競賽 的二分內容的一些個人總結 1.2 判斷乙個解是否可行 1.3 最大化最小值問題 1.4 最大化平均值 2.二分習題 我們常見的二分查詢,只需要找到乙個符合的值就行。預設公升序 intbinarysearch int find,int length,int value ...