LeetCode#53暨最大连续子序列和问题
2017-10-15 07:01
337 查看
这是一道很有意思的算法题。说它有意思包含了几个方面的内容:首先,它的直观上的求解显而易见、非常容易,但是它的优化求解直到上世纪八十年代才被发现;其次,很多算法书籍(例如《算法导论》、《编程珠玑》,以及Mark Allen Weiss的算法书等)都会讨论它,可见它已经是算法设计的典型教学案例了;最后,它也是各种IT公司笔试面试时常常考察的一道经典算法题目(LeetCode网站上它的题目编号是53)。
来看一下LeetCode网站上关于这道题目的描述:
一、解决方法(一):Brute Force
暴搜的方法最straightforward,我们不做解释。仅给出实现代码如下:
class Solution {
public:
int maxSubArray(vector<int>& nums) {
//int length = nums.size();
int sum = 0;
int max = -2147483648;
for(vector<int>::iterator it1 = nums.begin(); it1 != nums.end(); it1++){
sum = *it1;
if(max < sum)
max = sum;
for (vector<int>::iterator it2 = it1+1; it2 != nums.end(); it2++) {
sum += *it2;
if(max < sum)
max = sum;
}
}
return max;
}
};
但是,BF的复杂度是 O(n2),如果你将上述答案提交到LeetCode,则会显示超时!
二、解决方法(二): Divide to Conquer
算法导论上有讨论这个方法。它的基本认识是,如果把数组分成左右两段,那么加和最大的连续子序列,要么出现在数组的左半部分,要么出现在数组的右半部分,要么出现在中间,即从左半部分和右半部分相邻的地方各区一段。所以可以用分治法来求解,具体实现时需要借助递归。实例代码如下:
class Solution {
public:
int maxSubSumRec(vector<int>& nums, int left, int right){
if(left >= right){
return nums[left];
}
int i,center;
center = (left + right)/2;
int lmax = maxSubSumRec(nums, left, center - 1);
int rmax = maxSubSumRec(nums, center +1, right);
int mmax = nums[center], t = mmax;
for(i = center - 1; i >= left; --i){
t += nums[i];
mmax = max(mmax, t);
}
t = mmax;
for(i = center + 1; i <=right; ++i){
t += nums[i];
mmax = max(mmax, t);
}
return max(mmax, max(lmax, rmax));
}
int maxSubArray(vector<int>& nums) {
int length = int(nums.size()-1);
return maxSubSumRec(nums, 0, length);
}
};
算法的复杂度是O(nlogn)。
三、解决方法(三): Dynamic Programming
这个算法又称为Kadane算法,它是又美国卡耐基梅隆大学的教授Kadane发明的一种用于求解最大连续子序列和问题的最优算法。对于一个长度为n的数组A而言,从A[0] 到 A[j] 是一个子数组(j<n),那么以A[j]结尾的子数组之最大和,要么是 A[j], 要么是 max(A[i]~A[j-1])+A[j] ,其中0 ≤ i ≤ j-1。这就是该算法设计的出发点。
如果你需要了解Kadane算法的更多细节,参考文献【1】是讲解该算法的一个非常好的视频。下面我直接给出基于该算法实现的程序代码:
class Solution {
public:
int maxSubArray(vector<int>& nums) {
vector<int>::iterator it = nums.begin();
int maxSum = *it;
int theSum = *it;
for(it = it+1 ; it != nums.end(); it++){
theSum = max(theSum + *it, *it);
if(theSum > maxSum)
maxSum = theSum;
}
return maxSum;
}
};上述实现的复杂度是O(n)。
最后给出一个用来测试上述函数执行的主程序代码:
#include <iostream>
#include <vector>
using namespace std;
int main(int argc, const char * argv[]) {
int n[] = {6,85,46,-74,26,30,25,-15,-22};
vector<int> a(n,n+9);
Solution sol;
cout<<sol.maxSubArray(a)<<endl;
return 0;
}
参考文献
【1】https://www.youtube.com/watch?v=86CQq3pKSUw
(本文完)
本博客中已经讨论过的LeetCode题目列表
来看一下LeetCode网站上关于这道题目的描述:
一、解决方法(一):Brute Force
暴搜的方法最straightforward,我们不做解释。仅给出实现代码如下:
class Solution {
public:
int maxSubArray(vector<int>& nums) {
//int length = nums.size();
int sum = 0;
int max = -2147483648;
for(vector<int>::iterator it1 = nums.begin(); it1 != nums.end(); it1++){
sum = *it1;
if(max < sum)
max = sum;
for (vector<int>::iterator it2 = it1+1; it2 != nums.end(); it2++) {
sum += *it2;
if(max < sum)
max = sum;
}
}
return max;
}
};
但是,BF的复杂度是 O(n2),如果你将上述答案提交到LeetCode,则会显示超时!
二、解决方法(二): Divide to Conquer
算法导论上有讨论这个方法。它的基本认识是,如果把数组分成左右两段,那么加和最大的连续子序列,要么出现在数组的左半部分,要么出现在数组的右半部分,要么出现在中间,即从左半部分和右半部分相邻的地方各区一段。所以可以用分治法来求解,具体实现时需要借助递归。实例代码如下:
class Solution {
public:
int maxSubSumRec(vector<int>& nums, int left, int right){
if(left >= right){
return nums[left];
}
int i,center;
center = (left + right)/2;
int lmax = maxSubSumRec(nums, left, center - 1);
int rmax = maxSubSumRec(nums, center +1, right);
int mmax = nums[center], t = mmax;
for(i = center - 1; i >= left; --i){
t += nums[i];
mmax = max(mmax, t);
}
t = mmax;
for(i = center + 1; i <=right; ++i){
t += nums[i];
mmax = max(mmax, t);
}
return max(mmax, max(lmax, rmax));
}
int maxSubArray(vector<int>& nums) {
int length = int(nums.size()-1);
return maxSubSumRec(nums, 0, length);
}
};
算法的复杂度是O(nlogn)。
三、解决方法(三): Dynamic Programming
这个算法又称为Kadane算法,它是又美国卡耐基梅隆大学的教授Kadane发明的一种用于求解最大连续子序列和问题的最优算法。对于一个长度为n的数组A而言,从A[0] 到 A[j] 是一个子数组(j<n),那么以A[j]结尾的子数组之最大和,要么是 A[j], 要么是 max(A[i]~A[j-1])+A[j] ,其中0 ≤ i ≤ j-1。这就是该算法设计的出发点。
如果你需要了解Kadane算法的更多细节,参考文献【1】是讲解该算法的一个非常好的视频。下面我直接给出基于该算法实现的程序代码:
class Solution {
public:
int maxSubArray(vector<int>& nums) {
vector<int>::iterator it = nums.begin();
int maxSum = *it;
int theSum = *it;
for(it = it+1 ; it != nums.end(); it++){
theSum = max(theSum + *it, *it);
if(theSum > maxSum)
maxSum = theSum;
}
return maxSum;
}
};上述实现的复杂度是O(n)。
最后给出一个用来测试上述函数执行的主程序代码:
#include <iostream>
#include <vector>
using namespace std;
int main(int argc, const char * argv[]) {
int n[] = {6,85,46,-74,26,30,25,-15,-22};
vector<int> a(n,n+9);
Solution sol;
cout<<sol.maxSubArray(a)<<endl;
return 0;
}
参考文献
【1】https://www.youtube.com/watch?v=86CQq3pKSUw
(本文完)
本博客中已经讨论过的LeetCode题目列表
LeetCode中的两道动态规划题目(#62、#63)
LeetCode中的动态规划题目解答(2)(#64)
LeetCode中的动态规划题目解答(3)(#72、#718)
最大连续子序列和问题(#53)
看看你是否真正掌握了Binary Search(#35)
ZigZag排列问题与经典笔试面试题目解析(#6)
括号匹配问题与经典笔试面试题目解析(#20、#32)
牛顿迭代法与一道经典编程问题(#69)
三道tricky的Leetcode面试题目解析(#48、#169、#231)
道关于哈希的Leetcode题目解析(#187、#389)
杨辉三角与一道经典笔试面试题目(#118、#119)
波兰表达式(Reverse Polish Notation)(#150)
相关文章推荐
- 连续子序列最大和问题的分析
- 关于最大连续子序列问题
- 连续子序列最大和问题的分析
- HDU 1231 最大连续子序列 &&HDU 1003Max Sum (区间dp问题)
- hdu-1003-动态规划-求连续子序列最大和问题
- 算法设计与分析——最大连续子序列之和问题
- 六种姿势拿下连续子序列最大和问题,附伪代码(以HDU 1003 1231为例)
- 六种姿势拿下连续子序列最大和问题,附伪代码(以HDU 1003 1231为例)
- 使用分治法解最大连续子序列和问题
- 总结java最大连续子序列和的问题
- 算法分析——最大连续子序列和的问题
- 最大连续子序列之和问题
- 1003---求最大连续子序列和的动态规划问题
- 最大连续子序列和问题O(n)解法
- 最大连续子序列问题的研究
- 最大连续子序列问题
- 连续子序列和最大问题
- 1011 最大连续子序列问题
- 最大连续子序列和的问题
- 最大连续子序列问题(含环路)