【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)(栈空间),这里牺牲了空间换取了时间。
相关推荐
那个村的李富贵6 小时前
CANN加速下的AIGC“即时翻译”:AI语音克隆与实时变声实战
人工智能·算法·aigc·cann
power 雀儿6 小时前
Scaled Dot-Product Attention 分数计算 C++
算法
琹箐6 小时前
最大堆和最小堆 实现思路
java·开发语言·算法
renhongxia17 小时前
如何基于知识图谱进行故障原因、事故原因推理,需要用到哪些算法
人工智能·深度学习·算法·机器学习·自然语言处理·transformer·知识图谱
坚持就完事了7 小时前
数据结构之树(Java实现)
java·算法
算法备案代理7 小时前
大模型备案与算法备案,企业该如何选择?
人工智能·算法·大模型·算法备案
赛姐在努力.7 小时前
【拓扑排序】-- 算法原理讲解,及实现拓扑排序,附赠热门例题
java·算法·图论
野犬寒鸦8 小时前
从零起步学习并发编程 || 第六章:ReentrantLock与synchronized 的辨析及运用
java·服务器·数据库·后端·学习·算法
霖霖总总8 小时前
[小技巧66]当自增主键耗尽:MySQL 主键溢出问题深度解析与雪花算法替代方案
mysql·算法
rainbow68898 小时前
深入解析C++STL:map与set底层奥秘
java·数据结构·算法