15-3Sum-找和为0的所有三元素解组合

xiaoxiao2021-02-28  30

给定一个含n个元素的序列,找出其中所有和为0的三元素解集合。

例子:

For example, given array S = [-1, 0, 1, 2, -1, -4], A solution set is: [ [-1, 0, 1], [-1, -1, 2] ]

首先一点,暴力穷尽搜索能够很简单的解决问题,但是时间超过了及格线,所以一定要用些优化的算法。解决这道题的基本思路如下:

(1)将整个序列用sort方法进行升序排列。本例中,经过sort排列之后的序列为[-4,-1,-1,0,1,2];

(2)固定第一个元素,后面的两个元素用夹逼准则判定。如固定第一个数为-4记为i,在-4之后余下的数列中,取最低位-1记为low,取最高位2记为high。我们要求一组解i+low+high=0,则可以将整个题目简化为判断low+high是否等于-i(记为sum);

(3)在low位和high位相遇之前,一直执行循环。在此循环体中,low和high需要不断移动调整:

    如果low+high>sum,则说明两个数不够小,则让high位向前移动一格;

    如果low+high<sum,则说明两个数不够大,则让low位往后移动一格;

    如果low+high=sum,那么这一组解就可以存储在结果表里,循环继续,low位后移一位,high位前移一位。

这里需要特别注意!对于low+high=sum情况,low位和high位的移动一位,应该是指移动一个数值,即要移动到下一个不相等值的末端,比如[...,-2,-1,-1,0,...]中,若low原本在-2,后移一位的概念则是将low移动到0之前的那一个一个-1上;若high原本在0,前移一位的概念则是将high移动到-2之后的那个-1上。这样做的目的,是避免重复解组合,一起往中间夹逼就不需要我解释了吧?

而在未解出答案的情况,即low+high不等于sum的时候,只需要一格一格的移动就行了,为什么不直接移动到相同数字序列的端点呢?因为这样会造成一种解组合的缺失,这种组合中包含了多个相同的元素,只有让low与high一个个移动才能同时进入这一段相同数字中,取到这个解。一旦取到了这种解的话,实际上这次的循环也结束了。

下面贴代码实现,else if模块的理解可参见上述文字表达:

public static List<List<Integer>> threeSum2(int[] nums) { Arrays.sort(nums); List<List<Integer>> res = new LinkedList<>(); for(int i = 0; i < nums.length - 2; i++) { if( i == 0 || nums[i] != nums[i-1]) { int low = i+1, high = nums.length-1, sum = -nums[i];//sum指代的是另外两个数所需的和 while( low < high ) { if( nums[low] + nums[high] < sum ) low++; //检查low是否需要后移,一格一格的移动 else if( nums[low] + nums[high] > sum ) high--; //检查high是否需要前移,一格一格的移动 else { res.add( Arrays.asList( nums[i], nums[low], nums[high] )); //找到了解,写入结果表 while( low<high && nums[low]==nums[low+1] ) low++; //跳过相同的数字,移动到相同数字序列的最末端 while( low<high && nums[high]==nums[high-1] ) high--; //跳过相同的数字,移动到相同数字序列的最前端 low++; high--; //真正让low与high一起夹逼,向内收拢各自跳到一个新的数字 } } } } return res; }

说的啰嗦了点,也为了自己方便理解吧。leetcode讨论区的高票答案走了个弯路,把我绕进去半天,结果看懂了他的方法之后改进了一下if else分支,算法速度就得到了极大提升,见下图:

转载请注明原文地址: https://www.6miu.com/read-2629332.html

最新回复(0)