【華為機試】122. 買賣股票的最佳時機 II

文章目錄

  • 122. 買賣股票的最佳時機 II
    • 描述
    • 示例 1
    • 示例 2
    • 示例 3
    • 提示
    • 解題思路
      • 核心觀察
      • 關鍵洞察
    • 算法實現
      • 方法1:貪心算法(推薦)
      • 方法2:動態規劃
      • 方法3:動態規劃(空間優化)
      • 方法4:波峰波谷法
    • 算法分析
      • 復雜度對比
      • 算法流程圖
      • 貪心算法證明
      • 示例分析
    • 動態規劃詳解
      • 狀態轉移方程
      • 狀態轉移圖
      • DP狀態表(示例)
    • 實際應用
      • 1. 投資策略
      • 2. 類似問題
      • 3. 實際約束
    • 代碼實現要點
      • 邊界條件處理
      • 數組越界防護
      • 整數溢出考慮
    • 練習建議
    • 相關題目
    • 完整題解代碼

122. 買賣股票的最佳時機 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。

提示

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

解題思路

這道題的核心思想是:由于可以進行多次買賣,我們需要抓住每一次股價上漲的機會來獲得最大利潤

核心觀察

  1. 無限制交易:可以進行任意次數的買賣
  2. 同一天可以買賣:先買再賣,或者先賣再買
  3. 最多持有一股:不能同時持有多股
  4. 目標:獲得最大總利潤

關鍵洞察

貪心策略:只要明天的價格比今天高,就今天買入明天賣出。這樣可以捕獲所有的價格上漲段。

算法實現

方法1:貪心算法(推薦)

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

原理:累加所有相鄰的正收益。

時間復雜度:O(n)
空間復雜度:O(1)

方法2:動態規劃

func maxProfitDP(prices []int) int {n := len(prices)dp := make([][2]int, n)dp[0][0] = 0          // 不持有股票dp[0][1] = -prices[0] // 持有股票for i := 1; i < n; i++ {dp[i][0] = max(dp[i-1][0], dp[i-1][1]+prices[i])  // 賣出dp[i][1] = max(dp[i-1][1], dp[i-1][0]-prices[i])  // 買入}return dp[n-1][0]
}

狀態定義

  • dp[i][0]:第i天結束后不持有股票的最大利潤
  • dp[i][1]:第i天結束后持有股票的最大利潤

時間復雜度:O(n)
空間復雜度:O(n)

方法3:動態規劃(空間優化)

func maxProfitDPOptimized(prices []int) int {hold := -prices[0]  // 持有股票的最大利潤sold := 0           // 不持有股票的最大利潤for i := 1; i < len(prices); i++ {newSold := max(sold, hold+prices[i])newHold := max(hold, sold-prices[i])sold, hold = newSold, newHold}return sold
}

時間復雜度:O(n)
空間復雜度:O(1)

方法4:波峰波谷法

找到所有的波谷和波峰,在波谷買入,在波峰賣出。

func maxProfitPeakValley(prices []int) int {i, maxProfit := 0, 0for i < len(prices)-1 {// 找波谷for i < len(prices)-1 && prices[i+1] <= prices[i] {i++}valley := prices[i]// 找波峰for i < len(prices)-1 && prices[i+1] >= prices[i] {i++}peak := prices[i]maxProfit += peak - valley}return maxProfit
}

算法分析

復雜度對比

方法時間復雜度空間復雜度優點缺點
貪心算法O(n)O(1)簡潔高效,易理解需要理解貪心思想
動態規劃O(n)O(n)狀態轉移清晰空間開銷大
動態規劃優化O(n)O(1)空間效率高狀態維護復雜
波峰波谷O(n)O(1)直觀易懂代碼稍復雜
狀態機O(n)O(1)邏輯清晰理解成本高

算法流程圖

開始
輸入價格數組
數組長度 <= 1?
返回0
初始化利潤 = 0
遍歷第1天到最后一天
今天價格 > 昨天價格?
利潤 += 今天價格 - 昨天價格
跳過這一天
還有下一天?
返回總利潤
結束

貪心算法證明

命題:對于任意價格序列,貪心策略(累加所有正差值)能夠獲得最大利潤。

證明

  1. 可行性:貪心策略產生的交易序列是合法的(每次買入后必須賣出才能再次買入)
  2. 最優性:任何其他交易策略的利潤都不會超過貪心策略

設最優解包含交易 (buy_i, sell_i),其總利潤為:

profit = Σ(sell_i - buy_i)

貪心策略的利潤為:

greedy_profit = Σ(prices[i] - prices[i-1]) for all i where prices[i] > prices[i-1]

可以證明:greedy_profit >= profit

示例分析

示例1prices = [7,1,5,3,6,4]

貪心分析:
第1天: 7 -> 第2天: 1,下跌,不交易
第2天: 1 -> 第3天: 5,上漲,利潤 = 5-1 = 4
第3天: 5 -> 第4天: 3,下跌,不交易  
第4天: 3 -> 第5天: 6,上漲,利潤 = 6-3 = 3
第5天: 6 -> 第6天: 4,下跌,不交易總利潤 = 4 + 3 = 7

可視化

價格走勢圖:7 |●|  \5 |   ●|    \3 |     ●---●|          \1 | ●         ●+--+--+--+--+--+1  2  3  4  5  天數交易策略:第2天買入(1) -> 第3天賣出(5):利潤4第4天買入(3) -> 第5天賣出(6):利潤3

動態規劃詳解

狀態轉移方程

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

狀態轉移圖

不操作
買入股票
不操作
賣出股票
不持有股票
持有股票

DP狀態表(示例)

對于 prices = [7,1,5,3,6,4]

天數價格不持有股票持有股票決策
070-7初始狀態
110-1在第1天買入更優
254-1第2天賣出獲利4
3341第3天買入
4671第4天賣出獲利3
5473保持不持有狀態

實際應用

1. 投資策略

  • 短線交易:頻繁買賣獲取價差
  • 波段操作:在價格波動中獲利
  • 算法交易:程序化交易系統

2. 類似問題

  • 買賣股票的最佳時機系列題目
  • 背包問題的變種
  • 區間調度問題

3. 實際約束

在實際投資中還需考慮:

  • 交易手續費
  • 稅收影響
  • 市場流動性
  • 價格滑點

代碼實現要點

邊界條件處理

if len(prices) <= 1 {return 0  // 少于2個價格無法交易
}

數組越界防護

for i := 1; i < len(prices); i++ {  // 從第2個元素開始// 安全訪問 prices[i] 和 prices[i-1]
}

整數溢出考慮

對于極大數據,考慮使用 int64 或添加溢出檢查。

練習建議

  1. 理解貪心思想:為什么累加所有正差值是最優的?
  2. 掌握狀態轉換:DP中兩個狀態如何相互轉換?
  3. 空間優化技巧:如何從O(n)優化到O(1)?
  4. 擴展思考:如果有交易手續費怎么辦?
  5. 變種練習:限制交易次數的情況如何處理?

相關題目

  • 121. 買賣股票的最佳時機(只能交易一次)
  • 123. 買賣股票的最佳時機 III(最多交易兩次)
  • 188. 買賣股票的最佳時機 IV(最多交易k次)
  • 309. 最佳買賣股票時機含冷凍期(含冷凍期)
  • 714. 買賣股票的最佳時機含手續費(含手續費)

完整題解代碼

package mainimport ("fmt""time"
)// 方法1:貪心算法 - 抓住每一次上漲機會
// 時間復雜度:O(n),空間復雜度:O(1)
func maxProfitGreedy(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
}// 方法2:動態規劃
// 時間復雜度:O(n),空間復雜度:O(n)
func maxProfitDP(prices []int) int {if len(prices) <= 1 {return 0}n := len(prices)// dp[i][0] 表示第i天不持有股票的最大利潤// dp[i][1] 表示第i天持有股票的最大利潤dp := make([][2]int, n)// 初始狀態dp[0][0] = 0          // 第0天不持有股票,利潤為0dp[0][1] = -prices[0] // 第0天買入股票,利潤為-prices[0]for i := 1; i < n; i++ {// 第i天不持有股票:可能是前一天就不持有,或者前一天持有今天賣出dp[i][0] = max(dp[i-1][0], dp[i-1][1]+prices[i])// 第i天持有股票:可能是前一天就持有,或者前一天不持有今天買入dp[i][1] = max(dp[i-1][1], dp[i-1][0]-prices[i])}// 最后一天不持有股票肯定比持有股票利潤更大return dp[n-1][0]
}// 方法3:動態規劃(空間優化)
// 時間復雜度:O(n),空間復雜度:O(1)
func maxProfitDPOptimized(prices []int) int {if len(prices) <= 1 {return 0}// 只需要記錄當前的狀態,不需要整個數組hold := -prices[0] // 持有股票的最大利潤sold := 0          // 不持有股票的最大利潤for i := 1; i < len(prices); i++ {newSold := max(sold, hold+prices[i]) // 今天賣出newHold := max(hold, sold-prices[i]) // 今天買入sold = newSoldhold = newHold}return sold
}func max(a, b int) int {if a > b {return a}return b
}func main() {testCases := [][]int{{7, 1, 5, 3, 6, 4}, // 預期輸出: 7{1, 2, 3, 4, 5},    // 預期輸出: 4{7, 6, 4, 3, 1},    // 預期輸出: 0{1, 2, 1, 2, 1},    // 預期輸出: 2{5},                // 預期輸出: 0{},                 // 預期輸出: 0}fmt.Println("=== 買賣股票的最佳時機 II - 多種解法測試 ===")fmt.Println()methods := []struct {name stringfn   func([]int) int}{{"貪心算法", maxProfitGreedy},{"動態規劃", maxProfitDP},{"動態規劃(優化)", maxProfitDPOptimized},}for i, testCase := range testCases {fmt.Printf("測試用例 %d: %v\n", i+1, testCase)for _, method := range methods {start := time.Now()result := method.fn(testCase)duration := time.Since(start)fmt.Printf("  %s: %d (用時: %v)\n", method.name, result, duration)}fmt.Println()}// 算法思路演示fmt.Println("=== 算法思路演示 ===")demonstrateGreedyApproach([]int{7, 1, 5, 3, 6, 4})
}func demonstrateGreedyApproach(prices []int) {fmt.Printf("\n貪心算法思路演示:\n")fmt.Printf("價格數組: %v\n", prices)maxProfit := 0transactions := []string{}for i := 1; i < len(prices); i++ {if prices[i] > prices[i-1] {profit := prices[i] - prices[i-1]maxProfit += profittransaction := fmt.Sprintf("第%d天買入(%d) -> 第%d天賣出(%d), 利潤: %d", i, prices[i-1], i+1, prices[i], profit)transactions = append(transactions, transaction)}}fmt.Println("交易記錄:")for _, transaction := range transactions {fmt.Printf("  %s\n", transaction)}fmt.Printf("總利潤: %d\n", maxProfit)
} 

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/915141.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/915141.shtml
英文地址,請注明出處:http://en.pswp.cn/news/915141.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Spring MVC @RequestParam注解全解析

RequestParam 注解詳解 RequestParam 是 Spring MVC 中最常用的注解之一&#xff0c;用于從 HTTP 請求中提取查詢參數&#xff08;Query String&#xff09;或表單數據。它主要處理 application/x-www-form-urlencoded 類型的請求&#xff08;如 GET 請求或 POST 表單提交&…

從零掌握XML與DTD實體:原理、XXE漏洞攻防

本文僅用于技術研究&#xff0c;禁止用于非法用途。 Author:枷鎖 文章目錄一、XML基礎1. 什么是XML&#xff1f;2. XML語法規則3. 數據類型二、DTD1. 認識DTD2. 聲明DTD3. DTD實體4. 如何防御XXE攻擊&#xff1f;5. 總結一、XML基礎 1. 什么是XML&#xff1f; XML &#xff1…

.NET 8 Release Candidate 1 (RC1)現已發布,包括許多針對ASP.NET Core的重要改進!

.NET 8 Release Candidate 1 (RC1)發布&#xff1a;ASP.NET Core重大改進來襲&#xff01; 近日&#xff0c;.NET 8 Release Candidate 1 (RC1)正式發布&#xff0c;這是在今年晚些時候計劃發布的最終 .NET 8 版本之前的兩個候選版本中的第一個。此版本包含了大部分計劃中的功…

Jenkins pipeline 部署docker通用模板

Jenkinsfile: Docker的NETWORK_NAME不要使用bridge默認網絡&#xff0c;要使用自定義的網絡如test默認 bridge 網絡&#xff1a;容器間不能用名字互相訪問&#xff0c;只能用 IP。自定義網絡&#xff1a;容器間可以用名字互相訪問&#xff0c;Docker 自動做了 DNS 解析。pipeli…

【每日算法】專題十五_BFS 解決 FloodFill 算法

1. 算法思想 Flood Fill 問題的核心需求 給定一個二維網格&#xff08;如像素矩陣&#xff09;、一個起始坐標 (x, y) 和目標顏色 newColor&#xff0c;要求&#xff1a; 將起始點 (x, y) 的顏色替換為 newColor。遞歸地將所有與起始點相鄰&#xff08;上下左右&#xff09; …

ESLint 完整功能介紹和完整使用示例演示

以下是ESLint的完整功能介紹和完整使用示例演示&#xff1a; ESLint 完整功能介紹 一、核心功能靜態代碼分析&#xff1a; 通過解析JavaScript/TypeScript代碼為抽象語法樹&#xff08;AST&#xff09;&#xff0c;識別語法錯誤、潛在問題&#xff08;如未定義變量、未使用變量…

解決問題七大步驟

發現問題后尋找解決方案的流程可以細化為 7個核心步驟&#xff0c;每個步驟包含具體措施、信息源和關鍵技巧&#xff0c;形成“從自查到驗證、從獨立解決到尋求幫助”的完整閉環。以下是完善后的流程&#xff1a; 一、明確問題與初步自查&#xff08;前提&#xff1a;減少無效搜…

思維鏈(CoT)技術全景:原理、實現與前沿應用深度解析

一、核心概念與原理 定義與起源 CoT 是一種引導大語言模型&#xff08;LLM&#xff09;顯式生成中間推理步驟的技術&#xff0c;通過模擬人類逐步解決問題的過程&#xff0c;提升復雜任務&#xff08;如數學證明、多步邏輯推理&#xff09;的準確性。該概念由 Google Brain 團…

實驗-華為綜合

華為綜合實驗 一 實驗拓撲二 實驗配置交換機2 vlan batch 10 20 int e0/0/2 port link-type access port default vlan 10 int e0/0/1 port link-type access port default vlan 20 int e0/0/3 port link-type trunk port trunk allow-pass vlan alltelnet交換機3 鏈路類型配置…

Matlab打開慢、加載慢的解決辦法

安裝完畢后直接打開會非常慢&#xff0c;而且打開了之后還得加載很久才能運行 解決辦法如下&#xff1a; 1.找到路徑“D:\Program Files\Polyspace\R2020a\licenses”&#xff08;我是把matlab安裝在D盤了&#xff0c;如果是其他盤修改路徑即可&#xff09;&#xff0c;該路徑記…

混沌趨勢指標原理及交易展示

1. 引言在金融市場交易中&#xff0c;尤其是加密貨幣合約交易&#xff0c;趨勢跟蹤是最主流的策略之一。然而&#xff0c;傳統趨勢指標如均線、MACD等存在明顯的滯后性&#xff0c;往往在趨勢確立后才發出信號&#xff0c;導致交易者錯失最佳入場時機。更糟糕的是&#xff0c;市…

Java面試寶典:Maven

一、Maven的本質與核心價值 項目管理革命 POM驅動:通過pom.xml文件定義項目結構、依賴、構建規則,實現標準化管理()。示例配置: <dependencies> <dependency> <groupId>org.springframework

可靠消息最終一致性分布式事務解決方案

之前文章寫過主流的一些 分布式事務的解決方案&#xff0c;但其實工作中很少有一些高并發的業務中去使用這些方案&#xff0c;因為對于高并發的場景來說&#xff0c;引入這些方案的性能損耗太大&#xff0c;且對系統事務侵入性太強影響系統穩定性。 所以在高并發的業務中&…

ISIS基礎

拓撲計算方式 模型 支持的網絡 支持的地址OSPF SPF TCP/IP IP網絡 IPv4地址ISIS SPF OSI CLNP網絡 NSAP地址集成ISIS SPF TCP/IP IP網絡 NSAP地址&#xff0c;但可以支持IPv4地址12. …

基于ASP.NET+SQL Server實現(Web)排球賽事網站

排球賽事網的設計與實現摘要隨著近幾年來計算機技術、網絡技術及相應軟件技術的迅猛發展&#xff0c;人們的生活已越來越離不開計算機了&#xff0c;而且總是要花費很多時間在它上面。一直以來&#xff0c;排球作為一項大眾喜愛的運動&#xff0c;得到廣泛傳播。隨著各項排球賽…

【PTA數據結構 | C語言版】根據后序和中序遍歷輸出前序遍歷

本專欄持續輸出數據結構題目集&#xff0c;歡迎訂閱。 文章目錄題目代碼題目 本題要求根據給定的一棵二叉樹的后序遍歷和中序遍歷結果&#xff0c;輸出該樹的前序遍歷結果。 輸入格式: 第一行給出正整數 n (≤30)&#xff0c;是樹中結點的個數。隨后兩行&#xff0c;每行給出…

Java HashMap高頻面試題深度解析

在 Java 面試中&#xff0c;HashMap 是必問的核心知識點&#xff0c;以下是高頻問題和深度解析框架&#xff0c;助你系統性掌握&#xff1a;一、基礎概念HashMap 的本質是什么&#xff1f; 基于哈希表的 Map 接口實現&#xff0c;存儲鍵值對&#xff08;Key-Value&#xff09;非…

GitHub Pages無法訪問以點號.開頭的目錄

目錄 前言 Jekyll 是什么 啟用訪問 總結 前言 一些前端項目經常會使用GitHub Pages進行部署展示&#xff0c;但是GitHub Pages 使用的是 Jekyll 引擎&#xff0c;對 Jekyll 引擎不熟悉的小伙伴就會出現如文章標題所言的情況。 Jekyll 是什么 Jekyll 是 GitHub Pages 默認…

JS JSON.stringify介紹(JS序列化、JSON字符串 )(遍歷輸入值的所有可枚舉屬性,將其轉換為文本表示)緩存序列化、狀態管理與時間旅行、replacer

文章目錄JSON.stringify 全解析1. 基本概念2. 序列化原理1. 對于原始類型&#xff0c;直接轉換為對應的字符串表示2. 對于對象和數組&#xff0c;遞歸處理其每個屬性或元素3. 應用特殊規則處理日期、函數、Symbol 等特殊類型4. 檢測并防止循環引用5. 應用 replacer 函數或數組進…

SQLite / LiteDB 單文件數據庫為何“清空表后仍占幾 GB”?——原理解析與空間回收實戰

關鍵詞&#xff1a; SQLite、LiteDB、VACUUM、WAL、auto_vacuum、文件瘦身、數據庫維護在嵌入式或桌面、IoT 網關等場景&#xff0c;很多同學都會選擇單文件數據庫&#xff08;SQLite、LiteDB、SQL CE…&#xff09;。 最近群里一位朋友反饋&#xff1a;“我的 test.db 已經把業…