最大连续子序列和(java)

最大连续子序列和

最大连续子序列和是一个常见的面试题,也是算法中经典的一个问题。作为总结,今天就从最简单的暴力求解,写道目前能达到最好的线性时间复杂度的算法。

问题描述:找出一个整型数组中的最大连续子序列的和
测试用例:int[] a = { 1, -3, 7, 8, -4, 12, -10, 6 };
输出:23

//即最大连续子序列和是 7 + 8 - 4+ 12 = 23

1、O(n^3)的暴力求解

/**
     * 求解思路:暴力枚举所有的可能性,得出最后的结果
     *  时间复杂度为O(n^3) 
     * 相当糟糕的一种解题思路,只能用于参考,没有实用价值
     */
    public static int maxSubSum1(int[] a) {
        int maxSum = 0;

        for (int i = 0; i < a.length; i++) {
            for (int j = i; j < a.length; j++) {
                int tempSum = 0;

                for (int k = i; k <= j; k++) {
                    tempSum += a[k];
                }

                if (tempSum > maxSum) {
                    maxSum = tempSum;
                }

            }
        }
        return maxSum;
    }

2、O(n^2)的暴力求解改进算法

/**
     * 求解思路:稍稍看一眼暴力求解的思路没,就会发现,用k去逐个标记其实是一个多余的做法
     * 所以,在此进行修改代码,减少一个for循环
     * 时间复杂度:O(n^2)
     * 这个算法比暴力求解稍微好了点儿,但是依然效率糟糕,没有实用价值
     */
    public static int maxSubSum2(int[] a) {
        int maxSum = 0;

        for (int i = 0; i < a.length; i++) {
            int tempSum = 0;

            for (int j = i; j < a.length; j++) {
                tempSum += a[j];

                if (tempSum > maxSum)
                    maxSum = tempSum;
            }
        }
        return maxSum;
    }

3、O(NlogN)的分治策略算法

/**
     * 解题思路:在一个数组中要找到最大连续子序列和,这个和要么出现在左半部分,要么出先在右半部分
     *          要么出现在横跨两部分之间
     * 时间复杂度:O(NlogN)
     * 这个算法就有一定的实用价值,虽然在效率上还是逊色于线性复杂度的算法
     */

    public static int maxSubSum3(int[] a) {
        return subSum3(a, 0, a.length - 1);
    }

    private static int subSum3(int[] a, int left, int right) {
        if (left == right)
            if (a[left] > 0)
                return a[left];
            else
                return 0;

        int center = (left + right) / 2;
        int maxLeftSum = subSum3(a, left, center);
        int maxRightSum = subSum3(a, center + 1, right);

        int maxLeftBorderSum = 0, leftBorderSum = 0;
        for (int i = center; i >= left; i--) {
            leftBorderSum += a[i];
            if (leftBorderSum > maxLeftBorderSum)
                maxLeftBorderSum = leftBorderSum;
        }

        int maxRightBorderSum = 0, rightBorderSum = 0;
        for (int i = center + 1; i <= right; i++) {
            rightBorderSum += a[i];
            if (rightBorderSum > maxRightBorderSum)
                maxRightBorderSum = rightBorderSum;
        }

        return Math.max(Math.max(maxLeftSum, maxRightSum), maxLeftBorderSum + maxRightBorderSum);

    }

4、O(N)的优化算法

/**
     * 求解思路:在算法一和算法二中,我们一直在用两个变量来标识遍历数组
     *  j代表当前序列的重点,i代表当前序列的起点
     *  如果我们只是单纯的想知道最大连续子序列的和,而不想知道最佳连续子序列的起点和终点的话
     *  那么这个i是完全可以被优化掉的
     * 时间复杂度:O(N)
     * 这个算法就是我们经常采用的算法之一,但是有遗憾的是没办法标识最佳连续子序列的位置
     */
    public static int maxSubSum4(int[] a) {
        int maxSum = 0;
        int tempSum = 0;

        for (int i = 0; i < a.length; i++) {
            tempSum += a[i];

            if (tempSum > maxSum)
                maxSum = tempSum;
            else if (tempSum < 0)
                tempSum = 0;
        }
        return maxSum;
    }

5、O(N)的动态规划算法

/**
     * 求解思路:用sum(j)表示a1到aj的和,很容易求出动态规划的递归式:
     *  sum(j) = max(sum(j-1)+aj , aj)
     * 时间复杂度:O(N)
     * 动态规划的好处在于,能很清楚的返回最佳连续子序列和的起始位置和终点位置
     *
     */
    public static int maxSubSum5(int[] a) {
        int maxSum = 0;
        int tempSum = 0;
        int begin = 0;

        for (int i = 0; i < a.length; i++) {
            if (tempSum > 0)
                tempSum += a[i];
            else {
                tempSum = a[i];
                begin = i;  //标记
            }

            if (tempSum > maxSum) {
                maxSum = tempSum;
                //可以在这里获取最佳连续子序列和的起点位置begin和重点位置i
            }

        }
        return maxSum;
    }

测试

源代码

public class Main {

    public static int maxSubSum1(int[] a) {
        int maxSum = 0;
        for (int i = 0; i < a.length; i++) {
            for (int j = i; j < a.length; j++) {
                int tempSum = 0;

                for (int k = i; k <= j; k++) {
                    tempSum += a[k];
                }

                if (tempSum > maxSum) {
                    maxSum = tempSum;
                }

            }
        }
        return maxSum;
    }

    public static int maxSubSum2(int[] a) {
        int maxSum = 0;

        for (int i = 0; i < a.length; i++) {
            int tempSum = 0;

            for (int j = i; j < a.length; j++) {
                tempSum += a[j];

                if (tempSum > maxSum)
                    maxSum = tempSum;
            }
        }
        return maxSum;
    }

    public static int maxSubSum3(int[] a) {
        return subSum3(a, 0, a.length - 1);
    }

    private static int subSum3(int[] a, int left, int right) {
        if (left == right)
            if (a[left] > 0)
                return a[left];
            else
                return 0;

        int center = (left + right) / 2;
        int maxLeftSum = subSum3(a, left, center);
        int maxRightSum = subSum3(a, center + 1, right);

        int maxLeftBorderSum = 0, leftBorderSum = 0;
        for (int i = center; i >= left; i--) {
            leftBorderSum += a[i];
            if (leftBorderSum > maxLeftBorderSum)
                maxLeftBorderSum = leftBorderSum;
        }

        int maxRightBorderSum = 0, rightBorderSum = 0;
        for (int i = center + 1; i <= right; i++) {
            rightBorderSum += a[i];
            if (rightBorderSum > maxRightBorderSum)
                maxRightBorderSum = rightBorderSum;
        }

        return Math.max(Math.max(maxLeftSum, maxRightSum), maxLeftBorderSum + maxRightBorderSum);

    }

    public static int maxSubSum4(int[] a) {
        int maxSum = 0;
        int tempSum = 0;

        for (int i = 0; i < a.length; i++) {
            tempSum += a[i];

            if (tempSum > maxSum)
                maxSum = tempSum;
            else if (tempSum < 0)
                tempSum = 0;
        }
        return maxSum;
    }

    public static int maxSubSum5(int[] a) {
        int maxSum = 0;
        int tempSum = 0;
        int begin = 0;

        for (int i = 0; i < a.length; i++) {
            if (tempSum > 0)
                tempSum += a[i];
            else {
                tempSum = a[i];
                begin = i;  //标记
            }

            if (tempSum > maxSum) {
                maxSum = tempSum;
                //可以在这里获取最佳连续子序列和的起点位置begin和重点位置i
            }

        }
        return maxSum;
    }

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        int[] a = { 1, -3, 7, 8, -4, 12, -10, 6 };
        System.out.println("第一种暴力方法:" + maxSubSum1(a));
        System.out.println("第二种暴力改进方法:" + maxSubSum2(a));
        System.out.println("第三种分治策略方法:" + maxSubSum3(a));
        System.out.println("第四种线性方法:" + maxSubSum4(a));
        System.out.println("第五种动态规划方法:" + maxSubSum5(a));
    }

}

结果:
这里写图片描述

  • 8
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值