LeetCode 278. 第一个错误的版本 解析

这个问题要求找到第一个错误的版本,其中给定一个 API isBadVersion(version) 可以判断某个版本是否错误。由于版本号是有序的,且错误版本之后的所有版本都是错误的,因此可以使用二分查找高效地定位第一个错误版本。

方法思路

  1. 二分查找框架
    初始化左右指针 leftright 分别指向第一个和最后一个版本。
    在每次循环中,计算中间版本 mid,并调用 isBadVersion(mid) 判断:

    • mid 是错误版本,说明第一个错误版本在 mid 或其左侧,更新 right = mid
    • mid 不是错误版本,说明第一个错误版本在 mid 右侧,更新 left = mid + 1
  2. 终止条件
    leftright 相遇时,循环结束,此时 left 即为第一个错误版本。

C++ 代码实现

// The API isBadVersion is already defined for you.
// bool isBadVersion(int version);class Solution {
public:int firstBadVersion(int n) {int left = 1;int right = n;while (left < right) {int mid = left + (right - left) / 2;  // 防止整数溢出if (isBadVersion(mid)) {right = mid;  // 错误版本在 mid 或其左侧} else {left = mid + 1;  // 错误版本在 mid 右侧}}return left;  // 循环结束时,left 即为第一个错误版本}
};

代码解释

  1. 二分查找初始化
    left = 1right = n 分别表示版本范围的左右边界。

  2. 防止整数溢出
    使用 mid = left + (right - left) / 2 代替 mid = (left + right) / 2,避免当 leftright 都很大时发生整数溢出。

  3. 循环条件
    while (left < right) 确保循环在 leftright 相遇时终止。

  4. 条件判断

    • isBadVersion(mid) 为真,说明第一个错误版本在 [left, mid] 范围内,更新 right = mid
    • 若为假,说明第一个错误版本在 [mid+1, right] 范围内,更新 left = mid + 1
  5. 返回结果
    循环结束时,leftright 指向同一个位置,即第一个错误版本。

复杂度分析

  • 时间复杂度:O(log n),每次迭代将搜索范围缩小一半。
  • 空间复杂度:O(1),只需要常数级额外空间。

示例

假设 n = 5,且第一个错误版本是 4
二分查找过程如下:

  1. 初始left = 1, right = 5, mid = 3
    • isBadVersion(3) 返回 falseleft = 4
  2. 第二次迭代left = 4, right = 5, mid = 4
    • isBadVersion(4) 返回 trueright = 4
  3. 终止条件left == right == 4,返回 4

关键点

  1. 二分查找变体
    与传统二分查找不同,这里的 right 指向错误版本,因此更新时直接将 right = mid,而非 mid - 1

  2. 防止溢出
    使用 left + (right - left) / 2 计算中间值,避免整数溢出。

  3. 循环终止条件
    left < right 确保不会陷入死循环,且最终 left 即为所求结果。

这种方法高效且简洁,是解决有序数组中查找特定元素问题的经典思路。

在二分查找解决“第一个错误版本”问题时,while (left < right) 循环的设计是为了精准收缩搜索范围,最终让 leftright 相遇在目标位置(第一个错误版本)。以下从逻辑推导和实例验证两方面详细解释:

核心逻辑:范围收缩的必然性

二分查找的本质是通过不断排除不可能的范围,最终将搜索范围缩小到唯一解。在“第一个错误版本”问题中:

  1. 范围的单调性
    题目明确“错误版本之后的所有版本都是错误的”,即版本序列具有单调性:
    [正确版本, 正确版本, ..., 第一个错误版本, 错误版本, ..., 错误版本]
    这种单调性保证了二分查找的可行性。

  2. 循环条件 left < right 的作用

    • left < right 时,搜索范围至少包含两个版本,需要继续收缩。
    • left == right 时,搜索范围仅剩一个版本,这个版本必然是“第一个错误版本”(因为范围收缩过程中已排除所有不可能的选项),循环终止。

范围收缩的细节

在循环中,通过 isBadVersion(mid) 的结果调整 leftright,确保每次收缩都严格包含目标位置

  • isBadVersion(mid) == true
    mid 是错误版本,说明“第一个错误版本”可能是 mid 或在 mid 左侧(因为左侧可能有更早的错误版本)。因此收缩右边界:right = mid

  • isBadVersion(mid) == false
    mid 是正确版本,说明“第一个错误版本”必然在 mid 右侧(因为 mid 及左侧都是正确的)。因此收缩左边界:left = mid + 1

为什么必然相遇在目标位置?

假设目标位置为 ans(第一个错误版本),证明过程如下:

  1. 初始范围left = 1right = n,显然 ans[left, right] 内。
  2. 每次收缩后
    • mid 是错误版本(mid >= ans),则 right = mid,新范围 [left, right] 仍包含 ans(因为 ans <= mid = right)。
    • mid 是正确版本(mid < ans),则 left = mid + 1,新范围 [left, right] 仍包含 ans(因为 ans > mid = left - 1)。
  3. 范围大小的变化
    每次循环后,范围 [left, right] 的大小严格减小(至少减少 1),因此最终会收缩到 left == right
  4. 终止时的位置
    由于每次收缩都包含 ans,最终 left == right 时,这个位置必然是 ans

实例验证

n = 5,第一个错误版本 ans = 4 为例:

步骤leftrightmid = left + (right-left)/2isBadVersion(mid)调整后范围
1153false(正确版本)left = 4,范围 [4,5]
2454true(错误版本)right = 4,范围 [4,4]
344循环终止(left == right)-结果为 4

可见,最终 leftright 相遇在 4,即目标位置。

总结

while (left < right) 的循环设计通过单调收缩范围,确保每次调整都不遗漏目标位置,最终让 leftright 必然相遇在“第一个错误版本”。这种逻辑适用于所有有序且存在唯一解的二分查找问题,是二分查找的经典应用。

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

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

相关文章

【RK3568+PG2L50H开发板实验例程】FPGA部分 | Pango 的时钟资源——锁相环

本原创文章由深圳市小眼睛科技有限公司创作&#xff0c;版权归本公司所有&#xff0c;如需转载&#xff0c;需授权并注明出处&#xff08;www.meyesemi.com) 1.实验简介 实验目的&#xff1a; 了解 PLL IP 的基本使用方法。 实验环境&#xff1a; Window11 PDS2022.2-SP6.4…

Graph Contrastive Learning with Generative Adversarial Network基于生成对抗网络的图对比学习

1. 什么是图&#xff1f;&#xff08;Graph&#xff09;想象一下社交网络&#xff0c;每个人是一个“点”&#xff08;节点&#xff09;&#xff0c;他们之间的朋友关系是“线”&#xff08;边&#xff09;。这样的点和线组成的结构就是“图”。在计算机科学中&#xff0c;图被…

PyTorch中的torch.argmax()和torch.max()区别

在PyTorch中&#xff0c;torch.argmax()和torch.max()都是针对张量操作的函数&#xff0c;但它们的核心区别在于返回值的类型和用途&#xff1a;1. torch.argmax() 作用&#xff1a;仅返回张量中最大值所在的索引位置&#xff08;下标&#xff09;。返回值&#xff1a;一个整数…

WebSocket主从服务器架构完整教程

目录 1. 前言:为什么要学习WebSocket主从架构 第一章:基础知识准备 2.1 什么是WebSocket 生活中的例子 技术特点 2.2 WebSocket vs HTTP 什么时候用WebSocket? 2.3 什么是主从架构 生活中的例子 技术架构图 2.4 环境准备 需要的软件 项目结构 第二章:WebSock…

Java的extends通配符

在Java泛型中&#xff0c;extends通配符用于限定泛型类型的上界&#xff0c;即指定泛型可以是某个类型或其子类型。它有两种常见用法&#xff1a;类型参数限定和通配符限定&#xff0c;下面详细介绍&#xff1a; 1. 类型参数限定&#xff08;在类/方法定义中&#xff09; 在定义…

vue自定义提示框组件

不想要elementui的消息提示&#xff0c;自定义一个组件系统统一使用 一、写页面 vue &#xff08;我放的目录是src/plugins/message.vue&#xff09;&#xff08;这里面使用elementui 里面icon 需要单独引入&#xff09; <template><Transition name"down"&…

自动驾驶数据集综述:统计特征、标注质量与未来展望

自动驾驶数据集综述&#xff1a;统计特征、标注质量与未来展望 A Survey on Autonomous Driving Datasets: Statistics, Annotation Quality, and a Future Outlook 得益于硬件和深度学习技术的快速进步&#xff0c;自动驾驶近年来迅速发展并展现出良好的性能。高质量的数据集…

redis数据结构和数据类型

1.动态字符串SIMPLE DYNAMIC STRING(SDS)观察上图中的SDS结构&#xff0c;头部包含字符串长度和分配的空间&#xff0c;可以以O&#xff08;1&#xff09;的时间复杂度计算出字符串长度&#xff0c;并且有了字符串长度后可以无视c语言的字符串缺陷&#xff08;\0作为结尾标识&a…

深度学习--神经网络

一、深度学习的简单概念深度学习是一种模仿人类大脑的运行方式&#xff0c;从大量数据中学习特征的学习模式。深度学习是机器学习的子集&#xff0c;它与机器学习的关系如下&#xff1a;二、感知神经网络2.1简单定义神经网络&#xff08;Neural Networks&#xff09;是一种模拟…

.NET 程序的强名称签名与安全防护技术干货

在 .NET 开发领域&#xff0c;保障程序的安全性和完整性至关重要。强名称签名和有效的安全防护措施是实现这一目标的关键手段。下面将详细介绍 .NET 程序的强名称签名以及相关的安全防护方法。一、什么是强名称签名强名称签名是 .NET 框架提供的一种安全机制&#xff0c;其主要…

DNS(Domain Name System,域名系统)

目录 **一、DNS的核心功能****二、DNS的工作原理****1. 解析流程(以车载导航请求为例)****2. 关键机制****三、车载以太网中DNS的特殊性**1. **高可靠性要求**2. **低延迟优化**3. **安全挑战与防护****四、DNS相关协议与技术****五、车载DNS配置示例****六、DNS故障排查工具…

优化 ECharts 多条折线:折线数据不完整导致的X轴日期错乱问题

目录 一、简单介绍 1.1 常见类型 二、时间轴错乱问题 2.1 示例 2.2 示例完整代码 2.3 问题分析 2.4 修复方法 第一步 第二步 2.5 优化后完整代码 一、简单介绍 ECharts 是一款基于 JavaScript 的数据可视化图表库&#xff0c;动态图表是 ECharts 的一个重要应用场景…

网络安全之注入攻击:原理、危害与防御之道

网络安全之注入攻击&#xff1a;原理、危害与防御之道 引言 在OWASP Top 10安全风险榜单中&#xff0c;注入攻击常年占据首位。2023年Verizon数据泄露调查报告显示&#xff0c;67%的Web应用漏洞与注入类攻击直接相关。本文从技术视角系统解析注入攻击的核心原理、典型场景及防御…

Python爬虫动态IP代理报错全解析:从问题定位到实战优化

目录 一、代理IP失效&#xff1a;爬虫的"隐形杀手" 1.1 失效场景复现 1.2 解决方案 二、403封禁&#xff1a;反爬机制的"精准打击" 2.1 封禁原理剖析 2.2 破解方案 三、速度瓶颈&#xff1a;代理性能的"致命短板" 3.1 性能对比测试 3.2…

机器学习基础知识【 激活函数、损失函数、优化器、 正则化、调度器、指标函数】

目录标题机器学习基础知识概览激活函数 (Activation Functions)损失函数 (Loss Functions / Cost Functions)优化器 (Optimizers)正则化 (Regularization)调度器 (Schedulers / Learning Rate Schedulers)指标函数 (Metric Functions)其他重要概念训练流程机器学习基础知识概览…

【达梦数据库|JPA】后端数据库国产化迁移记录

项目背景 经典的springbootjpa&#xff0c;java1.8数据库MySQL需要迁移到国产化数据库达梦上 开发环境安装 最简单的方式&#xff1a; 官方网站下载安装时选择“典型安装”即可 Linux安装 国产化一律上docer不要犹豫 下载三方提供的docker镜像按页面文档启动即可同上下载官…

ubuntu22默认安装firefox使用snap安装还老打不开解决办法

终极解决方案&#xff08;100% 避免 Snap 版 Firefox&#xff09; 步骤 1&#xff1a;彻底移除 Snap 版 Firefox bash sudo snap remove --purge firefox 步骤 2&#xff1a;添加 Mozilla 官方 PPA&#xff08;提供 .deb 版 Firefox&#xff09; bash sudo add-apt-repository …

MyBatis02-mybatis-config.xml配置文件讲解

mybatis-config.xml 是 MyBatis 的核心配置文件&#xff0c;用于配置整个 MyBatis 框架的全局行为&#xff0c;比如环境&#xff08;数据源&#xff09;、事务、类型别名、插件、Mapper 映射等。示例&#xff1a;<?xml version"1.0" encoding"UTF-8" ?…

合上电脑不关机

在Debian 系统上&#xff0c;如何实现合上电脑不关机的效果&#xff1f; 可以修改配置文件&#xff1a; sudo vim /etc/systemd/logind.conf1.找到 HandleLidSwitch &#xff0c;将其值改为 ignore &#xff08;处理盖子开关为忽略&#xff09; 2.将 LidSwitchIgnoreInhibited …

服务器深夜告警?可能是攻击前兆!

凌晨三点&#xff0c;刺耳的告警铃声把你从梦中惊醒&#xff1a;服务器CPU 100%&#xff0c;内存耗尽&#xff01;你手忙脚乱地登录服务器&#xff0c;发现某个进程疯狂占用资源。是程序Bug&#xff1f;还是业务突增&#xff1f;排查半天&#xff0c;最后在角落的日志里发现蛛丝…