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. 取模操作需在每一步乘法后执行,避免整数溢出且满足题目要求。
相关推荐
Heartache boy3 小时前
野火STM32_HAL库版课程笔记-ADC多通道采集热敏、光敏、反射传感器(轮询)
笔记·stm32·单片机
yoothey3 小时前
Java字节流与字符流核心笔记(问答+考点复盘)
java·开发语言·笔记
老师好,我是刘同学4 小时前
force与deposit在SystemVerilog中的区别详解
笔记
Theodore_10225 小时前
深度学习(11):偏差与方差诊断、学习曲线
人工智能·笔记·深度学习·神经网络·机器学习·计算机视觉
2401_835792546 小时前
Linux复习笔记
linux·服务器·笔记
C羊驼6 小时前
C语言学习笔记(十五):预处理
c语言·经验分享·笔记·学习·算法
不会聊天真君6476 小时前
基础语法·中(golang笔记第二期)
开发语言·笔记·golang
态态态6 小时前
平板PDF充足笔记空间的最优解
笔记·pdf
老鼠只爱大米7 小时前
LeetCode经典算法面试题 #295:数据流的中位数(双堆法、有序列表、平衡树等多种实现方案详解)
算法·leetcode·优先队列··数据流·中位数·java 面试题