• 最大子段和问题Java实现


    最大子段和问题

    一、问题描述
      给定长度为n的整数序列,a[1...n], 求[1,n]某个子区间[i , j]使得a[i]+…+a[j]和最大。

      例如(-2,11,-4,13,-5,2)的最大子段和为20,所求子区间为[2,4],子序列是(11,-4,13),最大子段和是20。

    二、解决方法

    1、穷举法(初级版):SimpleAlgorithmOfMaximumIntervalSumLow.java

      最容易理解,

      用三重循环,可遍历所有子区间,一个表示起始位置,一个表示终点位置,一个求当前子序列的和
     1 package cn.com.zfc.everyday.test;
     2 
     3 /** 5  * 
     6  * @title SimpleAlgorithmOfMaximumSegmentSumLow
     7  * @describe 最大子段和的简单算法的初级版
     8  * @author 张富昌
     9  * @date 2017年4月9日下午10:45:51
    10  */
    11 public class SimpleAlgorithmOfMaximumIntervalSumLow {
    12     // 最大子段和的首元素在原序列中的位置
    13     private static int startIndex;
    14     // 最大子段和的尾元素在原序列中的位置
    15     private static int endIndex;
    16 
    17     public static void main(String[] args) {
    18         // 原序列
    19         int[] array = { -2, 11, -4, 13, -5, -2 };
    20         System.out.println("原序列是:");
    21         // 输出原序列
    22         printSegment(0, array.length - 1, array);
    23         // 求最大子段和
    24         int maxSum = maxSum(array);
    25         System.out.println("最大字段和:" + maxSum);
    26         System.out.println("最大子段和的首元素在原序列中的位置:" + startIndex + ",最大子段和的尾元素在原序列中的位置:" + endIndex);
    27         System.out.println("最大字段和的子序列式:");
    28         // 输出最大子段和序列
    29         printSegment(startIndex, endIndex, array);
    30     }
    31 
    32     /**
    33      * 
    34      * @param array:原序列
    35      * @return:最大子段和
    36      */
    37     private static int maxSum(int[] array) {
    38         // 假设最大子段和为 0
    39         int maxSum = 0;
    40         // 双重 for 循环遍历所有的子序列
    41         for (int i = 0; i < array.length; i++) {
    42             for (int j = 0; j < array.length; j++) {
    43                 // 一个 for 循环求当前子序列的和
    44                 int currentSum = 0;
    45                 for (int k = i; k <= j; k++) {
    46                     currentSum += array[k];
    47                 }
    48                 if (currentSum > maxSum) {
    49                     maxSum = currentSum;
    50                     // 最大子段和的首元素在原序列中的位置
    51                     startIndex = i;
    52                     // 最大子段和的尾元素在原序列中的位置
    53                     endIndex = j;
    54                 }
    55             }
    56         }
    57         return maxSum;
    58     }
    59 
    60     /**
    61      * 输出序列
    62      * 
    63      * @param start:序列的开始下标
    64      * @param end:序列的结束下标
    65      * @param array:要输出的序列
    66      */
    67     private static void printSegment(int start, int end, int[] array) {
    68         for (int i = start; i <= end; i++) {
    69             System.out.print(array[i] + " ");
    70         }
    71         System.out.println();
    72     }
    73 
    74 }

    2、穷举法(升级版):SimpleAlgorithmOfMaximumIntervalSumHigh.java

     避免子序列的重复计算

     1 package cn.com.zfc.everyday.test;
     2 
     3 /**
     4  * 
     5  * @title SimpleAlgorithmOfMaximumIntervalSumHigh
     6  * @describe 最大子段和的简单算法的升级版
     7  * @author 张富昌
     8  * @date 2017年4月9日下午11:17:33
     9  */
    10 public class SimpleAlgorithmOfMaximumIntervalSumHigh {
    11     // 最大子段和的首元素在原序列中的位置
    12     private static int startIndex;
    13     // 最大子段和的尾元素在原序列中的位置
    14     private static int endIndex;
    15 
    16     public static void main(String[] args) {
    17         // 原序列
    18         int[] array = { -2, 11, -4, 13, -5, -2 };
    19         System.out.println("原序列是:");
    20         // 输出原序列
    21         printSegment(0, array.length - 1, array);
    22         // 求最大子段和
    23         int maxSum = maxSum(array);
    24         System.out.println("最大字段和:" + maxSum);
    25         System.out.println("最大子段和的首元素在原序列中的位置:" + startIndex + ",最大子段和的尾元素在原序列中的位置:" + endIndex);
    26         System.out.println("最大字段和的子序列式:");
    27         // 输出最大子段和序列
    28         printSegment(startIndex, endIndex, array);
    29     }
    30 
    31     /**
    32      * 
    33      * @param array:原序列
    34      * @return:最大子段和
    35      */
    36     private static int maxSum(int[] array) {
    37         // 假设最大子段和为 0
    38         int maxSum = 0;
    39         // 双重 for 循环遍历所有的子序列
    40         for (int i = 0; i < array.length; i++) {
    41             // 当前序列的和
    42             int currentSum = 0;
    43             // 注意 j 的初始值,避免重复计算
    44             for (int j = i; j < array.length; j++) {
    45                 currentSum += array[j];
    46                 if (currentSum > maxSum) {
    47                     maxSum = currentSum;
    48                     // 最大子段和的首元素在原序列中的位置
    49                     startIndex = i;
    50                     // 最大子段和的尾元素在原序列中的位置
    51                     endIndex = j;
    52                 }
    53             }
    54         }
    55         return maxSum;
    56     }
    57 
    58     /**
    59      * 输出序列
    60      * 
    61      * @param start:序列的开始下标
    62      * @param end:序列的结束下标
    63      * @param array:要输出的序列
    64      */
    65     private static void printSegment(int start, int end, int[] array) {
    66         for (int i = start; i <= end; i++) {
    67             System.out.print(array[i] + " ");
    68         }
    69         System.out.println();
    70     }
    71 
    72 }

    3、分治算法:BranchAlgorithmOfMaximumIntervalSum.java

     1 package cn.com.zfc.everyday.test;
     2 
     3 /**
     4  * 
     5  * @title BranchAlgorithmOfMaximumIntervalSum
     6  * @describe 最大子段和的分治算法: 
     7  *                 所有子区间[start, end]只可能有以下三种可能性: 
     8  *                     在[1,n/2]这个区域内 
     9  *                     在[n/2+1,n]这个区域内
    10  *                     起点位于[1,n/2],终点位于[n/2+1,n]内 
    11  *                 以上三种情形的最大者,即为所求.
    12  *           前两种情形符合子问题递归特性,所以递归可以求出. 对于第三种情形,则需要单独处理.
    13  *           第三种情形必然包括了n/2和n/2+1两个位置,这样就可以利用第二种穷举的思路求出:
    14  *           以n/2为终点,往左移动扩张,求出和最大的一个startSum,以n/2+1为起点,往右移动扩张,求出和最大的一个endSum
    15  *           startSum + endSum是第三种情况可能的最大值
    16  * @author 张富昌
    17  * @date 2017年4月9日下午10:46:32
    18  */
    19 public class BranchAlgorithmOfMaximumIntervalSum {
    20 
    21     public static void main(String[] args) {
    22         // 原序列
    23         int[] array = { -2, 11, -4, 13, -5, -2 };
    24         System.out.println("原序列是:");
    25         // 输出原序列
    26         printSegment(0, array.length - 1, array);
    27         // 求最大子段和
    28         int maxSum = maxSum(array);
    29         System.out.println("最大字段和:" + maxSum);
    30     }
    31 
    32     private static int maxSum(int[] array) {
    33         return maxSubSum(array, 1, array.length - 1);
    34     }
    35 
    36     private static int maxSubSum(int[] array, int start, int end) {
    37         int sum = 0;
    38         if (start == end) {
    39             sum = array[start] > 0 ? array[end] : 0;
    40         } else {
    41             int center = (start + end) / 2;
    42             // 以n/2为终点,往左移动扩张,求出和最大的一个startSum
    43             int startSum = maxSubSum(array, start, center);
    44             // 以n/2+1为起点,往右移动扩张,求出和最大的一个endSum
    45             int endSum = maxSubSum(array, center + 1, end);
    46             int s1 = 0;
    47             int starts = 0;
    48             // 以n/2为终点,往左移动扩张,求出和最大的一个 startSum
    49             for (int i = center; i >= start; i--) {
    50                 starts += array[i];
    51                 if (starts > s1) {
    52                     s1 = starts;
    53                 }
    54             }
    55             int s2 = 0;
    56             int ends = 0;
    57             // 以n/2+1为起点,往右移动扩张,求出和最大的一个 endSum
    58             for (int i = center + 1; i <= end; i++) {
    59                 ends += array[i];
    60                 if (ends > s2) {
    61                     s1 = ends;
    62                 }
    63             }
    64             sum = s1 + s2;
    65             if (sum < startSum) {
    66                 sum = startSum;
    67             }
    68             if (sum < endSum) {
    69                 sum = endSum;
    70             }
    71         }
    72         return sum;
    73     }
    74 
    75     /**
    76      * 输出序列
    77      * 
    78      * @param start:序列的开始下标
    79      * @param end:序列的结束下标
    80      * @param array:要输出的序列
    81      */
    82     private static void printSegment(int start, int end, int[] array) {
    83         for (int i = start; i <= end; i++) {
    84             System.out.print(array[i] + " ");
    85         }
    86         System.out.println();
    87     }
    88 
    89 }

    4、动态规划算法

     1 package cn.com.zfc.everyday.test;
     2 
     3 /**
     4  * 
     5  * @title DynamicProgrammingOfMaximumIntervalSum
     6  * @describe 最大子段和的动态规划算法
     7  * @author 张富昌
     8  * @date 2017年4月9日下午10:47:00
     9  */
    10 public class DynamicProgrammingOfMaximumIntervalSum {
    11     public static void main(String[] args) {
    12         // 原序列
    13         int[] array = { -2, 11, -4, 13, -5, -2 };
    14         System.out.println("原序列是:");
    15         // 输出原序列
    16         printSegment(0, array.length - 1, array);
    17         // 求最大子段和
    18         int maxSum = maxSum(array);
    19         System.out.println("最大字段和:" + maxSum);
    20     }
    21 
    22     /**
    23      * 
    24      * @param array:原序列
    25      * @return:最大子段和
    26      */
    27     private static int maxSum(int[] array) {
    28         int sum = 0;
    29         int b = 0;
    30         for (int i = 0; i < array.length; i++) {
    31             if (b > 0) {
    32                 b += array[i];
    33             } else {
    34                 b = array[i];
    35             }
    36             if (b > sum) {
    37                 sum = b;
    38             }
    39         }
    40         return sum;
    41     }
    42 
    43     /**
    44      * 输出序列
    45      * 
    46      * @param start:序列的开始下标
    47      * @param end:序列的结束下标
    48      * @param array:要输出的序列
    49      */
    50     private static void printSegment(int start, int end, int[] array) {
    51         for (int i = start; i <= end; i++) {
    52             System.out.print(array[i] + " ");
    53         }
    54         System.out.println();
    55     }
    56 }
  • 相关阅读:
    【剑指Offer】53、表示数值的字符串
    【剑指Offer】52、正则表达式匹配
    【剑指Offer】51、构建乘积数组
    【双系统】windows 和 Ubuntu 双系统安装
    【ubuntu子系统】使用windows自带的ubuntu子系统
    【剑指Offer】50、数组中重复的数字
    【剑指Offer】49、把字符串转换成整数
    【剑指Offer】48、不用加减乘除做加法
    【剑指Offer】47、求1+2+3+4+···+n
    【maven】成功生成jar包,提示找不到主类?
  • 原文地址:https://www.cnblogs.com/zfc-java/p/6687442.html
Copyright © 2020-2023  润新知