【2022-09-04每日一题】1582. 二进制矩阵中的特殊位置

2022-09-04
3分钟阅读时长

2022-09-04每日一题:1582. 二进制矩阵中的特殊位置

  • 难度:Easy
  • 标签:数组 、 矩阵

给你一个大小为 rows x cols 的矩阵 mat,其中 mat[i][j]01,请返回 矩阵 mat 中特殊位置的数目

特殊位置 定义:如果 mat[i][j] == 1 并且第 i 行和第 j 列中的所有其他元素均为 0(行和列的下标均 从 0 开始 ),则位置 (i, j) 被称为特殊位置。

 

示例 1:

输入:mat = [[1,0,0],
            [0,0,1],
            [1,0,0]]
输出:1
解释:(1,2) 是一个特殊位置,因为 mat[1][2] == 1 且所处的行和列上所有其他元素都是 0

示例 2:

输入:mat = [[1,0,0],
            [0,1,0],
            [0,0,1]]
输出:3
解释:(0,0), (1,1) 和 (2,2) 都是特殊位置

示例 3:

输入:mat = [[0,0,0,1],
            [1,0,0,0],
            [0,1,1,0],
            [0,0,0,0]]
输出:2

示例 4:

输入:mat = [[0,0,0,0,0],
            [1,0,0,0,0],
            [0,1,0,0,0],
            [0,0,1,0,0],
            [0,0,0,1,1]]
输出:3

 

提示:

  • rows == mat.length
  • cols == mat[i].length
  • 1 <= rows, cols <= 100
  • mat[i][j]01

方法一:模拟

func numSpecial(mat [][]int) (ans int) {
    rowsSum, colsSum := make([]int, len(mat)), make([]int, len(mat[0]))
    for i, rows := range mat {
        for j, num := range rows {
            // 计算每行每列有多少个1
            rowsSum[i]+=num
            colsSum[j]+=num
        }
    }
    for i, rows := range mat {
        for j, num := range rows {
            if num == 1 && rowsSum[i] == 1 && colsSum[j] == 1 {
                ans++
            }
        }
    }
    return ans
}

复杂度分析

  • 时间复杂度:O(m×n),其中 m 为矩阵 mat 的行数,n 为矩阵 mat 的列数。
  • 空间复杂度:O(m+n),主要为预处理每一行和列的空间开销。

方法二:列的标记值

思路

在方法一的基础上,我们可以看到对于 (i,j),它为特殊位置的条件为mat[i][j]=1 且该行和该列中 1 的数量都为 1。据此,定义第 j 列的标记值为:该列所有 1 所在行中的 1 的数量之和。下面证明,(i,j) 为特殊位置的充要条件是,第 j列的标记值恰好为 1:

如果 (i,j) 为特殊位置,则说明第 j 列只有一个 1,这一个 1 所在的第 i 行也只有它这一个 1,那么按照标记值的定义可得,第 j列的标记值为1。 如果第 j 列的标记值为 1,那么说明该列只能有一个 1。反证:如果有 x 个 1(x > 1),则第 j 列的标记值一定 ≥x。既然只能有一个 1,设其在第 行,那么标记值也是第 i 行中的 1 的数量,即:第 i 行也有且仅有这个 1。所以 (i,j) 为特殊位置。 那么整个矩阵的特殊位置的数量就是最后标记值为 1 的列的数量。

进一步地,我们可以用原始矩阵的第一行来作为我们标记列的额外空间,从而使空间复杂度降至 O(1)。

代码

// 压缩
func numSpecial(mat [][]int) (ans int) {
    for i, row := range mat {
        cnt1 := 0
        // 遍历列,统计第i行中的1的个数
        for _, x := range row {
            cnt1 += x
        }
        // 记录代表的行
        if i == 0 {
            // 若代表行正好有元素1
            // 直接累计cnt则会出现重复计算的bug
            cnt1--
        }
        // 其实可以不判,因为该行没有1,永远不会累计上-1
        if cnt1 > 0 {
            // 遍历列,若该列有1,则代表col中累计这行的1的个数    
            for j, x := range row {
                if x == 1 {
                    mat[0][j] += cnt1
                }
            }
        }
    }
    for _, x := range mat[0] {
        if x == 1 {
            ans++
        }
    }
    return ans
}

cpp

// 非压缩
class Solution {
public:
    int numSpecial(vector<vector<int>>& mat) {
        int n = mat.size();
        int m = mat[0].size();

        // 每列中数字是1的那行
        // 的1的个数之和
        // 最后只有col[i] == 1才是合格的
        vector<int> col(m);

        for (int i = 0; i < n; i += 1) {
            int cnt = 0;
            // 遍历列
            // 统计第i行中的1的个数
            for (int j = 0; j < m; j += 1) {
                cnt += mat[i][j];
            }

            // 遍历列
            // 若该列有1,则代表col中累计这行的1的个数
            for (int j = 0; j < m; j += 1) {
                if (mat[i][j] == 1) {
                    col[j] += cnt;
                }
            }
        }

        return count(col.begin(), col.end(), 1);
    }
};

复杂度分析

  • 时间复杂度:O(m×n),其中 m 为矩阵 mat 的行数,n 为矩阵 mat 的列数。
  • 空间复杂度:O(1) 。

LeetCode题库地址