正确解法是排序+双指针,时间复杂度o(n²);先排序才能用双指针判断移动方向;去重需在外层循环开头及内层匹配后分别对left/right独立while跳过重复;加法用long防溢出。

为什么不能直接用三层 for 循环暴力枚举
能跑通,但时间复杂度是 O(n³),遇到 10⁴ 量级的数组就超时。LeetCode 上多数三数之和题目的测试用例会卡掉纯暴力解法,尤其当输入含大量重复数字时,不仅慢,还容易在去重逻辑上出错。
真正实用的解法是「排序 + 双指针」,把内层两重循环压成 O(n),整体降到 O(n²)。核心前提是:必须先排序,否则双指针无法判断该移动左还是右。
常见错误现象:[-1,0,1,2,-1,-4] 输入下漏掉 [-1,-1,2] 或重复返回 [-1,0,1] 多次——这通常是因为去重位置写错,或没跳过相邻相同元素。
- 排序前别修改原数组索引需求(如有),否则需额外记录原始位置
- 去重只在「外层循环起点」和「双指针内部」做,不要在结果 push 后再 dedup,效率低且易错
- 用
long防止三数相加溢出(比如INT_MAX相加)
如何正确实现双指针收缩逻辑
固定第一个数 nums[i],然后在 i+1 到 n-1 区间设 left 和 right 指针。根据当前三数和 sum = nums[i] + nums[left] + nums[right] 决定收缩方向:
- 若
sum == target:存入结果,同时left++且right--,并跳过所有重复值(避免同一组数被多次录入) - 若
sum :<code>left++(增大总和) - 若
sum > target:right--(减小总和)
关键细节:去重必须在找到解之后立即做,且要分别对 left 和 right 单独 while 跳过重复,不能只跳一次。例如 [0,0,0,0] 中,left 从索引 1 跳到 3 才算清空重复。
while (left <h3>去重逻辑最容易写错的三个位置</h3><p>三处去重缺一不可,且顺序不能颠倒:</p><div class="aritcle_card flexRow artxards"> <div class="artcardd flexRow"> <a class="aritcle_card_img" rel="nofollow" href="/xiazai/gongju/2823" title="C++14"><img src="https://img.php.cn/upload/manual/001/431/639/6ac8b33c327c4749.png" alt="C++14" onerror="this.onerror='';this.src='/static/lhimages/moren/morentu.png'" ></a> <div class="aritcle_card_info flexColumn"> <a rel="nofollow" href="/xiazai/gongju/2823" title="C++14" class="overflowclass">C++14</a> <p class="overflowclass">C++14 对 C++11 的修正与增强版本,适合旧系统维护和较老工具链兼容。</p> </div> <a rel="nofollow" href="/xiazai/gongju/2823" title="C++14" class="aritcle_card_btn flexRow flexcenter"><b></b><span>下载</span> </a> </div> </div>
-
外层 i 去重:
if (i > 0 && nums[i] == nums[i - 1]) continue;—— 放在循环开头,跳过与前一个相同的起始值 -
内层 left 去重:仅在
sum == target之后、left++前执行,防止漏掉[-2,-1,3]这类边界组合 -
内层 right 去重:同样只在匹配成功后、
right--前执行,且必须和 left 去重独立进行
典型错误:把去重写成 if (nums[left] == nums[left+1]) left++ —— 这样只跳一次,漏掉连续三个相同数;或者把去重放到 push 结果之前,导致 [-1,-1,2] 被判为重复而丢弃。
C++ 实现中要注意的类型与边界细节
输入是 vector<int></int>,但中间计算可能溢出。虽然题目常保证答案不溢出,但 nums[i] + nums[left] + nums[right] 在加法过程中就可能越界。稳妥做法是用 long 转换:
long sum = (long)nums[i] + nums[left] + nums[right];
另外,right 初始化为 n - 1,但循环条件必须是 left ,不是 <code>left —— 否则当 <code>left == right 时会把同一个元素用两次(违反“三个不同下标”的隐含要求)。
最后,别忘了在进入双指针前检查 i 的上界:for (int i = 0; i ,否则 <code>left 和 right 会越界。
最常被忽略的是:当数组长度小于 3 时,必须提前返回空结果,不然 nums[n-2] 访问非法内存。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










