122. 买卖股票的最佳时机 II
链接
题目链接
文章链接
第一想法
这道题和摆动序列这道题非常类似,由于昨天做过摆动序列,所以这道题很快就想到了股价就是折线图,每次在低点买入,高点卖出,代码如下:
function maxProfit(prices: number[]): number {
let res: number = 0 //手机结果
for (let i = 1; i < prices.length; i++) {
if (prices[i] <= prices[i - 1]) continue //股价下降直接不购买股票
let index = i - 1 //从index时 估计开始上升
while (prices[i] >= prices[i - 1]) i++; //股票一直上升
res += prices[i - 1] - prices[index] //这是prices[i]是低于prices[i-1],所以应该在prices[i-1]卖出 获利为prices[i - 1] - prices[index]
}
return res
};
看完文章的想法
文章的想法和我的完全不同,文章的比较好理解,例如第一天买入第三天卖出,则利润为prices[2]-prices[0]
,也就是可以看作为prices[2]-prices[1]+prices[1]-prices[0]
,也就说,我们可以看每两天的差值作为利润,也就是把整体的利润分解为每天耳朵利润,例如这张图:
所以说只需要手机每天利润为正的就可以,代码如下:
function maxProfit(prices: number[]): number {
let res: number = 0
for (let i = 1; i < prices.length; i++) {
res += Math.max(0, prices[i] - prices[i - 1])
}
return res
};
思考
这道题的文章写法很奇妙,我是按照摆动序列的解法修改来解决这道题(只求上坡度的差值),而文章用的贪心算法更妙,因为1-3天的的股票获利可以看成1-2,2-3股票获利之和,这样子文章的写法就容易理解了,只求值为正的和就是正确的结果.
55. 跳跃游戏
链接
题目链接
文章链接
第一想法
这道题要求是判断能否调到最后一个下标的位置,刚开始没有想法,但是想了一会发现我可以看看能否到达每个位置到达的最远位置,如果能达到的最远位置大于数组最后一个位置,则肯定能达到,代码如下:
function canJump(nums: number[]): boolean {
let max: number = 0//最大可以跳哪
for (let i = 0; i < nums.length; i++) {
if (max < i) return false
max = Math.max(max, nums[i] + i)
}
return true
};
看完文章后的想法
文章的想法和我的完全一致,都是通过最远到达的位置(查看能否覆盖),例如这张图:
我刚刚写的是负向代码,也可以正向解决这个问题,代码如下:
function canJump(nums: number[]): boolean {
let max: number = 0//最大可以跳哪
for (let i = 0; i <= max; i++) { //这里的是要<=max表示最远能跳到哪,不能是nums.length 负责最后一个元素一定能到达 因为max必定可以为nums[nums.length-1].
max = Math.max(max, nums[i] + i)
if (max >= nums.length - 1) return true
}
return false
};
思考
这道题想了一会发现有了思路,表示很惊喜,之后看完文章后,文章的写法确实考虑的更多,我的那种写法是直接排除了最后一个元素的if判断(if (max < i) return false
),正好是正确的解法,但是当初我是没想到的.
45. 跳跃游戏 II
链接
文章链接
题目链接
第一想法
我的想法是模拟,尝试用递归模拟跳跃,但是发现代码是超时的,代码如下:
//以下方法是超时的
function jump(nums: number[]): number {
let min: number = Number.MAX_SAFE_INTEGER
let n: number = nums.length
const foo = (num: number, count: number) => {
if (num >= n - 1) {
min = Math.min(min, count)
return
}
for (let i = 1; i <= nums[num]; i++) {
foo(i + num, count + 1)
}
}
foo(0, 0)
return min
};
之后又换了一种写法,思路为每跳跃一次就通过记录比较跳跃到当前的最最小次数,所以第一层for来模拟从当前开始跳,里面的循环模拟跳的位置:
function jump(nums: number[]): number {
let arr: number[] = new Array(nums.length).fill(nums.length)
arr[0] = 0
for (let i = 0; i < nums.length; i++) {
for (let j = 1; j <= nums[i]; j++) {
if (i + j > nums.length - 1) continue
arr[i + j] = Math.min(arr[i + j], arr[i] + 1)//比较得出调到当前位置的最小次数
}
}
return arr[arr.length - 1]
};
看完文章的想法
看完文章后的想法发现,如果要我自己想我是真的想不到,这道题居然还是可以用最大覆盖范围来解决,不过这回是需要两个:一个为当前的最大范围,另一个为下一步的最大范围,这样,如果下一步的最大范围到达nums的最后一个元素,则返回步数,详细解释可以看这张图:
代码如下:
function jump(nums: number[]): number {
let curr: number = 0 //当前覆盖范围
let next: number = 0 //下一步覆盖范围
let step: number = 0 //步数
if (nums.length == 1) return 0
for (let i = 0; i < nums.length; i++) {
next = Math.max(next, nums[i] + i) //下一步的最大覆盖范围
if (curr === i) { //达到当前覆盖范围时
step++ //下一步就要走到下一步的最大范围
curr = next//更新当前最大范围
if (curr >= nums.length - 1) return step //当前最大范围达到最后一个元素直接返回step
}
}
return step
};
思考
这道题确实没想到用最大覆盖范围还能做,这个需要能想清楚当前最大最大覆盖范围时可以移步到达的,通过当前最大覆盖范围和下一步最大覆盖范围进行迭代来完成的,每进行一次迭代(下一步覆盖最大范围赋值给当前覆盖最大范围),步数就需要加一,直到可以最大覆盖范围包括最后一个元素时截止.
今日总结
今天耗时2.5小时,三道都是不同类型的贪心算法,前两道都是自己想到的,但是最后一题的贪心是没想到,贪心的学习还要继续努力.