news 2026/9/3 8:47:20

算法题 钥匙和房间

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
算法题 钥匙和房间

841. 钥匙和房间

问题描述

n个房间,编号从0n-1。每个房间都有一些钥匙,可以打开其他房间。

给定一个数组rooms,其中rooms[i]是一个列表,表示你进入房间i后可以拿到的所有钥匙(钥匙对应房间的编号)。

你从房间0开始,初始时可以进入房间0

返回true如果你可以进入所有房间,否则返回false

示例

输入:rooms=[[1],[2],[3],[]]输出:true解释:-从房间0开始,拿到钥匙1-用钥匙1进入房间1,拿到钥匙2-用钥匙2进入房间2,拿到钥匙3-用钥匙3进入房间3-所有房间都已访问
输入:rooms=[[1,3],[3,0,1],[2],[0]]输出:false解释:-从房间0开始,拿到钥匙13-可以进入房间13,但无法进入房间2-房间2中有钥匙2,但无法获得

算法思路

图的遍历(DFS/BFS)

  1. 建模

    • 将房间看作图的节点,钥匙看作有向边
    • 问题转化为:从节点0出发,能否访问所有节点?
  2. 遍历

    • DFS(深度优先搜索):递归或栈实现
    • BFS(广度优先搜索):队列实现

代码实现

方法一:DFS递归

importjava.util.*;classSolution{/** * 判断是否能进入所有房间 * 使用DFS递归遍历,从房间0开始 * * @param rooms 房间钥匙信息,rooms[i]表示房间i中的钥匙列表 * @return 如果能进入所有房间返回true,否则返回false */publicbooleancanVisitAllRooms(List<List<Integer>>rooms){intn=rooms.size();boolean[]visited=newboolean[n];// 记录房间访问状态// 从房间0开始DFSdfs(rooms,0,visited);// 检查是否所有房间都被访问for(booleanroomVisited:visited){if(!roomVisited){returnfalse;}}returntrue;}/** * 深度优先搜索遍历房间 * * @param rooms 房间钥匙信息 * @param room 当前房间编号 * @param visited 访问状态数组 */privatevoiddfs(List<List<Integer>>rooms,introom,boolean[]visited){// 标记当前房间为已访问visited[room]=true;// 遍历当前房间中的所有钥匙for(intkey:rooms.get(room)){// 如果对应的房间未访问过,递归访问if(!visited[key]){dfs(rooms,key,visited);}}}}

方法二:DFS栈实现

importjava.util.*;classSolution{/** * 使用栈实现DFS遍历 */publicbooleancanVisitAllRooms(List<List<Integer>>rooms){intn=rooms.size();boolean[]visited=newboolean[n];Stack<Integer>stack=newStack<>();// 从房间0开始stack.push(0);visited[0]=true;intvisitedCount=1;// 已访问房间数while(!stack.isEmpty()){intcurrentRoom=stack.pop();// 遍历当前房间的所有钥匙for(intkey:rooms.get(currentRoom)){if(!visited[key]){visited[key]=true;stack.push(key);visitedCount++;// 如果已经访问了所有房间,提前结束if(visitedCount==n){returntrue;}}}}returnvisitedCount==n;}}

方法三:BFS队列实现

importjava.util.*;classSolution{/** * 使用BFS(队列)实现遍历 */publicbooleancanVisitAllRooms(List<List<Integer>>rooms){intn=rooms.size();boolean[]visited=newboolean[n];Queue<Integer>queue=newLinkedList<>();// 从房间0开始queue.offer(0);visited[0]=true;intvisitedCount=1;while(!queue.isEmpty()){intcurrentRoom=queue.poll();// 遍历当前房间的所有钥匙for(intkey:rooms.get(currentRoom)){if(!visited[key]){visited[key]=true;queue.offer(key);visitedCount++;// 提前结束if(visitedCount==n){returntrue;}}}}returnvisitedCount==n;}}

方法四:使用Set记录访问状态

importjava.util.*;classSolution{/** * 使用Set记录已访问房间 */publicbooleancanVisitAllRooms(List<List<Integer>>rooms){Set<Integer>visited=newHashSet<>();Stack<Integer>stack=newStack<>();stack.push(0);while(!stack.isEmpty()){introom=stack.pop();visited.add(room);for(intkey:rooms.get(room)){if(!visited.contains(key)){stack.push(key);}}}returnvisited.size()==rooms.size();}}

算法分析

  • 时间复杂度:O(N + K)
    • N 是房间数量
    • K 是所有钥匙的总数(图中边的数量)
    • 每个房间和每把钥匙最多被访问一次
  • 空间复杂度:O(N)
    • visited数组:O(N)
    • 递归栈/显式栈/队列:最坏情况下 O(N)
    • 总体空间复杂度为 O(N)

算法过程

1:rooms = [[1],[2],[3],[]]

DFS递归过程

  1. 访问房间0visited = [true, false, false, false]

    • 钥匙:[1]
    • 递归访问房间1
  2. 访问房间1visited = [true, true, false, false]

    • 钥匙:[2]
    • 递归访问房间2
  3. 访问房间2visited = [true, true, true, false]

    • 钥匙:[3]
    • 递归访问房间3
  4. 访问房间3visited = [true, true, true, true]

    • 钥匙:[]
    • 返回
  5. 检查结果:所有房间都已访问 →true

2:rooms = [[1,3],[3,0,1],[2],[0]]

DFS递归过程

  1. 访问房间0visited = [true, false, false, false]

    • 钥匙:[1, 3]
    • 先递归访问房间1
  2. 访问房间1visited = [true, true, false, false]

    • 钥匙:[3, 0, 1]
    • 房间3未访问,递归访问房间3
    • 房间0和1已访问,跳过
  3. 访问房间3visited = [true, true, false, true]

    • 钥匙:[0]
    • 房间0已访问,跳过
    • 返回到房间1,再返回到房间0
  4. 回到房间0:处理钥匙3(已访问),结束

  5. 检查结果visited = [true, true, false, true],房间2未访问 →false

测试用例

importjava.util.*;publicclassMain{publicstaticvoidmain(String[]args){Solutionsolution=newSolution();// 测试用例1:可以访问所有房间List<List<Integer>>rooms1=Arrays.asList(Arrays.asList(1),Arrays.asList(2),Arrays.asList(3),Arrays.asList());System.out.println("Test 1: "+solution.canVisitAllRooms(rooms1));// true// 测试用例2:无法访问所有房间List<List<Integer>>rooms2=Arrays.asList(Arrays.asList(1,3),Arrays.asList(3,0,1),Arrays.asList(2),Arrays.asList(0));System.out.println("Test 2: "+solution.canVisitAllRooms(rooms2));// false// 测试用例3:只有一个房间List<List<Integer>>rooms3=Arrays.asList(Arrays.asList());System.out.println("Test 3: "+solution.canVisitAllRooms(rooms3));// true// 测试用例4:两个房间,互相有钥匙List<List<Integer>>rooms4=Arrays.asList(Arrays.asList(1),Arrays.asList(0));System.out.println("Test 4: "+solution.canVisitAllRooms(rooms4));// true// 测试用例5:两个房间,单向有钥匙List<List<Integer>>rooms5=Arrays.asList(Arrays.asList(1),Arrays.asList());System.out.println("Test 5: "+solution.canVisitAllRooms(rooms5));// true// 测试用例6:两个房间,没有钥匙List<List<Integer>>rooms6=Arrays.asList(Arrays.asList(),Arrays.asList());System.out.println("Test 6: "+solution.canVisitAllRooms(rooms6));// false// 测试用例7:复杂情况List<List<Integer>>rooms7=Arrays.asList(Arrays.asList(1,2,3),Arrays.asList(2,3),Arrays.asList(3),Arrays.asList());System.out.println("Test 7: "+solution.canVisitAllRooms(rooms7));// true// 测试用例8:环形结构List<List<Integer>>rooms8=Arrays.asList(Arrays.asList(1),Arrays.asList(2),Arrays.asList(0));System.out.println("Test 8: "+solution.canVisitAllRooms(rooms8));// true// 测试用例9:多个分支List<List<Integer>>rooms9=Arrays.asList(Arrays.asList(1,2),Arrays.asList(3),Arrays.asList(4),Arrays.asList(),Arrays.asList());System.out.println("Test 9: "+solution.canVisitAllRooms(rooms9));// true}}

关键点

  1. 图遍历

    • 房间是节点,钥匙是有向边
    • 问题等价于判断从节点0出发是否能到达所有节点
  2. 起始条件

    • 保证可以从房间0开始(不需要钥匙)
    • 这是连通性判断的起点
  3. 访问标记

    • 避免重复访问和无限循环
    • 环形结构(如房间0→1→2→0)需要正确处理

常见问题

  1. 为什么不需要考虑钥匙的使用顺序?
    能否访问所有房间,而不是访问的顺序。只要存在一条路径能到达某个房间,就认为可以访问。

  2. DFS和BFS有什么区别?
    在这个问题中,两种方法的结果完全相同。DFS可能会更快到达某些房间,BFS按层次访问,最终的可达性判断是一样的。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/2 22:27:04

瑞芯微(EASY EAI)RV1126B SPI使用

1. SPI简介 SPI是串行外设接口(Serial Peripheral Interface)的缩写&#xff0c;是 Motorola 公司推出的一种同步串行接口技术&#xff0c;是一种高速、全双工、同步的通信总线在用户空间的应用程序中&#xff0c;完全可以不必理会SPI协议的详细规定。只需要按照驱动层提供给我…

作者头像 李华
网站建设 2026/9/2 22:25:57

爆肝整理!2025年十大企业级Agent应用案例,看完我直接原地封神

在2025年的全球技术语境下&#xff0c;生成式人工智能&#xff08;Generative AI&#xff09;已正式跨越了简单的对话式交互阶段&#xff0c;进入了以自主代理&#xff08;Autonomous Agents&#xff09;为核心的生产力变革元年。本文旨在通过对全球领先企业的十大生产级应用案…

作者头像 李华
网站建设 2026/9/2 17:50:49

提示工程架构师必知:AI提示设计的常见挑战与对策

提示工程架构师必知&#xff1a;AI提示设计的常见挑战与对策 1. 引入与连接&#xff1a;你是否也曾被AI“误解”&#xff1f; 清晨8点&#xff0c;你急着给客户写一封产品推广邮件&#xff0c;于是给AI发了条提示&#xff1a; “写一封关于我们新推出的智能手表的推广邮件&…

作者头像 李华
网站建设 2026/9/3 0:04:44

RAG的一点思考

RAG有一个很典型的模块叫文本切块&#xff0c;然后检索。切块部分可以按window窗口切&#xff0c;检索部分也可以多路径检索&#xff0c;兼具稀疏检索&#xff0c;稠密检索和数据流检索。 这个流程和token化,然后embedding很像&#xff0c;所以我今天就陷入了迷茫&#xff1f;…

作者头像 李华
网站建设 2026/9/3 0:05:19

你真的了解cogagent和Open-AutoGLM吗:5大常见误解与真相解析

第一章&#xff1a;你真的了解cogagent和Open-AutoGLM吗&#xff1a;5大常见误解与真相解析在人工智能工程快速演进的当下&#xff0c;cogagent 与 Open-AutoGLM 作为新兴的智能体框架与开源语言模型工具链&#xff0c;正受到广泛关注。然而&#xff0c;伴随热度而来的是一系列…

作者头像 李华