算法题:求数组中连续和最大的值

问题描述

求数组中连续和最大的值,例如数组:{1, 2, 3, 4, -5, 6, 7, 8 , 9 ,-10 ,-12}

/**
 * 求数组中连续和中最大的值
 */
public class Demo {
    
    
    public static void main(String[] args) {
    
    
        int[] arr = {
    
    1, 2, 3, 4, -5, 6, 7, 8 , 9 ,-10 ,-12};
    }
}    

方式一:

计算出所有可能的情况,留下最大值

实现思路:将数组拆分出所有可能的子列,使用三层循环的方式去求每一个子列中连续和的值,第一层循环确定子列起始的位置【i】,第二层确定子列终止的位置【j】,第三层循环就是计算子列【i ~ j】的和,如果当前子列和大于之前的子列和,则更新最大值。

    public static int maxSubseqSum1(int[] arr) {
    
    
        int n = arr.length;
        int maxSum = 0; // 定义最大值为0
        for (int i = 0; i < n; i++) {
    
     // i 是子列左端位置
            for (int j = i; j < n; j++) {
    
     // j 是子列右端位置
                int thisSum = 0; // thisSum 是从 arr[i] 到 arr[j] 的子列和
                for (int k = i; k <= j; k++) {
    
    
                    thisSum += arr[k];
                }
                if (thisSum > maxSum) {
    
     // 如果得到该子列和更大,则更新结果
                    maxSum = thisSum;
                }
            }
        }
        return maxSum;
    } 

这个算法的复杂度是 T(N)=O(N^3) ,因为有三层嵌套的 for 循环,如果每一层循环都是从 0~N ,那么乘起来就是 N 的三次方

方式二:

方式一使用三层嵌套循环求每一个子列的和,其实第三个 for 循环可以去除掉,这个循环做的事就是将 i ~ j 中间的数字求和,比如你第二个循环 j = 100,j++ 之后 j =101,那么你计算 i ~ j 之和的时候是将 i ~ 101 的数据重新求和方便还是在 i ~ 100 和 的基础上加上 arr[101] 的值方便点?对于相同的 i 不同的 j 只需要在前一项的基础上多加一个 arr[j] 的值就可以了。

实现思路:将 【i ~ j】求和的值放在第一个循环下,第二个循环求子列的和,如果得到该子列和更大,则更新结果。

    public static int maxSubseqSum2(int[] arr) {
    
    
        int n = arr.length;
        int maxSum = 0;
        for (int i = 0; i < n; i++) {
    
     // i 是子列左端位置
            int thisSum = 0; // thisSum 是从 arr[i] 到 arr[j] 的子列和
            for (int j = i; j < n; j++) {
    
     // j 是子列右端位置
                thisSum += arr[j];
                // 对于相同的 i,不同的 j,只要 j-1 次循环的基础上累加 1 项即可
                if (thisSum > maxSum) {
    
    // 如果得到该子列和更大,则更新结果
                    maxSum = thisSum;
                }
            }
        }
        return maxSum;
    }

这个算法的复杂度是 T(N)=O(N^2),假如 N =100000 时,该算法就已经比算法一效率高了十万倍。

方式三:

采用分而治之(二分法)的思想去求解。

分而治之:分而治之的思想可以用于解决很多问题,大概的思路就是把一个比较大的复杂的问题切分成小的块,然后分头去解决他们,最后再把结果合并起来,就是“分而治之”。

实现思路:这个算法第一步就是先“分”,也就是把这个数组从中间一分为二,然后递归地去解决左右两边的问题,递归的去解决左边的问题,我们会得到左边的一个最大子列和,反之亦然,跨越边界的最大子列和。

    public static int maxSubseqSum3(int[] arr) {
    
    
        return divideAndConquer(arr,0,arr.length-1);
    }

    /**
     * 返回 3 个整数中的最大值
     */
    public static int max(int a,int b,int c) {
    
    
        return a>b?(Math.max(a, c)):(Math.max(b, c));
    }

    /**
     * 分治法求 List[left] 到 List[right] 的最大子列和
     */
    public static int divideAndConquer(int[] arr,int left, int right) {
    
    
        int maxLeftSum,maxRightSum; //存放左右子问题的解
        int maxLeftBorderSum,maxRightBorderSum; //存放跨分界线的结果
        int leftBorderSum,rightBorderSum;
        int center,i;
        // 递归的终止条件,子列只有1个数字
        if (left==right) {
    
    
            //如果大于0则返回 arr[left] 否则返回 0
            return Math.max(arr[left], 0);
        }

        //下面是 “分” 的过程
        center = (left+right)/2;
        //找到中分点
        //递归求得两边子列的最大和
        maxLeftSum = divideAndConquer(arr,left,center);
        maxRightSum = divideAndConquer(arr,center+1,right);
        //下面求跨分界线的最大和
        maxLeftBorderSum = 0;
        leftBorderSum = 0;
        for (i=center;i>=left;i--) {
    
     //从中线向左扫描
            leftBorderSum += arr[i];
            if (leftBorderSum>maxLeftBorderSum) {
    
    
                maxLeftBorderSum = leftBorderSum;
            }
        }

        maxRightBorderSum = 0;
        rightBorderSum = 0;
        for (i=center+1;i<=right;i++) {
    
    //从中线向右边扫描
            rightBorderSum += arr[i];
            if (rightBorderSum>maxRightBorderSum) {
    
    
                maxRightBorderSum = rightBorderSum;
            }
        }

        //获取治的结果
        return max(maxLeftSum,maxRightSum,maxLeftBorderSum+maxRightBorderSum);
    }

这个算法的复杂度是 T(N)=O(NlogN)

方式四:

在线处理,“在线” 的意思是指每输入一个数据就进行及时处理,在任何一个地方终止输入,算法都能正确给出当前的解。

实现思路:一个集合中的元素累加,如果当前子列和为负,则不可能使后面的部分和增大,抛弃之

    public static int maxSubseqSum4(int[] arr) {
    
    
        int n = arr.length;
        int maxSum = 0;
        int thisSum = 0;
        for (int i = 0; i < n; i++) {
    
     // i 是子列左端位置
            thisSum += arr[i]; // 向右累加
            if ( thisSum > maxSum) {
    
    
                maxSum = thisSum; // 发现更大的结果则更新当前结果
            } else if (thisSum < 0 ) {
    
     // 如果当前子列和为负
                thisSum = 0; // 则不可能使后面的部分和增大,抛弃之
            }
        }
        return maxSum;
    }

这个算法的复杂度是 T(N)=O(N)

おすすめ

転載: blog.csdn.net/xhmico/article/details/125206345