题目描述

给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。

你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。

返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回 0

示例 1:

输入:[7,1,5,3,6,4]
输出:5
解释:在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。
     注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格;同时,你不能在买入前卖出股票。

示例 2:

输入:prices = [7,6,4,3,1]
输出:0
解释:在这种情况下, 没有交易完成, 所以最大利润为 0。
力扣原题目地址:https://leetcode.cn/problems/...

思路分析

关于这道题目,可以两次循环暴力求结果,不过不太好,此处不说暴力求解结果的代码方式(附在最后)

我们一般遇到一个复杂问题的时候,可以把复杂问题进行简化拆解,在解决简化拆解问题的时候,寻找规律、发现规律,最终在复杂问题上使用我们之前发现的简化问题相似的规律,从而解决之

比如老板上来就让我们制造一个原子弹,啊,不会啊。没关系我们先研究一下,如何制造一个手榴弹。反正都是蛋嘛,都是爆炸用的,总是有相通之处,手榴弹制造出来了,原子弹也就快了。这个比喻不太恰当,反正是那个意思,大家自行体会哈 ^_^

阅读题目以后我们大概知道需求如下:

  • 要想获得最高收益,就必须在低位买进、高位卖出

    • 即:找到数组中的最大值,最小值,最大减最小就是最高收益
  • 股票是,先买后卖。所以买股票的时间靠前、卖股票的时间靠后

    • 即:最小值的位置索引要小于最大值的位置索引
  • 问题是,我们暂时不知道那天买进股票最低,那天最高???

干脆,我们就假设,头一天股票最低,3块钱就能买,后面股票涨涨跌跌,但是,始终没有低于3块的。所以头一天买进最划算。我们以5天为周期,刷卡付款头一天买了。于是就模拟定义一个数组:let arr = [3,8,6,12,9],接下来,我们把问题转化成:

已知第一天买的股价最低,后面的每一天都比第一天股价高,求那一天卖掉最多能赚多少钱

复杂问题拆解之简化

于是我们根据需求,可以写出下面的代码:

let arr = [3, 8, 6, 12, 9]
function maxProfit(prices) {
    // 1. 假设初始收益为0,后面每一天的价格减去第一天的买入价格就是收益
    let maxProfitValue = 0
    for (let i = 1; i < prices.length; i++) {
        // 2. 只要收益大的(收益大的记在本子上,收益小的划掉不要)
        maxProfitValue = Math.max(maxProfitValue, prices[i] - 3)
    }
    // 3. 最终得到一个最大收益,并返回
    return maxProfitValue;
}
console.log( maxProfit(arr) ); // 9

上述代码 maxProfitValue = Math.max(maxProfitValue, prices[i] - 3)我们可以换一下写法,因为3是第一天的价格,所以换成arr[0] 即:

复杂问题拆解之简单变换

let arr = [3, 8, 6, 12, 9]
function maxProfit(prices) {
    let min = prices[0] // 变换之~我们已知最低点股价是第一项,故定义变量表示
    // 1. 假设初始收益为0,后面每一天的价格减去第一天的买入价格就是收益
    let maxProfitValue = 0
    for (let i = 1; i < prices.length; i++) {
        // 2. 只要收益大的(收益大的记在本子上,收益小的划掉不要)
        maxProfitValue = Math.max(maxProfitValue, prices[i] - min) // 变换之~最低价是min,这里卖出高价-买入最低价也是用min变量了
    }
    // 3. 最终得到一个最大收益,并返回
    return maxProfitValue;
}
console.log( maxProfit(arr) ); // 9

看到这里,有的朋友说了,假如第一天、数组中的第一项不是最小的呢?假如第二项有可能比第一项小呢?没关系,咱们对比一下就行了,谁小用谁。反正有循环,可以拿到每一项的值,对比就是喽。伪代码如下:

let min = arr[0] // 假设第一项最小
min = Math.min(min,arr[i]) // 第二项和当下最小值做对比,谁小就保留谁
Math.min(5,2,8,3) // Math.min求一组数中的最小值 左侧代码执行得到结果2
于是上述代码又可以做一个变换

复杂问题之变换解决之

function maxProfit(prices) {
    let min = prices[0] // 1. 假设最低股价是第一项,若后续有更低的股价,就用后续更低的
    let maxProfitValue = 0 // 2. 假设初始收益为0,后面每一天的价格减去买入价格就是收益
    for (let i = 1; i < prices.length; i++) {
        min = Math.min(min, prices[i]) // 3. 变换解决之,使用Math.min对比一下,看那一天的股价最低,就用那个
        // 4. 只要收益大的(收益大的记在本子上,收益小的划掉不要)
        maxProfitValue = Math.max(maxProfitValue, prices[i] - min) // 5. 已知最低价是min,那么收益就等于卖出价-最低价
    }
    return maxProfitValue; // 6. 最终得到一个最大收益,并返回
}

好了,看到这里,力扣算法题也就解决啦。所以思路很重要:复杂的问题简单化,在简单化处理的过程中不断寻找规律,从而向复杂化过渡,最终通过寻得的规律解决复杂化的问题

附:双层循环暴力解法

function maxProfit(prices) {
    let arr = [0] // 指定默认收益为0
    for (let i = 0; i < prices.length; i++) {
        for (let j = i + 1; j < prices.length; j++) {
            arr.push(prices[j] - prices[i]) // 把所有计算出的利润都追加到一个数组中
        }
    }
    return Math.max(...arr) // 然后求出这个数组中的最大值即为最大收益
}

水冗水孚
1.1k 声望585 粉丝

每一个不曾起舞的日子,都是对生命的辜负