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

两数之和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;
    }
}

在这里插入图片描述

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

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

相关文章

天眼销:超有用的企业获客工具

天眼销是资深数据团队开发的一个客户资源查询平台&#xff0c;可以通过多重筛选&#xff1a;企业名称/信用代码&#xff0c;所在地区&#xff0c;行业&#xff0c;注册资本&#xff0c;年限&#xff0c;是否在营/有电话/邮箱等。 天眼销和某查查有什么区别&#xff1f; 天*查/…

鸿蒙(HarmonyOS)应用开发——应用程序入口UIAbility

概述 UIAbility是一种包含用户界面的应用组件&#xff0c;主要用于和用户进行交互 UIAbility是系统调度的单元&#xff0c;为应用提供窗口在其中绘制界面 应用程序的几种交互界面形式 点击桌面图标进入应用 一个应用拉起另一个应用 最近任务列表切回应用 每一个UI Abili…

含压缩空气储能的零排放综合能源系统优化调度程序代码!

本程序参考SCI期刊论文《Optimal dispatch of zero-carbon-emission micro Energy Internet integrated with non-supplementary fired compressed air energy storage system》&#xff0c;程序中有详细的热网模型&#xff0c;温度控制模块&#xff0c;压缩机模块&#xff0c;…

二、shell编程快速入门

目录 1、入门示例 2、解释器 3、shell脚本执行方式 3.1 方式一&#xff1a;sh执行脚本 3.2 方式二&#xff1a;工作目录执行 3.3 方式三&#xff1a;绝对路径执行 ​​​​​​​4、shell的数据类型 4.1 字符串 4.2 整数型 1、入门示例 以下所有操作都在/export/shel…

Web应用渗透测试完全指南(二)

&#x1f4e2;专注于分享软件测试干货内容&#xff0c;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; 如有错误敬请指正&#xff01;&#x1f4e2;交流讨论&#xff1a;欢迎加入我们一起学习&#xff01;&#x1f4e2;资源分享&#xff1a;耗时200小时精选的「软件测试」资…

【开源】基于JAVA的天然气工程运维系统

项目编号&#xff1a; S 022 &#xff0c;文末获取源码。 \color{red}{项目编号&#xff1a;S022&#xff0c;文末获取源码。} 项目编号&#xff1a;S022&#xff0c;文末获取源码。 目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 系统角色分类2.2 核心功能2.2.1 流程…

lxd提权

lxd/lxc提权 漏洞介绍 lxd是一个root进程&#xff0c;它可以负责执行任意用户的lxd&#xff0c;unix套接字写入访问操作。而且在一些情况下&#xff0c;lxd不会调用它的用户权限进行检查和匹配 原理可以理解为用用户创建一个容器&#xff0c;再用容器挂载宿主机磁盘&#xf…

数据库安全运维系统厂家在深圳的有哪些?咨询电话多少?

IT小伙伴都知道&#xff0c;数据库安全运维至关重要&#xff0c;因为随着信息技术的不断发展&#xff0c;数据库已经成为企业存储、管理和处理数据的关键平台&#xff0c;数据库承载着企业不少数据资产。因此使用数据库安全运维系统是必要的。那你知道数据库安全运维系统厂家在…

Vue3 刷新后,pinia存储的数据丢失怎么解决

这个问题有两种解决办法&#xff1a; 一是使用pinia的持久化存储一是使用vue的依赖注入 刷新后&#xff0c;通过pinia存储的vue store数据丢失&#xff0c;实际上是因为Vue原组件卸载、新组件重新挂载导致的&#xff0c;vue store是挂载在组件上的&#xff0c;当刷新导致组件…

数据库的多表查询(MYSQL)表表联立

根据以上三张表格&#xff0c;对三张表格进行不同的联立&#xff0c;查询并显示符合条件的内容。 1. 查出至少有一个员工的部门。显示部门编号、部门名称、部门位置、部门人数。 mysql> SELECT d.deptno AS 部门编号, d.dname as 部门名称, d.loc as 部门位置, COUNT(e.emp…

uniapp2023年微信小程序头像+昵称分别获取

1、DOM <view class"m-user"><view class"user-info"><!--头像 GO--><button class"avatar avatar-wrapper" open-type"chooseAvatar" chooseavatar"onChooseAvatar"slot"right"><im…

2023-11-30 AIGC-让图片动起来的主流 AI 工具

摘要&#xff1a; 2023-11-30 AIGC-让图片动起来的主流 AI 工具 让图片动起来的主流 AI 工具 一、数字人播报 1、HeyGen 2、D-ID 3、SadTalker 二、图片生成视频 1、Runway Gen-2 2、Pika Labs 3、Genmo 三、伪3D动态效果 1、LeiaPix 2、剪映手机版 四、角色动画 Animated …

Sectigo代码签名证书——最优惠的解决方案

在软件开发领域&#xff0c;代码签名证书是确保软件安全和完整性的重要工具。它为开发者提供了一种验证其软件来源和内容的方式&#xff0c;同时向用户传递了信任和可靠性的信息。然而&#xff0c;高昂的代码签名证书费用一直是许多开发者面临的挑战之一。而Sectigo代码签名证书…

【漏洞复现】万户协同办公平台ezoffice SendFileCheckTemplateEdit.jsp接口存在SQL注入漏洞 附POC

漏洞描述 万户ezOFFICE协同管理平台是一个综合信息基础应用平台。 万户协同办公平台ezoffice SendFileCheckTemplateEdit.jsp接口存在SQL注入漏洞。 免责声明 技术文章仅供参考,任何个人和组织使用网络应当遵守宪法法律,遵守公共秩序,尊重社会公德,不得利用网络从事危害…

智能客服核心技术——预测会话与答案生成

1.信息检索 2. 句型模板匹配标准问题生成答案 3.根据知识图谱推理得到答案

【目标检测】进行实时检测计数时,在摄像头窗口显示实时计数个数

这里我是用我本地训练的基于yolov8环境的竹签计数模型&#xff0c;在打开摄像头窗口增加了实时计数显示的代码&#xff0c;可以直接运行&#xff0c;大家可以根据此代码进行修改&#xff0c;其底层原理时将检测出来的目标的个数显示了出来。 该项目链接&#xff1a;【目标检测…

区分(GIOU、DIOU、CIOU)(正则化、归一化、标准化)

一、IOU IoU 的全称为交并比&#xff08;Intersection over Union&#xff09;。IoU 计算的是 “预测的边框” 和 “真实的边框” 的交集和并集的比值。 1.GIOU&#xff1a;预测框&#xff08;蓝框&#xff09;和真实框&#xff08;绿框&#xff09;的最小外接矩形C。来获取预…

Pycharm使用远程服务器运行本地python文件

一、连接远程服务器 路径&#xff1a;Tools → Deployment → Configuration → SFTP → 取名 填写配置信息 二、配置python解释器 三、运行python文件

面试:SpringMVC问题

文章目录 SpringMVC运行流程MVC的概念与请求在MVC中的执行路径&#xff0c;ResponsBody注解的用途SpringMVC启动流程SpringMVC的拦截器和过滤器有什么区别&#xff1f;执行顺序&#xff1f;Spring和SpringMVC为什么需要父子容器&#xff1f; SpringMVC运行流程 • 客户端&#…

cddd 安装指南(pip install cddd)

pip install cddd 这个命令可能会报错&#xff0c;因为要求是TensorFlow1.10.0 TensorFlow1.10.0对应的Python版本是3.6&#xff0c;所以如果你的Python版本是3.6以上是不行的.....