Leetcode刷题笔记65:图论5(127. 单词接龙-841. 钥匙和房间)

143 阅读3分钟

导语

leetcode刷题笔记记录,主要记录题目包括:

Leetcode

题目描述

字典 wordList 中从单词 beginWord **和 endWord 的 转换序列 是一个按下述规格形成的序列 beginWord -> s1 -> s2 -> ... -> sk

  • 每一对相邻的单词只差一个字母。
  •  对于 1 <= i <= k 时,每个 si 都在 wordList 中。注意, beginWord **不需要在 wordList 中。
  • sk == endWord

给你两个单词 **beginWord **和 endWord 和一个字典 wordList ,返回 从 beginWord 到 endWord 的 最短转换序列 中的 单词数目 。如果不存在这样的转换序列,返回 0 。

 

示例 1:

输入: beginWord = "hit", endWord = "cog", wordList = ["hot","dot","dog","lot","log","cog"]
输出: 5
解释: 一个最短转换序列是 "hit" -> "hot" -> "dot" -> "dog" -> "cog", 返回它的长度 5

示例 2:

输入: beginWord = "hit", endWord = "cog", wordList = ["hot","dot","dog","lot","log"]
输出: 0
解释: endWord "cog" 不在字典中,所以无法进行转换。

 

提示:

  • 1 <= beginWord.length <= 10
  • endWord.length == beginWord.length
  • 1 <= wordList.length <= 5000
  • wordList[i].length == beginWord.length
  • beginWordendWord 和 wordList[i] 由小写英文字母组成
  • beginWord != endWord
  • wordList 中的所有字符串 互不相同

解法

这是一道困难题目,需要我们自己去建立图,例如示例1如下图所示(参考代码随想录)。

根据代码随想录的思路提示,本题只需要求出最短路径的长度就可以。这里无向图求最短路,广搜最为合适,广搜只要搜到了终点,那么一定是最短的路径。因为广搜就是以起点中心向四周扩散的搜索。

另外需要有一个注意点:

  • 无向图需要用标记位,标记着节点是否走过,否则就会死循环!
  • 集合转成set结构,查找更快一些。
from collections import deque
from typing import List  # 如果你没有导入List,请添加这行

class Solution:
    def ladderLength(self, beginWord: str, endWord: str, wordList: List[str]) -> int:
        # 将wordList转换为一个集合,这样查找效率更高
        word_set = set(wordList)

        # 如果目标词不在字典中,直接返回0,因为无法转换
        if endWord not in word_set:
            return 0

        # 初始化一个双端队列,并将开始词及其长度1加入队列
        queue = deque([(beginWord, 1)])

        # 当队列不为空时,持续进行搜索
        while queue:
            # 弹出队列中的一个元素,word是当前单词,length是到达这个单词的路径长度
            word, length = queue.popleft()

            # 如果当前单词就是目标词,则返回路径长度
            if word == endWord:
                return length
            
            # 遍历当前单词的每一个字符
            for i in range(len(word)):
                # 尝试将当前单词的每一个字符替换为其他字母
                for char in 'abcdefghijklmnopqrstuvwxyz':
                    # 形成新单词
                    next_word = word[:i] + char + word[i+1:]

                    # 如果新单词在字典中
                    if next_word in word_set:
                        # 将新单词及其路径长度加入队列
                        queue.append((next_word, length+1))

                        # 从字典中移除这个单词,防止重复访问
                        word_set.remove(next_word)

        # 如果队列为空还没有找到目标词,则返回0
        return 0

Leetcode 841. 钥匙和房间

题目描述

有 n 个房间,房间按从 0 到 n - 1 编号。最初,除 0 号房间外的其余所有房间都被锁住。你的目标是进入所有的房间。然而,你不能在没有获得钥匙的时候进入锁住的房间。

当你进入一个房间,你可能会在里面找到一套不同的钥匙,每把钥匙上都有对应的房间号,即表示钥匙可以打开的房间。你可以拿上所有钥匙去解锁其他房间。

给你一个数组 rooms 其中 rooms[i] 是你进入 i 号房间可以获得的钥匙集合。如果能进入 所有 房间返回 true,否则返回 false

 

示例 1:

输入: rooms = [[1],[2],[3],[]]
输出: true
解释:
我们从 0 号房间开始,拿到钥匙 1。
之后我们去 1 号房间,拿到钥匙 2。
然后我们去 2 号房间,拿到钥匙 3。
最后我们去了 3 号房间。
由于我们能够进入每个房间,我们返回 true

示例 2:

输入: rooms = [[1,3],[3,0,1],[2],[0]]
输出: false
解释: 我们不能进入 2 号房间。

 

提示:

  • n == rooms.length
  • 2 <= n <= 1000
  • 0 <= rooms[i].length <= 1000
  • 1 <= sum(rooms[i].length) <= 3000
  • 0 <= rooms[i][j] < n
  • 所有 rooms[i] 的值 互不相同

解法

使用深搜三部曲:

  1. 确定函数参数,这里定义一个全局的visited数组用于记录哪些节点被遍历过,因此不需要传递这个参数,所以这里=只需要传递当前的房间key和房间与钥匙之间的关系rooms;
  2. 返回条件:如果当前房间被遍历过了,直接返回就行;
  3. 单层递归逻辑:如果当前房间没有被遍历过,则标记为被遍历过,之后,拿到钥匙,遍历当前房间能够访问的所有房间;

注意:如果是无向图,则很容易使用并查集来解决,但是有向图中即使所有节点都是相连的,也有可能到不了,反例参考代码随想录

image.png

使用dfs的完整代码如下:

from typing import List  # 请记得导入 List,如果你还没有导入的话

class Solution:
    def __init__(self):
        # 初始化一个 visited 列表来跟踪哪些房间已经被访问
        self.visited = []

    def canVisitAllRooms(self, rooms: List[List[int]]) -> bool:
        # 将 visited 列表设置为与房间数量相同的长度,并全部初始化为 False
        self.visited = [False] * len(rooms)
        
        # 从 0 号房间开始进行深度优先搜索
        self.dfs(0, rooms)

        # 检查是否所有房间都已被访问(即 visited 列表的所有元素都是 True)
        return all(item == True for item in self.visited)

    def dfs(self, key, rooms):
        # 如果这个房间已经被访问,直接返回
        if self.visited[key]:
            return
        
        # 标记这个房间为已访问
        self.visited[key] = True

        # 对这个房间中的每把钥匙进行深度优先搜索
        for key in rooms[key]:
            self.dfs(key, rooms)