LeetCode #841 Medium

Keys and Rooms

Keys and Rooms is LeetCode 841 (Medium). There are n rooms numbered 0 to n - 1, and every room except room 0 starts locked. rooms[i] lists the keys lying in room i, and key j unlocks room j.

  • You can enter any room you hold a key for, in any order, and you keep every key you pick up.
  • Return true if you can enter every room, and false otherwise.

There are at most 1,000 rooms and 3,000 keys in total, so one pass over the rooms and keys is enough.

Constraints
  • n == rooms.length
  • 2 <= n <= 1000
  • 0 <= rooms[i].length <= 1000
  • 1 <= sum(rooms[i].length) <= 3000
  • 0 <= rooms[i][j] < n
  • All the values of rooms[i] are unique.
depth-first-searchbreadth-first-searchgraph
Open on LeetCode ↗
02

Intuition

Draw each room as a node and each key as an arrow from the room it lies in to the room it opens. Room j can be entered exactly when a path of arrows leads from room 0 to it: walking that path picks up each key you need on the way.

So the question is whether every node can be reached from node 0, and one depth-first search from room 0 answers it. If the search marks all n rooms, the answer is true.

The arrows only go one way. A key to room 2 lying in room 1 says nothing about getting from room 2 back to room 1.

How to spot this pattern

Whenever picking something up opens more things to pick up, and getting it costs nothing, the question is plain reachability: unlockable levels in a game, pages reachable by links, packages pulled in by a dependency.

03

Approach

Try it first

Before reading on, restate the problem without keys or rooms: what is it asking about a directed graph? Then decide when a room should be marked so it is never handled twice. Aim for O(n + k), with k the total number of keys.

1

Mark room 0 and put it on the stack

Room 0 is open from the start, so mark it as seen and push it. A room is marked the moment its key is found, not when it is entered, so it can never be on the stack twice.

2

Pop a room and pick up its keys

Pop the top room from the stack; you are now inside it and hold all of its keys. Each key opens exactly one room, so the keys are the arrows to follow next.

3

Push each key's room the first time you see it

For a key to a room that is not marked yet, mark the room and push it. A key to a marked room, including a key to the room you are standing in, is ignored: that room is already waiting on the stack or done.

4

Compare the count with n

When the stack is empty, every room that can be reached has been marked. Return true only if the number of marked rooms equals n; a room left unmarked has no path of keys from room 0.

04

Keys and Rooms solution in Python | C++ | Java

▶1class Solution:
▶2 def canVisitAllRooms(self, rooms):
▶3 seen = {0}
▶4 stack = [0]
▶5 while stack:
▶6 room = stack.pop()
▶7 for key in rooms[room]:
▶8 if key not in seen:
▶9 seen.add(key)
▶10 stack.append(key)
▶11 return len(seen) == len(rooms)
0unlocked1locked2locked3locked0stackseen, 1 of 4 rooms0room 0 is open → mark it, push it
seen{0}
stack[0]
Each arrow is a key: it starts in the room where the key lies and points at the room it opens. Room 0 needs no key, so it is marked as seen and pushed. Its keys are read when it is popped.
0unlocked1locked2locked3lockedemptystackseen, 1 of 4 rooms0pop room 0 → keys to 1
room0just entered
keys[1]
Pop room 0 off the stack and walk in. It holds the key to room 1, checked one by one next.
0unlocked1unlocked2locked3locked1stackseen, 2 of 4 rooms01key to room 1: not seen yet → mark it, push it
key1
seen2 of 4
Room 1 was locked until now. Mark it as seen right away, before entering it, so no other key can push it a second time. It goes on the stack to be opened later.
0unlocked1unlocked2locked3lockedemptystackseen, 2 of 4 rooms01pop room 1 → keys to 2
room1just entered
keys[2]
Pop room 1 off the stack and walk in. It holds the key to room 2, checked one by one next.
0unlocked1unlocked2unlocked3locked2stackseen, 3 of 4 rooms012key to room 2: not seen yet → mark it, push it
key2
seen3 of 4
Room 2 was locked until now. Mark it as seen right away, before entering it, so no other key can push it a second time. It goes on the stack to be opened later.
0unlocked1unlocked2unlocked3lockedemptystackseen, 3 of 4 rooms012pop room 2 → keys to 3
room2just entered
keys[3]
Pop room 2 off the stack and walk in. It holds the key to room 3, checked one by one next.
0unlocked1unlocked2unlocked3unlocked3stackseen, 4 of 4 rooms0123key to room 3: not seen yet → mark it, push it
key3
seen4 of 4
Room 3 was locked until now. Mark it as seen right away, before entering it, so no other key can push it a second time. It goes on the stack to be opened later.
0unlocked1unlocked2unlocked3unlockedemptystackseen, 4 of 4 rooms0123pop room 3 → no keys inside
room3just entered
keys[]
Pop room 3 off the stack and walk in. It holds no keys, so it opens nothing new.
0unlocked1unlocked2unlocked3unlockedemptystackseen, 4 of 4 rooms0123stack empty, seen 4 of 4 → true
seen4rooms = 4
answertrue
The stack is empty and all 4 rooms were marked, so every room can be entered. Return true.
05

Common pitfalls

Pushing rooms without marking them

✗ Wrong
for key in rooms[room]:
    stack.append(key)
✓ Right
for key in rooms[room]:
    if key not in seen:
        seen.add(key)
        stack.append(key)

For [[1], [0]], room 0 pushes room 1, room 1 pushes room 0, room 0 pushes room 1 again, and the loop never ends. The seen check is what breaks the cycle.

Counting keys instead of rooms you can reach

✗ Wrong
keys = {0}
for r in rooms:
    keys.update(r)
return len(keys) == len(rooms)
✓ Right
return len(seen) == len(rooms)  # after the DFS

For [[1], [], [3], [2]] every room has a key somewhere, so this returns true. But the keys to rooms 2 and 3 lie inside rooms 3 and 2, which nothing you can reach opens. The answer is false.

Recursive DFS in Python on a long chain

✗ Wrong
def dfs(r):
    seen.add(r)
    for k in rooms[r]:
        if k not in seen:
            dfs(k)
✓ Right
stack = [0]
while stack:
    ...

Python stops recursion at a depth of 1,000 by default. For 1,000 rooms in a chain, [[1], [2], ..., [999], []], the calls nest about 1,000 deep and raise RecursionError. An explicit stack has no such limit.

06

Complexity

Time
O(n + k)
Space
O(n)
n is the number of rooms and k the total number of keys. Each room is marked and popped at most once, and each key is read once, when its room is popped. The seen set and the stack hold at most n rooms.
07

Keys and Rooms FAQ

Does BFS work too?

Yes. Swap the stack for a queue and nothing else changes. BFS reaches the same rooms in a different order, and the answer depends only on which rooms were reached, not when.

How is this different from Number of Provinces?

Number of Provinces counts the separate groups in an undirected graph, so it starts a new search from every unvisited city. Here the arrows have a direction and there is only one start, room 0, so a single search decides the answer.