【LeetCode刷题笔记(6-1)】【Python】【三数之和】【哈希表】【中等】

文章目录

  • 三数之和
    • 题目描述
    • 示例
      • 示例1
      • 示例2
      • 示例3
    • 提示
    • 解决方案1:【三层遍历查找】
    • 解决方案2:【哈希表】+【两层遍历】
  • 结束语

三数之和

三数之和

题目描述

给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != ji != kj != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0

请你返回所有和为 0 且不重复的三元组。

注意:答案中不可以包含重复的三元组。

示例

示例1

  • 输入:nums = [-1,0,1,2,-1,-4]
  • 输出:[[-1,-1,2],[-1,0,1]]

示例2

  • 输入:nums = [0,1,1]
  • 输出:[]
  • 解释:唯一可能的三元组和不为 0 。

示例3

  • 输入:nums = [0,0,0]
  • 输出:[[0,0,0]]
  • 解释:唯一可能的三元组和为 0 。

提示

  • 3 <= nums.length <= 3000
  • -105 <= nums[i] <= 105

解决方案1:【三层遍历查找】

对于解决【三数之和】这个问题,一种直观的解法是三层循环枚举所有可能的三元组,然后判断它们的和是否为零,但是这样的时间复杂度是 O(n3),对于较大的数组来说是不可接受的。

解决方案2:【哈希表】+【两层遍历】

在探讨【三数之和】这一算法题之前,我相信许多读者已经对【两数之和】有所涉猎。在我们深入理解题目要求时,我们明确了解决【两数之和】问题的核心是【如何高效查找目标值】。而【哈希表】以其迅速的查找速度脱颖而出,成为解决此类问题的得力助手。

现在,摆在我们面前的是【三数之和】问题,它与【两数之和】有着诸多相似之处。因此,我们很自然地会联想到运用【哈希表】来助力解决。这种思维跳跃不仅体现了我们对已知知识的灵活运用,更展示了我们在面对新问题时的敏捷思维。

与【三层遍历】相比,【哈希表】是一种以空间换时间的解决方案。首先,数组nums中可能存在大量值相同但索引不同的元素,如下所示:

nums1 = [0] * 10 + [1] * 10 + [-1] * 10
print(nums1)
# [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1]

对于这个问题而言,这些大量重复的元素显然是冗余的。我们的核心目标是在数组中找到N个不重复的三元组,在这些三元组中,元素之和为0 ==> 除了[0, 0, 0]这种特殊情况,其它任何数都不可能在一个三元组中重复三次(和不可能为0)。那么nums1实际上等价于数组[0, 0, 0, 1, 1, -1, -1] ==> 这意味着我们可以先对原数组nums先进行【去重】操作,形成一个新数组new_nums,在新数组中,除了0可以重复三次,其它值至多重复两次。

原数组【去重】的重要意义:某些情况可以显著降低整体算法的时间复杂度。比如说上面代码里的nums1,原来有30个元素,那么两层循环遍历的时间复杂度是O(n2),n=30; 如果对nums1进行去重,去重后的新数组实际上只包含7个元素,两层循环遍历的时间复杂度从O(302)将至O(72)!

原数组【去重】代码如下

# 创建哈希表,记录去重后的新数组元素,方便后续遍历查找目标值。
hash_map = {} # 哈希表的键为新数组元素值,值为元素值在新数组的新下标num_idx
num_idx = 0 # 新数组的下标初始化为0
new_nums = [] # 用新的数组记录去重后的数组# 遍历原数组
for idx, num in enumerate(nums): # 当前元素不在哈希表中if num not in hash_map:hash_map[num] = [num_idx] # 创建新的键-值对,记录该元素# 记录去重后仍保留的数组元素new_nums.append(num) num_idx += 1 else: # 当前元素已在哈希表中# 如果该元素已在哈希表中记录两次if len(hash_map[num]) == 2:if num == 0: # 除非该元素是0,否则不再记录hash_map[num].append(num_idx)new_nums.append(num)num_idx += 1# 如果该元素已在哈希表中记录一次elif len(hash_map[num]) == 1:# 在哈希表中再次记录该元素hash_map[num].append(num_idx)# 新数组同时记录该元素new_nums.append(num)num_idx += 1# 其它情况不做任何处理else:pass

当有了哈希表hash_map后,便可以通过【两层遍历】在哈希表中查找目标值来得到有效的三元组。

算法步骤

  1. 第一层循环:遍历数组元素nums[i] —> i从0–>n,n为新数组的元素个数,执行步骤(2);
  2. 第二层循环:遍历数组元素nums[j] —> ji+1–>n,n为新数组的元素个数,执行步骤(3);
  3. nums[i] + nums[j]相反数-(nums[i] + nums[j])不存在于哈希表hash_map中,则返回步骤(1); 若存在,说明找到可能正确的三元组,执行步骤(4);
  4. 遍历哈希表中-(nums[i] + nums[j])】对应的每个索引k —> k至多有三个不同的值(分别是三个0元素所对应的索引),执行步骤(5);
  5. k=i或者k=j, 说明这个元素三元组将出现重复的元素(同一索引),不符合题意,返回步骤(1)<— 第一次去重:避免在元素三元组中出现同一元素(同索引的元素) ;若k!=i and k!=j,说明当前的三个索引i,j,k互不相同,可能是正确的三元组,执行步骤(6);
    细节】:尽管三个索引i,j,k互不相同,但仍然不能保证由索引三元组对应的元素三元组不会在结果列表中出现重复 <— 不同的索引三元组可能对应同一种元素三元组;
  6. 参考字母异位词分组的解决方案,对当前三个索引所生成的结果列表[new_nums[k], new_nums[i], new_nums[j]]进行【排序】。因为一旦出现重复的三元组结果(如[1, 0, -1] 和[0, 1, -1]),它们虽然顺序不同,但排序结果一定是相同
    检查排序后的元素三元组sorted_result是否存在于哈希表is_used_results中,若已存在,说明出现了重复的元素三元组,不符合题意,返回步骤(1)<— 第二次去重:避免出现重复的元素三元组,尽管三个索引i,j,k互不相同 ;若不存在,说明这个元素三元组是无重复的,执行步骤(7);
  7. 将元素三元组保存于结果列表result_list中,重复执行步骤1-7,直到循环结束。

细节】既然已经有一个结果列表result_list记录元素三元组,为什么不直接判断排序后的元素三元组sorted_result是否存在于结果列表result_list中,而是重新创建一个哈希表is_used_results来协助判断?

答:因为哈希表的查找速度非常快!!!,如果在列表中查找,可能会超时

完整代码如下

class Solution:  def threeSum(self, nums: List[int]) -> List[List[int]]:# 创建哈希表,记录去重后的新数组元素,方便后续遍历查找目标值。hash_map = {} # 哈希表的键为新数组元素值,值为元素值在新数组的新下标num_idxnum_idx = 0 # 新数组的下标初始化为0new_nums = [] # 用新的数组记录去重后的数组# 遍历原数组for idx, num in enumerate(nums): # 当前元素不在哈希表中if num not in hash_map:hash_map[num] = [num_idx] # 创建新的键-值对,记录该元素# 记录去重后仍保留的数组元素new_nums.append(num) num_idx += 1 else: # 当前元素已在哈希表中# 如果该元素已在哈希表中记录两次if len(hash_map[num]) == 2:if num == 0: # 除非该元素是0,否则不再记录hash_map[num].append(num_idx)new_nums.append(num)num_idx += 1# 如果该元素已在哈希表中记录一次elif len(hash_map[num]) == 1:# 在哈希表中再次记录该元素hash_map[num].append(num_idx)# 新数组同时记录该元素new_nums.append(num)num_idx += 1# 其它情况不做任何处理else:passresult_list = [] # 存放元素三元组n = len(new_nums) is_used_results = set() # 创建哈希表,协助判断元素三元组是否重复for i in range(n):  for j in range(i+1, n):  if -(new_nums[i] + new_nums[j]) in hash_map: for k in hash_map[-(new_nums[i] + new_nums[j])]: # 查找目标值, 依次返回目标值索引kif k == i:continue # 第一次去重,避免元素三元组出现重复的元素elif k == j:continue # 第一次去重,避免元素三元组出现重复的元素else:sorted_result = tuple(sorted([new_nums[k], new_nums[i], new_nums[j]]))if sorted_result in is_used_results: # 涉及查找时,用哈希表最快pass # 第二次去重,避免结果列表中出现重复的元素三元组else:result_list.append([new_nums[k], new_nums[i], new_nums[j]])  is_used_results.add(sorted_result)  return result_list

运行结果
在这里插入图片描述
复杂度分析

  • 时间复杂度:O(N2),其中 N 是新数组new_nums元素的数量。
    • 双层循环遍历新数组 ===> O(N2)
  • 空间复杂度:O(N)
    • 需要用哈希表列表存放新数组 ===> O(N)

结束语

  • 亲爱的读者,感谢您花时间阅读我们的博客。我们非常重视您的反馈和意见,因此在这里鼓励您对我们的博客进行评论。
  • 您的建议和看法对我们来说非常重要,这有助于我们更好地了解您的需求,并提供更高质量的内容和服务。
  • 无论您是喜欢我们的博客还是对其有任何疑问或建议,我们都非常期待您的留言。让我们一起互动,共同进步!谢谢您的支持和参与!
  • 我会坚持不懈地创作,并持续优化博文质量,为您提供更好的阅读体验。
  • 谢谢您的阅读!

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

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

相关文章

人工智能_机器学习066_SVM支持向量机_对偶问题转化_强对偶问题_弱对偶问题_弱对偶问题详解---人工智能工作笔记0106

上一节我们就得到了9,这个公式,这个公式要求,先去求maxL(x,lamada) ,也就是求,lamada是多少的时候,对应的, L(x,lamada) = f(x) + h(x) * lamada <=P 中的这个h(x) * lamada,最大,因为h(x)是小于0的也就是,lamada是什么的时候,h(x) * lamada最大,也就是越接近于0对吧. 然…

GaussDB如何创建和管理视图

GaussDB如何创建和管理视图 一、什么是视图 当用户对数据库中的一张或者多张表的某些字段的组合感兴趣&#xff0c;而又不想每次键入这些查询时&#xff0c;用户就可以定义一个视图&#xff0c;以便解决这个问题。 视图与基本表不同&#xff0c;不是物理上实际存在的&#x…

nginx反向代理实践指南:访问Tomcat

目录 前言1 实现的效果2 访问流程分析3 安装tomcat并测试4 配置4.1 在Windows系统的hosts文件进行域名和IP对应关系的配置4.2 在NGINX进行请求转发的配置&#xff08;反向代理配置&#xff09; 5 最终测试结论 前言 从Windows系统访问Tomcat Web应用程序&#xff0c;设置和配置…

字符串——OJ题

&#x1f4d8;北尘_&#xff1a;个人主页 &#x1f30e;个人专栏:《Linux操作系统》《经典算法试题 》《C》 《数据结构与算法》 ☀️走在路上&#xff0c;不忘来时的初心 文章目录 一、字符串相加1、题目讲解2、思路讲解3、代码实现 二、仅仅反转字母1、题目讲解2、思路讲解3…

Todesk、向日葵等访问“无显示器”主机黑屏问题解决

我的环境是 ubuntu 22.04 安装 要安装 video dummy&#xff0c;请在终端中运行以下命令&#xff1a; sudo apt install xserver-xorg-video-dummy配置 video dummy 的配置文件请自行搜索 使用任何文本编辑器打开此文件。 我的是 /etc/X11/xorg.conf 默认配置文件包含以下内…

Python等比例缩放图片并修改对应的Labelme标注文件(v2.0)

Python等比例缩放图片并修改对应的Labelme标注文件&#xff08;v2.0&#xff09; 前言前提条件相关介绍实验环境Python等比例缩放图片并修改对应的Labelme标注文件Json文件代码实现输出结果 前言 此版代码&#xff0c;相较于Python等比例缩放图片并修改对应的Labelme标注文件&a…

nodejs微信小程序+python+PHP血液中心管理平台的设计与实现-计算机毕业设计推荐

在二十一世纪的今天&#xff0c;我国献血总量已经不容小觑&#xff0c;在全国人民的不懈努力下&#xff0c;贫血、缺血的病人已经有了足够的血液保障。与此同时&#xff0c;采血工作和血液入库、出库等工作也日愈繁重。为进一步提高采血工作和血液中心的工作效率&#xff0c;开…

架构设计系列之常见架构(二)

五、DDD&#xff08;领域驱动设计&#xff09; 领域驱动设计&#xff08;Domain-Driven Design&#xff0c;DDD&#xff09;是一种开发思想&#xff0c;强调将软件系统的注意力集中在业务领域上&#xff0c;将领域视为应用的核心。在架构设计中&#xff0c;DDD 提供了一种不同…

IDEA debug窗口左边工具栏隐藏与显示

今天在debug排查代码的时候一不小心点到哪里&#xff0c;结果变成这样 我们可以这样恢复&#xff0c;右键Debug 点击show Toolbar

Excel实现字母+数字拖拉自动递增,步长可更改

目录 1、带有字母的数字序列自增加&#xff08;步长可变&#xff09; 2、仅字母自增加 3、字母数字同时自增 1、带有字母的数字序列自增加&#xff08;步长可变&#xff09; 使用Excel通常可以直接通过拖拉的方式&#xff0c;实现自增数字&#xf…

牛客后端开发面试题2

微软2021 1、给你一个凸多边形&#xff0c;你怎么用一条线&#xff0c;把它分成面积相等的两部分 将凸多边形的任意一个顶点作为顶点&#xff0c;然后连接另外两个相邻的顶点&#xff0c;将凸多边形划分成多个三角形。 计算每个三角形的面积&#xff0c;并且累加面积&#xff…

安恒明御安全网关 aaa_local_web_preview文件上传漏洞复现

0x01 产品简介 明御安全网关秉持安全可视、简单有效的理念,以资产为视角,构建全流程防御的下一代安全防护体系,并融合传统防火墙、入侵检测、入侵防御系统、防病毒网关、上网行为管控、VPN网关、威胁情报等安全模块于一体的智慧化安全网关。 0x02 漏洞概述 明御安全网关在…

[css] flex wrap 九宫格布局

<div class"box"><ul class"box-inner"><li>九宫格1</li><li>九宫格2</li><li>九宫格3</li><li>九宫格4</li><li>九宫格5</li><li>九宫格6</li><li>九宫格7&l…

Axure之动态面板轮播图

目录 一.介绍 二.好处 三.动态面板轮播图 四.动态面板多方式登录 五.ERP登录 六.ERP的左侧菜单栏 七.ERP的公告栏 今天就到这了哦&#xff01;&#xff01;&#xff01;希望能帮到你了哦&#xff01;&#xff01;&#xff01; 一.介绍 Axure中的动态面板是一个非常有用的组…

案例066:基于微信小程序的家政预约设计与实现

文末获取源码 开发语言&#xff1a;Java 框架&#xff1a;SSM JDK版本&#xff1a;JDK1.8 数据库&#xff1a;mysql 5.7 开发软件&#xff1a;eclipse/myeclipse/idea Maven包&#xff1a;Maven3.5.4 小程序框架&#xff1a;uniapp 小程序开发软件&#xff1a;HBuilder X 小程序…

Qt容器QMdiArea 小部件提供一个显示 MDI 窗口的区域

## QMdiArea ## 控件简介 QMdiArea 继承 QAbstractScrollArea。QMdiArea 小部件提供一个显示 MDI 窗口的区域。QMdiArea的功能本质上类似于MDI窗口的窗口管理器。大多数复杂的程序,都使用MDI框架,在 Qt designer 中可以直接将控件 MDI Area 拖入使用。 ## 用法示例 例 qm…

Linux系统中如何开启和配置OpenGauss数据库的远程连接

文章目录 前言1. Linux 安装 openGauss2. Linux 安装cpolar3. 创建openGauss主节点端口号公网地址4. 远程连接openGauss5. 固定连接TCP公网地址6. 固定地址连接测试7. 结语 前言 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍…

「Swift」Xcode多Target创建

前言&#xff1a;我们日常开发中会使用多个环境&#xff0c;如Dev、UAT&#xff0c;每个环境对应的业务功能都不同&#xff0c;但每个环境之间都只存在较小的差异&#xff0c;所以此时可以使用创建多个Target来实现&#xff0c;每个Target对应这个一个App&#xff0c;可以实现一…

(WPF)Serilog 使用demo实例

Serilog 日志效果&#xff1a; 引入的Serilog库文件 实现代码 xaml 代码&#xff1a; <Window x:Class"Wpf_demo_Serilog.MainWindow" xmlns"http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x"http://sche…

Python语言学习笔记之十一(DotEnv)

本课程对于有其它语言基础的开发人员可以参考和学习&#xff0c;同时也是记录下来&#xff0c;为个人学习使用&#xff0c;文档中有此不当之处&#xff0c;请谅解。 1、认识Python DotEnv dotenv是Python中的一个工具包&#xff0c;它主要用于谈取项目中的.env文件&#xff0…