导语
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 <= 10endWord.length == beginWord.length1 <= wordList.length <= 5000wordList[i].length == beginWord.lengthbeginWord、endWord和wordList[i]由小写英文字母组成beginWord != endWordwordList中的所有字符串 互不相同
解法
这是一道困难题目,需要我们自己去建立图,例如示例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.length2 <= n <= 10000 <= rooms[i].length <= 10001 <= sum(rooms[i].length) <= 30000 <= rooms[i][j] < n- 所有
rooms[i]的值 互不相同
解法
使用深搜三部曲:
- 确定函数参数,这里定义一个全局的visited数组用于记录哪些节点被遍历过,因此不需要传递这个参数,所以这里=只需要传递当前的房间key和房间与钥匙之间的关系rooms;
- 返回条件:如果当前房间被遍历过了,直接返回就行;
- 单层递归逻辑:如果当前房间没有被遍历过,则标记为被遍历过,之后,拿到钥匙,遍历当前房间能够访问的所有房间;
注意:如果是无向图,则很容易使用并查集来解决,但是有向图中即使所有节点都是相连的,也有可能到不了,反例参考代码随想录:
使用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)