841. 钥匙和房间

中等 含交互动画

学完这道你应该能: 说清它为什么用「DFS」,而不是死记步骤; 合上页面,自己默写出核心代码; 用 30 秒把思路讲清楚。

AI 私教 · 就着本题动画讲
卡住了不用硬扛,按 8 步把这题真正走通。
从读题、试解、找法到手写代码,边问边打勾,适合第一次系统学算法的同学。

题目描述

每个房间里放着若干钥匙,每把钥匙通往一个房间。从 0 号房开始,问能否访问到所有房间?

rooms = [[1], [2], [3], []]
输出 = true

思路解析

最朴素的想法:维护一堆已拿到的钥匙,每轮扫一遍看能开哪些还没开的门。可你会一次次重新检查那些早就开过的房间,钥匙一多就大量重复。

转折:把它看成图——房间是节点,钥匙是有向边。从 0 开始 BFS/DFS,每进一个房就立刻标记 visited,只对没访问过的房入队。这样每个房只处理一次,不再回头重扫。最后数 visited 等于房间总数就是 true。

建图 · 0 入队:画成图:rooms[0]=[1]、rooms[1]=[2]、rooms[2]=[3]、rooms[3]=[],即三条有向边 0→1→2→3。起点 0 标记 visited 并入队,作为遍历的源头。

出队 0 · 看它的钥匙:取出 0(标橙=正在处理),它已访问。看它手上的钥匙 rooms[0]=[1],准备去开 1 号房。

0→1 · 1 没访问过 → 入队:沿边 0→1:房 1 还没访问,所以拿钥匙开门、标记 visited、入队。0 处理完变绿(done)。

出队 1 · 看它的钥匙:取出 1 处理。它的钥匙 rooms[1]=[2],指向房 2。

1→2 · 2 没访问过 → 入队:沿边 1→2:房 2 没访问过,开门、标记、入队。注意每次进房第一件事就是标 visited——这是防止绕回来死循环的关键。

出队 2 · 1→2 已访问(负例:跳过):取出 2 处理,它的钥匙 rooms[2]=[3]。设想此刻又冒出一把指回 1 的钥匙:房 1 已经在 visited 里——直接跳过、不再入队。这就是负例:已访问的房一律不重复处理。

2→3 · 3 没访问过 → 入队:沿边 2→3:房 3 没访问,开门、标记、入队。

出队 3 · 没钥匙了 · 队列空:取出 3 处理,rooms[3]=[] 没钥匙了。没有新房可入队,队列空,遍历结束。

判断 · 数 visited:数一下 visited:4 个,正好等于房间总数 4 → 全部可达,返回 true。若某房没人给钥匙、永远进不了 visited,最终 len(visited) 小于 N,就返回 false。

凡是「从一个点出发能不能走到所有点」「有几个连通块」,都用 BFS/DFS 数可达节点。钥匙房间、被围绕的区域、省份数量,全是这一类。

参考代码(Python)

Python
1visited = {0}                       # 起点先标记
2q = deque([0])
3while q:
4    room = q.popleft()
5    for key in rooms[room]:          # 这房里的每把钥匙
6        if key not in visited:       # 只开没访问过的门
7            visited.add(key)         # 进门立刻标记
8            q.append(key)
9return len(visited) == len(rooms)   # 全访问到才 true

复杂度分析

  • 时间复杂度:O(N+E) —— N 个房各出队一次,E 把钥匙各检查一次
  • 空间复杂度:O(N) —— visited 集合 + 队列最多装 N 个房

套路模板

记住骨架:起点先标记、邻居只处理没访问的、入队前立刻加 visited、最后数 visited。把队列换成递归就是 DFS,效果一样。

Python
1# 「从起点能到达哪些点」都套
2visited = {start}; q = deque([start])
3while q:
4    u = q.popleft()
5    for v in graph[u]:
6        if v not in visited:        # 只走没访问的
7            visited.add(v); q.append(v)
8# len(visited) == N ? 全连通

易错点

  • 错误写法:入队 / 进 dfs 才忘标 visited正确写法:加入 visited 和入队同时做(不标记遇到指回来的钥匙会重复入队,有环时无限循环)
  • 错误写法:判断只看「有没有到某个终点」正确写法:比对 len(visited) == len(rooms)(题目要的是访问到全部房间,不是到达某一个)

以上文字与上方动画完全同一思路:先看动画建立直觉 → 读文字巩固 → 关掉页面自己默写一遍代码,能写出来才算真的掌握

下一题 →605. 种花问题 ← 返回题库