本文代码均采用C++
0x01.问题
给定一个整数数组
nums
,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
输入示例:[-2,1,-3,4,-1,2,1,-5,4]
输出示例:6
解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。
C++函数形式为 int maxSubArray(vector
& nums)
这是一个连续的子列和问题,很明显,第一种做法就出来了,暴力枚举,时间复杂度为O(n^2),这里面存在许多相互独立的子问题,所以可以考虑动态规划,时间复杂度为O(n),然后,可以根据实际的子列情况在线处理,贪心算法就出来了,时间复杂度因也可以为O(n),在这个问题中,我们采用时间复杂度更低的分治法解决。
分治法的基本思路:
分治法的基本特征:
分治算法大多采用递归实现。
分治法的思维过程:
若原序列只有一个数,那么最大值就是它本身。当原序列的元素非常多,我们应该怎样使用分治的思维去处理呢?
分治,就是分而治之,把每个小问题分到小,最后解决它,然后合并所有小问题,就能得到最终的解。
对于一个序列,如何去分开它呢,最简单的分治思想,就是左右各一半,然后左边处理,右边处理,再合起来处理,找出里面的最大值,就可以了,那么,是什么原理在支撑着我们只要把左边的,右边的,一起的最大值求出来就可以了呢?
关键:以数组中点为界,目标值只能在三处---要么在中点左边(不含中点),要么必须包含中点,要么右边(不含中点)。
有了这条思路,我们就可以开始写代码了。
具体如何把分治的思想用代码表述出来,可以参考 归并排序算法,原理差不多:戳我前往
int crossSum(vector& nums, int left, int right, int p) {
if (left == right) return nums[left];
int leftSubsum = INT_MIN;
int currSum = 0;
for (int i = p; i > left - 1; i--) {
currSum += nums[i];
leftSubsum = max(leftSubsum, currSum);
}
int rightSubsum = INT_MIN;
currSum = 0;
for (int i = p + 1; i < right + 1; i++) {
currSum += nums[i];
rightSubsum = max(rightSubsum, currSum);
}
return leftSubsum + rightSubsum;
}
int helper(vector& nums, int left, int right) {
if (left == right) return nums[left];
int p = (left + right) / 2;
int leftSum = helper(nums, left, p);
int rightSum = helper(nums, p+1, right);
int crosssum = crossSum(nums, left, right, p);
return max(max(leftSum, rightSum), crosssum);
}
int maxSubArray(vector& nums) {
return helper(nums, 0, nums.size() - 1);
}
int maxSubArray(vector& nums){
int max = INT_MIN;
int numsSize = int(nums.size());
for (int i = 0; i < numsSize; i++){
int sum = 0;
for (int j = i; j < numsSize; j++){
sum += nums[j];
if (sum > max){
max = sum;
}
}
}
return max;
}
空间复杂度:O(1)
状态转移方程:dp[i] = max(dp[i - 1] + nums[i], nums[i]) result = max(result, dp[i]); dp[i]表示表示nums中以nums[i]结尾的最大子序和,result记录所有最大自序和中的最大值。
int maxSubArray(vector& nums){
int result = INT_MIN;
int numsSize = int(nums.size());
vector dp(numsSize);
dp[0] = nums[0];
result = dp[0];
for (int i = 1; i < numsSize; i++){
dp[i] = max(dp[i - 1] + nums[i], nums[i]);
result = max(result, dp[i]);
}
return result;
}
空间复杂度:O(n)
int maxSubArray(vector& nums){
int result = INT_MIN;
int numsSize = int(nums.size());
int dp(nums[0]);
result = dp;
for (int i = 1; i < numsSize; i++){
dp = max(dp + nums[i], nums[i]);
result = max(result, dp);
}
return result;
}
空间复杂度:O(1)
int maxSubArray(vector& nums) {
int n = nums.size();
int currSum, maxSum;
currSum = maxSum = nums[0];
for (int i = 1; i < n; i++) {
currSum = max(currSum + nums[i], nums[i]);
maxSum = max(maxSum, currSum);
}
return maxSum;
}
本质上和改进的动态规划差不多
空间复杂度:O(1)
这是这一类问题的通用解法,如果遇到某些全为负数则输出0的,只需要加入一些判断条件即可。
如果本文对你有帮助,请分享给你的朋友吧!
ATFWUS --Writing By 2020--03--15