一个矩阵标记的问题分析

问题描述

    给定一个矩阵,假设为n * n的,我们这个矩阵里有若干个数字为1的元素。其他地方的元素值则不为1. 现在需要我们提供一个方法将里面为1的元素所在的行和列都设置成1. 同时要求算法的空间复杂度为O(1)。

 

分析

    这个问题看起来有点容易让人混淆,因为如果我们从前往后这么一行一行的去遍历时,如果碰到一个为1的元素就直接将它所在行和列都设置成1的话。相当于将后面要遍历的一些元素也变成了1,这样我们就没法判断这些元素是本来为1的还是后来被设置成1的。而且,因为有限的空间复杂度我们不能记录下来每个为1的元素的位置信息。

    我们以下面的图为例,假定有一个矩阵,它里面包含的1元素如下:

    因为在图中只有第2行,第3行和第5行有元素分别为1,那么按照前面设置的思路,最后被设置成的结果应该是这样的:

    而如果我们当时碰到一个1元素就直接将它本行或者本列后面的部分置1的话,我们后面遍历到的时候就会产生问题,如下图:

        在我们标红的这个地方,如果实现被设置成了1,后面因为没法判断,就会将整个矩阵都置成1了。

    所以说,这种原来的思路就有问题。

 

标记和设置

    在讨论这个问题的时候,当时因为受到前面这个思路的影响,可以说被带到沟里去了。其实我们用标记和设置的这个思路来考虑的话,就很好办了。

    我们这样来看,既然我们每碰到一个元素,就要将它所在行和列都设置为1, 而为了不影响它后面的遍历,我们肯定不能去修改它后面的元素的值。另外,我们在一个矩阵里,如何去确定一个元素的位置呢?其实很简单,就和二维坐标一样,只要能够确定它所在的行号和列号就可以了。那么如果我们知道一个元素为1的时候,我们就将它所在的元素的行的第一个元素设置为1并将它所在列的第一个元素设置为1。这样不就相当于我们画一些方格的时候给打的点吗?而且有了这些点我们不就可以确定它们后面所画的线了吗?这样也不会对后面的修改造成任何混淆。

    依然以前面的图为例,我们在碰到第一个为1的元素时,所要做的事就是设置它所在的行和列的第一个元素,如下图:

    这个标红的两个点表示所在的行和列以后要设置为1. 按照这样的思路,我们第一回遍历完整个矩阵之后,得到如下的一个标记结果:

    ok,有了这个图我们后面该怎么办呢?其实很简单了,我们就是遍历第一行和第一列,将里面为1的元素对应的行或者列设置为1。当然,在遍历的时候,比如说第一行的时候,我们还有一个特殊的情况要注意,就是假如我们矩阵里m[0][0]的元素为1,我们是不是要将它所在的列置为1呢?因为对应的是第一列,如果一开始我们就将它给设置了的话就破坏了原来设置的结果。所以针对这个特殊的情况,我们需要先跳过,在将后面的所有元素都设置好了之后再回过头来处理它,这样就没问题了。

    现在我们来写一个代码的实现,这里主要分为两个部分,第一个是遍历数组设置第一行和第一列的标点元素,第二个是遍历第一行和第一列,然后将对应的行和列标注出来,然后再来判断一下最左上角的元素。

第一部分的实现如下:

 

void mark(int[][] m, int n) {
    for(int i = 0; i < n; i++)
        for(int j = 0; j < n; j++) {
            if(m[i][j] == 1) {
                m[0][j] = 1;
                m[i][0] = 1;
            }
    }
}

     方法很简单,做个标记。

     第二个部分的代码:

public void set(int[][] m, int n) {
    for(int i = 1; i < n; i++) { //注意这里i取从1开始,故意跳过m[0][0]元素。
        if(m[0][i] == 1) {
            setColumn(m, n, i);
        }
        if(m[i][0] == 0) {
            setRow(m, n, i);
        }
    }
    if(m[0][0] == 1) {
        setRow(m, n, 0); 
        setColumn(m, n, 0);
    }
}

    而setRow, setColumn这两个方法的实现只要注意一点,不要从它们这一行第一个元素开始设置就行了。它们的实现如下:

void setColumn(int[][] m, int n, int col) {
    for(int i = 1; i < n; i++)
        m[i][col] = 1;
}

void setRow(int[][] m, int n, int row) {
    for(int i = 1; i < n; i++)
        m[row][i] = 1;
}

 

总结

    这种问题并不难,有的时候就是要看自己分析的时候思路是不是清晰。当然,这种问题和刷题党的应对手段差不多。鸟儿大了,什么样的林子没见过?酷有点考核人思考能力的意义,但是并不大。

 

 

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值