微软面试100题系列---最大二维子矩阵和

题目

求一个矩阵中最大的二维矩阵(元素和最大);

实现

方法1

思路:遍历每个二维矩阵,记录最大的和;时间复杂度o(mn);
代码:

    public static void Max2DMatrix(int[][] A,int row,int col){
        int maxSum=0;
        int sum=0;
        int res_i=0,res_j=0;
        for(int i=0;i<row-1;i++){
            for(int j=0;j<col-1;j++){
                sum=A[i][j]+A[i+1][j]+A[i][j+1]+A[i+1][j+1];
                if(maxSum<sum){
                    maxSum=sum;
                    res_i=i;
                    res_j=j;
                }
            }
        }
        result[0][0]=A[res_i][res_j];
        result[0][1]=A[res_i][res_j+1];
        result[1][0]=A[res_i+1][res_j];
        result[1][1]=A[res_i+1][res_j+1];
    }

方法2

对方法1进行分析,方法1在每次遍历时,必须同时访问4个元素,如图所示:
这里写图片描述

从图中可以看出,多个元素被重复访问多次。
我们对其进行改进,使每个元素的访问次数尽可能的减少。改进方法如下:
1)增加一个变量last_vsum(最新纵向和),初始化为A[0][0]+A[1][0];
2)改变遍历方式,每次遍历只访问纵向的2个元素A[i][j]和A[i+1][j];横向遍历的起始点从第二列开始;
3)改变求和方式,求和方法:将上次保存的last_vsum+A[i][j]+A[i+1][j];last_vsum=A[i][j]+A[i+1][j];
这样可以减少某些元素的访问次数;

代码实现:

public static void Max2DMatrix_2(int[][] A,int row,int col){
        int last_vsum=A[0][0]+A[1][0];
        int maxSum=0;
        int sum=0;
        int res_i=0,res_j=0;
        for(int i=0;i<row-1;i++){
            for(int j=1;j<col;j++){
                sum=last_vsum;
                last_vsum=A[i][j]+A[i+1][j];
                sum+=last_vsum;
                if(maxSum<sum){
                    maxSum=sum;
                    res_i=i;
                    res_j=j-1;
                }
            }
        }

        result[0][0]=A[res_i][res_j];
        result[0][1]=A[res_i][res_j+1];
        result[1][0]=A[res_i+1][res_j];
        result[1][1]=A[res_i+1][res_j+1];
    }

方法2与方法1的时间复杂度相同,但效率高,尤其在大矩阵情况下。但是当矩阵的行数大于列数,方法2效率下降;
===》如果列大于行,使用方法2—横向遍历;
如果列小于行,使用–纵向遍历;

扩展

要求求出最大的子矩阵。
分析:
这是一维最大字串的二维扩展问题。
一维最大字串(最大连续子数组和)问题–动态规划:
b[i]–表示包含a[i]的子数组和;b[i]=max{b[i-1]+a[i],a[i]};

将最大字串应用到该题目中,步骤:
1)压缩列
将每列自上向下做累加,存入b[i][j]中;b[i][j]表示第j列,自第一行到第i行的和;
对于第j列,从第i行到第k行的和:b[k][j]-b[i][j];
2)DP列
得到b后,对b求最大子数组和问题;b是一维的;

然后循环DP,总共三层循环:
最外层i从1-n,表明子矩阵从第i行到第开始累加的;
第二层j从i-n,表明子矩阵从第i行累加到第j行;
第三层k从1到m做DP;

代码实现

public static int maxSubMatrix(int[][] A,int row,int col){
        int[] b=new int[col];
        int i,j,k;
        int max=0,sum=Integer.MIN_VALUE;

        for(i=0;i<row;i++){
            for(k=0;k<col;k++){
                b[k]=0;
            }
            for(j=i;j<row;j++){//从i行到j行的和;先是第i行(1行);然后从i到i+1(2行),依次求,最后从i到j行的和;每求一次得到一个b的一维数组,然后对该数组求最大连续子数组和(子矩阵的和),判断是否比当前维护的最大子矩阵和大;--从第i行开始依次压缩列,压缩1列/压缩2列...;
                for(k=0;k<col;k++){ 
                    b[k]+=A[j][k];
                }
                max=maxSubArray(col,b);
                if(max>sum){
                    sum=max;
                }
            }
        }
        return sum;
    }

    public static int maxSubArray(int  n,int[] a){
        int b=0;
        int sum=Integer.MIN_VALUE;
        for(int i=0;i<n;i++){
            if(b>0){
                b+=a[i];
            }else{
                b=a[i];
            }           
            if(b>sum){
                sum=b;
            }
        }
        return sum;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值