LeetCode 每日一题笔记 日期:2025.03.24 题目:2906.构造乘积矩阵

LeetCode 每日一题笔记

0. 前言

  • 日期:2025.03.24
  • 题目:2906.构造乘积矩阵
  • 难度:中等
  • 标签:数组 矩阵 前缀和

1. 题目理解

问题描述

给你一个下标从 0 开始、大小为 n * m 的二维整数矩阵 grid,定义一个下标从 0 开始、大小为 n * m 的二维矩阵 p。如果满足以下条件,则称 pgrid乘积矩阵

  • 对于每个元素 p[i][j],它的值等于除了 grid[i][j] 外所有元素的乘积,且乘积对 12345 取余数。
    返回 grid 的乘积矩阵。

示例

输入:grid = [[1,2],[3,4]]

输出:[[24,12],[8,6]]

解释:

  • p[0][0] = grid[0][1] * grid[1][0] * grid[1][1] = 2 * 3 * 4 = 24
  • p[0][1] = grid[0][0] * grid[1][0] * grid[1][1] = 1 * 3 * 4 = 12
  • p[1][0] = grid[0][0] * grid[0][1] * grid[1][1] = 1 * 2 * 4 = 8
  • p[1][1] = grid[0][0] * grid[0][1] * grid[1][0] = 1 * 2 * 3 = 6

2. 解题思路

核心观察

  • 暴力求解的问题 :若直接对每个元素 grid[i][j],遍历所有元素计算乘积(排除自身),时间复杂度为 O((n∗m)2)O((n*m)^2)O((n∗m)2)。当矩阵规模较大时(如 n、m 为 1000),计算量会达到 101210^{12}1012 级别,远超时间限制,且多次乘法会导致数值溢出(即使取模也无法降低时间复杂度)。
  • 前缀/后缀乘积优化 :将二维矩阵展平为一维数组,通过「前缀乘积数组」和「后缀乘积数组」快速计算每个位置排除自身的总乘积:
    • 前缀乘积 pre[i]:表示一维数组中前 i 个元素的乘积(包含 i);
    • 后缀乘积 suf[i]:表示一维数组中从 i 到末尾的乘积(包含 i);
    • 对于位置 k,排除自身的总乘积 = 前缀乘积 pre[k-1] * 后缀乘积 suf[k+1](边界位置单独处理)。

算法步骤

  1. 展平矩阵 :将二维矩阵 grid 转换为一维数组 arr,便于统一计算前缀/后缀乘积;
  2. 计算前缀乘积pre[0] = arr[0]pre[i] = (pre[i-1] * arr[i]) % 12345
  3. 计算后缀乘积suf[len-1] = arr[len-1]suf[i] = (suf[i+1] * arr[i]) % 12345
  4. 重构结果矩阵:遍历每个位置,根据前缀/后缀乘积计算排除自身的总乘积,转换回二维矩阵并取模。

3. 代码实现

java 复制代码
package com.sheeta1998.lec.lc2906;

class Solution {
    public int[][] constructProductMatrix(int[][] grid) {
        int n = grid.length;    // 矩阵行数
        int m = grid[0].length; // 矩阵列数
        int total = n * m;      // 一维数组总长度
        int[] arr = new int[total];
        int[] pre = new int[total]; // 前缀乘积数组
        int[] suf = new int[total]; // 后缀乘积数组
        
        // 步骤1:将二维矩阵展平为一维数组
        int idx = 0;
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                arr[idx++] = grid[i][j];
            }
        }
        
        // 步骤2:计算前缀乘积
        pre[0] = arr[0] % 12345;
        for (int i = 1; i < total; i++) {
            pre[i] = (pre[i-1] * arr[i]) % 12345;
        }
        
        // 步骤3:计算后缀乘积
        suf[total-1] = arr[total-1] % 12345;
        for (int i = total-2; i >= 0; i--) {
            suf[i] = (suf[i+1] * arr[i]) % 12345;
        }
        
        // 步骤4:重构乘积矩阵
        idx = 0;
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                if (idx == 0) {
                    // 第一个元素:只有后缀乘积
                    grid[i][j] = suf[1] % 12345;
                } else if (idx == total-1) {
                    // 最后一个元素:只有前缀乘积
                    grid[i][j] = pre[total-2] % 12345;
                } else {
                    // 中间元素:前缀*后缀
                    grid[i][j] = (pre[idx-1] * suf[idx+1]) % 12345;
                }
                idx++;
            }
        }
        return grid;
    }
}

4. 代码优化说明

  1. 空间优化 :原代码中 prearr/sufarr 命名简化为 pre/suf,变量名更直观;
  2. 取模时机 :每一步乘法后立即对 12345 取模,避免整数溢出(Java 中 int 最大值约 2×1092×10^92×109,多次乘法易溢出);
  3. 边界处理:明确区分第一个/最后一个元素的计算逻辑,避免数组越界;
  4. 变量复用 :直接修改原矩阵 grid 存储结果,无需额外创建二维数组,节省空间。

5. 复杂度分析

  • 时间复杂度 :O(n×m)O(n×m)O(n×m)。展平矩阵、计算前缀/后缀乘积、重构矩阵均为一次遍历,总次数为 3×n×m3×n×m3×n×m,属于线性时间复杂度;
  • 空间复杂度 :O(n×m)O(n×m)O(n×m)。需要额外存储一维数组 arr、前缀数组 pre、后缀数组 suf,总空间为 3×n×m3×n×m3×n×m(可进一步优化为 O(1)O(1)O(1) 空间,直接在二维矩阵上计算前缀/后缀,但代码可读性降低)。

6. 总结

  1. 暴力求解不可行的原因 :时间复杂度为 O((n×m)2)O((n×m)^2)O((n×m)2),矩阵规模较大时会超时,且多次乘法易导致数值溢出;
  2. 核心优化思路:利用「前缀/后缀乘积」将时间复杂度降至线性,通过展平二维矩阵简化计算逻辑;
  3. 关键细节:每一步乘法后取模避免溢出,边界位置单独处理防止数组越界。

关键点回顾

  1. 暴力解法因 O((n∗m)2)O((n*m)^2)O((n∗m)2) 时间复杂度无法通过大规模用例,必须用前缀/后缀乘积优化;
  2. 展平二维矩阵是简化前缀/后缀计算的核心技巧,最终需还原为二维结果;
  3. 取模操作需在每一步乘法后执行,避免整数溢出且满足题目要求。
相关推荐
sbjdhjd20 小时前
Docker 网络工业级实战手册
linux·运维·经验分享·笔记·docker·云原生·云计算
Flittly20 小时前
【日常小问】解决 Jenkins 部署 Spring Cloud 微服务到 Docker 容器启动失败的问题
运维·笔记·docker·微服务·jenkins
晓梦林20 小时前
Fuzzz靶场学习笔记
笔记·学习·安全·web安全
Controller-Inversion20 小时前
240. 搜索二维矩阵 II
线性代数·算法·矩阵
Hammer_Hans21 小时前
DFT笔记47
笔记
Anjgst21 小时前
宝塔面板命令行
linux·运维·服务器·笔记
程序猿乐锅21 小时前
【Tilas|第七篇】学员管理实现
java·笔记·idea·tlias
ouliten21 小时前
C++笔记:Lambda表达式
c++·笔记
问心无愧051321 小时前
ctf show web 入门38
笔记
OYangxf21 小时前
力扣hot100【子串专题】
算法·leetcode·职场和发展