【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 来存储 hBarsvBars 中的所有元素。
    • 空间消耗与输入数组的长度成线性关系。
  • 结论 : O ( H + V ) O(H + V) O(H+V)。
    • 对比 :排序解法的空间复杂度为 O ( log ⁡ N ) O(\log N) O(logN)(栈空间),这里牺牲了空间换取了时间。
相关推荐
百度搜不到…4 小时前
背包问题递推公式中的dp[j-nums[j]]到底怎么理解
算法·leetcode·动态规划·背包问题
一起养小猫4 小时前
LeetCode100天Day13-移除元素与多数元素
java·算法·leetcode
ACERT3334 小时前
10.吴恩达机器学习——无监督学习01聚类与异常检测算法
python·算法·机器学习
诗词在线4 小时前
从算法重构到场景复用:古诗词数字化的技术破局与落地实践
python·算法·重构
不穿格子的程序员4 小时前
从零开始写算法——二叉树篇7:从前序与中序遍历序列构造二叉树 + 二叉树的最近公共祖先
数据结构·算法
hetao17338374 小时前
2026-01-12~01-13 hetao1733837 的刷题笔记
c++·笔记·算法
无限码力4 小时前
美团秋招笔试真题 - 放它一马 & 信号模拟
算法·美团秋招·美团笔试·美团笔试真题
qq_433554545 小时前
C++ 图论算法:强连通分量
c++·算法·图论
开开心心就好5 小时前
内存清理工具显示内存,优化释放自动清理
java·linux·开发语言·网络·数据结构·算法·电脑