二分查找可能是最早接觸的查找方式,時間複雜度僅爲O(lg2(n))級別,而且易於理解
但是理解了是一回事,動手寫起來又是一回事,絕大部分人會在是否取等以及邊界值+1的問題上栽倒很多次,就像《只狼》拼刀,看通關視頻都很容易,葦名一心也不過爾爾,結果自己打的時候上來就在新手村先死個20次
先附上原碼:
int binary_search(vector<int>& nums, int target)//搜索後返回數據在數組的座標值,如果不存在則返回-1
{
int right=nums.size()-1;//二分查找的右極限
int left=0;//二分查找的左極限
int middle=left+(right-left)/2;//中間數的座標
int number=nums[middle];//中間的數據
while(number!=target)
{
if(number>target)
{
right=middle;
middle=left+(right-left)/2;
number=nums[middle];
}
else if(number<target)
{
left=middle+1;
middle=left+(right-left)/2;
number=nums[middle];
}
if(right==left&&number!=target)
return -1;
}
return middle;
}
接下來解釋細節部分:
1.取等問題
很多人都在left和right比較大小的地方猶豫很久,這裏我特意繞開了這個判斷,直接在循環中省去了兩者比較的判斷,單獨列出一個if作爲判斷條件:既然left和right已經取等,說明查找也到了最後一步,如果再沒有合適的值,就直接退出即可
2.+1問題
這裏是絕大部分人在第一次嘗試的時候絕對會栽跟頭的地方
由於計算機計算數據的時候採用2進制編碼數據,對於無法整除數據的情況,採用的是“向0取整”,即:結果大於0的小數,僅保留整數部分,結果小於0的小數,採用進一的方式取整
由於數組的下標均爲非負數,所以二分查找的平均數也一定爲正數,因此,每次取整的時候,數據會偏小,也就是left會更新爲一個較小的整數
例如:當面臨要查找的數據恰好處於下標7上,此時left=6,right=7,每次對middle和left進行更新的時候,left都因爲向下取整的問題而無限循環……
這時候,我們需要捨棄:既然之前,middle上的數據已經不是我們需要的了,那就讓left在頂替middle的時候自動+1
但由於right沒有向下取整的問題,所以不需要進行對應的-1操作,如果進行了-1操作,反而會出現新的問題:
例如,我們在數組[5]當中查找數字-2,此時left,middle,right均爲0,在第一輪循環的時候,由於需要對right進行-1,這時就會出現數組下標負溢出的情況(right=-1)
3.細節問題
由於我們需要查找的數組可能會很大,大到下標即將溢出int型的範圍,這時候我們就要小心了:middle在採用(right+left)/2的計算模式下, 可能會溢出
所以我們修改了middle的計算方式,將其改爲一種較爲安全的計算方法,保證計算的過程中,不會出現溢出的可能