zoukankan      html  css  js  c++  java
  • LeetCode——15. 三数之和

    给定一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?找出所有满足条件且不重复的三元组。

    注意:答案中不可以包含重复的三元组。

    示例:
    
    给定数组 nums = [-1, 0, 1, 2, -1, -4],
    
    满足要求的三元组集合为:
    [
      [-1, 0, 1],
      [-1, -1, 2]
    ]
    

    https://leetcode-cn.com/problems/3sum/

    排序 + 双指针

    对原数组进行排序,然后开始遍历排序后的数组,这里注意不是遍历到最后一个停止,而是到倒数第三个就可以了。

    这里可以先做个剪枝优化,就是当遍历到正数的时候就 break,为啥呢,因为数组现在是有序的了,如果第一个要 fix 的数就是正数了,则后面的数字就都是正数,就永远不会出现和为0的情况了。

    然后还要加上重复就跳过的处理,处理方法是从第二个数开始,如果和前面的数字相等,就跳过,因为不想把相同的数字fix两次。

    对于遍历到的数,用0减去这个 fix 的数得到一个 target,然后只需要再之后找到两个数之和等于 target 即可。

    用两个指针分别指向 fix 数字之后开始的数组首尾两个数,如果两个数和正好为 target,则将这两个数和 fix 的数一起存入结果中。然后就是跳过重复数字的步骤了,两个指针都需要检测重复数字。

    如果两数之和小于 target,则将左边那个指针i右移一位,使得指向的数字增大一些。同理,如果两数之和大于 target,则将右边那个指针j左移一位,使得指向的数字减小一些,代码如下:

    c++

    class Solution {
    public:
        vector<vector<int>> threeSum(vector<int>& nums) {
            vector<vector<int>> res;
            sort(nums.begin(), nums.end());
            if (nums.empty() || nums.back() < 0 || nums.front() > 0) return {};
            for (int k = 0; k < (int)nums.size() - 2; ++k) {
                if (nums[k] > 0) break;
                if (k > 0 && nums[k] == nums[k - 1]) continue;
                int target = 0 - nums[k], i = k + 1, j = (int)nums.size() - 1;
                while (i < j) {
                    if (nums[i] + nums[j] == target) {
                        res.push_back({nums[k], nums[i], nums[j]});
                        while (i < j && nums[i] == nums[i + 1]) ++i;
                        while (i < j && nums[j] == nums[j - 1]) --j;
                        ++i; --j;
                    } else if (nums[i] + nums[j] < target) ++i;
                    else --j;
                }
            }
            return res;
        }
    };
    

    Java

    class Solution {
        public static List<List<Integer>> threeSum(int[] nums) {
            List<List<Integer>> ans = new ArrayList();
            int len = nums.length;
            if(nums == null || len < 3) return ans;
            Arrays.sort(nums); // 排序
            for (int i = 0; i < len ; i++) {
                if(nums[i] > 0) break; // 如果当前数字大于0,则三数之和一定大于0,所以结束循环
                if(i > 0 && nums[i] == nums[i-1]) continue; // 去重
                int L = i+1;
                int R = len-1;
                while(L < R){
                    int sum = nums[i] + nums[L] + nums[R];
                    if(sum == 0){
                        ans.add(Arrays.asList(nums[i],nums[L],nums[R]));
                        while (L<R && nums[L] == nums[L+1]) L++; // 去重
                        while (L<R && nums[R] == nums[R-1]) R--; // 去重
                        L++;
                        R--;
                    }
                    else if (sum < 0) L++;
                    else if (sum > 0) R--;
                }
            }        
            return ans;
        }
    }
    

    Python

    class Solution:
        def threeSum(self, nums: List[int]) -> List[List[int]]:
    		n=len(nums)
        	res=[]
        	if(not nums or n<3):
            	return []
        	nums.sort()
        	res=[]
        	for i in range(n):
            	if(nums[i]>0):
                	return res
            	if(i>0 and nums[i]==nums[i-1]):
                	continue
            	L=i+1
            	R=n-1
            	while(L<R):
                	if(nums[i]+nums[L]+nums[R]==0):
                    	res.append([nums[i],nums[L],nums[R]])
                    	while(L<R and nums[L]==nums[L+1]):
                        	L=L+1
                    	while(L<R and nums[R]==nums[R-1]):
                        	R=R-1
                    	L=L+1
                    	R=R-1
                	elif(nums[i]+nums[L]+nums[R]>0):
                    	R=R-1
                	else:
                    	L=L+1
        return res
    

    排序 + 双指针+TreeSet

    或者我们也可以利用 TreeSet 的不能包含重复项的特点来防止重复项的产生,那么就不需要检测数字是否被 fix 过两次,参见代码如下:

    c++

    class Solution {
    public:
        vector<vector<int>> threeSum(vector<int>& nums) {
            set<vector<int>> res;
            sort(nums.begin(), nums.end());
            if (nums.empty() || nums.back() < 0 || nums.front() > 0) return {};
            for (int k = 0; k < (int)nums.size() - 2; ++k) {
                if (nums[k] > 0) break;
                int target = 0 - nums[k], i = k + 1, j = (int)nums.size() - 1;
                while (i < j) {
                    if (nums[i] + nums[j] == target) {
                        res.insert({nums[k], nums[i], nums[j]});
                        while (i < j && nums[i] == nums[i + 1]) ++i;
                        while (i < j && nums[j] == nums[j - 1]) --j;
                        ++i; --j;
                    } else if (nums[i] + nums[j] < target) ++i;
                    else --j;
                }
            }
            return vector<vector<int>>(res.begin(), res.end());
        }
    };
    
  • 相关阅读:
    动态代理方案性能对比(转载)
    Java类的初始化
    第78天:jQuery事件总结(一)
    第77天:jQuery事件绑定触发
    第76天:jQuery中的宽高
    第75天:jQuery中DOM操作
    第74天:jQuery实现图片导航效果
    第73天:jQuery基本动画总结
    第72天:jQuery实现下拉菜单
    第71天:jQuery基本选择器(二)
  • 原文地址:https://www.cnblogs.com/wwj99/p/12254960.html
Copyright © 2011-2022 走看看