Leetcode刷题(18)栈的应用:钥匙和房间 (深度分析堆栈使用的易错点)

题目

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 分析:

  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 分析:
  1. 运行速度快了点,不过不稳定,理论上,使用数组索引,不应该把set()慢呀!
  2. 跟解法二的区别就是用不同方式记录探索过的房间。

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, 显著的速度提升!

此处我用的是栈,因此属于深度优先搜索;如果这道题,我把栈换成队列,就变成广度优先搜索,然而,对于这道题来说,这两种方法并没有什么差别。因为这而的主体是每进入一个房间,判断里面的钥匙是否用过,然后没用过的,加入待探索的表,而待探索的表要以什么顺序去找,是无所谓的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值