【LeetCode 每日一题】2943. 最大化网格图中正方形空洞的面积——(解法二)哈希集合

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) 的经典算法一致。

  • 逻辑步骤 :
    1. 去重与快速查找 :将所有栏杆编号放入 HashSet 中。这一步同时也完成了去重(虽然题目通常保证栏杆不重复)。
    2. 寻找序列起点 :遍历集合中的每个元素 x。
      • 检查 x - 1 是否存在。
      • 如果 x - 1 存在,说明 x 并不是一个连续序列的起点 。我们跳过它,因为包含 x 的那个序列肯定已经从更小的数字(比如 x-1 或更小)开始计算过了。
      • 如果 x - 1 不存在,说明 x 是一个新的连续序列的开头。
    3. 延伸序列 :从起点 x 开始,不断检查 x+1, x+2... 是否在集合中,直到断裂。
    4. 更新最大值 :计算该序列长度,更新全局最大值 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)(栈空间),这里牺牲了空间换取了时间。
相关推荐
dialect_24 分钟前
KMP算法
开发语言·c++·算法
reikocao32 分钟前
卡尔曼滤波的动态机制:P如何改变融合权重
算法
漂流瓶jz1 小时前
UVA-1612 猜名次 题解答案代码 算法竞赛入门经典第二版
数据结构·算法·题解·排序·aoapc·算法竞赛入门经典
吞下星星的少年·-·2 小时前
Div.2 CF 1130B题(构造,贪心)
算法·贪心·构造
文丑颜不良啊2 小时前
LeetCode1481~1500题目解析
java·leetcode
文丑颜不良啊2 小时前
LeetCode1381~1400题目解析
java·leetcode
是main不是漫3 小时前
【双指针——移动零】数组划分类题目的入门基石
c++·算法
深圳市快瞳科技有限公司4 小时前
野外鸟类AI识别落地实录:输入适配、结构化输出、去重聚合与置信度分流的工程实践
算法·计算机视觉·鸟类识别·鸟类ai识别·野外鸟类观测·林业调查
专注仿真5 小时前
梯度提升类(XFBoost)算法详解与代码实例
开发语言·人工智能·python·算法·大模型训练·控制算法
小豆庄6 小时前
视频遮罩追踪退场后ID撞号的排查—多目标场景下的失效
opencv·算法·bug·视频·python3.11