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

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

  1. 买卖股票的最佳时机 给定一个数组 prices ,它的第 i 个元素 pricesi 表示一支给定股票第 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。
go 复制代码
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
}
go 复制代码
//动态规划解答

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 ,其中 pricesi 表示某支股票第 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 。
go 复制代码
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 天开始。
// 如果当天的股票价格比前一天高,则将差值加入最大利润中。
// 遍历结束后,返回最大利润。
// 这种方法的思路是,当股票价格上涨时,我们可以在价格上涨期间多次买入和卖出,从而获取更多的利润。而当股票价格下跌时,我们不进行任何操作。
go 复制代码
//动态规划

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
go 复制代码
//动态规划

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 ,其中 pricesi 是某支给定的股票在第 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 。
go 复制代码
//动态规划

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],即为最大利润。
相关推荐
智者知已应修善业13 分钟前
【51单片机8位数码管同时倒计时从9999】2024-1-25
c++·经验分享·笔记·算法·51单片机
洛水水16 分钟前
【力扣100题】86.柱状图中最大的矩形
算法·leetcode·职场和发展
渡之23 分钟前
GRiM-Net 深度解析 | 无人机 GNSS 拒止场景下两阶段跨视角视觉定位框架
深度学习·算法·动态规划·无人机
测试仪器廖生1359025638542 分钟前
罗德与施瓦茨 FSP13频谱分析仪FSP30
网络·人工智能·算法
happymaker06261 小时前
LeetCodeHot100——560.和为K的子数组
算法
dtq04241 小时前
C语言刷题数组5,6(求平均值,求最大值)
c语言·数据结构·算法
郭梧悠1 小时前
Hash算法入门Hash冲突解决方案
算法·哈希算法
洛水水2 小时前
【力扣100题】81.寻找两个正序数组的中位数
数据结构·算法·leetcode
happymaker06262 小时前
LeetCodeHot100——155.最小栈
算法
洛水水3 小时前
【力扣100题】85.每日温度
算法·leetcode·职场和发展