求最大子数组之和
蛮力法:
算法思路:找出所有子数组,然后求出子数组的和然后,在所有子数组的和中取最大值
时间复杂度:O(n^3) 优化后O(n^2)
代码实现:
/**
*
* 暴力法求最大子数组的和问题
* 时间复杂度n(n^3)
* @return
*/
public static int maxSubArray(int arr[]){
int n = arr.length;//数组大小
int ThisSum = 0;//记录主数组的和
int MaxSum = 0;//记录最大子数组的和
int i,j,k;
for (i=0;i<n;i++){//记录子数组的底
for(j=0;j<n;j++){//记录子数组的高
ThisSum =0;
for (k=0;k<j;k++){//计算所遍历出来的子数组的和
ThisSum+=arr[k];
}
if (ThisSum > MaxSum){//找出最大的子数组
MaxSum = ThisSum;
}
}
}
return MaxSum;
}
/**
* 时间复杂度O(n^2)
* @param arr
* @return
*/
public static int maxSubArray1(int arr[]){
int size = arr.length;
int maxSum = Integer.MIN_VALUE;
for(int i = 0; i<size; i++){
int sum = 0;
for (int j=i;j<size;j++){
sum+=arr[j];
if (sum>maxSum){
maxSum = sum;
}
}
}
return maxSum;
}
动态规划法:
算法思路:根据数组中最后一个元素arr[n-1]与最大子数组的关系分为以下三种情况:
1、最大子数组中包含arr[n-1],以arr[n-1]结尾
2、arr[n-1]单独构成最大子数组
3、最大子数组中不包含arr[n-1],求arr[1,...,n-1]的最大子数组转化为求arr[1,...,n-2]的最大子数组
假设:
(arr[0],...,arr[i-1])的最大的一段数组和为nAll
(arr[0],...,arr[i-1])包含arr[i-1]的最大的一段数组和为nEnd
得出关系:
nAll = max{arr[i-1],nEnd(i-1),nAll(不包括arr[i-1]的最大子数组)}
时间复杂度: O(n)
空间·复杂度O(n)
代码实现:
/**
* 时间复杂度O(n)
* 空间复杂度O(n)
* 方法描述:
* 根据数组中最后一个元素arr[n-1]与最大子数组的关系分为以下三种情况:
* 1、最大子数组中包含arr[n-1],以arr[n-1]结尾
* 2、arr[n-1]单独构成最大子数组
* 3、最大子数组中不包含arr[n-1],求arr[1,...,n-1]的最大子数组转化为求arr[1,...,n-2]的最大子数组
*
* 假设:
* (arr[0],...,arr[i-1])的最大的一段数组和为nAll
* (arr[0],...,arr[i-1])包含arr[i-1]的最大的一段数组和为nEnd
* 得出关系:
* nAll = max{arr[i-1],nEnd(i-1),nAll(不包括arr[i-1]的最大子数组)}
*
* @param arr
* @return
*/
public static int maxSubArray2(int arr[]){
int n = arr.length;
int End[] = new int[n];
int All[] = new int[n];
End[n-1] = arr[n-1];
All[n-1] = arr[n-1];
End[0]=All[0]=arr[0];
for (int i = 0; i < n; i++) {
End[i] = max(End[i-1], End[i-1]+arr[i]);
All[i] = max(End[i], All[i-1]);
}
return All[n-1];
}
/**
* 思路同上
* 上述思路只用到End[i-1]和All[i-1],而不是数组中全部的值,
* 所以可以定义两个变量来保存End[i-1]和All[i-1]的值,
* 并可以反复调用
* 空间复杂度降低
* @param arr maxSubArray22
* @return
*/
public static int maxSubArray22(int[] arr){
int n = arr.length;
int nAll = arr[0];
int nEnd = arr[0];
for (int i = 0; i < n; i++) {
nEnd = max(nEnd+arr[i], arr[i]);//单独的arr[i]与包含arr[i]的最大子数组和比较,较大值赋给nEnd
nAll = max(nEnd, nAll);//nEnd与不包含arr[i]的数组进行比较得出最终的最大值
}
return nAll;
}
/**
* 获取较大的值
* @param m
* @param n
* @return
*/
public static int max(int m ,int n){
return m>n?m:n;
}
扩展:确定最大子数组的位置
算法思路:当End[i-1]<0时,End[i]=array[i];子数组从i开始
代码实现:
private static int begin = 0;//最大子数组起始位置
private static int end = 0;//最大子数组结束位置
/**
* 确定最大子数组的位置
* @param arr
* @return
*/
public static int maxSubArray3(int arr[]){
int maxSum = Integer.MIN_VALUE;
int nSum = 0;
int nStart = 0;
for (int i = 0; i < arr.length; i++) {
if (nSum<0) {//End[i-1]<0,子数组从i重新开始
nSum = arr[i];
nStart = i;
}
else {
nSum += arr[i];
}
if (nSum > maxSum) {
maxSum = nSum;//记录最大子数组
begin = nStart;//此时子数组的开始值
end = i;//此时子数组的结束值
}
}
return maxSum;
}
}
完整代码为:
package JBArray;
public class MaxSubArrary {
private static int begin = 0;//最大子数组起始位置
private static int end = 0;//最大子数组结束位置
/**
* 确定最大子数组的位置
* @param arr
* @return
*/
public static int maxSubArray3(int arr[]){
int maxSum = Integer.MIN_VALUE;
int nSum = 0;
int nStart = 0;
for (int i = 0; i < arr.length; i++) {
if (nSum<0) {//End[i-1]<0,子数组从i重新开始
nSum = arr[i];
nStart = i;
}
else {
nSum += arr[i];
}
if (nSum > maxSum) {
maxSum = nSum;//记录最大子数组
begin = nStart;//此时子数组的开始值
end = i;//此时子数组的结束值
}
}
return maxSum;
}
/**
*
* 暴力法求最大子数组的和问题
* 时间复杂度n(n^3)
* @return
*/
public static int maxSubArray(int arr[]){
int n = arr.length;//数组大小
int ThisSum = 0;//记录主数组的和
int MaxSum = 0;//记录最大子数组的和
int i,j,k;
for (i=0;i<n;i++){//记录子数组的底
for(j=0;j<n;j++){//记录子数组的高
ThisSum =0;
for (k=0;k<j;k++){//计算所遍历出来的子数组的和
ThisSum+=arr[k];
}
if (ThisSum > MaxSum){//找出最大的子数组
MaxSum = ThisSum;
}
}
}
return MaxSum;
}
/**
* 时间复杂度O(n^2)
* @param arr
* @return
*/
public static int maxSubArray1(int arr[]){
int size = arr.length;
int maxSum = Integer.MIN_VALUE;
for(int i = 0; i<size; i++){
int sum = 0;
for (int j=i;j<size;j++){
sum+=arr[j];
if (sum>maxSum){
maxSum = sum;
}
}
}
return maxSum;
}
/**
* 时间复杂度O(n)
* 方法描述:
* 根据数组中最后一个元素arr[n-1]与最大子数组的关系分为以下三种情况:
* 1、最大子数组中包含arr[n-1],以arr[n-1]结尾
* 2、arr[n-1]单独构成最大子数组
* 3、最大子数组中不包含arr[n-1],求arr[1,...,n-1]的最大子数组转化为求arr[1,...,n-2]的最大子数组
*
* 假设:
* (arr[0],...,arr[i-1])的最大的一段数组和为nAll
* (arr[0],...,arr[i-1])包含arr[i-1]的最大的一段数组和为nEnd
* 得出关系:
* nAll = max{arr[i-1],nEnd(i-1),nAll(不包括arr[i-1]的最大子数组)}
*
* @param arr
* @return
*/
public static int maxSubArray2(int[] arr){
int n = arr.length;
int nAll = arr[0];
int nEnd = arr[0];
for (int i = 0; i < n; i++) {
nEnd = max(nEnd+arr[i], arr[i]);//单独的arr[i]与包含arr[i]的最大子数组和比较,较大值赋给nEnd
nAll = max(nEnd, nAll);//nEnd与不包含arr[i]的数组进行比较得出最终的最大值
}
return nAll;
}
/**
* 获取较大的值
* @param m
* @param n
* @return
*/
public static int max(int m ,int n){
return m>n?m:n;
}
public static void main(String[] args){
int[] arr = {1,-2,4,8,-4,7,-1,-5};
System.out.println(maxSubArray(arr));
System.out.println(maxSubArray2(arr));
}
}
求数对之差的最大值(与上述类似)
暴力法::
动态规划:
* 时间复杂度O(n)
* 空间复杂度O(n)
* a:数组
* diff[i] 以i为减数的所有对数之差的最大值
* max[i] 前i+1个数的最大值
* diff[i+1]有两种可能性:
* diff[i];
* max[i]-a[i]
* 动态规划的计算表达式为:
* diff[i+1] = max(diff[i],max[i]-a[i])
* max[i+1]= max(max[i],a[i+1])
动态规划的空间优化:
* 思路同上
* 上述思路只用到diff[i]和max[i],而不是数组中全部的值,
* 所以可以定义两个变量来保存diff[i]和max[i]的值,
* 并可以反复调用
* 空间复杂度降低
package JBArray;
/**
* 求数对之差的最大值
* @author Dan
*
*/
public class Maxnum {
/**
* 蛮力法
*/
public static int getMax(int[] a){
if(a==null){
return Integer.MIN_VALUE;
}
int len = a.length;
if (len<=1) {
return Integer.MIN_VALUE;
}
int max = Integer.MIN_VALUE;
for (int i = 0; i < len-1; i++) {
for (int j = i+1; j < len; j++) {
if (a[i]-a[j]>max) {
max = a[i]-a[j];
}
}
}
return max;
}
/**
* 动态规划法
* 时间复杂度O(n)
* 空间复杂度O(n)
* a:数组
* diff[i] 以i为减数的所有对数之差的最大值
* max[i] 前i+1个数的最大值
* diff[i+1]有两种可能性:
* diff[i];
* max[i]-a[i]
* 动态规划的计算表达式为:
* diff[i+1] = max(diff[i],max[i]-a[i])
* max[i+1]= max(max[i],a[i+1])
* @param a
* @return
*/
public static int getMax1(int[] a){
if(a == null)
return Integer.MIN_VALUE;
int len = a.length;
if (len<=1) {
return Integer.MIN_VALUE;
}
int[] diff = new int[len];
int[] max = new int[len];
diff[0]=Integer.MIN_VALUE;
max[0] = a[0];
for(int i = 1;i < len; i++){
diff[i]= Max(diff[i-1],max[i-1] - a[i]);
max[i] = Max(max[i-1],a[i]);
}
return diff[len-1];
}
/**
* 思路同上
* 上述思路只用到diff[i]和max[i],而不是数组中全部的值,
* 所以可以定义两个变量来保存diff[i]和max[i]的值,
* 并可以反复调用
* 空间复杂度降低
* @param a
* @return
*/
public static int getMax2(int[] a){
if(a == null)
return Integer.MIN_VALUE;
int len = a.length;
if (len<=1) {
return Integer.MIN_VALUE;
}
int diff = 0;
int max = a[0];
for(int i = 1;i < len; i++){
diff= Max(diff,max - a[i]);
max = Max(max,a[i]);
}
return diff;
}
public static int Max(int a ,int b){
return a > b ? a : b;
}
public static void main(String[] args) {
int[] a = {1,4,17,3,2,9};
System.out.println(getMax(a));
System.out.println(getMax1(a));
System.out.println(getMax2(a));
}
}