求数组最大子段和

给定一个数组,求这个数组中最大连续子段和:

例如:

Input: [-2,1,-3,4,-1,2,1,-5,4],
Output: 6
Explanation: [4,-1,2,1] has the largest sum = 6.

(1)首先看到这个题我能想到的暴力破解,就是遍历数组,定义一个最大连续数组和,当前数组和大于则替换

时间复杂度为O(n^2)

//暴力破解O(n^2)
	public static int maxSubSum2(int[] a) {
		int maxSum=0;
		for(int i=0;imaxSum) {
					maxSum=sum;
				}
			}
		}
		return maxSum;
	}

(2)接下来我们来优化,采用分治法来解决

将一个大问题,划分为两个小问题,2个小问题再划分,直至问题规模小的可以简单解决,那么数组的最大子数组和就分为三种情况:

  • 右侧数组的最大子数组和
  • 左侧数组的最大子数组和
  • 左右两侧数组中间毗邻位置连接形成的子数组的和的最大值(如-6,4,-3,2####5,-6,9,-8,左侧最大值为4,右侧为9,中间毗邻位置从2和5向两侧相加,得到中间毗邻子数组4,-3,2,5,-6,9和为11,三者比较得最大子数组和为11)

递归到数组中只包含一个数字,进行ln(n)次划分,每次划分后进行n次比较,所以算法时间复杂度为n*ln(n),但是还达不到O(n)的要求

public static int maxSubSum3(int[] a) {
		return max(a,0,a.length-1);
	}
	public static int max(int[] arr,int leftIndex,int rightIndex) {
		int sum = 0,leftHalfMax=0,rightHalfMax=0;
		if(rightIndex-leftIndex==0) {
			return arr[leftIndex];
		}else {
			int center = (leftIndex+rightIndex)/2;
			int maxLeft = max(arr,leftIndex,center);
			int maxRight = max(arr,center+1,rightIndex);
		//以下是查找跨越中间点的最大子序列
			//从中间到左侧
			for(int i=center;i>=leftIndex;--i) {
				sum+=arr[i];
				if(sum>leftHalfMax) {
					leftHalfMax=sum;
				}
			}
			sum=0;
			//从中间到右侧
			for(int i=center+1;i<=rightIndex;++i) {
				sum+=arr[i];
				if(sum>rightHalfMax) {
					rightHalfMax=sum;
				}
			}
			return max(maxLeft,maxRight,leftHalfMax+rightHalfMax);
		}
	}
	public static int max(int a,int b,int c) {
		return a>b?(a>c?a:c):(b>c?b:c);
	}

          上述是采用分治法解决的,时间复杂度为n*ln(n)

(3)接下来我们来使用减治法优化这个问题

假设我们当前已经知道一个最大子数组和,现在在该数组后面增加一个数字,新数组的最大子数组和可能是什么呢

  • 原数组的最大数组和
  • 新增加的数字为正数,和最右侧的子数组形成了一个最大子数组和

然后将这两个数字进行比较即可

public static long maxSubSum4(int[] arr) {
		if(arr==null||arr.length<0){
			return -1;
		}
		long maxSum = arr[0];//所有子数组中最大的和
		long rightEdge = arr[0];
		for(int i=1;ib?a:b;
	}

这种方法的时间复杂度为O(n)

欢迎大家指正!

你可能感兴趣的:(一丢丢算法)