📘 概念導讀

Maximum Subarray Evolution

「找出和最大的連續子陣列」這題之所以經典,不在答案本身,而在它能用四種解法一路優化:從 O(n^3) 暴力到 O(n) 的 Kadane。每一次提速,本質都是辨識並消除重複計算——這正是動態規劃與前綴和的共同精神,看懂這條演進,比記住任何單一模板都有用。

解法一:暴力枚舉所有區間 O(n^3)

枚舉每個 (i, j) 區間,再從頭加總一次。

fun maxSub1(nums: IntArray): Int {
    var best = Int.MIN_VALUE
    for (i in nums.indices) {
        for (j in i until nums.size) {
            var sum = 0
            for (k in i..j) sum += nums[k]   // 每次重算整段和
            best = maxOf(best, sum)
        }
    }
    return best
}

瓶頸:最內層「重算整段和」是純粹的重複功。

解法二:邊延伸邊累加 O(n^2)

固定左端 i,右端 j 往右延伸時,和只要在前一步上加 nums[j],省掉最內層迴圈。

fun maxSub2(nums: IntArray): Int {
    var best = Int.MIN_VALUE
    for (i in nums.indices) {
        var sum = 0
        for (j in i until nums.size) {
            sum += nums[j]                   // 重用上一步的和
            best = maxOf(best, sum)
        }
    }
    return best
}

解法三:分治 O(n log n)

最大子陣列要嘛全在左半、要嘛全在右半、要嘛跨越中點。前兩種遞迴解決,跨中點的情形從中點分別向左、向右各掃一次求最大延伸和,再合併。遞迴式 T(n) = 2T(n/2) + O(n),解得 O(n log n)

fun maxSub3(nums: IntArray, lo: Int, hi: Int): Int {
    if (lo == hi) return nums[lo]
    val mid = lo + (hi - lo) / 2
    val left = maxSub3(nums, lo, mid)
    val right = maxSub3(nums, mid + 1, hi)

    var sum = 0; var leftBest = Int.MIN_VALUE
    for (i in mid downTo lo) { sum += nums[i]; leftBest = maxOf(leftBest, sum) }
    sum = 0; var rightBest = Int.MIN_VALUE
    for (i in mid + 1..hi) { sum += nums[i]; rightBest = maxOf(rightBest, sum) }

    return maxOf(left, right, leftBest + rightBest)
}

解法四:Kadane O(n)

關鍵洞見:定義 cur 為「以當前元素結尾的最大子陣列和」。對每個元素,要嘛把它接到前面那段(前提是前面那段為正、值得接),要嘛從它自己重新開始:

cur = max(nums[i], cur + nums[i])
fun maxSubArray(nums: IntArray): Int {
    var cur = nums[0]
    var best = nums[0]
    for (i in 1 until nums.size) {
        cur = maxOf(nums[i], cur + nums[i])   // 接續 or 重啟
        best = maxOf(best, cur)
    }
    return best
}

TIP

Kadane 就是一維 DP 的縮影:狀態 cur 只保留「影響未來決策的最少資訊」(以 i 結尾的最佳和),於是 dp 陣列退化成一個變數,空間 O(1)。「前面那段為負就果斷捨棄」正是它的貪心式轉移。

四解對照

解法時間空間核心改進
暴力枚舉O(n^3)O(1)
延伸累加O(n^2)O(1)重用區間和,消除最內層
分治O(n log n)O(log n)拆左/右/跨中點
KadaneO(n)O(1)只記「以 i 結尾的最佳和」

跨倉庫導讀