【LeetCode 算法专题突破】二分查找(⭐)

news2024/11/28 4:31:39

文章目录

  • 前言
  • 1. 二分经典模板题目
    • 题目描述
    • 代码:
  • 2. 在排序数组中查找元素的第一个和最后一个位置
    • 题目描述
    • 代码
  • 3. 有效的完全平方数
    • 题目描述
    • 代码
  • 4. 寻找峰值
    • 题目描述
    • 代码
  • 5. 寻找旋转排序数组中的最小值
    • 题目描述
    • 代码
  • 6. 点名
    • 题目描述
    • 代码
  • 总结

前言

我刷过不少算法题目,也得过算法竞赛的奖状,但是并没有成体系的总结,或者说学习算法的类型,所以我决定把常见的算法进行一次归类,然后总结每个经典类型的算法的知识重点,加强算法能力,完善算法体系,也希望能对你有所帮助~

1. 二分经典模板题目

力扣链接:https://leetcode.cn/problems/binary-search/

题目描述

题目描述
这道题目是一道非常经典的二分查找的模板题,可以说,所有二分算法入门都离不开这道题目,题目的要求很简单,就是从一段有序的数组中查找 target 值。

代码:

func search(nums []int, target int) int {
    left, right := 0, len(nums)-1
    for left <= right {
        mid := left+(right-left)/2
        if nums[mid] < target {
            left = mid+1
        } else if nums[mid] > target {
            right = mid-1
        } else {
            return mid 
        }
    }
    return -1
}

我的习惯是每做完一道题目,要能从题目中学习到一些什么,也许是熟练度,也许是对这个算法的理解加深了,那做完这道题目我学到了什么?

我加深了对二分查找排除区间的理解:

当 mid 位置的值小于 target(nums[mid] < target ),证明左半边的区间,包括 mid 位置,不可能存在 target 值,所以左区间 left = mid+1;

当 mid 位置的值大于 target(nums[mid] > target),证明右半边的区间,包括 mid 位置,不可能存在 target 值,所以右区间 right = mid-1;

当 mid 位置等于 target,那当然就证明找到了,可以 return 了。

我个人认为这道题目的核心思想,或者说二分的一个思想,就体现在这里

2. 在排序数组中查找元素的第一个和最后一个位置

现在你已经学会二分了,来一道变式题试试吧~

题目链接:https://leetcode.cn/problems/find-first-and-last-position-of-element-in-sorted-array/

题目描述


这道题的难点其实就是怎么求到 target 值的起点和终点,那我们只需要用两次二分查找,分别查找他的起点和终点就行啦

代码

func searchRange(nums []int, target int) []int {
    left, right, begin, end := 0, len(nums)-1, -1, -1
    // 求左区间
    for left <= right {
        mid := left+(right-left)/2
        if nums[mid] > target {
            right = mid-1
        } else if nums[mid] < target {
            left = mid+1
        } else {
            begin = mid
            right--
        }
    }
    // 恢复 left 和 right
    left, right = 0, len(nums)-1
    // 求右区间
    for left <= right {
        mid := left+(right-left)/2
        if nums[mid] > target {
            right = mid-1
        } else if nums[mid] < target {
            left = mid+1
        } else {
            end = mid
            left++
        }
    }
    return []int{begin, end}
}

这道题目的二分查找使用的方法和模板题是一模一样的,但是为了找到左右区间,我们在遇到 nums[mid] == target 情况,稍微进行了一些调整:

在找左区间的时候,我们记录下 begin 之后,让 right–,这样 mid 的位置也会不断向左,直到 nums[mid] != target,这样 begin 的值就是左区间

在找右区间的时候,我们记录下 end 之后,让 left++,这样 mid 的位置也会不断向右,直到 nums[mid] != target,这样 end 的值就是右区间

3. 有效的完全平方数

现在你已经学会在有序数组中使用二分查找了,那,如果题目没有给你有序数组,也没有提示你时间复杂度要求呢?作为一个算法菜鸟,我的办法就是:多刷题,见多识广(说人话:你刷过类似的题目不就会做了)

题目链接:https://leetcode.cn/problems/valid-perfect-square/description/

题目描述


抛开题目不谈,如果要你来求完全平方数,你会怎么做?那肯定是用原数折半之后乘一下看看对不对的上,对不上就继续折半,为什么这样做?因为这样效率很高~ 既然如此,那我们为什么不用二分做来试试

代码

func isPerfectSquare(num int) bool {
    left, right := 0, num
    for left <= right {
        mid := left+(right-left)/2
        sqrt := mid*mid
        if sqrt < num {
            left = mid+1
        } else if sqrt > num {
            right = mid-1
        } else {
            return true
        }
    }
    return false
}

咱也没有细想代码的细节,就是简单的把二分算法套进了这个场景,现在来看,以后如果遇到类似的问题,也可以用二分~

4. 寻找峰值

那接下来咱们就多刷一点题目,看看哪里还能用二分查找算法,增加我们的二分熟练度和能力~

题目链接:https://leetcode.cn/problems/find-peak-element/

题目描述


这道题我们连 target 值都没有,他甚至也不是一个有序的序列,我们能用二分算法来做吗?来看看代码

代码

func findPeakElement(nums []int) int {
    left, right := 0, len(nums)-1
    for left < right {
        mid := left+(right-left)/2
        if nums[mid] < nums[mid+1] {
            left = mid+1
        } else if nums[mid] > nums[mid+1] {
            right = mid
        }
    }
    return right
}

乍一看,我们好像无从下手,那就慢慢分析,看看该怎么解决。

题目要求我们要查找数组中任意一个峰值,那我们可以怎么找到峰值呢?我们可以分析到,其实这个数组无非就分为连个区间,一个是递增区间,一个是递减区间,而峰值就是两个区间交替的标志

假设我们随机找一个点,如果他比右边位置的值小,那就证明他在一个递增的区间;如果他比右边的位置的值大,那就证明他在一个递减的区间

通过这个性质,我们使用二分算法,当 nums[mid] < nums[mid+1] 时,在一个递增的区间,山顶必定在 mid+1 以及之后的位置;当 nums[mid] > nums[mid+1] 时,在一个递减的区间,山顶则可能在 mid 或者是之前的位置(这里要注意了,山顶有可能就是 mid 位置)

所以 right = mid,而不需要 -1 的操作。也因为这里不需要 -1,当 left == right 的时候,如果我们已经找到山顶了,那该怎么跳出循环?

这里我们可以选择特判一下,也可以把循环条件改成 left < right。有些时候,模板也不是一定要定死的,我们是可以根据实际情况进行调整的。这也我们做完这道题目学到的东西,又或者说积累到的经验。

5. 寻找旋转排序数组中的最小值

咱们继续来做一些不同场景下的二分的应用~

题目链接:https://leetcode.cn/problems/find-minimum-in-rotated-sorted-array/

题目描述


这道题目的解题思路其实和上一道题类似,我们要根据题目给的条件,找到一个能够比较的参照物,或者说一个参照系。来看代码

代码

func findMin(nums []int) int {
    n := len(nums)-1
    left, right := 0, n
    for left < right {
        mid := left+(right-left)/2
        if nums[mid] > nums[n] {
            left = mid+1
        } else if nums[mid] < nums[n] {
            right = mid
        }
    }
    return nums[left]
}

我们还是从题目条件出发,题目给出的是一个旋转过的升序数组,你发现,这又不是一个有序的数组,这该咋用二分来解呢?

分析一下,有序数组旋转之后,他会分成两个递增的区间,一个区间大,一个区间小,我们可以拿区间的最大值作为参照物

举个例子,这个数组最右边的值,他只有两种可能,1. 是小区间的最大值,2. 大区间的最大值(这种情况下,这个数组就是没有旋转过的情况,那我们可以先忽略特殊情况(这算是我的一点做题的技巧,因为特殊情况可以到时候想着怎么特判))

题目要求我们找到最小元素,也就是我们只需要找到小区间,再在小区间找到区间内最小元素即可,那就:

当 nums[mid] > nums[n] 时,mid 位置大于小区间的最大值,证明 mid 位置在大区间,所以让 left = mid+1;
当 nums[mid] < nums[n] 时,mid 位置小于小区间的最大值,证明 mid 位置在小区间,所以让 right = mid;因为我们要找的值就在小区间,所以不能 -1

然后我们在模拟一下特殊情况,当 nums[mid] < nums[n] 时,n 位置是大区间的最大值,让 right = mid 会导致最后的出现 left = right 的情况,那我们就把循环的条件调整为:left < right

又是一道变式的二分题目,其实我们只要分析出,以什么作为参照系来排除区间的位置,那之后的工作就很容易了~

6. 点名

那咱们就趁热打铁,再来最后一道,增强一下信心,以后二分题目就不用愁啦~

题目链接:https://leetcode.cn/problems/que-shi-de-shu-zi-lcof/

题目描述


这道题我其实一开始也想不出来,选这道题的目的其实也是想说明,算法积累的重要性,见多识广,才能思路开阔,临危不乱。来看代码:

代码

func takeAttendance(records []int) int {
    left, right := 0, len(records)-1
    for left < right {
        mid := left+(right-left)/2
        if records[mid] == mid {
            left = mid+1
        } else {
            right = mid
        }
    }
    if records[right] == right {
        return right+1
    }
    return right
}

分析题目我们看到,数组是从 0 开始一个升序数组,所以我们可以发现,数组中的数字是和下标是一一对应的,但是缺失了数字之后,数组的数就会比下标大,这样,我们又能将这个数组分为两个区间

我们画个图来看,会更加的清晰:

正常的区间是一一对应的,而失去数字的区间都是对不上的,如果对的上,也就是 records[mid] == mid,就让 left = mid+1;如果对不上,就让 right = mid,因为答案可能就在这个区间

现在就只剩下一个特殊情况了,如果消失的是最后一个数呢?那我们就最后特判一下就行

总结

相信你刷完这六道题目之后,二分已经不在话下了~

再接再厉,去迎接新的挑战吧~

如果哪天对二分又不熟悉了,也可以回来再刷一刷,练练手感~

最后,祝你今后刷题愉快~

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

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

相关文章

java: 警告: 源发行版 17 需要目标发行版 17

一、遇到问题&#xff1a; java: 警告: 源发行版 17 需要目标发行版 17 二、分析原因&#xff1a;JDK版本不一致 在idea中编辑器中修改JDK配置 三、解决问题 找到settings -- Build,Execution,Deployment -- compiler -- JavaCompiler 进行更改版本 另外还要找到两个地方的J…

科普②| 大数据有什么用?大数据技术的应用领域有哪些?

1、提供个性服务很多人觉得大数据好像离我们很远&#xff0c;其实我们在日常所使用的智能设备&#xff0c;就需要大数据的帮助。比如说我们运动时候戴的运动手表或者是运动手环&#xff0c;就可以在我们平时运动的时候&#xff0c;帮助我们采集运动数据及热量消耗情况。进入睡眠…

类目体系设计总结

一、背景 公司窗帘产品在做分类调整&#xff0c;从原先二级类目调整为三级类目&#xff0c;相对于平台电商我们的类目层次结构要简单很多&#xff08;没有定义商品动态属性等&#xff09;&#xff0c;但对于也有上万款SKU的系统来讲,做好基础的分类对于采购、商品促销、数据报…

消息称三星智能戒指 Galaxy Ring 将延期发布

三星和苹果旗下的智能戒指早有传闻&#xff0c;而最近根据外媒The Elec 报道&#xff0c;三星的智能戒指可能被延期至 2024 年第三季度后发布&#xff0c;这款名为 Galaxy Ring 的智能戒指主要面向健康和 XR 头显市场&#xff0c;可以比 Galaxy Watch 提供更准确的身体及健康数…

Flutter_Slider_SliderTheme_滑杆/滑块_渐变色

调用示例以及效果 SliderTheme(data: SliderTheme.of(context).copyWith(trackHeight: 3,// 滑杆trackShape: const GradientRectSliderTrackShape(radius: 1.5),// 滑块thumbShape: const GradientSliderComponentShape(rectWH: 14, overlayRectSpace: 4, overlayColor: Colou…

网络模型之OSI七层网络模型、TCP/IP四层网络模型

一、计算机网络是什么&#xff1f; 计算机网络是指由通讯网络相互连接的许多自主工作的计算机构成的集合体。 二、网络模型是干什么的&#xff1f; 网络模型就是研究计算机网络中各个部件是以何种规则进行通行。 三、OSI七层网络模型 OSI 是 Open System Interconnection 的…

【Amazon】基于AWS云实例(CentOS 7.9系统)使用kubeadm方式搭建部署Kubernetes集群1.25.4版本

文章目录 前言实验架构介绍K8S集群部署方式说明使用CloudFormation部署EC2实例集群环境准备修改主机名并配置域名解析&#xff08;ALL节点&#xff09;禁用防火墙禁用SELinux加载br_netfilter模块安装ipvs安装 ipset 软件包同步服务器时间关闭swap分区安装Containerd 初始化集群…

40V汽车级P沟道MOSFET SQ4401EY-T1_GE3 工作原理、特性参数、封装形式—节省PCB空间,更可靠

AEC-Q101车规认证是一种基于失效机制的分立半导体应用测试认证规范。它是为了确保在汽车领域使用的分立半导体器件能够在严苛的环境条件下正常运行和长期可靠性而制定的。AEC-Q101认证包括一系列的失效机制和应力测试&#xff0c;以验证器件在高温、湿度、振动等恶劣条件下的可…

97 # session

koa 里的 cookie 用法 koa 里内置了设置 cookie 的方法 npm init -y npm i koa koa/router用法&#xff1a; const Koa require("koa"); const Router require("koa/router"); const crypto require("crypto");const app new Koa(); let …

10_8C++

X-Mind #include <iostream>using namespace std; class Rect { private:int width;int heigjt; public:void init(int w,int h){width w;heigjt h;}void set_w(int w){width w;}void set_h(int h){heigjt h;}void show(){cout << "矩形的周长" <…

【算法练习Day15】平衡二叉树二叉树的所有路径左叶子之和

​&#x1f4dd;个人主页&#xff1a;Sherry的成长之路 &#x1f3e0;学习社区&#xff1a;Sherry的成长之路&#xff08;个人社区&#xff09; &#x1f4d6;专栏链接&#xff1a;练题 &#x1f3af;长路漫漫浩浩&#xff0c;万事皆有期待 文章目录 平衡二叉树二叉树的所有路径…

C++并发与多线程(3) | 其他创建线程的方式

1. 用类(可调用对象) 必须要重载括号运算符,否则不是可调用对象。这种方式其实就是一个仿函数。 示例: #include <iostream> #include <thread> using namespace std;class TA { public:void operator() ()// 不能带参数 {cout << "子线程operato…

外汇天眼:一步错步步错,投资者表示真后悔遇到DIFX杀猪盘

随着现在大互联网时代的发展&#xff0c;外汇投资变得越来越普及&#xff0c;因为外汇的特殊性&#xff0c;很多交易都是通过互联网进行的&#xff0c;但面对良莠不齐的外汇平台&#xff0c;投资者若不能对这些平台进行筛选&#xff0c;极易陷入一些黑平台精心设计的诈骗陷阱内…

第八章 排序 十二、败者树

一、多路平衡带来的问题 二、败者树的构造 三、败者树在K路平衡归并中的应用 1、我们有如下例子 2、接着我们构造一棵败者树&#xff0c;并且选出最小的数的归并段序号 3、接着把归并段3的数据填充进入败者树&#xff0c;这次最多只需要和之前的胜者比3次就能得到最终胜者 也…

SpringCloudGateway网关整合swagger3+Knife4j3,basePath丢失请求404问题

在集成 Spring Cloud Gateway 网关的时候&#xff0c;会出现没有 basePath 的情况&#xff0c;例如定义的 /jeeplus-auth、/jeeplus-system 等微服务前缀导致访问接口404&#xff1a; maven依赖&#xff1a; swagger2于17年停止维护&#xff0c;现在最新的版本为 Swagger3&am…

光引擎、光模块、光器件之间的关系和区别

最近小编有收到一些用户问“光引擎、光模块、光器件之间的关系和区别&#xff1f;”&#xff0c;众所周知光通信技术一直在不断演进&#xff0c;为满足不断增长的数据传输需求提供了强大的解决方案。而光通信系统中&#xff0c;光引擎、光模块和光器件是关键的组成部分&#xf…

2023-10-07 LeetCode每日一题(股票价格跨度)

2023-10-07每日一题 一、题目编号 901. 股票价格跨度二、题目链接 点击跳转到题目位置 三、题目描述 设计一个算法收集某些股票的每日报价&#xff0c;并返回该股票当日价格的 跨度 。 当日股票价格的 跨度 被定义为股票价格小于或等于今天价格的最大连续日数&#xff08…

Openfire身份认证绕过漏洞

漏洞详情&#xff1a; Openfire是采用Java编程语言开发的实时协作服务器&#xff0c;Openfire的管理控制台是一个基于Web的应用程序&#xff0c;被发现可以使用路径遍历的方式绕过权限校验。未经身份验证的用户可以访问Openfire管理控制台中的后台页面。同时由于Openfire管理控…

情侣飞行棋情侣游戏源码

之前的链接失效了&#xff0c;所以重新补充一个 最近很火的抖音上非常火的东西 首先是源码下载地址&#xff1a; http://pan.xiaou61.top/down.php/892e381f7cdb508b5ac55fc9fc0047b3.zip 然后是演示地址&#xff1a; http://fxq.xiaou61.top/#/ 2023最新情侣飞行棋源码 最新情…

bigemap在林业勘测规划设计行业的一些应用

选择Bigemap的原因&#xff1a; 主要注重影像的时效性&#xff0c;软件的影像时效性比其他的更新快&#xff0c;更清晰。 使用场景&#xff1a; 1.林业督查&#xff0c;主要是根据国家下发的图斑&#xff0c;结合测绘局的影像以及bigemap的较新影像对比去年和今年的林地变化。…