力扣刷题笔记-三数之和

力扣题目-三数之和C++实现

给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != j、i != k 且 j != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。请你返回所有和为 0 且不重复的三元组。

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

分析:

题目要求找到的是不能重复的三元组,为了更好地处理重复的数据,想到先对数据进行排序,因为排序以后重复的数据都挨在一起了,并且在排序之后计算也更加方便,能通过指针的移动来进行控制。

然后,我们通过指针的移动来寻找和为0的三个数,如果其中一个数进行了固定,就只需要用双指针来找剩下的两个数了。

以示例nums = [-1,0,1,2,-1,-4]为例,排序后为[-4,-1,-1,0,1,2]

首先可以固定第一个元素i,去找剩下的两个数,剩下的两个数分为用左指针left和右指针right指向他们。

当第一个数固定之后,计算三个数的和,

如果总和 > 0:说明数值太大,需要减小总和 → 右指针左移(right--)

如果总和 < 0:说明数值太小,需要增大总和 → 左指针右移(left++)

在这里考虑一种特殊情况,减少计算量,就是如果那个固定的数本来就已经大于0了,那就不用往下走了,因为left和right指向的数肯定都是在i的右边的更大的正数

到了这里还没结束,最重要的来了,就是题目说三元组是不能重复的,那么我们就要进行关键的去重工作,主要分为这三个方面:
固定数的去重

-4, -1, -1, 0, 1, 2\]中,可能会用第一个 -1 当固定数找到 \[-1,-1,2\],再用第二个 -1 当开头,又找到一次 \[-1,-1,2\],这就重复了。解决办法就是一开始从左到右遍历数组时,只要当前数和它左边紧邻的数一样,就直接跳过当前数。 **左指针位置去重** 固定了第一个数,正常寻找过程中left会往右移动,往右移动的话left也可能右边的数也是一样的,所以会造成重复返回。假设数组是 nums = \[-1, -1, -1, 0, 1, 2\](索引 0、1、2 都是 - 1),初始 left=1(指向第二个-1),right=5(指向最右边那个2),此时nums\[0\] + nums\[1\] + nums\[5\] = -1 + (-1) + 2 = 0 → 组合 \[-1,-1,2\](有效)。如果不处理重复:直接移动 left 到 2(left++),此时 left=2: nums\[0\] + nums\[2\] + nums\[5\] = -1 + (-1) + 2 = 0 → 又得到 \[-1,-1,2\],重复。 解决办法是去判断是否nums\[left\] == nums\[left+1\],如果当前left指向数和下一个索引的数值重复,就 left++ 跳过。 **右指针位置去重** 原理和左指针去重一样,解决办法是判断是否nums\[right\] == nums\[right-1\],当前right指向的数和前一个索引的数值重复),就 right-- 跳过。 注意,left 和 right 的去重逻辑,只在 "找到有效组合(sum=0)之后" 才需要触发,目的是消除重复的结果。 ```cpp class Solution { public: vector> threeSum(vector& nums) { vector> result; // 先对数组进行排序 sort(nums.begin(), nums.end()); // 外层循环:遍历固定数(第一个数) // 确保至少有i、left、right三个不同索引 for (int i = 0; i < nums.size() - 2; i++) { // 固定数去重:如果当前数与前一个数相同,跳过 if (i > 0 && nums[i] == nums[i - 1]) { continue; } // 初始化双指针 int left = i + 1; // 第二个数的指针(左指针) int right = nums.size() - 1; // 第三个数的指针(右指针) // 双指针循环:寻找满足条件的第二个数和第三个数 while (left < right) { int sum = nums[i] + nums[left] + nums[right]; if (sum < 0) { // 总和太小,左指针右移(增大数值) left++; } else if (sum > 0) { // 总和太大,右指针左移(减小数值) right--; } else { // 找到有效三元组,加入结果集 result.push_back({nums[i], nums[left], nums[right]}); // 第二个数去重:如果当前左指针数与下一个数相同,左指针右移跳过 while (left < right && nums[left] == nums[left + 1]) { left++; } // 第三个数去重:如果当前右指针数与前一个数相同,右指针左移跳过 while (left < right && nums[right] == nums[right - 1]) { right--; } // 找到有效组合后,双指针同时移动寻找下一组 left++; right--; } } } return result; } }; ``` **第一轮循环:i=0(固定数 nums\[0\] = -4)** ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/3d3dfd8108fe41f888ee9112115cc502.png) 初始指针:left=1(数值 - 1),right=5(数值 2) 第一步:sum = -4 + (-1) + 2 = -3 \< 0 → 执行 left++ 新指针:left=2(数值 - 1),right=5(数值 2) 第二步:sum = -4 + (-1) + 2 = -3 \< 0 → 执行 left++ 新指针:left=3(数值 0),right=5(数值 2) 第三步:sum = -4 + 0 + 2 = -2 \< 0 → 执行 left++ 新指针:left=4(数值 1),right=5(数值 2) 第四步:sum = -4 + 1 + 2 = -1 \< 0 → 执行 left++ 新指针:left=5(数值 2),right=5(数值 2) left=5 不小于 right=5 → 本轮结束 **第二轮循环:i=1(固定数 nums\[1\] = -1)** ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/dcb85bad0ce1490897f7f8998567e8f6.png) 初始指针:left=2(数值 - 1),right=5(数值 2) 此时sum = -1 + (-1) + 2 = 0(找到有效组合) 记录 \[-1,-1,2

去重判断:

left=2:nums[2] == nums[3](-1 == 0?否)→ 不移动 left

right=5:nums[5] == nums[4](2 == 1?否)→ 不移动 right

执行 left++ 和 right--

新指针:left=3(数值 0),right=4(数值 1)

此时sum = -1 + 0 + 1 = 0(找到有效组合)

记录 [-1,0,1]

去重判断:

left=3:nums[3] == nums[4](0 == 1?否)→ 不移动 left

right=4:nums[4] == nums[3](1 == 0?否)→ 不移动 right

执行 left++ 和 right--

新指针:left=4(数值 1),right=3(数值 0)

left=4 不小于 right=3 → 本轮结束
第三轮循环:i=2(固定数 nums[2] = -1)

固定数去重判断:i=2 > 0 且 nums[2] == nums[1](-1 == -1)→ 执行 continue

结果:不初始化指针,直接跳过本轮
第四轮循环:i=3(固定数 nums[3] = 0)

初始指针:left=4(数值 1),right=5(数值 2)

sum = 0 + 1 + 2 = 3 > 0 → 执行 right--

新指针:left=4(数值 1),right=4(数值 1)

终止:left=4 不小于 right=4 → 本轮结束
i的值为4时不满足循环条件<nums.size()-2,整体循环结束

相关推荐
Pafey3 小时前
VS2022 + Qt5.9 中文乱码/项目设置utf-8编码
c++·qt·中文乱码
minji...3 小时前
C++ STL之list的使用
开发语言·c++
被遗忘的旋律.3 小时前
Linux驱动开发笔记(十)——中断
linux·驱动开发·笔记
青草地溪水旁3 小时前
23 种设计模式
开发语言·c++·设计模式
一碗白开水一3 小时前
【第19话:定位建图】SLAM点云配准之3D-3D ICP(Iterative Closest Point)方法详解
人工智能·算法
编码浪子3 小时前
趣味学RUST基础篇(函数式编程闭包)
开发语言·算法·rust
Want5954 小时前
C/C++圣诞树②
c语言·c++·算法
nnerddboy4 小时前
Linux嵌入式自学笔记(基于野火EBF6ULL):1.配置环境
linux·笔记·单片机·嵌入式硬件
天天代码码天天4 小时前
基于海康SDK的C++实时视频流逐帧抓取存图小工具
c++·海康sdk·逐帧抓图