文章目录
- 题目描述
- 题目链接
- 题目难度——中等
- 方法一:暴力(参考)
- 代码/Python 参考
 
- 方法二:哈希
- 代码/Python参考
 
- 方法三:排序+双指针
- 代码/Python
- 代码/C++
 
- 总结
 
题目描述
龙门阵:这个n个数之和是不是有什么深意啊,好几个这种题,这个题虽然标的是中等,但我咋觉得考思维考得像困难一样。这个题跟两数之和还有点想,没看过两数之和的伙伴可以看我这篇LeetCode题目笔记——1.两数之和
前两种方法都会超时,仅供参考和自己记录,小伙伴可以跳过,直接到方法三。
给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != j、i != k 且 j != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。请你返回所有和为 0 且不重复的三元组。
注意:答案中不可以包含重复的三元组。
示例 1:
输入:nums = [-1,0,1,2,-1,-4]
 输出:[[-1,-1,2],[-1,0,1]]
 解释:
 nums[0] + nums[1] + nums[2] = (-1) + 0 + 1 = 0 。
 nums[1] + nums[2] + nums[4] = 0 + 1 + (-1) = 0 。
 nums[0] + nums[3] + nums[4] = (-1) + 2 + (-1) = 0 。
 不同的三元组是 [-1,0,1] 和 [-1,-1,2] 。
 注意,输出的顺序和三元组的顺序并不重要。
  
示例 2:
输入:nums = [0,1,1]
 输出:[]
 解释:唯一可能的三元组和不为 0 。
 
 示例 3:
输入:nums = [0,0,0]
 输出:[[0,0,0]]
 解释:唯一可能的三元组和为 0 。
提示:
- 3 <= nums.length <= 3000
- -105 <= nums[i] <= 105
题目链接
题目难度——中等
方法一:暴力(参考)
根据提示,数据的长度最多也只有3000,所以我们可以尝试暴力一下,但是注意,虽然理论上可以暴力,但是三重循环的话,最坏情况下将会执行30003 = 2.7 * 1010 次比较,同时答案也可能会有这么多,因此其时间和空间复杂度都非常高,到了这个量级就不能暴力了,而且在这过程中还要考虑到元素的去重问题,所以仅限于在本地跑着玩。
代码/Python 参考
class Solution:
    def threeSum(self, nums: list[int]) -> list[list[int]]:
    	res = []
    	n = len(nums)
    	for i in range(n):
    		for j in range(n):
    			for k in range(n):
    				if i != j and i != k and k != j and nums[i] + nums[j] + nums[k] == 0:
    					 res.append([nums[i], nums[j], nums[k])
    	# 去重略
    	return res
    	
方法二:哈希
前面说过这题和两数之和有点像,所以我们还可以把问题转换为对数组中的每个x,将其作为target,求剩余元素中的两数之和问题。如果用两数之和中哈希表的做法,时间复杂度可以降到O(N2),但同时还要考虑到如何进行元素去重的问题,所以仍然可能会超时。
代码/Python参考
class Solution:
    def threeSum(self, nums: List[int]) -> List[List[int]]:
        # 转换为两数之和问题
            res = defaultdict(int)
            ans = []
            pos = dict()
            for i, x in enumerate(nums):
                target = 0 - x
                for j, y in enumerate(nums):
                    if i != j:
                        tmp = target - y
                        if tmp in pos and pos[tmp] != j and pos[tmp] != i:
                            res[tuple(sorted([x, y, tmp]))] += 1
                        else:
                            pos[y] = j
        	# 用字典的键实现去重
            return list(map(list, res.keys()))
方法三:排序+双指针
前面提到的去重问题,我觉得也是本题的考点之一,那我们让每个答案三元组(a,b,c)都是非递减的,同时三元组整体也是非递减的,那不就能保证答案不会重复了吗,所以我们先排序原数组,同时用上一个方法的思想,将每个x都视为一次target,然后使用双指针的方法找到答案。具体的解释见代码里:
代码/Python
class Solution:
    def threeSum(self, nums: List[int]) -> List[List[int]]:
        res = []
        nums.sort()
        n = len(nums)
        pre = 1000001
        for i in range(n):
            if nums[i] == pre:      # 保证不重复
                continue
            if nums[i] > 0:         # 排序后,a>0,后面不可能有b,c使得a+b+c=0
                break
            pre = nums[i]
            a = nums[i]
            target = -a         # 每个target=-a
            p2 = n - 1          # 后指针
            p1pre = 1000001
            for p1 in range(i + 1, n - 1):      #前指针
                if p1pre == nums[p1]:           # 保证不重复
                    continue
            
                b = nums[p1]
                p1pre = b
                while p1 < p2:
                    c = nums[p2]
                    if b + c < target:  # 保证前指针的数先找完
                        break
                    elif b + c > target:    
                        p2 -= 1
                    else:
                        res.append([a, b, c])
                        p2 -= 1
                        break           # 找到一个答案,退出while,让前指针后移
    
        return res

代码/C++
class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        vector<vector<int>> res;
        sort(nums.begin(), nums.end());
        int n = nums.size(), pre, p1pre, i, target, a, b, c, p1, p2;
        pre = 1000001;
        for(i = 0; i < n; i++){
            if(nums[i] == pre){
                continue;
            }
            if(nums[i] > 0){
                break;
            }
            a = nums[i];
            pre = a;
            target = -a;
            p2 = n - 1;
            for(p1 = i + 1; p1 < n; p1++){
                if(p1pre == nums[p1]){
                    continue;
                }
                b = nums[p1];
                p1pre = b;
                while(p1 < p2){
                    c = nums[p2];
                    if(b + c < target){
                        break;
                    }
                    else if(b + c > target){
                        p2--;
                    }
                    else{
                        res.push_back({a, b, c});
                        p2--;
                        break;
                    }
                }
            }
        }
        return res;
    }
};
总结
前两种方法都是O(N3),第三种优化后是O(N2),空间上都是O(1)。



















