LeetCode 53. Maximum Subarray

来源:互联网 发布:世界营销网络地图 编辑:程序博客网 时间:2024/06/05 07:01

             这道题的题意是给定一个数组,要求求出最大的连续子序列的和。

             这道题有很多的做法,而我做的有四种,下面我按照时间复杂度从高到低说明一下这些算法。

(1) 最简单的做法是直接枚举左右端点,然后计算两个端点之间元素的和,枚举左右端点复杂度为O(n2),计算和的复杂度为O(n),总时间复杂度为O(n3)。实现比较简单,就

         不给出代码。而O(n3)的时间复杂度不足以通过这道题。

(2)通过观察,我们可以发现上一种做法中的计算和的步骤是可以简化的。第二种做法可以先声明一个sum数组。sum[i] 表示原数组的元素0~i的和。计算的过程的时间复杂度

         为O(n),有了这个数组,计算i~j的和只要用sum[j] - sum[i-1]即可,计算和的复杂度为O(1)。总的时间复杂度为O(n2),代码如下:

       

const int maxn = 1000000 + 20 ;int sum[maxn] ;# define INF 100000000 class Solution {public:    int maxSubArray(vector<int>& nums) ;};int Solution::maxSubArray(vector<int>& nums) {memset(sum, 0, sizeof(sum)) ;vector<int> a ;a.push_back(0) ;int n = nums.size() ;for (int i=0; i<n; i++) a.push_back(nums[i]) ;sum[0] = 0 ;int ans = -INF ;for (int i=1; i<=n; i++) sum[i] = sum[i-1] + a[i] ;for (int i=1; i<=n; i++) {for (int j=0; j<i; j++) ans = max(sum[i]- sum[j], ans) ;}return ans ;} 

            但O(n2)的时间复杂度依然不足以通过这道题。


(3) 第三种方法用到了分治的思想。我们可以每次把数组分成两半,则有最大连续和的子序列有三种可能:1、左半边数组;2、右半边数组;3、左半边和右半边都有一部分

          (需要连续)。因此我们可以写出一个递归的程序:当前序列的最大连续和等于左半边最大连续和、右半边最大连续和、还有通过中间的最大连续和。计算通过中间的最

           大连续和的时间复杂度为O(n) (只要从中间向两边扫描即可)。则有推导公式,T(n) = T(n/2) + O(n),则时间复杂度O(nlogn),这样就能够通过这道题,代码如下:

const int maxn = 1000000 + 20 ;int sum[maxn] ;# define INF 100000000 class Solution {public:    int maxSubArray(vector<int>& nums) ;    int dfs(vector<int>& nums, int x, int y) ;};int Solution::maxSubArray(vector<int>& nums) {return dfs(nums, 0, nums.size() ) ;} int Solution::dfs(vector<int>& nums, int x, int y) {if (x >= y) return -INF ;if (x + 1 == y) return nums[x] ;int ans = -INF ;int m = x + (y-x)/2 ;ans = max(max(dfs(nums,x,m), dfs(nums,m,y)), ans) ;int L = nums[m-1], R = nums[m] ;int lv = 0, rv = 0 ;for (int i=m-1; i>=x; i--) {lv += nums[i] ; L = max(L, lv) ;}for (int i=m; i<y; i++) {rv += nums[i] ; R = max(R, rv) ;}ans = max(L+R, ans) ;//cout << x << " " << y << " " << ans << endl ;return ans ;}


(4)但O(nlogn)的做法并不是最快的。回到第二种做法,经过思考,我们可以发现第一维的枚举是不必要的,我们不需要枚举左端点,只要维护最小的左端点即可,而维护可

         以在枚举右端点的过程中做到。这样时间复杂度就降到了O(n),代码如下:

const int maxn = 1000000 + 20 ;int sum[maxn] ;# define INF 100000000 class Solution {public:    int maxSubArray(vector<int>& nums) ;};int Solution::maxSubArray(vector<int>& nums) {memset(sum, 0, sizeof(sum)) ;vector<int> a ;a.push_back(0) ;int n = nums.size() ;for (int i=0; i<n; i++) a.push_back(nums[i]) ;sum[0] = 0 ;int ans = -INF ;for (int i=1; i<=n; i++) sum[i] = sum[i-1] + a[i] ;    int minsum = sum[0] ;    for (int i=1; i<=n; i++) {    ans = max(ans, sum[i] - minsum) ;    minsum = min(minsum, sum[i]) ;    }return ans ;} 



0 0
原创粉丝点击