[leetcode]分治法求解最大子序列问题——Java实现

</pre>问题描述:</h1><p></p><p style="margin-top:0px; margin-bottom:10px; color:rgb(51,51,51); font-family:'Helvetica Neue',Helvetica,Arial,sans-serif; font-size:14px; line-height:30px">Find the contiguous subarray within an array (containing at least one number) which has the largest sum.</p><p style="margin-top:0px; margin-bottom:10px; color:rgb(51,51,51); font-family:'Helvetica Neue',Helvetica,Arial,sans-serif; font-size:14px; line-height:30px">For example, given the array <code style="font-family:Monaco,Menlo,Consolas,'Courier New',monospace; font-size:13px; padding:2px 4px; color:rgb(199,37,78); white-space:nowrap; background-color:rgb(249,242,244)">[−2,1,−3,4,−1,2,1,−5,4]</code>,<br style="" />the contiguous subarray <code style="font-family:Monaco,Menlo,Consolas,'Courier New',monospace; font-size:13px; padding:2px 4px; color:rgb(199,37,78); white-space:nowrap; background-color:rgb(249,242,244)">[4,−1,2,1]</code> has the largest sum = <code style="font-family:Monaco,Menlo,Consolas,'Courier New',monospace; font-size:13px; padding:2px 4px; color:rgb(199,37,78); white-space:nowrap; background-color:rgb(249,242,244)">6</code>.</p><span style="font-family:SimHei; font-size:18px">本例一般有两种解法,第一种被称为Kadane's Algorithm,使用O(n)时间。第二种是利用分支策略(Divide-Conquer),时间复杂度为O(nlogn)。主要描述第二种方法Java实现,思路来自算法导论第四章分治策略。</span><p><span style="font-family:SimHei; font-size:18px">分治主要分为三个步骤:</span></p><p><span style="font-family:SimHei; font-size:18px">分解(Divide):将问题划分为一些子问题,其形式与原问题相同,只是规模更小。</span></p><p><span style="font-family:SimHei; font-size:18px">解决(Conquer):递归求解子问题。如果子问题规模足够小,则停止递归,直接求解。</span></p><p><span style="font-family:SimHei; font-size:18px">合并(Combine):将子问题的解合并成原问题的解。</span></p><p><span style="font-family:SimHei; font-size:18px"></span></p><p><span style="font-family:SimHei; font-size:18px">分析本问题,一个数组可以看做由其左右子数组构成,而其最大子序列必定为以下三种情况之一:</span></p><p><span style="font-family:SimHei; font-size:18px">1. 在其左子数组中</span></p><p><span style="font-family:SimHei; font-size:18px">2. 在其右子数组中</span></p><p><span style="font-family:SimHei; font-size:18px">3. 横跨左右子数组</span></p><p><span style="font-family:SimHei; font-size:18px"></span></p><p><span style="font-family:SimHei; font-size:18px">因此,根据此分类可到的分治法主程序如下:</span></p><p><span style="font-family:SimHei; font-size:18px"></span></p><pre name="code" class="java">private int[] findMaxSubarray(int[] A, int low, int high) {
        int[] results = new int[3];//初始化返回数组results,包含最大子数组的low,high和sum
        int[] resultsleft, resultsright, resultscross;
        int mid;
        if (low == high) {
            //基本情况(base case):只有单个元素
            results[0] = low;
            results[1] = high;
            results[2] = A[low];
            return results;
        } else {
            //递归情况(recursive case)
            mid = (low+high) / 2;
            resultsleft = findMaxSubarray(A, low, mid);//递归求解左子序列
            resultsright = findMaxSubarray(A, mid+1, high);//递归求解右子序列
            resultscross = findMaxCrossSubarray(A, low, mid, high);//求解横跨左右子序列的情况
            //比较三种结果,取大者返回
            if (resultsleft[2] >= resultsright[2] && resultsleft[2] >= resultscross[2]) return resultsleft;
            else if (resultsright[2] >= resultsleft[2] && resultsright[2] >= resultscross[2]) return resultsright;
            else return resultscross;
        }
        
    }

求解很跨左右子序列情况的方法如下:

private int[] findMaxCrossSubarray(int[] A, int low, int mid, int high) {
        int[] results = new int[3];
        double leftsum = -1.0 / 0.0;//初始化左部分最大数组和为负无穷
        double rightsum = -1.0 / 0.0;//初始化右部分最大数组和为负无穷
        double sum = 0;
        int maxleft = mid;
        int maxright = mid + 1;
        for (int i = mid; i >= low; i--) {
            //从mid到low计算连续子数组和sum
            sum = sum + A[i];
            if (sum > leftsum) {
                //如果sum比左部分最大数组和大,则更leftsum为sum并记录下标为maxleft
                leftsum = sum;
                maxleft = i;
            }
        }
        
        sum = 0;
        for (int i = mid+1; i <= high; i++) {
            //右子数组处理与左子数组相同
            sum = sum + A[i];
            if (sum > rightsum) {
                rightsum = sum;
                maxright = i;
            }
        }
        results[0] = maxleft;
        results[1] = maxright;
        results[2] = (int)(leftsum + rightsum);
        return results;
    }

引导程序如下:

public int maxSubArray(int[] A) {
        int[] results;
        results = findMaxSubarray(A, 0, A.length - 1);
        return results[2];
    }


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值