两数之和[中等]

一、题目

给你一个下标从1开始的整数数组numbers,该数组已按非递减顺序排列,请你从数组中找出满足相加之和等于目标数target的两个数。如果设这两个数分别是numbers[index1]numbers[index2],则1 <= index1 < index2 <= numbers.length。以长度为2的整数数组[index1, index2]的形式返回这两个整数的下标index1index2。你可以假设每个输入 只对应唯一的答案 ,而且你不可以重复使用相同的元素。你所设计的解决方案必须只使用常量级的额外空间。

示例 1:
输入:numbers = [2,7,11,15], target = 9
输出:[1,2]
解释:27之和等于目标数9。因此index1 = 1, index2 = 2。返回[1, 2]

示例 2:
输入:numbers = [2,3,4], target = 6
输出:[1,3]
解释:24之和等于目标数6。因此index1 = 1, index2 = 3。返回[1, 3]

示例 3:
输入:numbers = [-1,0], target = -1
输出:[1,2]
解释:-10之和等于目标数-1。因此index1 = 1, index2 = 2。返回[1, 2]

2 <= numbers.length <= 3 * 104
-1000 <= numbers[i] <= 1000
numbers非递减顺序排列
-1000 <= target <= 1000
仅存在一个有效答案

二、代码

【1】使用二分查找法: 通过target - numbers[i]得到目标值,然后根据二分查找目标值,二分查找小于目标值,取右边进行递归,否则取左边的数据进行递归。

class Solution {
    public int[] twoSum(int[] numbers, int target) {
        // 通过 target - numbers[i] 得到目标值,然后根据二分查找目标值,二分查找小于目标值,取右边进行递归,否则取左边的数据进行递归。
        int left = 0, right = 0, val = 0, mid = 0;
        for (int i = 0; i < numbers.length; i++) {
            left = i + 1;
            right = numbers.length - 1;
            // 需要查找的值
            val = target - numbers[i];
            // 中间数据
            while(left <= right) {
                mid = (right - left) / 2 + left;
                // 循环退出条件
                if (numbers[mid] == val) {
                    return new int[] {i + 1 , mid + 1};
                }
                // 如果中间值大于目标值,二分查找左边数组,否则查找右边数组
                if (numbers[mid] > val) {
                    right = mid;
                } else {
                    left = mid + 1;
                }
            }
        }
        return new int[]{-1,-1};
    }
}

时间复杂度: O(nlog⁡n)其中n是数组的长度。需要遍历数组一次确定第一个数,时间复杂度是O(n),寻找第二个数使用二分查找,时间复杂度是O(log⁡n),因此总时间复杂度是O(nlog⁡n)
空间复杂度: O(1)

【2】双指针: 初始时两个指针分别指向第一个元素位置和最后一个元素的位置。每次计算两个指针指向的两个元素之和,并和目标值比较。如果两个元素之和等于目标值,则发现了唯一解。如果两个元素之和小于目标值,则将左侧指针右移一位。如果两个元素之和大于目标值,则将右侧指针左移一位。移动指针之后,重复上述操作,直到找到答案。

class Solution {
    public int[] twoSum(int[] numbers, int target) {
        // 思想:将左右指针累加后,如果sum > target 右指针-1,sum < target 左指针 + 1
        int left = 0 , right = numbers.length - 1;
        while (left < right) {
            int sum = numbers[left] + numbers[right];
            if (sum == target) {
                return new int[]{left + 1 , right + 1};
            } else if (sum > target){
                --right;
            }else {
                ++left;
            }
        }
        return new int[]{-1,-1};
    }
}

时间复杂度: O(n)其中n是数组的长度。两个指针移动的总次数最多为n次。
空间复杂度: O(1)

【3】O(n)的双指针解法的本质原理: 很多人做这个题目想不到正确的O(n)解法,即使看了答案理解了,下次再做的时候还是会忘记。要想真正理解这道题,就要明白解法背后的道理。这样不仅可以记住这道题,还能举一反三解决类似的题目。

很多题解只给出了双指针解法的代码,但没有说明解法的正确性。为什么双指针往中间移动时,不会漏掉某些情况呢?要解答这个问题,我们要从 缩减搜索空间 的角度思考这个解法。下面我将以文字和图片两种方式进行讲解。

首先放上参考答案:

public int[] twoSum(int[] numbers, int target) {
    int i = 0;
    int j = numbers.length - 1;
    while (i < j) {
        int sum = numbers[i] + numbers[j];
        if (sum < target) {
            i++;
        } else if (sum > target) {
            j--;
        } else {
            return new int[]{i+1, j+1};
        }
    }
    return new int[]{-1, -1};
}

需要注意的是,虽然本题叫做Two Sum II,但解法和Two Sum完全不同。

图解双指针解法的原理: 在这道题中,我们要寻找的是符合条件的一对下标(i,j),它们需要满足的约束条件是:
【1】ij都是合法的下标,即0≤i<n,0≤j<n
【2】i<j(题目要求);
而我们希望从中找到满足A[i] + A[j] == target的下标(i,j)。以n=8为例,这时候全部的搜索空间是:

由于ij的约束条件的限制,搜索空间是白色的倒三角部分。可以看到,搜索空间的大小是O(n^2)数量级的。如果用暴力解法求解,一次只检查一个单元格,那么时间复杂度一定是O(n^2)。要想得到O(n)的解法,我们就需要能够一次排除多个单元格。那么我们来看看,本题的双指针解法是如何削减搜索空间的:

一开始,我们检查右上方单元格(0,7),即计算A[0] + A[7],与target进行比较。如果不相等的话,则要么大于target,要么小于target

假设此时A[0] + A[7]小于target。这时候,我们应该去找和更大的两个数。由于A[7]已经是最大的数了,其他的数跟A[0]相加,和只会更小。也就是说A[0] + A[6]A[0] + A[5]、……、A[0] + A[1]也都小于target,这些都是不合要求的解,可以一次排除。这相当于i=0的情况全部被排除。对应用双指针解法的代码,就是i++,对应于搜索空间,就是削减了一行的搜索空间,如下图所示。

排除掉了搜索空间中的一行之后,我们再看剩余的搜索空间,仍然是倒三角形状。我们检查右上方的单元格(1,7),计算A[1] + A[7]target进行比较。

假设此时A[0] + A[7]大于target。这时候,我们应该去找 和更小的两个数。由于A[1]已经是当前搜索空间最小的数了,其他的数跟A[7]相加的话,和只会更大。也就是说A[1] + A[7]A[2] + A[7]、……、A[6] + A[7]也都大于target,这些都是不合要求的解,可以一次排除。这相当于j=0的情况全部被排除。对应用双指针解法的代码,就是j++,对应于搜索空间,就是削减了一列的搜索空间,如下图所示。

可以看到,无论A[i] + A[j]的结果是大了还是小了,我们都可以排除掉一行或者一列的搜索空间。经过n步以后,就能排除所有的搜索空间,检查完所有的可能性。搜索空间的减小过程如下面动图所示:

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

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

相关文章

Oracle报错:ORA-12541:TNS:无监听程序 (很大概率是listener.log满了,4G就无法写入了)

目录标题 一、前提二、查看listener.log三、如果是listener.log满了&#xff0c;内存达到4G,可以使用以下方法解决。&#xff08;一&#xff09;停用服务&#xff08;二&#xff09;将满了的listener.log日志删除或者改名&#xff0c;然后新建一个一样的listener.log文件&#…

助力工业生产质检,基于YOLOv7【tiny/l/x】不同系列参数模型开发构建生产制造场景下布匹瑕疵缺陷检测识别分析系统

纯粹的工业制造没有办法有长久的发展过程&#xff0c;转制造为全流程全场景的生产智造才是未来最具竞争力的生产场景&#xff0c;在前面的开发实践中我们已经涉足工业生产场景下进行了很多实地的项目开发&#xff0c;如&#xff1a;PCB电路板缺陷检测、焊接缺陷检测、螺母螺钉缺…

面试题-【消息队列】

消息队列 问题1 如何进行消息队列的技术选型优点解耦 &#xff08;pub/sub模型&#xff09;异步&#xff08;异步接口性能优化&#xff09;削峰 使用消息队列的缺点几种消息队列的特性 问题2 引入消息队列之后该如何保证其高可用性RabbitMQ的高可用kafka高可用 问题3 在消息队列…

07 队列

目录 1.队列 2.实现 3.OJ题 1. 队列 只允许在一段进行插入数据操作&#xff0c;在另一端进行数据删除操作的特殊线性表&#xff0c;队列具有先进先出FIFO&#xff08;First In Firtst Out&#xff09;&#xff0c;插入操作的叫队尾&#xff0c;删除操作的叫队头 2. 实现 队列…

前端echarts图形报表常见的样式配置

文章目录 &#x1f412;个人主页&#x1f3c5;Vue项目常用组件模板仓库&#x1f4d6;前言&#xff1a;&#x1f415;1.深色主题&#x1f415;2.改变柱状图颜色&#x1f415;突然发现去问ai&#xff0c;更容易理解&#xff0c;那就不总结了 &#x1f412;个人主页 &#x1f3c5;…

太阳光模拟器汽车耐老化太阳跟踪聚光户外加速老化试验

1 范围 1.1 本标准适用于以太阳为光源的菲涅耳反射系统来进行汽车外饰材料的加速老化试验。 1.2 本标准规定的设备和方法可用于确定曝露于日光、热和潮湿下的汽车材料的相对耐老化性&#xff0c; 前提是假设试验期间发生的对材料加速老化速率起决定性作用的物理、化学变化机理…

缓存高并发问题

Redis 做缓存虽减轻了 DBMS 的压力&#xff0c;减小了 RT&#xff0c;但在高并发情况下也是可能会出现各种问题的。 缓存穿透 当用户访问的数据既不在缓存也不在数据库中时&#xff0c;就会导致每个用户查询都会“穿透”缓存“直抵”数据库。这种情况就称为缓存穿透。当高度发…

什么是网络?

你是一台电脑&#xff0c;你的名字叫 A 很久很久之前&#xff0c;你不与任何其他电脑相连接&#xff0c;孤苦伶仃。 直到有一天&#xff0c;你希望与另一台电脑 B 建立通信&#xff0c;于是你们各开了一个网口&#xff0c;用一根网线连接了起来。 用一根网线连接起来怎么就能&…

Oracle BIEE 示例(一)数据透视表2

1 背景 版本:BIEE 12C 视图:数据透视表 实现内容(顺序与具体内容不一致): 2 空列显示(方法一) 2.1 问题 列为空时,标题栏不显示信息。 2.2 期望 即使数据为空,也要显示列名。 2.3 官方资料 2.3.1 操作步骤 2.3.1.1 要在分析级别关闭空值隐藏,请执行以下操作…

不停机迁移,TDengine 在 3D 打印技术中的“焕新”之路

小T导读&#xff1a;自 2021 年我们正式使用 TDengine 至今已接近三年&#xff0c;现在 TDengine 已经成熟应用于我们多个项目当中&#xff0c;凭借着强大的读写存储能力&#xff0c;为我司多项业务的核心数据保驾护航。近期我们团队刚好完成 TDengine 2.x 到 3.x 的数据迁移&a…

基于EfficientNet(B0-B7)全系列不同参数量级模型开发构建中草药图像识别分析系统,实验量化对比不同模型性能

EfficientNet系列的模型在我们前面开发识别类项目或者是检测类项目都是比较少去使用的&#xff0c;一方面是技术本身迭代发展的速度是比较快的&#xff0c;可能新的东西还没学习更新的东西就出来了&#xff0c;另一方面是EfficientNet本身实际业务使用度并不高&#xff0c;可能…

C++ STL之deque的理解及使用

文章目录 1. 介绍2. 实现原理&#xff08;简单理解&#xff09;3. deque的优缺点4. deque类的使用4.1 deque类对象的构造函数4.2 deque类对象的容量操作4.3 deque类对象的修改操作4.4 deque类对象的访问及遍历操作 1. 介绍 deque(双端队列)&#xff1a;是一种双开口的连续空间的…

UCAS-AOD遥感旋转目标检测数据集——基于YOLOv8obb,map50已达96.7%

1.UCAS-AOD简介 1.1数据说明 遥感图像&#xff0c;又名高分辨率遥感图像。遥感图像的分类依据是根据成像的介质不同来进行分类的。UCAS-AOD (Zhu et al.&#xff0c;2015)用于飞机和汽车的检测&#xff0c;包含飞机与汽车2类样本以及一定数量的反例样本&#xff08;背景&…

第4章 面向对象(下)

4.1 继承 4.1.1 继承的概念 在现实生活中&#xff0c;继承一般指的是子女继承父辈的财产。在程序中&#xff0c;继承描述的是事物之间的所属关系&#xff0c;通过继承可以使多种事物之间形成一种关系体系。例如&#xff0c;猫和狗都属于动物&#xff0c;程序中便可以描述为猫…

2017年认证杯SPSSPRO杯数学建模C题(第二阶段)移动端考研产品的春天真的到来了吗全过程文档及程序

2017年认证杯SPSSPRO杯数学建模 C题 移动端考研产品的春天真的到来了吗 原题再现&#xff1a; 2017 年的全国硕士研究生招生考试共有 201 万人报名参加&#xff0c;比去年增加了 24 万名考生&#xff0c;增加 13.56%。看起来新一轮的考研热潮即将到来&#xff0c;而考研教学和…

JAVA工程中引用本地jar的3种常用方式,你用过哪种?

文章目录 前言1. 第1种方式2. 第2种方式3. 第3种方式 前言 实际项目过程中咱们经常会碰到需要本地引用jar包到java工程中的场景&#xff0c;本文就介绍一下遇到此场景时如何在IDEA中导入本地jar包到工程中的3种方式&#xff0c;简单却很常用。 1. 第1种方式 IDEA -> File …

MySQL函数—流程函数

MySQL函数—流程函数&#xff1a;用于实现条件筛选&#xff0c;从而题搞语句的效率。 MySQL函数—流程函数 函数功能IF(value,t,f)如果value为true&#xff0c;则返回t&#xff0c;否则返回fIFNULL(value1,value2)如果value1不为空&#xff0c;返回value1&#xff0c;否则返回v…

单点登陆(SSO)基于CAS实现前后端分离的SSO系统开发「IDP发起」

关于其他前端常见登录实现单点登录方案&#xff0c;请见「前端常见登录实现方案 单点登录方案 」 前沿 单点登录&#xff08;SSO&#xff09;&#xff0c;英文全称为 Single Sign On。 SSO 是指在多个应用系统中&#xff0c;用户只需要登录一次&#xff0c;就可以访问所有相互…

分布变化下的Test-Time adaption 综述

论文 https://arxiv.org/abs/2303.15361 代码 https://github.com/tim-learn/awesome-test-time-adaptation &#xff08;其实这是相关领域代码和论文合集之类的东西&#xff09; Abstract 机器学习方法努力在训练过程中获得一个鲁棒模型&#xff0c;即使在分布变化的情况下…

RDMA vs InfiniBand 网卡接口如何区分?

(该架构图来源于参考文献) 高性能计算网络&#xff0c;RoCE vs. InfiniBand该怎么选&#xff1f; 新 RoCEv2 标准可实现 RDMA 路由在第三层以太网网络中的传输。RoCEv2 规范将用以太网链路层上的 IP 报头和 UDP 报头替代 InfiniBand 网络层。这样&#xff0c;就可以在基于 IP…