[LeetCode] 15. 3Sum

原题链接: https://leetcode.com/problems/3sum/

1. 题目描述

Leetcode 第15题

输入:一组整数数组。
输出:在这个数组中,有a、b、c三个数满足a+b+c=0,把所有满足这个条件的[a,b,c]找出来,并且去掉重复的。

Example:
Given array nums = [-1, 0, 1, 2, -1, -4],

A solution set is:
[
[-1, 0, 1],
[-1, -1, 2]
]

2. 解题思路

Java中List的用法

List是一个有序的集合,允许存储项的值为空,也允许相等值的存储项。

List<String> ex=new ArrayList<>();
			//增加用add()
            ex.add("aa");   //索引为0 
            ex.add("bb");    //索引为1
            ex.add("cc");    //索引为2
            ex.add("dd");   //索引为3
            ex.add("ee");    //索引为4
             
            ex.remove(3);   //.remove(index)
            ex.remove("cc");     //.remove(Object o)
             
            String per="";
            per=person.get(1);//.get(index)
            System.out.println(per);  
             
            for (int i = 0; i < ex.size(); i++) {
                System.out.println(ex.get(i));  //.get(index)
            }

这个题的主要思路是这样的:
首先遍历数组,找到一个a,
然后在数组中寻找满足b + c = 0 - a 的b、c,
怎么寻找满足条件的b和c呢 ?一个比较好的方法是二分。

假定我们有一个如下的排序的数组:在这里插入图片描述
我们需要找出两个数b、c,让b+c为某个特定的值。而且符合条件的b、c有可能不只一对。假定在这里,我们要求使得b + c = 9, 那么我们可以这样来看:

如果我们取这个目标数字9的一半,也就是4.5,用它来划分这个数组的话,它左边的数字表示比它小的,它右边的数字表示比它大的。那么这里对应的一个划分则如下图所示:
在这里插入图片描述
如果我们需要去找和为9的两个数,那么肯定有一个数在前面smaller部分,有一个数在后面bigger部分。假设在前面的数是b,后面的数是c。

如果存在两个数字它们的和等于目标数字。它们肯定是一个大于我们给定数字的一半而另一个小于它的一半。我们可以先计算这两个数字的和,用它们来比较目标数字。如果大于目标数字,则右边的下标往左移一步,相当于将它们的和调小一点,如果小于的话,则移动左边的下标往右一步。这样我们就可以通过这样一次循环找到所有符合条件的数字了。

我们一开始从数组的开头和结尾去找,
b=1,c=7,b+c=8,8<9,实际的和小于目标数字,我们就让b向右移动一步,b=2。
b=2,c=7,b+c=9,符合要求,算是找到一对了,然后b向右,c向左各移动一步,继续找。
b=3,c=6,b+c=9,符合要求,继续找
b=4,c=5,b+c=9,符合要求,继续找
b=5,c=4,b>c,此时跳出。

按照前面的分析,我们对数组排序用的时间为O(NlogN),我们匹配两个数字的和,用的时间为O(N),但是这一步外面还嵌套一个遍历数组找a的循环,所以它的时间复杂度为O(N*N)。这样, 整个问题的时间复杂度为O(N^2)

2-sum, 3-sum的问题在一定的问题规模下,是可以找到有效的方法来解决的。我们常用的办法就是首先针对这个数组排序,然后再去通过二分法查找目标值。还有一种就是通过从排序后的数组两头来凑这个给定的目标值。基本上所有这些问题的变体都可以通过这几种方法来解决。而且这些解决方法的时间复杂度还是相当客观的,对于2-sum来说它达到了O(NlogN),对于3-sum来说,它可以达到O(N^2)。以前也有人总结过,说碰到这种和数组的东西打交道时,如果实在没什么思路,干脆就不管三七二十一给它排个序,也许思路就有了,看来还是有点道理的。

注:以上方法及图片来自 https://blog.csdn.net/iteye_12150/article/details/82583581


实现代码

class Solution {
    List<List<Integer>> res = new ArrayList<List<Integer>>();
    
	public List<List<Integer>> threeSum(int[] nums) {
		Arrays.sort(nums);//首先对数组进行排序
		int length = nums.length;
		for(int i = 0; i<length ;i++) {
			erfen(i,0-nums[i],nums);//二分寻找b、c
            while(i < nums.length - 2 && nums[i] == nums[i+1])
                      ++i;
            /*上面这一步是为了排除重复
          	比如-1,-1, 0,1 如果没有上面这一步
          	就会有[-1,0,1]、[-1,0,1]重复出现    
			*/
		}
		return res;
    }
    
 
	public void erfen(int index,int sum,int[] nums) {
		int l = index+1;
		int r = nums.length-1;
		while(l<r) {
			if(nums[l] + nums[r]<sum) {
				l++;
			}
			else if (nums[l] + nums[r] > sum) {
				r--;
			}
			else {	
				List<Integer> resson = new ArrayList<>(3);
                resson.add(nums[index]);
				resson.add(nums[l]);
				resson.add(nums[r]);
				
				res.add(resson);
				//相同的值就可以跳过
                while(l < r && nums[l+1] == nums[l]) 
                    l++;
                while(l < r && nums[r-1] == nums[r]) 
                    r--;

                r--;
				l++;
			}
		}
	}   
}

3. 参考材料

https://blog.csdn.net/iteye_12150/article/details/82583581

4. n-Sum专题相关题目

1. Two Sum
15. 3Sum
16. 3Sum Closest
18. 4Sum
167. Two Sum II - Input array is sorted
560. Subarray Sum Equals K
653. Two Sum IV - Input is a BST

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值