leetcode: Two Sum

news2024/12/30 3:38:30

leetcode: Two Sum

  • 1. 题目
    • 1.1 题目描述
  • 2. 解答
    • 2.1 baseline
    • 2.2 基于baseline的思考
    • 2.3 优化思路的实施
      • 2.3.1 C++中的hashmap
      • 2.3.2 实施
      • 2.3.3 再思考
      • 2.3.4 最终实施
  • 3. 总结

1. 题目

1.1 题目描述

Given an array of integers nums and an integer target, return indices of the two 
numbers such that they add up to target. You may assume that each input would have 
exactly one solution, and you may not use the same element twice.
You can return the answer in any order.

Example 1:

Input: nums = [2,7,11,15], target = 9
Output: [0,1]
Explanation: Because nums[0] + nums[1] == 9, we return [0, 1].

Example 2:

Input: nums = [3,2,4], target = 6
Output: [1,2]

Example 3:

Input: nums = [3,3], target = 6
Output: [0,1]

Constraints:

  • 2 <= nums.length <= 104
  • -109 <= nums[i] <= 109
  • -109 <= target <= 109
  • Only one valid answer exists.

2. 解答

2.1 baseline

  一个比较直观的想法是,罗列出所有的可能方案,然后找到和等于target的方案,返回即可。这里面蕴含的数学概念是:组合,从n个元素中取出2个元素; 用数学公式表示为 c n 2 c_{n}^{2} cn2
  在用代码表示 c n 2 c_{n}^2 cn2之前,可以先绘制一下示意图:

  根据该示意图不难写出对应的代码。

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        for(int i = 0; i < nums.size() - 1; ++i){
            for(int j = i + 1; j < nums.size(); ++j){
                if(target == nums[i] + nums[j]){
                    return {i, j};
                }
            }
        }
        return {-1, -1};
    }
};

2.2 基于baseline的思考

  baseline是一种“Brute Force”的思想,它的时间复杂度是 o ( n 2 ) o(n^2) o(n2),这个时间复杂度极大概率不是最优的。
  多层循环的常用优化点在于将循环解耦。例如将 o ( n 2 ) o(n^2) o(n2)-----> o ( n ) + o ( n ) o(n) + o(n) o(n)+o(n)。外层循环表示的含义是数组中的每一个元素都有机会作为候选答案。因此这层循环很难去除。
  来看内层循环:内层循环在做的事情是对于当前 n u m s [ i ] nums[i] nums[i], 通过遍历数组的方式,确认是否在其他元素中存在与之相加等于sum的元素;如果有找到答案。加粗的几个词语,是优化的关键:

“Brute Force”之所以效率低,是因为它在内循环中,试图以数组这一种数据结构,来解决查找问题。而数组的查找智能以遍历的方式进行,其查找的时间复杂度为n。

  而哈希表(hashmap)这种数据结构,可以做到查找问题以 o ( 1 ) o(1) o(1)的时间复杂度进行。因此在进行真正的解决方案之前,先要根据数组构建对应的hashmap,以这种辅助数据的方式,将两层for循环进行解耦,从而时间复杂度降低为 o ( n ) + o ( n ) o(n) + o(n) o(n)+o(n)

2.3 优化思路的实施

2.3.1 C++中的hashmap

   hashmap(哈希表)是一个概念,不同编程语言对其有自己的实现。c++将其实现为std::unordered_map形式,(这边需要强调,不是std::map,std::map是对read-black tree的实现,其插入元素和访问元素的时间复杂度是 l o g ( n ) log(n) log(n))。
  具体相关的代码有:

#include <unordered_map>  // 头文件
std::unordered_map<int, int> um;  // 定义一个unordered_map 对象
um.contains(k);  //判断k键值是否在um中,该时间复杂度是o(1) c++20才支持
um.find(k) != um.end();  //判断k键值是否在um中,该时间复杂度是o(1) c++11才支持
int index = um[k];  //获得k健值对应的value,该时间复杂度为o(1)

2.3.2 实施

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        std::unordered_map<int, int> um;
        for(int i = 0; i < nums.size(); ++i){
            um[nums[i]] = i;
        }
        for(int i = 0; i < nums.size(); ++i){
            int another = target - nums[i];
            if(um.find(another) != um.end() && um[another] != i)
                return {i, um[another]};
        }
        return {-1, -1};
    }
};

  这里要注意一下

um[another] != i

主要是对应题干中的you may not use the same element twice.

2.3.3 再思考

  但该方案提交leetcode之后,耗时仍旧位于第二峰值区域。说明有继续优化的空间。目前的方案时间复杂度为 o ( N ) + o ( n ) o(N) + o(n) o(N)+o(n), 大写字母N表示是构建哈希表的部分,必须要遍历掉所有的元素;小写o(n)则是大概率仅仅会遍历部分元素,在中途就会找到答案中途退出。因此 o ( N ) o(N) o(N)是当前的瓶颈。这里面细分, o ( N ) o(N) o(N)是存在冗余信息的:
  对于当前待查找对象nums[i], 我们只需知道在该元素之前是否有与之能够成功匹配的元素即可。因为若出现与nums[i]匹配的元素nums[ii]在i之后,则在处理元素nums[ii]的时候,nums[i]是作为ii之前,已经存在于hash表中的。这个时候nums[i]仍旧能够被翻出来。
  这样做的好处在于将 o ( N ) + o ( n ) o(N) + o(n) o(N)+o(n)转变为 o ( n ) + o ( n ) o(n) + o(n) o(n)+o(n)

2.3.4 最终实施

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        std::unordered_map<int, int> um;
        for(int i = 0; i < nums.size(); ++i){
            int another = target - nums[i];
            if(um.find(another) != um.end())
                return {i, um[another]};
            um[nums[i]] = i; 
        }
        return {-1, -1};
    }
};

  此时的解决方案,就可以位于第一峰值处。
在这里插入图片描述

3. 总结

  虽然"Brute Force"解法一般不是最优的,但快速的写出该解法作为baseline,是做进一步分析的前提。
  分析耗时的瓶颈所在:两层for循环导致的 o ( n 2 ) o(n^2) o(n2)时间复杂度。往往可以借助于“辅助数据结构”,解耦到"o(N) + o(N)"的方案。而hashmap作为可以获得常量级插入和访问的数据结构,是非常优质的,需要熟悉其用法。
  若想要进一步优化,“o(N) + o(n)” --> "o(n) + o(n)"是一种手段。因为此时的瓶颈在于o(N)。
  最后在大的框架代码写完之后,要思考题干中的特殊限制代表的含义,例如you may not use the same element twice. 思考自己的代码是否已经体现了其含义。

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

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

相关文章

Fluent Python 笔记 第 4 章 文本和字节序列

Python 3 明确区分了人类可读的文本字符串和原始的字节序列。隐式地把字节序列转换成 Unicode 文本已成过去。本章将要讨论 Unicode 字符串、二进制序列&#xff0c;以及在二者之间转 换时使用的编码。 没啥可看的&#xff0c;就一句话&#xff0c;一定不能依赖默认编码&#x…

DP优化 - 斜率优化

假设当前的 DP 方程为 fimin⁡0≤j<i{−K(i)X(j)Y(j)}F(i)f_i\min\limits_{0\leq j< i}\{-K(i)X(j)Y(j)\} F(i)fi​0≤j<imin​{−K(i)X(j)Y(j)}F(i) 或 fimax⁡0≤j<i{−K(i)X(j)Y(j)}F(i)f_i\max\limits_{0\leq j< i}\{-K(i)X(j)Y(j)\} F(i)fi​0≤j<im…

Node.js笔记-Express(基于Node.js的web开发框架)

目录 Express概述 Express安装 基本使用 创建服务器 编写请求接口 接收请求参数 获取路径参数(/login/2) 静态资源托管-express.static&#xff08;内置中间件&#xff09; 什么是静态资源托管&#xff1f; express.static() 应用举例 托管多个静态资源 挂载路径前缀…

车厢调度(train)(栈)

目录 题目描述 解题思路&#xff1a; 代码部分&#xff1a; 题目描述 有一个火车站&#xff0c;铁路如图所示&#xff0c;每辆火车从A驶入&#xff0c;再从B方向驶出&#xff0c;同时它的车厢可以重新组合。假设从A方向驶来的火车有n节&#xff08;n≤1000&#xff09;&…

Revit中关于屋顶编辑线移动的问题

一、Revit中关于屋顶编辑线移动的问题 在绘制屋顶的时候&#xff0c;如果出现有稍微偏差的时候&#xff0c;个别习惯移动编辑线&#xff0c;这种方法是不可取的&#xff0c;接下来为大家介绍一下这种方法的问题所在。 首先我们绘制几面这样的墙体&#xff0c;主要做测试用的&am…

锁升级之Synchronized

Synchronized JVM系统锁一个对象里如果有多个synchronized方法&#xff0c;同一时刻&#xff0c;只要有一个线程去调用其中的一个synchronized方法&#xff0c;其他线程只能等待&#xff01;锁的是当前对象&#xff0c;对象被锁定后&#xff0c;其他线程都不能访问当前对象的其…

流程引擎之发展史及对比总结

流程引擎渊源市场上比较有名的开源流程引擎有 jBPM、Activiti、Camunda、Flowable 和 Compileflow。其中 jBPM、Activiti、Flowable、camunda 四个框架同宗同源&#xff0c;祖先都是 jbpm4&#xff0c;开发者只要用过其中一个框架&#xff0c;基本上就会用其它三个。而 Compile…

SOFA Weekly|SOFANew、本周贡献 issue 精选

SOFA WEEKLY | 每周精选 筛选每周精华问答&#xff0c;同步开源进展欢迎留言互动&#xff5e;SOFAStack&#xff08;Scalable Open Financial Architecture Stack&#xff09;是蚂蚁集团自主研发的金融级云原生架构&#xff0c;包含了构建金融级云原生架构所需的各个组件&#…

基于Gromacs配体修饰自由能FPE计算(手动版)

基于Gromacs配体修饰自由能FPE计算(手动版) 本教程来自于https://github.com/huichenggong/Learning-Computation-with-Chenggong/tree/main/CC_news_008_ddG_uniFEP 我们将要使用的系统来自这篇论文 配体和受体pdb文件 A. 介绍 在本教程中&#xff0c;我们将使用非平衡自…

使用开源实时监控系统 HertzBeat 5分钟搞定 Mysql 数据库监控告警

使用开源实时监控系统 HertzBeat 对 Mysql 数据库监控告警实践&#xff0c;5分钟搞定&#xff01; Mysql 数据库介绍 MySQL是一个开源关系型数据库管理系统&#xff0c;由瑞典MySQL AB 公司开发&#xff0c;属于 Oracle 旗下产品。MySQL 是最流行的开源关系型数据库管理系统之…

VHDL语言基础-时序逻辑电路-锁存器

目录 锁存器的设计&#xff1a; RS锁存器&#xff1a; 真值表&#xff1a; 电路结构图&#xff1a; RS锁存器的仿真波形如下&#xff1a; D锁存器&#xff1a; D锁存器的仿真波形如下&#xff1a; 锁存器的设计&#xff1a; 为了与触发器相类比&#xff0c;我们先介绍锁…

奇舞周刊第 481 期 数据不够实时:试试长连接?

记得点击文章末尾的“ 阅读原文 ”查看哟~下面先一起看下本期周刊 摘要 吧~奇舞推荐■ ■ ■数据不够实时&#xff1a;试试长连接&#xff1f;在特定场景下&#xff0c;我们往往需要实时的去获取最新的数据&#xff0c;如获取消息推送或公告、股票大盘、聊天消息、实时的日志和…

面试(九)小米C++开发一面 21.11.02

1、局部变量与全局变量的区别?可以同名嘛? 首先是作用域: 局部变量只在变量声明的代码块范围内生效 全局变量在其声明后的所有位置都能访问到 在局部变量与全局变量同名的情况下,全局变量会被屏蔽掉,只会使用局部变量的内容 2、extern 当在a.c中想要使用b.c中的函数fu…

【Mac OS】JDK 多版本切换配置

前言 由于不同的项目可能需要使用的 JDK 版本不一样&#xff0c;所以在系统中配置多个 JDK 版本&#xff0c;并且能随时切换&#xff0c;是一个必要的配置。 查看已安装的 JDK 版本 /usr/libexec/java_home -V框框1是执行的命令 框框2是当前系统下所有的 JDK 版本 框框3是当…

1.7 Web学生管理系统

1.定义通讯协议基于前面介绍过的 FLask Web 网站 与 urlib 的访问网站的方法&#xff0c;设计一个综合应用实例。它是一个基于 Web 的学生记录管理程序。学生的记录包括 id(学号) 、name(姓名) 、grade(成绩)&#xff0c;服务器的作用是建立与维护一个Sqllite 的学生数据库 stu…

单目相机、双目相机和RGB-D相机学习笔记(一些视频和博文网址)

目录1. 单目相机1.1 摄像头原理1.2 单目相机的标定2 双目相机2.1 双目相机定位原理2.2 双目相机的缺陷3 RGB-D相机3.1 深度相机结构光原理3.2 RGB-D相机的应用1. 单目相机 1.1 摄像头原理 视频网址&#xff1a;【全网最详细】摄像头原理分析&#xff08;约25分钟课程&#xf…

RPC框架设计的安全性考量

RPC里面该如何提升单机资源的利用率&#xff0c;你要记住的关键点就一个&#xff0c;那就是“异步化”。调用方利用异步化机制实现并行调用多个服务&#xff0c;以缩短整个调用时间&#xff1b;而服务提供方则可以利用异步化把业务逻辑放到自定义线程池里面去执行&#xff0c;以…

springboot 注解

上一篇&#xff1a;初识springboot接收参数常用注解RequestBody 常用于POST表单提交参数接收RequestMapping("/test") public String test(RequestBody String data){return data; }PathVariable 获取路径上的参数&#xff1a;/test/{id} RequestMapping("/test…

开源流程引擎Camunda

开源流程引擎Camunda 文章作者&#xff1a;智星 1.简介 Camunda是一个轻量级的商业流程开源平台&#xff0c;是一种基于Java的框架&#xff0c;持久层采用Mybatis&#xff0c;可以内嵌集成到Java应用、SpringBooot应用中&#xff0c;也可以独立运行&#xff0c;其支持BPMN&a…

ThingsBoard-规则引擎介绍

1、什么是规则引擎? 规则引擎是一个易于使用的框架,用于构建基于事件的工作流。有3个主要组成部分: 消息- 任何传入事件。它可以是来自设备的传入数据、设备生命周期事件、REST API 事件、RPC 请求等。规则节点- 对传入消息执行的功能。有许多不同的节点类型可以过滤、转换…