LeetCode 126:单词接龙 II
LeetCode 126:单词接龙 II
问题本质与核心挑战
需找到 所有从 beginWord
到 endWord
的最短转换序列,要求:
- 相邻单词仅差一个字母;
- 中间单词必须在
wordList
中。
核心挑战:
- 如何高效找到最短路径(BFS 天然适合,但需记录路径);
- 如何收集所有最短路径(需回溯前驱节点);
- 如何避免重复搜索(通过层级控制和距离记录)。
核心思路:BFS 找层级 + 回溯找路径
1. 预处理:构建模式映射(邻接表优化)
将每个单词转换为 通配符模式(如 hot
→ *ot
、h*t
、ho*
),同一模式的单词互为相邻(差一个字母)。通过哈希表 patternMap
存储模式→单词列表,快速查找相邻单词。
2. BFS 遍历:记录距离和前驱
- 距离
distance
:记录每个单词到beginWord
的最短步数(层级); - 前驱
predecessors
:记录每个单词的前驱节点(用于回溯路径); - 层级控制:BFS 保证层级递增,一旦找到
endWord
,后续层级更长,可提前终止。
3. 回溯生成路径
从 endWord
出发,递归回溯所有前驱节点,直到 beginWord
,反转路径后收集结果。
算法步骤详解(以示例 1 为例:beginWord="hit", endWord="cog", wordList=["hot","dot","dog","lot","log","cog"]
)
步骤 1:预处理与边界检查
- 将
wordList
转成Set
(快速判断存在性); - 若
endWord
不在wordSet
中,直接返回空列表(如示例 2); - 构建
patternMap
:
例如,Map<String, List<String>> patternMap = new HashMap<>(); for (String word : wordSet) {for (int i = 0; i < word.length(); i++) {String pattern = word.substring(0, i) + "*" + word.substring(i+1);patternMap.computeIfAbsent(pattern, k -> new ArrayList<>()).add(word);} }
hot
生成模式*ot
、h*t
、ho*
,对应单词dot
、lot
、hot
等。
步骤 2:BFS 遍历,记录距离和前驱
- 初始化:队列加入
beginWord
,distance
记录beginWord
层级为1
; - 遍历队列:
- 对当前单词
current
,生成所有模式,找到相邻单词neighbor
; - 若
neighbor
未访问:记录距离(currentLevel+1
)、前驱,加入队列; - 若
neighbor
已访问且距离等于currentLevel+1
(同一层级的不同路径):添加前驱; - 若遇到
endWord
,记录当前层级shortestLevel
,处理完当前层级后终止 BFS。
- 对当前单词
示例 1 的 BFS 过程(关键步骤):
层级 | 当前单词 | 相邻单词 | 距离更新 | 前驱记录 |
---|---|---|---|---|
1 | hit | hot | hot→2 | hot: [hit] |
2 | hot | dot、lot | dot→3, lot→3 | dot: [hot], lot: [hot] |
3 | dot | dog | dog→4 | dog: [dot] |
3 | lot | log | log→4 | log: [lot] |
4 | dog | cog | cog→5 | cog: [dog] |
4 | log | cog | cog→5(补充前驱) | cog: [dog, log] |
步骤 3:回溯生成所有最短路径
从 endWord
出发,递归遍历前驱节点,直到 beginWord
,反转路径后加入结果:
private void backtrack(String current, String beginWord, Map<String, List<String>> predecessors, List<String> path, List<List<String>> result) {if (current.equals(beginWord)) {List<String> reversed = new ArrayList<>(path);Collections.reverse(reversed);result.add(reversed);return;}for (String pre : predecessors.getOrDefault(current, new ArrayList<>())) {path.add(pre);backtrack(pre, beginWord, predecessors, path, result);path.remove(path.size() - 1);}
}
示例 1 的回溯过程:
cog
的前驱是dog
和log
:dog
→dot
→hot
→hit
→ 路径[hit, hot, dot, dog, cog]
;log
→lot
→hot
→hit
→ 路径[hit, hot, lot, log, cog]
。
完整代码(Java)
import java.util.*;class Solution {public List<List<String>> findLadders(String beginWord, String endWord, List<String> wordList) {List<List<String>> result = new ArrayList<>();Set<String> wordSet = new HashSet<>(wordList);// 边界:endWord不在字典中if (!wordSet.contains(endWord)) {return result;}// 构建模式映射:通配符模式→单词列表Map<String, List<String>> patternMap = new HashMap<>();for (String word : wordSet) {for (int i = 0; i < word.length(); i++) {String pattern = word.substring(0, i) + "*" + word.substring(i + 1);patternMap.computeIfAbsent(pattern, k -> new ArrayList<>()).add(word);}}// BFS初始化:队列、距离、前驱、最短层级Queue<String> queue = new LinkedList<>();queue.offer(beginWord);Map<String, Integer> distance = new HashMap<>();distance.put(beginWord, 1);Map<String, List<String>> predecessors = new HashMap<>();int shortestLevel = -1;while (!queue.isEmpty()) {if (shortestLevel != -1) break; // 已找到最短路径,终止后续层级int size = queue.size();for (int i = 0; i < size; i++) {String current = queue.poll();int currentLevel = distance.get(current);// 找到endWord,记录最短层级if (current.equals(endWord)) {shortestLevel = currentLevel;}// 生成所有相邻单词(通过模式匹配)for (int j = 0; j < current.length(); j++) {String pattern = current.substring(0, j) + "*" + current.substring(j + 1);List<String> neighbors = patternMap.getOrDefault(pattern, new ArrayList<>());for (String neighbor : neighbors) {if (!distance.containsKey(neighbor)) {// 未访问过,记录距离和前驱distance.put(neighbor, currentLevel + 1);predecessors.computeIfAbsent(neighbor, k -> new ArrayList<>()).add(current);queue.offer(neighbor);} else if (distance.get(neighbor) == currentLevel + 1) {// 同一层级的不同路径,补充前驱predecessors.computeIfAbsent(neighbor, k -> new ArrayList<>()).add(current);}}}}}// 未找到路径if (shortestLevel == -1) {return result;}// 回溯生成所有路径List<String> path = new ArrayList<>();path.add(endWord);backtrack(endWord, beginWord, predecessors, path, result);return result;}// 回溯函数:从endWord递归找前驱,直到beginWord,收集路径private void backtrack(String current, String beginWord, Map<String, List<String>> predecessors, List<String> path, List<List<String>> result) {if (current.equals(beginWord)) {List<String> reversed = new ArrayList<>(path);Collections.reverse(reversed);result.add(reversed);return;}// 遍历所有前驱,递归回溯for (String pre : predecessors.getOrDefault(current, new ArrayList<>())) {path.add(pre);backtrack(pre, beginWord, predecessors, path, result);path.remove(path.size() - 1); // 回溯}}
}
关键逻辑解析
- 模式映射:将单词转换为通配符模式,快速定位相邻单词,时间复杂度从
O(n²)
降为O(n×L)
(L
为单词长度)。 - BFS 层级控制:保证首次到达
endWord
时的层级为最短路径,后续层级无需处理。 - 前驱记录:同一层级的不同路径需补充前驱,确保回溯时收集所有可能的最短路径。
- 回溯生成路径:从
endWord
反向遍历前驱,通过递归和路径回溯,生成所有符合条件的序列。
该方法结合 BFS 的层级优势 和 回溯的路径收集能力,高效解决了“最短路径全收集”问题,时间复杂度为 O(N×L + P)
(N
为单词数,L
为单词长度,P
为最短路径数量),是处理此类问题的经典范式。