1. 程式人生 > >程式設計師程式設計藝術第二十五章:Jon Bentley:90%無法正確實現二分查詢

程式設計師程式設計藝術第二十五章:Jon Bentley:90%無法正確實現二分查詢

第二十五章:二分查詢實現(Jon Bentley:90%程式設計師無法正確實現)

作者:July
出處:結構之法演算法之道

引言

    Jon Bentley:90%以上的程式設計師無法正確無誤的寫出二分查詢程式碼。也許很多人都早已聽說過這句話,但我還是想引用《程式設計珠璣》上的如下幾段文字: 

二分查詢可以解決(預排序陣列的查詢)問題:只要陣列中包含T(即要查詢的值),那麼通過不斷縮小包含T的範圍,最終就可以找到它。一開始,範圍覆蓋整個陣列。將陣列的中間項與T進行比較,可以排除一半元素,範圍縮小一半。就這樣反覆比較,反覆縮小範圍,最終就會在陣列中找到T,或者確定原以為T所在的範圍實際為空。對於包含N個元素的表,整個查詢過程大約要經過log(2)N次比較。 
多數程式設計師都覺得只要理解了上面的描述,寫出程式碼就不難了;但事實並非如此。如果你不認同這一點,最好的辦法就是放下書本,自己動手寫一寫。試試吧。 
我在貝爾實驗室和IBM的時候都出過這道考題。那些專業的程式設計師有幾個小時的時間,可以用他們選擇的語言把上面的描述寫出來;寫出高階虛擬碼也可以。考試結束後,差不多所有程式設計師都認為自己寫出了正確的程式。於是,我們花了半個鐘頭來看他們編寫的程式碼經過測試用例驗證的結果。幾次課,一百多人的結果相差無幾:90%的程式設計師寫的程式中有bug(我並不認為沒有bug的程式碼就正確)。 
我很驚訝:在足夠的時間內,只有大約10%的專業程式設計師可以把這個小程式寫對。但寫不對這個小程式的還不止這些人:高德納在《計算機程式設計的藝術 第3卷 排序和查詢》第6.2.1節的“歷史與參考文獻”部分指出,雖然早在1946年就有人將二分查詢的方法公諸於世,但直到1962年才有人寫出沒有bug的二分查詢程式。 

——喬恩·本特利,《程式設計珠璣(第1版)》第35-36頁。

    你能正確無誤的寫出二分查詢程式碼麼?不妨一試。

二分查詢程式碼

     二分查詢的原理想必不用多解釋了,不過有一點必須提醒讀者的是,二分查詢是針對的排好序的陣列。OK,紙上讀來終覺淺,覺知此事要躬行。我先來寫一份,下面是我寫的一份二分查詢的實現(之前去某一家公司面試也曾被叫當場實現二分查詢,不過結果可能跟你一樣,當時就未能完整無誤寫出),有任何問題或錯誤,懇請不吝指正:

  1. //二分查詢V0.1實現版
  2. //[email protected] July
  3. //隨時歡迎讀者找bug,email:[email protected]
  4. //首先要把握下面幾個要點:
  5. //right=n-1 => while(left <= right) => right=middle-1;
  6. //right=n   => while(left <  right) => right=middle;
  7. //middle的計算不能寫在while迴圈外,否則無法得到更新。
  8. int binary_search(int array[],int n,int value)  
  9. {  
  10.     int left=0;  
  11.     int right=n-1;  
  12.     //如果這裡是int right = n 的話,那麼下面有兩處地方需要修改,以保證一一對應:
  13.     //1、下面迴圈的條件則是while(left < right)
  14.     //2、迴圈內當array[middle]>value 的時候,right = mid
  15.     while (left<=right)          //迴圈條件,適時而變
  16.     {  
  17.         int middle=left + ((right-left)>>1);  //防止溢位,移位也更高效。同時,每次迴圈都需要更新。
  18.         if (array[middle]>value)  
  19.         {  
  20.             right =middle-1;   //right賦值,適時而變
  21.         }   
  22.         elseif(array[middle]<value)  
  23.         {  
  24.             left=middle+1;  
  25.         }  
  26.         else
  27.             return middle;    
  28.         //可能會有讀者認為剛開始時就要判斷相等,但畢竟陣列中不相等的情況更多
  29.         //如果每次迴圈都判斷一下是否相等,將耗費時間
  30.     }  
  31.     return -1;  
  32. }  
    簡單測試下,執行結果如下所示(當然,一次測試正確不代表程式便0 bug了,且測試深度遠遠不夠):

測試

    也許你之前已經把二分查詢實現過很多次了,但現在不妨再次測試一下。關閉所有網頁,視窗,開啟記事本,或者編輯器,或者直接在本文評論下,不參考上面我寫的或其他任何人的程式,給自己十分鐘到N個小時不等的時間,立即編寫一個二分查詢程式。獨立一次性正確寫出來後,可以留下程式碼和郵箱地址,我給你傳一份本blog的博文集錦CHM檔案 && 十三個經典演算法研究帶標籤+目錄的PDF文件(你也可以去我的資源下載處下載:http://download.csdn.net/user/v_july_v)。

    當然,能正確寫出來不代表任何什麼,不能正確寫出來亦不代表什麼,僅僅針對Jon Bentley的言論做一個簡單的測試而已。下一章,請見第二十六章:基於給定的文件生成倒排索引的編碼與實踐。謝謝。

總結

    本文發表後,馬上就有很多朋友自己嘗試了。根據從朋友們在本文評論下留下的程式碼,發現出錯率最高的在以下這麼幾個地方

  1. 註釋裡已經說得很明白了,可還是會有不少朋友犯此類的錯誤:
    1. //首先要把握下面幾個要點:  
    2. //right=n-1 => while(left <= right) => right=middle-1;  
    3. //right=n   => while(left <  right) => right=middle;  
    4. //middle的計算不能寫在while迴圈外,否則無法得到更新。  
  2. 還有一個最最常犯的錯誤是@土豆:
    middle= (left+right)>>1; 這樣的話left與right的值比較大的時候,其和可能溢位。
    各位繼續努力。

相關推薦

程式設計師程式設計藝術-----第二-----二分查詢實現Jon Bentley90%程式設計師無法正確實現

第二十五章:二分查詢實現(Jon Bentley:90%程式設計師無法正確實現)作者:July出處:結構之法演算法之道引言    Jon Bentley:90%以上的程式設計師無法正確無誤的寫出二分查詢程式碼。也許很多人都早已聽說過這句話,但我還是想引用《程式設計珠璣》上的如下幾段文字:  “二分查詢可以解決

程式設計師程式設計藝術第二Jon Bentley90%無法正確實現二分查詢

第二十五章:二分查詢實現(Jon Bentley:90%程式設計師無法正確實現) 作者:July 出處:結構之法演算法之道 引言     Jon Bentley:90%以上的程式設計師無法正確無誤的寫出二分查詢程式碼。也許很多人都早已聽說過這句話,但我

程式設計師程式設計藝術-----第二-----不改變正負數相對順序重新排列陣列

10、翻轉句子中單詞的順序。題目:輸入一個英文句子,翻轉句子中單詞的順序,但單詞內字元的順序不變。句子中單詞以空格符隔開。為簡單起見,標點符號和普通字母一樣處理。例如輸入“I am a student.”,則輸出“student. a am I”。而此題可以在O(N)的時間複雜度內解決:    由於本題需要翻

程式設計師程式設計藝術第二基於給定的文件生成倒排索引(含原始碼下載)

第二十六章:基於給定的文件生成倒排索引的編碼與實踐作者:July、yansha。出處:結構之法演算法之道引言    本週實現倒排索引。實現過程中,尋找資料,結果發現找份資料諸多不易:1、網上搜倒排索引實現,結果千篇一律,例子都是那幾個同樣的單詞;2、到谷歌學術上想找點稍微有價

程式設計師程式設計藝術-----第二八 ~ 二-----最大連續乘積子串、字串編輯距離

               第二十八~二十九章:最大連續乘積子串、字串編輯距離前言    時間轉瞬即逝,一轉眼,又有4個多月沒來更新blog了,過去4個月都在幹啥呢?對的,今2013年元旦和朋友利用業餘時間一起搭了個方便朋友們找工作的程式設計面試演算法論壇:為學論壇http://www.51weixue.c

程式設計師程式設計藝術-----第 ~ 二-----全排列、跳臺階、奇偶、第一個出現一次字元、一致性hash

第十六~第二十章:全排列,跳臺階,奇偶排序,第一個只出現一次等問題作者:July、2011.10.16。出處:http://blog.csdn.net/v_JULY_v。引言    最近這幾天閒職在家,一忙著投簡歷,二為準備面試而蒐集整理各種面試題。故常常關注個人所建的Algorithms1-14群內朋友關於

程式設計師程式設計藝術-----第二十三 ~ 二-----楊氏矩陣、不重複Hash編碼

  第二十三、四章:楊氏矩陣查詢,倒排索引關鍵詞Hash不重複編碼實踐作者:July、yansha。程式設計藝術室出品。出處:結構之法演算法之道。前言    本文闡述兩個問題,第二十三章是楊氏矩陣查詢問題,第二十四章是有關倒排索引中關鍵詞Hash編碼的問題,主要要解決不重複以及追加的功能,同時也是經典演算法研

程式設計師程式設計藝術-----第-----最長公共子序列(LCS)問題

  程式設計師程式設計藝術第十一章:最長公共子序列(LCS)問題0、前言    程式設計師程式設計藝術系列重新開始創作了(前十章,請參考程式設計師程式設計藝術第一~十章集錦與總結)。回顧之前的前十章,有些程式碼是值得商榷的,因當時的程式碼只顧闡述演算法的原理或思想,所以,很多的與程式碼規範相關的問題都未能做到

程式設計師程式設計藝術-----第一 ~ -----海量整數處理、蓄水池抽樣、迴文

       程式設計師程式設計藝術第十二~十五章:中籤概率,IP訪問次數,迴文等問題(初稿)作者:上善若水.qinyu,BigPotato,luuillu,well,July。程式設計藝術室出品。前言    本文的全部稿件是由我們程式設計藝術室的部分成員:上善若水.qinyu,BigPotato,luuil

程式設計師程式設計藝術-----第-----尋找滿足和為定值的兩個或多個數

                    程式設計師程式設計藝術:第五章、尋找和為定值的兩個或多個數 前奏    希望此程式設計藝術系列能給各位帶來的是一種方法,一種創造力,一種舉一反三的能力。本章依然同第四章一樣,選取比較簡單的面試題,恭祝各位旅途愉快。同樣,有任何問題,歡迎不吝指正。謝謝。第一節、尋找和為定

程式設計師程式設計藝術第三~三字串轉換成整數,萬用字元字串匹配

第三十~三十一章:字串轉換成整數,帶萬用字元的字串匹配前言    之前本一直想寫寫神經網路演算法和EM演算法,但寫這兩個演算法實在需要大段大段的時間,而平時上班,週末則跑去北大教室自習看書(順便以時間為序,說下過去半年看過的自覺還不錯的數學史方面的書:《數理統計學簡史》《微積

程式設計藝術第二十三 四 一續 楊氏矩陣查詢 倒排索引關鍵詞Hash編碼

                  第二十三、四章:楊氏矩陣查詢,倒排索引關鍵詞Hash不重複編碼實踐作者:July、yansha。程式設計藝術室出品。出處:結構之法演算法之道。前言    本文闡述兩個問題,第二十三章是楊氏矩陣查詢問題,第二十四章是有關倒排索引中關鍵詞Hash編碼的問題,主要要解決不重複以及

程式設計師程式設計藝術-----第六-----求解500萬以內的親和數(素數、完數)

前奏    本章陸續開始,除了繼續保持原有的字串、陣列等面試題之外,會有意識的間斷性節選一些有關數字趣味小而巧的面試題目,重在突出思路的“巧”,和“妙”。本章親和數問題之關鍵字,“500萬”,“線性複雜度”。第一節、親和數問題題目描述:求500萬以內的所有親和數如果兩個數a和b,a的所有真因數之和等於b,b的

程式設計師程式設計藝術-----第七-----求連續子陣列的最大和

 程式設計師程式設計藝術:第七章、求連續子陣列的最大和 前奏希望更多的人能和我一樣,把本狂想曲系列中的任何一道面試題當做一道簡單的程式設計題或一個實質性的問題來看待,在閱讀本狂想曲系列的過程中,希望你能儘量暫時放下所有有關面試的一切包袱,潛心攻克每一道“程式設計題”,在解決程式設計題的過程中,好好享受程式設計

程式設計師程式設計藝術-----第四-----現場編寫類似strstr/strcpy/strpbrk的函式

               第四章、現場編寫類似strstr/strcpy/strpbrk的函式   作者:July。    說明: 如果在部落格中程式碼使用了\n,csdn blog系統將會自動回給我變成/n。據後續驗證,可能是原來舊blog版本的bug,新版已不存在此問題。至於,本文程式碼,日後統一修正

程式設計師程式設計藝術-----第三續-----Top K演算法問題的實現

                     程式設計師程式設計藝術:第三章續、Top K演算法問題的實現前奏    在上一篇文章,程式設計師面試題狂想曲:第三章、尋找最小的k個數中,後來為了論證類似快速排序中partition的方法在最壞情況下,能在O(N)的時間複雜度內找到最小的k個數,而前前後後update

程式設計師程式設計藝術-----第九-----閒話連結串列追趕問題

前奏    有這樣一個問題:在一條左右水平放置的直線軌道上任選兩個點,放置兩個機器人,請用如下指令系統為機器人設計控制程式,使這兩個機器人能夠在直線軌道上相遇。(注意兩個機器人用你寫的同一個程式來控制)。    指令系統:只包含4條指令,向左、向右、條件判定、無條件跳轉。其中向左(右)指令每次能控制機器人向左

程式設計師程式設計藝術-----第三-----尋找最小的k個數

                    程式設計師程式設計藝術:第三章、尋找最小的k個數作者:July。時間:二零一一年四月二十八日。致謝:litaoye, strugglever,yansha,luuillu,Sorehead,及狂想曲創作組。微博:http://weibo.com/julyweibo。出處

程式設計師程式設計藝術-----第八-----從頭至尾漫談虛擬函式

前奏    有關虛擬函式的問題層出不窮,有關虛擬函式的文章千篇一律,那為何還要寫這一篇有關虛擬函式的文章呢?看完本文後,相信能懂其意義之所在。同時,原狂想曲系列已經更名為程式設計師程式設計藝術系列,因為不再只專注於“面試”,而在“程式設計”之上了。ok,如果有不正之處,望不吝賜教。謝謝。第一節、一道簡單的虛擬

程式設計師程式設計藝術第六、求解500萬以內的親和數

   第六章、親和數問題--求解500萬以內的親和數 前奏     本章陸續開始,除了繼續保持原有的字串、陣列等面試題之外,會有意識的間斷性節選一些有關數字趣味小而巧的面試題目,重在突出思路的“巧”,和“妙”。本章親和數問題之關鍵字,“500萬”,“線性複雜度”。