优化算法1 — 二分查找

二分查找

1.朴素二分查找

朴素二分查找时while内的判断条件是while(left <= right),求解mid = left + (right - left) / 2,当nums[mid] > target,说明目标值在mid的左面,right = mid - 1,反之,left = mid + 1

1.LeetCode704.二分查找
代码解析:

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

2.左右边界的二分查找(重点)

当数据具有二段性时可以考虑使用二分查找,二分查找是最容易写出死循环的算法,一定注意left / right的变化和while中的判断条件

模板总结:1. 查找区间左端点的模板 / 2. 查找区间右端点的模板

模板总结(重点)

查看数组是否具有二段性(以中间某个数为界,左边数(不含此数)均小于此中间点数,右边界数(含此数)均大于等于此中间数)

不同点:假设nums[mid] = x,当x < target,说明目标值在mid的右边,此时与朴素二分查找一样left = mid + 1,假设 x >= target,此时说明mid已经命中target所在的区间,但是并不能确定是否位于右边界点处,此时与而朴素二分查找不同,mid可能处于右边界点处,如果right - 1,right指向会发生错误,所以right = mid,在下一次mid继续向前判断

模板: x < target -> left = mid + 1 / x >= target -> right = mid

left不断跳出不符合target的区域,right在符合target的区域内不动

循环条件

重点:决定算法是否会进入死循环的关键:while(left < right)
注意没有等号!!!

三种情况: 1.当数组符合中间值的左边小,右边大时,且有等于target的值此时left = right时就是最终结果无需判断。 2.数组中的值全部大于target,right最终会移动到left位置,此时若nums[left] != target则返回-1,若判断 left == right(mid 恒= left)则陷入死循环 3.数组中的值全部小于target,left最终会移动到right的位置

求中点的操作

仅数组个数为偶数时需要考虑(可能进入死循环的条件之一!!!)

mid = left + (right - left + 1) / 2(靠左位置) 或 mid = left + (right - left) / 2(靠右位置)(依据题目要求自行进行判断)— 方法:mid落在left = mid + 1的left上 或 right = mid - 1的right上(right - left落在靠左即left的位置,不能出现left = mid的情况,同理right - left + 1落在靠右即right的位置,不能出现right = mid的情况)

总结:二分查找的三大细节:模板中对于left / right移动的处理 / 循环条件(正常情况下都是 left < right)/ 中间点mid的判断 / 解题策略:当查找区间左端点时,左边界的值均小于target,右边界的值大于等于target,反之,当查找区间右端点时,左边界的值小于等于target,右边界的值大于target(上述模板均已查找区间左端点为例,查找区间右端点要修改模板中left / right移动的条件 : x <= target -> left = mid / x >= target -> right = mid - 1)

2.LeetCode34.二分查找模板的应用
代码解析:

class Solution {
public:
    vector<int> searchRange(vector<int>& nums, int target) 
    {
        if(nums.size() == 0) return {-1, -1}; // 直接返回数组的形式
        
        vector<int> v;
        int left = 0, right = nums.size() - 1;
    //查找二分区间左端点模板 - left = mid + 1 / right = mid(< 和 >=)
        while(left < right)
        {
        // mid落在left,即左端点上(left = mid + 1)
            int mid = left + (right - left) / 2;
            if(nums[mid] < target)
                left = mid + 1;
            if(nums[mid] >= target)
                right = mid;
        }
        if(nums[left] == target) v.push_back(left);
        else v.push_back(-1);
    //查找二分区间右端点 - left = mid / right = mid - 1(<= 和 >)
        left = 0, right = nums.size() - 1;
        while(left < right)
        {
        // mid落在right,即右端点上(right = mid - 1)
            int mid = left + (right - left + 1) / 2;
            if(nums[mid] <= target)
                left = mid;
            if(nums[mid] > target)
                right = mid - 1;
        }
        if(nums[left] == target) v.push_back(left);
        else v.push_back(-1);
        
        return v;
    }
};

总结:查看数组是否具有二段性(不一定有序),依据题意改变二分查找的判断条件

3.题目练习链接

LeetCode35.搜素插入位置
LeetCode69.x的平方根
LeetCode852.山脉数组的峰顶索引
LeetCode162.寻找峰值
LeetCode153.寻找旋转排序数组中的最小值

总结:二分查找是一种时间复杂度为O(logN)的算法,可以解决一些需多次遍历数组的题目,技巧方面,若下面right出现mid - 1,则上面要right - left + 1,即下面出现-1,上面就+1,一句题目分析,就题论题即可

End!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值