【面试经典】从一维到二维——从最大子数组到最大子矩阵

由简入难,先从最简单的问题的入手吧:

给出一个整形数组,问最大子数组的和是多少?要求数组连续。

输入样例:nums = [-3,4,-1,2,1,-5,4]

输出样例:6,即连续子数组[4,-1,2,1]的和最大

思路很简单:

可以DP,但没必要。可以这样理解:当之前已有的数组和大于0时,则它必然对于此时的和有贡献,因此在其基础上累加(if sum > 0 : sum += nums[i]);当之前已有的数组和小于0时,则此时应该另起炉灶(if sum < 0 : sum = nums[i])。

代码也简单:

class Solution {
    
    
    public int maxSubArray(int[] nums) {
    
    
        int max = nums[0];
        int sum = nums[0];
        for (int i = 1; i < nums.length; i++) {
    
    
            sum = nums[i] + (sum > 0 ? sum : 0);
            max = Math.max(max, sum);
        }
        return max;
    }
}

 
 

更进一步,如果想要的是最大数组位置而非最大数组和:

思路也不难:

仍旧需要维护最大值max、数组和sum;设置一个start表示数组左边界,而右边界就是当前的位置i,不用专门设置变量;

想一个重要的问题,start在何时更新呢?在由于sum小于0而另起炉灶时更新start。

代码如下:

class Solution {
    
    
    public int[] maxSubArray(int[] nums) {
    
    
        int start = 0;
        int sum = nums[0];
        int max = nums[0];
        int[] res = new int[2];
        for (int i = 1; i < nums.length; i++) {
    
    
            if (sum > 0) {
    
    
            	// 之前的数组和有"贡献",因此在其基础上累加
                sum += nums[i];
            } else {
    
    
            	// 之前的数组无"贡献",因此另起炉灶
                sum = nums[i];
                start = i;
            }
            if (sum > max) {
    
    
                max = sum;
                res[0] = start;
                res[1] = i;
            }
        }
        return res;
    }
}

 
 

问题升维,当数组变成矩阵,如何找到这个最大值及其位置:

给定一个M × N的矩阵,返回最大子矩阵的位置,要求连续。返回值格式为一个数组 [r1, c1, r2, c2]。

思路会让你恍然大悟:我们将任意连续行合并为一行,不就变成了多个最大子数组问题了吗?

在这里插入图片描述
看着图片,代码思路也就清晰明了了:

设置一个上边界top和一个下边界bottom,将之间的行进行合并([top, bottom]),尝试更新最大值;最终结果的"上下"即为当前的上下边界,"左右"在求解最大子数组问题时得出。

在进行行的合并时,可以进行前缀和优化,即对每一列生成前缀和数组。

最终代码如下,还是足够优雅的:

class Solution_6789 {
    
    
    public int[] getMaxMatrix(int[][] matrix) {
    
    
        int m = matrix.length;
        int n = matrix[0].length;
        int max = matrix[0][0];		// 全局最大值
        int[] res = new int[4];		// 最终结果

        // 构造列的前缀和
        int[][] preSum = new int[m + 1][n];
        for (int i = 1; i < m + 1; i++) {
    
    
            for (int j = 0; j < n; j++) {
    
    
                preSum[i][j] = preSum[i - 1][j] + matrix[i - 1][j];
            }
        }

        // 合并行
        for (int top = 0; top < m; top++) {
    
    
            for (int bottom = top; bottom < m; bottom++) {
    
    
                // 构造一维矩阵
                int[] arr = new int[n];
                for (int i = 0; i < n; i++) {
    
    
                    arr[i] = preSum[bottom + 1][i] - preSum[top][i];
                }
                // 最大子数组问题
                int start = 0;
                int sum = arr[0];
                for (int i = 1; i < n; i++) {
    
    
                    if (sum > 0) {
    
    
                        sum += arr[i];
                    } else {
    
    
                        sum = arr[i];
                        start = i;
                    }
                    if (sum > max) {
    
    
                        max = sum;
                        res[0] = top;
                        res[1] = start;
                        res[2] = bottom;
                        res[3] = i;
                    }
                }
            }
        }

        return res;
    }
}

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

E N D END END

猜你喜欢

转载自blog.csdn.net/m0_46202073/article/details/115175436
今日推荐