二分查找真的有你想象中那么簡單嗎?


二分查找是查找算法里家喻戶曉的算法了,其時間復雜度為O(logn),可是如果真的讓你立馬拿出筆寫一個二分查找的函數出來,你確定你可以比較快的完全寫對嗎?

我們的目的是從一個已經按從小到大的順序排序好的數組arr中查找值為value的元素的位置。

大體思路我們應該都很清楚:有三個游標,一個low在頭,一個high在尾,還有一個mid指向中間,如果要檢索的數據value比中間的元素arr[mid]小,那么應該在[low,mid)區間繼續查找,即將high指向mid前面那個元素(也許你可能認為是指向mid元素的位置);如果要檢索的數據value比中間的元素arr[mid]大,那么應該在(mid,high]區間繼續查找,即將low指向mid后面那個元素(也許你可能認為是指向mid元素的位置)。一直執行這個步驟來縮小搜索區間直到找到arr[k]==value返回k 或 low>high時返回-1表示沒找到。

其中的細節有很多是需要格外注意的。下面我就通過一個我的一段代碼來引出需要注意的地方。

 1 typedef int DataType;  2 int binarySearch(const DataType arr[],const DataType value,size_t len)  3 {  4     int low = 0, high = len-1, mid;  5     while(low <= high) {  6         mid = low + ((high-low)>>1); //思考為什么不寫作(high+low)/2;
 7         if(value-arr[mid]<1e-6 && arr[mid]-value<1e-6)//思考為何不寫作arr[mid]==value
 8             return mid;  9         if(value<arr[mid]) 10             high = mid-1;    //如果寫作high = mid;可以嗎
11         else
12             low = mid+1;    //如果寫作low = mid;可以嗎
13  } 14     return -1; 15 }

  在看完了上面的代碼后,你有木有想到代碼中注釋部分的問題?當你第一遍寫代碼的時候真的考慮到了嗎,如果沒考慮這些會有什么過果呢?下面讓我們來一一道來:

  (1)第六行如果寫作mid = (high+low)/2;,有木有發現high+low有點蹊蹺?如果你看出來了,恭喜你說明你對數據類型對應的取值范圍很了解!當DataType定義為int型時,兩個int相加,不要以為不會越界哈~另外改成移位操作同樣完成了除以2一樣的效果,但是效率卻提高了。如果用移位的話一定要記得移位運算優先級很低,所以記得加括號!!記得加括號!括號!(重要的事說三遍,哈哈)

  (2)第七行說好的判等呢,為嘛寫成了區間的形式?這個嘛,就要考慮代碼可重用性,因為細心的你可能會發現,傳入的第一個參數是數組類型,什么類型的數組?這里暫時定義為int,那如果是float呢?double呢?判等還用==?所以這里考慮的是普遍情況,通過將兩個數的差值在很小范圍內來表示他們相等,int時照樣適用。

  (3)第10行第12行,才開始寫的時候可能會糾結是不是要減1或者加1,當然還有第五行是寫low <= high還是low < high?舉幾個讓另一種情況出現問題的例子然后你就會明白其中的奧秘了。

  好了,基本上要注意主要的問題就這些了,下面給出一個用模板函數寫好的完整代碼吧!

 1 #include<vector>
 2 #include<iostream>
 3 using namespace std;  4 
 5 //二分查找模板
 6 template<typename T1,typename T2>
 7 int binarySearch(const T1 &arr,const T2 &value,size_t len)  8 {  9     int low = 0, high = len-1, mid; 10     while(low <= high) { 11         mid = low + ((high-low)>>1); //思考為什么不寫作(high+low)/2;
12         if(value-arr[mid]<1e-6 && arr[mid]-value<1e-6)//思考為何不寫作arr[mid]==value
13             return mid; 14         if(value<arr[mid]) 15             high = mid-1;    //如果寫作high = mid;可以嗎
16         else
17             low = mid+1;    //如果寫作low = mid;可以嗎
18  } 19     return -1; 20 } 21 
22 int main() 23 { 24     double arr[10]; 25     int i; 26     for(i=0; i<10; i++) 27         arr[i] = i; 28     for(i=-1; i<11; i++) 29         cout<<"the index of '"<<i<<"': "<<binarySearch(arr,i,10)<<endl; 30 
31     cout<<endl; 32     vector<int> arr_i(arr,arr+10); 33     for(i=-1; i<11; i++) 34         cout<<"the index of '"<<i<<"': "<<binarySearch(arr_i,i,arr_i.size())<<endl; 35     return 0; 36 }

這個模板函數可以接受不同類型的數組,當然為了兼容C++ STL中的vector容器,又對參數做了小小的改進!

————————————————————我是分割線———————————————————————

附加——如果考慮到一種特殊情況:對於數組中有相同值的元素(比如[0,1,1,1,4,5,6]),

(1)查找中希望得到該值的元素最早出現的位置(返回1),應該怎么實現?

(2)查找中希望得到該值的元素最后出現的位置(返回3),應該怎么實現?

 

時間倉促,如果內容上有什么疑問或者錯誤之處,請指出,謝謝!

轉載請注明出處:http://www.cnblogs.com/webary/p/4753231.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM