力扣1001网格照明解法

力扣1001 网格照明 题解

问题分析

网格照明问题(LeetCode 1001)要求模拟在 n x n 网格上的一系列操作:放置灯和查询位置是否被照亮。核心挑战在于高效处理大规模网格(n 可达 10^9)和大量操作(lamps 和 queries 长度可达 2 * 10^4)。直接存储和遍历网格的二维数组显然不可行。

问题的关键在于理解:一盏灯会照亮其所在的行、列和两条对角线。因此,我们可以通过维护四个哈希表来分别记录被照亮的行、列和两条对角线的状态,从而将空间复杂度从 O(n²) 降低到 O(L + Q),其中 L 是灯的数量,Q 是查询的数量。

算法思路与数据结构设计

1. 核心数据结构

使用四个哈希表(在C++中为 unordered_map)来分别记录:

  • row:行索引 -> 该行上亮着的灯的数量。
  • col:列索引 -> 该列上亮着的灯的数量。
  • diag1:主对角线(左上-右下)索引 -> 该对角线上亮着的灯的数量。对角线索引可通过 行索引 - 列索引 计算。
  • diag2:副对角线(右上-左下)索引 -> 该对角线上亮着的灯的数量。对角线索引可通过 行索引 + 列索引 计算。

此外,需要一个快速查找灯位置的数据结构,用于在关闭周边灯时判断该位置是否真的有灯。使用 unordered_set 存储灯的唯一位置编码(例如 长整型字符串)是高效的选择。

2. 算法步骤

  1. 初始化 :将所有灯的位置加入 灯位置集合,并更新四个哈希表的计数。
  2. 处理查询
    • 对于每个查询位置 (query_x, query_y),检查四个哈希表中对应的计数是否大于0。只要有一个大于0,则该位置被照亮,答案记录为1,否则为0。
    • 随后,关闭 查询位置及其周围8个相邻格子(九宫格)内的所有灯。
      • 遍历九宫格内的每个位置 (nx, ny)
      • 如果 (nx, ny)灯位置集合 中,则将其从集合中移除,并将四个哈希表中对应的计数减1(如果减到0,可以选择删除该键值对以节省空间)。
  3. 返回结果:返回所有查询结果的数组。

C++ 代码实现

cpp 复制代码
#include <vector>
#include <unordered_map>
#include <unordered_set>
using namespace std;

class Solution {
public:
    vector<int> gridIllumination(int n, vector<vector<int>>& lamps, vector<vector<int>>& queries) {
        // 核心数据结构初始化
        unordered_map<int, int> row, col, diag1, diag2;
        // 使用长整型编码灯的位置,避免字符串操作开销
        unordered_set<long long> lampSet;
        // 方向数组,表示查询位置周围的8个邻居和自身(共9个位置)
        int dirs[9][2] = {{0,0}, {0,1}, {0,-1}, {1,0}, {-1,0}, {1,1}, {1,-1}, {-1,1}, {-1,-1}};

        // 步骤1:放置所有灯,初始化哈希表
        for (const auto& lamp : lamps) {
            int x = lamp[0];
            int y = lamp[1];
            long long key = (long long)x * n + y; // 将二维坐标编码为一维唯一键
            // 防止重复的灯被重复添加
            if (lampSet.insert(key).second) {
                row[x]++;
                col[y]++;
                diag1[x - y]++; // 主对角线索引
                diag2[x + y]++; // 副对角线索引
            }
        }

        vector<int> ans;
        ans.reserve(queries.size()); // 预分配空间提升效率

        // 步骤2:处理每个查询
        for (const auto& query : queries) {
            int x = query[0];
            int y = query[1];

            // 判断当前位置是否被照亮:检查对应的行、列、对角线是否有灯
            bool isLit = (row[x] > 0) || (col[y] > 0) || (diag1[x - y] > 0) || (diag2[x + y] > 0);
            ans.push_back(isLit ? 1 : 0);

            // 步骤3:关闭九宫格内的灯
            for (const auto& dir : dirs) {
                int nx = x + dir[0];
                int ny = y + dir[1];
                // 检查新坐标是否在网格范围内
                if (nx >= 0 && nx < n && ny >= 0 && ny < n) {
                    long long key = (long long)nx * n + ny;
                    // 如果该位置有灯,则关闭它
                    if (lampSet.erase(key)) {
                        // 从哈希表中移除该灯的影响
                        if (--row[nx] == 0) row.erase(nx);
                        if (--col[ny] == 0) col.erase(ny);
                        if (--diag1[nx - ny] == 0) diag1.erase(nx - ny);
                        if (--diag2[nx + ny] == 0) diag2.erase(nx + ny);
                    }
                }
            }
        }
        return ans;
    }
};

复杂度分析

维度 复杂度 说明
时间复杂度 O(L + Q) 其中 L 是 lamps 的长度,Q 是 queries 的长度。放置灯为 O(L),每个查询的检查和关灯操作是常数时间(最多检查9个位置)。
空间复杂度 O(L) 主要用于存储 lampSet 和四个哈希表,最坏情况下都与灯的数量 L 成正比。

关键点与技巧总结

  1. 坐标编码 :将二维坐标 (x, y) 编码为 (long long)x * n + y,确保在 unordered_set 中唯一且高效。
  2. 对角线索引计算
    • 主对角线(斜率1):索引值为 x - y。同一条主对角线上的点,此值相同。
    • 副对角线(斜率-1):索引值为 x + y。同一条副对角线上的点,此值相同。
  3. 避免重复灯 :使用 unordered_setinsert 方法返回值来判断灯是否已存在,防止哈希表计数错误增加。
  4. 哈希表清理 :当某个行、列或对角线的灯数量减为0时,使用 erase 方法删除该键,有助于减少哈希表的大小,提升后续查询效率(尽管不是必须的)。
  5. 方向遍历 :使用预定义的方向数组 dirs 使代码更简洁,避免手动书写9个条件判断。

此解法通过将空间状态从网格转换到线性的哈希表,巧妙地解决了超大网格带来的内存限制问题,是典型的利用数据结构降维思想的案例。


参考来源

相关推荐
川石课堂软件测试1 小时前
作为一名测试工程师如何学习Kubernetes(k8s)技能
学习·测试工具·容器·职场和发展·kubernetes·测试用例·harmonyos
fengenrong1 小时前
20260601
算法·深度优先·图论
晚笙coding1 小时前
从“看起来像双指针”到真正的动态规划 —— 最长公共子序列
算法·动态规划
05候补工程师1 小时前
【考研高数核心突破】极限的本质、高频解题套路与海涅定理深度解析(附经典例题思维导图式拆解)
经验分享·笔记·考研·算法
智者知已应修善业1 小时前
【51单片机8个LED的花样12亮34熄56间隔78闪烁3秒3闪烁】2023-11-4
c++·经验分享·笔记·算法·51单片机
老鱼说AI2 小时前
统计学习方法第五章:从浅入深解析决策树
人工智能·深度学习·算法·决策树·机器学习·学习方法
KaMeidebaby2 小时前
卡梅德生物技术快报|蛋白修饰调控 NETosis 分子机制及实验研究进展
前端·数据库·人工智能·算法·百度
初中就开始混世的大魔王2 小时前
5 Fast DDS-Discovery
网络·c++·算法·中间件
Deep-w2 小时前
【MATLAB】基于模型预测控制的自适应巡航车辆过渡工况安全控制研究
开发语言·人工智能·算法·机器学习·matlab