连续数组的最大和

一维数组的连续子数组的最大和
题目:输入一个整型数组,数组里有正数也有负数。数组中一个或连续的多个整数组成一个子数组。求所有子数组的和的最大值。要求时间负责度为O(n)。
假如输入数组为{1,-2,3,10,-4,7,2,-5},我们尝试从头到尾累加其中的正数,初始化和为0,第一步加上1,此时和为1,第二步加上-2,此时和为-1,第三步加上3,此时我们发现-1+3=2,最大和2反而比3一个单独的整数小,这是因为3加上了一个负数,发现这个规律以后我们就重新作出累加条件:如果当前和为负数,那么就放弃前面的累加和,从数组中的下一个数再开始计数。
剑指offer和编程之美都有此题。参考这里:剑指offer(14)-最大子向量和 九度1372。算法可以优化为下面的几行代码:
1    static int MaxSum(int arr[], int n){    
2        int currentSum = arr[0];    
3        int ans = currentSum;    
4        for(int i=1; i<n; i++){    
5            currentSum = Math.max(currentSum+arr[i], arr[i]);    
6            ans = Math.max(ans, currentSum);    
7        }    
8        return ans;    
9    }    
二维数组的连续子数组的最大和
二维数组的连续子数组即为一个矩阵,如下图所示
设矩阵的坐上顶点为A(i,j), 右下顶点为 B(endi, endj). 在图中即为 A(1,1), B(3,3)。我们可以用Rect(A,B)即 (1,1,3,3,)来表示矩形区域。
1. 方法1:直接计算
我们可以遍历所有的矩形区域,找出其中的最大值,其中遍历的话复杂度为O(M^2 * N^2),假设二维数组为M行N列。
其中怎么计算子矩阵的和呢?通过预处理,我们可以再O(1)的时间内算出。具体看下面的代码:
arrSum函数即做预处理,得到数组p,p[i][j]表示已Rect(0, 0, i-1, j-1)矩形区域的总和。有了p数组就可以直接计算出任意矩形的总和。
01    public static int[][] arrSum(int arr[][]){    
02        int m = arr.length;    
03        int n = arr[0].length;    
04        int p[][] = new int[m+1][n+1];    
05        p[0][0] = arr[0][0];    
06        for(int i=0; i<=m; i++) p[i][0] = 0;    
07        for(int i=0; i<=n; i++) p[0][i] = 0;    
08        for(int i=1; i<=m; i++){    
09            for(int j=1; j<=n; j++){    
10                p[i][j] = p[i-1][j] + p[i][j-1] + arr[i-1][j-1] - p[i-1][j-1];    
11            }    
12        }    
13        return p;    
14    }    
15    //遍历所有二维数组的矩形区域    
16    static int  maxArrSum(int arr[][]){    
17        int m = arr.length;    
18        int n = arr[0].length;    
19        int p[][] = arrSum(arr);    
20        int ans = Integer.MIN_VALUE;    
21        for(int i=1; i<=m; i++){    
22            for(int j=1; j<=n; j++){    
23                for(int endi=i; endi <=m; endi++){    
24                    for(int endj=j; endj<=n; endj++){    
25                        int sum = p[endi][endj] - p[i-1][endj] - p[endi][j-1] + p[i-1][j-1];    
26                        if(ans < sum) ans = sum;    
27                    }    
28                }    
29            }    
30        }    
31        return ans;    
32    }    
2. 方法二 转化为一维数组计算
这里并不是把整个二维数组转化为一维的,而是说把部分连续的行合并,看成是一行计算。这样枚举所有连续的行,把这些连续的行看成是一个整体,把一列看成是一个数字,问题就转化为上面的一维数组的算法了。还可以采用上面的预处理方法,在O(1)的时间内计算出任意一列的和。代码如下,colSum函数即为预处理函数,我们还可以根据M,N的大小做些优化。算法的复杂度为 O(N * M * min(M,N) ).
01    //求一维数组的最大连续和    
02    static int maxSum(int p[][], int startLine,int endLine,int n){    
03        int ans = p[endLine][1] - p[startLine-1][1]; //即第一个列(startLine行到endLine行)的和    
04        int cmax = ans;    
05        for(int i=2; i<=n; i++){    
06            int ci = p[endLine][i] - p[startLine-1][i];    
07            cmax = Math.max(cmax+ci, ci);    
08            ans = Math.max(cmax, ans);    
09        }    
10        //System.out.println(startLine + " " + endLine + " " +ans);    
11        return ans;    
12    }    
13    
14    static int[][] colSum(int arr[][]){    
15        int m = arr.length;    
16        int n = arr[0].length;    
17        int  p[][] = new int[m+1][n+1];    
18        for(int i=1; i<=m; i++)    
19            for(int j=1; j<=n; j++)    
20                p[i][j] = p[i-1][j] + arr[i-1][j-1];    
21        return p;    
22    }    
23    
24    static int maxArrSum2(int arr[][]){    
25        int m = arr.length;    
26        int n = arr[0].length;    
27        if(m > n){    
28            //对数组进行逆置    
29            arr = reverseArr(arr);    
30            int tmp = m;    
31            m = n;    
32            n = tmp;    
33        }    
34        int p[][] = colSum(arr);    
35        int tempMax = Integer.MIN_VALUE;    
36    
37        //h表示当前矩阵的高度. 即把多少行合并为一行看    
38        for(int h=1; h<=m; h++){    
39            for(int i=1; i+h-1 <= m; i++){    
40                int endLine = i+h-1;    
41                //转化为长度为n一维数组,复杂度为O(n)    
42                tempMax = Math.max(tempMax, maxSum(p, i, endLine, n));    
43            }    
44        }    
45        return tempMax;    
46    }    
47    
48    static int[][] reverseArr(int arr[][]){    
49        int m = arr.length;    
50        int n = arr[0].length;    
51        int newArr[][] = new int[n][m];    
52        for(int i=0; i<m; i++)    
53            for(int j=0; j<n; j++)    
54                 newArr[j][i] = arr[i][j];    
55        return newArr;    
56    }    
57    
58    public static void main(String[] args) {    
59        int arr[][] = {{1, 2, -1, -4, -20},    
60                {-8, -3, 4, 2, 1},    
61                {3, 8, 10, 1, 3},    
62                {-4, -1, 1, 7, -6}    
63               };    
64    
65        int ans = maxArrSum(arr);    
66        System.out.println("method 1: " + ans);    
67        ans = maxArrSum2(arr);    
68        System.out.println("method 2: " + ans);    
69    }    
 扩展问题
如果二维数组首尾相连,如何计算?
可以先考虑一维数组首位相连的问题。一个方法是把原来的数组进行扩充,例如对于 arr[0 ... n-1 ] 可以看成是  arr[0 ... n-1, 0, 1, ....n-2]。即原数组的长度由原来的n,变为了 2*n-1。实际中计算中没有必要扩充,求余即可。需要注意的是,如果全部都是非负的,我们需要加一个判断,防止计算结果超出数组的总和。当然还有其他的计算方法,大家可以参考编程之美一书。
01    static int MaxSumLinked(int arr[], int n){    
02            int currentSum = arr[0];    
03            int ans = currentSum;    
04            boolean hasNegative = false;//有可能全部都是非负,这时就没必要往后计算    
05            for(int i=1; i<n*2-1; i++){    
06                if(i < n && arr[i] < 0) hasNegative = true;    
07                currentSum = Math.max(currentSum+arr[i%n], arr[i%n]);    
08                ans = Math.max(ans, currentSum);    
09                if(i == n-1 && !hasNegative) return ans;    
10            }    
11            return ans;    
12        }    
二维数组的计算方法和这个类似。

转载于:https://my.oschina.net/u/2533184/blog/617525

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值