连续最大字段和问题
最大子段和问题描述
给定由 n 个整数(可能为负整数)组成的序列a1,a2,a3...an,求该数列中连续子段和最大!
例如:当(a1,a2,a3,a4,a5)=(-2,11,-4,13,-5,-2)时,最大字段和为 20 (11 + (-4) + 13);
以下例子都是以int data[6] = {-2,11,-4,13,-5,-2}; int n = 6;
初始化数组:
//初始化数组 private static Integer[] array = {-2, 11, -4, 13, -5, -2};
算法一:对所有满足0<=i<=j<=n的(i,j)整数对进行迭代,对每个整数对,程序都要计算array[i...j]的总和,并检验该总和是否大于迄今为止的最大总和
这段代码简洁明了,便于理解,但是程序执行的速度很慢,时间复杂度为O(n^3)
/** * 时间复杂度为 O(n^3) */ public static Integer maxSum1() { int maxSum = 0; //存储最大子段和 int tempSum; //临时存储最大子段和 for (int i = 0; i < array.length - 1; i++) { for (int j = i; j < array.length; j++) { tempSum = 0; for (int k = i; k <= j; k++) { tempSum += array[k]; if (tempSum > maxSum) { maxSum = tempSum; } } } } return maxSum; }
算法二:对于算法一有一个明显的缺点,大量的计算重复。大家可以注意到:
这段代码简洁明了,便于理解,相比算法一程序执行的速度较快,时间复杂度为O(n^2)
注意:array[i...j]的总和与前面计算出的总和(array[i...j-1])密切相关,只需要在其基础上累加即可,无需大量重复计算!
/** * 时间复杂度为 O(n^2) */ public static Integer maxSum2() { int maxSum = 0; //存储最大子段和 int tempSum; //临时存储最大子段和 for (int i = 0; i < array.length - 1; i++) { tempSum = 0; for (int j = i; j < array.length; j++) { tempSum += array[j]; if (tempSum > maxSum) { maxSum = tempSum; } } } return maxSum; }
算法三:可以采用分治算法求解,采用二分法进行二分,然后进行递归求解,分别求出左边连续子段和最大值,右边连续子段和最大值,以及左边和右边连续子段和最大值之和,三者进行比较,从中选择一个最大值进行返回!(这个值即就是当前划分的小区间中最大值)
注意:这段代码不太便于理解,但是程序相对于算法二执行的速度快,时间复杂度为O(n*logn)
/** * 采用分治算法 * 时间复杂度为 O(n*logN) */ public static Integer maxSum3(int left, int right) { int maxSum = 0; if (left == right) { //递归结束条件 if (array[left] > 0) { maxSum = array[left]; } else { maxSum = 0; } return maxSum; } int mid = (left + right) / 2; int leftMaxSum = maxSum3(left, mid); //递归求解左部分最大值 int rightMaxSum = maxSum3(mid + 1, right); //递归求解右部分最大值 //求解左边最大值和右边最大值之和 int leftMax = 0; //记录左边最大值 int leftMaxTemp = 0; //记录左边最大值的临时变量 for (int i = mid; i >= left; i--) { leftMaxTemp += array[i]; if (leftMaxTemp > leftMax) { leftMax = leftMaxTemp; //左边最大值放在 leftMax } } int rightMax = 0; int rightMaxTemp = 0; for (int j = mid + 1; j <= right; j++) { rightMaxTemp += array[j]; if (rightMaxTemp > rightMax) { rightMax = rightMaxTemp; //右边最大值放在 rightMax } } maxSum = leftMax + rightMax;//(左边最大值和右边最大值之和)计算第 3 种情况的最大子段和 //比较(左边最大值)和(右边最大值)以及(两边最大值之和)进行比较,从中选择一个最大值返回 if (maxSum < leftMaxSum) { maxSum = leftMaxSum; } if (maxSum < rightMaxSum) { maxSum = rightMaxSum; } return maxSum; }
算法四:使用动态规划来求解 ,由data[]数组我们易知,当maxSumTemp > 0时,maxSumTemp = data[i] + maxSumTemp (越加越大),否则maxSumTemp = data[i](不然越加越小)
这段代码便于理解,但是程序相对于算法三执行的速度最快,时间复杂度为O(n)
/** * 时间复杂度为 O(n) */ public static Integer maxSum4() { int maxSum = array[0]; int maxSumTemp = array[0]; //初始化 for (int i = 1; i < array.length; i++) { if (maxSumTemp > 0) { //最大值临时变量只有大于零,才可能越加越大 maxSumTemp += array[i]; } else { //最大值临时变量只有小于零,直接等于data[i],否则越加越小 maxSumTemp = array[i]; } if (maxSumTemp > maxSum) { //判断赋值 maxSum = maxSumTemp; } } return maxSum; }
测试代码:
public static void main(String[] args) { // Integer maxSum = maxSum1(); // Integer maxSum = maxSum2(); // Integer maxSum = maxSum3(0, array.length - 1); Integer maxSum = maxSum4(); System.out.println("maxSum = " + maxSum); }
每天进步一点点,日记月累就会变成大牛!