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