leetcode第16题,给定一个数组与一个目标数,找出数组中其中的三个数,这三个数的和要与目标数最接近。html
按惯例先来一次O(n3)的暴力:java
int temp = nums[0]+nums[1]+nums[2]; for(int i=0;i<nums.length;++i) for(int j=i+1;j<nums.length;++j) for(int k=j+1;k<nums.length;++k) { int temp1 = nums[i]+nums[j]+nums[k]; if(Math.abs(temp-target) > Math.abs(temp1-target)) { temp = temp1; if(temp == target) return target; } } return temp;
而后。。。。
受宠若惊啊,直接暴力竟然给过了。。。git
算了,这种暴力笔者本身也看不下去,搞点正经事,暴力的话直接三个循环,每一次都加三个数并判断与target的距离,若是是target直接返回,若是不是则继续,可是...O(n3)啊...
其实这也能够用笔者上一篇文章中提到的双指针法,先对数组排序,而后固定一个数,再用两个指针指向起始端与末端,而后不断向中间逼近。github
Arrays.sort(nums); int t1 = nums[0]+nums[1]+nums[2]; for(int i=0;i<nums.length-2;++i) { int left = i+1; int right = nums.length-1; while(left < right) { int t2 = nums[i]+nums[left]+nums[right]; if(t2 == target) return target; else if(t2 > target) --right; else ++left; if(abs(t1-target) > abs(t2-target)) { t1 = t2; } } } return t1;
首先将数组排序,nums[i]为固定的数,left和right为两个两个指针,根据计算的t2=nums[i]+nums[left]+nums[right]判断与target关系,大于的话向左移动右指针,小于的话向右移动左指针,直到两指针相遇。排序须要O(n log n),两个循环须要O(n2),总的时间复杂度为O(n2).算法
去看了一下第一的那个解答,2ms,确实是快,主要是手写了快排,而后在for里面的循环中用了最大最小剪枝。数组
去查了一下Arrays.sort()的算法,它是几种算法的组合:
(图片来源)spa
只有当数组的长度小于286大于等于47时,才会调用快速排序,所以这里直接手写了一个快排,不管长度多少都直接使用快排。
(原理就很少说了,手写快排仍是稍微有那么一点难度的...).net
public void qs(int [] nums,int l,int r) { if(l < r-1) { int t = l; int ll = l+1; int rr = r-1; int temp; while(true) { while(t < rr && nums[t] < nums[rr]) --rr; if(t < rr) { temp = nums[rr]; nums[rr] = nums[t]; nums[t] = temp; t = rr--; } else break; while(ll < t && nums[ll] < nums[t]) ++ll; if(ll < t) { temp = nums[ll]; nums[ll] = nums[t]; nums[t] = temp; t = ll++; } else break; } qs(nums,l,t); qs(nums,t+1,r); } }
本来两个while循环中的条件是3d
while(ll < rr && ...)
后来出了bug,调了一下,发现范围不对,改为了两个while:指针
while(t < rr && ...) while(ll < t && ...)
最小剪枝就是计算固定的那个数,还有两个最小的数之和,判断与目标值的大小,若是这个最小值大于目标值,那么,结果有多是这个最小值,可是,不多是其余值,由于这个值最小了,并且大于目标值,再与其余值相加的话只会离目标值更远,所以判断是最小值后能够直接break.
最大剪枝也相似,计算最大的两个数与固定的那个数之和,判断与目标值的大小,若是小于目标值,则结果有多是这个最大值,不多是其余值,判断完后也是直接break.
int left = i+1; int right = nums.length-1; if(left < right) { int min = nums[i] + nums[left] + nums[left+1]; if(min > target) { if(abs(min - target) < abs(t1 - target)) t1 = min; continue; } } int max = nums[i] + nums[right] + nums[right-1]; if(max < target) { if(abs(max - target) < abs(t1 - target)) t1 = max; continue; }
一个字,开心。