【剑指Offer-Swift】42.(图解)连续子数组的最大和

200 阅读1分钟

「这是我参与2022首次更文挑战的第3天,活动详情查看:2022首次更文挑战」。

👋

我的个人项目扫雷Elic 无尽天梯梦见账本隐私访问记录
类型游戏财务工具
AppStoreElicUmemi隐私访问记录

更多专栏:

Lawliet的独立开发碎碎念

Lawliet的iOS游园会

Lawliet的iOS底层实验室

Lawliet的iOS逆向实验室

Lawliet的刷题小本本

Lawliet的Flutter实验室

一、 题目

输入一个整型数组,数组中的一个或连续多个整数组成一个子数组。求所有子数组的和的最大值。

要求时间复杂度为O(n)。

示例1:

输入: nums = [-2,1,-3,4,-1,2,1,-5,4]

输出: 6

解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。  

提示:

1 <= arr.length <= 10^5

-100 <= arr[i] <= 100

注意:本题与主站 53 题相同:leetcode-cn.com/problems/ma…

来源:力扣(LeetCode)

链接:leetcode-cn.com/problems/li…

著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

二、 解法一:找规律图解

42-01.png

如图中,纵坐标表示的是累加和,绿色代表的是我们需要求的最大子数组和。我们发现下面的规律:

  • 我们需要找的是整体呈上升趋势的分段
    • 即:区段内的累加和大于0
  • 如果区段内的累加和小于0那么当前区段是呈下降趋势的
    • 这段我们可以丢弃,即将累加和重置
  • 分析出这样的规律后我们就可以编码了
func maxSubArray(_ nums: [Int]) -> Int {
    var currentSum = nums[0]
    var maxSum = nums[0]
    
    for idx in 1..<nums.count {
        if currentSum < 0 { // 前面的是下降趋势,丢弃之前的
            currentSum = nums[idx]
        }
        else {
            currentSum += nums[idx]
        }
        maxSum = max(maxSum, currentSum)
    }
    return maxSum
}

42-02.png

三、解法二:动态规划

如果用函数 f(i) 代表第i个数子结尾的子数组最大和,那么有下面的关系:

  • i == 0 或者 f(i-1) <= 0
    • f(i) = nums[i]
  • else
    • f(i) = f(i-1) + nums[i]

转化成代码:

func maxSubArray(_ nums: [Int]) -> Int {
    var dp: [Int] = Array(repeating: 0, count: nums.count)

    for i in 0..<nums.count {
        if i == 0 || (i > 0 && dp[i-1] <= 0) {
            dp[i] = nums[i]
        }
        else {
            dp[i] = dp[i - 1] + nums[i]
        }
    }

    return dp.max() ?? nums[0]
}

42-03.png

四、 总结

  • 本题考查对时间复杂度的理解。如果给出 O(n2) O(n3) 的算法,是很难通过面试的。
  • 考察队动态规划的理解,如果熟练掌握动态规划算法,那么就可以轻松的找到解题方案。
  • 如果没有想到使用动态规划,通过分析计算过程寻找规律同样可以找到解法。