leetcode热题 - 2

子矩阵元素加 1

问题描述

给你一个正整数 n ,表示最初有一个 n x n 、下标从 0 开始的整数矩阵 mat ,矩阵中填满了 0 。

另给你一个二维整数数组 query 。针对每个查询 query[i] = [row1i, col1i, row2i, col2i] ,请你执行下述操作:

找出 左上角 为 (row1i, col1i) 且 右下角 为 (row2i, col2i) 的子矩阵,将子矩阵中的 每个元素 加 1 。也就是给所有满足 row1i <= x <= row2i 和 col1i <= y <= col2i 的 mat[x][y] 加 1 。

返回执行完所有操作后得到的矩阵 mat 。

(真题链接:子矩阵元素加 1

解题思路

如果之前了解过二维数组差分的朋友们很容易就可以解决这个问题,所以这里着重给没了解过的朋友们介绍一下。

来自deepseek:

二维差分数组是一种通过O(1)时间 记录子矩阵更新、再通过二维前缀和还原的技术。具体来说,要对原矩阵中某个矩形区域统一加一个值,只需在差分数组的四个角做标记:起点加该值、右侧和下方的边界外减该值、右下角外再加回该值;最后对差分数组求二维前缀和,就能得到每个位置被累计更新的总量,从而高效处理大量批量区域更新问题。

简单来说:

就像差分数组一样,我们只需要在一个二维数组的[0, 0]位置放上一个 1,就可以实现整个二维数组的每一个位置都是1。

下面笔者将以可视化的形式带读者更清晰的认识二维差分数组:

二维差分数组原理图示

复制代码
原矩阵:
1  2  4  3
5  3  2  1
1  4  2  5

差分矩阵(初始化为0):
0  0  0  0
0  0  0  0
0  0  0  0

1. 单点更新(以左上角(1,1)到右下角(2,2)矩形区域+3为例)

复制代码
差分矩阵操作点(假设行列从1开始计数):
(1,1) += 3
(1,3) -= 3
(3,1) -= 3
(3,3) += 3

更新后差分矩阵:
+3  0 -3  0
 0  0  0  0
-3  0 +3  0

2. 前缀和还原矩阵

通过二维前缀和计算还原修改后的原矩阵:

复制代码
第一行递推:
1→4→1→1 (原值1+3=4;4+(-3)=1;1+0=1)
其他行列类似递推...

最终原矩阵:
4  5  1  3
5  3  2  1
-2  1  5  5

可视化辅助图

复制代码
操作区域示意图:
(1,1)───────(1,2)
   │ +3      │
   │         │
(2,1)───────(2,2)

差分矩阵影响点:
● (1,1) +c      ○ (1,3) -c
○ (3,1) -c      ● (3,3) +c

代码实现参考

一维差分:a[i] = diff[i] + a[i-1]

二维差分:a[i][j] = diff[i][j] + a[i-1][j] + a[i][j-1] - a[i-1][j-1]

有了上面关于二维差分数组的基本理解,我们再来看题目就很容易了。我们只需要在每一个标记的点的位置将他 +1并平衡数组,等全部的位置标记完成后再进行二维差分数组的复原即可。

代码实现

cpp 复制代码
class Solution {
public:
    vector<vector<int>> rangeAddQueries(int n, vector<vector<int>>& queries) {
        vector<vector<int>> ret(n, vector<int>(n, 0));
        int x1, y1, x2, y2;
        for(int i = 0; i < queries.size(); i++)
        {
            x1 = queries[i][0],y1 = queries[i][1],x2 = queries[i][2],y2 = queries[i][3];
            ret[x1][y1] += 1;
            if(x2 < n - 1)ret[x2 + 1][y1] += -1;
            if(y2 < n - 1)ret[x1][y2 + 1] += -1;
            if(x2 < n - 1 && y2 < n - 1)ret[x2 + 1][y2 + 1] += 1;
        }
        for (int i = 0; i < n; i++)
        {
            for (int j = 0; j < n; j++) 
            {
                if (i > 0) ret[i][j] += ret[i-1][j];
                if (j > 0) ret[i][j] += ret[i][j-1];
                if (i > 0 && j > 0) ret[i][j] -= ret[i-1][j-1];
            }
        }
        return ret;
    }
};

复杂度分析

项目 复杂度
时间复杂度 O(N^2+M)
空间复杂度 O(M)

总结

二维差分核心

  • 区间加操作 → O(1) 修改差分数组的 4 个角点
  • 最后求二维前缀和还原 → O(n²)

关键细节

  • 差分数组直接复用在结果矩阵上,无需额外空间
  • 边界判断 x2+1 < n 防止越界
  • 还原公式:a[i][j] += a[i-1][j] + a[i][j-1] - a[i-1][j-1]

使数组所有元素变成1的最少操作次数

问题描述

给你一个下标从 0 开始的 正 整数数组 nums 。你可以对数组执行以下操作任意 次:

选择一个满足 0 <= i < n - 1 的下标 i ,将 nums[i] 或者 nums[i+1] 两者之一替换成它们的最大公约数。

请你返回使数组 nums 中所有元素都等于 1 的 最少 操作次数。如果无法让数组全部变成 1 ,请你返回 -1 。

两个正整数的最大公约数指的是能整除这两个数的最大正整数。

(真题链接:使数组所有元素变成1的最少操作次数


解题思路

要解决这个问题我们可以换一种方法:

首先我们需要先判断一下是否可以实现把所有元素都变成 1,即所有数的最大公约数是否为 1,如果不可以,不要犹豫,直接返回 -1。

因为这种情况下不可能将所有元素都变成 1。如果可以,我们先寻找一个最小的len,在这个区间内数组的最大公约数为 1,所需要将这个区间中一个元素变成1这个操作的次数就为len - 1;有一个 1 之后,如果要将整个数组都变成1只需要再进行n-1次扩散即可。所有使整个数组变成1的操作次数就是n+len-2,最小操作次数只需要我们找出这个最小的minlen即可。


代码实现

cpp 复制代码
class Solution {
public:
    int minOperations(vector<int>& nums) {
        int n = nums.size();
        int num1 = 0,g = 0;

        for(int x : nums)
        {
            if(x == 1)num1++;

            g = gcd(g, x);
        }
        if(num1 > 0)return n - num1;
        if(g > 1)return -1;

        // 找到一个最小的可以区间可以使区间所有数组gcd = 1
        int minlen = n;
        for(int i = 0; i < n; i++)
        {
            int g = 0;
            for(int j = i; j < n; j++)
            {
                g = gcd(g, nums[j]);
                if(g == 1)
                {
                    minlen = min(minlen, j - i + 1);
                    break;
                }
            }
        }
        return minlen + n - 2;
    }
};

复杂度分析

维度 最优解
时间复杂度 O(n^2 logN) 双重循环 + gcd 计算(log M 为数值范围)
空间复杂度 O(1) 无法再优化,因为不需要额外存储

总结

三步走策略:
预判断

统计已有的 1 的个数num1

计算全局 gcd,若 >1 则返回 -1(不可能全变成 1)

特殊情况

如果已有 1,直接返回n - num1(每个 1 可扩散一次)

一般情况

寻找最短的gcd=1的子数组,长度为minlen需要minlen - 1次操作在该区间内造出一个 1,再用n - 1次操作将这个 1 扩散到整个数组
总操作数 = minlen + n - 2

相关推荐
Ivanqhz2 小时前
SMT(Satisfiability Modulo Theories,基于模理论的可满足性)
人工智能·算法·机器学习
游乐码2 小时前
C#Dicitionary
算法·c#
华清远见IT开放实验室2 小时前
AI 算法核心知识清单(深度实战版1)
人工智能·python·深度学习·学习·算法·机器学习·ai
牧瀬クリスだ2 小时前
七大排序一次满足
数据结构·算法·排序算法
liu****2 小时前
第15届省赛蓝桥杯大赛C/C++大学B组
开发语言·数据结构·c++·算法·蓝桥杯·acm
无缘之缘2 小时前
蓝桥杯手把手教你备战(C/C++ B组)(最全面!最贴心!适合小白!)
c语言·c++·算法·蓝桥杯
Dfreedom.2 小时前
【实战篇】神经网络在回归任务中的应用
人工智能·神经网络·算法·机器学习·回归
嘿黑嘿呦2 小时前
17届蓝桥杯考前准备
算法·职场和发展·蓝桥杯
Q741_1472 小时前
每日一题 3740. 三个相等元素之间的最小距离 I 3741. 三个相等元素之间的最小距离 II 模拟 哈希表 C++ 题解
c++·算法·leetcode·模拟·数组·哈希表