【刷题笔记】两数之和II_二分法||二分查找||边界||符合思维方式

news2024/11/18 11:25:08

两数之和II_二分法||二分查找

1 题目描述

https://leetcode.cn/problems/two-sum-ii-input-array-is-sorted/

给你一个下标从 1 开始的整数数组 numbers ,该数组已按 非递减顺序排列 ,请你从数组中找出满足相加之和等于目标数 target 的两个数。如果设这两个数分别是 numbers[index1] 和 numbers[index2] ,则 1 <= index1 < index2 <= numbers.length 。

以长度为 2 的整数数组 [index1, index2] 的形式返回这两个整数的下标 index1 和 index2。

你可以假设每个输入 只对应唯一的答案 ,而且你 不可以 重复使用相同的元素。

你所设计的解决方案必须只使用常量级的额外空间。

2 思路

看到这一个题目,数组和有序,第一个想法就是二分查找(当然看了官方题解之后,发现还有双指针做法)。

做法很简单,给定了target,我们对数组numbers进行遍历,对于numbers的每一个元素,重新在数组中寻找和该元素之和为target的元素。如果找到的元素和遍历到的元素重复了,那么继续遍历,然后再寻找;如果不重复,那就将两个元素的位置都加一获得真实位置,然后返回。

public int[] twoSum(int[] numbers, int target) {
    int pre = 0;
    for (int i = 0; i < numbers.length; i++) {
        if (i > 0 && numbers[i] == pre) // 如果遇到重复元素,就别浪费这个时间了。
            continue;
        int other_tar = target - numbers[i];
        int pos = biSearch(numbers, other_tar);
        if (pos >= 0) {
            if (pos == i) continue; // 防止找到自己头上, 比如参数是([0,4,5], 8), 就有会找到两个4。
            return new int[]{i + 1, pos + 1}; // 索引+1变成真实位置。
        }
        pre = numbers[i]; // 记录前一个值
    }
    return null;
}

那么最重要的问题是,如何进行二分查找?

我们就事论事,如何分析这个题目呢?

相似题目可以看我的这篇文章【刷题笔记】H指数||数组||二分查找的变体

题目本身已经强调了,非递减顺序,也就是说,数组中可能会存在一些相等的元素。举个例子

towSum([1,3,4,4], 8)

当我们遍历到第一个4的时候,为了让和为8,我们需要在数组中找到另一个为4的元素。如果二分是普通的二分查找:

 if (numbers[mid_index] == num) {return mid_index;}
else if (numbers[mid_index] < num) l = real_mid + 1;
else r = real_mid - 1;

我们极有可能在碰到第一个4的时候,就返回了其坐标,而这和我们题目中的要求是冲突的。那么我们就会接着遍历numbers数组,碰到第二个4,然后继续进行二分查找,找到了第一个4,返回位置,发现这个组合是符合要求的,最后我们返回的两个位置是[4,3]。我替你们试过了,就算你返回的的两个坐标是对的,还是要保证坐标是从小到大的,反过来也会报错。

怎么解决这种存在多个相等元素的问题呢?我的解决方法是分成两步:

  1. 遍历numbers的时候,遇到重复元素,只遍历第一个。上面代码中的:
 if (i > 0 && numbers[i] == pre) // 如果遇到重复元素,就别浪费这个时间了。
        continue;

就是解决这个问题的。

  1. 在二分查找的时候,只找重复元素的最后一个。这样假设碰到多个重复元素,而且恰好两个重复元素之和等于我们target的情况,最终返回的坐标一定是递增顺序的。

那么我们接下来考虑,怎么在二分查找的时候,找到最后一个符合条件的元素呢?

对于二分查找有两个最重要的问题:如何计算mid如何跳转left和right

我在【刷题笔记】H指数||数组||二分查找的变体这篇博客中提出了一个思考的范式:

这个两个问题本身是一个问题,只要我们确定了如何跳转left和right,就能确定如何计算mid。

(这只是我的一点浅薄的看法,大家要根据自己的刷题情况实时更新自己的理念,我考虑出来的东西也不一定具有普适性,我只是刚刷题的菜鸡。)

怎么理解上面这句话呢?比如我们这道题,我们确定了目标是寻找满足条件(即等于other_tar)的最后一个元素,也就是右边界,一个很符合直觉的想法就是,left指针是不断右移的,要找右边界,left指针最合适。

在这里插入图片描述

那么我们看到,当遇到这种情况的时候,numbers[mid]==other_tarmid可能是右边界吗?可能。那mid右边的位置还可能是右边界吗?不一定。如果left移动到mid右边,会不会错过右边界?可能会。所以,为了避免这种跳过边界的可能性,当mid满足条件的时候,我们不是直接返回mid,而是让left转移到mid的位置上,通过left不断寻找右边界。而当numbers[mid] < other_tar的时候,让left++;当numbers[mid] > other_tar的时候,right--,这些都是常规操作了。

也就是说,当查找边界的时候,我们的left指针最后可能会指向边界。为什么是可能?因为数组里可能没有我们要找的值,这时候left一路向右,势不可挡,直接窜到了数组的边界之外,也是可能的。

现在我们解决了第二个问题,如何跳转left和right。那么我们回过头解决第一个问题,如何计算mid

第二个问题,我们可以直接考虑在只剩下两个元素的时候(即只剩下left和right的时候),该如何计算mid

在这里插入图片描述

众所周知,当我们在只剩下两个元素的时候,mid元素要么是(left + right) / 2,放在left上,要么是(left + right) / 2 + 1,放在right上。

我们已经确定了,left在某些条件下是可能直接跳转到mid上的, 如果让mid=left,下一步如果left需要跳转,left=mid,然后mid=left。。。。。。无限循环。

所以,为了避免死循环,当只有偶数个元素的时候,我们需要让mid跳转到中间两个元素的后一个元素上。所以我说,当我们确定了leftright的跳转问题之后,如何计算mid的问题就迎刃而解。

当然,传统的二分法,left跳转到mid+1,right跳转到mid-1,不会出现死循环的。咱们现在讨论的是边界问题,所以有些特殊。

我还是那句话,我花生豆大的小脑仁接受不了太多弯弯绕绕,面对二分问题的时候,left和right的取值,我倾向于直接使用真实位置,即从1开始的位置。

public int biSearch(int[] numbers, int num) {
    int l = 1, r = numbers.length;
    ...
}

这样有一个好处就是符合我们的思维直觉,本身二分法的变化就多,能简化思考的地方就简化思考。

那么如果l~r范围内(闭区间)的元素个数是奇数个,(l+r)/2就是中间数的真实位置,如果是偶数个,我们就设为(l+r)/2 + 1。这个方法虽然笨,但是符合我们的思维直觉。

 public int biSearch(int[] numbers, int num) {
    int l = 1, r = numbers.length;
    while (l < r) { // 一旦两个指针重合,遍历结束,要么是找到了,要么是l跳到边界外了。
        int real_mid = (l + r) / 2 + ((l - r + 1) % 2 == 0 ? 1 : 0);
        int mid_index = real_mid - 1;
        if (numbers[mid_index] == num) l = real_mid;
        else if (numbers[mid_index] < num) l = real_mid + 1;
        else r = real_mid - 1;
    }
    if (l <= numbers.length && numbers[l-1] == num) return l-1;
    else return -1;
}

在这里,所有的指针我都使用了真实值,只有在用到numbers[mid_index]的时候,才用的索引。当然,这些都是我自己的习惯。

因为我们是用left指针来判断边界,left在跳转的过程中,我们计算mid的时候是选择了靠右型,如果left跳转到mid+1的位置,可能跳出边界。

所以我们最后的判断条件是if (l <= numbers.length && numbers[l-1] == num) return l-1;
返回索引。

3 代码

class Solution {
    public int[] twoSum(int[] numbers, int target) {
        int pre = 0;
        for (int i = 0; i < numbers.length; i++) {
            if (i > 0 && numbers[i] == pre)
                continue;
            int other_tar = target - numbers[i];
            int pos = biSearch(numbers, other_tar);
            if (pos >= 0) {
                if (pos == i) continue;
                return new int[]{i + 1, pos + 1};
            }
            pre = numbers[i];
        }
        return null;
    }

    public int biSearch(int[] numbers, int num) {
        int l = 1, r = numbers.length;
        while (l < r) {
            int real_mid = (l + r) / 2 + ((l - r + 1) % 2 == 0 ? 1 : 0);
            int mid_index = real_mid - 1;
            if (numbers[mid_index] == num) l = real_mid;
            else if (numbers[mid_index] < num) l = real_mid + 1;
            else r = real_mid - 1;
        }
        if (l <= numbers.length && numbers[l-1] == num) return l-1;
        else return -1;
    }
}

在这里插入图片描述

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

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

相关文章

相机标定张正友、opencv和halcon对比(1)

本文将从基本标定开始&#xff0c;结合实际工作经验&#xff0c;分析张正友、opencv和halcon三者相机标定的深层原理与不同之处&#xff0c;内容比较多&#xff0c;如果出现错误请指正。 相机光学模型 我们使用的镜头都是由多组镜片组成&#xff0c;它实际上是一种厚透镜模型…

企业微信应用文本消息

应用支持推送文本、图片、视频、文件、图文等类型&#xff0c;本篇主要实现发送应用文本消息。 获取企业凭证 发送应用消息首先需要获取调用凭证access_token&#xff0c;此处的凭证为企业凭证&#xff0c;可通过企业授权安装时返回的授权信息中获取access_token&#xff1b;之…

nexus 制品库管理

目录 一、nexus 介绍 二、nexus 支持的仓库 三、nexus 部署 四、nexus 数据备份 五、创建一个内网yum源 六、创建一个代理yum仓库 七、jenkins 使用 nexus插件 7.1 jenkins 安装插件 7.2 配置 maven 工程 7.3 查看构建和上传 一、nexus 介绍 Nexus 是一个强大的仓库管…

【重点文章】将Java程序打包成exe文件,无Java环境也可以运行(解决各种疑难杂症)

文章目录 一、将Java程序打成jar包二、将Jar打成exe三、加壳改造成安装包 编译器为IDEA 一、将Java程序打成jar包 2. 3. 你打的包一般会出现在根目录下面的out文件夹下面  当然你也可以用maven的package功能打包&#xff0c;效果是一样的   二、将Jar打成exe 使用工具e…

AD4209 双路LED、集成充电一体 可做双色温芯片调节 适用于台灯、手电筒

AD4209 是一款双路输出的可调色温的 台灯控制芯片&#xff0c;集成了锂电池充电管理模块、 LED 功能控制模块和保护模块&#xff0c;关机待机电流仅 3uA。 AD4209 为双路输出控制&#xff0c;每一路最大输出 电 流 为 1.2A 开 关 模 式 &#xff1a;CH1→CH2→CH1CH2→OFF。…

Qt MVC示例 simpletreemodel 树模型

Qt MVC示例 simpletreemodel 树模型 从文本中读取树模型数据&#xff0c;缩进代表子项 TreeItem 表示一行字符串数据 treeitem.h #ifndef TREEITEM_H #define TREEITEM_H#include <QList> #include <QVariant>//! [0] class TreeItem { public:explicit Tree…

EasyRecovery2024免费强大的电脑文件恢复软件

文件的保存一直是我们对电子化数据较为关注的一个环节&#xff0c;毕竟这是一份文件或数据的根本所在。但是在计算机操作中难免会出现有文件丢失或文件误删的现象&#xff0c;给我们的日常和工作带来不必要的困扰。 今天小编就要和大家分享一款功能超级强大的文件恢复软件&…

DCGAN 使用指南:将卷积神经网络和对抗网络结合,适用于生成小尺寸的图像

DCGAN 使用指南&#xff1a;将卷积神经网络和对抗网络结合 网络结构细节设计 论文地址&#xff1a;https://arxiv.org/abs/1511.06434 项目代码&#xff1a;https://github.com/tensorlayer/DCGAN.git DCGAN 适用于生成小尺寸的图像&#xff0c;并且具有简单易用的优势 Styl…

使用 Webshell 访问 SQL Server 主机并利用 SSRS

本文将指导您使用RDS SQL Server实例的主机账号登录和管理SQL Server Reporting Services&#xff08;SSRS&#xff09;数据库。 背景信息 RDS SQL Server提供Webshell功能&#xff0c;用户可以通过Web界面登录RDS SQL Server实例的操作系统。通过Webshell&#xff0c;用户可…

中国毫米波雷达产业分析3——毫米波雷达市场分析(1~3)

一、总体市场 &#xff08;一&#xff09;总规模 近几年&#xff0c;得益于汽车智能化的高速发展与雷达芯片制作工艺的进步&#xff0c;国内毫米波雷达整体市场增速较快。根据初步测算&#xff0c;2022年中国毫米波雷达市场总规模达到86亿元&#xff0c;实现同比增长24.6%。 图…

加密挖矿、AI发展刺激算力需求激增!去中心化算力时代已来临!

2009年1月3日&#xff0c;中本聪在芬兰赫尔辛基的一个小型服务器上挖出了比特币的创世区块&#xff0c;并获得了50BTC的出块奖励。自加密货币诞生第一天起&#xff0c;算力一直在行业扮演非常重要的角色。行业对算力的真实需求&#xff0c;也极大推动了芯片厂商的发展&#xff…

安装vmware_esxi 超详细

安装vmware_esxi 超详细 </h2><div id"cnblogs_post_body" class"blogpost-body blogpost-body-html">esxi安装手册 1、esxi介绍 ESXI原生架构模式的虚拟化技术&#xff0c;是不需要宿主操作系统的&#xff0c;它自己本身就是操作系统。因此…

Maven 进阶学习指南---setting详解

前言 当我们在开发项目时&#xff0c;有时需要用到外部依赖组件&#xff0c;例如当我们需要 Json 序列化的时候需要用到 FastJson 组件&#xff0c;我们可以通过下载对应 jar 包加载到项目中。但当一个大的项目同时需要依赖各种各样的外部服务&#xff0c;就存在着配置繁琐、依…

Ubuntu22.04 server版本关闭DHCP,手动设置ip

在Ubuntu 22.04 中&#xff0c;网络配置已迁移到 Netplan&#xff0c;因此可以使用 Netplan 配置文件来手动设置 IP 地址并关闭 DHCP。 以下是在 Ubuntu 22.04 上手动设置 IP 地址并禁用 DHCP 的步骤&#xff1a; 打开终端&#xff0c;使用 root 权限或 sudo 执行以下命令&…

004:Direct 2D离屏渲染(Qt中实现)

简介&#xff1a; 用QT开发图像显示的小程序&#xff0c;需要一些标注工具&#xff0c;由于用的是opengl渲染&#xff0c;所以就在内存中进行绘制&#xff0c;然后纹理贴图贴出去&#xff0c;发现Qt绘制的效果太差&#xff0c;且速度一般&#xff0c;于是就想着用direct2d来绘制…

48、Flink DataStream API 编程指南(3)- 完整版

Flink 系列文章 1、Flink 部署、概念介绍、source、transformation、sink使用示例、四大基石介绍和示例等系列综合文章链接 13、Flink 的table api与sql的基本概念、通用api介绍及入门示例 14、Flink 的table api与sql之数据类型: 内置数据类型以及它们的属性 15、Flink 的ta…

【element-plus使用】el-select自定义样式、下拉框选项过长等问题解决

1、自定义样式 <template><el-select v-model"value" style"width: 150px"><el-option label"选项一" value"option1"></el-option><el-option label"选项二" value"option2"><…

C++ 抽象类和接口 详解

目录 0 引言1 抽象类2 接口2.1 Java与C接口的区别 &#x1f64b;‍♂️ 作者&#xff1a;海码007&#x1f4dc; 专栏&#xff1a;C专栏&#x1f4a5; 标题&#xff1a;C 抽象类和接口 详解❣️ 寄语&#xff1a;书到用时方恨少&#xff0c;事非经过不知难&#xff01;&#x1f…

springboot整合easy-es实现数据的增删改查

背景 目前公司的一个老项目&#xff0c;查询贼慢&#xff0c;需要想办法提升一下速度&#xff0c;于是就想到了ES&#xff0c;现在尝试一下将ES整合到项目中来提升检索效率。 ES是基于倒排索引实现的&#xff0c;倒排索引中一个表相当于一个索引&#xff0c;表中的每条记录都…

yolov1网络结构说明

文章目录 一. 网络结构二. 网络说明1. 网络的输入2. 网络的输出(1) 5 5表示:每个网格使用两个先验框进行预测。(2) “5”表示&#xff1a;每个先验框包含的预测信息的数量。(3) 20表示&#xff1a;20个分类预测值(4) 每个网格能预测几个目标&#xff1f; 一. 网络结构 论文下…