LeetCode 股票的最大利润

假设把某股票的价格按照时间先后顺序存储在数组中,请问买卖该股票一次可能获得的最大利润是多少?

示例 1:

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

输入: [7,6,4,3,1]
输出: 0
解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。

限制:

0 <= 数组长度 <= 10^5

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/gu-piao-de-zui-da-li-run-lcof

题目的理解:题目求解的是最大利润,所以一开始我以为的是只要找到了最大值和最小值,然后判断最大值是否出现在这个最小值的后面,如果是,那么就说明最大利润就是两者的差。否则,找到的最大值出现在最小值的前面,就说明没有进行交易,直接返回0.

在这里插入图片描述
理解题目的要求之后:
方法一:利用暴力解决
1、获取当前下标 i 的值,表示买入票的价钱
2、遍历 i 后面的数字,表示卖出票所得到的钱。如果prices[ j ] > prices[ i ],那么就说明了可以进行交易,所以两者的差值就是交易得到的利润。
3、重复上述操作,直到整个数组遍历完毕。将所得到的利润的最大值返回。
对应的代码:

class Solution {
    public int maxProfit(int[] prices) {
    /*
    max表示利润的最大值,之所以赋值为0,表示第一天买入票时,之后再这一天卖
    出去得到的利润.同时赋值为0的好处时,如果遍历完整个数组,都没有进行交易
    是直接返回max就行,而如果是将max初始值为极小的数时,还需要通过定义一个
    变量,从而判断是否有进行果交易
    */
         int max = 0,i,j;
         for(i = 0; i < prices.length - 1; i++){
             for(j = i + 1; j < prices.length; j++){
                    if(prices[j] > prices[i] && prices[j] - prices[i] > max){
                        max = prices[j] - prices[i];
                    }
             }
         }
         return max;
    }

}

运行结果:
在这里插入图片描述

方法二:动态规划
状态方程:dp[ i ]表示从第一天开始到 i 这一天所能获得的最大利润。之所以说是这样定义,是因为如果在 i 这一天没有进行交易,那么这一天得到的利润就是 i 这一天之前所得到的利润的最大值。
转移方程dp[ i ] = max{dp[ i - 1],prices[ i ] - min[prices[[0,i)] },其中min[prices[[0,i)]表示在这一天之前的最小值,作为买入票所花的钱。如果[0,i)中的值都比prices[ i ]大,那么表示这一天没有进行交易,那么目前所得到的利润的最大值就是dp[ i - 1],所以将dp[ i - 1]赋值给dp[ i ];同样的,即使进行了交易,但是所得的利润小于之前的利润,那么至今得到的利润最大依旧是dp[ i - 1],所以将dp[i - 1] 赋值给dp[ i ],如果这一天得到的利润比之前得到的利润大,那么这一天的利润就是prices[ i ] - min(prices[0,i))。所以转移方程得证。

过程分析:
1、定义一个数组dp,长度和prices这个数组相同,从而表示每一天所能得到的最大的利润。
2、dp[ 0 ] = 0.之所以是0,是因为最多是在这一天买入票之后,然后再这一天卖出去了,所以初始值为0.
3、从下标为1开始遍历,根据转移方程,从而给dp[ i ]进行调整更新
4、返回值:有状态方程的定义以及转移方程的执行,dp[ i ] 表示的是从第一天开始到 i 这一天所得到的最大的利润。所以即使没有进行交易,那么dp[ n - 1]最小值就是0,表示从来都没有进行过交易。所以直接返回dp[n - 1].

对应的代码:

class Solution {
    public int maxProfit(int[] prices) {
          if(prices == null || prices.length == 0) 
             return 0;
          int[] dp = new int[prices.length];
          int i,len;
          len = prices.length;
          dp[0] = 0;//第一天获取到的利润为0
          for(i = 1; i < prices.length; i++){
              int profit= prices[i] - getMin(prices,i);
              //有可能当前卖出去得钱小于买入得钱,所以这一天得到得利润依旧是之前得到的,从而符合状态方程的定义
              dp[i] = dp[i - 1] >= profit? dp[i - 1] : profit; 
          }
          return dp[len - 1];
    }
    public int getMin(int[] prices,int i){
        //获取i这一天卖出票的时候,买入票的最小值
        int min = 99999;
        for(int k = 0; k < i; k++){
            if(prices[k] < min)
              min = prices[k];
        }
        return min;
    }
}

运行结果:
在这里插入图片描述
对于动态规划中的进一步优化:
1、不需要定义dp这个数组,而是直接定义一个变量,表示到 i 这一天位置所能获得的最大利润。因为我们并不需要对dp这个数组进行任何操作,而是简单将dp数组的最后一个数返回即可。
2、查找买入票时的最小值不需要重复调用方法,从0开始查找。因为再第一次查找min的时候,已经时从0开始查找了,那么再找到最小值min之后,表示min之前的数都比min大,因此下一次进行操作的时候(即获取i + 1这一天所能获得的最大利润),只需要将min和当前的prices[i]进行比较即可。

对应的代码:

class Solution {
    public int maxProfit(int[] prices) {
          if(prices == null || prices.length == 0)
             return 0;
          int profit = 0;//利润的初始值为0
          int i,min = prices[0],lirun; //min表示买入票所花的钱,由于i = 1卖出去的时候,肯定是再prices[0]买入的,所以min的初始值是这个
          for(i = 1; i < prices.length; i++){
              lirun = prices[i] - min; //获取 i 这一天进行交易的情况
              profit = profit > lirun ? profit : lirun;//更新获得的利润的最大值,如果 i 这一天进行了交易,并且得到的利润比之前得到的利润都大时就更新profit
              min = min > prices[i] ? prices[i] : min;//更新买入票时所花的钱
          }
         /*
          直接返回profit,之所以不需要考虑判断profit没有进行交易,
          是因为一开始就已经是0了,然后后面卖出去的票都小于买入的
          票,那么这时候得到的利润lirun是一个负数,小于profit,那么
          profit依旧是0,符合题目的要求
          */
          return profit;
    }

}

运行结果:
在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值