3Sum Closest
题意:给定一个array和一个target,找出三个数的和与target最接近,输出这三个数的和。
解法:
O(N^3):暴力方法,竟然能过,不能理解。
public int threeSumClosest2(int[] num, inttarget) {
intclosest=Integer.MAX_VALUE;
intans=-1;
intn=num.length;
longtime1=System.currentTimeMillis();
for(int i=0;i<n;i++){
for(int j=i+1;j<n;j++){
for(int k=j+1;k<n;k++){
if(Math.abs(target-(num[i]+num[j]+num[k]))<closest){
closest=Math.abs(target-(num[i]+num[j]+num[k]));
ans=num[i]+num[j]+num[k];
}
}
}
}
System.out.println(System.currentTimeMillis()-time1);
returnans;
}
O(N^2logN):将所有数对和排序。O(N^2log(N^2))=O(N^2logN)
枚举任意一个数,找出其在排序好的数对和中,绝对值差距最小的index,尝试index,以及index-1向左开始第一个不包含该数的数对和,以及index+1向右开始第一个不包含该数的数对和。
还有预处理,让每个数字出现的次数不超过3次。
import java.util.ArrayList;
import java.util.Hashtable;
public class Solution136 {
public int threeSumClosest(int[] num, int target) {
long time1=System.currentTimeMillis();
Hashtable<Integer,Integer>hashnum=new Hashtable<Integer,Integer>();
int[] num2=new int[num.length];
int newN=0;
for (int i=0;i<num.length;i++){
if(!hashnum.containsKey(num[i])){
hashnum.put(num[i],1);
}else{
if(hashnum.get(num[i])==1){
hashnum.put(num[i],2);
}else{
if(hashnum.get(num[i])==2){
hashnum.put(num[i],3);
}else{
continue;
}
}
}
num2[newN]=num[i];
newN++;
}
num=num2;
int n=newN;
int tempN=0;
for (int i=0;i<n;i++){
for (int j=i+1;j<n;j++){
tempN++;
}
}
//ArrayList<Integer> twoSum=newArrayList<Integer>();
//ArrayList<Integer> a=newArrayList<Integer>();
//ArrayList<Integer> b=newArrayList<Integer>();
int[] twoSum=new int[tempN];
int[] a=new int[tempN];
int[] b=new int[tempN];
int high=0;
for (int i=0;i<n;i++){
for (int j=i+1;j<n;j++){
//twoSum.add(num[i]+num[j]);
//a.add(i);
//b.add(j);
twoSum[high]=num[i]+num[j];
a[high]=i;
b[high]=j;
high++;
}
}
long time2=System.currentTimeMillis();
//System.out.println(System.currentTimeMillis()-time1);
qsort(twoSum,a,b,0,twoSum.length-1);
//System.out.println(System.currentTimeMillis()-time2);
long time3=System.currentTimeMillis();
int closest=Integer.MAX_VALUE;
int ans=-1;
for (int i=0;i<n;i++){
intpos=binarySearch(twoSum,target,num[i],0,twoSum.length-1);
if(Math.abs(target-(num[i]+twoSum[pos]))<closest){
if(a[pos]!=i&&b[pos]!=i){
closest=Math.abs(target-(num[i]+twoSum[pos]));
ans=(num[i]+twoSum[pos]);
}
}
for (intj=pos+1;j<twoSum.length;j++){
if(a[j]!=i&&b[j]!=i){
if(Math.abs(target-(num[i]+twoSum[j]))<closest){
closest=Math.abs(target-(num[i]+twoSum[j]));
ans=num[i]+twoSum[j];
}
break;
}
}
for (intj=pos-1;j>=0;j--){
if(a[j]!=i&&b[j]!=i){
if(Math.abs(target-(num[i]+twoSum[j]))<closest){
closest=Math.abs(target-(num[i]+twoSum[j]));
ans=num[i]+twoSum[j];
}
break;
}
}
}
//System.out.println(System.currentTimeMillis()-time3);
return ans;
}
privateint binarySearch(int[] twoSum, int target, int number,int x, int y) {
if(twoSum[x]+number==target){
returnx;
}
if(twoSum[y]+number==target){
returny;
}
intmid=twoSum[(x+y)/2];
if(mid+number==target){
return(x+y)/2;
}
if(y-x==1){
if(Math.abs(target-(twoSum[x]+number))<Math.abs(target-(twoSum[y]+number))){
returnx;
}else{
returny;
}
}
if(mid+number>target){
returnbinarySearch(twoSum,target,number,x,(x+y)/2);
}else{
returnbinarySearch(twoSum,target,number,(x+y)/2,y);
}
}
privatevoid qsort(int[] twoSum, int[] a,
int[]b, int x, int y) {
inti=x;
intj=y;
intt=twoSum[(i+j)/2];
do{
while(twoSum[i]<t){
i++;
}
while(twoSum[j]>t){
j--;
}
if(i<=j){
inttemp=twoSum[i];
twoSum[i]=twoSum[j];
twoSum[j]=temp;
temp=a[i];
a[i]=a[j];
a[j]=temp;
temp=b[i];
b[i]=b[j];
b[j]=temp;
i++;
j--;
}
}while(i<=j);
if(j>x){
qsort(twoSum,a,b,x,j);
}
if(i<y){
qsort(twoSum,a,b,i,y);
}
}
}
O(N^2):参考http://www.cnblogs.com/obama/p/3275574.html
将数组排序之后,枚举每个数,寻找剩下数中与target最接近的两个数,这个过程可以通过与twoSum很类似的方法进行寻找。由于数组有序,left和right分别指向最左侧和最右侧,当a[left]+a[right]>target时,right—否则left++。
正确性证明:left和right一定有一个先触及最优区间边界,当先触及左边界时,此时一定a[left]+a[right]>target,否则a[left]+a[right-1]<=a[left]+a[right],与target差的很远,那么与假设相悖,同理可证