44-矩阵置零

给定一个 m x n 的矩阵,如果一个元素为 ,则将其所在行和列的所有元素都设为 0 。请使用 原地 算法

    示例 1:

    输入:matrix = [[1,1,1],[1,0,1],[1,1,1]]
    输出:[[1,0,1],[0,0,0],[1,0,1]]
    

    示例 2:

    输入:matrix = [[0,1,2,0],[3,4,5,2],[1,3,1,5]]
    输出:[[0,0,0,0],[0,4,5,0],[0,3,1,0]]
    

    提示:

    • m == matrix.length
    • n == matrix[0].length
    • 1 <= m, n <= 200
    • -231 <= matrix[i][j] <= 231 - 1

    进阶:

    • 一个直观的解决方案是使用  O(mn) 的额外空间,但这并不是一个好的解决方案。
    • 一个简单的改进方案是使用 O(m + n) 的额外空间,但这仍然不是最好的解决方案。
    • 你能想出一个仅使用常量空间的解决方案吗?
    方法一:使用额外集合(O (m+n) 空间)

    这种方法通过两个集合记录需要置零的行和列,然后遍历矩阵完成置零操作。

    function setZeroes(matrix: number[][]): void {
        const m = matrix.length;
        const n = matrix[0].length;
        const rows = new Set<number>();
        const cols = new Set<number>();
    
        // 记录需要置零的行和列
        for (let i = 0; i < m; i++) {
            for (let j = 0; j < n; j++) {
                if (matrix[i][j] === 0) {
                    rows.add(i);
                    cols.add(j);
                }
            }
        }
    
        // 将记录的行和列置零
        for (let i = 0; i < m; i++) {
            for (let j = 0; j < n; j++) {
                if (rows.has(i) || cols.has(j)) {
                    matrix[i][j] = 0;
                }
            }
        }
    }
    

    方法二:使用矩阵行列标记(O (1) 空间)

    利用矩阵的第一行和第一列作为标记,避免使用额外空间,同时用两个变量记录第一行和列是否需要置零。

    function setZeroes(matrix: number[][]): void {
        const m = matrix.length;
        const n = matrix[0].length;
        let firstRowHasZero = false;
        let firstColHasZero = false;
    
        // 检查第一行是否需要置零
        for (let j = 0; j < n; j++) {
            if (matrix[0][j] === 0) {
                firstRowHasZero = true;
                break;
            }
        }
    
        // 检查第一列是否需要置零
        for (let i = 0; i < m; i++) {
            if (matrix[i][0] === 0) {
                firstColHasZero = true;
                break;
            }
        }
    
        // 使用第一行和第一列记录其他行和列是否需要置零
        for (let i = 1; i < m; i++) {
            for (let j = 1; j < n; j++) {
                if (matrix[i][j] === 0) {
                    matrix[i][0] = 0;
                    matrix[0][j] = 0;
                }
            }
        }
    
        // 根据第一行和第一列的标记,置零矩阵(除第一行和列)
        for (let i = 1; i < m; i++) {
            for (let j = 1; j < n; j++) {
                if (matrix[i][0] === 0 || matrix[0][j] === 0) {
                    matrix[i][j] = 0;
                }
            }
        }
    
        // 处理第一行和第一列的置零
        if (firstRowHasZero) {
            for (let j = 0; j < n; j++) {
                matrix[0][j] = 0;
            }
        }
    
        if (firstColHasZero) {
            for (let i = 0; i < m; i++) {
                matrix[i][0] = 0;
            }
        }
    }
    
    方法三:两次遍历直接标记(O (1) 空间变种)

    另一种原地算法,通过两次遍历实现置零,第一次遍历标记非零元素,第二次置零。

    function setZeroes(matrix: number[][]): void {
        const m = matrix.length;
        const n = matrix[0].length;
    
        // 第一次遍历:将非零元素标记为特殊值(如Infinity)
        for (let i = 0; i < m; i++) {
            for (let j = 0; j < n; j++) {
                if (matrix[i][j] === 0) {
                    // 标记所在行的非零元素
                    for (let k = 0; k < n; k++) {
                        if (matrix[i][k] !== 0) {
                            matrix[i][k] = Infinity;
                        }
                    }
                    // 标记所在列的非零元素
                    for (let k = 0; k < m; k++) {
                        if (matrix[k][j] !== 0) {
                            matrix[k][j] = Infinity;
                        }
                    }
                }
            }
        }
    
        // 第二次遍历:将标记为Infinity的元素置零
        for (let i = 0; i < m; i++) {
            for (let j = 0; j < n; j++) {
                if (matrix[i][j] === Infinity) {
                    matrix[i][j] = 0;
                }
            }
        }
    }
    

    复杂度分析

    方法时间复杂度空间复杂度说明
    方法一(集合标记)O(m×n)O(m+n)使用两个集合记录置零的行和列,实现简单,空间复杂度优于 O (m×n)
    方法二(行列标记)O(m×n)O(1)利用矩阵自身的行列作为标记,空间复杂度最优,实现稍复杂
    方法三(直接标记)O(m×n)O(1)通过特殊值标记非零元素,避免使用额外空间,但可能受限于数值范围

    测试示例

    // 测试示例1
    const matrix1 = [
        [1, 1, 1],
        [1, 0, 1],
        [1, 1, 1]
    ];
    setZeroes(matrix1);
    console.log(matrix1);  // 输出: [[1,0,1],[0,0,0],[1,0,1]]
    
    // 测试示例2
    const matrix2 = [
        [0, 1, 2, 0],
        [3, 4, 5, 2],
        [1, 3, 1, 5]
    ];
    setZeroes(matrix2);
    console.log(matrix2);  // 输出: [[0,0,0,0],[0,4,5,0],[0,3,1,0]]
    

    方法说明

    • 方法一是最直观的解决方案,通过集合记录需要置零的行和列,适合理解问题本质,但空间复杂度为 O (m+n)。

    • 方法二是最优解决方案,利用矩阵的第一行和第一列作为标记,将空间复杂度降为 O (1)。需要特别注意处理第一行和第一列本身是否需要置零的情况,通过两个布尔变量firstRowHasZerofirstColHasZero来记录。

    • 方法三通过特殊值标记非零元素,避免使用额外空间,但存在局限性:如果矩阵中本身包含该特殊值(如 Infinity),则需要选择其他标记值。这种方法在数值范围允许时有效,否则可能需要调整标记策略。

    在实际应用中,方法二是最推荐的解决方案,既满足原地修改的要求,又具有最优的空间复杂度。

     

    评论
    添加红包

    请填写红包祝福语或标题

    红包个数最小为10个

    红包金额最低5元

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

    抵扣说明:

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

    余额充值