1. 程式人生 > >LeetCode中3Sum(a+b+c=0)的解法與證明

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等於一個固定的值的問題,用上面這個方法對一個有序的序列進行搜尋,只要這個值存在,那麼這樣搜尋就一定能夠得到這個值。