来自力扣上 剑指offer - 57 题
题目描述
输入一个递增排序的数组和一个数字 s
,在数组中查找两个数,使得它们的和正好是 s
。如果有多对数字的和等于 s
,则输出任意一对即可。
示例
输入:nums = [2,7,11,15], target = 9
输出:[2,7] 或者 [7,2]
输入:nums = [10,26,30,31,47,60], target = 40
输出:[10,30] 或者 [30,10]
思路
法一:双指针
sum<target, start++
sum>target, end--
法二:双指针+二分法
代码
1.双指针
/**
* @param {number[]} nums
* @param {number} target
* @return {number[]}
*/
var twoSum = function(nums, target) {
let start = 0
let end = nums.length - 1
let sum = nums[start] + nums[end]
while(sum != target) {
if(sum > target) {
end--
} else if (sum < target) {
start++
}
sum = nums[start] + nums[end]
}
return [nums[start], nums[end]]
};
用时84.8ms,消耗内存55.9MB
2.双指针+二分法
/**
* @param {number[]} nums
* @param {number} target
* @return {number[]}
*/
var twoSum = function(nums, target) {
let midTarget = parseInt(target / 2)
let left = 0
let right = nums.length - 1
while(left <= right) {
let mid = parseInt((left+right)/2)
if(nums[mid] <= midTarget) {
left = mid + 1
} else {
right = mid - 1
}
}
while(right>= 0 && left <= nums.length-1) {
if(left == right) {
left--
} else if(nums[left]+nums[right] > target) {
left--
} else if(nums[left]+nums[right] < target) {
right++
} else if(nums[left]+nums[right] == target) {
return [nums[left],nums[right]]
}
}
};
用时79.2ms,消耗内存56.04MB
提升了一点,但是感觉我没有很好的利用二分法,还要再思考一下
/**
* @param {number[]} nums
* @param {number} target
* @return {number[]}
*/
var twoSum = function(nums, target) {
let new_nums = []
// 1.过滤数组,将小于等于 target 的值存放到 new_nums 中
if(nums[nums.length - 1] <= target) {
new_nums = nums
} else {
for(let i = 0; i < nums.length; i++) {
if(nums[i] <= target) {
new_nums[i] = nums[i]
} else {
break
}
}
}
// 2.检查new_nums数组内是否存在和为target的两个数字
let left = 0
let right = new_nums.length - 1
while(left < right) {
// target分为两部分组成fist_part+second_part
let fist_part = new_nums[left]
let second_part = target - fist_part
// 在new_nums中的left_small到right_small之间,二分法寻找second_part
// 因为new_nums[left]是fist_part,且小于fist_part的已经排除,故从new_nums[left + 1]开始
let left_small = left + 1
let right_small = right
while(left_small <= right_small) {
let mid = parseInt((left_small + right_small) / 2)
if(new_nums[mid] < second_part) {
left_small = mid + 1
} else if (new_nums[mid] > second_part) {
right_small = mid - 1
} else {
// second_part = new_nums[mid]
return [fist_part, new_nums[mid]]
}
}
left++
}
return []
};
我的想法是,先画查找范围,在用二分确定其中一个数,如上面代码所示。
这种方式耗时是第一种的两倍,还要再改进一下看看