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,紙上讀來終覺淺,覺知此事要躬行。我先來寫一份,下面是我寫的一份二分查詢的實現(之前去某一家公司面試也曾被叫當場實現二分查詢,不過結果可能跟你一樣,當時就未能完整無誤寫出),有任何問題或錯誤,懇請不吝指正:

//二分查詢V0.1實現版  
//[email protected] July  
//隨時歡迎讀者找bug,email:[email protected]。  
  
//首先要把握下面幾個要點:  
//right=n-1 => while(left <= right) => right=middle-1;  
//right=n   => while(left <  right) => right=middle;  
//middle的計算不能寫在while迴圈外,否則無法得到更新。  int binary_search(int array[],int n,int value)  
{  
    
int left=0;  
    
int right=n-1;  
    
//如果這裡是int right = n 的話,那麼下面有兩處地方需要修改,以保證一一對應:  
    
//1、下面迴圈的條件則是while(left < right)  
    
//2、迴圈內當array[middle]>value 的時候,right = mid  while (left<=right)          //迴圈條件,適時而變      {  
        
int middle=left + ((right-left)>>1);  //防止溢位,移位也更高效。同時,每次迴圈都需要更新。  if (array[middle]>value)  
        {  
            right 
=middle-1;   //right賦值,適時而變          }   
        
elseif(array[middle]<value)  
        {  
            left
=middle+1;  
        }  
        
elsereturn middle;    
        
//可能會有讀者認為剛開始時就要判斷相等,但畢竟陣列中不相等的情況更多  
        
//如果每次迴圈都判斷一下是否相等,將耗費時間      }  
    
return-1;  
}