☰
LeetCode 1878题解析:斜向前缀和优化菱形区域求和
2026/11/3 3:04:30 网站建设 项目流程

1. 问题背景与核心挑战

LeetCode 1878题要求我们在一个给定的方形矩阵中,找出所有可能的菱形区域,并计算每个菱形区域内元素的和,最终返回其中最大的三个和值。这里的菱形定义为矩阵中由相同中心点向外等距扩展的正方形旋转45度后的形状。

这个问题看似简单,实则暗藏几个关键难点:

  1. 菱形定义与边界处理:不同于常规的正方形区域,菱形在矩阵中的边界条件更为复杂。对于一个n×n的矩阵,有效的菱形大小受到严格限制——菱形的"半径"不能超过矩阵中心到边缘的最大距离。

  2. 暴力解法的时间复杂度:最直观的暴力解法需要枚举所有可能的菱形中心点,对每个中心点枚举所有可能的半径,然后计算对应菱形区域的和。这种方法的时间复杂度高达O(n^4),对于较大的矩阵(如100×100)完全不可行。

  3. 和值计算的重复性:在暴力解法中,相邻菱形区域的和值计算存在大量重叠部分,如何避免重复计算成为优化关键。

2. 斜向前缀和的核心思想

斜向前缀和(Diagonal Prefix Sum)是解决此类区域求和问题的利器。与常规的前缀和不同,斜向前缀和专门针对45度斜线方向进行预处理,使得任意菱形区域的和可以在O(1)时间内得到。

2.1 斜向前缀和的构建方法

构建斜向前缀和矩阵需要两个步骤:

  1. 主对角线方向前缀和:对于矩阵中的每个元素matrix[i][j],我们计算从矩阵左上角到该元素的主对角线方向的前缀和。具体实现如下:
# 构建主对角线方向前缀和 n = len(matrix) prefix1 = [[0]*(n+2) for _ in range(n+2)] for i in range(1, n+1): for j in range(1, n+1): prefix1[i][j] = prefix1[i-1][j-1] + matrix[i-1][j-1]
  1. 副对角线方向前缀和:同样地,我们计算从矩阵右上角到每个元素的副对角线方向的前缀和:
# 构建副对角线方向前缀和 prefix2 = [[0]*(n+2) for _ in range(n+2)] for i in range(1, n+1): for j in range(n, 0, -1): prefix2[i][j] = prefix2[i-1][j+1] + matrix[i-1][j-1]

2.2 菱形区域和的快速计算

有了这两个前缀和矩阵,我们可以用"包含-排除"原理快速计算任意菱形区域的和。对于一个中心在(i,j)、半径为r的菱形:

  1. 主对角线方向:从(i-r,j)到(i,j-r)的对角线
  2. 副对角线方向:从(i-r,j)到(i,j+r)的对角线

菱形区域的和可以通过四个角点的前缀和值组合得到:

def get_rhombus_sum(i, j, r): # 转换为1-based索引 i += 1 j += 1 a = prefix1[i+r][j] - prefix1[i][j-r] b = prefix2[i+r][j] - prefix2[i][j+r] return a + b - matrix[i-1][j-1] # 中心点被计算了两次,需要减去一次

3. 完整算法实现与优化

3.1 算法步骤详解

基于斜向前缀和的完整解决方案包含以下几个步骤:

  1. 预处理阶段:构建主、副对角线方向的前缀和矩阵。
  2. 枚举所有有效菱形:对于矩阵中的每个可能中心点,枚举所有可能的半径。
  3. 计算和值并维护Top3:使用最大堆或有序数组来维护最大的三个和值。
  4. 处理边界条件:确保菱形的边界不超出矩阵范围。

3.2 代码实现

def getBiggestThree(matrix): n = len(matrix) if n == 0: return [] # 构建两个方向的前缀和 prefix1 = [[0]*(n+2) for _ in range(n+2)] # 主对角线方向 prefix2 = [[0]*(n+2) for _ in range(n+2)] # 副对角线方向 for i in range(1, n+1): for j in range(1, n+1): prefix1[i][j] = prefix1[i-1][j-1] + matrix[i-1][j-1] for i in range(1, n+1): for j in range(n, 0, -1): prefix2[i][j] = prefix2[i-1][j+1] + matrix[i-1][j-1] max_sums = set() for i in range(n): for j in range(n): # 半径从0开始(单点)到最大可能半径 max_r = min(i, j, n-1-i, n-1-j) for r in range(0, max_r+1): if r == 0: current_sum = matrix[i][j] else: # 计算菱形四个顶点的和 a = prefix1[i+1+r][j+1] - prefix1[i+1][j+1-r] b = prefix2[i+1+r][j+1] - prefix2[i+1][j+1+r] current_sum = a + b - matrix[i][j] max_sums.add(current_sum) # 取最大的三个和 result = sorted(max_sums, reverse=True)[:3] return result

3.3 复杂度分析

  • 时间复杂度:O(n^3)
    • 前缀和构建:O(n^2)
    • 枚举所有菱形:O(n^3)(最坏情况下)
  • 空间复杂度:O(n^2)(存储前缀和矩阵)

相比暴力解法的O(n^4),这是一个显著的优化。

4. 边界条件与特殊案例处理

在实际编码中,有几个边界条件需要特别注意:

  1. 单元素矩阵:当矩阵只有一个元素时,唯一的菱形和就是该元素本身。
  2. 半径为零的菱形:这种情况实际上就是矩阵中的单个元素。
  3. 矩阵元素全相同:所有菱形和都相同,需要正确去重。
  4. 负数和零值:算法应能正确处理矩阵中的负数和零值。

测试案例示例:

# 案例1:3x3矩阵 matrix1 = [ [3,4,5], [1,2,6], [7,8,9] ] # 预期输出:[17,15,13](或其他顺序) # 案例2:全1矩阵 matrix2 = [ [1,1], [1,1] ] # 预期输出:[1] # 案例3:单元素矩阵 matrix3 = [[5]] # 预期输出:[5]

5. 算法优化与变种思考

5.1 进一步优化空间

虽然斜向前缀和已经将时间复杂度从O(n^4)优化到O(n^3),但对于特别大的矩阵(如1000×1000),这仍然不够高效。可以考虑以下优化方向:

  1. 并行计算:不同中心点的菱形和计算是独立的,可以并行处理。
  2. 半径枚举优化:对于某些中心点,可以提前终止半径的枚举(如果和值开始递减)。
  3. 空间优化:如果只需要Top3结果,可以实时维护而不用存储所有和值。

5.2 问题变种

这个问题可以有多种变种,每种变种可能需要不同的优化策略:

  1. 最大单个菱形和:只需要找最大的一个菱形和,可以用贪心策略提前终止。
  2. 非正方形矩阵:矩阵不是正方形时,菱形的定义需要调整。
  3. 加权菱形和:不同位置的元素有不同的权重系数。
  4. 动态矩阵:矩阵元素会动态更新,需要设计支持快速查询的数据结构。

6. 实际应用场景

虽然这个问题看起来是纯算法题,但其核心思想在实际中有广泛应用:

  1. 图像处理:在图像中检测菱形或旋转正方形的特征。
  2. 数据挖掘:在二维数据中寻找特定模式的区域。
  3. 游戏开发:处理菱形地图或斜视角游戏中的区域计算。
  4. 地理信息系统:计算不规则地理区域内的统计量。

斜向前缀和的技术也可以推广到其他方向的区域求和,如任意角度的旋转矩形、圆形区域等。掌握这种预处理技巧,可以大幅提高类似问题的解决效率。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询