二分查找题目:山脉数组中查找目标值

文章目录

  • 题目
    • 标题和出处
    • 难度
    • 题目描述
      • 要求
      • 示例
      • 数据范围
  • 解法
    • 思路和算法
    • 代码
    • 复杂度分析

题目

标题和出处

标题:山脉数组中查找目标值

出处:1095. 山脉数组中查找目标值

难度

8 级

题目描述

要求

这是一个交互式问题

当且仅当以下条件满足时,数组 arr \texttt{arr} arr山脉数组

  • arr.length ≥ 3 \texttt{arr.length} \ge \texttt{3} arr.length3
  • 存在下标 i \texttt{i} i,其范围是 0 < i < arr.length   -   1 \texttt{0} < \texttt{i} < \texttt{arr.length - 1} 0<i<arr.length - 1,使得:
    • arr[0] < arr[1] < … < arr[i   -   1] < arr[i] \texttt{arr[0]} < \texttt{arr[1]} < \ldots < \texttt{arr[i - 1]} < \texttt{arr[i]} arr[0]<arr[1]<<arr[i - 1]<arr[i]
    • arr[i] > arr[i   +   1] > … > arr[arr.length   -   1] \texttt{arr[i]} > \texttt{arr[i + 1]} > \ldots > \texttt{arr[arr.length - 1]} arr[i]>arr[i + 1]>>arr[arr.length - 1]

给定一个山脉数组 mountainArr \texttt{mountainArr} mountainArr,返回使得 mountainArr.get(index) = target \texttt{mountainArr.get(index)} = \texttt{target} mountainArr.get(index)=target最小下标 index \texttt{index} index。如果不存在这样的下标 index \texttt{index} index,返回 -1 \texttt{-1} -1

不能直接访问该山脉数组,必须通过 MountainArray \texttt{MountainArray} MountainArray 接口访问数组:

  • MountainArray.get(k) \texttt{MountainArray.get(k)} MountainArray.get(k) 返回数组下标 k \texttt{k} k 处的元素(下标从 0 \texttt{0} 0 开始)。
  • MountainArray.length() \texttt{MountainArray.length()} MountainArray.length() 返回数组的长度。

MountainArray.get \texttt{MountainArray.get} MountainArray.get 调用超过 100 \texttt{100} 100 次的提交将被视为错误答案。此外,任何试图规避判题系统的解法都是不允许的。

示例

示例 1:

输入: array   =   [1,2,3,4,5,3,1],   target   =   3 \texttt{array = [1,2,3,4,5,3,1], target = 3} array = [1,2,3,4,5,3,1], target = 3
输出: 2 \texttt{2} 2
解释: 3 \texttt{3} 3 在数组中出现了两次,下标分别为 2 \texttt{2} 2 5 \texttt{5} 5。返回最小的下标 2 \texttt{2} 2

示例 2:

输入: array   =   [0,1,2,4,2,1],   target   =   3 \texttt{array = [0,1,2,4,2,1], target = 3} array = [0,1,2,4,2,1], target = 3
输出: -1 \texttt{-1} -1
解释: 3 \texttt{3} 3 在数组中没有出现,返回 -1 \texttt{-1} -1

数据范围

  • 3 ≤ mountainArr.length() ≤ 10 4 \texttt{3} \le \texttt{mountainArr.length()} \le \texttt{10}^\texttt{4} 3mountainArr.length()104
  • 0 ≤ target ≤ 10 9 \texttt{0} \le \texttt{target} \le \texttt{10}^\texttt{9} 0target109
  • 0 ≤ mountainArr.get(index) ≤ 10 9 \texttt{0} \le \texttt{mountainArr.get(index)} \le \texttt{10}^\texttt{9} 0mountainArr.get(index)109

解法

思路和算法

根据山脉数组的定义,山脉数组中存在唯一的最大值,该最大值为峰顶元素,峰顶元素所在下标为峰顶下标,峰顶左侧和右侧为两个非空子数组。峰顶左侧的子数组为严格单调递增的数组,峰顶右侧的子数组为严格单调递减的数组,因此峰顶同一侧的子数组中的元素各不相同。如果一个元素在山脉数组中出现且该元素不是峰顶元素,则该元素最多在山脉数组中出现两次,即在峰顶左侧的子数组和峰顶右侧的子数组中最多各出现一次。

为了判断目标值在山脉数组中是否存在,需要首先得到山脉数组的峰顶下标,将山脉数组分成三个部分:峰顶、左侧子数组和右侧子数组,然后分别在三个部分寻找目标值。由于 MountainArray.get \texttt{MountainArray.get} MountainArray.get 操作的调用次数有上限,因此寻找峰顶下标和在每个部分寻找目标值都需要使用二分查找实现。

为了方便表述,用 array \textit{array} array 表示数组,用 n n n 表示山脉数组的长度,用 peakIndex \textit{peakIndex} peakIndex 表示峰顶下标,则 1 ≤ peakIndex ≤ n − 2 1 \le \textit{peakIndex} \le n - 2 1peakIndexn2,峰顶下标为下标范围 [ 1 , n − 2 ] [1, n - 2] [1,n2] 内使得 array [ peakIndex ] > array [ peakIndex + 1 ] \textit{array}[\textit{peakIndex}] > \textit{array}[\textit{peakIndex} + 1] array[peakIndex]>array[peakIndex+1] 的最小下标 peakIndex \textit{peakIndex} peakIndex

low \textit{low} low high \textit{high} high 分别表示二分查找的下标范围的下界和上界,初始时 low = 1 \textit{low} = 1 low=1 high = n − 2 \textit{high} = n - 2 high=n2。每次查找时,取 mid \textit{mid} mid low \textit{low} low high \textit{high} high 的平均数向下取整,比较 array [ mid ] \textit{array}[\textit{mid}] array[mid] array [ mid + 1 ] \textit{array}[\textit{mid} + 1] array[mid+1] 的大小关系,调整查找的下标范围。

  • 如果 array [ mid ] > array [ mid + 1 ] \textit{array}[\textit{mid}] > \textit{array}[\textit{mid} + 1] array[mid]>array[mid+1],则 peakIndex ≤ mid \textit{peakIndex} \le \textit{mid} peakIndexmid,因此在下标范围 [ low , mid ] [\textit{low}, \textit{mid}] [low,mid] 中继续查找。

  • 如果 array [ mid ] < array [ mid + 1 ] \textit{array}[\textit{mid}] < \textit{array}[\textit{mid} + 1] array[mid]<array[mid+1],则 peakIndex > mid \textit{peakIndex} > \textit{mid} peakIndex>mid,因此在下标范围 [ mid + 1 , high ] [\textit{mid} + 1, \textit{high}] [mid+1,high] 中继续查找。

low = high \textit{low} = \textit{high} low=high 时,查找结束,此时 low \textit{low} low 即为峰顶下标。

得到峰顶下标 peakIndex \textit{peakIndex} peakIndex 之后, array [ peakIndex ] \textit{array}[\textit{peakIndex}] array[peakIndex] 即为峰顶元素。比较峰顶元素与目标值的大小关系,以下两种情况可直接返回结果。

  • 如果峰顶元素等于目标值,则由于峰顶元素在山脉数组中只出现一次,因此峰顶下标即为元素值等于目标值的最小下标,返回峰顶下标。

  • 如果峰顶元素小于目标值,则由于峰顶元素是山脉数组中的最大值,因此山脉数组中的所有元素都小于目标值,返回 − 1 -1 1

如果峰顶元素大于目标值,则为了得到目标值在山脉数组中的最小下标,需要依次在左侧子数组和右侧子数组中寻找目标值。由于左侧子数组严格单调递增,右侧子数组严格单调递减,因此在左侧子数组和右侧子数组中使用二分查找的方式寻找目标值。在左侧子数组和右侧子数组中寻找目标值的顺序如下。

  1. 在左侧子数组中寻找目标值,如果左侧子数组中找到目标值则返回目标值的下标。

  2. 如果左侧子数组中没有找到目标值,则在右侧子数组中寻找目标值,如果右侧子数组中找到目标值则返回目标值的下标。

  3. 如果右侧子数组中也没有找到目标值,则返回 − 1 -1 1

由于二分查找的时间复杂度是 O ( log ⁡ n ) O(\log n) O(logn),因此该解法的时间复杂度是 O ( log ⁡ n ) O(\log n) O(logn)。已知山脉数组的长度 n n n 最大为 1 0 4 10^4 104,考虑一个问题: MountainArray.get \texttt{MountainArray.get} MountainArray.get 操作最多会被调用多少次?

  1. 寻找峰顶下标需要在整个山脉数组中二分查找,查找的次数是 ⌈ log ⁡ n ⌉ = 14 \lceil \log n \rceil = 14 logn=14。每次查找时,由于需要获得当前下标处的元素值与后一个下标处的元素值,因此每次查找需要调用 MountainArray.get \texttt{MountainArray.get} MountainArray.get 操作 2 2 2 次,一共调用 MountainArray.get \texttt{MountainArray.get} MountainArray.get 操作 2 × 14 = 28 2 \times 14 = 28 2×14=28 次。

  2. 得到峰顶下标之后,为了得到峰顶元素,需要对峰顶下标调用 MountainArray.get \texttt{MountainArray.get} MountainArray.get 操作 1 1 1 次。

  3. n 1 n_1 n1 n 2 n_2 n2 分别表示左侧子数组和右侧子数组的长度, n 1 n_1 n1 n 2 n_2 n2 都是正整数且 n 1 + n 2 + 1 = n n_1 + n_2 + 1 = n n1+n2+1=n,则左侧子数组和右侧子数组的二分查找次数分别是 ⌈ log ⁡ n 1 ⌉ \lceil \log n_1 \rceil logn1 ⌈ log ⁡ n 2 ⌉ \lceil \log n_2 \rceil logn2,每次二分查找都需要调用 MountainArray.get \texttt{MountainArray.get} MountainArray.get 操作 1 1 1 次。当 n 1 n_1 n1 n 2 n_2 n2 最接近时, ⌈ log ⁡ n 1 ⌉ + ⌈ log ⁡ n 2 ⌉ \lceil \log n_1 \rceil + \lceil \log n_2 \rceil logn1+logn2 的值最大,为 13 + 13 = 26 13 + 13 = 26 13+13=26

因此 MountainArray.get \texttt{MountainArray.get} MountainArray.get 操作最多会被调用 28 + 1 + 26 = 55 28 + 1 + 26 = 55 28+1+26=55 次,少于题目要求的 100 100 100 次调用上限。

代码

class Solution {
    public int findInMountainArray(int target, MountainArray mountainArr) {
        int n = mountainArr.length();
        int peakIndex = getPeakIndex(mountainArr, n);
        int peakElement = mountainArr.get(peakIndex);
        if (peakElement == target) {
            return peakIndex;
        }
        if (peakElement < target) {
            return -1;
        }
        int index = findInAscending(target, mountainArr, 0, peakIndex - 1);
        if (index >= 0) {
            return index;
        }
        index = findInDescending(target, mountainArr, peakIndex + 1, n - 1);
        return index;
    }

    public int getPeakIndex(MountainArray mountainArr, int n) {
        int low = 1, high = n - 2;
        while (low < high) {
            int mid = low + (high - low) / 2;
            int curr = mountainArr.get(mid), next = mountainArr.get(mid + 1);
            if (curr > next) {
                high = mid;
            } else {
                low = mid + 1;
            }
        }
        return low;
    }

    public int findInAscending(int target, MountainArray mountainArr, int low, int high) {
        while (low <= high) {
            int mid = low + (high - low) / 2;
            int num = mountainArr.get(mid);
            if (num == target) {
                return mid;
            } else if (num > target) {
                high = mid - 1;
            } else {
                low = mid + 1;
            }
        }
        return -1;
    }

    public int findInDescending(int target, MountainArray mountainArr, int low, int high) {
        while (low <= high) {
            int mid = low + (high - low) / 2;
            int num = mountainArr.get(mid);
            if (num == target) {
                return mid;
            } else if (num < target) {
                high = mid - 1;
            } else {
                low = mid + 1;
            }
        }
        return -1;
    }
}

复杂度分析

  • 时间复杂度: O ( log ⁡ n ) O(\log n) O(logn),其中 n n n 是山脉数组的长度。二分查找的时间复杂度是 O ( log ⁡ n ) O(\log n) O(logn)

  • 空间复杂度: O ( 1 ) O(1) O(1)

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

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

相关文章

android主题设置为..DarkActionBar.Bridge时自定义DatePicker选中日期颜色

安卓自定义DatePicker选中日期颜色 背景&#xff1a;解决方案&#xff1a;方案一&#xff1a;方案二&#xff1a;实践效果&#xff1a; 背景&#xff1a; 最近在尝试用原生安卓实现仿element-ui表单校验功能&#xff0c;其中的的选择日期涉及到安卓DatePicker组件的使用&#…

6.工厂模式(Factory Method)

定义 通过“对象创建” 模式绕开new&#xff0c;来避免对象创建&#xff08;new&#xff09;过程中所导致的紧耦合&#xff08;依赖具体类&#xff09;&#xff0c;从而支持对象创建的稳定。它是接口抽象之后的第一步工作。 动机 在软件系统中&#xff0c;经常面临着创建对象…

Java CAS操作

通过前面的学习认识到了CPU缓存&#xff0c;Java内存模型&#xff0c;以及线程安全的原子、可见、顺序三大特性。本文则重点认识CAS操作&#xff0c;这是Java并发编程常见的一个操作&#xff0c;AbstractQueuedSynchronizer基于此操作提供了丰富的同步器和各种锁。 CAS&#x…

【Numpy核心编程攻略:Python数据处理、分析详解与科学计算】1.25 视觉风暴:NumPy驱动数据可视化

1.25 视觉风暴&#xff1a;NumPy驱动数据可视化 目录 #mermaid-svg-i3nKPm64ZuQ9UcNI {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-i3nKPm64ZuQ9UcNI .error-icon{fill:#552222;}#mermaid-svg-i3nKPm64ZuQ9UcNI …

鸟瞰欧洲(意境欧洲) 第一季

目录 《鸟瞰欧洲 第一季》纪录片笔记一、基本信息二、详细内容&#xff08;一&#xff09;剧集设置&#xff08;二&#xff09;各国亮点1. **荷兰**2. **意大利**3. **德国**4. **英国**5. **西班牙**6. **波兰** &#xff08;三&#xff09;拍摄特色 三、特色与评价四、总结五…

【MQ】探索 Kafka

高性能 消息的顺序性、顺序写磁盘 零拷贝 RocketMQ内部主要是使用基于mmap实现的零拷贝&#xff0c;用来读写文件 减少cpu的拷贝次数和上下文切换次数&#xff0c;实现文件的高效读写操作 Kafka 零拷贝 Kafka 使用到了 mmap 和 sendfile 的方式来实现零拷贝。分别对应 Jav…

供应链系统设计-供应链中台系统设计(十一)- 清结算中心概念片篇

概述 上篇供应链系统设计-供应链中台系统设计&#xff08;十&#xff09;- 清结算中心概念片篇文中提到了什么是金融客户、资金账号、资金账户、以及资金账号和资金账户的关系&#xff0c;如下图所示&#xff1a; 这些对于清算和结算来说都是前置的概念&#xff0c;本篇文章我…

allegro修改封闭图形线宽

说在前面 我们先把最优解说在前面,然后后面再说如果当时不熟悉软件的时候为了挖孔是用了shapes该怎么修改回来。 挖空最方便的方式是在cutout层画一个圆弧,下面开始图解,先add一个圆弧 z 最好是在画的时候就选择好层,如果忘记了后续再换回去也行,但好像软件有bug,此处并…

使用scikit-learn中的KNN包实现对鸢尾花数据集的预测

引言 K最近邻&#xff08;KNN&#xff09;算法是一种简单且直观的分类算法。它通过计算数据点之间的距离来对新样本进行分类。鸢尾花数据集是一个经典的机器学习数据集&#xff0c;包含了三种不同类型的鸢尾花&#xff0c;每种类型由四个特征&#xff08;花萼长度、花萼宽度、…

Hive:静态分区(分区语法,多级分区,分区的查看修改增加删除)

hive在建表时引入了partition概念。即在建表时&#xff0c;将整个表存储在不同的子目录中&#xff0c;每一个子目录对应一个分区。在查询时&#xff0c;我们就可以指定分区查询&#xff0c;避免了hive做全表扫描&#xff0c;从而提高查询率。 oracle和Hive分区的区别 orcale在…

基于FPGA的BT656解码

概述 BT656全称为“ITU-R BT.656-4”或简称“BT656”,是一种用于数字视频传输的接口标准。它规定了数字视频信号的编码方式、传输格式以及接口电气特性。在物理层面上,BT656接口通常包含10根线(在某些应用中可能略有不同,但标准配置为10根)。这些线分别用于传输视频数据、…

随机矩阵投影长度保持引理及其证明

原论文中的引理 2 \textbf{2} 2 引理 2 \textbf{2} 2的内容​​ &#x1f449;前提 1 1 1&#xff1a;设一个随机矩阵 S ( s i j ) ∈ R t d S\text{}(s_{ij})\text{∈}\mathbb{R}^{t\text{}d} S(sij​)∈Rtd&#xff0c;每个元素 s i j s_{ij} sij​独立同分布于 N ( 0 , …

详细解释java当中的所有知识点(前言及数据类型及变量)(第一部分)

会将java当中的所有的知识点以及相关的题目进行分享&#xff0c;这是其中的第一部分&#xff0c;用红色字体标注出重点&#xff0c;以及加粗的方式进行提醒 目录 一、Java语言概述 1.Java语言简介 2.语言优势 二、main方法 1.Java程序结构组成 2.运行Java程序 3.注释 4.…

STM32 PWMI模式测频率占空比

接线图&#xff1a; PWMI基本结构 代码配置&#xff1a; 与上一章输入捕获代码一样&#xff0c;根据结构体&#xff0c;需要在输入捕获单元再配置一个通道。我们调用一个函数 这个函数可以给结构体赋值&#xff0c;当我们定义了一遍结构体参数&#xff0c;再调用这个函数&…

Fort Firewall:全方位守护网络安全

Fort Firewall是一款专为 Windows 操作系统设计的开源防火墙工具&#xff0c;旨在为用户提供全面的网络安全保护。它基于 Windows 过滤平台&#xff08;WFP&#xff09;&#xff0c;能够与系统无缝集成&#xff0c;确保高效的网络流量管理和安全防护。该软件支持实时监控网络流…

Baklib引领内容管理平台新时代优化创作流程与团队协作

内容概要 在迅速变化的数字化时代&#xff0c;内容管理平台已成为各种行业中不可或缺的工具。通过系统化的管理&#xff0c;用户能够有效地组织、存储和共享信息&#xff0c;从而提升工作效率和创意表达。Baklib作为一款新兴的内容管理平台&#xff0c;以其独特的优势和创新功…

【算法设计与分析】实验2:递归与分治—Hanoi塔、棋盘覆盖、最大子段和

目录 一、实验目的 二、实验环境 三、实验内容 四、核心代码 五、记录与处理 六、思考与总结 七、完整报告和成果文件提取链接 一、实验目的 掌握递归求解问题的思想及对应的程序编码结构。针对不同的问题&#xff0c;能够利用递归进行问题求解&#xff0c;并利用Jav…

mysql_init和mysql_real_connect的形象化认识

解析总结 1. mysql_init 的作用 mysql_init 用于初始化一个 MYSQL 结构体&#xff0c;为后续数据库连接和操作做准备。该结构体存储连接配置及状态信息&#xff0c;是 MySQL C API 的核心句柄。 示例&#xff1a; MYSQL *conn mysql_init(NULL); // 初始化连接句柄2. mysql_…

C语言------数组从入门到精通

1.一维数组 目标:通过思维导图了解学习一维数组的核心知识点: 1.1定义 使用 类型名 数组名[数组长度]; 定义数组。 // 示例&#xff1a; int arr[5]; 1.2一维数组初始化 数组的初始化可以分为静态初始化和动态初始化两种方式。 它们的主要区别在于初始化的时机和内存分配的方…

留学毕业论文如何利用不同问题设计问卷

在留学毕业论文的写作中&#xff0c;我们经常会遇到各种问题&#xff0c;例如选择合适的问题&#xff0c;选择合适的研究方法&#xff0c;以及设计合理的研究过程。然而在完成留学毕业论文的过程中&#xff0c;我们往往会在研究设计这里卡住。即使我们选准了研究问题和研究方法…