二分查找


  在序列中使用二分查找,可以在O(logn) 的時間內查找到需要的元素索引,二分查找的原理很容易理解,但是在代碼中有很多細節需要注意,在決定認真思考總結下二分查找之前,我一直隨緣判斷,直到返回正確結果

一般分爲兩種情況

  • 查找大於等於 val 的第一個元素索引 std::lower_bound
  • 查找大於 val 的第一個元素索引 std::upper_bound

公共代碼

int binaryFind(vector<int> &vi,int val)
{
	int left = -1,right = vi.size();
    while(right - left > 1)
    {
        int mid = (left + right) >> 1;
        //這裏一般沒有問題
        if(vi[mid] > val) right = mid;
        else if(vi[mid] < val) left = mid;
        //兩種情況的區別
        else if(vi[mid] == val) 
        {
            //如何選擇
            ? = mid;
        }
    }
    //到底返回哪個下標
    return ?;
}

查找大於等於 val 的第一個元素索引

... 4 5 5 5 5 5 6 7 8 ...

假如 val = 5,而此時 mid 指向第一個 5

  • 令 left = mid
      下一次 mid 就會指向後面的數 (這裏必然會繼續循環的原因是right至少在 6 的索引處),顯然查找區間在向右移動,而我們期望返回的是第一個 5 的下標,已經出現錯誤

  • 令 right = mid
      下一次 mid 就會指向 5 之前的數(不管是否繼續循環,left 必然在 5 之前,就算相鄰 mid 也會在 5 之前),同時這也是 right 最終的位置,因爲以後的 mid 肯定小於 val,那麼最終情況就是 left 指向 4,right 指向 5 跳出循環

假如 val = 5,而此時 mid 指向最後一個 5

  • 令 left = mid
      已經不可能返回期望的結果了
  • 令 right = mid
      和前面的結論一樣,left 一定在 5 之前,查找區間繼續向左移動,直到 right 指向第一個 5

  結論非常清楚

if(vi[mid] == val) right = mid;
return right;

查找大於 val 的第一個元素索引

... 4 5 5 5 5 5 6 7 8 ...

假如 val = 5,而此時 mid 指向第一個 5

假如 val = 5,而此時 mid 指向最後一個 5

  從前面的模擬可以得出,如果令 right = mid,那麼最終 left 指向 4,right 指向第一個 5,不符合此時期望的結果,所以 left = mid

  簡單模擬下 left = mid 的情況可知,最後一個 5 會成爲 left 最終指向的位置,不會繼續向右,所以最終跳出循環時,left 指向最後一個 5,right 指向 6

  結論也就非常清楚了

if(vi[mid] == val) left = mid;
return right;

總結

查找大於等於 val 的第一個元素索引

int binaryFind(vector<int> &vi,int val)
{
	int left = -1,right = vi.size();
    while(right - left > 1)
    {
        int mid = (left + right) >> 1;
        if(vi[mid] > val) right = mid;
        else if(vi[mid] < val) left = mid;
        else if(vi[mid] == val) right = mid;
    }
    return left;
}

  或者使用 std::lower_bound(vi.begin(),vi.end(),val)

查找大於 val 的第一個元素索引

int binaryFind(vector<int> &vi,int val)
{
	int left = -1,right = vi.size();
    while(right - left > 1)
    {
        int mid = (left + right) >> 1;
        if(vi[mid] > val) right = mid;
        else if(vi[mid] < val) left = mid;
        else if(vi[mid] == val) left = mid;
    }
    return left;
}

  或者使用 std::upper_bound(vi.begin(),vi.end(),val)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章