你真的会写二分查找吗?

上个月面试网易,面试官要我写个二分查找,查找与target相等的数字的下标。。。我心想:简单;刷刷一写,面试官说:你这不对,int mid  = (st + ed) >> 1;会溢出;

我:????

那好吧 我改成unsigned int tmp = st + ed ;int mid = tmp >> 1;

面试官: 你不用这么复杂?

我:???

然后 面试官又提了一些需求,比如允许重复的数组中相等的最后一个数字的下标,允许重复的数组中相等的最前一个数字的下标,旋转数组中的二分查找?允许重复的旋转数组中实现二分查找?

印象中二分查找不是很容易的吗?看来还是我太年轻了啊。。。

 二分查找其实有好几个坑。下面我来记录一下

1.<=还是<?

其实都可以 只不过我个人习惯于使用<=

2.使用<=的时候会出现一个问题,那就是可能会出现死循环!

为什么呢?来看一下下面这个错误示例,问题为已知一列可能有重复数字的有序数组,求某个元素出现时的第一个下标。

int findIndex(int target, int nums[], int n)
{
    int st = 0;
    int ed = n - 1;
    while (st <= ed)
    {
        int mid = st + (ed-st)/2;
        if (nums[mid] < target) st = mid + 1;
        else if (nums[mid] > target) ed = mid - 1;
        else ed = mid; //注意这一行
    }
    return st;
}

这个代码实际上是有问题的,原因在于被我标记的那一行在st = ed = mid 且nums[mid] == target的时候,程序会死循环

所以应该改为

int findIndex(int target, int nums[], int n)
{
    int st = 0;
    int ed = n - 1;
    while (st <= ed)
    {
        int mid = st + (ed-st)/2;
        if (nums[mid] < target) st = mid + 1;
        else ed = mid - 1;
    }
    return st;
}

就满足要求了

除此之外还有其他变式,比如:

求第一个大于num的数的下标

求第一个小于num的数的下标

求第一个不大于num的数的下标

求第一个不小于num的数的下标

求最后一个小于num的数的下标

......

这类问题怎么求呢?

有篇博客说得好,

你真的会写二分查找吗 - luoxn28 - 博客园

有个模板

    while (left <= right) {
        int mid = left + ((right - left) >> 1);
        int tmp = array[mid];
        if (tmp ? target) {
            right = mid - 1;
        }
        else {
            left = mid + 1;
        }

        return ?

由于边界条件right = left - 1;所以我们可以根据要求的是right还是left确定这两个问号填啥

如果是1 2 2 3 4 5,如果要求大于等于2的第一个数,那么肯定是求left(因为right指向1,left指向第一个3),我们可以根据left来确定;

然后是大于小于号的问题,这个肯定是当前中间大于目标,因为要左移右边界

第三个是加不加等号的问题,如果匹配到target,可能还需要不断左移右边界,所以是有等号的

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值