LeetCode中3Sum(a+b+c=0)的解法与证明

这道题。开始的时候想用hash表来做,可是需要排除重复就会造成O(n3)的复杂度,也可能是我这个方法没弄好。想了很久,也没有做出来,在网上看了别人的解放,遂恍然大悟,但是网上没有它解法的证明,所以在这里写下我的证明,以来以后看看。

原题目为:


唯一性:

这道题最关键的就是,我们需要保证每次搜索得到的结果都是不重复得到的,这样我们可以想一个办法,保证a,b,c有序,这样我们可以使a<=b<=c;这样就可以得到唯一的一组了,在其他的情况下不会得到重复的解。

原式子可以化成b+c=-a;我们对于数组中的每一个数,进行循环,使b+c等于一个固定值。并且只在当前的a后面的数中搜索,从而保证a是最小的。下面我们还要证明完备性,也就是,如果有满足条件的a,b,c,那么在后面就一定能找到,这是显然的。但是在算法中,我们是要这样做的,首先b为a后的第一个数,c为最后一个数。当b+c小于-a时,我们把b向右一个位置(当然重复数字可以跳过),当b+c大于-a时,我们把c向左一个位置。此时我们需要证明的就是,这种移动方法下,只要有任何一组a,b,c满足条件,这种方法就一定能够找到。

下面证明完备性:

用反证法证明:

如果我们要得不到需要的解,也就是正确的解会被跳过。也就是,左跳或者右跳跳大了,把正确的那个解的其中一项跳过了。首先证明左边,假设b是正确解的一项了,而此时b+c<-a,此时把b向右跳一格而跳过了正确的答案,假设有一个c2使得b+c2=-a,但是显然矛盾,c2显然比c小,则b+c2只会小于-a,当然可能有人会问如果正确的c2比c大呢?,则我们需要证明正确的c也不会被跳过。

一样的如果b+c>-a,右边的c是正确解,会被跳过,则正确的b2比b会大,b2+c的正确解只会比-a要大,显然与b2+c为正确解矛盾。

我们在不管是跳过b还是c,都是只动一边,所以只会要么可能有一次跳过b,或者c,不可能同时的去跳过,所以单方的跳过是永远不会被跳过的。所以不论是b还是c都不会被跳过。所以就证明了完备性。

比较抽象,需要仔细的思考。

下面是代码:

public static List<List<Integer>> threeSum(int[] num) {
		num=qsort(num,0,num.length-1);
		List<List<Integer>> list=new ArrayList<List<Integer>>();
		List<Integer> tmplist=new ArrayList<Integer>();
		int k=0;
		for(int i=0;i<num.length;i++){
			tmplist=twoSum(num,-num[i],i+1,num.length-1);
			for(int j=0;j<tmplist.size();j=j+3){
				List<Integer> tmplist2=new ArrayList<Integer>();
				tmplist2.add(0, tmplist.get(j));
				tmplist2.add(1, tmplist.get(j+1));
				tmplist2.add(2, tmplist.get(j+2));
				list.add(k++,tmplist2);
			}
			while(i<num.length-1&&num[i]==num[i+1]){
				i++;
			}
		}
		return list;
	}
	public static List<Integer> twoSum(int num[],int a,int i,int j){
		List<Integer> tmplist=new ArrayList<Integer>();
		int k=0;
		while(i<j){
			if(num[i]+num[j]<a){
				i++;
				continue;
			}
			else if(num[i]+num[j]>a){
				j--;
				continue;
			}
			else{
				tmplist.add(k++,-a);
				tmplist.add(k++,num[i]);
				tmplist.add(k++,num[j]);

			}
			while(i<j&&num[i]==num[i+1]){
				i++;
			}
			while(i<j&&num[j]==num[j-1]){
				j--;
			}
			i++;
			j--;
		}
		return tmplist;
	}
	public static int[] qsort(int num[],int start,int end){
		int i,j;
		i=start;j=end;
		int k=0;
		if(num.length>0)
			k=num[start];
		while(i<j){

			while(i<j&&num[j]>=k){
				j--;
			}
			if(i<j){
				int tmp=num[i];
				num[i]=num[j];
				num[j]=tmp;
				}
			while(i<j&&num[i]<k){
				i++;
			}
			if(i<j){
				int tmp=num[i];
				num[i]=num[j];
				num[j]=tmp;
				}

		}
		if(i-start>1){
			num=qsort(num,start,i-1);}
		if(end-i>1){
			num=qsort(num,i+1,end);}
		return num;
	}

在这段代码中我们要注意,所有的判断要记得判断是否会溢出,i<j这种要记得放到判断的前面。

综上所述,对于b+c等于一个固定的值的问题,用上面这个方法对一个有序的序列进行搜索,只要这个值存在,那么这样搜索就一定能够得到这个值。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值