【算法-哈希表4】 三数之和(去重版)

今天,带来哈希相关算法的讲解。文中不足错漏之处望请斧正!

理论基础点这里


三数之和

分析题意

这就是三数之和去重版嘛。

题意转化

求三元组, 满足每个元素相加为0,其中每个元素下标不同;而得到的三元组不能重复。

  • 构成三元组的三个元素下标不同
  • 三元组和三元组之间不同
    怎么理解“三元组和三元组之间不同”:[-1, 0, 1][-1, 0, 1] 是相同的,尽管构成这两个三元组的元素不同, 但它俩是相同的。

解决思路

1. 哈希法

这道题如果用哈希法,和之前差别不大:两层for遍历nums,一层取a,一层取b,求和映射到哈希表。然后再遍历nums,看看是否有(0-(a+b))这样的元素。

但是这道题用哈希法是比较复杂的,因为去重操作比较麻烦。

2. 双指针法

大概的思路是这样:

  • 排序
  • 给三个指针
    • i,用for控制,固定往后走,取a
    • left,初始化为i后面的元素,取b
    • right,是最后一个元素,取c
  • nums[i] + nums[left] + nums[right] > 0:三个数的和太大了,我们需要让某个数变小,所以--right
  • nums[i] + nums[left] + nums[right] < 0:三个数的和太小了,我们需要让某个数变大,所以++left
  • nums[i] + nums[left] + nums[right] == 0:收集结果

再简单点说:

  • 排序
  • i固定取a
  • 固定了a后,移动left和right取b和c,收割符合条件的结果集

排序带来的有序性让我们拿取元素的时候做到有的放矢。

怎么去重?

对三元组去重,只需要保证我们每次取完结果集后,把已经取过的结果集跳过就行。或者说,只要保证所有三元组的第一、二、三个元素的值不等就行。

首先,a的去重:排序后的nums,只要a不和相邻元素相等,就不是重复的。

下面两种选哪种呢?

  1. if(nums[i] == nums[i + 1]) continue;
  2. if(nums[i] == nums[i - 1]) continue;

这不是一样的吗?不一样:第一种是判断num[i]和它后面的一个元素是否相等,但这有可能会把某个结果给去掉了。比如[-1, -1, 2],这是符合条件的结果集,但是当i=0,你判断nums[i] == nums[i + 1](-1 == -1),就直接跳过了,不对。
简单理解,如果有一个结果集是[-1, b, c],那往后就不能再出现其他三元组是以[-1]开头的。

其次,b和c的去重。看这个例子:

[0, -1, -1, -1, 1, 1, 1],这个例子只有一个结果集:[0, -1, 0]当我们收获了第一个结果集[0, -1, 1]后,left和right按理讲都往里面收缩一下。但不一定只是一下,如果只收缩一下,那就可能会重复收割相同的结果集。所以,当left和right收缩后还是重复,就继续收缩,直到不重复。

对a的去重保证:在同一轮循环中不添加相同的三元组。
对bc的去重保证:在整体结果中不添加已经存在的三元组。

编程实现

class Solution {
public:vector<vector<int>> threeSum(vector<int>& nums) {int i = 0;int left = 0;int right = 0;vector<vector<int>> result;sort(nums.begin(), nums.end());for (auto &e : nums) cout << e << " ";cout << endl;for (i = 0; i < nums.size(); ++i) { // i 取 aif(nums[i] > 0) return result; //剪枝: 排序后,第一个数(min)都大于0,任何组合都不能等于0了if (i > 0 && nums[i] == nums[i - 1]) continue; // a的去重left = i + 1;right = nums.size() - 1;// 对于当前a, 不断用left, right 取 b, c, 看看能不能收割结果集while (left < right) {int sum = nums[i] + nums[left] + nums[right];if (sum > 0) --right;else if (sum < 0) ++left;else {while (left < right && nums[left] == nums[left + 1]) ++left; // b的去重while (left < right && nums[right] == nums[right - 1]) --right; // c的去重result.push_back({ nums[i], nums[left], nums[right] });++left;--right;                    }}}return result;}
};

不对a去重:

class Solution {
public:vector<vector<int>> threeSum(vector<int>& nums) {int i = 0;int left = 0;int right = 0;vector<vector<int>> result;sort(nums.begin(), nums.end());for (auto &e : nums) cout << e << " ";cout << endl;for (i = 0; i < nums.size(); ++i) { // i 取 a// if (i > 0 && nums[i] == nums[i - 1]) continue; left = i + 1;right = nums.size() - 1;// 对于当前a, 不断用left, right 取 b, c, 看看能不能收割结果集while (left < right) {int sum = nums[i] + nums[left] + nums[right];if (sum > 0) --right;else if (sum < 0) ++left;else {while (left < right && nums[left] == nums[left + 1]) ++left;while (left < right && nums[right] == nums[right - 1]) --right;result.push_back({ nums[i], nums[left], nums[right] });printf("new result: i=%d left=%d right=%d\n\t\nums[i]=%d nums[left]=%d nums[right]=%d\n", i, left, right, nums[i], nums[left], nums[right]);++left;--right;                    }}}return result;}
};

在这里插入图片描述

不对bc去重:

class Solution {
public:vector<vector<int>> threeSum(vector<int>& nums) {int i = 0;int left = 0;int right = 0;vector<vector<int>> result;sort(nums.begin(), nums.end());for (auto &e : nums) cout << e << " ";cout << endl;for (i = 0; i < nums.size(); ++i) { // i 取 aif (i > 0 && nums[i] == nums[i - 1]) continue; left = i + 1;right = nums.size() - 1;// 对于当前a, 不断用left, right 取 b, c, 看看能不能收割结果集while (left < right) {int sum = nums[i] + nums[left] + nums[right];if (sum > 0) --right;else if (sum < 0) ++left;else {// while (left < right && nums[left] == nums[left + 1]) ++left;// while (left < right && nums[right] == nums[right - 1]) --right;result.push_back({ nums[i], nums[left], nums[right] });printf("new result: i=%d left=%d right=%d\n\t\nums[i]=%d nums[left]=%d nums[right]=%d\n", i, left, right, nums[i], nums[left], nums[right]);++left;--right;                    }}}return result;}
};

在这里插入图片描述


今天的分享就到这里了,感谢您能看到这里。

这里是培根的blog,期待与你共同进步!

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

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

相关文章

GPU Microarch 学习笔记【3】Tensor Core

目录 1. 指令与架构 2. Load 3. 计算MMA 4. Set, Step 与thread group 5. OCTET 6. Tensor Core微架构 7. Final Nvidia自从Volta/Turing&#xff08;2018&#xff09;架构开始&#xff0c;在stream multi processor中加入了tensor core&#xff0c;用于加速矩阵计算。如…

OpenCvSharp从入门到实践-(01)认识OpenCvSharp开发环境搭建

目录 一、OpenCV 二、OpenCvSharp 三、OpenCvSharp开发环境搭建 四、下载 五、其他 一、OpenCV OpenCV是基于Apache2.0许可&#xff08;开源&#xff09;发行的跨平台计算机视觉和机器学习函数库&#xff0c;支持Windows、Linux、Android和Mac OS操作系统。OpenCV由一系…

Unity中 Start和Awake的区别

Awake和Start在Unity中都是MonoBehaviour脚本中的生命周期函数 Awake函数在游戏对象首次被加载时调用&#xff0c;在游戏对象初始化之前调用。 start函数在游戏对象初始化完成后调用&#xff0c;在update第一次执行前调用。 这两个函数在其生命周期内都只会调用一次&#xf…

auth 对象创建 session 时出现 异常 详细解析

在使用 requests 库的 1.0.3 版本时&#xff0c;尝试使用 auth 对象创建 session 时出现了 TypeError 异常。然而&#xff0c;在 0.14.2 版本中&#xff0c;这个操作是正常工作的。下面是具体的错误信息&#xff1a; python import requests requests.version 1.0.3 auth re…

【快速解决】实验四 对话框 《Android程序设计》实验报告

目录 前言 实验要求 实验四 对话框 正文开始 第一步建立项目 第二步选择empty views activity点击next ​编辑 第三步起名字&#xff0c;点击finish 第四步对 activity _main.xml文件操作进行布局 第五步&#xff0c;建立两个新文件&#xff0c;建立方法如下 SecondA…

springboot上传文件后显示权限不足

前言&#xff1a; 最近一个老项目迁移&#xff0c;原本一直好好的&#xff0c;迁移后上传文件的功能使用不正常&#xff0c;显示文件没有可读取权限&#xff0c;这个项目并不是我们开发和配置的&#xff0c;由第三方开发的&#xff0c;我们只是接手一下。 前端通过api上传文件…

(亲测有效)VMware Windows虚拟机扩容

场景&#xff1a; VMware安装了一个windows系统&#xff0c;现在windows系统剩余空间不足&#xff0c;需要扩容。 解决步骤&#xff1a; 关闭虚拟机&#xff0c;状态如下&#xff1a; 2、点击编辑虚拟机设置-》扩展 3、输入最大磁盘大小-》点击扩展 4、等待成功后&#xff0c…

机器人制作开源方案 | 钻孔植树一体化沙漠车

作者&#xff1a;徐邦国、张博宇、刘露、李晶晶、吕洁秀单位&#xff1a;天津职业技术师范大学 机械工程学院指导老师&#xff1a;何永利 摘要&#xff1a;本项目旨在设计一种专用于沙漠植树的植树车&#xff0c;以沙漠自动化植树为研究对象&#xff0c;提出一种创新式钻…

企业微信机器人定时发送图文信息,后续无需人工操作

企业微信群机器人是企业微信的内置功能&#xff0c;可以理解为是一个群提醒通知工具&#xff0c;接收数据并自动发送信息到企业微信群中。 数环通实现打通定时器和企业微信机器人的对接&#xff0c;定时执行自动化流程&#xff0c;无需人工干预&#xff0c;实现工作流程自动化&…

不懂找伦敦银趋势?3个方法搞定

趋势是我们的朋友&#xff0c;但是这个朋友却很喜欢跟我们开玩笑&#xff0c;如果我们不留意&#xff0c;根本发觉不了它的存在。怎么找到趋势本体并且和它做个好朋友呢&#xff1f;下面我们就来介绍三个方法。 数波段的高点和低点。我们以当前的市场波动价格为轴&#xff0c;向…

python 自动化福音,30行代码手撸ddt模块

用 python 做过自动化的小伙伴&#xff0c;大多数都应该使用过 ddt 这个模块&#xff0c;不可否认 ddt 这个模块确实挺好用&#xff0c;可以自动根据用例数据&#xff0c;来生成测试用例&#xff0c;能够很方便的将测试数据和测试用例执行的逻辑进行分离。 接下来就带大家一起…

信驰达科技加入智慧车联产业生态联盟ICCE,共创智慧车联未来

图1 信驰达加入智慧车联产业生态联盟 信驰达拥有60余项专利认证及软件著作权&#xff0c;以及BQB、SRRC、FCC、IC、CE、RoHS、REACH、KCC等数百个权威产品认证&#xff0c;公司是车联网联盟(CCC)和智慧车联产业生态联盟&#xff08;ICCE&#xff09;会员&#xff0c;已通过ISO…