【算法训练】和为s的两个数字(JavaScript版)

博客介绍了如何通过双指针法和双指针结合二分法解决力扣上的一个问题:在递增排序的数组中找到两数之和等于特定目标值的数对。给出了两种解决方案,第一种是简单的双指针法,第二种是尝试结合二分法优化,但实际效果并未显著提升。博主分析了每种方法的时间复杂度和内存消耗,并表达了对进一步优化的思考。
摘要由CSDN通过智能技术生成

来自力扣上 剑指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 []
};

我的想法是,先画查找范围,在用二分确定其中一个数,如上面代码所示。

这种方式耗时是第一种的两倍,还要再改进一下看看

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值