题目
841. 钥匙和房间
难度:中等
题目分析:
1. 解法一:我自己想的,兜圈了……
class Solution:
def canVisitAllRooms(self, rooms: List[List[int]]) -> bool:
# 基于栈的探索可以解决,反正就是一个个试
# 建立一个答案数组,没探索的标1,
# 等栈探索完,看答案数组的和是否为0,是可以走遍
if len(rooms) == 1:
return True # 一间房,肯定可以
ans =[1]*len(rooms) # 默认都是1,如果能进入,就标位0
my_stack = []
visited = set()
# 第一个房间进栈
if rooms[0] == []: # 没有钥匙
return False
ans[0] = 0
visited.add(0)
my_stack.append((0, 0)) # room_index, key_index
while my_stack:
room, i = my_stack.pop()
# 把能打开的门标记上
if rooms[room] == []: # 空房间不看
continue
for k in rooms[room]:
if ans[k] == 1: # 还没有钥匙
ans[k] = 0
if sum(ans) ==0: # 检查是否探索好
return True
#while i<len(rooms[room]) and rooms[room][i] == room: # 防止回到自身
# i += 1
# 前往下一个房间
while i < len(rooms[room]) and rooms[room][i] in visited:
i += 1
if i == len(rooms[room]):
continue # 这里就没什么可以探索的了
nxt = rooms[room][i] # 要进入下个房间
visited.add(nxt)
# 未探索的进栈
if i+1 < len(rooms[room]):
my_stack.append((room, i+1))
my_stack.append((nxt, 0))
return sum(ans) == 0
1.1 运行结果:
1.2 分析:
- 在完成栈的问题时,我还没能从教材的迷宫模板中走出来,导致了这道题走了弯路。上述代码有超级多的冗余。
- 以下是各个冗余点:
if rooms[room] == []: # 空房间不看
continue
for k in rooms[room]:
if ans[k] == 1: # 还没有钥匙
ans[k] = 0
不需要判断表是不是空,因为表空的话,下面的for循环不会运行,会自己跳过
然后,这里我再标记哪些房间可以探索的时候,就可以把他们加入栈里面待探索!然后,整个程序到这里就可以结束了……因此,下面这段代码都是多余的!
# 前往下一个房间
while i < len(rooms[room]) and rooms[room][i] in visited:
i += 1
if i == len(rooms[room]):
continue # 这里就没什么可以探索的了
nxt = rooms[room][i] # 要进入下个房间
visited.add(nxt)
# 未探索的进栈
if i+1 < len(rooms[room]):
my_stack.append((room, i+1))
my_stack.append((nxt, 0))
这段代码,我的本意是探索哪些钥匙要加入待探索的栈中,于是,进行了一大堆判断,然而,这部分工作,在前面的代码中已经完成了,这里的i并没有作用,上面的for循环,已经覆盖了。
同时,我这个代码,不需要set()也能求解
1.3 代码修正
class Solution:
def canVisitAllRooms(self, rooms: List[List[int]]) -> bool:
ans =[1]*len(rooms) # 默认都是1,如果能进入,就标位0
my_stack = []
ans[0] = 0
my_stack.append(0) # room_index
while my_stack:
room = my_stack.pop()
# 把能打开的门标记上
for k in rooms[room]: # 包括空
if ans[k] == 1: # 还没有钥匙
ans[k] = 0
my_stack.append(k)
return sum(ans) == 0
1.3.1 运行结果:
1.3.2 分析:
- 运行速度快了点,不过不稳定,理论上,使用数组索引,不应该把set()慢呀!
- 跟解法二的区别就是用不同方式记录探索过的房间。
2. 解法二:不兜圈子的解法
# 参考了最快答案后的解法
class Solution:
def canVisitAllRooms(self, rooms: List[List[int]]) -> bool:
my_stack = [] # 用来存储待探索的房间
visited = set() # 一方面记录探索过的房间,一方面用来判断是否完成
visited.add(0) # 0号房间探索过
my_stack.append(0) # 0号房间的钥匙需要遍历
while my_stack:
cur = my_stack.pop()
for k in rooms[cur]: # 钥匙列表
if k not in visited:
visited.add(k) # 说明可以进入新的房间
my_stack.append(k) # 新的房间有新的钥匙
return len(visited) == len(rooms)
2.1 运行结果:
2.2 分析:
快了40ms, 显著的速度提升!
此处我用的是栈,因此属于深度优先搜索;如果这道题,我把栈换成队列,就变成广度优先搜索,然而,对于这道题来说,这两种方法并没有什么差别。因为这而的主体是每进入一个房间,判断里面的钥匙是否用过,然后没用过的,加入待探索的表,而待探索的表要以什么顺序去找,是无所谓的。