原文轉自https://www.acwing.com/blog/content/307/
一、查找精確值
從一個有序數組中找到一個符合要求的精確值(如猜數遊戲)。如查找值爲Key的元素下標,不存在返回-1。
//這裏是left<=right。
//考慮這種情況:如果最後剩下A[i]和A[i+1](這也是最容易導致導致死循環的情況)首先mid = i,
//如果A[mid] < key,那麼left = mid+1 = i +1,如果是小於號,則A[i + 1]不會被檢查,導致錯誤
int left = 1,right = n;
while(left <= right)
{
//這裏left和right代表的是數組下標,所有沒有必要改寫成mid = left + (right - left)/2;
//因爲當代表數組下標的時候,在數值越界之前,內存可能就已經越界了
//如果left和right代表的是一個整數,就有必要使用後面一種寫法防止整數越界
int mid = (left + right) / 2;
if(A[mid] == key)
return mid;
else if(A[mid] > key)//這裏因爲mid不可能是答案了,所以搜索範圍都需要將mid排除
right = mid - 1;
else
left = mid + 1;
}
return -1;
二、查找大於等於/大於key的第一個元素
這種通常題目描述爲滿足某種情況的最小的元素。
int left = 1,right = n;
while(left < right)
{
//這裏不需要加1。我們考慮如下的情況,最後只剩下A[i],A[i + 1]。
//首先mid = i,如果A[mid] > key,那麼right = left = i,跳出循環,如果A[mid] < key,left = right = i + 1跳出循環,所有不會死循環。
int mid = (left + right) / 2;
if(A[mid] > key)//如果要求大於等於可以加上等於,也可以是check(A[mid])
right = mid;
//因爲找的是大於key的第一個元素,那麼比A[mid]大的元素肯定不是第一個大於key的元素,因爲A[mid]已經大於key了,所以把mid+1到後面的排除
else
left = mid + 1;
//如果A[mid]小於key的話,那麼A[mid]以及比A[mid]小的數都需要排除,因爲他們都小於key。不可能是第一個大於等於key的元素,
}
三、查找小於等於/小於key的最後一個元素
這種通常題目描述爲滿足某種情況的最大的元素。如Leetcode69題,求sqrt(x)向下取整就是這種模板。
int left = 1, right = n;
while(left < right)
{
//這裏mid = (left + right + 1) / 2;
//考慮如下一種情況,最後只剩下A[i],A[i + 1],如果不加1,那麼mid = i,如果A[mid] < key,執行更新操作後,left = mid,right = mid + 1,就會是死循環。
//加上1後,mid = i + 1,如果A[mid] < key,那麼left = right = mid + 1,跳出循環。如果A[mid] > key,left = mid = i,跳出循環。
int mid = (left + right + 1) / 2;
if(A[mid] < key)
left = mid;//如果A[mid]小於key,說明比A[mid]更小的數肯定不是小於key的最大的元素了,所以要排除mid之前的所有元素
else
right = mid - 1;//如果A[mid]大於key,那麼說明A[mid]以及比A[mid]還要大的數都不可能小於key,所以排除A[mid]及其之後的元素。
}
二分細節經常寫錯,就轉了一個模板方便查閱