听风是风

学或不学,知识都在那里,只增不减。

导航

JS leetcode 两数之和 II - 输入有序数组 题解分析

壹 ❀ 引

我在JS leetcode 两数之和 解答思路分析一文中首次解决两数之和等于目标值的问题,那么今天遇到的是两数之和的升级版,题目为leetcode167. 两数之和 II - 输入有序数组,题目描述如下:

给定一个已按照升序排列 的有序数组,找到两个数使得它们相加之和等于目标数。

函数应该返回这两个下标值 index1 和 index2,其中 index1 必须小于 index2。

说明:

返回的下标值(index1 和 index2)不是从零开始的。
你可以假设每个输入只对应唯一的答案,而且你不可以重复使用相同的元素。
示例:

输入: numbers = [2, 7, 11, 15], target = 9
输出: [1,2]
解释: 2 与 7 之和等于目标数 9 。因此 index1 = 1, index2 = 2 。

我们还是先分析题目,给出我的想法,再看看优质的解题思路。

贰 ❀ 解题思路

其实相比先前第一次遇到的两数之和,题目变化只有两处:

第一,数组这次变成了有序数组,数组元素排序为由小到大;

第二,这次返回的下标值不是从0开始,而是从1开始,且index1 必须小于 index2。

由于在上次的解答中,我们已经提出了可以使用哈希表的做法,只用一次遍历即可解决问题,但后来博客园用户MrSmileZhu留言说使用字典会比哈希略快,所以这里我贴字典的做法,实现思路与哈希表相同:

/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number[]}
 */
var twoSum = function (nums, target) {
    var i = 0,
        len = nums.length,
        dict = {};
    for (; i < len; i++) {
        var dif = target - nums[i];
        if (dif in dict) {
            return [dict[dif] + 1, i + 1];
        };
        dict[nums[i]] = i;
    };
    return [];
};

思路这里再简单复述,比如目标值是9,第一次遍历差值为9-2=7,我们创建一个空对象,很明显这时候不可能从空对象里找到key为7的属性,所以我们把当前遍历元素作为key,索引作为value存进去,此时对象变成了:

dict = {
    2:0
};

OK,第二次遍历,差值为9-7=2,哎,我们成功的再dict中找到了key为2的属性,所以上一次遍历的元素与当前元素的和刚好等于9,所以我们只用拿到dict[2]与数组当前遍历的 i 即可,由于这次下标是从1开始,所以最终返回dict[2] + 1, i + 1

值得注意的是,这道题出现在数组和字符串卡片的双指针技巧中,所以出题人本意是希望我们用双指针来解决这个问题的,关于双指针我们在JS leetcode 反转字符串 题解分析第一次遇到,说到底就是定义两个变量i与j,一个正向递增一个反向递减同时遍历。

而且我在两数之和的第一篇博客中,我的第二种思路也类似双指针的想法,我的思路其实是,一开始让i为0,j能一直递减遍历到i的后一位,中途一直相加与目标值比较,如果不满足,让i自增一位,j再次从尾部开始倒序遍历,再次相加对比。

但是这样我就又不得不使用两次遍历嵌套...外层遍历控制i,内层遍历控制j递减,想了很久也没好的办法。

无奈之下看了其他用户的思路,瞬间顿悟了....先贴代码,原理其实很简单,这里参考了leetcode用户CyC2018的实现思路:

/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number[]}
 */
var twoSum = function (nums, target) {
    var i = 0,
        j = nums.length - 1;
    while (i < j) {
        if (numbers[i] + numbers[j] === target) {
            return [i + 1, j + 1]
            // 如果两数之和比目标值小,让i往右移
        } else if (numbers[i] + numbers[j] < target) {
            i++
            // 如果两数之和比目标值大,让j往左移
        } else {
            j--
        };
    };
    return [];
};

看了代码中的两句注释,其实大家应该也明白了,一开始我们就选中了数组的第一位与最后一位元素,相加并与目标值比较,关键线索数组是从小到大排列的有序数组,所以如果第一次比较就比目标值大,我们就让j往左移动,这样两数之和会变小,再次与目标值比较。

如果这次小了,我们再让i往右移,这样两数之和会变大,所以通过这种思路,我们依赖if (numbers[i] + numbers[j] === target)这行代码,试探性的让 i 与 j 变化,直到找到目标索引,再分别加1即可。

OK,关于两数之和的升级版就说到这了。

posted on 2020-05-25 23:54  听风是风  阅读(467)  评论(0编辑  收藏  举报