算法_02

来源:互联网 发布:淘宝网显示不了图片 编辑:程序博客网 时间:2024/06/07 00:16
  1. 最大连续子数组
    给定一个数组A[0,1,….,n1],求A的连续子数组,使得该子数组的和最大
    例如:
    数组:1,-2,3,10,-4,7,2,-5
package day7_20;/** * Created by 朱博文 on 2017/7/20. * 最大连续子数组 * 给定一个数组A[0,1,....,n1],求A的连续子数组,使得该子数组的和最大 * 例如: * 数组:1,-2,3,10,-4,7,2,-5 */public class Demo_01 {    //暴力法    /**     * 直接求解A[i,...j]的值;     * 0<=i<n     * i<=j<n     * i,i+1,...,j-1,j的最大长度为n     * 因此:时间复杂度O(n^3)     */    public static int MaxSubArray(int[] A, int n) {        int maxSum = A[0];        int currSum = 0;        for (int i = 0; i < n; i++) {            for (int j = i; j < n; j++) {                for (int k = i; k < j; k++) {                    currSum += A[k];                    if (currSum > maxSum) {                        maxSum = currSum;                    }                }            }        }        return maxSum;    }    //分治法    /**     * 把数组从中间分开,那么最大子数组要么完全在左半边,要么完全在右半边数组,要么跨立在分界点上。     * 完全在左数组,右数组递归解决     * 跨立在分界点上:实际上是做数组的最大后缀和有数组的最大前缀的和。因为,从分界点向前扫,向后扫即可。     * 算法的递推关系:T(n) = 2*T(n/2)+c*n,c为常数     * 若n=2^k,则有:T(n):T(n)  = 2*T(n/2)+c*n     * = 2*(2*T(n/4)+c*n/2)+c*n = 4T(n/4)+2c*n     * = 4*(2*T(n/8)+c*n/4)+2c*n = 8T(n/8)+3c*n     * = 4*(2*T(n/16)+c*n/8)+3c*n = 16T(n/16)+4c*n     * = .....     * =2^k*T(1)+kc*n=an+c*nlog2n     * 若2^k<n<2^(k+1),则T(2^k) < T(n) <T(2^(k+1))     * 所以得:T(n) = O(nlogn)     **/    public static int MaxAddSub(int[] a, int from, int to) {        if (from == to) {  //递归出口            return a[from];        }        int middle = (from + to) / 2;        int m1 = MaxAddSub(a, from, middle);            //求一下前一段部分最大的子数组        int m2 = MaxAddSub(a, middle + 1, to);   //求一下后一段部分最大的子数组        int i, left = a[middle], now = a[middle];        for (i = middle - 1; i >= from; --i) {         //数组左半部分的最大后缀和            now += a[i];            left = Math.max(now, left);        }        int right = a[middle + 1];        now = a[middle - 1];        for (i = middle + 2; i <= to; ++i) {            //数组右半部分的最大前缀和            now += a[i];            right = Math.max(now, right);        }        int m3 = left + right;                          //中间部分的数组的最大值        return Math.max(Math.max(m1, m2), m3);          //比较是哪一个值大    }    //分析法(逻辑推理的算法应用)    /**     * 前缀和p[i] = a[0] + a[1] .... +a[i]     * s[i,j] = p[j] - p[i-1](定义p[-1] = 0)     * 算法过程     * 1.求i前缀p[i]     * 遍历i:0<=i<=n-1     * p[i] = p[i-1]+a[i]     * 2.计算p[i]-p[j]     * 遍历i: 0<=i<=n-1,求最小值m     * m的初值取0(p[-1]=0),然后遍历p[0...i-1],更新m     * p[i]-m即为以a[i]结尾的数组中最大的子数组     * 3.在第2步中,可顺手记录p[i]-m的最大值。     * 为什么     * 1,2步都是线性的,时间复杂度O(n)。     */    //动态规划法    /**     * 进一步分析     * 记S[i]为以A[i]结尾的数组中最大的子数组     * 则:S[i+1] = max(S[i]+A[i+1],A[i+1])     * S[0]=A[0]     * 遍历i:0<=i<=n-1     * 动态规划:最优子问题     * 时间复杂度:O(n)     */    public static int MaxMath(int[] a) {        int result = a[0];        int sum = a[0];        for (int i = 0; i < a.length; i++) {            if (sum > 0) {                sum += a[i];            } else {                sum = a[i];            }            if (sum > result) {                result = sum;            }        }        return result;    }    //在线法    /**     * 理解这个算法的关键是:使用thisSum来计算当前连续子数组的和     * 如果thisSum小于0,那么无论后面再加上什么数字都只会让子数组变小     * 所以抛弃当前子数组,重新开始计算子数组的值     */    public static int MaxOnline(int[] a) {        int sum = 0;//记录子数组的和        int thisSum = 0;        for (int i = 0; i < a.length; i++) {            thisSum += a[i];            if (thisSum > sum) {                sum = thisSum;            } else if (thisSum < 0) {                thisSum = 0;            }        }        return sum;    }}