[LeetCode]112. Maximum Subarray最大和连续子序列
2015-12-16 10:20
267 查看
Find the contiguous subarray within an array (containing at least one number) which has the largest sum.
For example, given the array
the contiguous subarray
click to show more practice.
More practice:
If you have figured out the O(n) solution, try coding another solution using the divide and conquer approach, which is more subtle.
解法1:当我们从头到尾遍历这个数组的时候,对于数组里的一个整数,它有几种选择呢?它只有两种选择: 1、加入之前的SubArray;2. 自己另起一个SubArray。那什么时候会出现这两种情况呢?如果之前SubArray的总体和大于0的话,我们认为其对后续结果是有贡献的。这种情况下我们选择加入之前的SubArray如果之前SubArray的总体和为0或者小于0的话,我们认为其对后续结果是没有贡献,甚至是有害的(小于0时)。这种情况下我们选择以这个数字开始,另起一个SubArray。
设状态为{f[j]},表示以{S[j]}结尾的最大连续子序列和,则状态转移方程如下:
解释如下:
情况一,S[j]不独立,与前面的某些数组成一个连续子序列,则最大连续子序列和为f[j-1]+S[j]。
情况二,S[j]独立划分成为一段,即连续子序列仅包含一个数S[j],则最大连续子序列和为S[j]。
由此易得代码,时间复杂度O(n):
另一种解释:已知了前k个元素的最大子序列和为maxSum(已经被记录下来了),以及一个临时和sum,如果添加了第k+1这个元素,由于是连续子序列这个限制,所以如果k+1这个元素之前的和是小于0的,那么对于增加k+1这个元素从而去组成最大子序列是没有贡献的,所以可以把sum 置0(相当于上面的情况二)。代码只是稍作改动:
解法2:分治方法,把序列分为两段,分别求最大连续子序列和,然后归并。时间复杂度O(nlogn)。注意计算maxSubArrayCross时,因为必须要"Cross"左右两半部分,因此左边一半数组的最后一个元素nums[mid]和右边一半数组的第一个元素nums[mid+1]必须要选中,因此计算时分别从最后向最前扫描、最前向最后扫描,方法同解法1。
For example, given the array
[−2,1,−3,4,−1,2,1,−5,4],
the contiguous subarray
[4,−1,2,1]has the largest sum =
6.
click to show more practice.
More practice:
If you have figured out the O(n) solution, try coding another solution using the divide and conquer approach, which is more subtle.
解法1:当我们从头到尾遍历这个数组的时候,对于数组里的一个整数,它有几种选择呢?它只有两种选择: 1、加入之前的SubArray;2. 自己另起一个SubArray。那什么时候会出现这两种情况呢?如果之前SubArray的总体和大于0的话,我们认为其对后续结果是有贡献的。这种情况下我们选择加入之前的SubArray如果之前SubArray的总体和为0或者小于0的话,我们认为其对后续结果是没有贡献,甚至是有害的(小于0时)。这种情况下我们选择以这个数字开始,另起一个SubArray。
设状态为{f[j]},表示以{S[j]}结尾的最大连续子序列和,则状态转移方程如下:
解释如下:
情况一,S[j]不独立,与前面的某些数组成一个连续子序列,则最大连续子序列和为f[j-1]+S[j]。
情况二,S[j]独立划分成为一段,即连续子序列仅包含一个数S[j],则最大连续子序列和为S[j]。
由此易得代码,时间复杂度O(n):
class Solution { public: int maxSubArray(vector<int>& nums) { int maxSum = INT_MIN, sum = 0; for (int i = 0; i < nums.size(); ++i) { sum = max(sum + nums[i], nums[i]); maxSum = max(maxSum, sum); } return maxSum; } };
另一种解释:已知了前k个元素的最大子序列和为maxSum(已经被记录下来了),以及一个临时和sum,如果添加了第k+1这个元素,由于是连续子序列这个限制,所以如果k+1这个元素之前的和是小于0的,那么对于增加k+1这个元素从而去组成最大子序列是没有贡献的,所以可以把sum 置0(相当于上面的情况二)。代码只是稍作改动:
class Solution { public: int maxSubArray(vector<int>& nums) { int maxSum = INT_MIN, sum = 0; for (int i = 0; i < nums.size(); ++i) { //sum = max(sum + nums[i], nums[i]); //maxSum = max(maxSum, sum); if (sum < 0) sum = 0; // 相当于断开前面对结果有害的序列 sum += nums[i]; maxSum = max(maxSum, sum); } return maxSum; } };
解法2:分治方法,把序列分为两段,分别求最大连续子序列和,然后归并。时间复杂度O(nlogn)。注意计算maxSubArrayCross时,因为必须要"Cross"左右两半部分,因此左边一半数组的最后一个元素nums[mid]和右边一半数组的第一个元素nums[mid+1]必须要选中,因此计算时分别从最后向最前扫描、最前向最后扫描,方法同解法1。
class Solution { public: int maxSubArray(vector<int>& nums) { int n = nums.size(); if (n <= 0) return INT_MIN; return maxSubArrayRecursion(nums, 0, n - 1); } private: int maxSubArrayRecursion(vector<int>& nums, int left, int right) { if (left == right) return nums[left]; int mid = (left + right) >> 1; int leftMax = maxSubArrayRecursion(nums, left, mid); // 左边半部分最大连续子序列 int rightMax = maxSubArrayRecursion(nums, mid + 1, right); // 右边半部分最大连续子序列 int crossMax = maxSubArrayCross(nums, left, mid, right); // 包含nums[mid]和nums[mid+1]的最大连续子序列 if (leftMax >= rightMax && leftMax >= crossMax) return leftMax; else if (rightMax >= leftMax && rightMax >= crossMax) return rightMax; else return crossMax; } int maxSubArrayCross(vector<int>& nums, int left, int mid, int right) { int leftSum = INT_MIN, lsum = 0, rightSum = INT_MIN, rsum = 0; for (int i = mid; i >= left; --i) { // 左边半部分数组以nums[mid]结束的最大连续子序列 lsum += nums[i]; leftSum = max(leftSum, lsum); } for (int i = mid + 1; i <= right; ++i) { // 右边半部分数组以nums[mid+1]开始的最大连续子序列 rsum += nums[i]; rightSum = max(rightSum, rsum); } return leftSum + rightSum; // "Cross"左右两半数组,因此必须至少选中nums[mid]和nums[mid+1] } };
相关文章推荐
- 理解GRUB2工作原理及配置选项与方法
- 一个经典例子让你彻彻底底理解java回调机制
- 机器学习--岭回归与偏最小二乘
- 调用一个UITableview的一个代理的方法,实现UITableview重载
- java线上调试小结
- MYSQL 环境变量添加
- 利用mmSeg4j分词实现网页文本倾向性分析
- java 包装类
- Android摄像头 怎么搞?
- Android TabHost取消默认加载第一个tab的问题
- AbstractReferenceCountedByteBuf 引用计数引用计数bytebuf基类
- 最全Pycharm教程(27)——Pycharm搜索导航之跳转到声明与定义
- 写给想当程序员的朋友 -- 一个还不太老的程序员的体会
- JAVA设计模式之单例模式
- 转:自定义ASP.NET MVC Html辅助方法
- 测试大端小端的两个个程序
- Spring基础知识汇总
- Windows Server 2008 R2 web服务器发布在线系统时遇到的问题
- c#可自定义码表的base64加密解密算法类
- SparkR的编译安装