【LeetCode 每日一题】2373. 矩阵中的局部最大值(easy)

文章介绍了两种解决矩阵中寻找局部最大值的方法:暴力解法,虽然简单但效率较低;以及使用单调队列进行优化,理论上更优,特别是在处理大窗口时。优化方法通过先横后竖两次遍历减少访问次数,但实际效果并不明显。最后提到了滑动窗口的单调队列实现,适用于更大范围的矩阵问题。
摘要由CSDN通过智能技术生成

2373. 矩阵中的局部最大值


一题两解,做简单题也不能忘记优化。

暴力

  这题数据量不大,最简单的就是暴力。四层循环,前两层遍历大数组,后两层枚举 3 ∗ 3 3*3 33的小矩阵找最大值。除最边缘一圈数字外,每个数字要被访问四次。时间复杂度是 O ( n 2 ) O(n^{2}) O(n2)

class Solution {
public:
    vector<vector<int>> largestLocal(vector<vector<int>>& grid) {
        int n=grid.size();
        vector<vector<int>> res(n-2,vector<int> (n-2,0));
        for(int i=0;i<n-2;i++){
            for(int j=0;j<n-2;j++){
                for(int x=i;x<i+3;x++){
                    for(int y=j;y<j+3;y++){
                        res[i][j]=max(res[i][j],grid[x][y]);
                    }
                }
            }
        }
        return res;
    }
};

先横后竖

  很明显,这题数据量小 ( n < 100 ) (n<100) (n<100),所以 O ( n 2 ) O(n^{2}) O(n2)也没问题。但是如果 n > 1000 n>1000 n>1000,大概率就会TLE了。所以我们考虑在极端情况下,该怎么去优化呢?
  我们先横着遍历一遍大数组,找出每个数和其左右邻居里面的最大值,存入一个 a n s [ n ] [ n − 2 ] ans[n][n-2] ans[n][n2]数组,其中
a n s [ i ] [ j ] = m a x ( g r i d [ i ] [ j ] , m a x ( g r i d [ i ] [ j + 1 ] , g r i d [ i ] [ j + 2 ] ) ) ans[i][j]=max(grid[i][j],max(grid[i][j+1],grid[i][j+2])) ans[i][j]=max(grid[i][j],max(grid[i][j+1],grid[i][j+2]))  然后,我们再竖着遍历这个 a n s ans ans数组,找出每个数和其邻居中的最大值,存入最终的结果数组 r e s [ n − 2 ] [ n − 2 ] res[n-2][n-2] res[n2][n2]

class Solution {
public:
    vector<vector<int>> largestLocal(vector<vector<int>>& grid) {
        int n=grid.size();
        vector<vector<int>> ans(n,vector<int> (n-2,0));
        vector<vector<int>> res(n-2,vector<int> (n-2,0));
        for(int i=0;i<n;i++){
            for(int j=0;j<n-2;j++){
                for(int x=j;x<j+3;x++){
                    ans[i][j]=max(ans[i][j],grid[i][x]);
                }
            }
        }
        for(int i=0;i<n-2;i++){
            for(int j=0;j<n-2;j++){
                for(int x=j;x<j+3;x++){
                    res[j][i]=max(res[j][i],ans[x][i]);
                }
            }
        }
        return res;
    }
};

  看上去优化了每个数字的访问次数,其实约等于没优化。暴力方法是 9 ∗ n 2 9*n^{2} 9n2,优化后的方法大概是 3 ∗ n ∗ ( n − 2 ) + 3 ∗ ( n − 2 ) 2 3*n*(n-2)+3*(n-2)^{2} 3n(n2)+3(n2)2  可以看到,并没有明显优化,相反多占用了空间。
在这里插入图片描在这里插入图片描述

单调队列(滑动窗口)

  单调队列的原理可以参考这个。然后针对这题如何运用单调队列参考这个。我只把他的Java代码翻译成了C++。

class Solution {
public:
    vector<vector<int>> largestLocal(vector<vector<int>>& grid) {
        int n=grid.size();
        vector<vector<int>> res(n-2,vector<int> (n-2,0));
        for(int i=0;i<n;i++){
            deque<int> q;//双端队列
            for(int j=0;j<n;j++){
                while(!q.empty()&&grid[i][j]>=grid[i][q.back()]) q.pop_back();//弹出位置靠前且不如新加入的元素大的,也就是即将会被取代的元素。
                q.push_back(j);//加入新的可能最大值的索引
                if(j>=2){//窗口已满(此题窗口为3)
                    int value=grid[i][q.front()];//取出最大值(在队列最左边)
                    for(int k=i-2;k<=i;k++){
                        if(k>=0&&k<n-2){
                            res[k][j-2]=max(res[k][j-2],value);
                        }
                    }
                    if(q.front()<=j-2){//当前最大值位于滑动窗口最左侧,弹出该元素。
                        q.pop_front();
                    }
                }
            }
        }
        return res;
    }
};

  结果时间上并没有减少,应该是力扣I/O向来随缘,且小数据量优化不明显,但是理论上肯定是更优化的。而且当窗口不局限于3,而是求每一个范围为 r ∗ c r*c rc的小矩阵最大值时,优化会十分明显。
在这里插入图片描述
  还有另一个滑动窗口版的先横后竖,代码在这里。我也跑了一遍,结果如下:
在这里插入图片描述
时间有点夸张了,理论上的表现肯定是优于暴力模拟,次于同时更新横竖方向的方法的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值