剑指offer57和为s的两个数
题目要求
输入一个递增排序的数组和一个数字s,在数组中查找两个数,使得它们的和正好是s。如果有多对数字的和等于s,则输出任意一对即可。
示例 1:
输入:nums = [2,7,11,15], target = 9
输出:[2,7] 或者 [7,2]
示例 2:
输入:nums = [10,26,30,31,47,60], target = 40
输出:[10,30] 或者 [30,10]
限制:
1 <= nums.length <= 10^5
1 <= nums[i] <= 10^6
普通解法
核心思路
由于数组递增有序,且寻找的是两个数。可以使用双指针分别指向区间的第一个和最后一个元素。循环进行判断,进行相应的移动:
当 一个最大的数加最小的都比target大,所以最大的数肯定不是寻找的两个数之一(加其余任意一个数都会比当前值还大)故舍弃,右指针向左移;同样最小数加最大的比target还要小,所以最小数舍弃,左指针右移。指针碰撞时循环结束。
具体代码
//
var twoSum = function(nums, target) {
var i=0;
var j=nums.length-1;
var sum=0;
while(i<=j){
sum=nums[i]+nums[j];
if(target>sum){
i+=1;
}
else if(target<sum){
j-=1;
}else if(target==sum){
var ans=[nums[i],nums[j]];
return ans;
}
}
return -1;
};
优化解法
核心思路
使用双指针的思路不变,但进行循环前首先使用二分查找对双指针的右指针开始位置进行精确(排除掉数组单个值比target大的元素),进一步缩小循环区间。
- 限制:比较适用于正整数,如果存在负数,可能会出现错误
具体代码
优化:使用二分查找和双指针
/**
* @param {number[]} nums
* @param {number} target
* @return {number[]}
*/
var twoSum = function(nums, target) {
var left=0;
var right=findRight(nums,target);
while(left<=right){
var sum=nums[left]+nums[right];
if(sum<target){
left+=1;
}else if(sum>target){
right-=1;
}
else{
var ans=[nums[left],nums[right]];
return ans;
}
}
return false;
};
//使用二分法寻找双指针上限
function findRight(nums,target){
var start=0;
var end=nums.length-1;
var mid
while(start<end){
mid=parseInt(start+(end-start)/2);//确保mid为整数
if(nums[mid]>target){
end=mid-1; //求和为s的两个数,而一个数nums[mid]已经比target大,故区间缩小到nums[mid]左侧;
}else{
start=mid+1; //注意函数的返回值是start做为双指针的上限(双指针第一次计算应是nums[0]+nums[start])
} //小于target的任何一个数均在可能出现结果的区间,nums[mid]<=target,所以start后移,继续寻找临界点
}
return start;
}