题目

image

暴力

思路:枚举所有连续子数组的左右端点:

  • 枚举起点 i
  • 枚举终点 j
  • 再计算 nums[i...j] 的和
    如果每次都重新计算区间和,需要三层循环。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Solution {
public:
int maxSubArray(vector<int>& nums) {
int n = nums.size();
int ans = INT_MIN;

for (int left = 0; left < n; left++) {
for (int right = left; right < n; right++) {
int sum = 0;

for (int k = left; k <= right; k++) {
sum += nums[k];
}

ans = max(ans, sum);
}
}

return ans;
}
};

时间复杂度:O(n3)
空间复杂度:O(1)

优化

思路:暴力法中,区间 [left, right] 的和是重复计算的。

例如:[left, right] 的和= nums[left] + ... + nums[right - 1] + nums[right]

当固定起点 left 后,可以不断扩展终点 right,动态维护当前区间和:sum += nums[right];

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Solution {
public:
int maxSubArray(vector<int>& nums) {
int n = nums.size();
int ans = INT_MIN;

for (int left = 0; left < n; left++) {
int sum = 0;

for (int right = left; right < n; right++) {
sum += nums[right];
ans = max(ans, sum);
}
}

return ans;
}
};

时间复杂度:O(n2)
空间复杂度:O(1)

最优

思路:定义dp[i]:以 nums[i] 结尾的最大子数组和

对于 nums[i],以它结尾的最大子数组只有两种选择:

选择一:接在之前的子数组后面dp[i - 1] + nums[i]

选择二:重新从 nums[i] 开始nums[i]

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

最后答案是所有 dp[i] 中的最大值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Solution {
public:
int maxSubArray(vector<int>& nums) {
int n = nums.size();
vector<int> dp(n);

dp[0] = nums[0];
int ans = dp[0];

for (int i = 1; i < n; i++) {
dp[i] = max(nums[i], dp[i - 1] + nums[i]);
ans = max(ans, dp[i]);
}

return ans;
}
};

时间复杂度:O(n)
空间复杂度:O(n)

空间优化后的 Kadane 算法

因为 dp[i] 只依赖 dp[i - 1],不需要保存整个数组,只保留前一个状态即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Solution {
public:
int maxSubArray(vector<int>& nums) {
int current = nums[0];
int ans = nums[0];

for (int i = 1; i < nums.size(); i++) {
current = max(nums[i], current + nums[i]);
ans = max(ans, current);
}

return ans;
}
};

时间复杂度:O(n)
空间复杂度:O(1)

分治法较为复杂,留空,之后补