暴力法
寻找所有可能的子序列和,得出最大值。
时间复杂度: O(n^2)
空间复杂度:O(1)
代码【C++】
class Solution {
public:
int maxSubArray(vector<int>& nums) {
//暴力法
int maxans = nums[0];
for(int i = 0; i < nums.size();i++){
int ret = 0; //保存每次循环子序列的和
for(int j = i; j < nums.size(); j++){
ret += nums[j];
maxans = maxans > ret ? maxans : ret;
}
}
return maxans;
}
};
贪心
**贪心贪在哪里?**计算起点的时候,如果 -2、1在一起,一定是从1开始,负数智慧拉低综合,这就是贪心的地方!
局部最优:当前“连续和”是负数的时候就立马放弃,从下一个元素计算 连续和。
在局部最优的情况下,记录最大的连续和,可以推出全局最优哦。
解题思路:遍历nums,当前子序列连续和使用cur记录,一旦cur的值是负数,就应该从nums[i+1]从0重新累计,因为变为负数的cur之后拉低总和。
这就相当于暴力解法中不断调整最大子序列和区间的起始位置。
对于区间的终点位置,因为每一次累计cur当前的值会和maxans进行比较,所以会及时记录下最大值。
时间复杂度 : O(n)
空间复杂度 : O(1)
代码C++
class Solution {
public:
int maxSubArray(vector<int>& nums) {
//贪心
int maxans = nums[0];
int cur = 0;
for(int i = 0; i < nums.size(); i++){
cur += nums[i];
maxans = maxans > cur ? maxans : cur;
if(cur < 0){
cur = 0;
continue;
}
}
return maxans;
}
};
注意点:cur重置为0的情况;maxans和cur进行比较的位子。
动态规划
dp[i] 表示 nums 中以nums[i] 结尾的最大子序和
dp[i] = max (dp[i-1] +nums[i], nums[i]);
dp[i]是当前的数字,或者是与前面的最大子序和的和。
C++ 【空间复杂度O(n)】
class Solution {
public:
int maxSubArray(vector<int>& nums) {
//动态规划
int result = INT_MIN;
vector<int> dp(nums.size());
dp[0] = nums[0];
result = dp[0];
for(int i = 1; i < nums.size(); i++){
dp[i] = max(dp[i-1]+nums[i], nums[i]);
result = max(result, dp[i]);
}
return result;
}
};
C++ 【空间复杂度O(1)】
class Solution {
public:
int maxSubArray(vector<int>& nums) {
//动态规划
int result = nums[0];
int dp = nums[0];
for(int i = 1; i < nums.size(); i++){
dp = max(dp+nums[i], nums[i]);
result = max(dp, result);
}
return result;
}
};
分治法
时间复杂度: O(nlog(n))
空间复杂度: O(log(n))
取数组中心点为中心,最大子序要么全在中心左边,要么在右边,要么跨中心,分三种情况进行考虑。跨中心的时候,再分治成中心点左侧和右侧的最大子序和问题。
(网络做法)
class Solution
{
public:
int maxSubArray(vector<int> &nums)
{
//类似寻找最大最小值的题目,初始值一定要定义成理论上的最小最大值
int result = INT_MIN;
int numsSize = int(nums.size());
result = maxSubArrayHelper(nums, 0, numsSize - 1);
return result;
}
int maxSubArrayHelper(vector<int> &nums, int left, int right)
{
if (left == right)
{
return nums[left];
}
int mid = (left + right) / 2;
int leftSum = maxSubArrayHelper(nums, left, mid);
//注意这里应是mid + 1,否则left + 1 = right时,会无线循环
int rightSum = maxSubArrayHelper(nums, mid + 1, right);
int midSum = findMaxCrossingSubarray(nums, left, mid, right);
int result = max(leftSum, rightSum);
result = max(result, midSum);
return result;
}
int findMaxCrossingSubarray(vector<int> &nums, int left, int mid, int right)
{
int leftSum = INT_MIN;
int sum = 0;
for (int i = mid; i >= left; i--)
{
sum += nums[i];
leftSum = max(leftSum, sum);
}
int rightSum = INT_MIN;
sum = 0;
//注意这里i = mid + 1,避免重复用到nums[i]
for (int i = mid + 1; i <= right; i++)
{
sum += nums[i];
rightSum = max(rightSum, sum);
}
return (leftSum + rightSum);
}
};