最大子數組和問題-詳解Kadane算法

最大子數組和問題-詳解Kadane算法

    • 一、問題定義與暴力解法
      • 1.1 問題描述
      • 1.2 暴力解法的低效性
    • 二、Kadane算法的核心原理
      • 2.1 動態規劃思想的應用
      • 2.2 優化空間復雜度
    • 三、Kadane算法的Java實現
      • 3.1 基礎版本(處理所有情況)
      • 3.2 算法正確性驗證
    • 四、Kadane算法的變種與拓展
      • 4.1 變種1:輸出最大子數組的起止索引
      • 4.2 變種2:限制子數組長度(最多`k`個元素)
      • 4.3 變種3:允許子數組為空(返回0)
    • 五、時間與空間復雜度
    • 六、實際應用場景

最大子數組和(Maximum Subarray Sum)是一個經典且高頻的數組算法考點,這個問題看似簡單——從一個整數數組中找到和最大的連續子數組,但暴力求解的效率極低。Kadane算法(卡丹算法)作為專門解決此問題的高效方法,能在O(n)O(n)O(n)時間內完成求解,是動態規劃思想的典型應用。本文我將深入解析Kadane算法的核心原理、實現細節、變種拓展及實際應用,結合Java代碼示例,幫你徹底掌握這一高效算法。

一、問題定義與暴力解法

1.1 問題描述

給定一個整數數組nums(可能包含負數),找到一個連續子數組(至少包含一個元素),使得該子數組的和最大,返回這個最大和。

例如:

  • 輸入:nums = [-2, 1, -3, 4, -1, 2, 1, -5, 4]
  • 輸出:6(對應子數組[4, -1, 2, 1]

1.2 暴力解法的低效性

最直觀的思路是枚舉所有可能的子數組,計算其和并記錄最大值:

// 暴力解法:枚舉所有子數組
public int maxSubArrayBruteForce(int[] nums) {int maxSum = Integer.MIN_VALUE;int n = nums.length;for (int i = 0; i < n; i++) {  // 子數組起點int currentSum = 0;for (int j = i; j < n; j++) {  // 子數組終點currentSum += nums[j];maxSum = Math.max(maxSum, currentSum);}}return maxSum;
}
  • 時間復雜度O(n2)O(n^2)O(n2)(嵌套循環枚舉所有子數組)。
  • 局限性:當n超過10410^4104時,會因超時無法通過測試,必須尋找更高效的算法。

二、Kadane算法的核心原理

2.1 動態規劃思想的應用

Kadane算法的本質是動態規劃,其核心是用局部最優推導全局最優

  • 定義dp[i]為“以nums[i]為結尾的最大子數組和”。
  • 對于每個元素nums[i],有兩種選擇:
    1. nums[i]加入之前的子數組(即dp[i-1] + nums[i])。
    2. nums[i]為起點重新開始一個子數組(即nums[i])。
  • 因此,狀態轉移方程為:
    dp[i] = max(nums[i], dp[i-1] + nums[i])
  • 全局最大和即為所有dp[i]中的最大值。

2.2 優化空間復雜度

由于dp[i]僅依賴于dp[i-1],無需存儲整個dp數組,可改用一個變量currentMax記錄當前值,將空間復雜度從O(n)O(n)O(n)優化至O(1)O(1)O(1)

  1. 初始化currentMax = nums[0](以第一個元素為結尾的子數組和),maxSum = nums[0](全局最大值)。
  2. 從第二個元素開始遍歷:
    • currentMax = max(nums[i], currentMax + nums[i])(更新局部最優)。
    • maxSum = max(maxSum, currentMax)(更新全局最優)。
  3. 遍歷結束后,maxSum即為結果。

三、Kadane算法的Java實現

3.1 基礎版本(處理所有情況)

public class KadaneAlgorithm {public int maxSubArray(int[] nums) {if (nums == null || nums.length == 0) {return 0;  // 邊界處理:空數組}int currentMax = nums[0];  // 以當前元素為結尾的最大子數組和int maxSum = nums[0];      // 全局最大子數組和for (int i = 1; i < nums.length; i++) {// 選擇:加入之前的子數組 或 重新開始currentMax = Math.max(nums[i], currentMax + nums[i]);// 更新全局最大值maxSum = Math.max(maxSum, currentMax);}return maxSum;}public static void main(String[] args) {KadaneAlgorithm solution = new KadaneAlgorithm();int[] nums = {-2, 1, -3, 4, -1, 2, 1, -5, 4};System.out.println(solution.maxSubArray(nums));  // 輸出 6}
}

3.2 算法正確性驗證

以示例nums = [-2, 1, -3, 4, -1, 2, 1, -5, 4]為例,分步驗證:

索引inums[i]currentMax(局部最優)maxSum(全局最優)
0-2-2-2
11max(1, -2+1)=1max(-2, 1)=1
2-3max(-3, 1-3)=-2max(1, -2)=1
34max(4, -2+4)=4max(1, 4)=4
4-1max(-1, 4-1)=3max(4, 3)=4
52max(2, 3+2)=5max(4, 5)=5
61max(1, 5+1)=6max(5, 6)=6
7-5max(-5, 6-5)=1max(6, 1)=6
84max(4, 1+4)=5max(6, 5)=6

最終maxSum=6,與預期結果一致,驗證了算法的正確性。

四、Kadane算法的變種與拓展

4.1 變種1:輸出最大子數組的起止索引

除了最大和,有時需要返回子數組的具體位置(起點和終點索引)。只需在更新currentMaxmaxSum時,同步記錄索引:

public int[] maxSubArrayWithIndex(int[] nums) {if (nums == null || nums.length == 0) {return new int[]{-1, -1};  // 空數組返回無效索引}int currentMax = nums[0];int maxSum = nums[0];int start = 0, end = 0;  // 當前子數組起止索引int tempStart = 0;       // 臨時起點(用于重新開始子數組時更新)for (int i = 1; i < nums.length; i++) {if (nums[i] > currentMax + nums[i]) {// 重新開始子數組,更新臨時起點currentMax = nums[i];tempStart = i;} else {// 加入之前的子數組currentMax += nums[i];}// 更新全局最大和及起止索引if (currentMax > maxSum) {maxSum = currentMax;start = tempStart;end = i;}}return new int[]{start, end, maxSum};  // 起點、終點、最大和
}

4.2 變種2:限制子數組長度(最多k個元素)

問題:找到長度不超過k的連續子數組的最大和。
思路:結合Kadane算法和前綴和優化,用滑動窗口維護前綴和的最小值(需額外處理長度限制):

public int maxSubArrayWithLimit(int[] nums, int k) {int n = nums.length;int[] prefixSum = new int[n + 1];  // 前綴和:prefixSum[i] = sum(nums[0..i-1])for (int i = 0; i < n; i++) {prefixSum[i + 1] = prefixSum[i] + nums[i];}int maxSum = Integer.MIN_VALUE;// 用單調隊列維護前綴和的最小值(窗口內)Deque<Integer> deque = new ArrayDeque<>();deque.offer(0);  // 初始前綴和prefixSum[0] = 0for (int i = 1; i <= n; i++) {// 移除窗口外的前綴和(超過k個元素)while (!deque.isEmpty() && i - deque.peekFirst() > k) {deque.pollFirst();}// 當前前綴和 - 窗口內最小前綴和 = 最大子數組和maxSum = Math.max(maxSum, prefixSum[i] - prefixSum[deque.peekFirst()]);// 維護單調隊列(保證前綴和遞增)while (!deque.isEmpty() && prefixSum[i] <= prefixSum[deque.peekLast()]) {deque.pollLast();}deque.offer(i);}return maxSum;
}

4.3 變種3:允許子數組為空(返回0)

若題目允許子數組為空(即最大和可以是0,如所有元素為負數時),只需在最后對結果取max(0, maxSum)

public int maxSubArrayAllowEmpty(int[] nums) {int currentMax = 0;  // 初始化為0(允許空數組)int maxSum = 0;for (int num : nums) {currentMax = Math.max(0, currentMax + num);  // 空數組對應0maxSum = Math.max(maxSum, currentMax);}return maxSum;
}

五、時間與空間復雜度

  • 時間復雜度O(n)O(n)O(n),僅需遍歷數組一次,每次操作均為常數時間。
  • 空間復雜度O(1)O(1)O(1),僅使用固定數量的變量(基礎版本),適合大規模數據。

六、實際應用場景

  1. 股票收益分析:計算某段時間內連續持有股票的最大收益(將股價數組轉為每日漲跌數組)。
  2. 信號處理:在噪聲信號中提取連續有效信號段(信號強度和最大的區間)。
  3. 能源消耗優化:找到連續時間段內能源消耗最高的區間,用于負載均衡。
  4. LeetCode經典題:LeetCode 53(最大子數組和)、LeetCode 152(乘積最大子數組,Kadane算法的變種)。

總結
Kadane算法通過動態規劃思想,以O(n)O(n)O(n)時間和O(1)O(1)O(1)空間高效解決最大子數組和問題,是算法優化的典型案例。其核心在于“局部最優選擇”——每個元素要么加入之前的子數組,要么重新開始,通過不斷更新局部最優和全局最優得到結果。
在實際應用中需注意:

  • 基礎版本可直接解決無長度限制的最大子數組和問題。
  • 變種問題(如限制長度、返回索引)可通過擴展算法邏輯實現。
  • 結合前綴和、單調隊列等工具,可處理更復雜的場景。

That’s all, thanks for reading~~
覺得有用就點個贊、收進收藏夾吧!關注我,獲取更多干貨~

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

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

相關文章

Mongoose網絡庫深度解析:從單線程到多線程的架構演進

0. 引言&#xff1a;C/C網絡編程的困境與突破 在C/C開發領域&#xff0c;網絡編程一直是一個令人頭疼的問題。與Python的requests庫或Go的net/http包不同&#xff0c;C/C缺乏統一的包管理體系和標準化的網絡API。開發者往往需要面對gcc/msvc版本差異、平臺兼容性問題、以及各種…

Jfinal+SQLite處理 sqlite數據庫執行FIND_IN_SET報錯

方法一原代碼sql " and FIND_IN_SET(s.M_ID," ids ")"; 修改為 sql " where s.M_ID"getInSql(ids);public static String getInSql(String ids) {String[] idArray ids.split(",");StringBuilder sql new StringBuilder(" I…

day24——Java高級技術深度解析:單元測試、反射、注解與動態代理

文章目錄一、單元測試&#xff1a;JUnit框架精要1.1 單元測試核心概念1.2 JUnit快速入門實戰基礎步驟&#xff1a;斷言機制驗證結果1.3 JUnit核心注解解析二、反射機制&#xff1a;框架設計的基石2.1 反射核心概念2.2 獲取Class對象的三種方式2.3 反射操作類成分獲取并執行構造…

網頁的性能優化,以及具體的應用場景

下面是每個性能優化技術的具體應用場景示例&#xff0c;結合代碼說明如何在實際項目中使用這些優化方法&#xff1a; 1. 批量DOM操作與DocumentFragment 應用場景&#xff1a;動態渲染大量列表項&#xff08;如評論區、商品列表&#xff09; 問題&#xff1a;逐個添加DOM元素會…

Fiddler 中文版 API 調試與性能優化實踐 官方中文網全程支持

在現代開發中&#xff0c;性能問題往往是產品上線后最容易被忽視的一環&#xff0c;尤其是API接口性能。一旦接口響應時間過長或在高并發場景下出現性能瓶頸&#xff0c;可能直接影響用戶體驗和系統穩定性。對于開發者來說&#xff0c;如何精確地找到瓶頸所在&#xff0c;如何模…

嵌入式硬件篇---機械臂運動學解算(3自由度)

實際 3 自由度機械臂的解算是機器人控制的核心&#xff0c;涉及運動學正解&#xff08;關節角度→末端位姿&#xff09;和逆解&#xff08;目標位姿→關節角度&#xff09;。以下從結構建模、解算方法、代碼實現和應用場景四個維度詳細展開&#xff0c;結合工業級機械臂的典型場…

在攝像機視圖中想像在普通 3D 視口里那樣隨意移動

有兩條最常用的方法&#xff1a;1. 「鎖定相機到視圖」(Lock Camera to View)步驟進入相機視圖&#xff1a;按 Numpad 0&#xff08;若無數字鍵盤&#xff0c;可在 Edit → Preferences → Input 勾選 Emulate Numpad 后用主鍵盤 0&#xff09;。右側呼出 N 面板&#xff0c;切…

An End-to-End Attention-Based Approach for Learning on Graphs NC 2025

NC 2025 | 一種基于端到端注意力機制的圖學習方法 Nature Communications IF=15.7 綜合性期刊 1區 參考:https://mp.weixin.qq.com/s/cZ-d8Sf8wtQ9wfcGOFimCg 今天介紹一篇發表在 Nature Communications 的圖學習論文《An end-to-end attention-based approach for learnin…

【牛客刷題】小紅的數字串

文章目錄 一、題目描述 1.1 輸入描述 1.2 輸出描述 1.3 示例1 二、高效解法 2.1 核心算法設計 2.2 算法設計理念 2.2.1 算法流程詳解 2.2.2 復雜度分析 2.3 算法優勢分析 2.3.1 關鍵優化點 2.3.2 正確性驗證 2.4 邊界處理 2.5 總結與擴展 一、題目描述 小紅拿到了一個數字串(由…

微算法科技技術創新,將量子圖像LSQb算法與量子加密技術相結合,構建更加安全的量子信息隱藏和傳輸系統

隨著信息技術的發展&#xff0c;數據的安全性變得尤為重要。在傳統計算模式下&#xff0c;即便采用復雜的加密算法&#xff0c;也難以完全抵御日益增長的網絡攻擊威脅。量子計算技術的出現為信息安全帶來了新的解決方案。然而&#xff0c;量子圖像處理領域仍面臨復雜度高、效率…

博客摘錄「 Springboot入門到精通(超詳細文檔)」2025年7月4日

1.Spring Boot返回Json數據及數據封裝1. Controller 中使用RestController注解即可返回 Json 格式的數據首先看看RestController注解包含了什么東西&#xff0c; ResponseBody 注解是將返回的數據結構轉換為 Json 格式Target({ElementType.TYPE}) Retention(RetentionPolicy.RU…

企業安全防護:堡壘機技術解析

目錄 一、堡壘機&#xff1a;企業IT運維的安全守門人 1.1 核心價值矩陣 1.2堡壘機典型部署架構 二、堡壘機如何構建安全防線 2.1 四層防護體系 2.2 關鍵工作流程 三、堡壘機關鍵技術指標對比表 四、智能堡壘機的發展趨勢 一、堡壘機&#xff1a;企業IT運維的安全守門人…

傳輸層協議 TCP

TCP 協議TCP 全稱為 "傳輸控制協議(Transmission Control Protocol"). 人如其名, 要對數據的傳輸進行一個詳細的控制TCP 協議段格式源/目的端口號: 表示數據是從哪個進程來, 到哪個進程去32 位序號/32 位確認號4 位 TCP 報頭長度: 表示該 TCP 頭部有多少個 32 位 bit…

RT-Thread的概念和移植

一、操作系統的概念 操作系統&#xff08;英語&#xff1a;Operating System&#xff0c;縮寫&#xff1a;OS&#xff09;是一組主管并控制計算機操作、運用和運行硬件、軟件資源和提供公共服務來組織用戶交互的相互關聯的系統軟件程序。根據運行的環境&#xff0c;操作系統可以…

基于單片機傾角測量儀/角度測量/水平儀

傳送門 &#x1f449;&#x1f449;&#x1f449;&#x1f449;其他作品題目速選一覽表 &#x1f449;&#x1f449;&#x1f449;&#x1f449;其他作品題目功能速覽 概述 本設計實現了一種基于單片機的高精度數字傾角測量儀。系統核心由傾角傳感器&#xff08;ADXL345傾…

深度學習 -- 初步認識Torch

深度學習 – 初步認識Torch 文章目錄深度學習 -- 初步認識Torch一&#xff0c;認識人工智能1.1 人工智能的本質1.2 人工智能的實現過程二&#xff0c;認識Torch2.1簡介2.2 概述2.3 Tensor的創建2.3.1 torch.tensor2.3.2 torch.Tensor三&#xff0c;創建線性和隨機張量3.1創建線…

BGP的“聰明選路”遇上了TCP的“路徑潔癖”,需人工調和

在路由器R1上有兩條外網&#xff0c;WAN1和WAN2。R1上做了域名分流功能&#xff0c;全局網址分到WAN1&#xff0c;指定域名分到WAN2&#xff08;優先級更高&#xff09;。癥狀是用戶反饋部分網頁無法打開。于是各種檢查嘗試...... 2天過去了......最終結論是&#xff1a;即使S…

ACWing算法筆記 | 二分

&#x1f50d; C 二分查找雙模板詳解&#xff1a;左閉右開 vs 左閉右閉&#xff08;二分筆記&#xff09;二分查找&#xff08;Binary Search&#xff09;是一類高效的搜索算法&#xff0c;在 O(log n) 的時間復雜度下查找答案&#xff0c;適用于單調性問題。C STL 的 lower_bo…

centos 新加磁盤分區動態擴容

你不能直接將一個分區分配給/dev/mapper/centos-root&#xff0c;因為這是一個邏輯卷&#xff08;屬于 LVM 系統&#xff09;。不過&#xff0c;你可以通過以下步驟將/dev/sda3添加到現有卷組或創建新的邏輯卷&#xff1a; 確認磁盤和分區信息 首先檢查分區是否已格式化以及是否…

python學智能算法(二十六)|SVM-拉格朗日函數構造

【1】引言 前序學習進程中&#xff0c;已經了解了拉格朗日乘數法求極值的基本原理&#xff0c;也了解了尋找最佳超平面就是尋找最佳分隔距離。 這篇文章的學習目標是&#xff1a;使用拉格朗日乘數法獲取最佳的分隔距離。 【2】構造拉格朗日函數 目標函數 首先是目標函數f&a…