LeetCode相关典型题解合集——二分查找

所有的题型目录在下面的链接
LeetCode相关典型题解合集(两百多道题)


二分查找的三个模板

在这里主要写二分查找的和新模板
注意:对于
int mid = left + (right - left) / 2
可以写成
int mid = left + (right - left) >>1;
为什么不能写成?
int mid = (left + right) / 2
因为left+right会造成最大值溢出

模板1

int left = 0, right = nums.length - 1;
  while(left <= right){
    // Prevent (left + right) overflow
    int mid = left + (right - left) / 2;
    if(nums[mid] == target){ return mid; }
    else if(nums[mid] < target) { left = mid + 1; }
    else { right = mid - 1; }
  }
  1. 模板1是二分查找的最基本的模板
  2. 向左查找是right=mid-1
  3. 向右查找是left=mid+1

模板2

 while(left < right){
    // Prevent (left + right) overflow
    int mid = left + (right - left) / 2;
    if(nums[mid] == target){ return mid; }
    else if(nums[mid] < target) { left = mid + 1; }
    else { right = mid; }
  }
  1. 对于left是赋值为mid+1
  2. 对于right是赋值为mid

这个模板是最常用的,退出的时候一定时left=right,所以把答案留到退出循环以后在判断,尤其是边界问题

模板3

while (left + 1 < right){
        // Prevent (left + right) overflow
        int mid = left + (right - left) / 2;
        if (nums[mid] == target) {
            return mid;
        } else if (nums[mid] < target) {
            left = mid;
        } else {
            right = mid;
        }
  1. 向左查找:right = mid
  2. 向右查找:left = mid

69. Sqrt(x) (Easy)

思路:二分查找,初始化的界限就是0到x,然后根据二分查找的方法继续往下做就行
二分查找记好这种写法,记住。这是模板

写法一

 int mySqrt(int x) {
        int l=0;
        int r=x;
        int temp=-1;
        while(l<=r){
            int mid=(l+r)/2;
            //因为只保留整数的部分而忽略小数部分,因此要返回的值肯定是小于等于
            if((long int)mid*mid<=x){
                temp=mid;
                l=mid+1;
            }else{
                r=mid-1;
            }
        }
        return temp;
    }

744. Find Smallest Letter Greater Than Target (Easy)

这道题用线性扫描就行,但是用二分查找效率会高点

char nextGreatestLetter(vector<char>& letters, char target) {
        int l=0;
        int r=letters.size()-1;
        if(target>=letters[r]||target<letters[0]){
            return letters[0];
        }
        while(l<r){
            int mid=l+((r-l)>>2);
            if(letters[mid]>target){
                r=mid;
            }else{
                l=mid+1;
            }
        }
        return letters[l];
    }

540. Single Element in a Sorted Array (Medium)

思路:这道题因为限定了logn的时间复杂度,所以用二分查找来做

  1. 首先确定mid,然后判断是mid左边和mid相等还是mid的右边和mid相等
  2. 然后判断是往左还是往右。这里面举个例子自己就知道了
  3. 注意一点,就是判断中出现了mid+1和mid-1,但是不会越界,因为边界(left或者right)是1和2变化的

做这道题时衍生出来一个问题,困扰了我很久。
当left=0,right=2时。mid应该为1,但
int mid=left+((right-left)>>2)=0
详细解释看这个:c++的左移和右移运算符

int singleNonDuplicate(vector<int>& nums) {
        int left=0;
        int right=nums.size()-1;
        while(left<right){
            int mid=left+((right-left)/2);
            //只算一边是否为偶数就行,另一边自动就知道了
            bool isDouble=(mid-left)%2==0;
            if(nums[mid]==nums[mid+1]){
                 if(isDouble){
                   left=mid+2;
                }else{  
                    right=mid-1;      
                }
            }
            else if(nums[mid]==nums[mid-1]){
                if(isDouble){
                     right=mid-2;
                }else{  
                   left=mid+1;
                }
            }else{
                return nums[mid];
            }
        }
        return nums[left];
    }

278. First Bad Version (Easy)

二分法,最后循环结束得到一个left值,第一个错误的肯定在left的左边或者右边

int firstBadVersion(int n) {
        int left=1;
        int right=n;
        while(left<right){
            int mid=left+(right-left)/2;
            if(isBadVersion(mid)){
               right=mid;
            }else{
                left=mid+1;
            }
        }
        //二分法之后如果这个索引是错误,就要--
        if(isBadVersion(left)){
            while(isBadVersion(left)){
                left--;
            }
        }else{
            while(!isBadVersion(left)){
                left++;
            }
        }
        return left+1;
    }

153. Find Minimum in Rotated Sorted Array (Medium)

看代码应该知道,我们确定往左还是往右搜索的时候是用mid和最右边的值比较的
首先这道题是一个升序的数组,左边的数小而右边的数大,这道题要找最小值,肯定是往左边找(同理,若改成最大值我们可以往右边找,比较最左边的值)
为什么比较mid和right的值呢?因为目标值右边的情况容易区分
这样子想:比如mid值小于最右边的,比如7123456,3<6,这在升序数组中时百分百肯定的,证明我只有mid的左边才会有问题。比如23451,4<1,按照升序数组这是不可能的,我就能确定最小值在mid的右边

int findMin(vector<int>& nums) {
        int left=0;
        int right=nums.size()-1;
        while(left<right){
            int mid=left+(right-left)/2;
            while(left<right){
                int mid=left+(right-left)/2;
                if(nums[mid]>nums[right]){
                    left=mid+1;
                }else{
                    right=mid;
                }
            }
        }
        return nums[left];
    }

34. Find First and Last Position of Element in Sorted Array(重点!)

下面两个链接是将二分法的细节的
二分查找的几种写法和区别
每次遇到二分法,都是一看就会,一写就废

这道题用二分法有点绕,当有序数组中出现两个或多个相同的值是,可以用两次二分法找到重复值出现的边界对应的索引下标,思想如下:
利用二分思想先找其左边界,再找其右边界即可,注意找左边界的时候,由右侧逼近;找右边界的时候,由左侧逼近,即可。
当往左找的时候,right=mid,当带上=号时,也要继续right=mid,这样就可以确定左侧边界。
往右找的时候同理。但是因为两次查找的方向不一致,为了保证边界条件,第二次不能使用n-1。

vector<int> searchRange(vector<int>& nums, int target) {
        vector<int> res(2,-1);
        if(nums.empty()){
            return res;
        }
        int left=0;
        int right=nums.size()-1;
        while(left<right){
            int mid=left+(right-left)/2;
            if(nums[mid]>=target){
                right=mid;
            }else{
                left=mid+1;
            }
        }
        if(nums[left]!=target){
            return res;
        }
        res[0]=left;
        //left和right在上一个循环时值已经发生改变,在第二次时right就不减一了
        right=nums.size();
        left=0;
        while(left<right){
            //这个mid的计算有问题,不然会进入死循环
            int mid=left+(right-left)/2;
            if(nums[mid]<=target){
                left=mid+1;
            }else{
                right=mid;
            }
        }
        res[1]=left-1;
        return res;
    }

总结

一张图彻底理解二分查找!
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值