二分查找


  在序列中使用二分查找,可以在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)

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