[算法] 优选算法(五):二分查找(上)

news2024/9/21 18:45:25

🌸个人主页:https://blog.csdn.net/2301_80050796?spm=1000.2115.3001.5343
🏵️热门专栏:
🧊 Java基本语法(97平均质量分)https://blog.csdn.net/2301_80050796/category_12615970.html?spm=1001.2014.3001.5482
🍕 Collection与数据结构 (92平均质量分)https://blog.csdn.net/2301_80050796/category_12621348.html?spm=1001.2014.3001.5482
🧀Java EE(96平均质量分) https://blog.csdn.net/2301_80050796/category_12643370.html?spm=1001.2014.3001.5482
🍭MySql数据库(93平均质量分)https://blog.csdn.net/2301_80050796/category_12629890.html?spm=1001.2014.3001.5482
🍬算法(97平均质量分)https://blog.csdn.net/2301_80050796/category_12676091.html?spm=1001.2014.3001.5482
🍃 Spring(97平均质量分)https://blog.csdn.net/2301_80050796/category_12724152.html?spm=1001.2014.3001.5482
感谢点赞与关注~~~
在这里插入图片描述

目录

  • 1. 二分查找(难度:🟢1度)
  • 2. 在排序数组中查找元素的第一个和最后一个位置(难度:🟡3度)
  • 3. 搜索插入位置(难度:🔵2度)
  • 4. x的平方根(难度:🔵2度)

1. 二分查找(难度:🟢1度)

OJ链接

  • 题目描述

给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。
示例 1:
输入: nums = [-1,0,3,5,9,12], target = 9
输出: 4
解释: 9 出现在 nums 中并且下标为 4
示例 2:
输入: nums = [-1,0,3,5,9,12], target = 2
输出: -1
解释: 2 不存在 nums 中因此返回 -1

  • 算法原理
    a. 定义left ,right 指针,分别指向数组的左右区间。
    b. 找到待查找区间的中间点mid,找到之后分三种情况讨论:
    i. arr[mid] == target 说明正好找到,返回mid 的值
    ii. arr[mid] > target 说明[mid, right] 这段区间都是大于target 的,因此舍去右边区间,在左边[left, mid -1] 的区间继续查找,即让right = mid - 1 ,然后重复2 过程;
    iii. arr[mid] < target 说明[left, mid] 这段区间的值都是小于target 的,因此舍去左边区间,在右边[mid + 1, right] 区间继续查找,即让left = mid + 1,然后重复2 过程;
    c. 当left 与right 错开时,说明整个区间都没有这个数,返回-1
  • 代码实现
class Solution {
    public int search(int[] nums, int target) {
        int left = 0, right = nums.length-1;
        while(left <= right){
            int mid = left + (right-left)/2;
            if (target < nums[mid]){
                right = mid - 1;
            }else if(target > nums[mid]){
                left = mid + 1;
            }else{
                return mid;
            }
        }
        return -1;
    }
}

[注意事项]

  1. 计算mid的时候,没有使用(left+right)/2,而是使用left+(right-left)/2的形式.
  2. 二分法一般采用最中间的点作为划分点,其他的等分点也可以作为划分点,但是查找效率没有中间作为划分点高.
  • 朴素二分模版总结
//首先定义left和right指针
while (left <= right){
	int mid = left+(right-left)/2;
	if(...) left = mid+1;
	else if(...) right = mid-1;
	else return ...;
}

2. 在排序数组中查找元素的第一个和最后一个位置(难度:🟡3度)

OJ链接

  • 题目描述

给你一个按照非递减顺序排列的整数数组 nums,和一个目标值 target。请你找出给定目标值在数组中的开始位置和结束位置。
如果数组中不存在目标值 target,返回 [-1, -1]。
你必须设计并实现时间复杂度为 O(log n) 的算法解决此问题。
示例 1:
输入:nums = [5,7,7,8,8,10], target = 8
输出:[3,4]
示例 2:
输入:nums = [5,7,7,8,8,10], target = 6
输出:[-1,-1]
示例 3:
输入:nums = [], target = 0
输出:[-1,-1]

  • 算法原理
    方便叙述,用x 表示该元素,resLeft 表示左边界,resRight 表示右边界
    • 寻找左边界
      我们注意到以左边界划分的两个区间的特点:
      ▪ 左边区间[left, resLeft - 1] 都是小于x 的;
      ▪ 右边区间(包括左边界) [resLeft, right] 都是大于等于x 的;
      • 因此,关于mid 的落点,我们可以分为下面两种情况:
      ◦ 当我们的mid 落在[left, resLeft - 1] 区间的时候,也就是arr[mid] < target 。说明[left, mid] 都是可以舍去的,此时更新left 到mid + 1 的位置这里需要注意在left上+1,因为mid左边的元素都已经小于目标值了,都可以舍去.继续在[mid + 1, right] 上寻找左边界;
      ◦ 当mid 落在[resLeft, right] 的区间的时候,也就是arr[mid] >= target 。说明[mid + 1, right] (因为mid 可能是最终结果,不能舍去)是可以舍去的,此时更新right 到mid 的位置由于mid的位置可能是最终的值,所以mid位置不可以舍去,不需要-1继续在[left, mid] 上寻找左边界;
      • 由此,就可以通过⼆分,来快速寻找左边界;
      [注意] 循环条件判断必须是left < right,如果加上等号,当找到目标值的时候.left和right重合,第一重合就证明已经找到了目标值,就没有必要再执行操作了,第二,如果找到了目标值,left和right依然会满足条件,会进入循环,会让程序陷入死循环.求中点的时候,必须采用向下取整的方式,如果采用向上取整,如果找到了目标值,right=mid的时候,right没有移动,就会陷入死循环.
    • 寻找右边界
      与寻找左边界同理
      ◦ 用resRight 表示右边界;
      ◦ 我们注意到右边界的特点:
      ▪ 左边区间(包括右边界) [left, resRight] 都是小于等于x 的;
      ▪ 右边区间[resRight+ 1, right] 都是大于x 的;
      • 因此,关于mid 的落点,我们可以分为下面两种情况:
      ◦ 当我们的mid 落在[left, resRight] 区间的时候,说明[left, mid - 1]( mid 不可以舍去,因为有可能是最终结果)都是可以舍去的,此时更新left 到mid 的位置
      ◦ 当mid 落在[resRight+ 1, right] 的区间的时候,说明[mid, right] 内的元素是可以舍去的,此时更新right 到mid - 1 的位置
      • 由此,就可以通过⼆分,来快速寻找右边界;
  • 代码实现
class Solution {
    public int[] searchRange(int[] nums, int target) {
        int left = 0;
        int right = nums.length - 1;
        int[] array = new int[2];
        array[0] = -1;
        array[1] = -1;
        if (nums.length == 0){//注意处理特殊情况
            return array;
        }
        while (left < right) {
            int mid = left + (right - left) / 2;
            if (nums[mid] < target) {
                left = mid + 1;//left下标不可能是t的值
            } else {
                right = mid;//right下标有可能是t的值
            }
        }
        if (nums[left] == target){
            array[0] = left;
        }else{
            return array;
        }
        left = 0;
        right = nums.length - 1;
        while (left < right) {
            int mid = left + (right - left + 1) / 2;
            if (nums[mid] > target) {
                right = mid - 1; //right下标不可能是t的值
            } else {
                left = mid;//left下标可能是t的值
            }
        }
        array[1] = right;
        return array;
    }
}
  • 查找边界二分算法模版
    • 左边界
    while(left < right){
    	int mid = left+(right-left)/2;
    	if(...) left = mid+1;
    	else right = mid;
    }
    
    • 右边界
    while(left < right){
    int mid = left+(right-left+1)/2;
    if(...) left = mid;
    else right = mid-1;
    }
    

3. 搜索插入位置(难度:🔵2度)

OJ链接

  • 题目解析

给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
请必须使用时间复杂度为 O(log n) 的算法。
示例 1:
输入: nums = [1,3,5,6], target = 5
输出: 2
示例 2:
输入: nums = [1,3,5,6], target = 2
输出: 1
示例 3:
输入: nums = [1,3,5,6], target = 7
输出: 4

  • 算法原理
    a. 分析插⼊位置左右两侧区间上元素的特点:
    设插⼊位置的坐标为 index ,根据插⼊位置的特点可以知道:
    • [left, index - 1] 内的所有元素均是小于 target 的;
    • [index, right] 内的所有元素均是大于等于 target 的。
    b. 设 left 为本轮查询的左边界, right 为本轮查询的右边界。根据 mid 位置元素的信息,分析下⼀轮查询的区间:
    当 nums[mid] >= target 时,说明 mid 落在了 [index, right] 区间上,mid 左边包括 mid 本身,可能是最终结果,所以我们接下来查找的区间在 [left,mid] 上。因此,更新 right 到 mid 位置,继续查找。
    当 nums[mid] < target 时,说明 mid 落在了 [left, index - 1] 区间上,mid 右边但不包括 mid 本身,可能是最终结果,所以我们接下来查找的区间在 [mid+ 1, right] 上。因此,更新 left 到 mid + 1 的位置,继续查找。
    c. 直到我们的查找区间的⻓度变为 1 ,也就是 left == right 的时候, left 或者right 所在的位置就是我们要找的结果
  • 代码实现
class Solution {
    public int searchInsert(int[] nums, int target) {
        int left = 0;
        int right = nums.length - 1;
        while (left < right) {
            int mid = left + (right - left) / 2;
            if (nums[mid] < target) {
                left = mid + 1;
            } else {
                right = mid;
            }
        }
        if (nums[right] < target) {
            return right + 1;
        }
        return right;
    }
}

4. x的平方根(难度:🔵2度)

OJ链接

  • 题目描述

给你一个非负整数 x ,计算并返回 x 的 算术平方根 。
由于返回类型是整数,结果只保留 整数部分 ,小数部分将被 舍去 。
注意:不允许使用任何内置指数函数和算符,例如 pow(x, 0.5) 或者 x ** 0.5 。
示例 1:
输入:x = 4
输出:2
示例 2:
输入:x = 8
输出:2
解释:8 的算术平方根是 2.82842…, 由于返回类型是整数,小数部分将被舍去。

  • 算法原理
    设 x 的平方根的最终结果为 index :
    a. 分析 index 左右两次数据的特点:
    ▪ [0, index] 之间的元素,平方之后都是小于等于 x 的;
    ▪ [index + 1, x] 之间的元素,平方之后都是大于 x 的。
    因此可以使用二分查找算法。
  • 代码实现
class Solution {
    public int mySqrt(int x) {
        if (x < 1){
            return 0;
        }
        long left = 1;
        long right = x;
        while(left < right){
            long mid = left + (right - left + 1)/2;//由于是向下取整,所以要加1
            if (mid * mid <= x){
                left = mid;
            }else{
                right = mid - 1;
            }
        }
        return (int)left;
    }
}

[注意] 我们这里使用long,防止数据溢出.

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Linux虚拟机扩展磁盘空间

文章目录 在VM上进行扩展新的磁盘空间进入虚拟机将扩展的磁盘空间分配给对应的分区 VM 下的Linux虚拟机提示磁盘空间不足&#xff0c;需要对其进行磁盘扩容&#xff0c;主要有以下两步&#xff1a; 在VM上进行扩展新的磁盘空间 先关闭虚拟机在VM的虚拟机设置处进行硬盘扩展 …

STM32自己从零开始实操:PCB全过程

一、PCB总体分布 以下只能让大家看到各个模块大致分布在板子的哪一块&#xff0c;只能说每个人画都有自己的理由&#xff1a; 电源&#xff1a;从外部接入电源&#xff0c;5V接到中间&#xff0c;向上变成4V供给无线&#xff0c;向下变成3V供给下面的接口&#xff08;也刻意放…

Java---SpringBoot详解二

勤奋勤劳铸梦成&#xff0c; 晨曦微露起长征。 汗水浇灌花似锦&#xff0c; 寒窗苦读岁月明。 千锤百炼心如铁&#xff0c; 万里征途志不倾。 持之以恒终有日&#xff0c; 功成名就笑谈中。 目录 一&#xff0c;统一响应结果 二&#xff0c;三层架构 三&#xff0c;分层解耦 四…

力扣第九题

回文数 提示&#xff1a; 给你一个整数 x &#xff0c;如果 x 是一个回文整数&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 。 回文数是指正序&#xff08;从左向右&#xff09;和倒序&#xff08;从右向左&#xff09;读都是一样的整数。 代码展示&#…

请你谈谈:AnnotatedBeanDefinitionReader 显式地注册一个Bean到Spring容器,以及注册并解析配置类

为了深入探讨Spring框架中的beanDefinition对象&#xff0c;我们不可避免地要提及BeanFactoryPostProcessor这一核心类&#xff0c;它作为Spring的bean工厂后置处理器发挥着关键作用。接下来&#xff0c;我们将详细讨论BeanFactoryPostProcessor的执行时机&#xff0c;这是一个…

顶顶通呼叫中心中间件-添加自定义变量到CDR方法(mod_cti基于FreeSWITCH)

顶顶通呼叫中心中间件-添加自定义变量到CDR方法(mod_cti基于FreeSWITCH) 1、自定义变量添加到cti.json 例&#xff1a;需要添加的变量为“bridge_uepoch" 2、添加进数据库 在数据库中找到表"cdr"在cdr表中也添加数据&#xff0c;数据名为新变量名&#xff1a…

基于Java的科大讯飞大模型API调用实现

写在前面&#xff1a;因为现在自己实习的公司新拓展的一个业务是结合AI的低代码平台&#xff0c;我负责后端的开发&#xff0c;之前一直都是直接使用gpt或者文心一言等ui界面来直接使用大模型&#xff0c;从来没有自己调接口过&#xff0c;所以本文记录一下自己第一次使用大模型…

P2p网络性能测度及监测系统模型

P2p网络性能测度及监测系统模型 网络IP性能参数 IP包传输时延时延变化误差率丢失率虚假率吞吐量可用性连接性测度单向延迟测度单向分组丢失测度往返延迟测度 OSI中的位置-> 网络层 用途 面相业务的网络分布式计算网络游戏IP软件电话流媒体分发多媒体通信 业务质量 通过…

从零开始做题:什么奇奇怪怪的东西

题目 解题 mrf拓展名&#xff0c;macro recorder打开&#xff0c;鼠标键盘的记录 然后解压flag.zip即可&#xff0c;发现有一个挂载的文件&#xff0c;直接打开后 显示所有的隐藏文件 一个一个打开 然后进行拼接运行吧估计。 首先打开txt文件直接久就给出了代码&#xff1…

maven项目容器化运行之2-maven中使用docker插件调用远程docker构建服务并在1Panel中运行

一.背景 公司主机管理小组的同事期望我们开发的maven项目能够在1Panel管理的docker容器部署。上一篇写了先开放1Panel中docker镜像构建能力maven项目容器化运行之1-基于1Panel软件将docker镜像构建能力分享给局域网-CSDN博客。这一篇就是演示maven工程的镜像构建、容器运行、运…

学习大数据DAY16 PLSQL基础语法5

目录 异常 自定义异常的格式 raise_application_error 处理异常 预定义异常 SQLcode和SQLerrm 非预定义异常 作业 触发器 触发器基本概念 DML触发器 DML触发器使用 instead of 触发器 管理触发器 作业2 函数、过程和包 函数 过程 参数 1. in 参数 2.out 参…

KALI使用MSF攻击安卓设备

这期是kali使用MSF进行安卓渗透的保姆级别教程&#xff0c;话不多说&#xff0c;直接开始。 准备材料&#xff1a; 1.装有kali的实体机或虚拟机&#xff08;这里用实体机进行演示&#xff09; 2.一台安卓10.0以下的手机 打开kali&#xff0c;先用ifconfig查看自己的kali IP地址…

MySQL第八次作业

一、备份与恢复作业&#xff1a; 创库,建表&#xff1a; CREATE DATABASE booksDB; use booksDB; CREATE TABLE books ( bk_id INT NOT NULL PRIMARY KEY, bk_title VARCHAR(50) NOT NULL, copyright YEAR NOT NULL ); CREATE TABLE authors …

Git提交了敏感信息,通过BFG工具撤回或修改很久之前已经提交的内容

Git 提交了敏感信息怎么办&#xff1f; 这篇文章的由来&#xff0c;由于自己在提交代码的时候不小心把服务器的ip地址&#xff0c;还有数据库的密码给push到github上面去了。那么问题来了&#xff0c;我这个已经是一个月之前的提交的呢&#xff0c;现在还能改吗&#xff1f;别…

SIM900发送中文短信的处理过程

SIM900发送中文短信的处理过程 1、短信中心号码处理: 短信中心号码可以使用ATCSCA?获取。 在获取之前&#xff0c;最好将设置使用GSM字符&#xff1a; ATCSCS"GSM" OK ATCSCA? CSCA: "8613800755500",145 OK 这样一来&#xff0c;我就得…

Python酷库之旅-第三方库Pandas(023)

目录 一、用法精讲 58、pandas.isnull函数 58-1、语法 58-2、参数 58-3、功能 58-4、返回值 58-5、说明 58-6、用法 58-6-1、数据准备 58-6-2、代码示例 58-6-3、结果输出 59、pandas.notna函数 59-1、语法 59-2、参数 59-3、功能 59-4、返回值 59-5、说明 5…

线程的复习

目录 大纲Java中的线程概念创建线程的方法线程的生命周期线程的同步和通信线程的优先级和调度线程的中断 案例 大纲 Java中的线程概念 在Java中&#xff0c;线程是操作系统能够进行运算调度的最小单位&#xff0c;它被包含在进程之中&#xff0c;是进程中实际运作的部分。一个…

python单测框架之pytest常见用法

单测框架的作用 测试发现&#xff1a;从多个文件中寻找测试用例。测试执行&#xff1a;按照一定顺序去执行并且生成结果。测试断言&#xff1a;判断最终结果与实际结果的差异。测试报告&#xff1a;统计测试进度、耗时、通过率&#xff0c;生成测试报告。 pytest简介 pytest是…

51单片机10(蜂鸣器介绍)

一、蜂鸣器介绍&#xff1a; 1、蜂鸣器是一种一体化结构的电子讯响器&#xff0c;采用直流电压供电&#xff0c;广泛应用于电子产品中作为发声器件。蜂鸣器主要分为压电式蜂鸣器和电磁式蜂鸣器。 &#xff08;1&#xff09;压电式蜂鸣器&#xff0c;它主要由多谐的一个增胀器…

Cyber Weekly #15

赛博新闻 1、OpenAI 绝密项目「草莓」首次曝光 据外媒路透社报道&#xff0c;OpenAI 内部正在一个代号为「草莓&#xff08;Strawberry&#xff09;」的项目中开发一种新的人工智能模型。该模型细节此前从未被报道过&#xff0c;而 OpenAI 正在努力证明该模型类型能够提供高级…