返回

从第15题开始,最大子数组和:动态规划才是王道

前端

最大子数组和问题,可谓是算法学习路上的一块试金石。它考察的不仅是对算法的理解,更重要的是对问题本质的分析能力。很多初学者面对这道题,往往一头雾水,不知从何下手。别担心,今天我们就来一层层揭开它的神秘面纱,让你轻松掌握两种解法:动态规划和分支法。

首先,咱们得搞清楚问题是什么。最大子数组和,顾名思义,就是在一个数组中,找到一个连续的子数组,使得这个子数组的元素和最大。比如说,数组[-2, 1, -3, 4, -1, 2, 1, -5, 4],它的最大子数组是[4, -1, 2, 1],和为6。

那么,如何找到这个最大子数组呢?

动态规划:像侦探一样抽丝剥茧

动态规划就像一个高明的侦探,它会把复杂的问题分解成一个个小的线索,然后根据线索之间的联系,逐步推理出最终的答案。

对于最大子数组和问题,我们可以把“以第i个元素结尾的最大子数组和”定义为一个状态,用dp[i]表示。换句话说,dp[i]就是包含数组第i个元素的所有子数组中,和最大的那个子数组的和。

接下来,我们就要寻找线索了。dp[i]dp[i-1]之间有什么联系呢?

仔细想想,dp[i]要么是dp[i-1]加上当前元素nums[i](也就是说,当前元素加入了之前的最大子数组),要么就是nums[i]本身(也就是说,之前的最大子数组对当前元素来说没什么帮助,反而拖后腿了)。

于是,我们得到了一个关键的递推公式:

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

有了这个公式,我们就可以像侦探一样,从dp[0]开始,一步步推算出dp[1]dp[2]……直到dp[n-1],其中n是数组的长度。最终,所有dp[i]中的最大值,就是我们要找的最大子数组和。

分支法:简单粗暴,但效率不高

除了动态规划,还有一种更直接的方法,那就是分支法,也叫暴力枚举法。

分支法的思路很简单:既然要找最大子数组,那我们就干脆把所有可能的子数组都列出来,然后挨个计算它们的和,最后找出最大的那个不就行了?

想法是挺好,但问题是,一个长度为n的数组,它有多少个子数组呢?答案是n*(n+1)/2个。当n很大的时候,这个数字可是相当惊人的。

举个例子,如果数组长度是10000,那就要计算50005000个子数组的和,这可不是闹着玩的。所以,分支法的效率比较低,只适合处理规模较小的数组。

代码实现:眼见为实

光说不练假把式,下面我们就来看看两种方法的代码实现,以C++为例。

动态规划:

int maxSubArray(vector<int>& nums) {
  int n = nums.size();
  vector<int> dp(n);
  dp[0] = nums[0];
  int maxSum = dp[0]; 
  for (int i = 1; i < n; i++) {
    dp[i] = max(dp[i-1] + nums[i], nums[i]);
    maxSum = max(maxSum, dp[i]);
  }
  return maxSum;
}

分支法:

int maxSubArray(vector<int>& nums) {
  int maxSum = INT_MIN;
  for (int i = 0; i < nums.size(); i++) {
    int sum = 0;
    for (int j = i; j < nums.size(); j++) {
      sum += nums[j];
      maxSum = max(maxSum, sum);
    }
  }
  return maxSum;
}

常见问题解答

1. 动态规划和分支法,哪个更好?

一般来说,动态规划的效率更高,因为它避免了重复计算。分支法虽然简单易懂,但效率较低,只适合处理小规模问题。

2. 动态规划的空间复杂度可以优化吗?

可以的。我们发现,dp[i]只依赖于dp[i-1],所以可以不用开辟一个数组来存储所有的dp值,只需要用一个变量来保存dp[i-1]即可。这样,空间复杂度就可以降到O(1)。

3. 最大子数组和问题有什么实际应用?

最大子数组和问题在很多领域都有应用,比如股票交易(寻找最大收益区间)、信号处理(寻找最强信号段)、基因序列分析(寻找相似度最高的基因片段)等等。

4. 如何处理数组中所有元素都是负数的情况?

如果数组中所有元素都是负数,那么最大子数组就是数组中最大的那个元素。

5. 如何理解动态规划中的“状态”和“状态转移方程”?

“状态”指的是问题的子问题,比如“以第i个元素结尾的最大子数组和”。“状态转移方程”指的是不同状态之间的关系,比如dp[i] = max(dp[i-1] + nums[i], nums[i])

希望这篇文章能帮助你更好地理解最大子数组和问题,掌握动态规划和分支法两种解法。算法学习之路漫漫,但只要坚持不懈,你一定能成为算法高手!