ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

LeetCode 1878题解:斜向前缀和优化菱形和计算

LeetCode 1878题解:斜向前缀和优化菱形和计算

1. 问题背景与核心概念解析

这道LeetCode 1878题要求我们在一个m×n的整数矩阵中,找出所有可能的"菱形和"中的前三大值。这里的"菱形"特指正方形旋转45度后的图形,其四个角必须位于矩阵网格点上。举个例子,在一个3×3矩阵中,中心点可以构成边长为1的菱形;而在5×5矩阵中,中心点可以构成边长为1和2的菱形。

菱形和的计算是这个问题的核心难点。不同于常规的矩形区域求和,菱形边界上的元素分布呈现对角线特征。对于一个边长为k的菱形,其边界元素位于四条对角线上:

  • 从中心点向上/下/左/右延伸k步的对角线
  • 每条边包含2k-1个元素(中心点被四个边共享)

2. 暴力解法与复杂度分析

最直观的解法是暴力枚举所有可能的菱形位置和大小,然后计算每个菱形的边界和。具体步骤:

  1. 遍历矩阵中每个点作为菱形中心
  2. 对于每个中心点,枚举可能的菱形边长k
  3. 对每个k值,收集菱形四条边上的元素并求和
  4. 维护一个大小为3的最大堆,记录前三大菱形和

这种方法的时间复杂度很高。假设矩阵尺寸为n×n:

  • 中心点选择:O(n²)
  • 每个中心可能的k值:O(n)
  • 每个菱形的边界元素收集:O(n) 总复杂度达到O(n⁴),对于n=100的情况(如题目上限)会有1亿次操作,显然无法通过测试用例。

3. 斜向前缀和优化策略

斜向前缀和是解决此类对角线求和问题的利器。我们需要构建两种斜线方向的前缀和数组:

3.1 斜线方向定义

  • 主对角线方向(左上到右下):同一斜线上i-j为常数
  • 副对角线方向(右上到左下):同一斜线上i+j为常数

3.2 前缀和数组构建

对于每个方向,我们构建一个二维前缀和数组:

# 主对角线方向前缀和 diag1 = [[0]*(n+1) for _ in range(m+1)] for i in range(1, m+1): for j in range(1, n+1): diag1[i][j] = diag1[i-1][j-1] + grid[i-1][j-1] # 副对角线方向前缀和 diag2 = [[0]*(n+1) for _ in range(m+1)] for i in range(1, m+1): for j in range(n-1, -1, -1): diag2[i][j] = diag2[i-1][j+1] + grid[i-1][j]

3.3 菱形边界求和优化

利用前缀和数组,我们可以在O(1)时间内获取任意斜线段的区间和。对于菱形的一条边,可以分解为两段斜线:

  • 例如右上边:从顶点向右下的斜线段 + 从顶点向左下的斜线段

具体计算公式:

def get_diagonal_sum(diag, i1, j1, i2, j2): # 确保i1<=i2,根据方向调整参数 return diag[i2][j2] - diag[i1-1][j1-1] # 示例:计算边长为k的菱形右上边和 # 假设中心在(i,j) right_top = get_diagonal_sum(diag1, i-k, j, i, j+k) + \ get_diagonal_sum(diag2, i, j+k, i+k, j)

4. 完整算法实现

结合上述优化,我们可以将算法复杂度降低到O(n³)。以下是Python实现的关键部分:

def getBiggestThree(grid): m, n = len(grid), len(grid[0]) res = set() # 构建两种斜向前缀和 diag1 = [[0]*(n+2) for _ in range(m+2)] diag2 = [[0]*(n+2) for _ in range(m+2)] for i in range(1, m+1): for j in range(1, n+1): diag1[i][j] = diag1[i-1][j-1] + grid[i-1][j-1] for i in range(1, m+1): for j in range(n, 0, -1): diag2[i][j] = diag2[i-1][j+1] + grid[i-1][j-1] # 枚举所有可能的菱形 for i in range(m): for j in range(n): res.add(grid[i][j]) # 边长为0的情况 max_k = min(i, j, m-1-i, n-1-j) for k in range(1, max_k+1): # 计算四条边的和 top = diag1[i-k+1][j+1] - diag1[i][j-k] right = diag2[i+1][j+k+1] - diag2[i-k][j] bottom = diag1[i+k][j+1] - diag1[i][j-k] left = diag2[i+1][j-k+1] - diag2[i+k][j] total = top + right + bottom + left - \ (grid[i-k][j] + grid[i][j+k] + grid[i+k][j] + grid[i][j-k]) res.add(total) # 返回前三大 res = sorted(res, reverse=True) return res[:3] if len(res) >=3 else res

5. 边界条件与优化技巧

在实际编码中,有几个关键点需要注意:

  1. 边界处理:前缀和数组通常比原矩阵多一圈,避免越界访问。我们使用(m+2)×(n+2)的数组,从(1,1)开始存储有效数据。

  2. 去重处理:使用集合来存储所有可能的菱形和,自动处理重复值。

  3. k值范围:对于中心点(i,j),最大可能的k值为min(i,j,m-1-i,n-1-j),确保菱形不超出矩阵范围。

  4. 顶点去重:四条边的和会重复计算四个顶点的值,需要在总和时减去。

  5. 结果处理:题目要求返回前三大值,当不足三个时返回所有存在的值。

6. 复杂度分析与对比

优化前后的复杂度对比:

  • 暴力法:O(n⁴)时间,O(1)空间
  • 斜向前缀和法:O(n³)时间,O(n²)空间

对于n=100的情况:

  • 暴力法:约1亿次操作(不可行)
  • 优化法:约100万次操作(可行)

空间方面,我们使用了两个额外的n×n数组存储前缀和,空间复杂度为O(n²),在合理范围内。

7. 实际编码中的常见错误

在实现这个算法时,容易犯的几个错误:

  1. 前缀和索引混淆:原矩阵从0开始计数,而前缀和数组通常从1开始,容易在索引转换时出错。建议在纸上画出对应关系。

  2. 菱形边计算遗漏:容易漏掉边长为0的情况(即单个点),或者忘记减去重复计算的顶点值。

  3. k值范围错误:最大k值计算错误会导致数组越界。正确的计算方式是取四个方向可用空间的最小值。

  4. 结果去重与排序:忘记使用集合去重,或者在返回结果时没有正确处理不足三个值的情况。

  5. 前缀和初始化:没有正确初始化前缀和数组的边界值,导致后续计算错误。

8. 测试用例设计

为了验证代码的正确性,应该设计以下几类测试用例:

  1. 最小矩阵测试:
grid = [[1]] # 预期输出:[1]
  1. 全等元素测试:
grid = [[2,2],[2,2]] # 预期输出:[2]
  1. 明显菱形测试:
grid = [ [1,2,3], [4,5,6], [7,8,9] ] # 中心菱形和为8+5+2+4+6=25(如果边长为1)
  1. 多个菱形竞争测试:
grid = [ [3,4,5,1,3], [3,3,4,2,3], [2,4,4,2,4], [2,4,5,3,1], [3,1,3,4,1] ] # 应能正确识别前三大菱形和
  1. 边界情况测试:
grid = [ [1,1,1], [1,1,1], [1,1,1] ] # 所有可能的菱形和都相同

9. 算法扩展与变种

这个问题可以延伸出几个有趣的变种:

  1. 最大单个菱形和:只需找最大的一个菱形和,可以用类似方法但只需维护一个最大值。

  2. 菱形内部和:计算菱形内部所有元素的和,而不仅是边界。这时需要结合矩形前缀和与斜向前缀和。

  3. 非正方形菱形:放宽菱形定义,允许长轴和短轴不同的菱形,需要调整边界计算方式。

  4. 动态更新查询:在矩阵元素可以动态更新的情况下,如何高效维护菱形和查询。这需要更高级的数据结构如二维线段树。

  5. 三维菱形和:将问题扩展到三维空间,计算立方体中旋转45度的菱形体和。需要构建三维斜向前缀和。

10. 实际应用场景

虽然这个问题看起来是纯数学的,但其核心思想在实际中有重要应用:

  1. 图像处理:在边缘检测和特征提取中,需要计算特定方向上的像素值变化。斜向前缀和可以加速对角线方向的特征计算。

  2. 地理信息系统:计算不规则区域(如菱形辐射区)内的统计量时,类似的技术可以优化计算效率。

  3. 游戏开发:在战棋类游戏中,计算角色技能的影响范围(如菱形攻击范围)和伤害值时,需要快速求和相关区域。

  4. 数据挖掘:在分析二维数据分布时,可能需要检测特定方向的异常模式,斜向求和可以帮助快速定位。

  5. 数值计算:在求解偏微分方程时,特定方向的差分计算可以转化为斜向区间求和问题。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进