用分治和递归的思想——寻找最大子数组

  寻找最大子数组的问题可描述为

输入:

  一个数组,一个低位,一个高位

输出:

  数组中从低位到高位中,连续和最大是多少

  首先能想到的最直接的办法是暴力解决,遍历所有可能的序列组合,如果有n个元素,则需遍历的子序列有,复杂度为n2,稍有些经验的就能马上意识到,有很多重复计算在里面,比如最长的子序列计算,包含了之前所有子序列的计算。接下来我们使用分治的思想求解这个最大子序列,前一篇博文提过,分治的思想是将大问题分解为同等类型的子问题,再将子问题求解,然后合并子问题得出原问题的解,其中用到了递归的思想,因为分解得到子问题也可以再次分解为更小的子问题,上一篇博文用分治的思想解决排序问题,就是著名的归并排序,将排序的复杂度降低到nlgn。在这个问题中,我们可以将原数组分解为两个同等大小的子数组,分别求两个子数组中的最大子数组,在比较这两个子数组,最后得出原数组的最大子数组,但是有一点需要考虑到,就是子数组可能会穿过中心,左数组和右数组的一部分共同构成最大子数组,但这个处理显然不能当成原问题的同等类型问题处理,而且上一篇讲过,将不属于原问题的解放在第三步合并中处理就好,所以整个问题的解决就可以分为三个部分。

  1.分解。分解原数组为两个相等大小(也可以不等)的两个数组。

  2.解决。分别计算两个数组中最大的子数组,当分解到只有一个元素时,它本身即为最大子数组。

  3.合并。计算穿过中心的最大子数组,与左右最大子数组比较后返回最大值。

  下面给出代码:

 1 public class Main {
 2     public static void main(String args[]){
 3         int[] a = {1,-2,13,-14,2,4,5,4};
 4         int b = 0;
 5         b = FMS(a,0,7);
 6         while(true);
 7     }
 8   //寻找跨越中心的最大子数组
 9     public static int FMCS(int[] a,int low,int mid,int high){
10         int left_sum = 0,right_sum = 0;
11         int sum = 0;
12         int max_left = 0,max_right = 0;
13         for(int i = mid;i>=low;i--){
14             sum += a[i];
15             if(sum > left_sum){
16                 left_sum = sum;
17                 max_left = i;
18             }
19         }
20         sum = 0;
21         for(int j = mid + 1;j <= high;j++){
22             sum += a[j];
23             if(sum > right_sum){
24                 right_sum = sum;
25                 max_right = j;
26             }
27         }
28         return left_sum+right_sum;
29     }
30   //寻找最大子数组
31     public static int FMS(int[] a,int low,int high){
32         int left_sum = 0,right_sum = 0,cross_sum = 0;
33         int mid = 0;
34         if(low == high){
35             return a[low];
36         }
37         else{
38             mid = (low+high)/2;
39             left_sum = FMS(a,low,mid);   //这两句可能会迷惑一下,乍一看,并没有任何求左右数组的最大子数组的代码,但仍然求出来了
40             right_sum = FMS(a,mid+1,high); //用递归的思想去思考,其实FMS本身即定义为求单边最大子数组的函数,再次使用即可,并不需要额外代码。
41             cross_sum = FMCS(a,low,mid,high); //重点在第一个判断上,当递归“探底”时,返回一个值,这个值即为当前所求的最大子数组,就是当分解的足够小时。
42             if(left_sum >= right_sum      //和归并搜索一样,其实分治加递归是把一个复杂的问题,转换为一个更加简单的问题的一种方式,将求最大子数组转换为
43                 &&left_sum >= cross_sum){   //求只有--一个元素的最大子数组。归并排序是把排序一个大数组转换为排序只有一个元素的数组的问题。
44                 return left_sum;         //重点在每次“探底"时的动作,和合并的动作。 合并在这个算法中体现在寻找过中值的子数组和最后的判断。
45             }
46             else if(right_sum >= left_sum
47                 &&right_sum >= cross_sum){
48                 return right_sum;
49             }
50             else 
51                 return cross_sum;
52         }
53     }
54 }

 

posted @ 2016-05-09 19:39  Thkeer  阅读(331)  评论(0编辑  收藏  举报