LeetCode 53. Maximum Subarray
2016-10-11 14:29
295 查看
这道题的题意是给定一个数组,要求求出最大的连续子序列的和。
这道题有很多的做法,而我做的有四种,下面我按照时间复杂度从高到低说明一下这些算法。
(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),代码如下:
但O(n2)的时间复杂度依然不足以通过这道题。
(3) 第三种方法用到了分治的思想。我们可以每次把数组分成两半,则有最大连续和的子序列有三种可能:1、左半边数组;2、右半边数组;3、左半边和右半边都有一部分
(需要连续)。因此我们可以写出一个递归的程序:当前序列的最大连续和等于左半边最大连续和、右半边最大连续和、还有通过中间的最大连续和。计算通过中间的最
大连续和的时间复杂度为O(n) (只要从中间向两边扫描即可)。则有推导公式,T(n) = T(n/2) + O(n),则时间复杂度O(nlogn),这样就能够通过这道题,代码如下:
(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 ;
}
这道题有很多的做法,而我做的有四种,下面我按照时间复杂度从高到低说明一下这些算法。
(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 ;
}
相关文章推荐
- [Leetcode]53. Maximum Subarray
- LeetCode 53. Maximum Subarray
- leetcode:53. Maximum Subarray
- [leetcode] 53. Maximum Subarray
- 【leetcode】53. Maximum Subarray
- [LeetCode] 53. Maximum Subarray
- LeetCode 53. Maximum Subarray
- leetcode - 53. Maximum Subarray
- LeetCode||53. Maximum Subarray
- LeetCode 53. Maximum Subarray
- LeetCode 53. Maximum Subarray
- LeetCode 53. Maximum Subarray
- leetcode_53. Maximum Subarray
- Leetcode:53. Maximum Subarray
- [勇者闯LeetCode] 53. Maximum Subarray
- 【LeetCode】53. Maximum Subarray
- 【leetcode】53. Maximum Subarray
- leetcode 53. Maximum Subarray
- leetcode 53. Maximum Subarray
- 【LeetCode】53. Maximum Subarray