求最大子数组之和

问题描述:一个有n个元素的数组,这n个元素可以是正数也可以是负数,数组中连续的一个或多个元素可以组成一个连续的子数组,一个数组可能有多个连续的子数组,求子数组和的最大值,例如:对于数组{1,-2,4,8,-4,7,-1,-5}而言,其最大和的子数组为{4,8,-4,7}最大值为15.

方法一:蛮力法

最简单也是最容易想到的方法就是找出所有子数组,然后求出子数组之和,在所有子数组的和中取最大值。

public class SubArray {
	public static int maxSubArray(int[] a){
		int n=a.length;
		int ThisSum=0,MaxSum=0;
		for(int i=0;i
   
   
    
    MaxSum)
						MaxSum=ThisSum;
				}
			}
		}
		return MaxSum;
	}
	public static void main(String[] args){
		int[] a={1,-2,4,8,-4,7,-1,-5};
		System.out.println(maxSubArray(a));
	}
}

   
   

以上这个算法的时间复杂度为O(n^3),显然效率太低,通过对该算法进行分析发现,许多子数组都重复计算了,鉴于此,下面给出了一种优化的方法。

方法二:重复利用已经计算的子数组和

例如Sum[i,j]=Sum[i,j]+arr[j],采用这种方法可以省去计算Sum[i,j-1]的时间,因此可以提高程序的效率。示例如下:

public class SubArray {
	public static int maxSubArray(int[] a){
		int size=a.length;
		int maxSum=Integer.MIN_VALUE;
		for(int i=0;i
   
   
    
    maxSum){
					maxSum=sum;
				}
			}
		}
		return maxSum;
	}
	public static void main(String[] args){
		int[] a={1,-2,4,8,-4,7,-1,-5};
		System.out.println(maxSubArray(a));
	}
}

   
   
以上这个算法的时间复杂度为O(n^2)

方法三:动态规划的方法:

可以采用动态规划的方法来降低算法的时间复杂度,实现思路如下:

首先可以根据数组的最后一个元素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])最大的一段数组和为All[i-1],同时也计算出(arr[0],....arr[i-1])中包含arr[i-1]的最大的一段数租和为End[i-1],则可以得出如下关系:All[i-1]=max{arr[i-1],End[i-1],All[i-2]}。利用这个公式和动态规划的思想可以得到如下代码:

public class SubArray {
	public static int max(int m,int n){
		return m>n?m:n;
	}
	public static int maxSubArray(int[] arr){
		int n=arr.length;
		int End[]=new int[n];
		int All[]=new int[n];
		End[n-1]=arr[n-1];
		End[0]=All[0]=arr[0];
		for(int i=1;i
   
   
与前面几种方法相比,这种方法的时间复杂度为O(n),显然效率更高,但是由于在计算的过程中额外的申请了两个数组空间,因此该算法的空间复杂度也为O(n)。

方法四:优化的动态规划:

方法三中每次只用到End[i-1]与All[i-1],而不是整个数组中的值,因此可以定义两个变量来保存End[i-1]与All[i-1]的值,并且可以反复利用,这样就可以在保证时间复杂度为O(n)的同时降低空间复杂度。示例如下:

public class SubArray {
	public static int max(int m,int n){
		return m>n?m:n;
	}
	public static int maxSubArray(int[] arr){
		int n=arr.length;
		int nAll=arr[0];//有n个数字数组的最大子数组和
		int nEnd=arr[0];//有n个数字数组包含最后一个元素的子数组的最大和
		for(int i=1;i
    
    
在知道子数组的最大和之后,如何才能确定最大子数组的位置呢?为了得到最大子数组的位置,首先介绍另外一种计算最大子数组和的方法。在方法三中,通过公示End[i]=max(End[i-1]+arr[i],arr[i])的分析可以看出,当End[i-1]<0时,End[i]=arr[i],其中,End[i]表示包含array[i]的子数组和,如果某一个值使得End[i-1]<0,那么就认为arr[i]重新开始。示例如下:
public class SubArray {
	private static int begin=0;//记录最大子数组的起始位置
	private static int end=0;//记录最大子数组的结束位置
	
	public static int max(int m,int n){
		return m>n?m:n;
	}
	public static int maxSubArray(int[] arr){
		int maxSum=Integer.MIN_VALUE;//子数组最大值
		int nSum=0;//包含子数组最后一位的最大值
		int nStart=0;
		for(int i=0;i
      
      
       
       maxSum){
				maxSum=nSum;
				begin=nStart;
				end=i;
			}
		}
		return maxSum;
	}
	public static void main(String[] args){
		int[] a={1,-2,4,8,-4,7,-1,-5};
		System.out.println(maxSubArray(a));
		System.out.println("begin="+begin+",end="+end);
		
	}
}

      
      

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

互联网极客

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值