C++二分查找算法

感谢 @TheNew123 指正错误

背景

二分查找(英语:binary search),也称折半搜索(英语:half-interval search)、对数搜索(英语:logarithmic search),是用来在一个 有序数组 中查找某一元素的算法。

by 张峻熙666的博客 - 《C++二分查找》

原理

定义左变量,右变量,表示查找区间;为数组长度;为要查找数

初值初值

先将数组位置的数与比较,如果大于,小于的话,,然后不断查找,直到找到或者小于为止。

算法设计

(1)设置左量和右量,分别赋值(数组长度)

int left=1,right=n;

(2)如果查找的范围 不存在,则查找失败,否则进(3)

(3)取中间位置 ,比较要查找的数

1. 若 x < a[mid],则right=mid-1;查找在左半区间进行,转(2);
2. 若 x > a[mid],则left=mid+1;查找在右半区间进行,转(2);
3. 若 x = a[mid],则查找成功,返回mid的值,结束;
4.如果left > right,跳出循环。

by 薛定谔的史蒂夫的博客 - 《C++二分查找》

时间复杂度

(1)普通循环查找

最好:

最坏:

(2)二分方法查找

最好(就在中间):

最坏(在两端):

代码实现

没有重复的情况

非递归

int binary_search(int left,int right,int x) {
    int ret = -1;  //未搜索到数据返回-1
    int mid;
    while (left <= right) {
        mid = (left+right) >>1;  //直接平均可能会范围超限,所以用位运算,即除以2的1次方
        if (a[mid] < x)    left = mid + 1;
        else if (a[mid] > x)    right = mid - 1;
        else{ //不大不小就等于
            ret = mid;
            break;
        }
    }
    return ret;
}

递归

懒人专用,但注意数据大时栈溢出的问题

int binary_search(int left,int right,int x) {
    int mid = (left+right) >>1;
    if(x < a[mid])    return binary_search(a,left,mid-1,x);
    else if(x > a[mid])    return binary_search(a,mid+1,right,x);
    else    return mid;
}

有重复的情况

如果有重复值,且需要让我们得到最左值、最右值或全部值,那么前面的代码我们只能找到一个。

我们需要想到一种方法找到最左和最右值。找到全部值其实等价于找到最左值和最右值。

如果直接遍历的话可能使得复杂度退化,是绝对不可取的!

其实我们在上面的问题中只需要将条件改一下,找最左值就变成,想找最右值就把改成

这样找最左值时我们找到目标值还要向左查找,找最右值时找到目标值要向右查找。不过写代码时还要注意一些细节。

改自 C++二分查找_shldy1999的博客-CSDN博客_二分查找c++

找最左值

//循环
int binary_search(int left,int right,int x) {
    int mid;
    while (left <= right) {
        mid = left + ((right - left) >> 1);  //直接平均可能会范围超限,所以用位运算,即除以2的1次方
        if (a[mid] < x)    left = mid + 1;
        else if (a[mid] >= x)    right = mid-1; //精髓
    }
    return a[left]==x? left:-1; //循环结束时实际上是left>right,所以left指的就是最左值
}

//递归错误示范
int binary_search(int left,int right,int x) {
    int mid = left + ((right - left) >> 1);
    if(x <= a[mid])    return binary_search(left,mid,x);
    //同上代码注释
    else if(x > a[mid])    return binary_search(mid+1,right,x);
    else    return mid;
}

但这样可以吗?——不可以!因为如果按照小于等于的逻辑去排,那么永远都递归不完,最终只会

boom:

找最右值

//循环
int binary_search(int left,int right,int x) {
    int mid;
    while (left <= right) {
        mid = (left+right) >>1;  //直接平均可能会范围超限,所以用位运算,即除以2的1次方
        if (a[mid] <= x)    left = mid+1; //精髓
        else if (a[mid] > x)    right = mid - 1;
    }
    return a[right]==x? right:-1; //同最左值
}

——————————————————————————————————————————

因为left=mid+1会越过边界值,所以我们采用这种写法,然而这样会出问题。我们在无重复值的代码里提到过,当left=1,right=2,那么mid将等于1,因此比较后如果a(mid) < b那么left必须等于mid+1,否则left,right和mid将不变,进入死循环。在这里也一样适用。因此此种方法不行。
解决的方法可能有很多,其中一种是的是不寻找最右值,改为寻找最右值右边的值,这样就不用担心left=mid+1会越过边界值。代码如下:
以上摘自 C++二分查找_shldy1999的博客-CSDN博客_二分查找c++
//函数内循环部分
while (left!=right) {
    mid = left + ((right - left) >> 1);
    if (a[mid] > x)    right = mid;
    else if (a[mid] <= x)    left = mid + 1;
    else{
        ret=mid;
        break;
    }
}
//需要特判
if(a[left]==x)    return left-1;
else    return left;

——————————————————————————————————————————

上面这一部分是参考了别人的博客,然而经过实验发现并不准确,其实并不会发生以上情况。(个人实验,仅为参考,有问题可以评论指出)

总结

二分查找是C++算法的重要部分和难点,up主也是查阅的多位dalao的博文才勉强弄懂,希望大家能潜心理解,多找例子做,多看文章……相信所有的困难都将在努力面前灰飞烟灭!

代码如果有问题的话请评论或私信up主,看到及时改正。

感谢 @TheNew123 指正错误

掰掰ヾ(•ω•`)o

本文可以转载,请注明作者,谢谢

  • 9
    点赞
  • 56
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值