引言
分享一道朋友面试遇到的算法题,这道题是力扣上的原题,还是有一定难度的,但是这类题都有简单的版本,我们将由浅入深,从易到难分析这类题的套路。如下:
分析: 题意就是给定一个整数数组和一个target,要求从数组中找到4个数,使得这4个数的和等于target,求出数组中所有不同的符合条件的四元组。
- 要求的是所有符合条件的四元组,而不是求符合条件的四元组个数或者有没有符合条件的四元组,所以我自然可以想到去暴力搜索,我去搜索所有的组合,然后筛选出符合条件的组合,添加到结果集。
这里求4元组,所以我们可以用4个for循环,这代码写着比较简单我就不写了。还有一种搜索就是回溯,一种利用递归更优雅的搜索。
- 既然是回溯的本质也是一种暴力搜索,那么为了提高搜索效率,自然会想到能不能进行剪枝(就是利用一些条件,提前终止一些无意义的搜索)来提高效率。
常用的剪枝小tip:
因为要求答案中不可以包含重复的四元组,那么常用的去重技巧就是把数组元素进行排序。
代码如下所示:
回溯代码基本都是这样一个模板,在代码中我也进行了注释。由于回溯不是本文的重点,所以就不过多解释了。
Two Sum I
首先我们来看一个简单的2sum问题,如下所示:
分析:2sum问题是一个很经典的问题,给定一个整数数组和target,找出两个元素,使他们和为target,返回两个元素的下标。
解法一:首先同上,可以用两个for循环暴力搜索所有组合。当找到符合条件的二元组时直接返回他们的下标。这种解法的时间复杂度是O(n^2^),空间复杂度是O(1)。
解法二:利用hashmap使时间复杂度降到O(n),核心思想就是用数组的元素作为Key,元素对应的下标作为value。遍历数组,对每一个元素,判断target-当前元素是否在Map中,如果存在说明数组中存在两数和为target的二元组,如果不存在,则把当前元素放入map中。
Two Sum II
现在我们把2sum I的题目改一改,改为找出对应的两个数,而不是其下标,同样每种输入只会有一个答案。
分析:给定一个整型数组nums和target,从数组中找出两个数,使得两数之和等于target,返回这两个数。
同样上面的两种方法都能解决这个问题:
其中解法一时间复杂度O(n^2^),空间复杂度O(1);解法二中需要遍历数组,所以时间复杂度O(n),空间上,由于最坏情况下可能要存储整个数组元素,所以空间复杂度O(n)。
现在我们并不需要返回下标,那有其他解法吗?当然,这就我们今天要讲的重点:双指针。
思路:先将数组排序(升序),然后使用两个指针,一个指针left指向数组第一个元素,一个指针right指向最后一个元素。求出当前nums[left] + nums[right]的和sum,如果sum > target,说明需要使sum更大,此时只需要移动左指针,使得sum变大,如果sum > target,此时只需要移动右指针,使得sun变小;如果sum=target,则此时两个指针所指的元素就是我们需要的。由于在遍历数组元素前要先排序,所以时间复杂度是O(nlogn),空间复杂度是O(1)。 代码如下:
Three Sum
介绍完2sum问题后,我们接着来看3sum问题。题目如下所示:
先来看看和2sum的区别:
- 2sum是两数之和,3sum是三数之和;
- 2sum中是某个target,3sum中target固定为0;
- 2sum中答案唯一,3sum答案需要去重。
分析:针对于区别一没啥说的;区别二,0可以看成是特殊的target;区别三,需要去重一般的套路就是排序或者hash嘛。2sum我们都会做了,那怎么才能把3sum转换为2sum呢? 一个重要的思想就是:先固定一个数,假设为a,那么题目就变成了,从剩下的数组元素中找到两个元素,其和为target-a,即0-a,发现了吗,当你固定一个数的时候剩下的就变成了2sum问题。
至于去重,可以先将数组排序,有两个地方需要注意:
- 当我们需要固定一个数的时候,如果这个数不是数组第一个元素,且和前一个元素一致的时候,就不需要再继续了;
- 当我们找到一组解时,看看左右两个指针附近有没有与他们相等元素,如果存在,则也不是我们需要的。
综上,代码如下:
时间复杂度O(n^2^),空间复杂度O(1)
Four Sum
看完三数之后,我们现在再回过头来看看最初的4sum问题。
与3sum如出一辙,我们想想怎么变成3sum,同样先固定一个数,就能变成3sum问题,那再固定一个数,是不是就可以变成2sum问题。和3sum唯一不同的地方就是进行去重时,第二层借助了hash,因为本身3sum变2sum时,固定的3sum已经不是数组中第一个元素了。所以不能用比较前一个数的方式来去重。代码如下所示:
时间复杂度:O(n^3^),空间复杂度O(n)。
n sum 问题
如果当我们遇到nsum问题时,是不是也同样会解决了,先将nsum变为(n-1)sum,再将(n-1)sum变成(n-2)sum,最终变成2sum。