分治法求最大子数组(java代码)

最大子数组

给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
示例:
[-20,17,8,-6,10,-12,-18,-30]
连续子数组 [17,8,-6,10]的和最大,为 29。
该题目就是《算法导论》分治的第一例题,可以在原书上找到答案。
最大子数组,则就是给定数组中和最大的一个子段
采用分治法即可
取给定数组的中间位 即可分成两个子数组
[-20,17,8,-6] 和 [10,-12,-18,-30] 两个子数组
则 这时候有三种情况
最大子数组完全在左边的数组中
最大子数组完全在右边的数组中
最大子数组横跨两个子数组
题目所给的最大子数组必然是上述三种情况的最大子数组的最大值
递归地去求解左右两个边的数组的最大子数组,这又是求最大子数组问题,只是规模更小。
Java代码

public class MaximumSubarray {
    /**
     * 定义一个内部类result方便输出,也方便debug查看结果
     */
    private static class Result{
        int low ;
        int high ;
        int sum ;

        public Result(int low, int high, int sum) {
            this.low = low;
            this.high = high;
            this.sum = sum ;
        }
    }

    /**
     * 主方法
     * @param args
     */
    public static void main(String[] args) {
        int array [] = {-20,17,8,-6,10,-12,-18,-30} ;
        Result result = findMaxSubarray(array, 0,  array.length-1);
        System.out.println("从"+result.low + "到" + result.high + "号,值为" + result.sum);    }

    /**
     * @param array
     * @param low
     * @param mid
     * @param high
     * @return result 返回一个result结果。就是跨过mid的子数组的值
     */
    static Result findMaxCrossingSubarray(int[] array, int low, int mid, int high) {
        // low为 数组的下界,mid为中点 high为上界
        int left_Sum = -1000 ; // 目前为止找到的最大和(左边数组 -1000是先给定的负无穷大值
        int sum = 0 ;   //左数组的和
        int max_left = 0 ;
        int max_right = 0 ;
        for (int i = mid ; i > low ; i--){ //从mid开始循环到上界
            sum += array[i];
            if (sum > left_Sum) {
                left_Sum = sum ;    //更新目前最大的和
                 max_left = i ;     //取得最大和处的索引
            }
        }
        int right_Sum = -1000 ; //目前为止找到的最大和(右边数组)
        sum = 0 ;       //右边数组的和 初始化一下 后面和前面基本一样
        for (int i = mid + 1; i < high; i++) {
            sum += array[i];
            if (sum > right_Sum) {
                right_Sum = sum ;
                 max_right = i ;
            }
        }
        return new Result(max_left, max_right, left_Sum + right_Sum);
        //返回左数组的最和  右数组最大和 左右数组之和
    }

    /**
     *
     * @param array
     * @param low
     * @param high
     * @return
     * 递归找到最大子数组
     */
    static Result findMaxSubarray(int [] array , int low , int high){
        //递归跳出条件
        if (low == high) {
            return new Result(low, high, array[low]);
        }else {
            int mid = (high+low)/2 ;
            Result left_Result = findMaxSubarray(array, low, mid);
            Result right_Result = findMaxSubarray(array, mid + 1, high);
            Result crossing_Result = findMaxCrossingSubarray(array, low, mid, high);
            //找出左右子数组与中间数组三者的最大值
            if (left_Result.sum >= right_Result.sum && right_Result.sum >= crossing_Result.sum) {
                return left_Result ;
            } else if (right_Result.sum >= left_Result.sum && right_Result.sum >= crossing_Result.sum){
                return right_Result ;
            }else {
                return crossing_Result ;
            }
        }
    }
}

类似一棵二叉树的结构
分治法求最大子数组(java代码)_第1张图片
勾是每次判断后得到的返回值
如果仅看findMaxCrossingSubarray方法的话迭代次数是(mid-low+1)+(high-mid)=high-low+1 为线性
整体时间复杂度为Θ(NlogN)

你可能感兴趣的:(分治法求最大子数组(java代码))