代码随想录-Day48

188. 买卖股票的最佳时机 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 。
在这里插入图片描述

方法一:三维 dp数组

// 版本一: 三维 dp数组
class Solution {public int maxProfit(int k, int[] prices) {if (prices.length == 0) return 0;// [天数][交易次数][是否持有股票]int len = prices.length;int[][][] dp = new int[len][k + 1][2];// dp数组初始化// 初始化所有的交易次数是为确保 最后结果是最多 k 次买卖的最大利润for (int i = 0; i <= k; i++) {dp[0][i][1] = -prices[0];}for (int i = 1; i < len; i++) {for (int j = 1; j <= k; j++) {// dp方程, 0表示不持有/卖出, 1表示持有/买入dp[i][j][0] = Math.max(dp[i - 1][j][0], dp[i - 1][j][1] + prices[i]);dp[i][j][1] = Math.max(dp[i - 1][j][1], dp[i - 1][j - 1][0] - prices[i]);}}return dp[len - 1][k][0];}
}

这段代码展示了如何使用三维动态规划数组来解决一个更通用的问题:给定一个整数 k 和一个整数数组 prices,找出在最多进行 k 次买卖交易的情况下可以获取的最大利润。这里的 prices 数组表示股票每天的价格,而 k 表示允许进行买卖交易的最大次数。

在三维数组 dp 中:

  • 第一维表示时间(天数),范围是 0len - 1len 是价格数组的长度)。
  • 第二维表示当前允许的交易次数,范围是 0k
  • 第三维是一个二元组,其中 0 表示不持有股票(即处于卖出状态),1 表示持有股票(即处于买入状态)。

初始化时,dp[0][i][1] 被设置为 -prices[0] 对于所有 i(表示在第一天买入股票的成本),而 dp[0][i][0] 被隐式初始化为 0(因为还没有进行任何交易)。

动态规划的状态转移方程如下:

  • dp[i][j][0] 更新为前一天 dp[i-1][j][0] 和前一天持有股票并今天卖出 dp[i-1][j][1] + prices[i] 中的较大值。
  • dp[i][j][1] 更新为前一天 dp[i-1][j][1] 和前一天未持有股票(即前一天卖出或没有进行过任何交易)并且今天买入 dp[i-1][j-1][0] - prices[i] 中的较大值。

最终,dp[len - 1][k][0] 将包含在给定价格序列下最多进行 k 次买卖交易所能获得的最大利润。

现在,让我们使用同样的股票价格数组 [7, 1, 5, 3, 6, 4] 并设定 k=2(允许进行两次交易),来计算最大利润。

def max_profit_k_transactions(prices, k):len_prices = len(prices)# Initialize DP arraydp = [[[0 for _ in range(2)] for _ in range(k + 1)] for _ in range(len_prices)]# Initialize all transactions to ensure the last result is the maximum profit with at most k transactionsfor i in range(k + 1):dp[0][i][1] = -prices[0]# DP calculationfor i in range(1, len_prices):for j in range(1, k + 1):dp[i][j][0] = max(dp[i - 1][j][0], dp[i - 1][j][1] + prices[i])dp[i][j][1] = max(dp[i - 1][j][1], dp[i - 1][j - 1][0] - prices[i])return dp[len_prices - 1][k][0]# Using the same example stock price array and setting k=2
prices = [7, 1, 5, 3, 6, 4]
k = 2
max_profit_value_k_transactions = max_profit_k_transactions(prices, k)
max_profit_value_k_transactions

调用结果
7

使用三维动态规划数组的方法,对于股票价格数组 [7, 1, 5, 3, 6, 4] 并且允许最多进行两次交易 (k=2) 的情况下,计算出的最大利润同样是 7

这表明算法正确地找到了在给定价格序列和交易次数限制下可以获得的最大利润。这种方法虽然空间复杂度较高(O(n*k)),但在 k 较小的情况下,它能有效地解决问题,并且提供了处理任意次数交易的能力。

如果您有任何疑问,或者想要了解其他相关问题,请随时告诉我!

方法二:二维dp数组

// 版本二: 二维 dp数组
class Solution {public int maxProfit(int k, int[] prices) {if (prices.length == 0) return 0;// [天数][股票状态]// 股票状态: 奇数表示第 k 次交易持有/买入, 偶数表示第 k 次交易不持有/卖出, 0 表示没有操作int len = prices.length;int[][] dp = new int[len][k*2 + 1];// dp数组的初始化, 与版本一同理for (int i = 1; i < k*2; i += 2) {dp[0][i] = -prices[0];}for (int i = 1; i < len; i++) {for (int j = 0; j < k*2 - 1; j += 2) {dp[i][j + 1] = Math.max(dp[i - 1][j + 1], dp[i - 1][j] - prices[i]);dp[i][j + 2] = Math.max(dp[i - 1][j + 2], dp[i - 1][j + 1] + prices[i]);}}return dp[len - 1][k*2];}
}

这个版本的代码通过使用二维动态规划数组来优化空间复杂度,同时仍然能够计算最多进行 k 次买卖交易的最大利润。与之前的三维数组版本相比,这个版本的数组只包含两个维度:天数和股票状态。

在这个二维数组 dp 中:

  • 第一维表示时间(天数),范围是 0len - 1len 是价格数组的长度)。
  • 第二维表示股票状态,其中奇数索引(从 1 开始)表示持有股票(买入状态),偶数索引(从 0 开始)表示不持有股票(卖出状态)。这里利用了 k * 2 + 1 的大小来容纳所有可能的交易状态,包括不进行任何交易的情况。

初始化时,所有奇数索引的 dp[0][i] 被设置为 -prices[0],这代表在第一天买入股票的成本。

动态规划的状态转移方程简化为:

  • dp[i][j + 1] 更新为前一天持有股票状态 dp[i-1][j + 1] 和前一天不持有股票状态并今天买入 dp[i-1][j] - prices[i] 中的较大值。
  • dp[i][j + 2] 更新为前一天不持有股票状态 dp[i-1][j + 2] 和前一天持有股票状态并今天卖出 dp[i-1][j + 1] + prices[i] 中的较大值。

由于数组的第二维表示的是状态,所以不需要再单独跟踪交易次数;状态自动编码了交易次数的信息。最后一天的最后一个状态(dp[len - 1][k*2])将包含在给定价格序列下最多进行 k 次买卖交易所能获得的最大利润。

现在,我们使用相同的股票价格数组 [7, 1, 5, 3, 6, 4]k=2 来计算最大利润。

def max_profit_k_transactions_2d(prices, k):len_prices = len(prices)# Initialize DP arraydp = [[0 for _ in range(k * 2 + 1)] for _ in range(len_prices)]# Initialize buy states to ensure the last result is the maximum profit with at most k transactionsfor i in range(1, k * 2, 2):dp[0][i] = -prices[0]# DP calculationfor i in range(1, len_prices):for j in range(0, k * 2 - 1, 2):dp[i][j + 1] = max(dp[i - 1][j + 1], dp[i - 1][j] - prices[i])dp[i][j + 2] = max(dp[i - 1][j + 2], dp[i - 1][j + 1] + prices[i])return dp[len_prices - 1][k * 2]# Using the same example stock price array and setting k=2
prices = [7, 1, 5, 3, 6, 4]
k = 2
max_profit_value_k_transactions_2d = max_profit_k_transactions_2d(prices, k)
max_profit_value_k_transactions_2d

调用结果
7
使用二维动态规划数组的方法,对于相同的股票价格数组 [7, 1, 5, 3, 6, 4] 和允许最多进行两次交易 (k=2) 的情况下,计算出的最大利润同样为 7

这表明即使在减少了一个维度的情况下,算法依然能够正确找到在给定价格序列和交易次数限制下的最大利润。这种方法的空间复杂度降低至 O(n),其中 n 是价格数组的长度,但时间复杂度保持不变,依然是 O(n*k)。这种优化对于处理大规模数据集尤其有用,因为它减少了内存使用量,同时也可能提高运行速度,特别是在 k 的值相对较大的时候。

如果您对这个方法有任何疑问,或者想进一步讨论相关问题,欢迎继续提问!

方法三:一维dp数组

//版本三:一维 dp数组 (下面有和卡哥邏輯一致的一維數組JAVA解法)
class Solution {public int maxProfit(int k, int[] prices) {if(prices.length == 0){return 0;}if(k == 0){return 0;}// 其实就是123题的扩展,123题只用记录2次交易的状态// 这里记录k次交易的状态就行了// 每次交易都有买入,卖出两个状态,所以要乘 2int[] dp = new int[2 * k];// 按123题解题格式那样,做一个初始化for(int i = 0; i < dp.length / 2; i++){dp[i * 2] = -prices[0];}for(int i = 1; i <= prices.length; i++){dp[0] = Math.max(dp[0], -prices[i - 1]);dp[1] = Math.max(dp[1], dp[0] + prices[i - 1]);// 还是与123题一样,与123题对照来看// 就很容易啦for(int j = 2; j < dp.length; j += 2){dp[j] = Math.max(dp[j], dp[j - 1] - prices[i-1]);dp[j + 1] = Math.max(dp[j + 1], dp[j] + prices[i - 1]);}}// 返回最后一次交易卖出状态的结果就行了return dp[dp.length - 1];}
}

这个版本的代码进一步优化了空间复杂度,使用了一维动态规划数组来存储中间结果。与之前二维数组版本相比,它不再需要一个完整的列来保存每一天的状态,而是直接在同一个数组上更新状态。这种优化使得空间复杂度降低到 O(k),其中 k 是允许的最大交易次数。

在这个一维数组 dp 中:

  • 数组的大小为 2 * k,其中奇数位置表示持有股票(买入状态),偶数位置表示不持有股票(卖出状态)。
  • 初始化时,所有偶数位置被设置为 -prices[0],即第一次买入股票的成本。
  • 动态规划的状态转移方程与二维数组版本相似,但是因为数组是一维的,所以需要在循环中交替更新状态。

关键点在于如何在不保留历史数据的情况下正确地更新 dp 数组。为了确保状态的正确性,在每次更新前,先更新买入状态,再更新卖出状态,这样可以避免未来状态影响过去状态的问题。

现在,让我们使用相同的价格数组 [7, 1, 5, 3, 6, 4]k=2 来验证这个一维数组方法是否也能得到正确答案。

def max_profit_k_transactions_1d(prices, k):if not prices or k == 0:return 0dp = [0] * (2 * k)for i in range(0, k):dp[i * 2] = -prices[0]for i in range(1, len(prices) + 1):dp[0] = max(dp[0], -prices[i - 1])dp[1] = max(dp[1], dp[0] + prices[i - 1])for j in range(2, 2 * k, 2):dp[j] = max(dp[j], dp[j - 1] - prices[i - 1])dp[j + 1] = max(dp[j + 1], dp[j] + prices[i - 1])return dp[-1]# Using the same example stock price array and setting k=2
prices = [7, 1, 5, 3, 6, 4]
k = 2
max_profit_value_k_transactions_1d = max_profit_k_transactions_1d(prices, k)
max_profit_value_k_transactions_1d

调用结果
7
使用一维动态规划数组的方法,对于相同的股票价格数组 [7, 1, 5, 3, 6, 4] 和允许最多进行两次交易 (k=2) 的情况下,计算出的最大利润同样为 7

这证明了一维数组版本的代码同样能够正确地解决问题,并且在空间效率方面比之前的版本更优。这种方法特别适用于当 k 较大时,因为其空间复杂度仅取决于 k 而非 n,从而减少了大量内存的使用。

如果您对这个一维数组版本的算法有任何疑问,或想要了解关于动态规划的更多细节,随时欢迎提问!

方法四:

class Solution {public int maxProfit(int k, int[] prices) {//edge casesif(prices.length == 0 || k == 0)return 0;int dp[] = new int [k * 2 + 1];//和卡哥邏輯一致,奇數天購入股票,故初始化只初始化奇數天。for(int i = 1; i < 2 * k + 1; i += 2){dp[i] = -prices[0];}for(int i = 1; i < prices.length; i++){ //i 從 1 開始,因爲第 i = 0 天已經透過初始化完成了。for(int j = 1; j < 2 * k + 1; j++){ //j 從 1 開始,因爲第 j = 0 天已經透過初始化完成了。//奇數天購買if(j % 2 == 1)dp[j] = Math.max(dp[j], dp[j - 1] - prices[i]);//偶數天賣出elsedp[j] = Math.max(dp[j], dp[j - 1] + prices[i]);}//打印DP數組//for(int x : dp)//    System.out.print(x +", ");//System.out.println();}//return 第2 * k次賣出的獲利。return dp[2 * k];}
}

这段代码实现了一个基于一维动态规划数组的解决方案,用于计算给定一系列股票价格和一个整数 k(最大交易次数)情况下的最大可能利润。以下是代码的详细解释:

  1. Edge Cases:

    • 如果价格数组为空或者 k(最大交易次数)为0,则直接返回0,因为没有交易发生。
  2. 初始化动态规划数组:

    • 创建一个长度为 k * 2 + 1 的数组 dp。这里 + 1 是为了确保数组包含 k 次交易后的最终状态。
    • 因为数组中的奇数位置代表持有股票的状态(买入),所以这些位置初始化为 -prices[0]。这是假设在第一天购买股票的亏损(负利润),因为还没有卖出。
  3. 动态规划迭代:

    • 从第二天开始(i = 1),遍历价格数组。
    • 对于每一个价格 prices[i],遍历动态规划数组 dp(从 j = 1 开始)。
      • j 是奇数时,表示我们处于买入状态。此时,我们计算如果今天买入股票所能获得的最大利润(或者最小亏损)。因此,我们比较 dp[j - 1] - prices[i] 和当前 dp[j] 的值,选择较大者。
      • j 是偶数时,表示我们处于卖出状态。此时,我们计算如果今天卖出股票所能获得的最大利润。因此,我们比较 dp[j - 1] + prices[i] 和当前 dp[j] 的值,选择较大者。
  4. 返回值:

    • 最终,dp[2 * k] 表示在进行了 k 次交易后(即进行了 k 次买入和 k 次卖出),所获得的最大利润。

注意到代码中有一个小错误,它在更新 dp[j] 的时候没有考虑到 j 可能超出 2 * k 的范围,但是由于 dp 数组的长度设置为 k * 2 + 1,所以实际上这个错误不会导致运行时错误,但理论上来说,dp 的有效索引范围应该是 [0, 2 * k]

此外,迭代过程中 dp[j - 1] 的使用是合理的,因为它表示上一个状态,而上一个状态可能是买入也可能是卖出,这取决于 j 的奇偶性。

最后,代码中注释掉的部分是用来打印每次迭代后 dp 数组的值,这对于调试和理解动态规划的过程非常有用。

309. 买卖股票的最佳时机含冷冻期

给定一个整数数组prices,其中第 prices[i] 表示第 i 天的股票价格 。​

设计一个算法计算出最大利润。在满足以下约束条件下,你可以尽可能地完成更多的交易(多次买卖一支股票):

卖出股票后,你无法在第二天买入股票 (即冷冻期为 1 天)。
注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。

示例 1:

输入: prices = [1,2,3,0,2]
输出: 3
解释: 对应的交易状态为: [买入, 卖出, 冷冻期, 买入, 卖出]
示例 2:

输入: prices = [1]
输出: 0
在这里插入图片描述
在这里插入图片描述

方法一:

class Solution {public int maxProfit(int[] prices) {if (prices == null || prices.length < 2) {return 0;}int[][] dp = new int[prices.length][2];// bad casedp[0][0] = 0;dp[0][1] = -prices[0];dp[1][0] = Math.max(dp[0][0], dp[0][1] + prices[1]);dp[1][1] = Math.max(dp[0][1], -prices[1]);for (int i = 2; i < prices.length; i++) {// dp公式dp[i][0] = Math.max(dp[i - 1][0], dp[i - 1][1] + prices[i]);dp[i][1] = Math.max(dp[i - 1][1], dp[i - 2][0] - prices[i]);}return dp[prices.length - 1][0];}
}

这段代码是一个Java实现,用于解决股票买卖问题中的一个变种:允许进行多次交易,但是每次交易之后必须休息一天才能进行下一次交易。这个问题通过动态规划的方法解决。下面是代码的详细解释:

  1. 初始化:

    • 首先检查输入数组是否为null或长度小于2,如果是,则直接返回0,因为没有足够的数据进行交易。
  2. 创建动态规划数组:

    • 创建一个二维数组 dp,其中 dp[i][0] 表示在第 i 天结束时不持有股票所能获得的最大利润,dp[i][1] 表示在第 i 天结束时持有股票所能获得的最大利润。
  3. 处理边界情况:

    • 第0天,不持有股票的最大利润是0,持有股票的最大利润是买入股票的成本(用负数表示亏损)。
    • 第1天,更新 dp[1][0]dp[1][1] 的值。dp[1][0] 表示在第1天结束时不持有股票的最大利润,它可以是从第0天开始就不持有股票 (dp[0][0]) 或者第0天持有股票并在第1天卖出 (dp[0][1] + prices[1]) 中取较大的一个;dp[1][1] 表示在第1天结束时持有股票的最大利润,它可以是第0天就持有股票 (dp[0][1]) 或者在第1天买入股票 (-prices[1]) 中取较大的一个。
  4. 动态规划迭代:

    • 从第2天开始,对于每一天,我们更新 dp[i][0]dp[i][1] 的值。
      • dp[i][0] 更新逻辑:在第 i 天结束时不持有股票的最大利润,可以从第 i-1 天就不持有股票 (dp[i-1][0]) 或者第 i-1 天持有股票并在第 i 天卖出 (dp[i-1][1] + prices[i]) 中取较大的一个。
      • dp[i][1] 更新逻辑:在第 i 天结束时持有股票的最大利润,可以从第 i-1 天就持有股票 (dp[i-1][1]) 或者在第 i 天买入股票,但因为规则限制,需要考虑在第 i-2 天结束时不持有股票 (dp[i-2][0] - prices[i]) 中取较大的一个。
  5. 返回结果:

    • 返回 dp[prices.length - 1][0],即在最后一天结束时不持有股票所能获得的最大利润。

注意,这里的 dp[i-2][0] 在更新 dp[i][1] 的时候使用,是因为题目规定了每次交易之后必须休息一天,所以不能连续两天都进行买卖操作。这样,dp[i][1] 的值要么继承自前一天的 dp[i-1][1](保持持有股票),要么来自前前天的 dp[i-2][0](卖出了股票并休息了一天后重新买入)。

方法二:

//using 2*4 array for space optimization
//這裡稍微說一下,我在LeetCode提交的時候,2*4 2-D array的performance基本上和下面的1-D array performance差不多
//都是time: 1ms, space: 40.X MB (其實 length*4 的 2-D array也僅僅是space:41.X MB,看起來不多)
//股票累的DP題目大致上都是這樣,就當作是一個延伸就好了。真的有人問如何優化,最起碼有東西可以講。
class Solution {/**1. [i][0] holding the stock2. [i][1] after cooldown but stil not buing the stock3. [i][2] selling the stock4. [i][3] cooldown*/public int maxProfit(int[] prices) {int len = prices.length;int dp[][] = new int [2][4];dp[0][0] = -prices[0];for(int i = 1; i < len; i++){dp[i % 2][0] = Math.max(Math.max(dp[(i - 1) % 2][0], dp[(i - 1) % 2][1] - prices[i]), dp[(i - 1) % 2][3] - prices[i]);dp[i % 2][1] = Math.max(dp[(i - 1) % 2][1], dp[(i - 1) % 2][3]);dp[i % 2][2] = dp[(i - 1) % 2][0] + prices[i];dp[i % 2][3] = dp[(i - 1) % 2][2];}return Math.max(Math.max(dp[(len - 1) % 2][1], dp[(len - 1) % 2][2]), dp[(len - 1) % 2][3]);}
}

这段代码提供了一种优化的空间复杂度解法,用于解决股票买卖问题中的一个变体,其中在卖出股票后需要等待一天的冷却期才能再次买入。这种变体要求我们找到能够最大化利润的交易策略。

解析

状态定义
  • dp[i % 2][0]: 表示在第 i 天结束时,手中持有股票的最大利润。
  • dp[i % 2][1]: 表示在第 i 天结束时,处于冷却期但不持有股票的最大利润。
  • dp[i % 2][2]: 表示在第 i 天结束时,刚刚卖出股票(不再处于冷却期)的最大利润。
  • dp[i % 2][3]: 表示在第 i 天结束时,处于冷却期的最大利润。
边界条件
  • 初始化 dp[0][0]-prices[0],表示在第0天买入股票的初始状态。
动态转移方程
  • dp[i % 2][0]: 要么保持昨天持有股票的状态 dp[(i - 1) % 2][0],要么昨天未持有股票(冷却期或未买入状态)并今天买入股票 dp[(i - 1) % 2][1] - prices[i]dp[(i - 1) % 2][3] - prices[i]
  • dp[i % 2][1]: 保持昨天冷却期的状态 dp[(i - 1) % 2][1] 或者从昨天卖出股票进入冷却期 dp[(i - 1) % 2][3]
  • dp[i % 2][2]: 由昨天持有股票的状态卖出股票得到 dp[(i - 1) % 2][0] + prices[i]
  • dp[i % 2][3]: 直接由昨天卖出股票的状态继承 dp[(i - 1) % 2][2]
结果返回
  • 返回最后一天所有四种状态中的最大值,即 Math.max(Math.max(dp[(len - 1) % 2][1], dp[(len - 1) % 2][2]), dp[(len - 1) % 2][3])

为什么使用i % 2

使用 i % 2 的目的是为了空间优化。由于动态规划只依赖于前两个状态,我们可以将 dp 数组设计为只有两行,交替使用这两行存储当前和前一时刻的状态,从而将空间复杂度降低到 O(1)(不考虑输出结果的空间消耗)。

这种方法称为“空间优化”,通常在动态规划问题中用来减少所需的额外空间。在本例中,它使得原本需要 O(n) 空间的算法仅使用常数级别的额外空间。

方法三:

// 一维数组优化
class Solution {public int maxProfit(int[] prices) {int[] dp=new int[4];dp[0] = -prices[0];dp[1] = 0;for(int i = 1; i <= prices.length; i++){// 使用临时变量来保存dp[0], dp[2]// 因为马上dp[0]和dp[2]的数据都会变 int temp = dp[0];int temp1 = dp[2];dp[0] = Math.max(dp[0], Math.max(dp[3], dp[1]) - prices[i]);dp[1] = Math.max(dp[1], dp[3]);dp[2] = temp + prices[i];dp[3] = temp1;}return Math.max(dp[3],Math.max(dp[1],dp[2]));}
}

这段代码展示了如何使用一维数组进行空间优化,以解决带有冷却期的股票买卖问题。目标是在遵循以下规则的情况下,计算出最大的可能利润:

  1. 你可以在任何时候买入一支股票。
  2. 你只能在买入后某一天卖出该股票。
  3. 卖出股票后,你无法在接下来的一天买入股票(即冷却期)。

代码解析

状态定义
  • dp[0]:表示在第 i 天结束时,手中持有股票的最大利润。
  • dp[1]:表示在第 i 天结束时,处于冷却期但不持有股票的最大利润。
  • dp[2]:表示在第 i 天结束时,刚刚卖出股票(不再处于冷却期)的最大利润。
  • dp[3]:表示在第 i 天结束时,处于冷却期的最大利润,与 dp[1] 类似,但在上下文中,它代表了前一时刻卖出股票后的状态。
初始化
  • dp[0] = -prices[0]:表示在第0天买入股票的初始状态。
  • dp[1] = 0:表示在第0天没有进行任何操作,不持有股票且不在冷却期。
动态转移方程

循环从 i = 1prices.length,在每一步中:

  • dp[0]:要更新为持有股票的最大利润,这包括从上一天持有股票继续持有 (dp[0])、从冷却期买入股票 (dp[3] - prices[i]) 或者从非冷却期买入股票 (dp[1] - prices[i])。
  • dp[1]:更新为在冷却期但不持有股票的最大利润,这主要是从上一天的冷却期状态继续 (dp[1]) 或者从卖出股票后进入冷却期 (dp[3])。
  • dp[2]:更新为刚卖出股票的最大利润,这来自于上一天持有股票的状态 (temp + prices[i]),其中 temp 是上一天 dp[0] 的值。
  • dp[3]:更新为在冷却期的最大利润,这直接来自于上一天的 dp[2],即 temp1
返回值

最后返回所有状态中的最大值,即 Math.max(dp[3],Math.max(dp[1],dp[2]))

空间优化

使用一维数组 dp 来代替原本可能需要的二维数组,通过循环更新数组中的值,从而减少了空间复杂度。这种方法利用了动态规划中状态之间的依赖关系,只保留当前和前一个状态的信息,避免了存储整个序列的历史状态,实现了空间优化。

这种方法将空间复杂度从 O(n) 减少到了 O(1)(忽略数组大小对 n 的依赖,因为数组大小是固定的),同时保持了时间复杂度为 O(n)

方法四:

//另一种解题思路
class Solution {public int maxProfit(int[] prices) {int[][] dp = new int[prices.length + 1][2];dp[1][0] = -prices[0];for (int i = 2; i <= prices.length; i++) {/*dp[i][0] 第i天持有股票收益;dp[i][1] 第i天不持有股票收益;情况一:第i天是冷静期,不能以dp[i-1][1]购买股票,所以以dp[i - 2][1]买股票,没问题情况二:第i天不是冷静期,理论上应该以dp[i-1][1]购买股票,但是第i天不是冷静期说明,第i-1天没有卖出股票,则dp[i-1][1]=dp[i-2][1],所以可以用dp[i-2][1]买股票,没问题*/dp[i][0] = Math.max(dp[i - 1][0], dp[i - 2][1] - prices[i - 1]);dp[i][1] = Math.max(dp[i - 1][1], dp[i - 1][0] + prices[i - 1]);}return dp[prices.length][1];}
}

这段代码提供了另一种解决带冷却期的股票买卖问题的动态规划方法。其核心思想在于维护两个状态:在某一天持有股票的最大利润和不持有股票的最大利润,并通过动态规划的方式逐步更新这两个状态,最终求得最大利润。下面是详细的解析:

状态定义

  • dp[i][0]:表示在第 i 天结束时,手中持有股票的最大利润。
  • dp[i][1]:表示在第 i 天结束时,不持有股票的最大利润(可能是因为卖出了股票,也可能是因为在冷却期内)。

初始化

  • dp[1][0] = -prices[0]:表示在第1天买入股票的初始状态,即花费了 prices[0] 的成本。
  • dp[i][1]:对于 i = 1,默认初始化为0,表示没有进行任何操作,不持有股票。

动态转移方程

  • dp[i][0]:表示在第 i 天持有股票的最大利润,可以是:

    • 从上一天持有股票的状态继续持有 (dp[i - 1][0])。
    • 或者从两天前不持有股票的状态买入股票 (dp[i - 2][1] - prices[i - 1])。这是因为卖出股票后有一日的冷却期,所以只能从两天前的不持有股票状态买入。
  • dp[i][1]:表示在第 i 天不持有股票的最大利润,可以是:

    • 从上一天不持有股票的状态继续不持有 (dp[i - 1][1])。
    • 或者从上一天持有股票的状态卖出股票 (dp[i - 1][0] + prices[i - 1])。

返回值

最后返回 dp[prices.length][1],即在最后一天不持有股票的最大利润。

解释

此代码通过动态规划方式,逐日更新两种状态(持有股票和不持有股票的最大利润),并且考虑到卖出股票后的冷却期,有效地解决了问题。通过对比前一天和前前一天的状态,以及当天的股票价格,计算出在当前状态下持有或不持有股票的最大可能利润,最终找到整个序列中的最大利润。

这种方法将时间复杂度保持在 O(n),同时空间复杂度为 O(n),但由于使用的是 prices.length + 1 长度的数组,实际的空间需求与输入数据的大小线性相关。可以通过进一步的空间优化(如使用滚动数组)将其降至 O(1)

714. 买卖股票的最佳时机含手续费

给定一个整数数组 prices,其中 prices[i]表示第 i 天的股票价格 ;整数 fee 代表了交易股票的手续费用。

你可以无限次地完成交易,但是你每笔交易都需要付手续费。如果你已经购买了一个股票,在卖出它之前你就不能再继续购买股票了。

返回获得利润的最大值。

注意:这里的一笔交易指买入持有并卖出股票的整个过程,每笔交易你只需要为支付一次手续费。

示例 1:

输入:prices = [1, 3, 2, 8, 4, 9], fee = 2
输出:8
解释:能够达到的最大利润:
在此处买入 prices[0] = 1
在此处卖出 prices[3] = 8
在此处买入 prices[4] = 4
在此处卖出 prices[5] = 9
总利润: ((8 - 1) - 2) + ((9 - 4) - 2) = 8
示例 2:

输入:prices = [1,3,7,5,10,3], fee = 3
输出:6

方法一:

/*** 卖出时支付手续费* @param prices* @param fee* @return*/
public int maxProfit(int[] prices, int fee) {int len = prices.length;// 0 : 持股(买入)// 1 : 不持股(售出)// dp 定义第i天持股/不持股 所得最多现金int[][] dp = new int[len][2];dp[0][0] = -prices[0];for (int i = 1; i < len; i++) {dp[i][0] = Math.max(dp[i - 1][0], dp[i - 1][1] - prices[i]);dp[i][1] = Math.max(dp[i - 1][0] + prices[i] - fee, dp[i - 1][1]);}return Math.max(dp[len - 1][0], dp[len - 1][1]);
}

这段代码提供了解决股票买卖问题的一个变体——在卖出股票时需要支付一定手续费的情况下的最优策略。目标是在遵循以下规则的情况下,计算出最大的可能利润:

  1. 你可以在任何时候买入一支股票。
  2. 你只能在买入后某一天卖出该股票。
  3. 卖出股票时,需要支付一笔固定的手续费。

代码解析

状态定义
  • dp[i][0]:表示在第 i 天结束时,手中持有股票的最大现金量(实际上是负数,表示买入股票后的亏损,因为尚未卖出)。
  • dp[i][1]:表示在第 i 天结束时,不持有股票的最大现金量(卖出股票后扣除手续费后的金额)。
初始化
  • dp[0][0] = -prices[0]:表示在第0天买入股票的初始状态,即花费了 prices[0] 的成本。
  • dp[0][1] = 0:默认初始化为0,表示没有进行任何操作,不持有股票。
动态转移方程

循环从 i = 1prices.length,在每一步中:

  • dp[i][0]:要更新为持有股票的最大现金量,这包括从上一天持有股票继续持有 (dp[i - 1][0]) 或者从上一天不持有股票状态买入股票 (dp[i - 1][1] - prices[i])。
  • dp[i][1]:要更新为不持有股票的最大现金量,这包括从上一天卖出股票扣除手续费 (dp[i - 1][0] + prices[i] - fee) 或者从上一天不持有股票状态继续不持有 (dp[i - 1][1])。
返回值

最后返回所有状态中的最大值,即 Math.max(dp[len - 1][0], dp[len - 1][1])。然而,由于 dp[i][0] 实际上表示的是负数(买入股票的亏损),最终返回的结果应当是 dp[len - 1][1],因为在不持有股票的状态下才有可能得到正向的利润。

时间与空间复杂度分析

  • 时间复杂度O(n),其中 n 是数组 prices 的长度。因为算法遍历了整个数组一次。
  • 空间复杂度O(n),主要来自于 dp 数组的使用。尽管实际的计算只依赖于前一个状态,可以进一步优化到 O(1) 的空间复杂度,例如使用滚动数组技术。

总结

这段代码有效地解决了股票买卖问题的一种特殊情况,即卖出时需要支付手续费的问题,通过动态规划的方式逐步更新两种状态(持有股票和不持有股票的最大现金量),并最终求得最大利润。

方法二:

/*** 买入时支付手续费* @param prices* @param fee* @return*/
public int maxProfit(int[] prices, int fee) {int len = prices.length;// 0 : 持股(买入)// 1 : 不持股(售出)// dp 定义第i天持股/不持股 所得最多现金int[][] dp = new int[len][2];// 考虑买入的时候就支付手续费dp[0][0] = -prices[0] - fee;for (int i = 1; i < len; i++) {dp[i][0] = Math.max(dp[i - 1][0], dp[i - 1][1] - prices[i] - fee);dp[i][1] = Math.max(dp[i - 1][0] + prices[i], dp[i - 1][1]);}return Math.max(dp[len - 1][0], dp[len - 1][1]);
}

这段代码解决的是股票买卖问题的一个变种,其中在买入股票时需要支付一笔手续费。与之前卖出时支付手续费的情况不同,这次手续费是在买入时扣除。让我们详细解析这段代码:

状态定义

  • dp[i][0]:表示在第 i 天结束时,手中持有股票的最大现金量(实际上是负数,表示买入股票后的亏损,因为尚未卖出,并且已经支付了手续费)。
  • dp[i][1]:表示在第 i 天结束时,不持有股票的最大现金量(卖出股票后所得的金额,无需考虑手续费,因为手续费是在买入时支付的)。

初始化

  • dp[0][0] = -prices[0] - fee:表示在第0天买入股票并支付手续费的初始状态,即花费了 prices[0] 的成本加上手续费 fee
  • dp[0][1] = 0:默认初始化为0,表示没有进行任何操作,不持有股票,也没有支付手续费。

动态转移方程

循环从 i = 1prices.length,在每一步中:

  • dp[i][0]:要更新为持有股票的最大现金量,这包括从上一天持有股票继续持有 (dp[i - 1][0]) 或者从上一天不持有股票状态买入股票并支付手续费 (dp[i - 1][1] - prices[i] - fee)。
  • dp[i][1]:要更新为不持有股票的最大现金量,这包括从上一天卖出股票 (dp[i - 1][0] + prices[i]) 或者从上一天不持有股票状态继续不持有 (dp[i - 1][1])。

返回值

最后返回所有状态中的最大值,即 Math.max(dp[len - 1][0], dp[len - 1][1])。然而,由于 dp[i][0] 实际上表示的是负数(买入股票的亏损,包括手续费),最终有意义的返回结果是 dp[len - 1][1],因为在不持有股票的状态下才有可能得到正向的利润。

时间与空间复杂度分析

  • 时间复杂度O(n),其中 n 是数组 prices 的长度。算法遍历了整个数组一次。
  • 空间复杂度O(n),主要来自于 dp 数组的使用。可以进一步优化到 O(1) 的空间复杂度,例如使用滚动数组技术。

总结

这段代码有效地解决了股票买卖问题的另一个特殊变种,即买入时需要支付手续费的问题,通过动态规划的方式逐步更新两种状态(持有股票和不持有股票的最大现金量),并最终求得最大利润。需要注意的是,在实际应用中,通常只关注不持有股票状态下的最大利润,因为持有股票状态下的数值是负数,表示的是亏损。

方法三:

// 一维数组优化
class Solution {public int maxProfit(int[] prices, int fee) {int[] dp = new int[2];dp[0] = -prices[0];dp[1] = 0;for (int i = 1; i <= prices.length; i++) {dp[0] = Math.max(dp[0], dp[1] - prices[i - 1]);dp[1] = Math.max(dp[1], dp[0] + prices[i - 1] - fee);}return dp[1];}
}

这段代码是对上述问题(买入时支付手续费的股票买卖问题)的进一步优化,采用了空间优化技巧,将动态规划数组从二维压缩到一维,以减少空间复杂度。下面是对这段代码的详细解析:

状态定义

  • dp[0]:表示在第 i 天结束时,手中持有股票的最大现金量(实际上是负数,表示买入股票后的亏损,包括手续费)。
  • dp[1]:表示在第 i 天结束时,不持有股票的最大现金量(卖出股票后所得的金额,无需考虑手续费,因为手续费是在买入时支付的)。

初始化

  • dp[0] = -prices[0]:表示在第1天买入股票并支付手续费的初始状态,即花费了 prices[0] 的成本。注意,这里没有立即减去手续费,而是稍后在循环中统一处理。
  • dp[1] = 0:默认初始化为0,表示没有进行任何操作,不持有股票,也没有支付手续费。

动态转移方程

循环从 i = 1prices.length,在每一步中:

  • dp[0]:要更新为持有股票的最大现金量,这包括从上一天持有股票继续持有 (dp[0]) 或者从上一天不持有股票状态买入股票 (dp[1] - prices[i - 1])。
  • dp[1]:要更新为不持有股票的最大现金量,这包括从上一天卖出股票并减去手续费 (dp[0] + prices[i - 1] - fee) 或者从上一天不持有股票状态继续不持有 (dp[1])。

返回值

最后返回 dp[1],即在最后一天不持有股票的最大现金量。这是最终的利润,因为我们关心的是最终的盈利,而不是持有股票的亏损状态。

空间优化

这段代码的关键优化点在于使用了一维数组 dp 来替代之前的二维数组。由于在更新 dp 数组中的每个元素时,只依赖于上一个状态,因此不需要存储整个历史状态。这使得空间复杂度从原来的 O(n) 减少到了 O(1),极大地节省了空间。

时间与空间复杂度分析

  • 时间复杂度O(n),其中 n 是数组 prices 的长度。算法遍历了整个数组一次。
  • 空间复杂度O(1),使用了一维数组 dp,无论 prices 数组的长度如何,空间复杂度都是常数级别。

总结

这段代码巧妙地利用了动态规划和空间优化技巧,有效地解决了买入时支付手续费的股票买卖问题,不仅计算出了最大利润,而且在保证算法效率的同时,大幅度降低了空间复杂度。

方法四:

//使用 2*2 array
class Solution {public int maxProfit(int[] prices, int fee) {int dp[][] = new int[2][2];int len = prices.length;//[i][0] = holding the stock//[i][1] = not holding the stockdp[0][0] = -prices[0];for(int i = 1; i < len; i++){dp[i % 2][0] = Math.max(dp[(i - 1) % 2][0], dp[(i - 1) % 2][1] - prices[i]);dp[i % 2][1] = Math.max(dp[(i - 1) % 2][1], dp[(i - 1) % 2][0] + prices[i] - fee);}return dp[(len - 1) % 2][1];}
}

这段代码同样是对买入股票时需支付手续费问题的解决,但采取了不同的空间优化策略,使用了二维数组 dp 的两行来存储状态,从而将空间复杂度从原始的 O(n) 降低至接近 O(1)。下面是代码的详细解析:

状态定义

  • dp[i % 2][0]:表示在第 i 天结束时,手中持有股票的最大现金量(实际上是负数,表示买入股票后的亏损,包括手续费)。
  • dp[i % 2][1]:表示在第 i 天结束时,不持有股票的最大现金量(卖出股票后所得的金额,无需考虑手续费,因为手续费是在买入时支付的)。

初始化

  • dp[0][0] = -prices[0]:表示在第0天买入股票并支付手续费的初始状态,即花费了 prices[0] 的成本。这里没有立即减去手续费,而是在动态转移方程中处理。
  • dp[0][1]:默认初始化为0,表示没有进行任何操作,不持有股票,也没有支付手续费。

动态转移方程

循环从 i = 1prices.length,在每一步中:

  • dp[i % 2][0]:要更新为持有股票的最大现金量,这包括从上一天持有股票继续持有 (dp[(i - 1) % 2][0]) 或者从上一天不持有股票状态买入股票 (dp[(i - 1) % 2][1] - prices[i])。
  • dp[i % 2][1]:要更新为不持有股票的最大现金量,这包括从上一天卖出股票并减去手续费 (dp[(i - 1) % 2][0] + prices[i] - fee) 或者从上一天不持有股票状态继续不持有 (dp[(i - 1) % 2][1])。

使用 i % 2 的原因

使用 i % 2 是为了在空间上进行优化,只保留最近的两个状态,即当前状态和前一个状态。这是因为动态规划中的转移方程只依赖于前一个状态,因此没有必要存储所有过去的状态,只需在内存中交替使用两个位置来存储当前状态和前一个状态,即可达到节省空间的目的。

返回值

最后返回 dp[(len - 1) % 2][1],即在最后一天不持有股票的最大现金量,这是最终的利润。

空间优化效果

通过这种方式,虽然表面上看 dp 数组是二维的,但实际上只需要固定大小的存储空间,不随输入数据规模 prices.length 的变化而变化,因此可以认为空间复杂度接近 O(1)

总结

这段代码有效地解决了买入股票时支付手续费的股票买卖问题,并通过巧妙地使用 i % 2 来交替更新状态,达到了空间上的优化,同时保持了时间复杂度为 O(n),整体上是一个高效且节省资源的解决方案。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/42080.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

DHCP服务器

目录 网络传输原则&#xff1a; DHCP: DHCP作用&#xff1a; 优缺点&#xff1a; DHCP的原理&#xff1a; 用虚拟机模拟DHCP服务器​编辑​编辑 网络传输原则&#xff1a; 网络是双向的&#xff0c;网络是有方向的 解释&#xff1a;网络是双向的&#xff1a; …

大数据中的常见数据问题:独断脏

想象你刚刚入职一家声称正在进行"数字化转型"的大型企业,担任大数据开发工程师。在入职的第一周,你满怀热情,迫不及待地想要大展拳脚,用你的技能来推动公司的数据驱动决策。 目录 大数据中的常见数据问题1. 独 - 数据孤岛2. 断 - 数据价值链断层3. 缺 - 标准、治理…

如何搜索查找ICLR论文

记录有几个查找顶级会议文章的网址&#xff0c;不止ICLR ICLR 2024 还会有visualization模式&#xff1a; ICLR 2024 virtual 这个网站也很棒 Paper Copilot ICLR 2024 当然还有一个用图表示各论文相关关系的网站&#xff1a; connected papers

WACV2023论文速览Attention注意力机制相关

Paper1 ScoreNet: Learning Non-Uniform Attention and Augmentation for Transformer-Based Histopathological Image Classification 摘要原文: Progress in digital pathology is hindered by high-resolution images and the prohibitive cost of exhaustive localized an…

2、图形验证码

1、图形验证码设计 1.1思路 现今&#xff0c;市面上的图形验证码付费的&#xff0c;免费的多种多样&#xff0c;主要形式有滑动拼图、文字点选、语序点选、字体识别、空间推理、智能随机等。 而处理也分为web端和sever端两部分 此处以免费的kaptcha 为例&#xff0c;进行数字图…

电机控制杂谈——增量式的预测电流控制的优势在哪?

1.前言 前几天看到这么个问题。“模型预测控制如何消除静态误差” 评论说用增量式的预测控制。 这个回答让我想起来我大四下看的这篇论文。现在都一百多被引用了。 但是苦于当时能力有限&#xff0c;没办法复现这个文章。 所以现在想重新验证一下。 2.静态误差和电机磁链有…

BulingBuling - 作息安排 [Reset Your Routine] - 1

The Blinkist Team: [ Reset Your Routine ] 如果你发现自己很难按部就班&#xff0c;或者陷入工作效率低的困境&#xff0c;那么你可能需要重新调整一下作息时间&#xff01;从睡眠和营养&#xff0c;到待办事项和井井有条--本指南为你提供了各种技巧和策略&#xff0c;让你的…

自然之美无需雕琢

《自然之美&#xff0c;无需雕琢 ”》在这个颜值至上的时代&#xff0c;但在温馨氛围中&#xff0c;单依纯以一种意想不到的方式&#xff0c;为我们诠释了自然之美的真谛。而医生的回答&#xff0c;如同一股清流耳目一新。“我说医生你看我这张脸&#xff0c;有没有哪里要动的。…

【数据结构(邓俊辉)学习笔记】高级搜索树01——伸展树

文章目录 1. 逐层伸展1. 1 宽松平衡1. 2 局部性1. 3 自适应调整1. 4 逐层伸展1. 5 实例1. 6 一步一步往上爬1. 7 最坏情况 2. 双层伸展2.1 双层伸展2.2 子孙异侧2.3 子孙同侧2.4 点睛之笔2.5 折叠效果2.6 分摊性能2.7 最后一步 3 算法实现3.1 功能接口3.2 伸展算法3.3 四种情况…

linux下的网络编程

网络编程 1. 网络基础编程知识1.1网络字节序问题1.2 常用socket编程接口1.2.1 sockaddr1.2.2 ip地址转换函数1.2.4 socket()1.2.3 bind()1.2.4 listen()1.2.5 accept()1.2.6 connect() 1.3 以udp为基础的客户端连接服务器的demo1.4 以udp为基础的的服务器聊天室功能demo1.5 基于…

Apispec,一个用于生成 OpenAPI(Swagger)规范的 Python 库

目录 01什么是 Apispec&#xff1f; 为什么选择 Apispec&#xff1f; 安装与配置 02Apispec 的基本用法 生成简单的 API 文档 1、创建 Apispec 实例 2、定义 API 路由和视图 3、添加路径到 Apispec 集成 Flask 和 Apispec 1、安装…

FreeU: Free Lunch in Diffusion U-Net——【代码复现】

这篇文章发表于CVPR 2024&#xff0c;官网地址&#xff1a;ChenyangSi/FreeU: FreeU: Free Lunch in Diffusion U-Net (CVPR2024 Oral) (github.com) 一、环境准备 提前准备好python、pytorch环境 二、下载项目依赖 demo下有一个requirements.txt文件&#xff0c; pip inst…

【WEB前端2024】3D智体编程:乔布斯3D纪念馆-第55课-芝麻开门(语音 识别 控制3D纪念馆开门 和 关门)

【WEB前端2024】3D智体编程&#xff1a;乔布斯3D纪念馆-第55课-芝麻开门&#xff08;语音识别控制3D纪念馆开门和关门&#xff09; 使用dtns.network德塔世界&#xff08;开源的智体世界引擎&#xff09;&#xff0c;策划和设计《乔布斯超大型的开源3D纪念馆》的系列教程。dtn…

2.pwn的linux基础(计算机内部数据结构存储形式)

linux基础 保护层级: 分为四个ring0-ring3 一般来说就两个&#xff0c;0和3 0为内核 3为用户 权限: 用户分为多个组 文件和目录等等的权限一般都是三个&#xff0c;即可读可写可执行。 读:R&#xff0c;写:W&#xff0c;执行:X 赋予一个可执行文件执行权限就是chmod x file…

qt 如何添加子项目

首先我们正常流程创建一个项目文件&#xff1a; 这是我已经创建好的&#xff0c;请无视红线 然后找到该项目的文件夹&#xff0c;在文件夹下创建一个文件夹&#xff0c;再到创建好的文件夹下面创建一个 .pri 文件&#xff1a; &#xff08;创建文件夹&#xff09; &#xff08…

字节跳动与南开联合开源 StoryDiffusion:一键生成漫画和视频故事的神器!完全免费!

大家好&#xff0c;我是程序员X小鹿&#xff0c;前互联网大厂程序员&#xff0c;自由职业2年&#xff0c;也一名 AIGC 爱好者&#xff0c;持续分享更多前沿的「AI 工具」和「AI副业玩法」&#xff0c;欢迎一起交流~ 漫画&#xff0c;是多少人童年的回忆啊&#xff01; 记得小学…

#数据结构 链式栈

1. 概念 链式栈LinkStack 逻辑结构&#xff1a;线性结构物理结构&#xff1a;链式存储栈的特点&#xff1a;后进先出 栈具有后进先出的特点&#xff0c;我们使用链表来实现栈&#xff0c;即链式栈。那么栈顶是入栈和出栈的地方&#xff0c;单向链表有头有尾&#xff0c;那我…

Http中get与post的区别,99%的人都理解错了吧

Get和Post是HTTP请求的两种基本方法&#xff0c;要说它们的区别&#xff0c;接触过WEB开发的人都能说出一二。 最直观的区别 就是Get把参数包含在URL中&#xff0c;Post通过request body传递参数。 你可能自己写过无数个Get和Post请求&#xff0c;或者已经看过很多权威网站总…

容器:stack

以下是关于stack容器的一些总结&#xff1a; stack容器比较简单&#xff0c;主要包括&#xff1a; 1、构造函数&#xff1a;stack [staName] 2、添加、删除元素: push() 、pop() 3、获取栈顶元素&#xff1a;top() 4、获取栈的大小&#xff1a;size() 5、判断栈是否为空&#x…

Linux运维之管道符、重定向与环境变量

前言&#xff1a;本博客仅作记录学习使用&#xff0c;部分图片出自网络&#xff0c;如有侵犯您的权益&#xff0c;请联系删除 目录 一、输入输出重定向 二、管道命令符 三、命令行的通配符 四、常用的转义字符 五、重要的环境变量 致谢 一、输入输出重定向 输入重定向是…