思路: 比较经典的一个题,求最大连续区间的和。
1. 暴力:直接利用两重循环枚举区间的左右边界点并不断取max即可,时间复杂度O().
2. dp:状态转移表达式——dp[i] = max(dp[i-1]+num[1], num[i]),dp[i]表示以num[i]为结尾的最大区间和,每次的当前节点结尾最大值肯定是上一节点最大值加上当前节点与否,时间复杂度O(n)。
class Solution {
public:
int maxSubArray(vector<int>& nums) {
vector<int> dp(nums.size());
dp[0] = nums[0];
int ans = dp[0];
for(int i = 1; i < nums.size(); i ++){
dp[i] = max(dp[i-1]+nums[i], nums[i]);
ans = max(ans, dp[i]);
}
return ans;
}
};
3. 贪心:从开头就一直选择节点,知道当前的sum<0,则以当前节点为起点重新开始选择;类似于选择最大最小值的思维,过程中不断对结果取max即可,时间复杂度O(n)。
class Solution {
public:
int maxSubArray(vector<int>& nums) {
int ans = INT_MIN, sum = 0;
for(int i = 0; i < nums.size(); i ++){
sum += nums[i];
ans = max(ans, sum);
sum = max(sum, 0);
}
return ans;
}
};
4. 分治:分治的思想即每次都以其中心点为基础,要么取其左侧区间,要么取其右侧区间,要么贯穿中心点(取左右两侧最大区间和的并),然后不断递归下去即可,时间复杂度O(nlogn)。
class Solution {
public:
int maxSubArray(vector<int>& nums) {
int ans = INT_MIN, len = int(nums.size());
ans = maxSubArrayHelper(nums, 0, len-1);
return ans;
}
int maxSubArrayHelper(vector<int>& nums, int left, int right){
if(left==right) return nums[left];
int mid = (left+right)>>1;
int lsum = maxSubArrayHelper(nums, left, mid); // 取左区间
int rsum = maxSubArrayHelper(nums, mid+1, right); // 取右区间
int msum = findMaxCrossingSubarray(nums, left, mid, right); // 贯穿中心点
int ans = max(lsum, rsum);
ans = max(ans, msum); // 取max
return ans;
}
int findMaxCrossingSubarray(vector<int>& nums, int left, int mid, int right){
int lsum = INT_MIN, sum = 0;
for(int i = mid; i >= left; i --){ // 向左延申找到最大区间和
sum += nums[i];
lsum = max(lsum, sum);
}
int rsum = INT_MIN;
sum = 0;
for(int i = mid+1; i <= right; i ++){ // 向右延申找到最大和
sum += nums[i];
rsum = max(rsum, sum);
}
return (lsum+rsum); // 取并
}
};