【算法】二分查找(下)

news2025/3/31 22:46:41

一、山峰数组的峰顶索引

题目链接:852. 山脉数组的峰顶索引 - 力扣(LeetCode)

题目描述:

给定一个长度为 n 的整数 山脉 数组 arr ,其中的值递增到一个 峰值元素 然后递减。

返回峰值元素的下标。

你必须设计并实现时间复杂度为 O(log(n)) 的解决方案。

示例 1:

输入:arr = [0,1,0]
输出:1

示例 2:

输入:arr = [0,2,1,0]
输出:1

示例 3:

输入:arr = [0,10,5,2]
输出:1

提示:

  • 3 <= arr.length <= 10^5
  • 0 <= arr[i] <= 10^6
  • 题目数据 保证 arr 是一个山脉数组

题目分析:

我们只需要在一个 先升序后降序的数组中 找到转折点的下标即可

需要注意的是,在【提示】中,明确说明:该数组的长度是大于等于3的,并且保证了数组arr是一个山脉数组(满足先升序后降序--必有转折点)。所以当数组长度为3时,返回值为1.

所以峰值元素肯定不会出现在数组的两端,而是出现在数组中间。

即返回值区间为:[1,arr.length-2]

题目所满足的条件在后续解题中,有很大的作用

在本题中,数组具有【二段性】,即当数组被分为两个部分后,每个部分内部都有某种特定的性质。在本题中,前半部分满足升序,后半部分满足降序。我们在【手撕二分查找】中的二分查找本质中,说过:当数组具有有序性/二段性时,存在一个分界点时,使得分界点前后元素的性质不同,便可以使用二分查找来解决问题。

解题思路:

在循环中,我们只需要判断mid处元素是否大于mid-1处的元素,然后依此更新左右边界,直到找到峰值元素

1.确认区间形式:【左开右闭】

2.维护区间形式:初始值、循环条件、左右边界

初始值left=0,right=arr.length-2/arr.length-1(由于峰值元素不出现在数组两端,搜索区间为[1,arr.length-2])。注意:left一定不能为-1,因为当left=-1时,可能会导致mid-1<0越界

循环条件:left<right

左右边界:left=mid,right=mid-1

3.选择mid的计算方式:向上调整:mid=left+(right-left+1)/2

4.返回值:return left

如果我们用left=-1去提交,发现会报一个越界异常,如下:

arr=[3,5,3,2,0]

越界的原因:

  1. 初始时:left= -1,right=4。mid=2,arr[mid]=3<arr[mid-1]=5。令right=mid-1=1
  2. 此时:left=-1,right=1。mid=0。由于mid-1=-1<0,所以越界

为了避免越界,要保证mid-1>=0,即mid>=1。

初始化left=0则可以解决这一问题

解题代码:

class Solution {
    public static int peakIndexInMountainArray(int[] arr) {
        int left=0;
        int right=arr.length-2;//或者可以为arr.length-1
        while(left<right){
            int mid=left+(right-left+1)/2;
            if(arr[mid]>arr[mid-1])left=mid;
            else right=mid-1;
        }
        return left;
    }
}

二、寻找峰值

1.题目链接:162. 寻找峰值 - 力扣(LeetCode)

2.题目描述:

峰值元素是指其值严格⼤于左右相邻值的元素。
给你⼀个整数数组 nums,找到峰值元素并返回其索引。数组可能包含多个峰值,在这种情况下,返
回 任何⼀个峰值 所在位置即可。
你可以假设 nums[-1] = nums[n] = -∞ 。
你必须实现时间复杂度为 O(log n) 的算法来解决此问题。
⽰例 1:
输⼊:nums = [1,2,3,1]
输出:2
解释:3 是峰值元素,你的函数应该返回其索引 2。
⽰例 2:
输⼊:nums = [1,2,1,3,5,6,4]
输出:1 或 5
解释:你的函数可以返回索引 1,其峰值元素为 2;
或者返回索引 5, 其峰值元素为 6。
提⽰:
1 <= nums.length <= 1000
-2^31 <= nums[i] <= 2^31 - 1
对于所有有效的 i 都有 nums[i] != nums[i + 1]

题目分析:

首先解析为什么给出nums[-1]=nums[n]= -∞

这保证了数组一定有峰值。

比如数组是严格递增的,那么nums[n-1]就是峰值。为什么?因为此时nums[n-2]<nums[n-1]>nums[n]。同样地,假如数组是严格递减的,那么nums[0]就是峰值。因为此时nums[-1]<nums[0]>nums[1]

其实简单来说,峰值元素就是一个大于其两侧的元素而已。而nums[-1]和nums[n]为 -∞,则保证了下标为0或者n-1处的元素也可能是峰值元素。

定理:如果i<n-1且nums[i-1]<nums[i],那么在下标[i,n-1]中一定存在至少一个峰值

反证法:假设下标[i,n-1]中没有峰值

·由于i处不是峰值且nums[i-1]<nums[i],所以一定有nums[i]<nums[i+1]成立,否则i就是峰值了。

·由于i+1处不是峰值且nums[i]<nums[i+1],所以一定有nums[i+1]<nums[i+2]成立,否则i+1就是峰值了。

·依此类推,得 nums[i-1]<nums[i]<nums[i+1]<nums[i+2]<........nums[n-1]>nums[n]= -∞

这意味着nums[n-1]是峰值,假设不成立,所以定理成立。

同理可得,如果i<n-1且nums[i-1]>nums[i],那么在[0,i-1]中一定存在至少一个峰值

解题思路:

根据上述分析,我们已知数组中一定存在至少一个峰值。只需要通过比较nums[i-1]和nums[i]得大小关系,从而不断地缩小峰值所在位置的范围,二分找到峰值即可

细节:

  1. 当数组只有一个元素时,该元素即为峰值。应当返回下标0
  2. 当n-1处的元素为峰值元素时,每次更新的都是left,当left走到n-1处(right初始值)时,结束循环,返回n-1

我们这里选择【左开右闭】来解决本题

由于当数组只有一个元素时,应该返回下标0。所以这里初始化left=0

解题代码:

class Solution {
    public int findPeakElement(int[] nums) {
        int left=0;
        int right=nums.length-1;//左开右闭 (0,n-1]
        while(left<right){
            int mid=left+(right-left+1)/2;
            if(nums[mid]>nums[mid-1])left=mid;
            else right=mid-1;
        }
        return left;
    }
}

三、搜索旋转排序数组中的最小值

1.题目链接:153. 寻找旋转排序数组中的最小值 - 力扣(LeetCode)

2.题目描述:

已知一个长度为 n 的数组,预先按照升序排列,经由 1 到 n 次 旋转 后,得到输入数组。例如,原数组 nums = [0,1,2,4,5,6,7] 在变化后可能得到:

  • 若旋转 4 次,则可以得到 [4,5,6,7,0,1,2]
  • 若旋转 7 次,则可以得到 [0,1,2,4,5,6,7]

注意,数组 [a[0], a[1], a[2], ..., a[n-1]] 旋转一次 的结果为数组 [a[n-1], a[0], a[1], a[2], ..., a[n-2]] 。

给你一个元素值 互不相同 的数组 nums ,它原来是一个升序排列的数组,并按上述情形进行了多次旋转。请你找出并返回数组中的 最小元素 。

你必须设计一个时间复杂度为 O(log n) 的算法解决此问题。

示例 1:

输入:nums = [3,4,5,1,2]
输出:1
解释:原数组为 [1,2,3,4,5] ,旋转 3 次得到输入数组。

示例 2:

输入:nums = [4,5,6,7,0,1,2]
输出:0
解释:原数组为 [0,1,2,4,5,6,7] ,旋转 4 次得到输入数组。

示例 3:

输入:nums = [11,13,15,17]
输出:11
解释:原数组为 [11,13,15,17] ,旋转 4 次得到输入数组。

提示:

  • n == nums.length
  • 1 <= n <= 5000
  • -5000 <= nums[i] <= 5000
  • nums 中的所有整数 互不相同
  • nums 原来是一个升序排序的数组,并进行了 1 至 n 次旋转

题目分析:

其实本题的数组可以看成由 两段升序数组(假设为数组M、N)拼接而成。

如果原数组由N+M拼接而成,那么M+N就是一个升序数组。而M、N分别又是升序的

我们用一张图表示,会更加清晰

其中C点就是我们所要求的点

二分的本质:找到一个判断标准,使得查找区间能够一分为二

通过图像我们发现,[A,B]区间内的点都是严格大于D点的值,C点的值是严格小于D点的值。但是当[C,D]区间只有一个元素的时候,C点的值可能等于D点的值

因此,初始化左右两个指针left,right:

根据mid的落点,我们可以这样划分下一次查询的区间:

·当mid在[A,B]区间的时候,也就是mid位置的值严格大于D点的值,下一次查询区间在[mid+1,right]

·当mid在[C,D]区间的时候,也就是mid位置的值严格小于等于D点的值,下一次查询区间在[left,mid]

当区间长度变成1的时候,就是我们要找的结果

注意:我们这里的D需要额外处理。我们将查询区间内的最后一个元素看作D。

解题代码:

class Solution {
        public static int findMin(int[] nums) {
        //左闭右开
        int left=0;
        int right=nums.length-1;
        int x=nums[right];//标记一下最后一个位置的值
        while(left<right){
            int mid=left+(right-left)/2 ;
            if(nums[mid]>x)left=mid+1;
            else right=mid;
        }
        return nums[left];
    }
}

四、0~n-1中缺失的数字

1.题目链接:LCR 173. 点名 - 力扣(LeetCode)

2.题目描述:

某班级 n 位同学的学号为 0 ~ n-1。点名结果记录于升序数组 records。假定仅有一位同学缺席,请返回他的学号。

示例 1:

输入:records = [0,1,2,3,5]
输出:4

示例 2:

输入:records = [0, 1, 2, 3, 4, 5, 6, 8]
输出:7

提示:

1 <= records.length <= 10000

简单来说:有一个长度为n的升序数组,数组内的元素应该为0~n-1。请找出缺少的那个数字

我们本题使用【左闭右开】来解题

这题其实更加简单了。我们只需要比较下标是否与元素相等即可。

如果相等,表示左侧没有缺少,右侧缺少,那么下一次查询区间为[mid+1,right]

如果不相等,表示左侧缺少,右侧不缺少,那么下一次查询区间为[left,mid]

左右指针相遇处的元素则表示 下标与元素不相等。返回此处下标--表示缺少元素

解题代码:

class Solution {
        public static int takeAttendance(int[] records) {
        int left=0;
        int right=records.length;
        //左闭右开
        while(left<right){
            int mid=left+(right-left)/2;
            if(records[mid]==mid)left=mid+1;
            else right=mid;
        }
        return left;
    }
}

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

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

相关文章

【动手学深度学习】#6 卷积神经网络

主要参考学习资料&#xff1a; 《动手学深度学习》阿斯顿张 等 著 【动手学深度学习 PyTorch版】哔哩哔哩跟李牧学AI 由于本系列一开始跳过了第一章引言部分&#xff0c;因此系列编号比书本章节编号提前。现改为和书本统一&#xff08;因为之前自己的原始笔记也是按照书本章节编…

认识一家公司:瑞芯微(Rockchip Electronics Co., Ltd.)以及旗下的两款芯片RK3288\RK3588

瑞芯微&#xff08;Rockchip Electronics Co., Ltd.&#xff09;简介 一、公司概况 瑞芯微电子股份有限公司&#xff08;简称“瑞芯微”&#xff09;成立于2001年&#xff0c;总部位于中国福建省福州市&#xff0c;是一家专注于集成电路设计与研发的高新技术企业。公司采用Fa…

Netty——零拷贝

文章目录 1. 什么是零拷贝&#xff1f;2. 为什么需要零拷贝&#xff1f;2.1 传统 I/O 的拷贝流程2.2 零拷贝的优化2.2.1 通过 sendfile 系统调用2.2.2 通过 mmap (内存映射) 系统调用 3. Netty 实现零拷贝的方式3.1 文件传输优化&#xff1a;FileRegion 封装3.2 直接内存 (Dire…

Java制作简单的聊天室(复习)

设计的知识点&#xff1a;几乎包含java基础的全部知识点&#xff08;java基础语法&#xff0c;java基础进阶&#xff1a;双列集合&#xff0c;io流&#xff0c;多线程&#xff0c;网络编程等&#xff09; 代码如下 客户端&#xff1a; 服务器采用的时多线程的循环多线程的方式…

内核、进程和线程---操作系统

操作系统 操作系统位于用户程序和硬件之间&#xff0c;通过系统调用提供接口可以让应用程序去使用硬件&#xff0c;但是硬件资源的管理和安全控制由操作系统负责。 用户空间和内存空间 在计算机系统中&#xff0c;内存可以分为两大区域&#xff1a;内核空间&#xff08;Ker…

如何在 Postman 中上传图片并在请求中正确引用?

Postman 是一款常用的 API 测试工具&#xff0c;它不仅可以测试 API 的请求和响应&#xff0c;还支持多种数据格式包括图片。如何在 Postman 中传输图片&#xff1f; Postman 如何上传图片并在请求中使用教程

安全+低碳+高效:Acrel-3000助力企业打造未来型电能管理体系-安科瑞黄安南

一 背景 电能因为方便传输、易于转换、便于控制等特性&#xff0c;成为广大企事业单位生产、办公最主要的能量来源。双碳背景下&#xff0c;由于电能清洁、高效、零排放的特点&#xff0c;能源消费侧将逐步以电代煤、以电代油、以电代气&#xff0c;形成以电为中心的能源消费体…

专注自习室:番茄工作法实践

专注自习室&#xff1a;番茄工作法实践 我需要一个任务管理工具&#xff0c;但在网上找了很多都找不到合适的工具。市面上的大多数产品过于强调任务完成性&#xff0c;给我带来了很强的心理压力&#xff0c;这种压力最终反而降低了我的工作效率。于是我决定自己动手&#xff0…

LeetCode算法题(Go语言实现)_16

题目 给定一个二进制数组 nums 和一个整数 k&#xff0c;假设最多可以翻转 k 个 0 &#xff0c;则返回执行操作后 数组中连续 1 的最大个数 。 一、代码实现 func longestOnes(nums []int, k int) int {left, zeroCnt, maxLen : 0, 0, 0for right : 0; right < len(nums); …

CORDIC算法:三角函数的硬件加速革命——从数学原理到FPGA实现的超高效计算方案

计算机该如何求解三角函数&#xff1f;或许你的第一印象是采用泰勒展开&#xff0c;或者采用多项式进行逼近。对于前者&#xff0c;来回的迭代计算开销成本很大&#xff1b;对于后者&#xff0c;多项式式逼近在较窄的范围內比较接近&#xff0c;超过一定范围后&#xff0c;就变…

JVM 面经

1、什么是 JVM? JVM 就是 Java 虚拟机&#xff0c;它是 Java 实现跨平台的基石。程序运行之前&#xff0c;需要先通过编译器将 Java 源代码文件编译成 Java 字节码文件&#xff1b;程序运行时&#xff0c;JVM 会对字节码文件进行逐行解释&#xff0c;翻译成机器码指令&#x…

Ubuntu平台下安装Node相关环境

说明&#xff1a;在进行VUE、TS等开发需要用到NodeJS相关环境&#xff0c;不同的项目有时候需要不同的Node版本支撑。本文将详细讲解NVM、Node、Yarn、PM2等环境安装的实施步骤。 测试服务器环境&#xff1a;22.04 LTS。 1. NVM 定义&#xff1a;Node Version Manager&#x…

Windows Server 2025 使用 IIS 搭建 ASP.NET 3.5 网站

开启远程桌面 参考文章Windows server开启远程桌面教程打开服务管理器。ECS 配置安全组&#xff0c;开启 3389Telnet 验证网络联通性 telnet x.x.x.x 338安装 Windows App&#xff0c;登录验证 安装 ASP.NET 3.5 1.参考文章Windows Server 2012安装 .NET Framework 3.5和 Wi…

【强化学习】基于深度强化学习的微能源网能量管理与优化策略研究【Python】

目录 主要内容 程序要点 2.1 微能源网系统组成 2.2 强化学习及Q学习算法 部分代码 运行结果 下载链接 主要内容 该程序借助深度 Q 网络&#xff08;DQN&#xff09;&#xff0c;学习预测负荷、风 / 光可再生能源功率输出及分时电价等环境信息&#xff0c;运用…

楼宇自控借何种技术,驱动建筑迈向高效绿色

在全球积极倡导可持续发展的大背景下&#xff0c;建筑行业作为能源消耗和碳排放的大户&#xff0c;实现高效绿色发展迫在眉睫。楼宇自控系统凭借其先进的技术手段&#xff0c;成为推动建筑向高效绿色转型的关键力量。那么&#xff0c;楼宇自控究竟借助哪些技术&#xff0c;让建…

监控易一体化运维:监控易机房管理,打造高效智能机房

在数字化浪潮中&#xff0c;企业对数据中心和机房的依赖程度与日俱增&#xff0c;机房的稳定运行成为业务持续开展的关键支撑。信息化的变迁&#xff0c;见证了机房管理从传统模式向智能化、精细化转变的过程。今天&#xff0c;就为大家深度剖析监控易在机房管理方面的卓越表现…

PHP安装HTML转图片的扩展GD库的使用

修改你的PHP.ini文件,找到以下位置 ;extensionphp_gd2.dll 把前面的;去掉…

清华大学第10讲:迈向未来的AI教学实验396页PPT 探索未来教育的无限可能|附PPT下载方法

导 读INTRODUCTION 今天跟大家分享的是清华大学新闻与传播学院、人工智能学院双聘教授沈阳教授团队出品的《迈向未来的AI教学实验》课程作业集&#xff0c;随着人工智能技术的飞速发展&#xff0c;教育领域也迎来了前所未有的变革。该报告为沈阳教授与学生们在“迈向未来的AI教…

《白帽子讲 Web 安全》之服务端请求伪造(SSRF)深度剖析:从攻击到防御

引言 在当今复杂的网络环境中&#xff0c;Web 应用安全犹如一座时刻需要精心守护的堡垒。随着技术的不断演进&#xff0c;各类安全威胁层出不穷&#xff0c;其中服务端请求伪造&#xff08;SSRF&#xff09;正逐渐成为令开发者与安全从业者头疼的一大难题。吴翰清在《白帽子讲…