30.连续子数组的最大和

来源:互联网 发布:htpc专用软件 编辑:程序博客网 时间:2024/06/05 21:44

一、题目描述
HZ偶尔会拿些专业问题来忽悠那些非计算机专业的同学。今天测试组开完会后,他又发话了:在古老的一维模式识别中,常常需要计算连续子向量的最大和,当向量全为正数的时候,问题很好解决。但是,如果向量中包含负数,是否应该包含某个负数,并期望旁边的正数会弥补它呢?例如:{6,-3,-2,7,-15,1,2,2},连续子向量的最大和为8(从第0个开始,到第3个为止)。你会不会被他忽悠住?(子向量的长度至少是1)

二、思路
首先,我们会想到用两个数来辅助存储当前的最大和,定义两个变量res和sum,res用来记录当前的最大和,sum用来记录当前的和,如果sum大于res,则更新res。这一步想好之后,接下来就是遍历数组,我们最容易想到的就是使用双层循环来遍历数组,以便找到从任意位置开始的连续子数组的最大和,但是,如果仔细想想,其实,我们只对数组遍历一次,也是能够找到连续子数组的最大和的,下面是两种方法的代码实现。
方法一:使用双层循环,时间复杂度O(n^2)

public class Solution {    public int FindGreatestSumOfSubArray(int[] array) {        int res = array[0];        for (int i = 0; i < array.length; i++){            int sum = 0;            for (int j = i; j < array.length; j++){                sum += array[j];                if (sum > res)                    res = sum;            }        }        return res;    }}

方法二:单层循环,在方法一中,如果数组第i个元素为负数,那么我们其实是没有必要进行i元素以后的遍历的,因为,负数总会让和变小,既然要变小,我们为什么还要这一次遍历呢?所以就有下面的改进方法。

/***改进版,遍历一次数组即可,时间复杂度O(n)。*用两个变量来进行记录,一个用于加和,一个用于记录最大和。**/public class Solution {    public int FindGreatestSumOfSubArray(int[] array) {        if (array.length == 0 || array == null)            return 0;        int res = array[0];        int sum = 0;        for (int i = 0; i < array.length; i++){            if (sum < 0){                sum = array[i];//因为加一个负数肯定会使结果小于array[i],所以直接舍去负数            } else {                 sum += array[i];            }            if (sum > res)                res = sum;        }        return res;    }}

该题目的变形:求使出现最大和的子数组;
思路:用两个标签分别记录子数组开始和结束的位置,如果使用for双层循环的话,当出现新的最大值时,分别记录i和j的值,下面是另一种循环求解:

/*     * 1.找出出现最大连续和的子数组     * 当数组有正有负时,i=0遍历数组,从大于0的那个元素开始,记录此时的下标为shart(最大子数组起始下标),从start开始遍历剩下的元素,若元素和num大于max的值则更新max,     * 且将此时的下标赋值给end(最大子数组终止下标),当num小于0则说明后面出现的(如果出现)最大子数组不可能包含这些元素,所以退出内层循环,继续外层循环,找下一个大于     * 0的数组元素,且外层循环的i变量此时变为temp+1,继续下面的循环。注意内层循环当temp>=length时,说明内层循环已经把所有数组元素都遍历结束,所以外层循环可以直接break。     */    public static ArrayList<Integer> findGreatestSumArrayOfSubArray(int arr[]) {        ArrayList<Integer> list = new ArrayList<Integer>();        int i = 0;        int temp = 0;        int num = 0;        int max = 0;        int start = 0;        int end = 0;        int flag = 0;//用来判断数组中是否全为负,如果flag = arr.length,则全为负数        while (i < arr.length){ //第一层循环,从大于零的元素位置开始,并记录开始下标,            if (arr[i] > 0){                temp = i;                start = i;                while (temp < arr.length){ //第二层循环,用temp做临时变量,找最大值,并记录最大值结束下标                    num += arr[temp];                    if (num < 0){                        num = 0;                        i = temp + 1;                        break;                    }                    if (num > max){                        max = num;                        end = temp;                    }                    temp++;                }                if(temp >= arr.length) //避免在外层循环中陷入死循环,因为此时i不一定等于数组长度,i值不增,外层循环则停不下来。                    break;            }            else {                i++;                flag++;            }        }        if (flag == arr.length) //如果元素全为负,则输出空数组            return list;        else {            for (int j = start; j<= end; j++)                list.add(arr[j]);        }        return list;    }
0 0
原创粉丝点击