首页 > 其他分享 >二分法的总结

二分法的总结

时间:2024-06-12 14:29:32浏览次数:17  
标签:总结 right nums int ret 二分法 middle left

一、前言

最初始版的二分法是力扣704. Binary Search,而后面的二分法都是在这个基础上进行的变化

class Solution {
public:
    int search(vector<int>& nums, int target) {
        int left=0;
        int right=nums.size()-1;
        while(left<=right){//在这里选择的是左闭右闭的区间,左闭右开也是可以的
            int middle=(left+right)/2;//这里其实最好还是写成(right-left)/2+left,防止溢出
            if(nums[middle]==target){
                return middle;
            }
            else if(nums[middle]>=target){
                right=middle-1;//right是否能够等于middle-1还是要看自己写的区间是左闭右闭还是左闭右开
            }
            else{
                left=middle+1;
            }
        }
        return -1;
    }
};

其次,我开始刷了第二道求下标的题目力扣35. Search Insert Position,这道题我一开始想不懂为什么突然多了一个ret,为什么要用ret来记录下标,不能直接返回吗?什么时候要用ret来表示,什么时候又不用呢?这些问题在后面解答。

class Solution {
public:
    int searchInsert(vector<int>& nums, int target) {
        int left=0;
        int right=nums.size()-1;
        int ret=nums.size();
        while(left<=right){//这里依旧选择了左闭右闭的区间
            int middle=left+(right-left)/2;
            if(nums[middle]>target){
                ret=middle;
                right=middle-1;
            }else if(nums[middle]<target){
                left=middle+1;
            }else{
                return middle;
            }
        }
        return ret;
    }
};

再接下来就是力扣69. Sqrt(x)。这道题我就开始更蒙圈了,和704比起来,不仅要用ret来记录,而且if条件也变了,虽然我当时想到了要这样写,但是if else判断句直接给我干晕了,为什么这里只用两个判断句就行了呢?

class Solution {
public:
    int mySqrt(int x) {
        int left=0;
        int right=x;
        int ret;
        while(left<=right){//依旧使用左闭右闭区间
            int middle=left+(right-left)/2;
            if((long long)middle*middle<=x){
                ret=middle;
                left=middle+1;
            }else{
                right=middle-1;
            }
        }
        return ret;
    }
};

下一道是力扣153. Find Minimum in Rota,这是一道中等题,前面的都还是简单题,既然是中等题,当然比简单题要难,果然,我尝试用左闭右闭的区间都失败了,最后只能用左闭右开,到底什么时候用左闭右闭,什么时候不能用呢?由于这道题也没有target,我就想着应该是和left和right比,但并不明白是为什么?而且这道题怎么不用ret?为什么最后又要返回nums[left]?

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

下一道是力扣162. Find Peak Element,这道题也是中等题,果然我的边界条件就错得离谱,这道题又需要用到ret了。

class Solution {
public:
    int findPeakElement(vector<int>& nums) {
        auto get=[&](int i)->pair<int,int>{
            if(i==-1 || i==nums.size()){
                return {0,0};
            }else{
                return {1,nums[i]};
            }
        };
        int left=0;
        int right=nums.size()-1;
        int ret;
        while(left<=right){
            int middle=left+(right-left)/2;
            if(get(middle+1)<get(middle) && get(middle-1)<get(middle)){
                ret=middle;
                break;
            }else if(get(middle)<get(middle+1)){
                left=middle+1;
            }else{
                right=middle-1;
            }
        }
        return ret;
    }
};

下一道,力扣33. Search in Rotated Sorte,是旋转数组,在旋转数组中找target,我是直接不知道和二分法有什么关系了,最后从别人的题解里面发现可以找到left到middle的顺序或者right到middle的顺序,可是我做题目的时候怎么想得起来?为什么我联想不到?联想到了又会划分错边界条件。

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

下一道,744. Find Smallest Letter G。感觉这道题和69题很像,除了if判断句条件以及yijiretret需要一个初始值之外几乎没有差别

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

最后一题是力扣441. Arranging Coins,它这道题也是,我无法用左闭右闭的区间去写,而且也用到了等差数列求和的一个问题,我一开始也是想不到,而且关于left的初始值以及middle的值的设置也是有很多小坑。并且left居然不可以等于middle+1,会超时,而且最后返回的值居然变成了left?为什么?

class Solution {
public:
    int arrangeCoins(int n) {
        int left=1;
        int right=n;
        while(left<right){
            int middle=left+(right-left+1)/2;
            if((long long)middle*(middle+1)<=(long long)2*n){
                left=middle;
            }else{
                right=middle-1;
            }
        }
        return left;
    }
};

二、总结

相信大家在刷二分法的时候也会遇到一些相似的问题,接下来就一一解答吧

1.ret什么时候使用

这个其实就是去看题目的要求,如果说题目只是要找到target的下标,那么可以不需要用到额外的变量去存储,但如果需要让我们搜索插入位置,又或者说寻找峰值的话,如果不用变量去保存的话,很容易就会丢失上一个数据。这个还是比较好判断到底需不需要用ret。完全可以先写出大概的代码大纲之后进行细节补充,然后发现需要用一个变量保存的时候再去添加一个变量就好了。当然了,要注意变量的初始值,可以回去看看题目要求,尤其是边界条件。

2.什么时候不能用左闭右闭

用几个示例代入代码,看是否会发生死循环,如果会,就说明不能用left<=right当做while循环的条件

3.返回值到底怎么判断是什么

其实也可以把一个实例带入代码,看最后究竟哪一个才是题目要得到的值。多做几道题之后就会有感觉了。

4.边界条件,越界

5.left和right到底怎么写?怎么有时候是=middle,有时候又是middle+1的

(1)区间问题,看自己的区间是左闭右闭还是左闭右开

(2)或者也可以用实例带入代码

三、题目分类

1.普通二分查找,求下标(或者其数值)

        35. 搜索插入位置

        704. 二分查找

2.二分求最小

3.二分求最大

4.其他

        162. 寻找峰值

       

四、不同题目分类的解法(待续)

标签:总结,right,nums,int,ret,二分法,middle,left
From: https://blog.csdn.net/2301_80161204/article/details/139621951

相关文章

  • redis知识点总结
    redis知识点什么是redisredis是一个基于内存的数据库,对数据的读写都在内存中完成,因此读写速度非常快,常用于缓存,消息队列,分布式锁等场景。除此之外,redis还支持事务,持久化,Lua脚本,多种集群方案,哨兵模式,切片集群,主从复制模式,发布/订阅模式,内存淘汰机制,过期删除机制。redis......
  • 个人总结
    在本学期的软件工程课程中,我经历了丰富的学习和成长。在课程开始时,我设定了学习目标,包括掌握Android开发等技能。尽管遇到了一些挑战,但我成功地掌握了Android开发的基本知识和技能。通过课堂学习、实践项目以及课后自主学习,我学会了使用AndroidStudio等开发工具,掌握了UI设计、用......
  • 软件工程课程 结组项目 事后总结分析报告
    从结果来看,我们完成的还是挺不错的,Web端,Android端,服务端,正常的使用流程,还算不错的界面,蹭了一些时兴的技术,按照截止日期交活。实际上这个项目是一堆大问题,我负主要责任吧,虽然不是组长,但它确实从选题,分工,开发,都主要是我一个人操办和完成的。最主要的疏忽,我想是对其他人的进度的监督......
  • 2024.6.12(个人总结)
    所学时间:2小时代码行数:110博客园数:1篇所学知识:  在第一周的课程计划中,我着重安排了学习安卓端的开发应用、掌握javaweb框架的应用、以及开始熟悉数据库的增删改查操作。下面是我在这些方面的具体进展:安卓端的开发应用,学习并掌握了安卓应用的基本结构,包括活动(Activity)、布......
  • 每日总结61
    MATLAB实验五:MATLAB最优化工具箱的使用(1)线性规划应用案例的求解1、基本要求通过一个农业生产计划优化安排的实例求解,培养学生解决实际线性规划问题的初步能力;熟悉线性规划的建模过程;掌握Matlab优化工具箱中线性规划函数的调用。2、主要内容某村计划在100公顷的土地上种植a......
  • [DP] DP优化总结
    写在前面$DP$,是每个信息学竞赛选手所必会的算法,而$DP$中状态的转移又显得尤为关键。本文主要从状态的设计和转移入手,利用各种方法对朴素$DP$的时间复杂度和空间复杂度进行优化与处理,以达到满足题目要求的目的;参考文献:动态规划算法的优化技巧毛子青c++DP总结《算......
  • C / C++ 保留两位小数(setprecision(n)的一些用法总结)
    转载:https://blog.csdn.net/qq_36667170/article/details/79265224做题遇到保留两位小数的题目,课本上写的又多又杂,网上查来的也是一堆内容需要筛选,눈_눈还是自己总结一下吧。首先说C++代码 #include<iomanip>//不要忘了头文件 //第一种写法 cout<<setiosflags(io......
  • 【课程总结】Day7:深度学习概述
    前言本篇文章,我们将通过示例来逐步学习理解导数、求函数最小值、深度学习的本质、以及使用numpy和pytorch实操深度学习训练过程。线性回归线性回归内容回顾在《【课程总结】Day5(下):PCA降维、SVD分解、聚类算法和集成学习》中,我们已经了解到线性回归以及线性回归可以表......
  • 持续总结中!2024年面试必问 20 道分布式、微服务面试题(九)
    上一篇地址:持续总结中!2024年面试必问20道分布式、微服务面试题(八)-CSDN博客十七、什么是配置管理在微服务架构中的重要性?在微服务架构中,配置管理是确保系统灵活性、可维护性和可扩展性的关键组成部分。以下是配置管理在微服务架构中的重要性:1. 环境一致性:微服务架构通常......
  • 苹果WWDC超全总结:GPT-4o加入iOS 18 | 最新快讯
    如果不是本届WWDC24(苹果全球开发者大会)最后阶段,苹果重新定义了AI,用「AppleIntelligence」取代「ArtificialIntelligence」,那么这场苹果年度盛会的高光时刻将会变成「iPad终于有了计算器应用」这种愚人节玩笑水平的更新。但好在,苹果玩的「谐音梗」,经得起推敲和琢磨......