Java学习手册:(数据结构与算法-数组)如何求最大连续子数组之和?

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/MaybeForever/article/details/99969749

问题描述

一个有n个元素的数组,这n个元素可以是正数也可以是负数,数组中连续的一个或多个元素可以组成一个连续的子数组,一个数组可能有多个这样的连续子数组,求子数组和的最大值。

示例:

对于数组{1,-2,4,8,-4,7,-1,-5}而言,其最大和的子数组为{4,8,-4,7},最大值为15。

方法一:

暴力法。找出所有子数组,然后求出子数组之和,在所有子数组和中取最大值。
时间复杂度:O(n^3)

方法一代码:

package com.haobi;
/*
 * 如何求最大子数组之和?
 */
public class Test8 {
	public static void main(String[] args) {
		int[] arr = {1,-2,4,8,-4,7,-1,-5};
		System.out.println(maxSubArray1(arr));
	}
	
	public static int maxSubArray1(int[] arr) {
		int n = arr.length;
		int Max = Integer.MIN_VALUE;
		int Sum = 0;
		for(int i=0;i<n;i++) {
			for(int j=i;j<n;j++) {
				//为Sum赋初值
				Sum = 0;
				//框定区间
				for(int k=i;k<j;k++) {
					//求总和
					Sum += arr[k];
				}
				//每个子数组的和与之前的和的最大值比较
				if(Sum > Max) {
					Max = Sum;
				}
			}
		}
		return Max;
	}
}

方法二:

重复利用已经计算出来的子数组和。例如Sum[i,j]=Sum[i,j-1]+arr[j]。
时间复杂度:O(n^2)

方法二代码:

package com.haobi;
/*
 * 如何求最大子数组之和?
 */
public class Test9 {
	public static void main(String[] args) {
		int[] arr = {1,-2,4,8,-4,7,-1,-5};
		System.out.println(maxSubArray2(arr));
	}
	
	public static int maxSubArray2(int[] arr) {
		int n = arr.length;
		int Max = Integer.MIN_VALUE;
		for(int i=0;i<n;i++) {
			//为Sum赋初值
			int Sum = 0;
			for(int j=i;j<n;j++) {
				Sum+= arr[j];
				if(Sum > Max) {
					Max = Sum;
				}
			}
		}
		return Max;
	}
}

方法三:

通过动态。判断数组的最后一个元素arr[n-1]与最大子数组的关系分为以下3种情况:
(1)最大子数组包含arr[n-1],即以arr[n-1]结尾。
(2)arr[n-1]单独构成最大子数组。
(3)最大子数组不包含arr[n-1],那么问题转换为求arr[0,…,n-2]的最大子数组。
因此,假设All[i-1]表示从arr[0]到arr[i-1]中最大的一段数组之和,End[i-1]表示从arr[0]到arr[i-1]中包含arr[i-1]的最大的一算数组之和,可得到动态方程:All[i-1] = max{arr[i-1],End[i-1],All[i-2]}
时间复杂度:O(n)

方法三代码:

package com.haobi;
/*
 * 如何求最大子数组之和?
 */
public class Test10 {
	public static void main(String[] args) {
		int[] arr = {1,-2,4,8,-4,7,-1,-5};
		System.out.println(maxSubArray3(arr));
	}
	
	public static int maxSubArray3(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=1;i<n;i++) {
			//求arr[i]、以arr[i]结尾的子数组 两者中的最大值
			End[i] = max(End[i-1]+arr[i], arr[i]);
			//状态方程
			All[i] = max(End[i],All[i-1]);
		}
		return All[n-1];
	}
	
	public static int max(int m, int n) {
		return m>n ? m:n;
	}
}

方法四:

优化动态规划。可以定义两个变量保存End[i-1]与All[i-1]的值,并且可以反复利用,这样可以降低空间复杂度。
时间复杂度:O(n)

方法四代码:

package com.haobi;
/*
 * 如何求最大子数组之和?
 */
public class Test11 {
	public static void main(String[] args) {
		int[] arr = {1,-2,4,8,-4,7,-1,-5};
		System.out.println(maxSubArray3(arr));
	}
	
	public static int maxSubArray3(int[] arr) {
		int n = arr.length;
		//有n个数字数组的最大子数组和
		int All = arr[0];
		//有n个数字数组包含最后一个元素的子数组的最大和
		int End = arr[0];
		for(int i=1;i<n;i++) {
			//求arr[i]、以arr[i]结尾的子数组 两者中的最大值
			End = max(End+arr[i], arr[i]);
			//状态方程
			All = max(End, All);
		}
		return All;
	}
	
	public static int max(int m, int n) {
		return m>n ? m:n;
	}
}

猜你喜欢

转载自blog.csdn.net/MaybeForever/article/details/99969749