[特殊字符] LeetCode 做题笔记(一):1878. 矩阵中最大的三个菱形和

📝 LeetCode 做题笔记(一):1878. 矩阵中最大的三个菱形和

题目链接1878. 矩阵中最大的三个菱形和
难度 :中等 | 语言 :Java | 标签:数组、数学、矩阵、前缀和、枚举


🔍 题目理解

给定一个 m x n 的整数矩阵 grid菱形和 指的是菱形边界上的元素之和。菱形定义为正方形旋转45度,四个角必须落在格子上,且可以为"面积为0"的单点菱形。

要求:返回矩阵中 三个最大的、互不相同的 菱形和,按降序排列。

📌 关键要点

  1. 只计算菱形的边界,不包含内部
  2. 菱形边长可以为0(即单个点)
  3. 结果要去重,不足3个时返回全部

💡 解题思路:暴力枚举 + TreeSet维护

核心思想

数据范围较小(m, n ≤ 100),可以直接枚举所有可能的菱形

步骤拆解:

1️⃣ 枚举菱形中心 + 半径

  • 遍历每个格子 (i, j) 作为菱形的中心点

  • 枚举菱形的"半径" k(从0开始,表示边长)

  • 对于半径为 k 的菱形,四个顶点坐标为:

    复制代码
    上: (i-k, j)     下: (i+k, j)
    左: (i, j-k)     右: (i, j+k)

2️⃣ 边界合法性检查

java 复制代码
// 确保四个顶点都在矩阵范围内
if (i - k < 0 || i + k >= m || j - k < 0 || j + k >= n) {
    break;  // 更大半径肯定也越界
}

3️⃣ 计算菱形边界和

  • 半径为0:直接取 grid[i][j]
  • 半径>0:沿四条边累加(注意四个顶点只算一次)

4️⃣ 维护前三大不重复值

  • 使用 TreeSet 自动去重 + 排序,保持大小不超过3

🧩 代码实现(Java)

java 复制代码
class Solution {
    public int[] getBiggestThree(int[][] grid) {
        int m = grid.length, n = grid[0].length;
        // TreeSet自动去重+升序排列,我们最后反转即可
        TreeSet<Integer> results = new TreeSet<>();
        
        // 枚举每个点作为菱形中心
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                // 半径为0的菱形(单点)
                results.add(grid[i][j]);
                // 保持最多3个元素
                if (results.size() > 3) {
                    results.pollFirst();  // 移除最小值
                }
                
                // 枚举更大半径
                int k = 1;
                while (i - k >= 0 && i + k < m && j - k >= 0 && j + k < n) {
                    // 计算菱形边界和
                    int sum = 0;
                    
                    // 上→右:从 (i-k, j) 到 (i, j+k)
                    for (int step = 0; step < k; step++) {
                        sum += grid[i - k + step][j + step];
                    }
                    // 右→下:从 (i, j+k) 到 (i+k, j)
                    for (int step = 0; step < k; step++) {
                        sum += grid[i + step][j + k - step];
                    }
                    // 下→左:从 (i+k, j) 到 (i, j-k)
                    for (int step = 0; step < k; step++) {
                        sum += grid[i + k - step][j - step];
                    }
                    // 左→上:从 (i, j-k) 到 (i-k, j)
                    for (int step = 0; step < k; step++) {
                        sum += grid[i - step][j - k + step];
                    }
                    
                    results.add(sum);
                    if (results.size() > 3) {
                        results.pollFirst();
                    }
                    
                    k++;
                }
            }
        }
        
        // 转为降序数组返回
        int[] ans = new int[results.size()];
        int idx = ans.length - 1;
        for (int num : results) {
            ans[idx--] = num;
        }
        return ans;
    }
}

⚡ 优化技巧:前缀和加速(可选)

如果追求极致性能,可预处理对角线前缀和

java 复制代码
// 预处理两条对角线方向的前缀和
// diag1: 左上→右下,diag2: 右上→左下
int[][] diag1 = new int[m + 1][n + 1];
int[][] diag2 = new int[m + 1][n + 1];

for (int i = 0; i < m; i++) {
    for (int j = 0; j < n; j++) {
        diag1[i + 1][j + 1] = grid[i][j] + diag1[i][j];
        diag2[i + 1][j] = grid[i][j] + diag2[i][j + 1];
    }
}

这样计算任意对角线段和可做到 O(1),整体复杂度从 O(mnk²) 降至 O(mnk)。


📊 复杂度分析

方法 时间复杂度 空间复杂度
暴力枚举 O(m·n·min(m,n)²) O(1)(不计结果集)
+前缀和优化 O(m·n·min(m,n)) O(m·n)

💡 实际测试中,由于数据范围小,暴力法已足够通过


🎯 总结 & 易错点

收获

  • 几何枚举题的坐标变换技巧
  • Java中 TreeSet 的使用:pollFirst() 移除最小值,迭代器默认升序
  • "边界和"与"区域和"的区别

易错点

  1. 菱形顶点重复计算(每条边累加时注意端点)
  2. 半径枚举的边界条件(while 而非 for 更易控制)
  3. 结果去重 + 降序输出的细节(TreeSet转数组时注意顺序)
相关推荐
observe1012 小时前
软件综合项目笔记
笔记
今儿敲了吗2 小时前
41| 快速乘
数据结构·c++·笔记·学习·算法
ysa0510302 小时前
树的定向(dfs并查集贪心)
数据结构·c++·笔记·算法·深度优先·图论
花间相见3 小时前
【JAVA基础11】—— 吃透原码、反码、补码:计算机数值表示的底层逻辑
java·开发语言·笔记
巧克力味的桃子3 小时前
最长连续因子问题 - C语言学习笔记
c语言·笔记·学习
TracyCoder1233 小时前
LeetCode Hot100(70/100)—— 322. 零钱兑换
算法·leetcode·职场和发展
朗迹 - 张伟4 小时前
UE5 Road Creator Pro 插件学习笔记
笔记·学习·ue5
暴躁小师兄数据学院4 小时前
【WEB3.0零基础转行笔记】Go编程篇-第11讲:Gin框架
笔记·golang·web3·区块链·智能合约
雷工笔记4 小时前
小笔记|常读常新
笔记
Mr.Cheng.4 小时前
【InternVL2-2B】MLLM内部架构学习笔记
笔记·学习·自然语言处理