求最大子段和

问题描述:

给定n个整数(可能为负数)组成的序列a1,a2,a3,...,an, 求该序列子段和的最大值

例如:

X={-2, 11, -4, 13, -5, -2}, 其最大子段和为20

最大子段为:11,-4,13

解法一:穷举法

列举所有的可能,求其中的最大值

算法实现如下:

Java代码   收藏代码
  1. /* 
  2.  * description: 最大子段和问题 
  3.  * 问题描述:给定n个整数(可能为负数)组成的序列a1,a2,a3,...,an, 
  4.  * 求该序列子段和的最大值 
  5.  * 解法一:穷举法,选取其中一个最大的值 
  6.  *  
  7.  * auther: cm 
  8.  * date: 2010/11/19 
  9.  */  
  10. public class MaxSubSum  
  11. {  
  12.     //穷举法  
  13.     public static int maxSubSum1(int[] a)  
  14.     {  
  15.         int maxSum = 0;  
  16.   
  17.         for (int i = 0; i < a.length; i++)  
  18.         {  
  19.             for (int j = i; j < a.length; j++)  
  20.             {  
  21.                 int sum = 0;  
  22.                 for (int k = i; k <= j; k++)  
  23.                 {  
  24.                     sum += a[k];  
  25.                 }  
  26.                 if (sum > maxSum)  
  27.                 {  
  28.                     maxSum = sum;  
  29.                 }  
  30.             }  
  31.         }  
  32.         return maxSum;  
  33.     }  
  34.   
  35.     //穷举法改进,减少一层循环  
  36.     public static int maxSubSum2(int[] a)  
  37.     {  
  38.         int maxSum = 0;  
  39.           
  40.         for (int i = 0; i < a.length; i++)  
  41.         {  
  42.             int sum = 0;  
  43.   
  44.             for (int j = i; j < a.length; j++)  
  45.             {  
  46.                 sum += a[j];  
  47.                   
  48.                 if (maxSum < sum)  
  49.                 {  
  50.                     maxSum = sum;  
  51.                 }  
  52.             }  
  53.         }  
  54.   
  55.         return maxSum;  
  56.     }  
  57.     public static void main(String[] args)   
  58.     {  
  59.         int[] a = {-211, -413, -5, -2};  
  60.         //System.out.println(MaxSubSum.maxSubSum1(a));  
  61.         System.out.println(MaxSubSum.maxSubSum2(a));  
  62.     }  


最大子段和问题

 

解法二:分治法

算法分析:

最大子段和可能在三处出现。
1)整个出现在输入数据的左半部
2)整个出现在右半部。

3)跨越输入数据的中部从而位于左右两半部分之中
前两种情况可以递归求解,第三种情况的最大和可以通过求出前半部分的最大和(包含前半部分的最后一个元素)
以及后半部分的最大和(包含后半部分的第一个元素)而得到,前后两部分和相加。
三部分中的最大者,即为最大子段和.

例如:

前半部分        后半部分

4  -3  5  -2    -1  2  6  -2

其中前半部分的最大子段和为6(A1到A3),而后半部分的最大子段和为8(A6到A7);

前半部分包含其最后一个元素的最大和为4(A1到A4),后半部分包含其第一个元素的最大和为7(A5到A7),

则横跨这两部分的最大和为4+7=11;

故该序列的最大字段和为11

 

算法实现如下:

Java代码   收藏代码
  1. /* 
  2.  * description: 最大子段和 
  3.  * 分治策略 
  4.  * 最大子段和可能在三处出现。 
  5.  * 1)整个出现在输入数据的左半部 
  6.  * 2)整个出现在右半部。 
  7.  * 3)跨越输入数据的中部从而位于左右两半部分之中 
  8.  * 前两种情况可以递归求解,第三种情况的最大和可以通过求出前半部分的最大和(包含前半部分的最后一个元素) 
  9.  * 以及后半部分的最大和(包含后半部分的第一个元素)而得到,前后两部分和相加。 
  10.  * 三部分中的最大者,即为最大子段和. 
  11.  * auther: cm 
  12.  * date: 2010/11/20 
  13.  */  
  14. public class MaxSubSumRec   
  15. {  
  16.     private static int maxSumRec(int[] a, int left, int right)  
  17.     {  
  18.         if (left == right)  
  19.         {  
  20.             if (a[left] > 0)  
  21.             {  
  22.                 return a[left];  
  23.             }  
  24.             else  
  25.             {  
  26.                 return 0;  
  27.             }  
  28.         }  
  29.   
  30.         int center = (left + right) / 2;  
  31.         int maxLeftSum = maxSumRec(a, left, center);  
  32.         int maxRightSum = maxSumRec(a, center + 1, right);  
  33.   
  34.         int maxLeftBorderSum = 0;  
  35.         int leftBorderSum = 0;  
  36.         for (int i = center; i >= left; i--)  
  37.         {  
  38.             leftBorderSum += a[i];  
  39.             if (leftBorderSum > maxLeftBorderSum)  
  40.             {  
  41.                 maxLeftBorderSum = leftBorderSum;  
  42.             }  
  43.         }  
  44.   
  45.         int maxRightBorderSum = 0;  
  46.         int rightBorderSum = 0;  
  47.         for (int i = center + 1; i <= right; i++)  
  48.         {  
  49.             rightBorderSum += a[i];  
  50.             if (rightBorderSum > maxRightBorderSum)  
  51.             {  
  52.                 maxRightBorderSum = rightBorderSum;  
  53.             }  
  54.         }  
  55.   
  56.         return max(maxLeftSum, maxRightSum, maxRightBorderSum + maxLeftBorderSum);  
  57.     }  
  58.   
  59.     //入口程序  
  60.     public static int maxSubSum(int[] a)  
  61.     {  
  62.         return maxSumRec(a, 0, a.length - 1);  
  63.     }  
  64.   
  65.     //求三数中的最大值  
  66.     private static int max(int a, int b, int c)  
  67.     {  
  68.         int max = a > b ? a : b;  
  69.         max = c > max ? c : max;  
  70.   
  71.         return max;  
  72.     }  
  73.   
  74.     public static void main(String[] args)   
  75.     {  
  76.         int[] a = {-211, -413, -5, -2};  
  77.         System.out.println(MaxSubSumRec.maxSubSum(a));  
  78.     }  
  79. }  
 

最大子段和问题

解法三:

算法分析:


对于序列a,设j代表当前序列的终点,i代表当前序列的起点

分析:如果a[i]是负的,那么它不可能是最大子段的起点,因为任何包含a[i]为起点的子段都可以通过
         用a[i+1]为起点而得到改进。类似的,任何负的子段都不可能是最优子段的前缀(原理相同).
         如果在循环中检测到a[i]到a[j]的子段是负的,那么可以推进i.

结论:不仅能把i推进到i+1,而且可以一直推进到j+1.
原因:令p为i+1和j之间的任一下标。开始于下标p的任意子段都不大于从下标i开始并包含从a[i]到a[p-1]的子段,

        因为a[i]到a[p-1]这个子段不是负的(j是使得从下标i开始,其值成为负值序列的第一个下标)

 

算法实现:

Java代码   收藏代码
  1. public class MaxSum   
  2. {  
  3.     public static int maxSubSum(int[] a)  
  4.     {  
  5.         int maxSum = 0;  
  6.         int thisSum = 0;  
  7.   
  8.         for (int j = 0; j < a.length; j++)  
  9.         {  
  10.             thisSum += a[j];  
  11.             if (thisSum > maxSum)  
  12.             {  
  13.                 maxSum = thisSum;  
  14.             }  
  15.             else if (thisSum < 0)  
  16.             {  
  17.                 thisSum = 0;  
  18.             }  
  19.         }  
  20.   
  21.         return maxSum;  
  22.     } 
  23. public static int maxSubSum3(int[] a){
            int maxSum = 0;
            int thisSum = 0;//记录前一个最大是子段和C[i]=max{c[i-1]+a[i],a[i]}
            for(int i=0;i<a.length;i++){
                if(thisSum >0){
                    thisSum += a[i];
                }else{
                    thisSum = a[i];
                }
                if(thisSum > maxSum){
                    System.out.print(a[i] + " ");
                    maxSum = thisSum;
                }
            }
            return maxSum;
        }
  24.     public static void main(String[] args)   
  25.     {  
  26.         int[] a = {-211, -413, -5, -2};  
  27.         System.out.println(MaxSum.maxSubSum(ar));  
  28.     }  
  29. }  

算法的优点:

1)运行时间最短;

2)而且只对数据进行一次扫描,一旦a[i]被读入并被处理,它就不在需要被记忆;

3)在任何时刻,算法都能对已读入的数据给出该问题的正确答案;




  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值