问题

一个N*N的矩阵,取值为0或1,有什么好的算法判断一行或一列全为1啊?

回答
你好!要判断一个NN的0/1矩阵中是否存在全为1的行或列,我们可以采取一些高效的策略。这里我将为你详细讲解几种思路,并尽量用易于理解的方式阐述。

问题的核心:

我们需要遍历矩阵,对于每一行,检查其所有元素是否都是1。同时,对于每一列,也要检查其所有元素是否都是1。一旦找到满足条件的行或列,我们就可以停止搜索并给出结果。

最直接的思路:朴素遍历

这是最容易想到的方法,也是最直观的实现。

1. 判断行:
我们从矩阵的第一行开始。
对于当前行,我们再从该行的第一个元素开始,逐个检查到最后一个元素。
如果在这个过程中,我们遇到的任何一个元素是0,那么这一行就不是全为1的行,我们可以停止检查这一行,然后移到下一行。
如果我们顺利地检查完当前行的所有元素,并且发现它们都是1,那么我们就找到了一个全为1的行,问题解决了。
如果检查完所有行都没有找到全为1的行,我们就继续判断列。

2. 判断列:
这个过程与判断行类似,只是我们是逐列进行的。
我们从矩阵的第一列开始。
对于当前列,我们从该列的第一个元素(即矩阵的第一行第一个元素)开始,逐个向下检查到该列的最后一个元素(即矩阵的最后一行第一个元素)。
如果在这个过程中,我们遇到的任何一个元素是0,那么这一列就不是全为1的列,我们可以停止检查这一列,然后移到下一列。
如果我们顺利地检查完当前列的所有元素,并且发现它们都是1,那么我们就找到了一个全为1的列,问题解决了。
如果检查完所有列都没有找到全为1的列,那么整个矩阵中既没有全为1的行,也没有全为1的列。

这种方法的优点:

易于理解和实现。

这种方法的缺点:

效率可能不是最高的。在最坏的情况下(即矩阵中不存在全为1的行或列,或者全为1的行/列在最后才出现),我们需要检查矩阵中的所有元素两次(一次检查行,一次检查列)。

如何优化?—— 利用“标志位”或“计数器”

我们可以稍微改进一下朴素遍历,使其在某些情况下能更快地得出结论。

思路一:行标记 + 列标记

我们可以引入两个辅助的布尔数组(或者说“标记位”):

`row_all_one[N]`:一个长度为N的数组,用于标记每一行是否为全为1。初始值都为`false`。
`col_all_one[N]`:一个长度为N的数组,用于标记每一列是否为全为1。初始值都为`false`。

然后,我们遍历矩阵一次:

1. 遍历矩阵: 从矩阵的左上角 `(0,0)` 开始,逐个元素 `matrix[i][j]` 进行检查。
2. 更新行标记: 如果 `matrix[i][j]` 是 0,那么第 `i` 行就不可能是全为1的行。我们可以将 `row_all_one[i]` 设置为 `false`(如果之前有迹象表明是true的话)。
3. 更新列标记: 同理,如果 `matrix[i][j]` 是 0,那么第 `j` 列就不可能是全为1的列。我们可以将 `col_all_one[j]` 设置为 `false`。
4. 初始假设: 为了简化逻辑,我们可以先假设所有行和列都是全为1的。当遇到一个0时,才去标记“不是全为1”。
我们创建一个 `row_is_all_one[N]` 数组,初始值全部设为 `true`。
我们创建一个 `col_is_all_one[N]` 数组,初始值全部设为 `true`。
然后遍历矩阵 `matrix[i][j]`:
如果 `matrix[i][j] == 0`:
将 `row_is_all_one[i] = false`。
将 `col_is_all_one[j] = false`。

最后检查:

遍历完整个矩阵后,我们再分别检查 `row_is_all_one` 和 `col_is_all_one` 数组:

如果在 `row_is_all_one` 数组中找到了任何一个 `true`,就说明存在全为1的行。
如果在 `col_is_all_one` 数组中找到了任何一个 `true`,就说明存在全为1的列。

这种方法的优点:

只需遍历矩阵一次。
逻辑清晰,易于管理。

这种方法的缺点:

需要额外的空间来存储标记数组(空间复杂度为 O(N))。

思路二:使用计数器(更精细的控制)

这个思路更接近朴素遍历,但通过记录每行每列1的数量来判断。

1. 行计数器: 创建一个长度为N的数组 `row_count[N]`,初始值都为0。
2. 列计数器: 创建一个长度为N的数组 `col_count[N]`,初始值都为0。

3. 遍历矩阵:
逐个遍历矩阵的元素 `matrix[i][j]`。
如果 `matrix[i][j] == 1`:
`row_count[i]++` (第i行中1的数量增加)
`col_count[j]++` (第j列中1的数量增加)

4. 检查结果:
遍历 `row_count` 数组:如果任何一个 `row_count[i] == N`,则说明第i行全为1。
遍历 `col_count` 数组:如果任何一个 `col_count[j] == N`,则说明第j列全为1。

这种方法的优点:

也只需遍历矩阵一次。
不仅能判断是否存在全为1的行/列,还能知道具体是哪一行/列,以及每行每列有多少个1。

这种方法的缺点:

也需要额外的空间存储计数器(空间复杂度为 O(N))。

更进一步的思考:能否避免额外的空间?

在某些特定场景下,如果只是为了尽快判断“是否存在”这样一个布尔值结果,并且允许我们修改原矩阵(虽然在实际应用中通常不建议修改原始数据,但从算法角度思考是值得的),或者我们可以在读取过程中就进行判断,那么我们可以尝试减少额外的空间。

思路三:原地判断(需要谨慎,不修改原始数据时有局限)

严格来说,如果不允许修改原矩阵且不使用额外空间来判断“是否存在”,那么最直接的方式仍然是朴素遍历,因为你需要存储每一行/列的“状态”,而这个状态就需要额外的空间。

但是,我们可以考虑一种“惰性”检查:

1. 遍历行:
对于每一行 `i`:
设置一个标志 `current_row_is_all_one = true`。
遍历该行 `matrix[i][j]`:
如果 `matrix[i][j] == 0`:
`current_row_is_all_one = false`;
`break;` (停止检查这一行,因为已经不是全1了)
如果 `current_row_is_all_one` 仍然是 `true` (意味着整个行都检查完了且没遇到0),那么我们找到了全为1的行,可以直接返回“找到”。

2. 遍历列(仅当没有找到全为1的行时):
对于每一列 `j`:
设置一个标志 `current_col_is_all_one = true`。
遍历该列 `matrix[i][j]`:
如果 `matrix[i][j] == 0`:
`current_col_is_all_one = false`;
`break;` (停止检查这一列)
如果 `current_col_is_all_one` 仍然是 `true`,那么我们找到了全为1的列,可以直接返回“找到”。

3. 无结果: 如果两轮检查都没找到,则返回“未找到”。

这种方法实际上就是朴素遍历,它不使用额外的空间来存储全局状态,而是每个循环都使用一个局部变量来记录当前行的/列的状态。

时间复杂度分析:

朴素遍历(思路三): 在最坏情况下(没有全1行或列,或者在最后发现),需要检查所有NN个元素两次。所以时间复杂度是 O(N^2)。
标记位/计数器(思路一和思路二): 遍历矩阵一次是 O(N^2),之后检查标记/计数器数组是 O(N)。总的时间复杂度仍然是 O(N^2)。

空间复杂度分析:

朴素遍历(思路三): O(1),只需要几个变量来存储当前行的/列的状态和循环的计数器。
标记位/计数器(思路一和思路二): O(N),需要额外的数组来存储状态。

总结一下选择哪种算法:

如果你追求最简单的实现,而且对空间不是特别敏感: 使用 思路一(行标记 + 列标记) 或 思路二(计数器)。它们都只需要一次矩阵遍历,而且逻辑相对清晰。计数器方法更通用一些,可以提供更多信息。
如果你对空间非常敏感,而且可以接受稍微“暴力”一点的遍历方式: 使用 思路三(朴素遍历)。它在空间上是最优的。

实际中的一些考虑:

矩阵大小 N: 如果 N 非常小,那么朴素遍历和优化遍历的实际执行时间差异可能微乎其微。但如果 N 很大,O(N^2) 的计算量会非常显著。
是否允许修改原矩阵? 如果允许修改,可能会有一些更巧妙的算法(但通常不推荐这样做)。
问题的具体要求: 如果只需要判断“是否存在”,那么在找到第一个全为1的行或列时就可以停止。如果需要找出所有全为1的行和列,那么就需要完整遍历。

举个例子:

假设我们有一个 3x3 的矩阵:

```
1 1 1
0 1 0
1 1 1
```

使用 思路一(行标记 + 列标记):

1. 创建 `row_is_all_one = [true, true, true]`,`col_is_all_one = [true, true, true]`。
2. 遍历矩阵:
`matrix[0][0] = 1`:不做任何修改。
`matrix[0][1] = 1`:不做任何修改。
`matrix[0][2] = 1`:不做任何修改。
`matrix[1][0] = 0`:将 `row_is_all_one[1] = false`,`col_is_all_one[0] = false`。
`matrix[1][1] = 1`:不做任何修改。
`matrix[1][2] = 0`:将 `row_is_all_one[1]` (已经是 false 了);将 `col_is_all_one[2] = false`。
`matrix[2][0] = 1`:不做任何修改。
`matrix[2][1] = 1`:不做任何修改。
`matrix[2][2] = 1`:不做任何修改。
3. 最终状态:`row_is_all_one = [true, false, true]`,`col_is_all_one = [false, true, false]`。
4. 检查:`row_is_all_one` 中有 `true`(在索引0和2),所以存在全为1的行(第0行和第2行)。 `col_is_all_one` 中有 `true`(在索引1),所以存在全为1的列(第1列)。

希望这些详细的讲解能帮助你理解如何判断矩阵中是否存在全为1的行或列!选择哪种方法取决于你对空间、时间以及实现复杂度的权衡。

网友意见

user avatar

这个好像没啥算法吧?

有的只是某些操作的速度快慢


上面是AISM的计算过程。

你说的矩阵就是一个布尔矩阵。

以上面一个矩阵为例子,可以存两个图,

一个是上面的矩阵,以队列方式存,或者以数组存都行。注意0 让它成为空值

另外一个是存转置的图。


比如求第1行,统计大小即可。

求第6列,统计反图的大小即可。


一般来说没有必要纯两个图

直接用个循环算二维数组即可。

最关键的还是数据格式的问题。


此外,求列的时候,只是一个简单的深度遍历问题,只要按顺序遍历下去即可。

比如从1开始,直接看看下面指向了2要素没有,如果没有,那肯定不是全部为1。这个跟循环是一回事的。

类似的话题

本站所有内容均为互联网搜索引擎提供的公开搜索信息,本站不存储任何数据与内容,任何内容与数据均与本站无关,如有需要请联系相关搜索引擎包括但不限于百度google,bing,sogou

© 2025 tinynews.org All Rights Reserved. 百科问答小站 版权所有