LeetCode题解:两数之和 II - 输入有序数组

两数之和 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++
        }
    }
};
结果

在这里插入图片描述

六、解法五(二分法)

思路:

  1. 将2个数字的问题,转换成确定1个数字,寻找第二位数字的问题
  2. 将问题转换成,排序数组内寻找指定元素
  3. 利用数组的有序性质,可以通过二分查找的方法寻找第二个数。
  4. 为了避免重复寻找,在寻找第二个数时,只在第一个数的右侧寻找。
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,水平有限,虚心学习中

其他沉淀

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

余光、

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值