LeetCode_15_三数之和
LeetCode 15 (LeetCode 15)
题目描述
给你一个整数数组
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
-10<sup>5</sup> <= nums[i] <= 10<sup>5</sup>
思路整理
- 两数之和问题很好解决,先把数组从小到大排序,然后使用双指针分别从开头和末尾开始移动,如果两个指针对应的值求和大于target,则判断是”大数太大“,则右指针左移,反之则判断”小数太小“,则左指针右移,如果刚好等于target,则把这一对对应的值保存下来,循环结束的条件:1. 左右指针相逢 2. 小数大于target
- 其实题目衍生为N数求和,都可以转化成2数求和,其中第一个数是 n u m 1 num_1 num1,第二个数 n u m 2 = n u m 2 + n u m 3 + . . . + n u m n num_2 = num_2 + num_3 + ... + num_n num2=num2+num3+...+numn,使用递归可以完美解决问题
- 题目中特别要求的:不能出现重复的组合,则让我们需要在移动指针时注意重复的元素,我们可以在每次移动之前保存一个历史记录,通过这个历史记录判断移动后的值是不是重复的。
代码
twoSum
函数
public:vector<vector<int>> twoSum(vector<int>nums, int start, int target){// 初始化左右指针int left = start;int right = nums.size() - 1;vector<vector<int>> ret;// 如果数组长度不足,直接返回空结果if (right - start < 1){return ret;}// 使用双指针法查找两数之和while(left != right){int sum = nums[left] + nums[right];int his_left = nums[left];int his_right = nums[right];// 如果左边的数大于目标值,说明需要减小左边的数,直接跳出循环if (his_left > target){break;}// 如果当前和大于目标值,右指针向左移动if (sum > target){while(left < right && nums[right] == his_right){right --;}}// 如果当前和小于目标值,左指针向右移动else if (sum < target){while(left < right && nums[left] == his_left){left ++;}}// 如果当前和等于目标值,找到一对解,并移动指针else{vector<int> match = {nums[left], nums[right]};ret.push_back(match);while(left < right && nums[right] == his_right){right --;}while(left < right && nums[left] == his_left){left ++;}}}return ret;}
threeSum
函数
vector<vector<int>> threeSum(vector<int>& nums) {// 初始化索引int i = 0;// 对数组进行排序sort(nums.begin(), nums.end());vector<vector<int>> ret;// 使用循环和两数之和的方法找到三数之和while(i < nums.size() - 1){int his_i = nums[i];// 如果当前数大于0,说明后面的数也会大于0,直接跳出循环if (his_i > 0){break;}// 使用两数之和的方法找到剩余两数之和为0-his_i的解vector<vector<int>> two_sum_ret = twoSum(nums, i + 1, (0 - his_i));// 将找到的解添加到结果中for(int j = 0; j < two_sum_ret.size(); j ++){two_sum_ret[j].push_back(his_i);ret.push_back(two_sum_ret[j]);}// 跳过重复的数while(i < nums.size() - 1 && nums[i] == his_i){i ++;}}return ret;}
方法和思想
- 双指针法:在
twoSum
函数中,通过设置左右指针来找到两数之和。这种方法适用于已排序的数组。 - 避免重复:在循环中,通过比较当前数和下一个数来避免重复的结果。
- 排序:在
threeSum
函数中,首先对数组进行排序,这是因为双指针法需要数组是有序的。