最大子序和--分治法(附四种算法代码)

 本文代码均采用C++

0x01.问题

给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。

输入示例:[-2,1,-3,4,-1,2,1,-5,4]

输出示例:6

解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。 

C++函数形式为   int maxSubArray(vector<int>& nums) 

0x02.分析问题

 这是一个连续的子列和问题,很明显,第一种做法就出来了,暴力枚举,时间复杂度为O(n^2),这里面存在许多相互独立的子问题,所以可以考虑动态规划,时间复杂度为O(n),然后,可以根据实际的子列情况在线处理,贪心算法就出来了,时间复杂度因也可以为O(n),在这个问题中,我们采用时间复杂度更低的分治法解决。

0x03.分治法基本思路

分治法的基本思路:

  • 将一个规模为N的问题,分解成K个规模较小的子问题,这些子问题相互独立且月原问题性质相同。
  • 求解出子问题的解,合并得到原问题的解。

分治法的基本特征:

  • 该问题的规模缩小到一定程度就可以容易的解决。
  • 该问题可以分解为若干个规模较小的相同问题,即该问题具有最优子结构性质。
  • 利用该问题分解出子问题的解,可以合并为该问题的解。
  • 该问题所分解出的各个子问题是相互独立的,即子问题之间不包含公共的子子问题。

分治算法大多采用递归实现。

分治法的思维过程:

  1. 找到最小问题规模时的求解方法。
  2. 考虑随着问题规模增大时的求解方法。
  3. 找到求解的递归函数,设计递归程序。

0x04.此问题的分治法分析过程

若原序列只有一个数,那么最大值就是它本身。当原序列的元素非常多,我们应该怎样使用分治的思维去处理呢?

分治,就是分而治之,把每个小问题分到小,最后解决它,然后合并所有小问题,就能得到最终的解。

对于一个序列,如何去分开它呢,最简单的分治思想,就是左右各一半,然后左边处理,右边处理,再合起来处理,找出里面的最大值,就可以了,那么,是什么原理在支撑着我们只要把左边的,右边的,一起的最大值求出来就可以了呢?

关键:以数组中点为界,目标值只能在三处---要么在中点左边(不含中点),要么必须包含中点,要么右边(不含中点)。

有了这条思路,我们就可以开始写代码了。

具体如何把分治的思想用代码表述出来,可以参考 归并排序算法,原理差不多:戳我前往

0x05.代码--分治法

int crossSum(vector<int>& 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<int>& 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<int>& nums) {
	return helper(nums, 0, nums.size() - 1);
}

0x06.复杂度分析

时间复杂度:O(logN)

空间复杂度:O(logN)--来源递归调用栈产生的空间

0x07.其他算法代码

1:全排列--暴力枚举     --O(N^2)

int maxSubArray(vector<int>& 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)

2.动态规划     --O(N)

状态转移方程:dp[i] = max(dp[i - 1] + nums[i], nums[i]) result = max(result, dp[i]); dp[i]表示表示nums中以nums[i]结尾的最大子序和,result记录所有最大自序和中的最大值。

int maxSubArray(vector<int>& nums){
	int result = INT_MIN;
	int numsSize = int(nums.size());
	vector<int> 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)

3.动态规划--改进     --O(N)

int maxSubArray(vector<int>& 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)

4.贪心算法     --O(N)

int maxSubArray(vector<int>& 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

  • 22
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
最大连续子序列问题是指在一个序列中,找到一个连续的子序列,使得它们的和最大。这个问题可以使用分治法动态规划算法解决。 1. 分治法 使用分治法求解最大连续子序列问题的思路是:将原序列分成两个部分,最大子序列可能存在于左半部分、右半部分或跨越左右两个部分。分别计算这三种情况下的最大子序列和,然后取最大值即可。 下面是使用分治法求解最大连续子序列问题的Python代码示例: ```python def max_subarray(nums: List[int]) -> int: return _max_subarray(nums, 0, len(nums) - 1) def _max_subarray(nums: List[int], left: int, right: int) -> int: if left == right: return nums[left] mid = (left + right) // 2 left_max = _max_subarray(nums, left, mid) right_max = _max_subarray(nums, mid + 1, right) # 计算跨越左右两个部分的最大子序列和 cross_max = nums[mid] left_cross_max = nums[mid] for i in range(mid - 1, left - 1, -1): left_cross_max += nums[i] cross_max = max(cross_max, left_cross_max) right_cross_max = nums[mid + 1] for i in range(mid + 2, right + 1): right_cross_max += nums[i] cross_max = max(cross_max, right_cross_max) return max(left_max, right_max, cross_max) ``` 在上面的代码中,我们使用递归的方式将原序列分成两个部分,然后计算跨越左右两个部分的最大子序列和、左半部分的最大子序列和、右半部分的最大子序列和,取三者中的最大值作为整个序列的最大子序列和。 2. 动态规划 使用动态规划算法求解最大连续子序列问题的思路是:从头开始遍历序列,对于每一个位置i,计算以i结尾的最大子序列和,然后取所有的最大子序列和的最大值作为问题的解。 具体来说,我们定义一个数组dp,其中dp[i]表示以i结尾的最大子序列和。对于dp[i]来说,它的值可以由dp[i-1]和nums[i]计算得到,即dp[i] = max(dp[i-1] + nums[i], nums[i])。 下面是使用动态规划算法求解最大连续子序列问题的Python代码示例: ```python def max_subarray(nums: List[int]) -> int: dp = [0] * len(nums) dp[0] = nums[0] max_sum = nums[0] for i in range(1, len(nums)): dp[i] = max(dp[i-1] + nums[i], nums[i]) max_sum = max(max_sum, dp[i]) return max_sum ``` 在上面的代码中,我们使用一个循环遍历整个序列,计算以每一个位置为结尾的最大子序列和,并且更新全局最大值。 希望这个回答能够帮到您!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ATFWUS

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值