LeetCode-题目整理【3】:买卖股票的最佳时机

买卖股票的最佳时机 都是求最大利润,但是在没有限制,如121和122,动态规划稍微复杂一些,建议不用,到最后两道难题,题目有限制,使用动态规划通过求解子问题的最优解来逐步求解原问题的最优解。

  1. 买卖股票的最佳时机 给定一个数组 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。
func maxProfit(prices []int) int {
    minPrice := prices[0]
    maxProfit := 0
    for i := 1; i < len(prices); i++ {
        if prices[i] < minPrice {
            minPrice = prices[i]
        } else if prices[i] - minPrice > maxProfit {
            maxProfit = prices[i] - minPrice
        }
    }
    return maxProfit
}
//动态规划解答

func maxProfit(prices []int) int {
	if len(prices) == 0 {
		return 0
	}

	n := len(prices)
	dp := make([]int, n)
	dp[0] = 0
	minPrice := prices[0]

	for i := 1; i < n; i++ {
		minPrice = min(minPrice, prices[i])
		dp[i] = max(dp[i-1], prices[i]-minPrice)
	}

	return dp[n-1]
}

func min(a, b int) int {
	if a < b {
		return a
	}
	return b
}

func max(a, b int) int {
	if a > b {
		return a
	}
	return b
}

// 使用动态规划来解决这个问题,可以通过定义状态和状态转移方程来求解最大利润。

// 具体步骤如下:

// 定义状态:使用一个一维数组 dp 来表示在第 i 天的最大利润,dp[i] 表示第 i 天卖出股票所能获取的最大利润。
// 初始化状态:dp[0] 初始化为 0。
// 状态转移方程:对于第 i 天,可以选择卖出股票或者不卖出股票。如果选择卖出股票,则最大利润为 prices[i] - minPrice,其中 minPrice 表示前 i 天的最低价格。如果选择不卖出股票,则最大利润为前一天的最大利润 dp[i-1]。因此,状态转移方程为 dp[i] = max(dp[i-1], prices[i] - minPrice)。
// 更新最低价格:在更新状态之前,需要将当前价格 prices[i] 和之前的最低价格 minPrice 进行比较,更新最低价格为较小的那个。
// 遍历结束后,返回 dp[n-1],其中 n 表示数组 prices 的长度。

  1. 买卖股票的最佳时机 II
    给你一个整数数组 prices ,其中 prices[i] 表示某支股票第 i 天的价格。
    在每一天,你可以决定是否购买和/或出售股票。你在任何时候 最多 只能持有 一股 股票。你也可以先购买,然后在 同一天 出售。
    返回 你能获得的 最大 利润 。
    示例 1:
    输入:prices = [7,1,5,3,6,4]
    输出:7
    解释:在第 2 天(股票价格 = 1)的时候买入,在第 3 天(股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5 - 1 = 4 。
    随后,在第 4 天(股票价格 = 3)的时候买入,在第 5 天(股票价格 = 6)的时候卖出, 这笔交易所能获得利润 = 6 - 3 = 3 。
    总利润为 4 + 3 = 7 。
    示例 2:
    输入:prices = [1,2,3,4,5]
    输出:4
    解释:在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5 - 1 = 4 。
    总利润为 4 。
    示例 3:
    输入:prices = [7,6,4,3,1]
    输出:0
    解释:在这种情况下, 交易无法获得正利润,所以不参与交易可以获得最大利润,最大利润为 0 。
func maxProfit(prices []int) int {
    if len(prices) <= 1 {
        return 0
    }

    maxProfit := 0
    for i := 1; i < len(prices); i++ {
        if prices[i] > prices[i-1] {
            maxProfit += prices[i] - prices[i-1]
        }
    }

    return maxProfit
}

// 使用了贪心算法来解决问题,通过每次在价格上升时买入股票,价格下降时卖出股票,从而获取最大利润。

// 具体步骤如下:

// 初始化最大利润为 0。
// 遍历股票价格数组,从第 2 天开始。
// 如果当天的股票价格比前一天高,则将差值加入最大利润中。
// 遍历结束后,返回最大利润。
// 这种方法的思路是,当股票价格上涨时,我们可以在价格上涨期间多次买入和卖出,从而获取更多的利润。而当股票价格下跌时,我们不进行任何操作。
//动态规划

func maxProfit(prices []int) int {
    if len(prices) == 0 {
        return 0
    }

    n := len(prices)
    buy := make([]int, n)  // 当前持有股票的最大利润
    sell := make([]int, n) // 当前不持有股票的最大利润

    // 初始化第一天的情况
    buy[0] = -prices[0]
    sell[0] = 0

    for i := 1; i < n; i++ {
        // 当前持有股票的最大利润,要么继续保持前一天的状态,要么今天买入
        buy[i] = max(buy[i-1], sell[i-1]-prices[i])
        // 当前不持有股票的最大利润,要么继续保持前一天的状态,要么今天卖出
        sell[i] = max(sell[i-1], buy[i-1]+prices[i])
    }

    // 最终返回最后一天不持有股票的最大利润
    return sell[n-1]
}

func max(a, b int) int {
    if a > b {
        return a
    }
    return b
}


// 使用动态规划来解决。
// 具体步骤如下:

// 定义状态:使用两个一维数组 buy 和 sell 来分别表示在第 i 天持有股票和不持有股票时的最大利润。buy[i] 表示第 i 天持有股票时的最大利润,sell[i] 表示第 i 天不持有股票时的最大利润。
// 初始化状态:buy[0] 初始化为 -prices[0],表示第 0 天持有股票的最大利润;sell[0] 初始化为 0,表示第 0 天不持有股票的最大利润。
// 状态转移方程:对于第 i 天,如果选择持有股票,则最大利润为前一天持有股票的最大利润 buy[i-1] 或者前一天不持有股票的最大利润减去当天的股价 sell[i-1] - prices[i] 中的较大值;如果选择不持有股票,则最大利润为前一天不持有股票的最大利润 sell[i-1] 或者前一天持有股票的最大利润加上当天的股价 buy[i-1] + prices[i] 中的较大值。因此,状态转移方程为 buy[i] = max(buy[i-1], sell[i-1] - prices[i]),sell[i] = max(sell[i-1], buy[i-1] + prices[i])。
// 遍历数组结束后,返回 sell[n-1],其中 n 表示数组 prices 的长度。
  1. 买卖股票的最佳时机 III
    给定一个数组,它的第 i 个元素是一支给定的股票在第 i 天的价格。
    设计一个算法来计算你所能获取的最大利润。你最多可以完成 两笔 交易。
    注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
    示例 1:
    输入:prices = [3,3,5,0,0,3,1,4]
    输出:6
    解释:在第 4 天(股票价格 = 0)的时候买入,在第 6 天(股票价格 = 3)的时候卖出,这笔交易所能获得利润 = 3-0 = 3 。
    随后,在第 7 天(股票价格 = 1)的时候买入,在第 8 天 (股票价格 = 4)的时候卖出,这笔交易所能获得利润 = 4-1 = 3 。
    示例 2:
    输入:prices = [1,2,3,4,5]
    输出:4
    解释:在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。
    注意你不能在第 1 天和第 2 天接连购买股票,之后再将它们卖出。
    因为这样属于同时参与了多笔交易,你必须在再次购买前出售掉之前的股票。
    示例 3:
    输入:prices = [7,6,4,3,1]
    输出:0
    解释:在这个情况下, 没有交易完成, 所以最大利润为 0。
    示例 4:
    输入:prices = [1]
    输出:0
//动态规划

func maxProfit(prices []int) int {
	if len(prices) == 0 {
		return 0
	}

	buy1 := -prices[0]
	sell1 := 0
	buy2 := -prices[0]
	sell2 := 0

	for i := 1; i < len(prices); i++ {
		buy1 = max(buy1, -prices[i])
		sell1 = max(sell1, buy1+prices[i])
		buy2 = max(buy2, sell1-prices[i])
		sell2 = max(sell2, buy2+prices[i])
	}

	return sell2
}

func max(a, b int) int {
	if a > b {
		return a
	}
	return b
}

// 可以使用动态规划来解决。

// 具体步骤如下:

// 定义状态:使用四个变量 buy1、sell1、buy2、sell2 来分别表示第一次买入、第一次卖出、第二次买入、第二次卖出时的最大利润。
// 初始化状态:buy1 初始化为 -prices[0],表示第一次买入的最大利润;sell1、buy2、sell2 初始化为 0。
// 状态转移方程:对于第 i 天,我们可以选择买入第一次、卖出第一次、买入第二次、卖出第二次或者不进行任何操作。因此,状态转移方程为:
// buy1 = max(buy1, -prices[i]),第一次买入的最大利润为前一天买入的最大利润 buy1 或者今天买入的价格 -prices[i] 中的较大值。
// sell1 = max(sell1, buy1 + prices[i]),第一次卖出的最大利润为前一天卖出的最大利润 sell1 或者今天卖出的价格加上第一次买入的最大利润 buy1 + prices[i] 中的较大值。
// buy2 = max(buy2, sell1 - prices[i]),第二次买入的最大利润为前一天买入的最大利润 buy2 或者今天买入的价格 -prices[i] 加上第一次卖出的最大利润 sell1 中的较大值。
// sell2 = max(sell2, buy2 + prices[i]),第二次卖出的最大利润为前一天卖出的最大利润 sell2 或者今天卖出的价格加上第二次买入的最大利润 buy2 + prices[i] 中的较大值。
// 遍历结束后,返回 sell2,即最大利润。
  1. 买卖股票的最佳时机 IV
    给你一个整数数组 prices 和一个整数 k ,其中 prices[i] 是某支给定的股票在第 i 天的价格。
    设计一个算法来计算你所能获取的最大利润。你最多可以完成 k 笔交易。也就是说,你最多可以买 k 次,卖 k 次。
    注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
    示例 1:
    输入:k = 2, prices = [2,4,1]
    输出:2
    解释:在第 1 天 (股票价格 = 2) 的时候买入,在第 2 天 (股票价格 = 4) 的时候卖出,这笔交易所能获得利润 = 4-2 = 2 。
    示例 2:
    输入:k = 2, prices = [3,2,6,5,0,3]
    输出:7
    解释:在第 2 天 (股票价格 = 2) 的时候买入,在第 3 天 (股票价格 = 6) 的时候卖出, 这笔交易所能获得利润 = 6-2 = 4 。
    随后,在第 5 天 (股票价格 = 0) 的时候买入,在第 6 天 (股票价格 = 3) 的时候卖出, 这笔交易所能获得利润 = 3-0 = 3 。
//动态规划

func maxProfit(k int, prices []int) int {
	if k == 0 || len(prices) == 0 {
		return 0
	}

// 定义了一个辅助函数 maxProfitUnlimited 来计算不限制交易次数时的最大利润。
	if k >= len(prices)/2 {
		return maxProfitUnlimited(prices)
	}

	dp := make([][]int, len(prices))
	for i := 0; i < len(prices); i++ {
		dp[i] = make([]int, k+1)
	}

	for j := 1; j <= k; j++ {
		buy := -prices[0]
		for i := 1; i < len(prices); i++ {
			dp[i][j] = max(dp[i-1][j], prices[i]+buy)
			buy = max(buy, dp[i-1][j-1]-prices[i])
		}
	}

	return dp[len(prices)-1][k]
}

// 和122. 买卖股票的最佳时机 II 使用贪心解法一样
// 定义了一个辅助函数 maxProfitUnlimited 来计算不限制交易次数时的最大利润。
func maxProfitUnlimited(prices []int) int {
	profit := 0

	for i := 1; i < len(prices); i++ {
		if prices[i] > prices[i-1] {
			profit += prices[i] - prices[i-1]
		}
	}

	return profit
}

func max(x, y int) int {
	if x > y {
		return x
	}
	return y
}

// 可以使用动态规划来解决。
// 定义一个二维数组 dp,其中 dp[i][j] 表示在第 i 天进行了 j 笔交易的最大利润。
// 具体步骤如下:

// 初始化 dp 为一个大小为 (len(prices), k+1) 的二维数组,所有元素初始化为 0。
// 对于每一天 i,对于每一笔交易次数 j:
// 如果 j = 0,表示没有交易,利润为 0。
// 如果 i = 0,表示第一天,利润为 0。
// 否则,根据以下两种情况来更新 dp[i][j]:
// 第一种情况是在第 i 天不进行交易,即 dp[i][j] = dp[i-1][j]。
// 第二种情况是在第 i 天进行交易,即在前 i-1 天进行了 j-1 笔交易,然后在第 i 天买入股票,再在第 i 天卖出股票,即 dp[i][j] = dp[i-1][j-1] + prices[i] - prices[i-1]。
// 返回 dp[len(prices)-1][k],即为最大利润。

// 在这个解法中,首先判断特殊情况,如果 k = 0 或者 prices 数组为空,直接返回 0。
// 然后,初始化二维数组 dp,并且对于每一天和每一笔交易次数,根据上述的两种情况来更新 dp。
// 最后,返回 dp[len(prices)-1][k],即为最大利润。
  • 23
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值