LeetCode53
题目
Given an integer array nums, find the contiguous subarray (containing at least one number) which has the largest sum and return its sum.
Example:
Input: [-2,1,-3,4,-1,2,1,-5,4],
Output: 6
Explanation: [4,-1,2,1] has the largest sum = 6.
穷举法
class Solution {
public int maxSubArray(int[] nums) {
int maxSoFar = 0;
int sum;
for(int i=0; i < nums.length; i++){
sum = 0;
for(int j = i; j < nums.length; j++){
sum += nums[j];
/* sum is sum of x[i..j] */
maxSoFar = max(maxSoFar, sum);
}
}
return maxSoFar;
}
}
时间复杂度为 O ( n 2 ) O(n^2) O(n2)。
动态规划(Kadane算法)
Kadane算法又被称为扫描法,为动态规划(dynamic programming)的一个典型应用。我们用DP来解决最大子数组和问题:对于数组
a
a
a ,用
c
i
c_i
ci 标记子数组
a
[
0..
i
]
a[0..i]
a[0..i]的最大和,那么则有
c
i
=
m
a
x
{
a
i
,
c
i
−
1
+
a
i
}
c_i=max \{ a_i, c_{i−1} + a_i \}
ci=max{ai,ci−1+ai}
子数组最大和即为
m
a
x
c
i
max\ c_i
max ci。
Kadane算法比上面DP更进一步,不需要用一个数组来记录中间子数组和。通过观察容易得到:若
c
i
−
1
≤
0
c_i−1≤0
ci−1≤0,则
c
i
=
a
i
c_i=a_i
ci=ai。用
e
e
e表示以当前为结束的子数组的最大和,以替代数组
c
c
c,那么
e
=
m
a
x
{
a
i
,
e
+
a
i
}
e=max\{a_i,e+a_i\}
e=max{ai,e+ai}
Java实现如下:
class Solution {
public int maxSubArray(int[] nums) {
int max_so_far=nums[0];
int curr_max=nums[0];
for(int i=1;i<nums.length;i++){
curr_max = Math.max(nums[i],curr_max+nums[i]);
max_so_far=Math.max(max_so_far,curr_max);
}
return max_so_far;
}
}
curr_max对应于标记 e e e,max_so_far记录已扫描到的子数组的最大和。Kadane算法只扫描了一遍数组,因此时间复杂度为 O ( n ) O(n) O(n)。
分治解法
所谓分治法,是指将一个问题分解为两个子问题,然后分而解决之。具体步骤如下:
- 找到中间位置,所求子串有三种情况:在中间位置左边、在中间位置右边、在中间位置两边;
- 中间位置左边右边的和最大的子串可以递归地求得;
- 第三种情况必定包含总区间的中间元素,因此等价于求从中间元素开始往左累加的最大值 + 从中间元素开始往右累加的最大值。
Java实现如下:
class Solution {
public int maxSubArray(int[] nums) {
if (nums == null || nums.length == 0)
return 0;
return rec(nums, 0, nums.length - 1);
}
//返回这个之间的最大子序和
private int rec(int[] arr, int L, int R) {
if (L == R)
return arr[L];
int mid = L + (R - L) / 2;
int LMax = rec(arr, L, mid);
int RMax = rec(arr, mid + 1, R);
int sum = 0, LSumMax = Integer.MIN_VALUE, RSumMax = Integer.MIN_VALUE;
for (int i = mid; i >= L; i--) {
sum += arr[i];
if (sum > LSumMax) {
LSumMax = sum;
}
}
sum = 0;
for (int i = mid + 1; i <= R; i++) {
sum += arr[i];
if (sum > RSumMax) {
RSumMax = sum;
}
}
int crossMax = LSumMax + RSumMax;
//compare crossMax、LMax,RMax
if (LMax >= RMax && LMax >= crossMax)
return LMax;
if (RMax >= LMax && RMax >= crossMax)
return RMax;
return crossMax;
}
}
时间复杂度为 O ( n ∗ l o g n ) O(n∗log n) O(n∗logn)。