【每日力扣35】买卖股票的zui佳时机

一、题目[LeetCode-121]

给定一个数组 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。

提示:

  • 1 <= prices.length <= 10^5
  • 0 <= prices[i] <= 10^4

二、思路

1)动态规划

在《Day2 买卖股票的最佳时机II》的动态规划法中,定义dp[i][0]表示第i天(i从0计)交易完成后手里没有股票的利润,dp[i][1]表示第i天(i从0计)交易完成后手里持有股票的利润。

则有状态转移方程

  • dp[i][0] = max( dp[i-1][0], dp[i-1][1] + price[i] )
  • dp[i][1] = max( dp[i-1][0] - price[i], dp[i-1][1] )

而本题也同理。然而需要作出改动。本题与《买卖股票的最佳时机II》的区别在于,在后者可以在这几天中买卖股票多次,而在本题只能买卖一次。对于dp[i][0]的状态转移方程dp[i][0] = max( dp[i-1][0], dp[i-1][1] + price[i] ),不用更改。但是对于dp[i][1],因为我们限定 只能购买一次,因此在dp[i][1]之前,如果买了一次,我们在dp[i][1]只能取dp[i-1][1],如果还没卖,那么dp[i][1]就是第一次购买,也是唯一一次购买,因此取-price[i]。

由此,得出dp[i][1] = max( -price[i], dp[i-1][1] )

初始条件:对于第一天,没有股票时,则说明没有购买,dp[0][0] = 0;

持有股票时,则说明恰好第一天购买了,dp[0][1] = -price[0];

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int n = prices.size();
        vector<vector<int>> dp(n);
        for(int i = 0; i < n; i++)
            dp[i] = vector<int>(2);//创建动态规划转移方程的数组
        dp[0][0] = 0;
        dp[0][1] = -prices[0];//动态规划转移方程初始条件
        for(int i = 1; i < n; i++)
        {
            dp[i][0] = max(dp[i-1][0], dp[i-1][1] + prices[i]);
            dp[i][1] = max(prices[i] * -1, dp[i-1][1]);//由动态规划转移方程构建动态规划数组
        }
        return dp[n-1][0];//对于最后一天,显然dp[n-1][0]>dp[n-1][1]
    }
};

2)动态规划进一步优化

容易看出,对于上一思路构建的动态规划数组,我们只需要最后的dp[n-1][0]和dp[n-1][1],而对于其他的区间段dp[0, n-1)[0]和dp[0, n-1)[1],我们最终不需要,而它们会占用O(n)的存储空间。因此,可以进一步优化,不使用数组的形式实现,而是直接进行迭代

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int n = prices.size();
        int dp0 = 0;
        int dp1 = prices[0] * -1;//动态规划转移方程初始条件
        for(int i = 1; i < n; i++)
        {
            dp0 = max(dp0, dp1 + prices[i]);
            dp1 = max(prices[i]*-1, dp1);//根据状态转移方程进行n-1次迭代
        }
        return dp0;//对于最后一天,显然dp0>dp1
    }
};

三、官方题解(来源:力扣(LeetCode))

暴力法

我们需要找出给定数组中两个数字之间的最大差值(即,最大利润)。此外,第二个数字(卖出价格)必须大于第一个数字(买入价格)。

形式上,对于每组 i 和 j(其中 j>i)我们需要找出 max(prices[j]−prices[i])。

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int n = (int)prices.size(), ans = 0;
        for (int i = 0; i < n; ++i){
            for (int j = i + 1; j < n; ++j) {
                ans = max(ans, prices[j] - prices[i]);
            }
        }
        return ans;
    }
};

作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock/solution/121-mai-mai-gu-piao-de-zui-jia-shi-ji-by-leetcode-/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

复杂度分析

  • 时间复杂度:O(n^2)。循环运行n(n-1)/2 次。
  • 空间复杂度:O(1)。只使用了常数个变量。

四、学习心得

动态规划题的关键在于在实际问题中找出状态转移方程。如《Day2买卖股票的最佳时机I》和《Day35买卖股票的最佳时机II》的第i天结束后我“持有股票”“没有股票”两种状态下的总利润与前一天第i-1天的状态的关系、《Day34爬楼梯》的爬至第n阶状态方法数与爬至第n-1阶状态和爬至第n-2阶状态方法数的关系

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值