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;
    }

总结

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 这道题是一道字符串处理题。给定一个字符串 s,它的所有的字符都是小写的英文字母。要求把这个字符串变成一个按照字典序排序的字符串,并且要求在变换过程中只能在原来的字符串中交换两个相邻的字符。 解题思路: - 从前往后扫描字符串 s,找到第一个不满足字典序的字符,记为字符 x。 - 从 x 往后扫描,找到最后一个比 x 大的字符 y,将 x 与 y 交换。 - 将 x 后面的字符串倒序排列,这样就得到了字典序更大的字符串。 下面是 Java 代码的实现: ``` class Solution { public String nextPermutation(String s) { char[] chars = s.toCharArray(); // 从后往前找到第一个不满足字典序的字符 x int i = chars.length - 2; while (i >= 0 && chars[i] >= chars[i + 1]) { i--; } // 如果 i < 0,说明原来的字符串已经是字典序最大的字符串,直接返回倒序排列的字符串 if (i < 0) { reverse(chars, 0, chars.length - 1); return new String(chars); } // 从 x 往后扫描,找到最后一个比 x 大的字符 y int j = chars.length - 1; while (j > i && chars[j] <= chars[i]) { j--; } // 将 x 与 y 交换 swap(chars, i ### 回答2: 题目LeetCode第38题:报数 题目描述: 给定一个正整数n,输出报数序列前n个数。 报数规则:从1开始报数,数到3的倍数时报Fizz,数到5的倍数时报Buzz,数到同时是3和5的倍数时报FizzBuzz,其他情况下则直接报数。 解题思路: 使用循环遍历1到n的所有数字,按照报数规则进行判断并输出。 具体步骤如下: 1. 创建一个StringBuilder对象res,用于存储报数序列。 2. 使用for循环从1遍历到n。 3. 判断当前数字是否同时是3和5的倍数,如果是,则将"FizzBuzz"添加到res中。 4. 判断当前数字是否是3的倍数,如果是,则将"Fizz"添加到res中。 5. 判断当前数字是否是5的倍数,如果是,则将"Buzz"添加到res中。 6. 如果以上条件都不满足,则将当前数字转换为字符串并添加到res中。 7. 循环结束后,将res转换为字符串并返回。 Java代码如下: ```java public String countAndSay(int n) { StringBuilder res = new StringBuilder(); for (int i = 1; i <= n; i++) { if (i % 3 == 0 && i % 5 == 0) { res.append("FizzBuzz"); } else if (i % 3 == 0) { res.append("Fizz"); } else if (i % 5 == 0) { res.append("Buzz"); } else { res.append(Integer.toString(i)); } } return res.toString(); } ``` 以上代码可以将1到n的报数序列输出,并按照题目要求进行相应转换。 ### 回答3: 题目要求是根据给定的正整数 n,返回一个字符串,该字符串包含从 1 到 n 的所有数字对应的字符串,并且满足以下条件: 1. 如果数字能被 3 整除,则使用字母 "Fizz" 替代该数字。 2. 如果数字能被 5 整除,则使用字母 "Buzz" 替代该数字。 3. 如果数字能同时被 3 和 5 整除,则使用字母 "FizzBuzz" 替代该数字。 解题思路: 利用循环遍历从 1 到 n 的所有数字,使用条件语句判断每个数字是否满足以上三个条件,然后根据条件替换数字并存入结果字符串中,最后返回结果。 Java代码如下: ```java class Solution { public String fizzBuzz(int n) { StringBuilder result = new StringBuilder(); for (int i = 1; i <= n; i++) { if (i % 3 == 0 && i % 5 == 0) { result.append("FizzBuzz"); } else if (i % 3 == 0) { result.append("Fizz"); } else if (i % 5 == 0) { result.append("Buzz"); } else { result.append(i); } if (i != n) { result.append(" "); } } return result.toString(); } } ``` 这段代码使用StringBuilder来构建结果字符串,判断每个数字是否满足条件,并根据条件拼接对应的字符串,每个数字之间用空格隔开。最后将StringBuilder转换成String并返回。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值