“数字大冒险:探索二分查找的神奇之旅“

前言

作者:小蜗牛向前冲

专栏小蜗牛算法之路

 专栏介绍"蜗牛之道,攀登大厂高峰,让我们携手学习算法。在这个专栏中,将涵盖动态规划、贪心算法、回溯等高阶技巧,不定期为你奉上基础数据结构的精彩算法之旅。一同努力,追逐技术的星辰大海。"

目录

一、二分查找 

二、二分的朴素模板

1、例题1

2、朴素模板

三、二分的万能模版

1、例题2

2、查找区间左端点

3、查找区间右端点

4、万能模板 

5、题目练习


一、二分查找 

二分查找是一种在有序数组中查找目标值的算法。它通过反复将待查找区间分成两部分并检查中间元素来进行查找,以缩小搜索范围,直到找到目标值或确定目标值不存在为止。这种算法的时间复杂度为 O(log n),其中 n 是数组的元素个数。

对于二分查找,我们简单的理解就是通过二段性,不断的排除不属于目标值的的算法

而为什么我们不三分,四分呢?

尽管三分、四分等算法在某些特定情况下可能会有所优势,但由于二分查找已经被广泛证明为高效且简单的解决方案,因此通常情况下选择二分更为合适。(这里和数学期望有关就不和大家证明了)

二分算法看起来非常简单,但是他的细节是非常多,而且查看能力非常强大。

比如当我们要查找2^32(42亿多),如果我们要暴力查找就要查找42亿多次,但是二次就只要查找32次。

为了解决二分细节太多问题(特别是在处理复杂问题,对边界情况的处理)

二、二分的朴素模板

1、例题1

这里我们直接用力扣上的一道题目来引出:

给定一个 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

提示:

  1. 你可以假设 nums 中的所有元素是不重复的。
  2. n 将在 [1, 10000]之间。
  3. nums 的每个元素都将在 [-9999, 9999]之间。

这里是非常经典的二分运用,在一个有序的数组中(有序就说明有二段性) ,让我们排查目标值target,暴力就不提了,那二分是如何解题的。

这时候结果区域就只剩5、9、12,然后继续二分排除。

这里就不在细说,直接看代码。

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

通过这道题目我们来总结一下朴素模板。

2、朴素模板

这里简单说明一下,x=nums[mid],t为目标值。

这里为了保证left<=right

  • 当x < t----->left =mid+1------[left,right]
  • 当x > t----->right =mid-1------[left,right]
  • 当x == t----->返回结果

从上面我们可以归纳出二分的朴素模版:

  while (left <= right){int mid = left + (right-left)/2;//防止溢出if (....){left = mid + 1;}else if (.....){right = mid - 1;}else{.....;}}

细节处理 

1、循环结束的条件 left<right可不可以

这里是一定不可以的, 这里会漏掉,当left和right都指向同一个元素的情况,大家注意在[left,right]区间的值都是没有被排查的,所以条件一定是lef<=right。

 2、在计算mid的时候有时候我们会看到二种写法:

  1.  int mid = left + (right-left)/2;
  2.  int mid = left + (right-left+1)/2;

对于二者的区别其实对奇数个元素是没有区别的,因为中间值一定是唯一的。

但是对于偶数个元素的,中间值mid就有区别了:

 ​​​​​​

可以看到mid 是第一种求法的时候取到是方框中左边的值,第二中求法求到是右边的值。

对于这中朴素模板来说是没有区别的,但是对后面的万能模版来说却非常重要。

三、二分的万能模版

1、例题2

给你一个按照非递减顺序排列的整数数组 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]

提示:

  • 0 <= nums.length <= 105
  • -109 <= nums[i] <= 109
  • nums 是一个非递减数组
  • -109 <= target <= 109

对于这道例题,如果我们继续用前面的朴素二分算法模本版,就要处理许多边界问题,那么我们应该如何求简化我们的思路。

2、查找区间左端点

为了解决上面朴素二分在求上面情况,可能会出现退化为暴力的情况,这里我们换一个思路,利用二分求我们要求区间的左端点。

 这里我们就分为了上面二种情况,但是仅仅到这里我们就可以求编写代码了吗?我觉的是远远不够,因为对于二分来说,最复制的是其中的边界问题,没有处理好就会出现一系列的死循环问题。

细节处理

循环条件:

  1. left<right?
  2. left<=right?

这里的循环条件是1还是2,这里我们先说结论,一定不能取等号(所以选择情况1)。为什么?

 对于情况1:

当我们要查看的是有结果的,也就是说在[left,right]区间内,存在我们要的结果(左区间端点)

对于left,一直是想要跳出1这个区域的,但是对于right就会在2区域内活动,当left==right=r=et,就是等于了结果,也就没有必要在进入循环(如果进行就会死循环,因为mid=right)。

对于情况2:

在想x>t,那么right就要一直向左移动,最终就会和left相遇,这里就只要判断当前值是否和t相等即可,没有必要进入循环(如果进入就会出现死循环)

对于情况3:

在想x<t,那么left就要一直向右移动,最终就会和right相遇,这里就只要判断当前值是否和t相等即可,没有必要进入循环(如果进入就会出现死循环)

综合上所述:

  • left==right的时候就是最终结果,没有必要进入循环判断
  • 如果判断就会死循环

 2、求mid的操作

  1.  int mid = left + (right-left)/2;
  2.  int mid = left + (right-left+1)/2;

这里也是选择1 ,为什么?

当最后只有二个值的时候,如果我们选择2求mid,可以想一下,mid就会指选择区域右边(前面已经解释过了),当是如果是x<=t的情况时候,这时候就会出现right=mid,[left,right]区间没有动而出现死循环。

而现在1就不会出现这种情况

代码实现:

class Solution {
public:vector<int> searchRange(vector<int>& nums, int target){int left = 0;int right = nums.size()-1;vector<int> result = {-1, -1};if(nums.size()==0) return result;//处理边界情况while(left<right){int mid =left+(right-left)/2;if(nums[mid]<target){left = mid+1;}else{right = mid;}}//得到左区间的的下标,这里出来一定是left==rightint begin=-1,end=-1;if(nums[left]==target){begin = left;int i = 0;//从left位置开始找右区间for(i = left;i<nums.size();i++){if(nums[i]!=target)break;}end = i-1;result[0]=begin;result[1]=end;}return result;}
};

3、查找区间右端点

对于找区间右端点,和找左端点的本质上是一样的,只是变了一种形式,下面我们快速了解一下:

这里不同点:就是区间划分不一样了,因为是找右端点,所以当x<=t时候,left在移动的时候,不能超过区间,所以最多到left=mid这的区间是,当x>t的时候,其实是和朴素二分哪里right的移动是一样的。 

对于查找区间右端点我们仍要关注二个细节:

  • 循环结束条件是:left<rigt
  • mid的求法是:left+(right-left+1)

 这里至于是为什么,可以参考区间左端点细节的解释,这里就不过多解释。

代码实现:

class Solution {
public:vector<int> searchRange(vector<int>& nums, int target) {vector<int> result{-1,-1};int left=0,right = nums.size()-1;//处理特殊情况if(nums.size()==0) return result;while(left<right){int mid= left+(right-left+1)/2;if(nums[mid]<=target) left=mid;else right = mid-1;}//到这里肯定是left==rightint begin =-1,end=-1,i=0;if(nums[left]==target){end = left;//在去找左端点for(i = left;i>=0;i--){if(nums[i]!=target)break;}begin = i+1;}result[0]=begin;result[1]=end;return result;}
};

4、万能模板 

根据对二分朴素模本进一步升级,我们可以写出二分的万能模版:

但是核心其实是前面的分析过程而这个模版只是一个思路,大家借鉴就好,还是要理解二分是在具有二段性的情况使用的。

5、题目练习

  • 搜索插⼊位置(easy)
  •  x 的平⽅根(easy)
  • ⼭峰数组的峰顶(easy)
  • 0〜n-1 中缺失的数字(easy)
  • 寻找峰值(medium)
  • 搜索旋转排序数组中的最⼩值(medium)

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

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

相关文章

算法:多重背包问题dp

文章目录 一、多重背包问题特点1.1、多重背包问题的特征1.2、解决多重背包问题的基本方法典型例题&#xff1a;AcWing——多重背包问题I 1.3、二进制优化1.3.1、二进制优化的思想1.3.2、多重背包问题的二进制优化 一、多重背包问题特点 多重背包问题是背包问题的又一变种&…

如何在Flutter应用中配置ipa Guard进行混淆

在移动应用开发中&#xff0c;保护应用代码安全至关重要。Flutter 提供了简单易用的混淆工具&#xff0c;帮助开发者在构建 release 版本应用时有效保护代码。本文将介绍如何在 Flutter 应用中使用混淆&#xff0c;并提供了相关的操作步骤和注意事项。 &#x1f4dd; 摘要 本…

彩虹聚合DNS管理系统源码

聚合DNS管理系统可以实现在一个网站内管理多个平台的域名解析&#xff0c;目前已支持的域名平台有&#xff1a;阿里云、腾讯云、华为云、西部数码、CloudFlare。本系统支持多用户&#xff0c;每个用户可分配不同的域名解析权限&#xff1b;支持API接口&#xff0c;支持获取域名…

LeetCode-冗余连接(并查集)

每日一题&#xff0c;今天又刷到一道使用并查集来解决的问题&#xff0c;再次加深了一遍自己对并查集的印象和使用。 题目要求 树可以看成是一个连通且 无环 的 无向 图。 给定往一棵 n 个节点 (节点值 1&#xff5e;n) 的树中添加一条边后的图。添加的边的两个顶点包含在 1…

对象参数验证工具, 解决非controller层数据校验问题, @Validated、@Valid

工具类 package com.common;import com.common.SysException;import javax.validation.ConstraintViolation; import javax.validation.Validation; import javax.validation.Validator; import java.util.Set; import java.util.stream.Collectors;/**1. author: 0i773. Desc…

【计算机毕业设计】超市进销存管理系统——后附源码

&#x1f389;**欢迎来到我的技术世界&#xff01;**&#x1f389; &#x1f4d8; 博主小档案&#xff1a; 一名来自世界500强的资深程序媛&#xff0c;毕业于国内知名985高校。 &#x1f527; 技术专长&#xff1a; 在深度学习任务中展现出卓越的能力&#xff0c;包括但不限于…

PostgreSQL入门到实战-第八弹

PostgreSQL入门到实战 PostgreSQL数据过滤(一)官网地址PostgreSQL概述PostgreSQL的where子命令介绍PostgreSQL的where子命令实操更新计划 PostgreSQL数据过滤(一) 官网地址 声明: 由于操作系统, 版本更新等原因, 文章所列内容不一定100%复现, 还要以官方信息为准 https://ww…

数据结构---顺序表实现

目录 1.顺序表 2.动态顺序表的实现 &#xff08;4&#xff09;顺序表初始化 &#xff08;5&#xff09;顺序表销毁 &#xff08;6&#xff09;顺序表的插入 a.尾插 b.头插 &#xff08;7&#xff09;顺序表的删除 a.尾删 b.头删 &#xff08;8&#xff09;指定位置之…

大话设计模式之桥接模式

桥接模式是一种结构型设计模式&#xff0c;它将抽象部分与它的实现部分分离&#xff0c;使它们可以独立地变化。这种模式通过提供一个桥接接口来实现这种分离&#xff0c;使得抽象部分和实现部分可以在运行时独立地进行修改。 桥接模式主要由两个部分组成&#xff1a;抽象部分…

Chat Ollama docker部署及运行 本地大语言模型+本地知识库搭建 强烈推荐

背景介绍 Ollama 是目前最流行的大模型本地化工具之一。 Ollama 支持一系列开源大模型&#xff0c;包括主流的聊天模型和文本嵌入模型&#xff08;Embedding Models&#xff09;等。 ChatOllama 是基于 Ollama 的 Web 应用&#xff0c;它可以让用户直接在浏览器中使用 Ollama。…

解锁电气数据新价值:SolidWorks Electrical助力企业转型

在信息化、数字化的时代&#xff0c;电气数据库已成为企业不可或缺的核心资产。它以其独特的功能和优势&#xff0c;助力企业在激烈的市场竞争中脱颖而出&#xff0c;实现数字化转型的跨越式发展。 SolidWorks Electrical电气数据库具备强大的数据整合能力。它能够将企业内部各…

Linux 学习之路 - 进程篇 - PCB介绍1-标识符

目录 一、基础的命令 <1> ps axj 命令 <2> top 命令 <3> proc 目录 二、进程的标识符 <1>范围 <2>如何获取标识符 <3>bash进程 三、创建进程 一、基础的命令 前面介绍了那么多&#xff0c;但是我们没有观察到进程相关状态&#x…

机器人码垛机的技术特点与应用

随着科技的飞速发展&#xff0c;机器人技术正逐渐渗透到各个行业领域&#xff0c;其中&#xff0c;机器人码垛机在物流行业的应用尤为引人瞩目。它不仅提高了物流效率&#xff0c;降低了成本&#xff0c;更在改变传统物流模式的同时&#xff0c;为行业发展带来了重大的变革。 一…

MQ死信队列:面试题

所谓的死信队列只不过是我们自己定义的一个队列&#xff0c;注意对于这个队列只能人工干预 面试题&#xff1a;你们是如何保证消息不会丢失的 1&#xff0c;什么是死信 在RabitMQ中充当主角的就是消息&#xff0c;在不同场景下&#xff0c;消息会有不同地表现。 死信就是在…

SpringBoot学习笔记三-原理分析

SpringBoot学习笔记三-原理分析 SpringBoot自动装配1.1 案例1.2 通过注解方式管理Bean1.3 小结1.4 Enable注解1.5 Import注解1.5.1 ImportSelector实现类1.5.2 导入ImportBeanDefinitionRegistrar 1.5 EnableAutoConfiguration1.6 案例 SpringBoot自动装配 当再pom.xml中导入对…

活动发布会新闻通稿如何写?

传媒如春雨&#xff0c;润物细无声&#xff0c;大家好&#xff0c;我是51媒体网胡老师。 撰写活动发布会的新闻通稿需要遵循一定的结构和内容要点&#xff0c;以确保信息的准确性、完整性和吸引力。以下是撰写活动发布会新闻通稿的基本步骤和建议&#xff1a; 标题&#xff1…

【智能算法】长鼻浣熊优化算法(COA)原理及实现

目录 1.背景2.算法原理2.1算法思想2.2算法过程 3.结果展示4.参考文献 1.背景 2023年&#xff0c;M Dehghani等人受到长鼻浣熊自然行为启发&#xff0c;提出了长鼻浣熊优化算法&#xff08;Coati Optimization Algorithm&#xff0c;COA&#xff09;。 2.算法原理 2.1算法思想…

C语言 函数——函数封装与程序的健壮性

目录 函数封装&#xff08;Encapsulation&#xff09; 如何增强程序的健壮性&#xff1f; 如何保证不会传入负数实参&#xff1f; 函数设计的基本原则 函数封装&#xff08;Encapsulation&#xff09; 外界对函数的影响——仅限于入口参数 函数对外界的影响——仅限于一个…

C++:内联函数inline,auto关键字,基于范围的for循环,nullpter

文章目录 1.内联函数 inline1.1 概念1.2查看方法1.3 特性1.4 题外话&#xff1a;宏 2.auto关键字2.1 auto 简介2.2 auto使用细则 3. 基于范围的for循环4. nullpter 1.内联函数 inline 1.1 概念 inline int Add(int x, int y) {return x y; } int main(){int ret 0;ret Add…

Vue input密码输入框自定义密码眼睛icon

我们用的饿了么UI组件库里,密码输入框的icon是固定不变的,如下所示: 点击"眼睛"这个icon不变,现在需求是UI给的设计稿里,密码输入框的"眼睛"有如下两种: 代码如下: <el-input:key="passwordType"ref="password"