【一刷剑指Offer】面试题 8:旋转数组的最小数字

news2024/12/29 9:08:59

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

牛客对应题目链接: 旋转数组的最小数字_牛客题霸_牛客网 (nowcoder.com)

核心考点 :数组理解,二分查找,临界条件。

一、《剑指Offer》对应内容


二、分析题目

  • 数组问题的本质:求最小值问题。

1、方法一

  • 理论上遍历一次即可,但是我们可以根据题意选择使用稍微高效且更简单一点的做法。
  • 按照题目要求,要么是一个非递减排序的数组(最小值在最开始),要么是一个旋转(最小值在中间某个地方)。
  • 在旋转之后有个特征:在遍历的时候原始数组是非递减的,在旋转之后就有可能出现递减,引起递减的数字就是最小值

2、方法二

  • 采用二分查找的方式,进行定位二分的本质是二段性,并非单调性。只要一段满足某个性质,另外一段不满足某个性质,就可以用二分。
  • 定义首尾下标,因为是非递减数组旋转,所以旋转后可以将数组看成两部分,前半部分整体非递减,后半部分整体非递减,前半部分整体大于后半部分。
  • 所以,我们可以假设如下定义,让 left 指向最左侧,right 指向最右侧,mid 为二分之后的中间位置。我们假设 nums[nums.size()-1](或 num[0])为 x,如果 nums[mid] > nums[x] 时,说明 mid 位置在原数组前半部分,也就是说,目标最小值在 mid 的右侧,那么我们让 left=mid+1。否则,说明 mid 位置在原数组后半部分,也就是说,目标最小值在 mid 的右侧,让那么我们让 right=mid。
  • 上述这个过程,会让 [left, right] 这个区间缩小。在这个过程中,left 永远在原数组前半部分,right 永远在原数组的后半部分,而范围会一直缩小。当不满足 left < right 时,left 指向的位置就是最小元素的位置。
  • 但是因为题目说的是非递减,也就意味着数据允许重复,因为有重复数据的可能,这意味着我们无法直接根据与 nums[0] 或 nums[nums.size()-1] 的大小关系,将数组划分为两段,即无法通过二分来找到旋转点。
  • 这时,我们就需要判断重复元素是否存在于两端,通过移动指针来避免数据重复的影响。

三、代码(C++) 

本题是153. 寻找旋转排序数组中的最小值 - 力扣(LeetCode)的延伸,可以先尝试第 153 题(区别:153 题元素值互不相同,没有重复值),体会在旋转数组中进行二分查找的思路,再来尝试解决本题。

因为这里找到的题目链接的数据范围都是数组长度 >= 1 的,所以这里就不做判空处理了。

这里贴出 AC 代码:(可以看看两体之间的区别)

//推荐:二分-取右端点为比较值 O(logN)
class Solution {
public:
    int findMin(vector<int>& nums) {
        int n=nums.size();
        int left=0, right=n-1;
        int x=nums[n-1];
        while(left<right)
        {
            int mid=left+(right-left)/2;
            if(nums[mid]>x) left=mid+1;
            else right=mid;
        }
        return nums[left];
    }
};

//不推荐:二分-取左端点为比较值 O(logN)
class Solution {
public:
    int findMin(vector<int>& nums) {
        int n=nums.size();
        if (n==1 || nums[0]<nums[n-1]) return nums[0];
        int left=0, right=n-1;
        int x=nums[0];
        while(left<right)
        {
            int mid=left+(right-left)/2;
            if(nums[mid]>=x) left=mid+1;
            else right=mid;
        }
        return nums[left];
    }
};

1、方法一(题目要求尽可能减少整个过程的操作步骤,所以推荐写方法二)

//暴力解法一
class Solution {
public:
    int findMin(vector<int>& nums) {
        int n=nums.size();
        int res=INT_MAX;
        for(int i=0; i<n; i++)
            if(res>nums[i])
                res=nums[i];
        return res;
    }
};

//暴力解法二(上述描述写法)
class Solution {
public:
    int findMin(vector<int>& nums) {
        int res=nums[0];
        int n=nums.size();
        for(int i=1; i<n; i++)
        {
            if(nums[i-1]>nums[i])
            {
                res=nums[i];
                break;
            }
        }
        return res;
    }
};

2、方法二

//推荐:二分-取右端点为比较值 O(logN)
class Solution {
public:
    int findMin(vector<int>& nums) {
        int n=nums.size();
        int left=0, right=n-1;
        while(left<right && nums[0]==nums[right])
            right--;
        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];
    }
};
//牛客网
class Solution {
public:
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param nums int整型vector 
     * @return int整型
     */
    int minNumberInRotateArray(vector<int>& nums) {
        int n=nums.size();
        int left=0, right=n-1;
        while(left<right && nums[0]==nums[right])
            right--;
        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];
    }
};

四、进阶

这道题与153. 寻找旋转排序数组中的最小值 - 力扣(LeetCode)类似,但 nums 可能包含重复元素。允许重复会影响算法的时间复杂度吗?会如何影响,为什么? 

这道题目的平均时间复杂度为 O(logN),其中 n 是数组 nums 的长度。如果数组是随机生成的,那么数组中包含相同元素的概率很低,在二分查找的过程中,大部分情况都会忽略一半的区间。而在最坏情况下,如果数组中的元素完全相同,那么 while 循环就需要执行 n 次,时间复杂度为 O(n)。

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

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

相关文章

Ajax和axios基础

AJAX Asynchronous JavaScript And XML 异步的JavaScript和XML 作用 数据交换: 通过Ajax可以给服务器发送请求,服务器将数据直接响应回给浏览器. 异步交互: 可以在不重新加载整个页面的情况下,与服务器交换数据并更新部分网页的技术. 同步和异步 同步发送请求: 浏览器发…

基于单片机的羽毛球计分器(含proteus仿真和程序)

目录 完整文本及仿真、程序可私信我获取 前言 第一章 设计任务及方案 1.1 设计任务 1.2 总体设计分析 1.3 功能模块方案设计 1.4 方案确定 第二章、硬件设计 2.1 AT89C51 单片机芯片介绍 2.1.1 主要特性 2.1.2 管脚说明 2.1.3 元件清单 2.2 电路介绍 2…

黑马微服务课程2

课程地址&#xff1a;2024最新SpringCloud微服务开发与实战&#xff0c;java黑马商城项目微服务实战开发&#xff08;涵盖MybatisPlus、Docker、MQ、ES、Redis高级等&#xff09;_哔哩哔哩_bilibili 课程名称&#xff1a;2024最新SpringCloud微服务开发与实战&#xff0c;java…

《数据密集型应用系统设计》笔记——第一部分 数据系统基础(ch1-4)

写在前面&#xff1a;对DDIA这本书慕名已久&#xff0c;粗看书里的一些知识都或多或少了解&#xff0c;但仔细阅读下来&#xff0c;还是缺少对细节的认识。目前看了四个章节&#xff0c;这本书一直在围绕两个问题&#xff1a;是什么和为什么&#xff0c;来做阐述&#xff0c;针…

AI大模型探索之路-训练篇3:大语言模型全景解读

文章目录 前言一、语言模型发展历程1. 第一阶段&#xff1a;统计语言模型&#xff08;Statistical Language Model, SLM&#xff09;2. 第二阶段&#xff1a;神经语言模型&#xff08;Neural Language Model, NLM&#xff09;3. 第三阶段&#xff1a;预训练语言模型&#xff08…

【高阶数据结构】B树 {B树的概念;B树的实现:节点设计,查找,插入,遍历,删除;B树的性能分析;B+树和B*树;B树的应用}

一、常见的搜索结构 以上结构适合用于数据量相对不是很大&#xff0c;能够一次性存放在内存中&#xff0c;进行数据查找的场景。如果数据量很大&#xff0c;比如有100G数据&#xff0c;无法一次放进内存中&#xff0c;那就只能放在磁盘上了&#xff0c;如果放在磁盘上&#xff…

Maven多模块快速升级超好用Idea插件-MPVP

功能&#xff1a;多模块maven项目快速升级指定版本插件&#xff0c;并提供预览和相关升级模块日志能力。 可快速进行版本升级&#xff0c;进行部署到Maven仓库。 安装&#xff1a; 可在idea插件中心进行安装 / 下载资源拖动安装 MPVP(Maven) - IntelliJ IDEs Plugin | Marke…

IPv4 NAT(含Cisco配置)

IPv4 NAT&#xff08;含Cisco配置&#xff09; IPv4私有空间地址 类RFC 1918 内部地址范围前缀A10.0.0.0 - 10.255.255.25510.0.0.0/8B172.16.0.0 - 172.31.255.255172.16.0.0/12C192.168.0.0 - 192.168.255.255192.168.0.0/16 这些私有地址可在企业或站点内使用&#xff0c…

【第35天】SQL进阶-SQL高级技巧-透视表(SQL 小虚竹)

回城传送–》《100天精通MYSQL从入门到就业》 文章目录 零、前言一、练习题目二、SQL思路初始化数据什么是透视表实战体验 三、总结四、参考 零、前言 今天是学习 SQL 打卡的第 35 天。 ​ 我的学习策略很简单&#xff0c;题海策略 费曼学习法。如果能把这些题都认认真真自己…

网络安全-Diffie Hellman密钥协商

密钥协商是保密通信双方&#xff08;或更多方&#xff09;通过公开信道来共同形成密钥的过程。一个密钥协商方案中&#xff0c;密钥的值是某个函数值&#xff0c;其输入量由两个成员&#xff08;或更多方&#xff09;来提供。密钥协商的记过是参与协商的双方&#xff08;或更多…

消息服务应用1——java项目使用websocket

在当前微服务项目中&#xff0c;由于业务模块众多&#xff0c;消息服务的使用场景变得异常活跃。而WebSocket由于其自身的可靠性强&#xff0c;实时性好&#xff0c;带宽占用更小的优势&#xff0c;在实时通讯应用场景中独占鳌头&#xff0c;加上HTML5标准的普及流行&#xff0…

MultiHeadAttention在Tensorflow中的实现原理

前言 通过这篇文章&#xff0c;你可以学习到Tensorflow实现MultiHeadAttention的底层原理。 一、MultiHeadAttention的本质内涵 1.Self_Atention机制 MultiHeadAttention是Self_Atention的多头堆嵌&#xff0c;有必要对Self_Atention机制进行一次深入浅出的理解&#xff0c;这…

Docker 入门篇(一)-- 简介与安装教程(Windows和Linux)

一、Docker简介 Docker是一个开源的应用容器引擎&#xff0c;让开发者可以打包他们的应用以及依赖包到一个可移植的容器中&#xff0c;然后发布到任何Linux机器上&#xff0c;也可以实现虚拟化。容器是完全使用沙箱机制&#xff0c;相互之间没有任何接口&#xff08;类似iPhon…

记录海豚调度器删除工作流实例失败的解决办法(DolphinScheduler的WebUI删除失败)

本博客记录以下问题解决办法&#xff1a;使用dolphinscheduler的WebUI运行工作流后出现内存占用过高导致的任务阻塞问题&#xff0c;并且在删除工作流实例时总是报错无法删除 解决步骤 在前端页面无法删除&#xff0c;于是搜索资料&#xff0c;发现可以登录数据库进行工作流实…

【C语言 |预处理指令】预处理指令详解(包括编译与链接)

目录 一、编译与链接 1.翻译环境 -预处理 -编译 -汇编 -链接 2.执行环境 二、预定义符号 三、#define定义常量 四、#define定义宏 五、带有副作用的宏参数 六、宏替换的规则 七、 宏函数的对比 八、#和## 1.#运算符 2.##运算符 九、命名约定 十、#undef 十一、 命…

【服务器部署篇】Linux下Tomcat安装和配置

作者介绍&#xff1a;本人笔名姑苏老陈&#xff0c;从事JAVA开发工作十多年了&#xff0c;带过刚毕业的实习生&#xff0c;也带过技术团队。最近有个朋友的表弟&#xff0c;马上要大学毕业了&#xff0c;想从事JAVA开发工作&#xff0c;但不知道从何处入手。于是&#xff0c;产…

Linux网络编程---Socket编程

一、网络套接字 一个文件描述符指向一个套接字(该套接字内部由内核借助两个缓冲区实现。) 在通信过程中&#xff0c;套接字一定是成对出现的 套接字通讯原理示意图&#xff1a; 二、预备知识 1. 网络字节序 内存中的多字节数据相对于内存地址有大端和小端之分 小端法&…

Ubuntu终端常用指令

cat cat 读取文件的内容 1、ls 一、 1、ll 显示当前目录下文件的详细信息,包括读写权限,文件大小,文件生成日期等(若想按照更改的时间先后排序,则需加-t参数,按时间降序(最新修改的时间排在最前)执行: $ ll -t, 按时间升序执行: $ ll -t | tac): ll 2、查看当前所处路径(完整…

Qt中常用对话框

Qt中的对话框&#xff08;QDialog&#xff09;是用户交互的重要组件&#xff0c;用于向用户提供特定的信息、请求输入、或进行决策。Qt提供了多种标准对话框以及用于自定义对话框的类。以下将详细介绍几种常用对话框的基本使用、使用技巧以及注意事项&#xff0c;并附带C示例代…

node.js 解析post请求 方法一

前提&#xff1a;依旧以前面发的node.js服务器动态资源处理代码 具体见 http://t.csdnimg.cn/TSNW9为模板&#xff0c;在这基础上进行修改。与动态资源处理代码不同的是&#xff0c;这次的用户信息我们借用表单来实现。post请求解析来获取和展示用户表单填写信息 1》代码难点&…