關於二分查找的理解
在leetcode_35搜索插入的位置
這道題中,liweiwei大佬詳細的介紹了二分查找最關鍵的一個問題,就是如何避免陷入死循環。在此我根據大佬的筆記,總結一下自己的理解。
首先來看題目:
給定一個排序數組和一個目標值,在數組中找到目標值,並返回其索引。如果目標值不存在於數組中,返回它將會被按順序插入的位置。
你可以假設數組中無重複元素。
輸入: [1,3,5,6], 5
輸出: 2
正常的解法如下:
class Solution {
public:
int searchInsert(vector<int>& nums, int target) {
int size = nums.size();
if (size == 0) {
return 0;
}
// 特判
if (nums[size - 1] < target) {
return size;
}
int left = 0;
int right = size - 1;
while (left < right) {
// int mid = left + (right - left + 1)/2;//右中位數
int mid = left + (right-left)/2; //左中位數
if (nums[mid] < target) {
left = mid + 1;
}
else if (nums[mid] == target) {
return mid;
}
else {
right = mid;
}
}
return left;
}
};
這道題目的重點在與 左右區間的如何劃分,這個地方如何理解呢?
正如二分法所要求的mid
,當求得mid
,之後,根據mid
的值和目標值進行比較,會存在區間的劃分的兩種情況:
-
情況1:
[left,mid]
和[mid+1,right]
-
情況2:
[left,mid-1]
和[mid,right]
但是mid
的值怎麼計算呢?這個地方有兩個需要注意的點!
注意點1:如何防止溢出
正常對於mid
的計算我們都是:
int mid = (left+right)/2;
這種情況下,當數組很長的時候,兩個數直接相加就可能溢出。
所以一種比較好的習慣是:
int mid = left + (right-left)/2;
注意點2:mid
值是上取整還是下取整?
假設我們最後剩下的搜索區間是[2,3],那麼因爲int向下取整的原因,用注意點1裏的方法,mid的值就是2;
如果是下取整,那麼會存在什麼情況呢?
我們分別代入區間劃分的兩種情況下進行測試:
-
情況1:
int mid = 2; //判斷1:此時right=mid [left,mid] = [2,2] //判斷2:left=mid+1 [mid+1,right] = [3,3]
沒什麼問題.
return left!
-
情況2:
int mid = 2; //判斷1:此時right=mid-1 [left,mid-1] = [2,1] //判斷2:left=mid [mid,right] = [2,3] //陷入死循環!!!
在判斷1的情況下明顯出錯了!區間都劃分的不對了。或者出現 死循環!
如果我們使用的是上取整呢?
也就是這樣:
int mid = left + (right-left+1)/2;
再次分別代入以下區間劃分的情況下進行測試:
-
情況1:
int mid = 3; //判斷1:此時right=mid [left,mid] = [2,3]//陷入死循環!!! //判斷2:left=mid+1 [mid+1,right] = [4,3]//區間不對了!!!
出現錯誤!不是陷入
死循環
,就是區間判斷錯誤!不知道該如何退出! -
情況2:
int mid = 3; //判斷1:此時right=mid-1 [left,mid-1] = [2,2] //判斷2:left=mid [mid,right] = [3,3]
這種情況下安全退出!
所以這就是大佬說的,當遇到left = mid
的時候,才需要調整爲上取整!!!!
相似的練習題!