连续最大字段和问题
最大子段和问题描述
给定由 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;
初始化数组:
1 2 | //初始化数组 private static Integer[] array = {- 2 , 11 , - 4 , 13 , - 5 , - 2 }; |
算法一:对所有满足0<=i<=j<=n的(i,j)整数对进行迭代,对每个整数对,程序都要计算array[i...j]的总和,并检验该总和是否大于迄今为止的最大总和
这段代码简洁明了,便于理解,但是程序执行的速度很慢,时间复杂度为O(n^3)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | /** * 时间复杂度为 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])密切相关,只需要在其基础上累加即可,无需大量重复计算!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | /** * 时间复杂度为 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)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | /** * 采用分治算法 * 时间复杂度为 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)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | /** * 时间复杂度为 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; } |
测试代码:
1 2 3 4 5 6 7 | 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); } |
每天进步一点点,日记月累就会变成大牛!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架