两数之和 II - 输入有序数组
一、题目
给定一个已按照升序排列 的有序数组,找到两个数使得它们相加之和等于目标数。
函数应该返回这两个下标值 index1 和 index2,其中 index1 必须小于 index2。
返回的下标值(index1 和 index2)不是从零开始的。
你可以假设每个输入只对应唯一的答案,而且你不可以重复使用相同的元素。
示例:
* 输入: numbers = [2, 7, 11, 15], target = 9
* 输出: [1,2]
* 解释: 2 与 7 之和等于目标数 9 。因此 index1 = 1, index2 = 2 。
二、解法一(暴力循环)
- 时间复杂度较高
- 由于是唯一值,所以找到第一个数,在它之后寻找第二数即可。
var twoSum = function(num, target) {
var len = num.length
for(let i = 0; i < len; i++){
for(let j = i+1; j < len; j++){
if(num[i]+num[j] === target) return [i+1, j+1]
}
}
};
结果
三、解法二(indexOf)
思路
- 由题干得知一定会有一组解,那么就考虑用 和 - A = B
- 在数组之中查找B,并返回下标
- indexOf这个API也是遍历数组,所以效率反而比双循环还差
代码
var twoSum = function(num, target) {
var len = num.length
var i = 0
while(i < len){
var temp = target - num[i]
if(num.lastIndexOf(temp) != -1){
return [i+1, num.lastIndexOf(temp)+1]
}
i++
}
};
结果
四、解法三(Map)
思路
x + y = target
y = target - x
x + (target - x) = target
- 时间优化明显,但是仍然没有利用上题干提供的信息——排序数组
- 时间复杂度:O(n)
套入题目的例子,遍历数组,数组遍历的当前值为numbers[i],那么 y 应该是 target - numbers[i]。
所以,只要在遍历的时候确定target - numbers[i]在数组里有,返回对应下标。
hash表方法有两次哈希表方法和一次哈希表方法。
两次hash表方法,在第一次迭代中,我们将每个元素的值和它的索引添加到表中。然后,在第二次迭代中,我们将检查每个元素所对应的目标元素(target−numbers[i])是否存在于表中。
代码
var twoSum = function(numbers, target) {
let map = new Map()
for(let i = 0; i < numbers.length; i ++) {
if(map.has(target - numbers[i])) {
return [map.get(target - numbers[i]) + 1, i + 1]
}
map.set(numbers[i], i)
}
};
或者
var twoSum = function(num, target) {
var obj = {}
for(let i = 0; i < num.length; i ++) {
let temp = target - num[i]
if(obj[temp]) {
return [obj[temp], i + 1]
}
obj[num[i]] = i+1
}
};
结果
五、解法四(双指针)
思路
- 利用排序数组特点
- 减少遍历次数
代码
var twoSum = function(num, target) {
var left = 0
var right = num.length - 1
while(left < right){
let sum = num[left] + num[right]
if(sum === target) return [left+1, right+1]
else if (sum > target){ //和>目标值,证明右边界大了,那就向左移
right--
}else if(sum < target){ //和<目标值,证明左边界小了,那就向右移
left++
}
}
};
结果
六、解法五(二分法)
思路:
- 将2个数字的问题,转换成确定1个数字,寻找第二位数字的问题
- 将问题转换成,排序数组内寻找指定元素
- 利用数组的有序性质,可以通过二分查找的方法寻找第二个数。
- 为了避免重复寻找,在寻找第二个数时,只在第一个数的右侧寻找。
var twoSum = function(numbers, target) {
// 每一个循环先确定1位,利用二分法寻找指定的元素
for (let i = 0; i < numbers.length; i++) {
let left = i + 1; // 这是因为
let right = numbers.length - 1;
while (left <= right) {
let mid = left + ((right - left) >> 1);
if (target === numbers[mid] + numbers[i]) {
return [i + 1, mid + 1];
}else if(target > numbers[mid] + numbers[i]){
left = mid + 1;
}else{
right = mid - 1;
}
}
}
return [-1, -1]
};
七、写在最后
本文是二分查找-模版 II 的最后一题,接下来我们将面对更大的挑战,加油~
如果对你有所帮助不妨给本项目的github 点个 star,这是对我最大的鼓励
关于我
- 花名:余光
- WX:j565017805
- 沉迷 JS,水平有限,虚心学习中
其他沉淀