Algorithms(一)---最大子数组和问题

1.You are given an array containing both positive and negative integers and required to find the sub-array with the largest sum (O(N) a la KBL). Write a routine for the above.

题目描述:
输入一个整形数组,数组里有正数也有负数。
数组中连续的一个或多个整数组成一个子数组,每个子数组都有一个和。
求所有子数组的和的最大值。要求时间复杂度为O(n)。

思想:
设sum(i)为以第i个元素结尾且和为最大的连续子数组。假设对于元素i,所有以它前面的元素结尾的子数组的长度都已经求得,那么以第i个元素结尾且和最大的连续子数组实际上,要么是以第i-1个元素结尾且和最大的连续子数组加上这个元素,要么是只包含第i个元素,即sum[i] = max(sum(i-1) + a[i], a[i])。可以通过判断sum(i-1) + a[i]是否大于a[i]来做选择,而这实际上等价于判断sum(i-1)是否大于0。

    public static int getMax(int[] arr) {

        if(0 == arr.length || null == arr) {
            System.out.println("数组不合法");
            return Integer.MIN_VALUE;
        }

        int max = arr[0], sum = arr[0];

        for(int i=1; i<arr.length; i++) {
            if(sum > 0) {
                sum += arr[i];
            } else {
                sum = arr[i];
            }

            if(sum > max) {
                max = sum;
            }
        }

        return max;
    }

现在假设数组首尾相接,就是组成了一个环状连续数组,求最大子数组之和。
如果数组是首尾相邻的,可以分成两种情况来讨论:

(1) 如果最大和子数组没有跨越首尾,则可以用原问题的解法来求解

(2)如果最大和子数组跨越首尾,则可以继续分两种情况讨论:
如果数组元素全部为正,则所有元素之和就是最大值
如果数组中有负数,则最大和子数组必然不包括某些元素,而不被包括的元素中必然有最小和子数组,这样我们就可以先查找最小和子数组的最后一个点,并以下一个点为起点,利用(1)的方法循环遍历一遍数组,就可以得到最大和子数组了
这里写图片描述
取得最小值的索引:

   public static int getMinIndex(int[] arr) {

        if(0 == arr.length || null == arr) {
            System.out.println("数组不合法");
            return Integer.MIN_VALUE;
        }

        int min = arr[0], sum = arr[0], minIndex = 0, i;

        for(i=1; i<arr.length; i++) {
            if(sum < 0) {
                sum += arr[i];
            } else {
                sum = arr[i];
            }

            if(sum < min) {
                min = sum;
                minIndex = i;
            }
        }

        return minIndex;
    }

取环状数组最大值:

   public static int getMaxConnect(int[] arr) {

        int minIndex = getMinIndex(arr);
        if(Integer.MIN_VALUE == minIndex) {
            return minIndex;
        }

        int max = arr[(minIndex+1) % arr.length], sum = arr[(minIndex+1) % arr.length];
        for(int i=2; i<arr.length; i++) {
            if(sum > 0) {
                sum += arr[(minIndex+i) % arr.length];
            } else {
                sum = arr[(minIndex+i) % arr.length];
            }

            if(sum > max) {
                max = sum;
            }
        }
        return max;
    }

测试程序:
因为上面考虑的是跨过首尾的,如果没有跨过首尾我们就需要用最开始说的按照不是环的连续数组来求最大值,所以对于一个我们并不知道是否最大和子数组跨过首尾的时候我们需要采取两种方法来分别求最大值,最终取两个中较大的值即可。

    public static void main(String[] args) {
        int[] arr = {1,-4,8,-2,-3,12,4,6,3,-1,-2};

        int noConnect = getMaxSum(arr); //求非环行数组的最大值
        int connect = getMaxConnect(arr);   //求环形数组的最大值

        //输出较大的值
         System.out.println(noConnect > connect? noConnect : connect);
    }

完整代码:

public class MaxSubArr {
    public static void main(String[] args) {
        int[] arr = {1,-4,8,-2,-3,12,4,6,3,-1,-2};

        int noConnect = getMaxSum(arr); //求非环行数组的最大值
        int connect = getMaxConnect(arr);   //求环形数组的最大值

        //输出较大的值
         System.out.println(noConnect > connect? noConnect : connect);
    }

    public static int getMaxSum(int[] arr) {
        if(0 == arr.length || null == arr) {
            System.out.println("数组不合法");
            return Integer.MIN_VALUE;
        }
        int max = arr[0];
        int sum = arr[0] > 0 ? arr[0] : 0;

        for(int i=1; i<arr.length; i++) {
            if(sum + arr[i] > 0) {
                sum += arr[i];
                max = max > sum ? max : sum;
            } else {
                sum = 0;
                max = max > arr[i] ? max : arr[i];
            }
        }
        return max;
    }

    public static int getMax(int[] arr) {

        if(0 == arr.length || null == arr) {
            System.out.println("数组不合法");
            return Integer.MIN_VALUE;
        }

        int max = arr[0], sum = arr[0];

        for(int i=1; i<arr.length; i++) {
            if(sum > 0) {
                sum += arr[i];
            } else {
                sum = arr[i];
            }

            if(sum > max) {
                max = sum;
            }
        }

        return max;
    }

    public static int getMinIndex(int[] arr) {

        if(0 == arr.length || null == arr) {
            System.out.println("数组不合法");
            return Integer.MIN_VALUE;
        }

        int min = arr[0], sum = arr[0], minIndex = 0, i;

        for(i=1; i<arr.length; i++) {
            if(sum < 0) {
                sum += arr[i];
            } else {
                sum = arr[i];
            }

            if(sum < min) {
                min = sum;
                minIndex = i;
            }
        }

        return minIndex;
    }

    public static int getMaxConnect(int[] arr) {

        int minIndex = getMinIndex(arr);
        if(Integer.MIN_VALUE == minIndex) {
            return minIndex;
        }

        int max = arr[(minIndex+1) % arr.length], sum = arr[(minIndex+1) % arr.length];
        for(int i=2; i<arr.length; i++) {
            if(sum > 0) {
                sum += arr[(minIndex+i) % arr.length];
            } else {
                sum = arr[(minIndex+i) % arr.length];
            }

            if(sum > max) {
                max = sum;
            }
        }
        return max;
    }
}
发布了74 篇原创文章 · 获赞 3 · 访问量 6万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章