LeetCode#53暨最大连续子序列和问题

这是一道很有意思的算法题。说它有意思包含了几个方面的内容:首先,它的直观上的求解显而易见、非常容易,但是它的优化求解直到上世纪八十年代才被发现;其次,很多算法书籍(例如《算法导论》、《编程珠玑》,以及Mark Allen Weiss的算法书等)都会讨论它,可见它已经是算法设计的典型教学案例了;最后,它也是各种IT公司笔试面试时常常考察的一道经典算法题目(LeetCode网站上它的题目编号是53)。


来看一下LeetCode网站上关于这道题目的描述:



一、解决方法(一):Brute Force

暴搜的方法最straightforward,我们不做解释。仅给出实现代码如下:

class Solution {
public:
    int maxSubArray(vector& nums) {
        //int length = nums.size();
        
        int sum = 0;
        int max = -2147483648;
        
        for(vector::iterator it1  = nums.begin(); it1 != nums.end(); it1++){
            sum = *it1;
            if(max < sum)
                max = sum;
            
            for (vector::iterator it2 = it1+1; it2 != nums.end(); it2++) {
                sum += *it2;
                
                if(max < sum)
                    max = sum;
            }
        }
        
        return max;
    }
};

但是,BF的复杂度是 O(n 2 ),如果你将上述答案提交到LeetCode,则会显示超时!


二、解决方法(二):  Divide to Conquer

算法导论上有讨论这个方法。它的基本认识是,如果把数组分成左右两段,那么加和最大的连续子序列,要么出现在数组的左半部分,要么出现在数组的右半部分,要么出现在中间,即从左半部分和右半部分相邻的地方各区一段。所以可以用分治法来求解,具体实现时需要借助递归。实例代码如下:

class Solution {
public:
    
    int maxSubSumRec(vector& 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& nums) {
        
        int length = int(nums.size()-1);
        return maxSubSumRec(nums, 0, length);
    }
};

算法的复杂度是O(nlogn)。


三、解决方法(三):  Dynamic Programming

这个算法又称为Kadane算法,它是又美国卡耐基梅隆大学的教授Kadane发明的一种用于求解最大连续子序列和问题的最优算法。对于一个长度为n的数组A而言,从A[0] 到 A[j] 是一个子数组(jA[j], 要么是 max(A[i]~A[j-1])+A[j] ,其中0 ≤ i ≤ j-1。这就是该算法设计的出发点。

如果你需要了解Kadane算法的更多细节,参考文献【1】是讲解该算法的一个非常好的视频。下面我直接给出基于该算法实现的程序代码:

class Solution {
public:
    
    int maxSubArray(vector& nums) {
        
        vector::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 
#include 

using namespace std;

int main(int argc, const char * argv[]) {
    
    int n[] = {6,85,46,-74,26,30,25,-15,-22};
    
    vector a(n,n+9);
    
    Solution sol;
    cout<


参考文献

【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)


你可能感兴趣的:(数据结构与算法)