房间 Room
图里的一个节点,编号 0 到 n-1。
一开始只有 0 号房间是打开的,其它房间都锁着。
当前:LC841 · LC841 钥匙和房间:从 0 号房间出发,能否进入所有房间 · 首次出现于 Day 32 · 路径:顶栏「56天打卡」→ 点击 LC 题号 → 逐题动画
你有 n 个房间,一开始只有 0 号房间是打开的。每个房间里可能有一些钥匙,房间 i 里的钥匙 x 表示可以打开 x 号房间。问题是:从 0 号房间出发,最后能不能进入所有房间?
你有 n 个房间,编号从 0 到 n-1。
一开始只有 0 号房间是打开的。
每个房间里可能有一些钥匙。
如果房间 i 里有钥匙 x,表示可以打开 x 号房间。
问题是:从 0 号房间出发,最后能不能进入所有房间?
每个 rooms[i] 是一个列表,里面的每个数字都是一把钥匙。钥匙 x 表示「我可以打开 x 号房间」。
建好图后,问题就变成:从节点 0 出发,沿着这些箭头能不能走到所有节点。
图里的一个节点,编号 0 到 n-1。
一开始只有 0 号房间是打开的,其它房间都锁着。
一条有向边:room i 里的 key x 指向 room x。
拿到钥匙 x,就能打开 x 号房间,相当于走一条 i → x 的边。
从 0 出发,沿着边能不能走到所有节点。
这题不求最短路、也不复制图结构,只问「能不能全部到达」。
用栈(DFS)或队列(BFS)把能到的房间一个个走完。
栈是后进先出,队列是先进先出,遍历到的房间集合是一样的。
记录哪些房间已经访问过。
防止重复进入同一个房间,也防止房间互相有钥匙时陷入死循环。
待处理房间的容器。
拿到新钥匙、且对应房间没访问过时,把它放进来等着处理。
输入 rooms = [[1,3],[3,0,1],[2],[0]]。Room 0 里有钥匙 1、3,钥匙 x 表示可以打开 x 号房间。问从 0 号房间出发能不能进入所有房间。
房间之间可能互相有钥匙,例如 0 能到 1,1 又能回到 0。
如果没有 visited,算法会反复进入同一个房间。
所以每次拿到钥匙时,先判断这个房间是否访问过:
1func canVisitAllRooms(rooms [][]int) bool {2 n := len(rooms)3 visited := make([]bool, n)4 5 stack := []int{0}6 visited[0] = true7 8 for len(stack) > 0 {9 room := stack[len(stack)-1]10 stack = stack[:len(stack)-1]11 12 for _, key := range rooms[room] {13 if !visited[key] {14 visited[key] = true15 stack = append(stack, key)16 }17 }18 }19 20 for _, ok := range visited {21 if !ok {22 return false23 }24 }25 26 return true27}visited 是一个长度为 n 的布尔数组,记录哪些房间已经打开。它是 O(n) 空间,也是这题不能写 O(1) 空间的原因。
0 号房间一开始就是打开的,所以先把 0 入栈,并标记 visited[0] = true。这是遍历的起点。
只要栈不空,就从栈顶取一个房间来处理。DFS 用栈(后进先出),换成队列就是 BFS。
读取当前房间里的每一把钥匙 key。如果 visited[key] 还是 false,就标记成 true 并入栈;如果已经访问过,就跳过——这一步就是防止死循环的关键。
遍历结束后扫描 visited,只要有一个房间是 false,就返回 false;全部为 true 才返回 true。
n 是房间数量,m 是所有钥匙的总数(每个 rooms[i] 长度加起来)。每个房间最多入栈一次,每把钥匙最多被检查一次,所以是 O(n + m)。
visited 数组占 O(n),stack 最坏情况下要装下所有房间,也是 O(n)。这里不是 O(1) 空间,因为必须记录访问状态。