Problem: 2943. 最大化网格图中正方形空洞的面积
文章目录
- 整体思路
-
-
- [1. 核心逻辑](#1. 核心逻辑)
- [2. 算法改进: O ( N ) O(N) O(N) 查找最长连续序列](#2. 算法改进: O ( N ) O(N) O(N) 查找最长连续序列)
-
- 完整代码
- 时空复杂度
-
-
- [1. 时间复杂度: O ( H + V ) O(H + V) O(H+V)](#1. 时间复杂度: O ( H + V ) O(H + V) O(H+V))
- [2. 空间复杂度: O ( H + V ) O(H + V) O(H+V)](#2. 空间复杂度: O ( H + V ) O(H + V) O(H+V))
-
整体思路
1. 核心逻辑
这段代码解决的问题与上一段完全相同,依然是通过计算水平和垂直方向移除栏杆后形成的最大连续空隙来确定最大正方形边长。
公式不变: S i d e = min ( MaxH , MaxV ) + 1 Side = \min(\text{MaxH}, \text{MaxV}) + 1 Side=min(MaxH,MaxV)+1。
2. 算法改进: O ( N ) O(N) O(N) 查找最长连续序列
之前的实现使用了排序 ( O ( N log N ) O(N \log N) O(NlogN)),这里改用 哈希集合 (HashSet) 来实现 O ( N ) O(N) O(N) 的查找。这与解决 LeetCode 128 (Longest Consecutive Sequence) 的经典算法一致。
- 逻辑步骤 :
- 去重与快速查找 :将所有栏杆编号放入
HashSet中。这一步同时也完成了去重(虽然题目通常保证栏杆不重复)。 - 寻找序列起点 :遍历集合中的每个元素
x。- 检查
x - 1是否存在。 - 如果
x - 1存在,说明x并不是一个连续序列的起点 。我们跳过它,因为包含x的那个序列肯定已经从更小的数字(比如x-1或更小)开始计算过了。 - 如果
x - 1不存在,说明x是一个新的连续序列的开头。
- 检查
- 延伸序列 :从起点
x开始,不断检查x+1,x+2... 是否在集合中,直到断裂。 - 更新最大值 :计算该序列长度,更新全局最大值
ans。
- 去重与快速查找 :将所有栏杆编号放入
完整代码
java
import java.util.HashSet;
import java.util.Set;
class Solution {
public int maximizeSquareHoleArea(int n, int m, int[] hBars, int[] vBars) {
// 逻辑与之前相同:
// 1. 计算水平和垂直方向上移除栏杆形成的最长连续区间
// 2. 边长取两者最小值 + 1(因为移除 k 个栏杆得到 k+1 的长度)
int l = Math.min(maxConsecutive(hBars), maxConsecutive(vBars)) + 1;
// 返回面积
return l * l;
}
// 辅助函数:使用 HashSet 计算数组中最长连续序列的长度
// 复杂度优化为 O(N)
private int maxConsecutive(int[] nums) {
// 1. 将所有元素放入 HashSet,实现 O(1) 查找
Set<Integer> st = new HashSet<>();
for (int num : nums) {
st.add(num);
}
int ans = 1; // 初始化为 1,只要数组非空,至少有长度 1
// 如果题目允许输入空数组,这里应处理 ans = 0 的情况。
// 但本题约束通常保证至少有一个栏杆。
// 2. 遍历集合中的每个元素
for (int x : st) {
// 核心剪枝逻辑:
// 如果集合中包含 x-1,说明 x 肯定不是连续序列的起点。
// 我们只在遇到序列起点时才开始计数,避免重复计算。
// 例如序列 [2, 3, 4],遍历到 3 和 4 时都会跳过,只有遍历到 2 时才进入 while。
if (st.contains(x - 1)) {
continue;
}
// 3. 从起点开始向后延伸查找
int y = x + 1;
while (st.contains(y)) {
y++;
}
// 4. 更新最大长度
// 序列是 [x, x+1, ..., y-1],长度为 (y-1) - x + 1 = y - x
ans = Math.max(ans, y - x);
}
return ans;
}
}
时空复杂度
假设 hBars 的长度为 H H H,vBars 的长度为 V V V。
1. 时间复杂度: O ( H + V ) O(H + V) O(H+V)
- 计算依据 :
maxConsecutive函数中:- 将数组转入 HashSet:遍历一次数组,耗时 O ( N ) O(N) O(N)。
- 遍历 HashSet:虽然外层有个
for循环,内层有个while循环,看起来像 O ( N 2 ) O(N^2) O(N2),但实际上每个数字最多被访问两次 。- 第一次:在外层
for循环中作为x被访问。 - 第二次:在内层
while循环中作为y被访问(仅当它是某个序列的一部分时)。 - 由于
if (st.contains(x - 1))的存在,每个连续序列只会被其头部元素触发一次while循环。
- 第一次:在外层
- 因此,
maxConsecutive(hBars)耗时 O ( H ) O(H) O(H),maxConsecutive(vBars)耗时 O ( V ) O(V) O(V)。
- 结论 : O ( H + V ) O(H + V) O(H+V)。相比排序解法 O ( N log N ) O(N \log N) O(NlogN),这在理论上更优。
2. 空间复杂度: O ( H + V ) O(H + V) O(H+V)
- 计算依据 :
- 我们需要两个
HashSet来存储hBars和vBars中的所有元素。 - 空间消耗与输入数组的长度成线性关系。
- 我们需要两个
- 结论 : O ( H + V ) O(H + V) O(H+V)。
- 对比 :排序解法的空间复杂度为 O ( log N ) O(\log N) O(logN)(栈空间),这里牺牲了空间换取了时间。