Skip to content
本页目录

0015-三数之和

题目描述

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

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

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

示例 1:

输入:nums = [-1,0,1,2,-1,-4]
输出:[[-1,-1,2],[-1,0,1]]

示例 2:

输入:nums = []
输出:[]

示例 3:

输入:nums = [0]
输出:[]

提示:

0 <= nums.length <= 3000
-10^5 <= nums[i] <= 10^5

思路 : 暴力循环,超时

三重循环,字典表去重,时间复杂度 O(n^3) 提交会超时

参考代码

csharp
public class Solution {

	private string getKey(int a, int b, int c){
		int temp;
		if(a > b){
			temp = a;
			a = b; 
			b = temp;
		}
		if(a > c){
			temp = a;
			a = c; 
			c = temp;
		}
		if(b > c){
			temp = b;
			b = c;
			c = temp;
		}
		return a.ToString() + b.ToString() + c.ToString();
	}

    public IList<IList<int>> ThreeSum(int[] nums) {
    	Dictionary<string,string> dict = new Dictionary<string,string>();
    	IList<IList<int>> result = new List<IList<int>>();

    	for(int i=0; i<nums.Length -2; i++){
    		for (int j=i+1; j<nums.Length -1; j++){
    			for(int k=j+1; k<nums.Length; k++){
    				if(nums[i] + nums[j] + nums[k] == 0){
    					string key = getKey(nums[i],nums[j],nums[k]);
    					if(!dict.ContainsKey(key)){
    						dict.Add(key,"1");
    						List<int> arr = new List<int>();
    						arr.Add(nums[i]);
    						arr.Add(nums[j]);
    						arr.Add(nums[k]);
    						result.Add(arr);
    					}
    				}
    			}
    		}
    	}

    	return result;

    }
}

思路2:排序+双指针【官方:TODO】

将数组排序

定义三指针,分别为开始指针,结束指针,中间指针

循环数组,固定第一个数[i]

如果 nums[i] + nums[left] + nums[right] > 0 那么 end 前移

如果 nums[i] + nums[left] + nums[right] < 0 那么 mid后移

中间状态 num[left] 从 i+1 移动到 length-1

注意:第一个数字也需要去重

注意:加入结果集去重后, left++, right-- 否则进入死循环

参考代码

csharp
public class Solution {
    public IList<IList<int>> ThreeSum(int[] nums) {
    	Array.Sort(nums);
    	IList<IList<int>> result = new List<IList<int>>();
        
        for(int i=0; i<nums.Length - 2; i++){
            //去掉第一个数字的重复数据
            if(i>0 && nums[i] == nums[i-1]){
                continue;
            }
            int left = i + 1;
            int right = nums.Length - 1;
           	while(left < right){
         		int sum = nums[i] + nums[left] + nums[right];
                if(sum > 0){
                    right--;
                }
                else if(sum < 0){
                    left++;
                }
                else{ //加入结果集
                    List<int> list = new List<int>(){nums[i],nums[left],nums[right]};
                    result.Add(list);
                    //去除重复数字
                    while(left < right && nums[right-1] == nums[right]){
                        right--;
                    }
                    while(left < right && nums[left+1] == nums[left]){
                        left++;
                    }
                    left++;
                    right--;
                }
            }
        }
        return result;
    }
}

思路3,转化为两数之和

  1. 固定第一个数
  2. 将后面的计算转化为两数之和
  3. 注意去除重复的方法 第一个数 和 第二个数处理方式不同
csharp
public class Solution {
    public IList<IList<int>> ThreeSum(int[] nums) {
    	Array.Sort(nums);
    	IList<IList<int>> result = new List<IList<int>>();
    	int start = 0;
    	int end = nums.Length-1;
    	while(start <= end - 2){
    		//如果第一个数就大于0,则后面都大于0,直接返回
    		if(nums[start] > 0){
    			break;
    		}
    		//如果数字已经重复计算过,则直接跳过
    		if(start > 0 && nums[start] == nums[start-1]){
    			continue;
    		}
    		//确定了第一个数之后,将后面的数作为两数之和处理
    		Dictionary<int,int> dict = new Dictionary<int,int>();
    		for(int j=start+1; j<=end; j++){
                Console.WriteLine("j={0}",j);
    			int sum = 0 - nums[start] - nums[j];
    			if(dict.ContainsKey(sum)){
    				List<int> arr = new List<int>(){nums[start],dict[sum],nums[j]};
                    result.Add(arr);
                    //不能重复计算 第二个数 , 如 测试用例[0,0,0,0]
                    while(j < nums.Length-1 && nums[j] == nums[j+1]){
                    	j++;
                    }
    			}
    			else{
                    if(!dict.ContainsKey(nums[j])){
                        //Console.WriteLine("add:{0}",nums[j]);
                        dict.Add(nums[j],nums[j]);
                    }
    			}
    		}
    		start++;
    	}
    	return result;
    }
}

复习: 需要再次复习写法

csharp
public class Solution {
    //要去取重复,先排序,则当前数字只处理一次
    //固定第一个数字之后,加入hash表,后续用两数之和和hash表做校验,是否满足条件
    //固定第一个数字之后,后面两个指针分别指向开始和最后一个数,依次往内收缩

    public IList<IList<int>> ThreeSum(int[] nums) {
        List<IList<int>> result = new List<IList<int>>();
        Array.Sort(nums);

        for(int i=0; i < nums.Length; i++){
            if(i>0 && nums[i] == nums[i-1]){
                //如果发现相同的数字,直接跳过
                continue;
            }

            int target = -nums[i];
            Dictionary<int,int> dict = new Dictionary<int,int>();

            for(int j = i+1; j<nums.Length; j++){
                
                int key = target - nums[j];
                if(dict.ContainsKey(key)){
                    //加入结果集
                    List<int> list = new List<int>();
                    list.Add(nums[i]);
                    list.Add(key);
                    list.Add(nums[j]);
                    result.Add(list);

                    while(j < nums.Length-1 && nums[j] == nums[j+1]){
                        //如果发现第二个数字相同,直接跳过
                        j++;
                    }
                }
                else{
                    if(!dict.ContainsKey(nums[j])){
                        dict.Add(nums[j],j);
                    }
                }
            }

        }
        return result;

    }
}

Released under the MIT License.