算法入门-二分搜索(长期更新)

文章目录

      • 情景一 : 二分查找
      • 情景二 : 找出一个 >= num 的最左侧的位置
      • 情景三 : 找出一个 <= num 的最右侧的位置
      • leetcode 162 :寻找峰值
      • leetcode 69 : x 的平方根

首先来简介一下二分搜索算法,二分搜索是一种每次砍半的算法,最经典的案例当然是我们的二分查找算法,但是大部分人所认知的二分搜索都是必须要满足这个 数组有序,才可以进行,其实不然,二分的本质逻辑是建立在"砍半",而砍半的本质就是你知道那一半一定没有,而另一半不一定有的前提下,只要满足了这一前提条件,那么你就可以尽可以进行二分…这个算法的时间复杂度是O(logN)
我们这个算法的系列将会长期更新,在遇见好题了之后就会加进来进行整理

情景一 : 二分查找

这个是二分最经典的情景,也就是在一个数组中进行每次砍半的查找元素
代码实现如下

public static int myBinarySearch(int[] nums,int key){
        if(nums == null || nums.length == 0){
            System.out.println("无法进行搜索");
            return -1;
        }
        int left = 0;
        int right = nums.length - 1;
        int mid;
        while(left <= right){
            //请注意这里我们对于我们这个平均值的处理情况
            mid = left + ((right - left)>>1);
            if(nums[mid] > key){
                right = mid - 1;
            }else if(nums[mid] < key){
               left = mid + 1;
            }else{
                return mid;
            }
        }
        return -1;
    }

注意一下我们这里的求其平均值的操作,是利用位运算,其一是防止其溢出,其二是加快运算的速度,因为位运算的速度要远大于除法运算…

情景二 : 找出一个 >= num 的最左侧的位置

这个其实也是二分的逻辑,我们定义一个标记物 ans 初始化置为-1,当我们的mid满足条件的时候,我们就将我们的ans置为 mid ,然后继续二分,当不满足条件的时候,我们就不进行操作,继续二分,然后最后返回我们的 ans 标记物…
代码实现如下 :

/**
     *      情景二 : 找到 >= num 的最左侧的位置
     *      这个基本的逻辑跟二分搜索法其实是一致的,但是也是有一定的差别,比如这个必须要搜索彻底才可以
     *      当每次满足条件的时候,就进行 ans 的更新...直到left == right;
     *
     *      可能你有疑问:为什么我们不进行 <= num 的最左侧位置的查找 ...
     *      因为这个问题是没有意义的..因为只需要判断 nums[0] 跟 key的关系
     * @param nums : 待搜索的数组
     * @param key : 待定的查找元素
     * @return
     */
    public static int findNumMaxLeft(int[] nums,int key){
        if(nums == null || nums.length == 0){
            System.out.println("无法进行搜索");
            return -1;
        }
        int left = 0;
        int right = nums.length - 1;
        int mid;
        int ans = -1;
        while(left <= right){
            mid = left +((right - left) >> 1);
            if(nums[mid] >= key){
                ans = mid;
                right = mid - 1;
            }else if(nums[mid] < key){
                left = mid + 1;
            }
        }
        return ans;
    }

情景三 : 找出一个 <= num 的最右侧的位置

这个其实跟情景二是对称的,原理是一致的,当满足条件的时候记录下来继续进行二分,当不满足条件的时候不进行记录然后继续进行二分,到二分到不能再次进行二分的情况之后,就返回我们的标记ans值…
代码实现如下:

public static int findNumMinRight(int[] nums,int key){
        if(nums == null || nums.length == 0){
            System.out.println("无法进行搜索");
            return -1;
        }
        int left = 0;
        int right = nums.length - 1;
        int mid;
        int ans = -1;
        while(left <= right){
            mid = left +((right - left) >> 1);
            if(nums[mid] <= key){
                left = mid + 1;
                ans = mid;
            }else if(nums[mid] > key){
                right = mid - 1;
            }
        }
        return ans;
    }

leetcode 162 :寻找峰值

在这里插入图片描述
首先我们来分析一下这个题干,这个数组中相邻的两个元素都是不相等的,然后让你返回其中的一个峰值(任意一个就可以),那这道题为什么可以进行二分呢…,我们来看体重的假设提示,我们假设 nums[-1] 和 nums[n] 都是 负无穷
首先我们判断一下特殊的情况,假设我们的 第一个元素大于第二个元素,所以此时第一个元素就是局部的峰值(其实这里有点类似函数极值点的概念),对应的如果最后一个元素要大于倒数第二个元素,那么最后一个元素就是峰值

特殊情况的制约
		if(nums == null || nums.length == 0){
		    System.out.println("这个数组不可能存在峰值");
		    return -1;
		}

        //下面都是一些特殊情况的判断...(端点处的极值问题)
        if(nums.length == 1){
            return 0;
        }
        if(nums[0] > nums[1]){
            return 0;
        }
        if(nums[nums.length-1] > nums[nums.length - 2]){
            return nums.length - 1;
        }

下面我们进行一般情况的分析
在这里插入图片描述
我们现在不满足特殊条件,所以我们数组的走向是这样的,那么因为我的开头处是上升,终点位置是下降,所以中间的某个位置一定存在至少一个极值点(有点类似中值定理) 所以我们可以进行二分
判断nums[mid] 和 nums[mid-1] 与nums[mid+1] 之间的关系…
而中值位置情况的只有以下四种
在这里插入图片描述
其中 1 2 4 都不是我们所需要的,所以要继续进行二分搜索
所以我们可以写出如下的代码

int left = 1;
        int right = nums.length - 2;
        int mid;
        /**
         * 注意:
         * 这里的控制条件其实非常合理,在中点处一共只有四种情况,而这个循环的控制体系可以控制其中的三种无峰值的情况...
         */
        while(left <= right){
            mid = left +((right - left) >> 1);
            if(nums[mid] < nums[mid - 1]){
                right = mid - 1;
            }else if(nums[mid] < nums [mid + 1]){
                left = mid + 1;
            }else{
                return mid;
            }
        }
        return -1;

下面是我们的完整代码

public static int findPeakNumber(int[] nums){
        if(nums == null || nums.length == 0){
            System.out.println("这个数组不可能存在峰值");
            return -1;
        }

        //下面都是一些特殊情况的判断...(端点处的极值问题)
        if(nums.length == 1){
            return 0;
        }
        if(nums[0] > nums[1]){
            return 0;
        }
        if(nums[nums.length-1] > nums[nums.length - 2]){
            return nums.length - 1;
        }

        //下面是普通的一个情况
        //其实有点类似数学的拉格朗日中值定理
        int left = 1;
        int right = nums.length - 2;
        int mid;
        /**
         * 注意:
         * 这里的控制条件其实非常合理,在中点处一共只有四种情况,而这个循环的控制体系可以控制其中的三种无峰值的情况...
         */
        while(left <= right){
            mid = left +((right - left) >> 1);
            if(nums[mid] < nums[mid - 1]){
                right = mid - 1;
            }else if(nums[mid] < nums [mid + 1]){
                left = mid + 1;
            }else{
                return mid;
            }
        }
        return -1;
    }

leetcode 69 : x 的平方根

在这里插入图片描述
这就是一个典型的可以进行二分的题
这个题的核心逻辑跟我们第三个题 : 找出 >= num 的最右侧位置是一致的
值得一提的是,我们这个题要控制我们的算数范围,否则就会出现溢出的问题,下面就是我们的代码解析…没啥可说的了

/**
     *  找出一个数的算术平方根向下取证的那个数然后返回
     * @param x : 待定的开方数
     * @return
     */public static int mySqrt(int x){
        if(x == 0){
            return 0;
        }

        int left = 1;
        int right = x / 2;
        int mid;
        int ans = -1;
        while(left <= right){
            mid = left + ((right - left)>>1);
            if(mid <= x/mid){
                ans = mid;
                left = mid + 1;
            }else if(mid > x/mid){
                right = mid - 1;
            }
        }
        return ans;
    }

唯一要注意的一点就是,这里我们不可以用mid*mid <= x,要改成除法,用mid <= x / mid,否则这个题就会数值溢出导致出错…

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/423893.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

数据结构从入门到精通——顺序表

顺序表 前言一、线性表二、顺序表2.1概念及结构2.2 接口实现2.3 数组相关面试题2.4 顺序表的问题及思考 三、顺序表具体实现代码顺序表的初始化顺序表的销毁顺序表的打印顺序表的增容顺序表的头部/尾部插入顺序表的头部/尾部删除指定位置之前插入数据和删除指定位置数据顺序表元…

PCB学习笔记4——生产过程

1开料&#xff0c;圆角&#xff0c;刨边 2 埋孔&#xff0c;盲孔&#xff0c;过孔 3沉铜 4压膜 5曝光 6显影

了解 SYN Flood 攻击

文章目录&#xff1a; 什么是 SYN Flood 攻击&#xff1f;对网络的影响SYN Flood 发生的迹象如何解决&#xff1f; 什么是 SYN Flood 攻击&#xff1f; SYN Flood&#xff08;SYN 洪水攻击&#xff09;是一种常见的分布式拒绝服务&#xff08;DDoS - Distributed Denial of Se…

【DDD】学习笔记-聚合和聚合根:怎样设计聚合?

今天我们来学习聚合&#xff08;Aggregate&#xff09;和聚合根&#xff08;AggregateRoot&#xff09;。 我们先回顾下上一讲&#xff0c;在事件风暴中&#xff0c;我们会根据一些业务操作和行为找出实体&#xff08;Entity&#xff09;或值对象&#xff08;ValueObject&…

Java+SpringBoot+Vue.js全栈实践:手机销售网站开发记

✍✍计算机编程指导师 ⭐⭐个人介绍&#xff1a;自己非常喜欢研究技术问题&#xff01;专业做Java、Python、微信小程序、安卓、大数据、爬虫、Golang、大屏等实战项目。 ⛽⛽实战项目&#xff1a;有源码或者技术上的问题欢迎在评论区一起讨论交流&#xff01; ⚡⚡ Java实战 |…

YOLOv9有效提点|加入SE、CBAM、ECA、SimAM等几十种注意力机制(一)

专栏介绍&#xff1a;YOLOv9改进系列 | 包含深度学习最新创新&#xff0c;主力高效涨点&#xff01;&#xff01;&#xff01; 一、本文介绍 本文将以SE注意力机制为例&#xff0c;演示如何在YOLOv9种添加注意力机制&#xff01; 《Squeeze-and-Excitation Networks》 SENet提出…

翻硬币 刷题笔记

通过模拟观察 我们发现 按一下会改变相邻两个硬币的状态 将硬币排成一排 从对位置下标为5到下标为7 依次翻其本身和其右边的硬币 对5&#xff0c;6&#xff0c;7操作 操作次数为3 此时我们只改变了硬币5和硬币8的状态 因此 每两处不一样的地方&#xff0c;我们想改变…

Java | vscode如何使用命令行运行Java程序

1.在vscode中新建一个终端 2.在终端中输入命令 javac <源文件>此命令执行后&#xff0c;在文件夹中会生成一个与原java程序同名的.class文件。然后输入如下命令&#xff1a; java <源文件名称>这样java程序就运行成功了。&#x1f607;

递归与递推(蓝桥杯 c++)

目录 题目一&#xff1a; 代码&#xff1a; 题目二: 代码&#xff1a; 题目三&#xff1a; 代码&#xff1a; 题目四&#xff1a; 代码&#xff1a; 题目一&#xff1a; 代码&#xff1a; #include<iostream> #include<cstring> using namespace std; int …

HTML和CSS (前端共三篇)【详解】

目录 一、前端开发介绍 二、HTML入门 三、HTML基础标签 四、CSS样式修饰 五、HTML表格标签 六、HTML表单标签 一、前端开发介绍 web应用有BS和CS架构两种&#xff0c;其中我们主要涉及的是BS架构。而BS架构里&#xff0c;B&#xff08;Browser浏览器&#xff09;是客户端的…

一些可以访问gpt的方式

1、Coze扣子是新一代 AI 大模型智能体开发平台。整合了插件、长短期记忆、工作流、卡片等丰富能力&#xff0c;扣子能帮你低门槛、快速搭建个性化或具备商业价值的智能体&#xff0c;并发布到豆包、飞书等各个平台。https://www.coze.cn/ 2、https://poe.com/ 3、插件阿里…

2023国赛样题路由部分【RIP RIPNG ACLRIP ACLRIPNG ISIS NAT64】

RT1串行链路、RT2串行链路、FW1、AC1之间分别运行RIP和RIPng协议&#xff0c;FW1、RT1、RT2的RIP和RIPng发布loopback2地址路由&#xff0c;AC1 RIP发布loopback2地址路由&#xff0c;AC1 RIPng采用route-map匹配prefix-list重发布loopback2地址路由。RT1配置offset值为3的路由…

一文认识蓝牙(验证基于Aduino IDE的ESP32)

1、简介 蓝牙技术是一种无线通信的方式&#xff0c;利用特定频率的波段&#xff08;2.4GHz-2.485GHz左右&#xff09;&#xff0c;进行电磁波传输&#xff0c;总共有83.5MHz的带宽资源。 1.1、背景 蓝牙&#xff08;Bluetooth&#xff09;一词取自于十世纪丹麦国王哈拉尔Haral…

linux的通信方案(SYSTEM V)

文章目录 共享内存(Share Memory)信号队列&#xff08;Message Queue&#xff09;信号量(semaphore) 进程间通信的核心理念&#xff1a;让不同的进程看见同一块资源 linux下的通信方案&#xff1a; SYSTEM V 共享内存(Share Memory) 特点&#xff1a;1.共享内存是进程见通信最…

使用 Docker 部署 Answer 问答平台

1&#xff09;介绍 GitHub&#xff1a;https://github.com/apache/incubator-answer Answer 问答社区是在线平台&#xff0c;让用户提出问题并获得回答。用户可以发布问题并得到其他用户的详细答案、建议或信息。回答可以投票或评分&#xff0c;有助于确定有用的内容。标签和分…

笨办法学 Python3 第五版(预览)(二)

原文&#xff1a;Learn Python the Hard Way, 5th Edition (Early Release) 译者&#xff1a;飞龙 协议&#xff1a;CC BY-NC-SA 4.0 练习 19&#xff1a;函数和变量 现在你将把函数与你从之前练习中了解到的变量结合起来。如你所知&#xff0c;变量给数据片段一个名称&#x…

数据挖掘入门项目二手交易车价格预测之数据分析

文章目录 1. 相关库的引入2. 数据的加载3. 数据概况3.1 统计值查看3.2 查看数据类型 4. 判断缺失值4.1 统计每一列空值的数量4.2 可视化缺失值数量 5. 判断异常值5.1 异常值检测 6. 了解预测值的分布6.1 统计各预测值的分布6.2 总体分布概况6.2 查看预测值的具体频数6.3 查看sk…

基于ssm旅社客房收费管理系统+vue

目 录 目 录 I 摘 要 III ABSTRACT IV 1 绪论 1 1.1 课题背景 1 1.2 研究现状 1 1.3 研究内容 2 2 系统开发环境 3 2.1 vue技术 3 2.2 JAVA技术 3 2.3 MYSQL数据库 3 2.4 B/S结构 4 2.5 SSM框架技术 4 3 系统分析 5 3.1 可行性分析 5 3.1.1 技术可行性 5 3.1.2 操作可行性 5 3…

Git 如何上传本地的所有分支

Git 如何上传本地的所有分支 比如一个本地 git 仓库里定义了两个远程分支&#xff0c;一个名为 origin&#xff0c; 一个名为 web 现在本地有一些分支是 web 远程仓库没有的分支&#xff0c;如何将本地所有分支都推送到 web 这个远程仓库上呢 git push web --all

【ArcGIS超级工具】基于ArcPy的矢量数据批量自动化入库工具

最近&#xff0c;有很多做规划的朋友私信我&#xff0c;想让我帮忙开发一款ArcGIS自动化脚本工具&#xff0c;实现点、线、面的自动化入库操作&#xff0c;帮他们在平时的内业数据处理工作中减少机械式重复性的工作&#xff0c;提高工作效率。为此&#xff0c;我详细了解了下目…