Leetcode53——Maximum Subarray
2017-03-14 17:18
302 查看
文章作者:Tyan
博客:noahsnail.com | CSDN | 简书
本文主要是对最大子数组(序列)问题求解的学习与总结,最大子数组问题是一道经典的算法题,这道题解法有很多,因此可以学习到很多求解问题的思路,并可以学习到算法的优化过程。
Find the contiguous subarray within an array (containing at least one number) which has the largest sum.
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.
中文:
主要是给定一个数组,求解数组的子数组中,数组元素和最大的那一个子数组,返回的是最大子数组的和。
最简单也是最容易想到的思路就是三层循环,对
Leetcode上的运行结果如下:
解法二
从Leetcode结果可以看出,时间超时了,O(n3)的时间复杂度确实太高了,需要进行优化。分析上面的代码,在i不变的情况下,j每增加1,其和都是在上次求和基础上加上最新的元素,而在第三层循环中都是重新从i开始计算求和,因此存在数据冗余(求和的重复计算),因此需要需要去掉算法中的冗余部分。这种情况下的代码复杂度变为O(n2),代码如下:
Leetcode上运行结果如下:
解法三
从Leetcode结果可以看出,时间还是超时了,但从执行的测试数据数量上来看,比第一次多执行了两个,但在最后一个测试数据上时间超时了。那么能不能有进一步的优化呢?答案是肯定有的。可以使用分治法来求解,算法复杂度为O(nlogn),但是其实本题并不适合使用分治法,太复杂。虽然算法复杂度降低了一些,因此这里略过分治法,直接寻找更优解法。
解法四
还有没有更好的方法呢?答案也是肯定的。首先假设存在最大子数组X,则最大子数组X中的任意一个子数组x都不应该为负数,如果x为负数,则X必定不是最大子数组(可用反证法证明)。根据这个思想,我们只需要以此累加数组元素,并将和与0比较,如果小于0,则需要在剩下的元素中重新寻找是否存在最大子数组,如果不小于0,则与保存的最大子数组值进行比较,如果大于最大子数组值,则更新最大子数组值。这样只需要一次遍历就能找到最大子数组,这种解法的算法复杂度为O(n)。根据这个思路,解决这个问题的算法复杂度代码如下:
Leetcode通过了。
解法五
还有没有别的方法呢?答案还是肯定的。使用动态规划求解。动态规划过程是:每次决策依赖于当前状态,又随即引起状态的转移。一个决策序列就是在变化的状态中产生出来的,所以,这种多阶段最优化决策解决问题的过程就称为动态规划。 使用动态规划求解问题,最重要的就是确定动态规划三要素:(1)问题的阶段;(2)每个阶段的状态;(3)从前一个阶段转化到后一个阶段之间的递推关系。
1.起始阶段
Leetcode通过了。
分析解法四与解法五
其实解法四与解法五是一致的,解法四中的sum等于解法五中的curMax[i],解法五中如果
博客:noahsnail.com | CSDN | 简书
本文主要是对最大子数组(序列)问题求解的学习与总结,最大子数组问题是一道经典的算法题,这道题解法有很多,因此可以学习到很多求解问题的思路,并可以学习到算法的优化过程。
1. 问题描述
英文:Find the contiguous subarray within an array (containing at least one number) which has the largest sum.
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.
中文:
主要是给定一个数组,求解数组的子数组中,数组元素和最大的那一个子数组,返回的是最大子数组的和。
2. 求解
解法一最简单也是最容易想到的思路就是三层循环,对
(i,j),i<=j的情况进行遍历,这种情况下的算法复杂度为O(n3)。代码如下:
public class Solution { public int maxSubArray(int[] nums) { //如果需要节省空间,可将n替换 int n = nums.length; int max = nums[0]; for(int i = 0; i < n; i++) { for(int j = i; j < n; j++){ int sum = 0; //注意k的边界,存在i=j的情况 for(int k = i; k <= j; k++) { sum += nums[k]; if(sum > max) { max = sum; } } } } return max; } }
Leetcode上的运行结果如下:
解法二
从Leetcode结果可以看出,时间超时了,O(n3)的时间复杂度确实太高了,需要进行优化。分析上面的代码,在i不变的情况下,j每增加1,其和都是在上次求和基础上加上最新的元素,而在第三层循环中都是重新从i开始计算求和,因此存在数据冗余(求和的重复计算),因此需要需要去掉算法中的冗余部分。这种情况下的代码复杂度变为O(n2),代码如下:
public class Solution { public int maxSubArray(int[] nums) { int n = nums.length; int max = nums[0]; for(int i = 0; i < n; i++) { int sum = 0; for(int j = i; j < n; j++){ sum += nums[j]; if(sum > max) { max = sum; } } } return max; } }
Leetcode上运行结果如下:
解法三
从Leetcode结果可以看出,时间还是超时了,但从执行的测试数据数量上来看,比第一次多执行了两个,但在最后一个测试数据上时间超时了。那么能不能有进一步的优化呢?答案是肯定有的。可以使用分治法来求解,算法复杂度为O(nlogn),但是其实本题并不适合使用分治法,太复杂。虽然算法复杂度降低了一些,因此这里略过分治法,直接寻找更优解法。
解法四
还有没有更好的方法呢?答案也是肯定的。首先假设存在最大子数组X,则最大子数组X中的任意一个子数组x都不应该为负数,如果x为负数,则X必定不是最大子数组(可用反证法证明)。根据这个思想,我们只需要以此累加数组元素,并将和与0比较,如果小于0,则需要在剩下的元素中重新寻找是否存在最大子数组,如果不小于0,则与保存的最大子数组值进行比较,如果大于最大子数组值,则更新最大子数组值。这样只需要一次遍历就能找到最大子数组,这种解法的算法复杂度为O(n)。根据这个思路,解决这个问题的算法复杂度代码如下:
public class Solution { public int maxSubArray(int[] nums) { int n = nums.length; int max = nums[0]; int sum = 0; for(int i = 0; i < n; i++) { sum += nums[i]; if(sum > max) { max = sum; } if(sum < 0) { sum = 0; } } return max; } }
Leetcode通过了。
解法五
还有没有别的方法呢?答案还是肯定的。使用动态规划求解。动态规划过程是:每次决策依赖于当前状态,又随即引起状态的转移。一个决策序列就是在变化的状态中产生出来的,所以,这种多阶段最优化决策解决问题的过程就称为动态规划。 使用动态规划求解问题,最重要的就是确定动态规划三要素:(1)问题的阶段;(2)每个阶段的状态;(3)从前一个阶段转化到后一个阶段之间的递推关系。
1.起始阶段
(i=0),
max = nums[0];2.第
i(i > 0)个阶段,
max = curMax[i],
curMax是第i个阶段的最大子序列和;3.第
i-1和第
i个阶段的关系,
curMax[i] = Math.max(curMax[i - 1] + nums[i], nums[i]);4.根据前面动态规划的定义,则最大子序列和
max = Math.max(max, curMax[i])。
public class Solution { public int maxSubArray(int[] nums) { int n = nums.length; //curMax是当前的最大子序列和 int[] curMax = new int ; curMax[0] = nums[0]; int max = nums[0]; for (int i = 1; i < n; i ++) { curMax[i] = Math.max(curMax[i - 1] + nums[i], nums[i]); max = Math.max(max, curMax[i]); } return max; } }
Leetcode通过了。
分析解法四与解法五
其实解法四与解法五是一致的,解法四中的sum等于解法五中的curMax[i],解法五中如果
curMax[i-1]小于0,则
curMax[i] = nums[i],而在解法四中由于第
i-1次时
sum=curMax[i-1],因此需要将sum重置为0,则
sum + nums[i] = nums[i],与
curMax[i] = nums[i]是一致的。如果解法五中
curMax[i-1]大于等于0,则curMax[i] =
curMax[i - 1] + nums[i],此时方法四中
sum = sum + nums[i]。而第
i-1次时,
sum = curMax[i - 1],两者也是等价的。解法五中的curMax[0]替换为sum,curMax[i]替换为sum,将
curMax[i] = Math.max(curMax[i - 1] + nums[i], nums[i]);变换为
sum += nums[i];和
if(sum < 0) { sum = 0; },即可将代码从解法五变换为解法四的代码。
相关文章推荐
- leetcode-53 maximum-subarray
- LeetCode(53) Maximum Subarray
- [Leetcode 53, medium] Maximal subarray
- LeetCode 53(Maximum Subarray)Java
- LeetCode解题-#53-Maximum subarray problem
- 第三周作业1(LeetCode53)
- LeetCode 53 Maximum Subarray
- LeetCode - 53 - Maximum Subarray
- LeetCode_OJ【53】Maximum Subarray
- LeetCode53——Maximum Subarray
- LeetCode-53-Maximum Subarray(C语言实现)
- LeetCode 53/152 Maximum Subarray/Maximum Product Subarray(动态规划)
- LeetCode 53 分治算法,求最大子串
- LeetCode53最大子序和
- leetcode解题之53 # Maximum Subarray Java版
- leetcode 53:Spiral Matrix
- [Leetcode-53]Maximum Subarray 最大子数组和
- LeetCode 53/152 Maximum Subarray/Maximum Product Subarray---DP **
- [LeetCode]53 Maximum Subarray
- leetcode-53 Maximum Subarray