JS:三大算法 day2

回溯、贪心、动态规划的思路是通过大量做题培养的,不是说几天就能掌握,而且题目不会告诉你使用哪个算法。坚持做题。。。

回溯算法——重点是结束条件(纵向)和循环条件(横向)

回溯就相当于遍历多叉树
三角遍历:下一层从 i + 1 的元素开始
矩阵遍历:下一层从 i 元素开始

77.组合——横向元素唯一、三角遍历

给定两个整数 n 和 k,返回范围 [1, n] 中所有可能的 k 个数的组合。
你可以按 任何顺序 返回答案。

if(temp.length === k)
------
for(let i = start; i <= n - (k - temp.length) + 1; i++)

216.组合总和 III——横向元素唯一、三角遍历

找出所有相加之和为 n 的 k 个数的组合,且满足下列条件:
只使用数字1到9
每个数字 最多使用一次

if(curSum > n) return
if(temp.length === k && curSum === n)
-----
const min = Math.min(n-curSum, 9 - (k - temp.length) + 1)
for(let i=start; i <= min; i++)

40.组合总和 II——横向元素重复(需去重)、三角遍历

给定一个候选人编号的集合 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。

candidates 中的每个数字在每个组合中只能使用 一次 。
先排序,记录同一层的上一次元素

if (curSum > target) return;
if(curSum === target)
-----
var lastVal = -1
for(let i=start; i<len; i++) {
   let val = candidates[i];

39.组合总和 ——横向元素唯一、矩阵遍历

给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target ,找出 candidates 中可以使数字和为目标数 target 的 所有 不同组合 ,并以列表形式返回。你可以按 任意顺序 返回这些组合。

candidates 中的 同一个 数字可以 无限制重复被选取 。如果至少一个数字的被选数量不同,则两种组合是不同的。

对于给定的输入,保证和为 target 的不同组合数少于 150 个。

先排序

if(curSum === target) 
------
for(let i=index; i<len; i++){
backtrack(i)

47.全排列 II——横向元素重复(需去重)、三角遍历

给定一个可包含重复数字的序列 nums ,按任意顺序 返回所有不重复的全排列。

排序的目的:上一层填的数可能与当层填的数相同,不排序就需要用数组来记录

var permuteUnique = function(nums) {
    var res=[], temp=[], used=new Array(nums.length).fill(false)
    nums.sort((a,b) => a-b)
    function backtrack(depth) {
        if(depth === nums.length) {
            res.push([...temp])
            return
        }
        var lastVal = -11
        for(let i=0; i<nums.length; i++){
            // 同一层不应该测试相同值
            if(used[i] || lastVal===nums[i]) continue
            temp.push(nums[i])
            used[i] = true
            lastVal = nums[i]
            backtrack(depth + 1)
            temp.pop()
            used[i] = false
        }
    }
    backtrack(0)
    return res
};

不排序

var permuteUnique = function(nums) {
    var res=[], temp=[], used=new Array(nums.length).fill(false)
    // nums.sort((a,b) => a-b)
    function backtrack(depth) {
        if(depth === nums.length) {
            res.push([...temp])
            return
        }
        var tested = []
        // var lastVal = -11
        for(let i=0; i<nums.length; i++){
            // 同一层不应该测试相同值
            if(used[i] || tested.includes(nums[i])) continue
            // if(used[i] || lastVal===nums[i]) continue
            temp.push(nums[i])
            used[i] = true
            tested.push(nums[i])
            lastVal = nums[i]
            backtrack(depth + 1)
            temp.pop()
            used[i] = false
        }
    }
    backtrack(0)
    return res
};

17.电话号码的字母组合

给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。答案可以按 任意顺序 返回。

给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。

var letterCombinations = function(digits) {
    const map = ["","","abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"]
    var res = [], temp = [], len = digits.length
    if(len === 0) return res
    function backtrack(index){
        if(index === len) {
            res.push(temp.join(""))
            return
        }
        let val = digits[index]
        for(let char of map[val]){
            // console.log(char)
            temp.push(char)
            backtrack(index + 1)
            temp.pop()
        }

    }
    backtrack(0)
    return res
};

贪心算法

(重做)376.摆动序列

针对序列[2,5],可以假设为[2,2,5],这样它就有坡度了即preDiff = 0
在这里插入图片描述
第一个2为虚构,左差值为0,如果右差值不等于0,则第二个2可以作为左边界,preDiff = curDiff。

/**
 * @param {number[]} nums
 * @return {number}
 */
var wiggleMaxLength = function(nums) {
    if(nums.length == 1) return 1;
    // 最右节点为第一个
    var leftDiff = 0, rightDiff = 0, res = 1;
    for(let i=0; i<nums.length-1; i++) {
        rightDiff = nums[i+1] - nums[i]
        // 左差值可以取0,是因为只取水平线的最右边节点
        if((rightDiff > 0 && leftDiff <= 0) || (rightDiff < 0 && leftDiff >= 0)) {
            res++;
            // i 记录为峰值时, 才改变左插值, i作为左边界
            leftDiff = rightDiff
        }
    }
    return res
};

53. 最大子数组和 (简单)

贪心算法

动态规划

var maxSubArray = function(nums) {
    var res = new Array(nums.length)
    res[0] = nums[0]
    for(let i=1; i<nums.length; i++) {
        res[i] = Math.max(res[i-1]+nums[i], nums[i])
    }
    return Math.max(...res)
};
var maxSubArray = function(nums) {
    var sum = Number.MIN_SAFE_INTEGER
    var curSum = 0
    for(let i=0; i<nums.length; i++) {
        curSum += nums[i];
        if(curSum > sum) sum = curSum;
        if(curSum < 0 ) curSum = 0;
    }
    return sum
};

122. 买卖股票的最佳时机 II (中等)

模拟

贪心算法

在这里插入图片描述

动态规划(情况复杂)

注意现金 + 股票才是总资产,今天可以买入(现金减少);或者卖出(现金增加)。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

/**
 * @param {number[]} prices
 * @return {number}
 */
var maxProfit = function(prices) {
    var len = prices.length
    var dp1 = new Array(len) //第i天持有股票所得现金
    var dp2 = new Array(len) //第i天不持有股票所得最多现金
    dp1[0] = -prices[0] //买入
    dp2[0] = 0
    for(let i=1; i<len; i++) {
        // 根据前一天持有或不持有
        dp1[i] = Math.max(dp1[i-1], dp2[i-1]-prices[i])
        dp2[i] = Math.max(dp1[i-1]+prices[i], dp2[i-1])
    }
    return dp2[len-1]
};
var maxProfit = function(prices) {
    // var diff = []
    var res = 0
    prices.reduce((pre, cur) => {
        // diff.push(cur - pre)
        res += Math.max(0, cur-pre)
        return cur
    })
    // console.log(diff)
    return res
};
var maxProfit = function(prices) {
    var st = 0, en = 0, res = 0;
    while(st < prices.length && en < prices.length) {
        while(en < prices.length-1) {
            if(prices[en+1]>prices[en]) {
                en++
            }else {
                break
            }
        }
        if(st != en) {
            res += (prices[en] - prices[st])
            st = en + 1
            en = st
        } else{
            en++
            st++
        }
    }
    return res
};

55. 跳跃游戏 (中等)

在这里插入图片描述
核心:覆盖区域不断增大

var canJump = function(nums) {
    var end = 0, i = 0;
    for(; i<nums.length && i<=end; i++) {
        var temp = i + nums[i]
        if(temp>end) end = temp
    }
    if(end >= nums.length-1) return true
    return false
};

45. 跳跃游戏 II (中等)

在这里插入图片描述
核心:得到本次跳跃的区间,选出区间中下一次能跳最远的位置。

var jump = function(nums) {
    var i = 0, end = 0, count = 0; //last = 1
    if(nums.length == 1) return 0
    while(i<nums.length) {
        count++
        end = i + nums[i]
        if(end >= nums.length-1) return count
        var temp = 0
        for(let j=i+1; j<=end; j++) {
            if(j + nums[j] > temp){
                temp = j + nums[j]
                i = j
            }
        }
        // last = end + 1
    }
};

官方代码:
核心:双指针,规定每一次跳跃的区间,到达区间末尾时,才更新下一次跳跃的区间

var jump = function(nums) {
    let curIndex = 0
    let nextIndex = 0
    let steps = 0
    for(let i = 0; i < nums.length - 1; i++) {
        nextIndex = Math.max(nums[i] + i, nextIndex)
        if(i === curIndex) {
            curIndex = nextIndex
            steps++
        }
    }

    return steps
};

1005. K 次取反后最大化的数组和 (简单)(不会)

答案简洁是因为:按绝对值的大小来排序

var largestSumAfterKNegations = function(nums, k) {
    nums.sort((a,b) => Math.abs(b) - Math.abs(a))
    for(let i = 0; i < nums.length && k > 0; i++) {
        if(nums[i] < 0) {
            nums[i] *= -1
            k--
        }
    }
    if(k > 0 && k % 2) {
        nums[nums.length - 1] *= -1   
    }
    k = 0
    var res = nums.reduce((a, b) => {
        return a + b
    })
    return res
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值