LeetCode 126. 单词接龙 II
字数 695 2025-11-12 05:04:12

LeetCode 126. 单词接龙 II

题目描述:
给定两个单词(beginWord 和 endWord)和一个字典 wordList,找出所有从 beginWord 到 endWord 的最短转换序列。转换需遵循如下规则:

  1. 每次转换只能改变一个字母
  2. 转换过程中的中间单词必须是字典中的单词

说明:

  • 如果不存在这样的转换序列,返回空列表
  • 所有单词具有相同的长度
  • 所有单词只由小写字母组成
  • 字典中不存在重复的单词
  • beginWord 和 endWord 非空,且不相同

解题过程:

步骤1:问题分析
这是一个典型的图搜索问题:

  • 每个单词可以看作图中的一个节点
  • 如果两个单词只有一个字母不同,它们之间有一条边
  • 需要找到所有从起点到终点的最短路径

步骤2:构建图结构
为了高效查找相邻节点,我们使用"通配符"方法:

  • 对每个单词,生成所有可能的变化模式
  • 比如"hot"可以生成"ot"、"ht"、"ho*"
  • 拥有相同模式的单词互为相邻节点
from collections import defaultdict

def build_graph(wordList):
    graph = defaultdict(list)
    for word in wordList:
        for i in range(len(word)):
            pattern = word[:i] + '*' + word[i+1:]
            graph[pattern].append(word)
    return graph

步骤3:双向BFS寻找最短路径长度
为了优化搜索效率,我们使用双向BFS:

  • 从起点和终点同时开始搜索
  • 当两个方向的搜索相遇时,找到最短路径
def bidirectional_bfs(beginWord, endWord, graph):
    if endWord not in wordSet:
        return 0
    
    # 初始化两个方向的队列和访问集合
    begin_queue = deque([beginWord])
    end_queue = deque([endWord])
    begin_visited = {beginWord: 1}
    end_visited = {endWord: 1}
    
    while begin_queue and end_queue:
        # 从较小的一端开始扩展
        if len(begin_queue) <= len(end_queue):
            level = expand_level(begin_queue, begin_visited, end_visited, graph)
        else:
            level = expand_level(end_queue, end_visited, begin_visited, graph)
        
        if level > 0:
            return level
    
    return 0

def expand_level(queue, visited, other_visited, graph):
    level_size = len(queue)
    for _ in range(level_size):
        word = queue.popleft()
        current_level = visited[word]
        
        # 生成所有相邻单词
        for i in range(len(word)):
            pattern = word[:i] + '*' + word[i+1:]
            for neighbor in graph[pattern]:
                if neighbor in other_visited:
                    return current_level + other_visited[neighbor]
                if neighbor not in visited:
                    visited[neighbor] = current_level + 1
                    queue.append(neighbor)
    return 0

步骤4:DFS回溯构建所有最短路径
知道最短路径长度后,使用DFS回溯构建所有路径:

def dfs_backtrack(word, endWord, path, result, graph, distance, visited):
    if word == endWord:
        result.append(path[:])
        return
    
    if len(path) >= distance:  # 超过最短路径长度,剪枝
        return
    
    for i in range(len(word)):
        pattern = word[:i] + '*' + word[i+1:]
        for neighbor in graph[pattern]:
            if neighbor not in visited and neighbor in wordSet:
                visited.add(neighbor)
                path.append(neighbor)
                dfs_backtrack(neighbor, endWord, path, result, graph, distance, visited)
                path.pop()
                visited.remove(neighbor)

步骤5:完整算法整合
将上述步骤整合成完整解决方案:

from collections import defaultdict, deque

def findLadders(beginWord, endWord, wordList):
    if endWord not in wordList:
        return []
    
    wordSet = set(wordList)
    wordSet.add(beginWord)
    
    # 构建图
    graph = defaultdict(list)
    for word in wordSet:
        for i in range(len(word)):
            pattern = word[:i] + '*' + word[i+1:]
            graph[pattern].append(word)
    
    # 双向BFS找到最短路径长度
    def bidirectional_bfs():
        if beginWord == endWord:
            return 1
            
        begin_visited = {beginWord: 1}
        end_visited = {endWord: 1}
        begin_queue = deque([beginWord])
        end_queue = deque([endWord])
        
        while begin_queue and end_queue:
            # 总是扩展较小的一端
            if len(begin_queue) <= len(end_queue):
                queue, visited, other_visited = begin_queue, begin_visited, end_visited
            else:
                queue, visited, other_visited = end_queue, end_visited, begin_visited
                
            level_size = len(queue)
            for _ in range(level_size):
                current = queue.popleft()
                current_level = visited[current]
                
                for i in range(len(current)):
                    pattern = current[:i] + '*' + current[i+1:]
                    for neighbor in graph[pattern]:
                        if neighbor in other_visited:
                            return current_level + other_visited[neighbor]
                        if neighbor not in visited:
                            visited[neighbor] = current_level + 1
                            queue.append(neighbor)
            return 0
    
    shortest_length = bidirectional_bfs()
    if shortest_length == 0:
        return []
    
    # DFS回溯构建所有路径
    result = []
    
    def dfs(current, path, visited):
        if len(path) == shortest_length:
            if current == endWord:
                result.append(path[:])
            return
        
        if len(path) > shortest_length:
            return
            
        for i in range(len(current)):
            pattern = current[:i] + '*' + current[i+1:]
            for neighbor in graph[pattern]:
                if neighbor not in visited and neighbor in wordSet:
                    visited.add(neighbor)
                    path.append(neighbor)
                    dfs(neighbor, path, visited)
                    path.pop()
                    visited.remove(neighbor)
    
    dfs(beginWord, [beginWord], set([beginWord]))
    return result

步骤6:复杂度分析

  • 时间复杂度:O(N × L²),其中N是单词数量,L是单词长度
  • 空间复杂度:O(N × L) 用于存储图结构和中间结果

这个算法通过双向BFS确定最短路径长度,再通过DFS回溯构建所有最短路径,既保证了找到最优解,又避免了不必要的搜索。

LeetCode 126. 单词接龙 II 题目描述: 给定两个单词(beginWord 和 endWord)和一个字典 wordList,找出所有从 beginWord 到 endWord 的最短转换序列。转换需遵循如下规则: 每次转换只能改变一个字母 转换过程中的中间单词必须是字典中的单词 说明: 如果不存在这样的转换序列,返回空列表 所有单词具有相同的长度 所有单词只由小写字母组成 字典中不存在重复的单词 beginWord 和 endWord 非空,且不相同 解题过程: 步骤1:问题分析 这是一个典型的图搜索问题: 每个单词可以看作图中的一个节点 如果两个单词只有一个字母不同,它们之间有一条边 需要找到所有从起点到终点的最短路径 步骤2:构建图结构 为了高效查找相邻节点,我们使用"通配符"方法: 对每个单词,生成所有可能的变化模式 比如"hot"可以生成" ot"、"h t"、"ho* " 拥有相同模式的单词互为相邻节点 步骤3:双向BFS寻找最短路径长度 为了优化搜索效率,我们使用双向BFS: 从起点和终点同时开始搜索 当两个方向的搜索相遇时,找到最短路径 步骤4:DFS回溯构建所有最短路径 知道最短路径长度后,使用DFS回溯构建所有路径: 步骤5:完整算法整合 将上述步骤整合成完整解决方案: 步骤6:复杂度分析 时间复杂度:O(N × L²),其中N是单词数量,L是单词长度 空间复杂度:O(N × L) 用于存储图结构和中间结果 这个算法通过双向BFS确定最短路径长度,再通过DFS回溯构建所有最短路径,既保证了找到最优解,又避免了不必要的搜索。