二分查找专题

目录

 

二分查找专题

基本框架

常见的几个问题

实战


二分查找专题

  • 基本框架

int binarySearch(vector<int>& nums,int target)
{
    int left = 0;
    int right = ...;
    int mid = 0;
    while(...){
        mid = left + ((right-mid) >> 1);
        if(nums[mid] == target)
            ...;
        else if(nums[mid] < target)
            left = ...;
        else if(nums[mid] > target)
            right = ...;
    
    }
    return ...;
}
  • 常见的几个问题

1.right的值,是nums.size() - 1  还是  nums.size()  ?

如果right = nums.size(),则搜索区间为[ left , right ),是左闭右开区间

如果right = nums.size() - 1,则搜索区间为[ left , right ],是左右都闭合区间

2.while循环条件,是 left <= right 还是 left < right ?

如果是<,则说明 left 不可以等于 right,搜索的是左闭右开区间[ left , right ),即对应的 right 初始化值应该为 right = nums.size(); 终止条件为 left == right;

如果是<=,则说明 left 可以等于 right,搜索的是左右都闭合区间[ left , right ],即对应的 right 初始化值应该为 right = nums.size() - 1; 终止条件为 left = right + 1;

3.返回值

如果前面right = nums.size() ,那么while循环条件应该为 left < right,则返回值需要检查,因为当nums = {5}只有一个元素时,left == right 直接跳出,返回值left或right都一样,要处理left

如果前面right = nums.size() - 1,那么while循环条件应该为 left <= right,则返回值需要检查left或者right是否越界,因为left == right时也会处理

4.mid值的计算

mid = ( left + right ) / 2,两个int型相加可能出现溢出

mid = left + (right - left) / 2,变形防止溢出,但除法操作比较慢

mid = left + ((right - left) >> 1),位运算操作速度更快,但是要注意:"+"的优先级高于位运算符">>",需要加括号

  • 实战

难度递增,代码统一风格为左右都闭合区间

704.二分查找

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

35.搜索插入位置

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

34.在排序数组中查找元素的第一个和最后一个位置

class Solution {
public:
    vector<int> searchRange(vector<int>& nums, int target) {
        int size = nums.size();
        if(size <= 0) return {-1,-1};
        vector<int> res;
        res.push_back(searchFirst(nums,target,size));
        res.push_back(searchLast(nums,target,size));
        return res;
    }
    int searchFirst(vector<int>& nums, int target,int size){
        int left = 0;
        int right = size -1;
        int mid = 0;
        while(left <= right){
            mid = left + ((right-left)>>1);
            if(nums[mid] == target)
                right = mid - 1;
            else if(nums[mid] > target)
                right = mid - 1;
            else if(nums[mid] < target)
                left = mid + 1;
        }
        //最后left可能越界
        if(left >= size || nums[left] != target)
            return -1;
        return left;
    }
    int searchLast(vector<int>& nums, int target,int size){
        int left = 0;
        int right = size -1;
        int mid = 0;
        while(left <= right){
            mid = left + ((right-left)>>1);
            if(nums[mid] == target)
                left = mid + 1;
            else if(nums[mid] > target)
                right = mid - 1;
            else if(nums[mid] < target)
                left = mid + 1;
        }
        //最后right可能越界
        if(right < 0 || nums[right] != target)
            return -1;
        return right;
    }
};

153.寻找旋转排序数组中的最小值(不含重复数字)

class Solution {
public:
    int findMin(vector<int>& nums) {
        int size = nums.size();
        if(size <= 0) return -1;
        int left = 0;
        int right = size -1;
        int mid = 0;

        while(left <= right){
            mid = left + ((right-left) >> 1);
            //将数组分为左右两部分,要找的最小值就是把数组分为两部分的数
            //看最后一个数,因为最后一个数是右半部分最大值
            //如果mid比右半部分最大值大,说明最小的数在右半部分
            //如果mid比右半部分最大值小,说明最小的数在左半部分
            //如果mid等于右半部分最大值,说明现在left == right,则必须跳出,否则死循环
            if(nums[mid] > nums[right])
                left = mid + 1;
            else if(nums[mid] < nums[right])
                right = mid;
            else if(nums[mid] == nums[right])
                break;    
        }
        return nums[left];
    }
};

154.寻找旋转排序数组中的最小值(含重复数字)

class Solution {
public:
    int findMin(vector<int>& nums) {
        int size = nums.size();
        if(size <= 0) return -1;
        int left = 0;
        int right = size -1;
        int mid = 0;
        while(left <= right){
            mid = left+((right-left)>>1);
            if(nums[mid] == nums[right])
                //不能确定向左或向右,线性缩小区间
                right--;
            else if(nums[mid] > nums[right])
                left = mid + 1;
            else if(nums[mid] < nums[right])
                right = mid;
        }
        return nums[left];
    }
};

33.搜索旋转排序数组(查指定值,不含重复数字)

class Solution {
public:
    int search(vector<int>& nums, int target) {
        int size = nums.size();
        if(size <= 0) return -1;
        int left = 0;
        int right = size - 1;
        int mid = 0;
        while(left <= right){
            mid = left + ((right - left) >> 1);
            if(nums[mid] == target)
                return mid;
            //[left,mid]是连续递增的
            if(nums[left] <= nums[mid]){
                //target在[left,mid)之间,到[left,mid-1]找
                if(nums[left] <= target && target < nums[mid])
                    right = mid - 1;
                //target在(mid,right]之间,到[mid+1,right]找
                else
                    left = mid + 1;
            }
            //[mid,right]是连续递增的
            else if(nums[left] > nums[mid]){
                //target在(mid,right]之间,到[mid+1,right]找
                if(nums[mid] < target && target <= nums[right])
                    left = mid + 1;
                //target在[left,mid)之间,到[left,mid-1]找
                else
                    right = mid - 1;
            }
        }
        return -1;
    }
};

81.搜索旋转排序数组 II(查指定值,含重复数字)

class Solution {
public:
    bool search(vector<int>& nums, int target) {
        //含重复值
        int size = nums.size();
        if(size <= 0) return false;
        int left = 0;
        int right = size - 1;
        int mid = 0;
        while(left <= right){
            mid = left + ((right - left) >> 1);
            if(nums[mid] == target)
                return true;
            if(nums[left] == nums[mid]){
                //nums[left] == nums[mid]时,无法判断向左还是向右,缩小区间
                left++;
                continue;
            }
            //[left,mid]连续递增
            if(nums[left] <= nums[mid]){
                //[left,mid)中找target
                if(nums[left] <= target && target < nums[mid])
                    right = mid - 1;
                //(mid,right]中找target
                else
                    left = mid + 1;
            }
            //[mid,right]连续递增
            else{
                //(mid,right]中找target
                if(nums[mid] < target && target <= nums[right])
                    left = mid + 1;
                //[left,mid)中找target
                else
                    right = mid - 1;
            }         
        }
        return false;
    }
};

图解待后续补充。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值