leetcode笔记 | 283. 移动零 167两数之和 II - 输入有序数组(JavaScript)

202 阅读1分钟

双指针

【283】移动零

CategoryDifficultyLikesDislikes
algorithmsEasy (63.86%)1142-

给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。

个人解法

var moveZeroes = function (nums) {
    /* let [left, right] = [0, nums.length - 1];
    while (left <= right) {
        if (nums[left] === 0) {
            nums.push(...nums.splice(left, 1));
            left++;
        }
        if (nums[right] !== 0) {
            nums.push(...nums.splice(right, 1));
            right--;
        }
    }
    return nums; 错误*/
    let j = 0;
    let [left, right] = [0, nums.length - 1];
    while (left <= right) {
        if (nums[left] === 0) {
            nums.splice(left, 1);
            j++;
        } else {
            left++;
        }
    }
    while (j > 0) {
        nums.push(0);
        j--;
    }
    return nums;
};
通过96 ms39.3 MBJavaScript

分析

一开始的思路出错,是因为nums元素被删后,循环时仍沿用原数组的下标,无法遍历原数组中所有的0。使用双指针法,下标随数组长度改变而改变,可以解决这个问题。

优化

var moveZeroes = function (nums) {
    let [left, right] = [0, 0];
    while (right < nums.length) {
        if (nums[right]) {
            /* let temp = nums[left];
            nums[left] = nums[right];
            nums[right] = temp; */
            [nums[left], nums[right]] = [nums[right], nums[left]];
            left++;
        }
        right++;
    }
    return nums;
};
通过72 ms39.9 MBJavaScript

分析

一开始虽然也想过定义leftright指针,交换右侧非零数至左侧,但只想到定义leftright为头尾端点下标,头尾交换,此时数组非零元素顺序被反转;未曾想到将leftright同指向数组开头,也就是逐位左右交换,这样可保证非零元素顺序不变。

right指针每次都向右移动一位,而left只在right指向非零数、二者交换值后右移。

tips:
使用解构赋值,比定义中间值temp以交换nums[left],nums[left]快。

【167】两数之和 II - 输入有序数组

CategoryDifficultyLikesDislikes
algorithmsEasy (58.19%)548-

给定一个已按照升序排列的整数数组 numbers ,请你从数组中找出两个数满足相加之和等于目标数 target 。

函数应该以长度为 2 的整数数组的形式返回这两个数的下标值 numbers 的下标 从 1 开始计数 ,所以答案数组应当满足 1 <= answer[0] < answer[1] <= numbers.length 。

你可以假设每个输入只对应唯一的答案,而且你不可以重复使用相同的元素。

个人解法

var twoSum = function (numbers, target) {
    /* let [left, right] = [0, nums.length];
    while (left < right) {
        let mid = parseInt((left + right) / 2);
        if (mid <= target) {
            left++;
        }
        if () 错误  */
    for (let i = 0; i < numbers.length; i++) {
        for (let j = i + 1; j < numbers.length; j++) {
            if (numbers[i] + numbers[j] === target) {
                return [i + 1, j + 1];
            }
        }
    }
};
通过264 ms38.4 MBJavaScript

分析

定义i、j两次遍历数组,不如固定第一个数,用二分查找法找第二个数。

优化(二分查找)

var twoSum = function (numbers, target) {
for (let i = 0; i < numbers.length; i++) {
        let [left, right] = [i + 1, numbers.length - 1];
        while (left <= right) {
            let mid = parseInt((left + right) / 2);
            if (numbers[i] + numbers[mid] === target) {
                return [i + 1, mid + 1];
            } else if (numbers[i] + numbers[mid] > target) {
                right = mid - 1;
            } else {
                left = mid + 1;
            }
        }
    }
}
通过84 ms39.9 MBJavaScript

第二种解法(双指针)

var twoSum = function (numbers, target) {
let [left, right] = [0, numbers.length - 1];
    while (left < right) {
        if (numbers[left] + numbers[right] === target) {
            return [left + 1, right + 1];
        } else if (numbers[left] + numbers[right] < target) {
            left++;
        } else {
            right--;
        }
    }
    return [left + 1, right + 1];
};
通过72 ms38.2 MBJavaScript

分析

此题使用双指针法的关键点在于:将左右指针初始化为0, numbers.length - 1 。假设numbers[i]+numbers[j]=target就是唯一解,0 ≤ i < j ≤ numbers.length−1。由于题目确保有唯一的答案,左指针不可能移到 i 的右侧,右指针不可能移到 j 的左侧,也不会遗漏解。