最大子数组和
问题引入
解决思路
1.蛮力枚举 O(n^3)
代码实现
public static int MaxSubArray(int[] arr){
int max = Integer.MIN_VALUE;
for(int l = 0; l < arr.length; l++){
for(int r = 0; r < arr.length; r++){
int Slr = 0; // S(l,r) = 0
for(int i = l; i <= r; i++){
Slr = Slr + arr[i];
}
max = Math.max(max,Slr);
}
}
return max;
}
2.优化枚举 O(n^2)
每次计算可以利用之前的计算结果,S (l , r) = S (l , r - 1) + arr [ r ]
public static int MaxSubArray(int[] arr){
int max = Integer.MIN_VALUE;
for(int l = 0; l < arr.length; l++){
int S = 0;
for(int r = l; r < arr.length; r++){
S = S + arr[r];
max = Math.max(max,S);
}
}
return max;
}
3.分而治之 O(nlogn)
重点在于怎样求解 S3 ,稍加分析可知,可将 S3 分为两左右部分进行计算,Left 是以 arr[mid] 结尾的最大子数组,Right 是以 arr[mid + 1] 开始的最大子数组。S3 = Left + Right。
代码实现
public static int CrossingSubArray(int[] arr,int low,int high){
int left = Integer.MIN_VALUE;
int sum = 0;
int mid = low + (high - low) / 2;
for(i = mid; i >= low; i--){
sum = sum + arr[i];
left = Math.max(left,sum);
}
int right = Integer.MIN_VALUE;
sum = 0;
for(int i = mid + 1; i <= high; i++){
sum = sum + arr[i];
right = Math.max(right,sum);
}
int S_3 = left + right;
return S_3;
}
算法实例
代码实现
public static int MaxSubArray(int[] arr,int low,int high){
if(low == high){
return arr[low];
}
int mid = low + (high - low) / 2;
int S_1 = MaxSubArray(arr,low,mid);
int S_2 = MaxSubArray(arr,mid + 1,high);
int S_3 = CrossingSubArray(arr,low,high);
int S = Math.max(S_1,S_2);
int S_max = Math.max(S,S_3);
return S_max;
}
4.动态规划 空间:O(n) 时间:O(n)
dp[i] 表示以 arr[i] 结尾的最大子数组的和,所以本题结果即为返回 dp 数组的最大值。
状态转移方程:
// arr[i] 要么自成一派,要么和前边的数组合并
dp[i] = Math.max(arr[i], dp[i - 1] + arr[i]);
代码实现:
public static int MaxSubArray(int[] arr){
int n = arr.length;
int[] dp = new int[n];
dp[0] = arr[0];
for(int i = 1; i < n; i++){
dp[i] = Math.max(arr[i],dp[i - 1] + arr[i]);
}
int res = Integer.MIN_VALUE;
for(int i = 0; i < n; i++){
res = Math.max(res,dp[i]);
}
return res;
}
5.动态规划(压缩空间) 时间:O(n)
观察发现,我们最后只需返回最大子数组的最大值,而 dp 数组中其他值我们并不需要,所以我们不必为其申请一个长度为 n 的数组保存其值,只需定义两个变量循环调用,不断更新结果最大值即可。
public static int MaxSubArray(int[] arr){
int n = arr.length;
if(n == 0){
return 0;
}
int dp_0 = arr[0];
int dp_1 = 0;
int res = dp_0;
for(int i = 1; i < n; i++){
dp_1 = Math.max(arr[i],dp_0 + arr[i]);
dp_0 = dp_1;
res = Math.max(res,dp_1);
}
return res;
}