【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)(栈空间),这里牺牲了空间换取了时间。
相关推荐
草履虫建模3 小时前
力扣算法 1768. 交替合并字符串
java·开发语言·算法·leetcode·职场和发展·idea·基础
naruto_lnq5 小时前
分布式系统安全通信
开发语言·c++·算法
Jasmine_llq5 小时前
《P3157 [CQOI2011] 动态逆序对》
算法·cdq 分治·动态问题静态化+双向偏序统计·树状数组(高效统计元素大小关系·排序算法(预处理偏序和时间戳)·前缀和(合并单个贡献为总逆序对·动态问题静态化
爱吃rabbit的mq6 小时前
第09章:随机森林:集成学习的威力
算法·随机森林·集成学习
(❁´◡`❁)Jimmy(❁´◡`❁)7 小时前
Exgcd 学习笔记
笔记·学习·算法
YYuCChi7 小时前
代码随想录算法训练营第三十七天 | 52.携带研究材料(卡码网)、518.零钱兑换||、377.组合总和IV、57.爬楼梯(卡码网)
算法·动态规划
不能隔夜的咖喱7 小时前
牛客网刷题(2)
java·开发语言·算法
VT.馒头7 小时前
【力扣】2721. 并行执行异步函数
前端·javascript·算法·leetcode·typescript
进击的小头8 小时前
实战案例:51单片机低功耗场景下的简易滤波实现
c语言·单片机·算法·51单片机
咖丨喱9 小时前
IP校验和算法解析与实现
网络·tcp/ip·算法