哈希表题目:网格照明

news2024/9/21 6:26:08

文章目录

  • 题目
    • 标题和出处
    • 难度
    • 题目描述
      • 要求
      • 示例
      • 数据范围
  • 解法
    • 思路和算法
    • 代码
    • 复杂度分析

题目

标题和出处

标题:网格照明

出处:1001. 网格照明

难度

6 级

题目描述

要求

n × n \texttt{n} \times \texttt{n} n×n 的二维网格 grid \texttt{grid} grid 上,每个单元格都有一盏灯,最初灯都处于关闭状态。

给你一个二维数组 lamps \texttt{lamps} lamps 表示灯的位置,其中 lamps[i]   =   [row i ,   col i ] \texttt{lamps[i] = [row}_\texttt{i}\texttt{, col}_\texttt{i}\texttt{]} lamps[i] = [rowi, coli] 表示打开位于 grid[row i ][col i ] \texttt{grid[row}_\texttt{i}\texttt{][col}_\texttt{i}\texttt{]} grid[rowi][coli] 的灯。即使同一盏灯在数组中出现多次,也是打开这盏灯。

当一盏灯打开时,会照亮自身单元格以及同一行、同一列和两条对角线上的所有其他单元格。

给你另一个二维数组 queries \texttt{queries} queries,其中 queries[j]   =   [row j ,   col j ] \texttt{queries[j] = [row}_\texttt{j}\texttt{, col}_\texttt{j}\texttt{]} queries[j] = [rowj, colj]。对于第 j \texttt{j} j 次查询,判断 grid[row j ][col j ] \texttt{grid[row}_\texttt{j}\texttt{][col}_\texttt{j}\texttt{]} grid[rowj][colj] 是否被照亮。在回答第 j \texttt{j} j 次查询之后,关闭位于 grid[row j ][col j ] \texttt{grid[row}_\texttt{j}\texttt{][col}_\texttt{j}\texttt{]} grid[rowj][colj] 的灯和相邻的 8 \texttt{8} 8 盏灯(如果存在)。如果一盏灯和 grid[row j ][col j ] \texttt{grid[row}_\texttt{j}\texttt{][col}_\texttt{j}\texttt{]} grid[rowj][colj] 共享边或角,则这盏灯是相邻的。

返回答案数组 ans \texttt{ans} ans,如果第 j \texttt{j} j 次查询的单元格被照亮,则 ans[j] \texttt{ans[j]} ans[j] 1 \texttt{1} 1,否则 ans[j] \texttt{ans[j]} ans[j] 0 \texttt{0} 0

示例

示例 1:

示例 1 图 1

输入: n   =   5,   lamps   =   [[0,0],[4,4]],   queries   =   [[1,1],[1,0]] \texttt{n = 5, lamps = [[0,0],[4,4]], queries = [[1,1],[1,0]]} n = 5, lamps = [[0,0],[4,4]], queries = [[1,1],[1,0]]
输出: [1,0] \texttt{[1,0]} [1,0]
解释:最初所有灯都是关闭的。上图中可以看到打开位于 grid[0][0] \texttt{grid[0][0]} grid[0][0] 的灯然后打开位于 grid[4][4] \texttt{grid[4][4]} grid[4][4] 的灯之后的网格。
0 \texttt{0} 0 次查询检查 grid[1][1] \texttt{grid[1][1]} grid[1][1] 是否被照亮(蓝色方框)。该单元格被照亮,所以 ans[0]   =   1 \texttt{ans[0] = 1} ans[0] = 1。然后,关闭红色方框中的所有灯。
示例 1 图 2
1 \texttt{1} 1 次查询检查 grid[1][0] \texttt{grid[1][0]} grid[1][0] 是否被照亮(蓝色方框)。该单元格没有被照亮,所以 ans[1]   =   0 \texttt{ans[1] = 0} ans[1] = 0。然后,关闭红色矩形中的所有灯。
示例 1 图 3

示例 2:

输入: n   =   5,   lamps   =   [[0,0],[4,4]],   queries   =   [[1,1],[1,1]] \texttt{n = 5, lamps = [[0,0],[4,4]], queries = [[1,1],[1,1]]} n = 5, lamps = [[0,0],[4,4]], queries = [[1,1],[1,1]]
输出: [1,1] \texttt{[1,1]} [1,1]

示例 3:

输入: n   =   5,   lamps   =   [[0,0],[0,4]],   queries   =   [[0,4],[0,1],[1,4]] \texttt{n = 5, lamps = [[0,0],[0,4]], queries = [[0,4],[0,1],[1,4]]} n = 5, lamps = [[0,0],[0,4]], queries = [[0,4],[0,1],[1,4]]
输出: [1,1,0] \texttt{[1,1,0]} [1,1,0]

数据范围

  • 1 ≤ n ≤ 10 9 \texttt{1} \le \texttt{n} \le \texttt{10}^\texttt{9} 1n109
  • 0 ≤ lamps.length ≤ 20000 \texttt{0} \le \texttt{lamps.length} \le \texttt{20000} 0lamps.length20000
  • 0 ≤ queries.length ≤ 20000 \texttt{0} \le \texttt{queries.length} \le \texttt{20000} 0queries.length20000
  • lamps[i].length = 2 \texttt{lamps[i].length} = \texttt{2} lamps[i].length=2
  • 0 ≤ row i ,   col i < n \texttt{0} \le \texttt{row}_\texttt{i}\texttt{, col}_\texttt{i} < \texttt{n} 0rowi, coli<n
  • queries[i].length = 2 \texttt{queries[i].length} = \texttt{2} queries[i].length=2
  • 0 ≤ row j ,   col j < n \texttt{0} \le \texttt{row}_\texttt{j}\texttt{, col}_\texttt{j} < \texttt{n} 0rowj, colj<n

解法

思路和算法

每个单元格都位于一行、一列和两条对角线上。一个单元格被照亮,等价于至少有一盏打开的灯和该单元格位于同一行、同一列或者同一条对角线上。因此,为了快速判断一个单元格是否被照亮,需要记录每一行、每一列和两个方向的每一条对角线上打开的灯的数量,可以使用哈希表记录。

维护四个哈希表,分别记录每一行打开的灯的数量、每一列打开的灯的数量、每一条方向一对角线上打开的灯的数量和每一条方向二对角线上打开的灯的数量,其中方向一对角线表示从左上到右下方向的对角线,方向二对角线表示从左下到右上方向的对角线。这四个哈希表分别称为行哈希表、列哈希表、对角线一哈希表和对角线二哈希表。

0 ≤ row , col < n 0 \le \textit{row}, \textit{col} < n 0row,col<n 时,如果位于第 row \textit{row} row 行第 col \textit{col} col 列的灯被打开,则需要将这盏灯存入四个哈希表。

  • 由于灯所在的行是 row \textit{row} row,因此在行哈希表中将 row \textit{row} row 对应的数量加 1 1 1

  • 由于灯所在的列是 col \textit{col} col,因此在列哈希表中将 col \textit{col} col 对应的数量加 1 1 1

  • 由于在方向一对角线上,如果向左上移动一步则 row \textit{row} row col \textit{col} col 同时减 1 1 1,如果向右下移动一步则 row \textit{row} row col \textit{col} col 同时加 1 1 1,因此在同一条方向一对角线上的每个单元格满足 row − col \textit{row} - \textit{col} rowcol 是定值,在对角线一哈希表中将 row − col \textit{row} - \textit{col} rowcol 对应的数量加 1 1 1

  • 由于在方向二对角线上,如果向左下移动一步则 row \textit{row} row 1 1 1 col \textit{col} col 1 1 1,如果向右上移动一步则 row \textit{row} row 1 1 1 col \textit{col} col 1 1 1,因此在同一条方向二对角线上的每个单元格满足 row + col \textit{row} + \textit{col} row+col 是定值,在对角线二哈希表中将 row + col \textit{row} + \textit{col} row+col 对应的数量加 1 1 1

数组 lamps \textit{lamps} lamps 表示打开的灯的位置。遍历数组 lamps \textit{lamps} lamps,对其中的每一个位置 [ row , col ] [\textit{row}, \textit{col}] [row,col],使用该位置更新四个哈希表。

由于数组 lamps \textit{lamps} lamps 中可能存在重复的位置,因此需要去重,避免同一个位置的灯被重复计算。去重的方法是使用哈希集合存储所有打开的灯的位置,哈希集合可以确保同一个位置只被加入一次。由于每一盏灯所在的行 row \textit{row} row 和列 col \textit{col} col 都是整数且都在范围 [ 0 , n − 1 ] [0, n - 1] [0,n1] 内,因此可以定义哈希函数 f ( row , col ) = row × n + col f(\textit{row}, \textit{col}) = \textit{row} \times n + \textit{col} f(row,col)=row×n+col,该哈希函数可以确保不同位置对应的哈希值不同。

假设存在位置 [ row 1 , col 1 ] [\textit{row}_1, \textit{col}_1] [row1,col1] [ row 2 , col 2 ] [\textit{row}_2, \textit{col}_2] [row2,col2] 满足 f ( row 1 , col 1 ) = f ( row 2 , col 2 ) f(\textit{row}_1, \textit{col}_1) = f(\textit{row}_2, \textit{col}_2) f(row1,col1)=f(row2,col2),则有 row 1 × n + col 1 = row 2 × n + col 2 \textit{row}_1 \times n + \textit{col}_1 = \textit{row}_2 \times n + \textit{col}_2 row1×n+col1=row2×n+col2,即 ( row 1 − row 2 ) × n = col 2 − col 1 (\textit{row}_1 - \textit{row}_2) \times n = \textit{col}_2 - \textit{col}_1 (row1row2)×n=col2col1。由于 ∣ row 1 − row 2 ∣ < n |\textit{row}_1 - \textit{row}_2| < n row1row2<n ∣ col 2 − col 1 ∣ < n |\textit{col}_2 - \textit{col}_1| < n col2col1<n,且位置都是整数,因此一定有 row 1 − row 2 = col 2 − col 1 = 0 \textit{row}_1 - \textit{row}_2 = \textit{col}_2 - \textit{col}_1 = 0 row1row2=col2col1=0 [ row 1 , col 1 ] [\textit{row}_1, \textit{col}_1] [row1,col1] [ row 2 , col 2 ] [\textit{row}_2, \textit{col}_2] [row2,col2] 是相同的位置。

定义哈希函数之后,根据哈希函数计算数组 lamps \textit{lamps} lamps 中的每个位置对应的哈希值。对于每个位置,只有当该位置的哈希值不在哈希集合中时,才将哈希值存入哈希集合,并使用该位置更新四个哈希表。

查询时,对于数组 queries \textit{queries} queries 中的每个位置 [ row , col ] [\textit{row}, \textit{col}] [row,col],首先判断该位置在四个哈希表中对应的数量是否大于 0 0 0,得到查询结果,然后将该位置和全部相邻位置的灯都关闭。具体做法如下。

  1. 在行哈希表中得到 row \textit{row} row 的数量、在列哈希表中得到 col \textit{col} col 的数量、在对角线一哈希表中得到 row − col \textit{row} - \textit{col} rowcol 的数量、在对角线二哈希表中得到 row + col \textit{row} + \textit{col} row+col 的数量,如果该位置在四条线上对应的数量至少有一个大于 0 0 0 则查询结果是 1 1 1,如果该位置在四条线上对应的数量都是 0 0 0 则查询结果是 0 0 0

  2. 遍历位置 [ row , col ] [\textit{row}, \textit{col}] [row,col] 和全部相邻位置,对于每个位置,如果该位置对应的哈希值在哈希集合中,则说明该位置的灯处于打开状态,需要关闭,将该位置对应的哈希值从哈希集合中删除,并将该位置在四个哈希表中对应的数量分别减 1 1 1

查询时的关灯操作可以优化。如果一个单元格所在位置和全部相邻位置中至少有一盏灯打开,则该单元格一定被照亮,因此如果一个单元格没有被照亮,则该单元格所在位置和全部相邻位置的灯一定都处于关闭状态。只有当查询结果是 1 1 1 时,才需要执行关灯操作。

代码

class Solution {
    public int[] gridIllumination(int n, int[][] lamps, int[][] queries) {
        Set<Long> set = new HashSet<Long>();
        Map<Integer, Integer> rowMap = new HashMap<Integer, Integer>();
        Map<Integer, Integer> columnMap = new HashMap<Integer, Integer>();
        Map<Integer, Integer> diagonal1Map = new HashMap<Integer, Integer>();
        Map<Integer, Integer> diagonal2Map = new HashMap<Integer, Integer>();
        for (int[] lamp : lamps) {
            int row = lamp[0], column = lamp[1];
            long position = (long) row * n + column;
            if (set.add(position)) {
                rowMap.put(row, rowMap.getOrDefault(row, 0) + 1);
                columnMap.put(column, columnMap.getOrDefault(column, 0) + 1);
                int diagonal1 = row - column;
                diagonal1Map.put(diagonal1, diagonal1Map.getOrDefault(diagonal1, 0) + 1);
                int diagonal2 = row + column;
                diagonal2Map.put(diagonal2, diagonal2Map.getOrDefault(diagonal2, 0) + 1);
            }
        }
        int[][] directions = {{0, 0}, {-1, -1}, {-1, 0}, {-1, 1}, {0, 1}, {1, 1}, {1, 0}, {1, -1}, {0, -1}};
        int queriesCount = queries.length;
        int[] ans = new int[queriesCount];
        for (int j = 0; j < queriesCount; j++) {
            int[] query = queries[j];
            int queryRow = query[0], queryColumn = query[1];
            boolean flag = rowMap.containsKey(queryRow) || columnMap.containsKey(queryColumn) || diagonal1Map.containsKey(queryRow - queryColumn) || diagonal2Map.containsKey(queryRow + queryColumn);
            if (flag) {
                ans[j] = 1;
                for (int[] direction : directions) {
                    int row = queryRow + direction[0], column = queryColumn + direction[1];
                    int diagonal1 = row - column, diagonal2 = row + column;
                    if (row >= 0 && row < n && column >= 0 && column < n) {
                        long position = (long) row * n + column;
                        if (set.remove(position)) {
                            rowMap.put(row, rowMap.get(row) - 1);
                            if (rowMap.get(row) == 0) {
                                rowMap.remove(row);
                            }
                            columnMap.put(column, columnMap.get(column) - 1);
                            if (columnMap.get(column) == 0) {
                                columnMap.remove(column);
                            }
                            diagonal1Map.put(diagonal1, diagonal1Map.get(diagonal1) - 1);
                            if (diagonal1Map.get(diagonal1) == 0) {
                                diagonal1Map.remove(diagonal1);
                            }
                            diagonal2Map.put(diagonal2, diagonal2Map.get(diagonal2) - 1);
                            if (diagonal2Map.get(diagonal2) == 0) {
                                diagonal2Map.remove(diagonal2);
                            }
                        }
                    }
                }
            }
        }
        return ans;
    }
}

复杂度分析

  • 时间复杂度: O ( l + q ) O(l + q) O(l+q),其中 l l l 是数组 lamps \textit{lamps} lamps 的长度, q q q 是数组 queries \textit{queries} queries 的长度。需要遍历数组 lamps \textit{lamps} lamps,对于每个打开的灯的位置,将该位置存入哈希集合以及更新四个哈希表的时间都是 O ( 1 ) O(1) O(1),然后需要遍历数组 queries \textit{queries} queries,对于每个查询,计算查询结果以及更新哈希表和哈希集合的时间都是 O ( 1 ) O(1) O(1),因此总时间复杂度是 O ( l + q ) O(l + q) O(l+q)

  • 空间复杂度: O ( l ) O(l) O(l),其中 l l l 是数组 lamps \textit{lamps} lamps 的长度。需要使用四个哈希表记录打开的灯的数量以及使用哈希集合存储打开的灯的位置,哈希表和哈希集合的空间复杂度都是 O ( l ) O(l) O(l)。注意返回值不计入空间复杂度。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/456643.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【C++技能树】原来比C方便这么多 --引用、内联函数、Auto、NULL与nullptr

Halo&#xff0c;这里是Ppeua。平时主要更新C语言&#xff0c;C&#xff0c;数据结构算法......感兴趣就关注我吧&#xff01;你定不会失望。 &#x1f308;个人主页&#xff1a;主页链接 &#x1f308;算法专栏&#xff1a;专栏链接 我会一直往里填充内容哒&#xff01; &…

【软考备战·希赛网每日一练】2023年4月24日

文章目录 一、今日成绩二、错题总结第一题第二题第三题第四题第五题 三、知识查缺 题目及解析来源&#xff1a;2023年04月24日软件设计师每日一练 一、今日成绩 二、错题总结 第一题 解析&#xff1a; 第二题 解析&#xff1a; DPI表示每英寸像素点的个数。 300DPI表示每英寸…

ESP32学习六-构建系统

一、简介 如果想要新建一个ESP32项目&#xff0c;需要包含很多其他的文件夹和文件&#xff0c;如果对ESP32的勾线系统原理不理解&#xff0c;就会产生出很多编译不通过的问题。这里就对ESP-IDF构建系统的实现原理做一个简单的总结。 测试环境&#xff1a;Ubuntu18.4 ESP-IDF&am…

Observability:添加免费和开放的 Elastic APM 作为 Elastic 可观察性部署的一部分 - 8.x

作者&#xff1a;David Hope 在最近的一篇博文中&#xff0c;我们向你展示了如何开始使用 Elastic 可观察性的免费开放层。 下面&#xff0c;我们将介绍你需要做些什么来扩展你的部署&#xff0c;这样你就可以开始免费从应用程序性能监控&#xff08;APM&#xff09;或跟踪集群…

我国直播电商行业市场增速下降 核心竞争力发生转变 新一轮洗牌变革步伐将加快

1、直播电商概念及其产业链图解 直播电商是属于网络直播的一个分支&#xff0c;属于一种泛娱乐类直播&#xff0c;电商店铺的店家或模特主播在直播间通过借助视频录制工具&#xff0c;将店铺所售的商品展示给用户&#xff0c;并为用户答疑解惑&#xff0c;提供实时的客服服务&…

【JavaEE初阶】多线程(二)线程状态以及多线程安全问题

摄影分享~~ 文章目录 线程的状态多线程带来的风险线程安全线程安全的原因解决线程不安全问题&#xff08;加锁&#xff09;synchronized关键字-监视器锁monitor locksynchronized的特性 java中的死锁问题死锁死锁的三个典型情况死锁的四个必要条件如何避免死锁&#xff1f; J…

家用洗地机怎么选?2023高性价比家用洗地机推荐

相信大家和我一样是妥妥的“懒人一族”了&#xff0c;不喜欢做家务、不喜欢碰脏水、不喜欢花费过多的时间在家务上&#xff0c;但是却想有一个整洁干净的家居环境。而作为家务清洁中面积最大、耗时耗力最多的就是扫地拖地了。传统的地面清洁方式&#xff0c;要先用扫把扫一遍&a…

Git上传本地代码到Github

参考 https://zhuanlan.zhihu.com/p/138305054 文章目录 上传本地代码到github仓库&#xff08;1&#xff09;创建一个空文件夹&#xff0c;在该文件夹下右键&#xff0c;Git bash here&#xff08;2&#xff09;初始化仓库&#xff08;3&#xff09; 建立本地与github上项目的…

ChatGPT 速通手册——让 ChatGPT 来写正则表达式

regex 生成 正则表达式可谓是一门让广大程序员们又爱又恨的技术。它易学难精&#xff0c;而且可维护性又差&#xff0c;别说交接给其他同事&#xff0c;同一个人写的正则表达式&#xff0c;三个月后回头再看&#xff0c;也可能完全不知所云。 因此&#xff0c;让 ChatGPT 来写…

【计网 从头自己构建协议】一、libpcap 介绍 手撕以太网帧

上一篇&#xff1a;IndexError: list index out of range 下一篇&#xff1a;[【计网 从头自己构建协议】二、收发 ARP 请求帧与响应帧] 介绍 理论的学习总是枯燥的&#xff0c;想要加深对理论的理解&#xff0c;最好的方法就是自己实践一遍。 想要亲手实现各种协议&#xf…

通讯方式连接成功,其他原因导致的连接失败解决方案

一、电脑中有其他品牌visa导致的冲突&#xff08;以tekvisa为例&#xff09; 1、删除tekvisa 2、下载一个NI Package Manager&#xff0c;卸载里面所有NI的东西 &#xff08;https://www.ni.com/zh-cn/support/downloads/software-products/download.package-manager.html#32…

❀五一劳动节来啦❀

今年“五一”&#xff0c;4月29日至5月3日放假调休&#xff0c;共5天。 如果你在5月4日到5月6日请假3天&#xff0c;加上5月7日周日&#xff0c;就可以形成9天的假期。 一&#xff0c;五一劳动节的由来⭐ 国际劳动节又称“五一国际劳动节”“国际示威游行日”&#xff08;英语…

人生中最好的等待叫做来日可期,社科院与杜兰大学金融管理硕士等你惊艳岁月

有句话说&#xff1a;“去日不可追&#xff0c;来日犹可期”。过去的已经过去&#xff0c;不管好的、坏的都已成为我们的回忆。人生中最好的等待就是未来可期。别辜负现在的好时光&#xff0c;努力做想做的事。社科院与杜兰大学金融管理硕士项目等你惊艳时光。 所有出众者的背…

【计算机网络:自顶向下方法】(三) 运输层 (TCP | UDP | 复用 | 传输原理rdt)

【计算机网络&#xff1a;自顶向下方法】 3.1 概述 传输层协议是在端系统中实现的传输层将发送的应用程序进程接受到的报文转换成传输层分组 (运输层报文段)实现的方法/过程 &#xff1a; 将应用报文划分为较小的块&#xff0c;并为每块加上传输层首部以生成传输层报文段ff。I…

【C++】类和对象(中篇)—— 默认成员函数,const成员函数,运算符重载

前言 类和对象没有技巧&#xff0c;只有多加练习&#xff0c;多多尝试自己完成代码&#xff0c;例如各种运算符的重载&#xff0c;或是实现一个自己的日期类 目录 一、类的六个默认成员函数 二、构造函数 2.1 概念 2.2 特点 2.3 默认无参的构造函数 三、析构函数 3.1 概…

ORB305与CISCO路由器构建L2TP over IPSec VPN操作手册

1、网络拓扑在思科路由器与ORB305之间建立一个安全隧道&#xff0c;对客户路由器端设备子网&#xff0c;与思科路由器端服务器子网之间的数据流进行安全保护&#xff0c;组网拓扑图如图所示。 2、思科路由器端配置指导(此处以多数客户使用专线上网形式为例)Cisco&#xff08;AR…

90年三本程序员,8年5跳,年薪4万变92万……

很多时候&#xff0c;虽然跳槽可能带来降薪的结果&#xff0c;但依然有很多人认为跳槽可以涨薪。近日&#xff0c;看到一则帖子。 发帖的楼主表示&#xff0c;自己8年5跳&#xff0c;年薪4万到92万&#xff0c;现在环沪上海各一套房&#xff0c;再干5年码农&#xff0c;就可以…

2022年NOC大赛创客智慧编程赛道图形化scratch初赛题,包含答案解析

目录 一、单选题 二、多选题 三、判断题 下载打印文档做题: 一、单选题

项目干系人是什么?如何有效管理项目干系人?

项目干系人是指对项目具有利益关系或影响力的个人、团体或组织。他们可能会对项目的目标、范围、进度、成本、质量等方面产生影响&#xff0c;因此&#xff0c;有效地管理项目干系人是项目管理成功的关键之一。 一、干系人识别和分类 项目经理应该首先识别和分类所有与项目有关…

STM32模数转换器(ADC)

1.ADC的简要 我们首先说一下ADC的转换过程&#xff0c;然后说一下原理&#xff0c;当然如果嫌啰嗦可以直接跳过。 ADC是英文Analog-to-Digital Converter缩写&#xff0c;翻译过来就是模数转换器&#xff0c;是指将连续变化的模拟信号转换为离散的数字信号的器件。A/D转换的作…