二分查找总结

所谓二分查找,就是对区间进行分解从而缩小区间范围进而找到所需值,二分查找的条件是有序,优点是一般时间复杂度为O(logn).

下面看题:

第一题

在这里插入图片描述

二分查找的难点在于哪里,就在于while(left<right)还是while(left<=right)还有right = middle还是right = middle - 1。这两者怎么区分呢,其实主要就是看你的区间的定义,看你定义的区间是左闭右闭的还是左闭右开的(按道理其他也行,但是一般不用)。

如果区间定义为左闭右闭,那么就是while(left <= while),因为这个时候等于有意义了,而且right = middle -1,因为middle因为比较过了,之后不用再比较了,如果不减1则middle又进入有效区间了。

两种都挺好,具体都可以写,不过写的时候要保持这个定义不变,而且要看最后的left和right代表的是不是自己想要的,需不需要进一步处理。

所以存在下面两种写法:

class Solution {
public:
    int search(vector<int>& nums, int target) {
        int left=0, right=0, middle = 0;
        right = nums.size() - 1;
        while (left <= right) {
            middle = left + (right - left) / 2; // 这样写是为了防止溢出
            if (nums[middle] == target) {
                return middle;
            }
            else if (nums[middle] < target) {
                left = middle + 1;
            }
            else if (nums[middle] > target) {
                right = middle - 1;
            }
        }

        return -1;
    }
};
class Solution {
public:
    int search(vector<int>& nums, int target) {
        int left=0, right=0, middle = 0;
        right = nums.size();
        while (left < right) {
            middle = left + (right - left) / 2; // 这样写是为了防止溢出
            if (nums[middle] == target) {
                return middle;
            }
            else if (nums[middle] < target) {
                left = middle + 1;
            }
            else if (nums[middle] > target) {
                right = middle;
            }
        }

        return -1;
    }
};
第二题

在这里插入图片描述

我感觉这个题有点莫名其妙的,好像和二分查找真没啥区别,不过可能最好还是用左闭右开的区间,因为插入有可能插入到最后一个位置,用左闭右闭不好表示了。

class Solution {
public:
    int searchInsert(vector<int>& nums, int target) {
                int left=0, right=0, middle = 0;
        right = nums.size();
        while (left < right) {
            middle = (left + right) / 2;
            if (nums[middle] == target) {
                return middle;
            }
            else if (nums[middle] < target) {
                left = middle + 1;
            }
            else if (nums[middle] > target) {
                right = middle;
            }
        }

        return right;
    }
};
第三题

在这里插入图片描述

其实这里面的题都是环环相扣的,好多都可以用前一道题的东西。例如这题吧,其实很明显是要用两次二分查找了,因为一次二分查找肯定解决不了问题了,一次二分查找最多找到一个位置,找到一个位置后又怎么确定左右两边有没有target呢。

用两次二分查找,而且这个二分查找吧,是可以很灵活的变换的,例如这里我们就变换了一下,把middle = target也变成了往左移,这样每次二分查找找到的就是最左边的和target相等的元素了,当然我们可以再写一个函数把middle = target变成往右移,不过我们这里可以直接去找target+1的元素,如果左边找到了则把右边位置减1即可。注意这里找target+1找的也是最左边的元素。

不过我觉得其实写两个函数也没啥,方法笨一点也没事。

第四题

在这里插入图片描述

这个也可以用二分查找,就看区间中间那个数的平方和x大小的比较,如果小于等于且中间数加1大于x,说明就是它了,否则改变区间。

/**
 * 解题思路:设定左边界(0)和右边界(x),左闭右闭,开始循环,求出中间位置元素值,若中间元素小于等于x且中间元素加1大于x,则返回该元素。
 * 若中间元素大于x,则将右边界设置为中间位置减1,若中间元素小于x,则将左边界设置为中间位置加1.
 * 
 */
class Solution {
public:
    int mySqrt(int x) {
        int left = 0, right = x;
        if(x == 0 || x == 1)
            return x;
        while (true) {
            int middle = left + (right - left) / 2;
            int temp1 = middle + 1;
            if (middle <= (x / middle) && temp1 > (x / temp1))
                return middle;
            else if (middle < (x / middle))
                left = middle + 1;
            else if (middle > (x / middle))
                right = middle - 1;
        }
    }
};

上面这个版本为啥要用除法呢,主要是编译器提醒我int溢出了,所以我就用了除法,也可以用long long

class Solution {
public:
    int mySqrt(int x) {
        int left = 0, right = x;
        while (true) {
            long long middle = left + (right - left) / 2;
            if ((middle * middle) <= x && ((middle + 1) * (middle + 1)) > x)
                return middle;
            else if (middle * middle < x)
                left = middle + 1;
            else if(middle * middle > x)
                right = middle - 1;
        }
    }
};
第五题

在这里插入图片描述

这题咋说呢,要先做了第四题,这题就是送的,直接用第四题的代码,然后在算出算术平方根后看看是不是平方得x,主要思想还是二分法吧,对了,第四题和第五题都可以用牛顿迭代法,不过我计算方法当初学的时候就没认真,现在更是忘得差不多了。

总结

总之,这个二分查找吧,前提条件是有序区间,作用是提高查找速率,用法就是要注意你的区间的定义不要变,是左闭右闭还是左闭右开。还有最后得到的结果要不要再处理一下。写完收工。
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值