LeetCode 3567.子矩阵的最小绝对差

LeetCode 3567.子矩阵的最小绝对差

题目描述

给定一个 m x n 的整数矩阵 grid 和一个整数 k,需要找出所有大小为 k x k 的子网格,并计算每个子网格内不同元素之间的最小绝对差

示例

cpp 复制代码
输入:grid = [[1,2,3],[4,5,6],[7,8,9]], k = 2
输出:[[1,1],[1,1]]
解释:所有2x2子网格的相邻不同元素的最小差值都是1

解法一:暴力枚举(基础解法)

思路分析

最直观的思路是:

  1. 遍历所有可能的 k x k 子网格
  2. 提取子网格中的所有元素
  3. 对元素进行排序
  4. 遍历排序后的数组,找出相邻元素的最小差值

代码实现

cpp 复制代码
class Solution {
public:
    vector<vector<int>> minAbsDiff(vector<vector<int>>& grid, int k) {
        int m = grid.size(), n = grid[0].size();
        vector ans(m - k + 1, vector<int>(n - k + 1));
        
        // 遍历所有可能的子网格起始位置
        for(int i = 0; i <= m - k; i++) {
            for(int j = 0; j <= n - k; j++) {
                vector<int> a;
                
                // 提取当前k x k子网格的所有元素
                for(int x = 0; x < k; x++)
                    for(int y = 0; y < k; y++)
                        a.push_back(grid[i + x][j + y]);
                
                // 排序
                ranges::sort(a);
                
                // 查找最小差值
                int res = INT_MAX;
                for(int p = 1; p < a.size(); p++) {
                    if(a[p] > a[p - 1])  // 只考虑不同元素
                        res = min(res, a[p] - a[p - 1]);
                }
                
                if(res < INT_MAX)
                    ans[i][j] = res;
            }
        }
        return ans;
    }
};

复杂度分析

  • 时间复杂度 :O(m × n × k² log k)
    • 子网格数量:O(m × n)
    • 每个子网格排序:O(k² log k)
  • 空间复杂度:O(k²)

优缺点

优点 :实现简单,思路清晰

缺点:效率较低,存在大量重复计算

解法二:滑动窗口优化

优化思路

观察发现,相邻子网格之间存在大量重叠元素。我们可以利用这个特性,使用滑动窗口和有序数据结构来避免重复排序。

核心思想

  1. 使用 multiset 维护当前窗口的所有元素
  2. 向右滑动时,移除左边界元素,添加右边界元素
  3. 每次滑动后只需重新计算最小差值,无需完全重排序

代码实现

cpp 复制代码
class Solution {
public:
    vector<vector<int>> minAbsDiff(vector<vector<int>>& grid, int k) {
        int m = grid.size(), n = grid[0].size();
        vector ans(m - k + 1, vector<int>(n - k + 1));
        
        for (int i = 0; i <= m - k; i++) {
            // 使用multiset维护当前窗口的所有元素
            multiset<int> window;
            
            // 初始化第一列窗口
            for (int x = 0; x < k; x++) {
                for (int y = 0; y < k; y++) {
                    window.insert(grid[i + x][y]);
                }
            }
            
            // 计算第一个窗口的最小差值
            ans[i][0] = getMinDiff(window);
            
            // 向右滑动窗口
            for (int j = 1; j <= n - k; j++) {
                // 移除左边界元素
                for (int x = 0; x < k; x++) {
                    auto it = window.find(grid[i + x][j - 1]);
                    window.erase(it);
                }
                
                // 添加右边界元素
                for (int x = 0; x < k; x++) {
                    window.insert(grid[i + x][j + k - 1]);
                }
                
                // 计算新的最小差值
                ans[i][j] = getMinDiff(window);
            }
        }
        return ans;
    }
    
private:
    // 计算有序集合的最小差值
    int getMinDiff(multiset<int>& window) {
        int res = INT_MAX;
        auto it = window.begin();
        int prev = *it;
        
        for (++it; it != window.end(); ++it) {
            if (*it > prev) {
                res = min(res, *it - prev);
            }
            prev = *it;
        }
        return res;
    }
};

复杂度分析

  • 时间复杂度 :O(m × n × k log k)
    • 每个子网格的更新操作:O(k log k)
  • 空间复杂度:O(m × n + k²)

解法三:桶排序优化(适用于小数值范围)

优化思路

如果题目给定的数值范围较小(例如 0-1000),可以使用计数排序的思想,将时间复杂度从 O(k² log k) 降到 O(k² + V),其中 V 是数值范围。

代码实现

cpp 复制代码
class Solution {
public:
    vector<vector<int>> minAbsDiff(vector<vector<int>>& grid, int k) {
        int m = grid.size(), n = grid[0].size();
        const int MAX_VAL = 1000;  // 根据题目给定的数值范围调整
        vector ans(m - k + 1, vector<int>(n - k + 1));
        
        for (int i = 0; i <= m - k; i++) {
            for (int j = 0; j <= n - k; j++) {
                vector<int> freq(MAX_VAL + 1, 0);
                
                // 统计频率
                for (int x = 0; x < k; x++) {
                    for (int y = 0; y < k; y++) {
                        freq[grid[i + x][j + y]]++;
                    }
                }
                
                // 查找最小差值
                int res = INT_MAX;
                int prev = -1;
                for (int val = 0; val <= MAX_VAL; val++) {
                    if (freq[val] > 0) {
                        if (prev != -1) {
                            res = min(res, val - prev);
                        }
                        prev = val;
                    }
                }
                ans[i][j] = res;
            }
        }
        return ans;
    }
};

复杂度分析

  • 时间复杂度:O(m × n × (k² + V)),其中 V 是数值范围
  • 空间复杂度:O(V)

适用场景

✅ 数值范围较小的题目

❌ 数值范围大时会浪费大量空间和时间

性能对比总结

解法 时间复杂度 空间复杂度 优点 缺点
暴力枚举 O(mn × k² log k) O(k²) 实现简单 效率低
滑动窗口 O(mn × k log k) O(mn + k²) 效率高,通用性强 实现较复杂
桶排序 O(mn × (k² + V)) O(V) 数值范围小时极快 受数值范围限制
相关推荐
夏日听雨眠1 小时前
文件学习9
数据结构·学习·算法
华农DrLai1 小时前
什么是自动Prompt优化?为什么需要算法来寻找最佳提示词?
人工智能·算法·llm·nlp·prompt·llama
黎阳之光2 小时前
十五五智赋新程 黎阳之光以AI硬核技术筑造产业数智底座
大数据·人工智能·算法·安全·数字孪生
2401_891482172 小时前
C++中的原型模式
开发语言·c++·算法
皙然2 小时前
深度解析三色标记算法:JVM 并发 GC 的核心底层逻辑
java·jvm·算法
sali-tec2 小时前
C# 基于OpenCv的视觉工作流-章40-特征找图
图像处理·人工智能·opencv·算法·计算机视觉
wearegogog1232 小时前
毫米波MIMO系统仿真中混合预编码的交替最小化算法
算法·预编码算法
油泼辣子多加2 小时前
【DL】Transformer算法应用
人工智能·深度学习·算法·机器学习·transformer
2301_795741792 小时前
C++中的代理模式变体
开发语言·c++·算法