ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

LeetCode 热题 HOT 100 第8天: “三数之和”

2022-01-11 14:00:59  阅读:213  来源: 互联网

标签:third nums 三数 second 三元组 HOT 100 partSum first


继续刷LeetCode 热题 HOT 100 的题目,并且在博客更新我的solutions。在csdn博客中我会尽量用文字解释清楚,相关Java代码大家可以前往我的个人博客jinhuaiyu.com中查看。
今天的题目是涉及到两个点,一个是探索题目背后的数学关系,另一个是巧用排序。
题目:三数之和
给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有和为 0 且不重复的三元组。
注意:答案中不可以包含重复的三元组。
示例 1:
输入:nums = [-1,0,1,2,-1,-4]
输出:[[-1,-1,2],[-1,0,1]]
示例 2:
输入:nums = []
输出:[]
示例 3:
输入:nums = [0]
输出:[]
提示:
0 <= nums.length <= 3000
-105 <= nums[i] <= 105
solution: 双指针+排序
在解题前,我们要剖析题目中的难点和可利用的数学关系。难点在于降低复杂度和去重。三个数我们总不能用O(n^3)的暴力方法求解吧,而且暴力求解碰到重复情况要不就得去比较,要不就得考虑利用hash,复杂程度飙升。而如果我们限定三元组必须是有序的,即first<=second<=third(有序无序都是同一个解),那么就能得到一些数学关系,比如:当first固定时,second变大,third只能变小;third再小不能小于second。那么就可以跳过一些不符合的情况不必讨论。当然,在这个过程中还有一些其他情况可以让我跳过很多讨论。于是乎,排序的必要性就显而易见了,我们需要先将原数组排序,并且在遍历讨论时,一直保持first<=second<=third。同时,由于排序了,重复的数字会排到一起,可以更容易地判断三元组重复情况。
将原数组进行升序排序后,我们进入具体分析。为了不走回头路,我们可以采取先固定first为新数组第一个数,这样问题就变成了,在first的右边找两个数和为partSum=-nums[first]。对first从左往右遍历,复杂度为O(n),而second和third只能在first的左侧找。
如果这一轮的first位置的值等于上一轮的first,那么这一轮找到的所有的三元组一定包含在上一轮找到的三元组中(即first值没变,但找second和third的数中少了一个数)。所以在每轮刚开始遍历first的时候,判断,是否和上一轮的first值相同,相同就不必继续讨论second和third,这样就跳过了很多情况。
如果新一轮的first值是不一样的,那么开始用双指针法寻找second和third。这里是将内循环的O(n^2)转化成O(n)的关键。second指针从first+1处开始从左往右移动,third指针从末尾往前移动。先遍历second(相当于每次移动second后固定second讨论third),在每次遍历second时,当nums[second]+ nums[third]>partSum时,逐渐减小third(third左移);当nums[second]+ nums[third]<=目标值时,third停止左移。因为此时再左移已经不可能满足条件了,这个时候必须把second变大(进入下一轮second循环,使second++)。新的一轮second固定中,third不必又从数组末尾开始找,因为second增大,third一定减少,直接从上一轮third停下的位置继续往左找即可。
注意,在每一轮second++开始,也要判断重复,如果second值和上一轮一样,那么符合的third一定还是一样的,后面的重复情况跳过讨论。
当third和second相遇时遍历second的循环就该被跳出了,移动first进入外层循环的下一轮。在每次固定了first、second,找到符合的third后将三元组记录下来。我们在first和second的固定时都考虑了去重从而减少重复情况讨论,但third是不用去重的,因为找到第一个符合的third的时候,就该去改变second或者first了,不会讨论同一组first和second下的下一个符合的third。
以上是官方和大多数人差不多的思想,但我发现还有一些情况可以被跳过讨论,从而进一步节省程序运行时间。
情况一:整个讨论过程中一直保持nums[first]<=nums[second]<=nums[third],如果其中最小的都大于0,那三者的和必不可能为0,可以直接结束讨论,这个break可以加在外层循环一开始对first进行检查。
情况二:因为nums[second] <= nums[third],如果2nums[second]大于partSum,那么后续不论second右移还是third左移,nums[second]+ nums[third]都不可能为partSum;2nums[third] < partSum时同理,这两种情况也可以直接退出second的循环。
加上上面的两种break后,时间和空间都比原来缩减了一点。
Finally,带有详细注释的代码放在放在我的个人博客http://jinhuaiyu.com/leetcode-3sum/

标签:third,nums,三数,second,三元组,HOT,100,partSum,first
来源: https://blog.csdn.net/qq_40967164/article/details/122428753

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有