LeetCode 127困难图 · BFS
单词接龙 图解题解
这道题到底在问什么
每次只改一个字母、且改完必须是词表里的词。求 hit → cog 的最短转换序列长度(含首尾)。
- 输入
- begin="hit", end="cog"
- 输出
- 5
最优解:一步一步想明白
- 3记住:每条边只差一个字母,BFS 逐层保证第一次到达就是最短。
- 4先把 10 个词全摆上:节点=单词,两词相差一个字母就连边。还没开始搜,全是灰的。
- 5起点 hit 入队,dist=1(它是序列第 1 个词)。队列:[hit]。
- 6出队 hit(标记为正在访问),看它能一步变成哪些还没访问过的词。
- 7hit 改一个字母可得 hot(走这条边),它的 dist = hit 的层 +1 = ②,入队。队列:[hot]。
- 8hit 改一个字母可得 hut(走这条边),它的 dist = hit 的层 +1 = ②,入队。队列:[hot, hut]。
- 9hit 改一个字母可得 lit(走这条边),它的 dist = hit 的层 +1 = ②,入队。队列:[hot, hut, lit]。
- 10出队 hot(标记为正在访问),看它能一步变成哪些还没访问过的词。
- 11hot 改一个字母可得 dot(走这条边),它的 dist = hot 的层 +1 = ③,入队。队列:[hut, lit, dot]。
- 12hot 改一个字母可得 lot(走这条边),它的 dist = hot 的层 +1 = ③,入队。队列:[hut, lit, dot, lot]。
- 13hot 改一个字母可得 bot(走这条边),它的 dist = hot 的层 +1 = ③,入队。队列:[hut, lit, dot, lot, bot]。
- 14出队 hut(标记为正在访问),看它能一步变成哪些还没访问过的词。
- 15出队 lit(标记为正在访问),看它能一步变成哪些还没访问过的词。
- 16出队 dot(标记为正在访问),看它能一步变成哪些还没访问过的词。
- 17dot 改一个字母可得 dog(走这条边),它的 dist = dot 的层 +1 = ④,入队。队列:[lot, bot, dog]。
- 18出队 lot(标记为正在访问),看它能一步变成哪些还没访问过的词。
- 19lot 改一个字母可得 log(走这条边),它的 dist = lot 的层 +1 = ④,入队。队列:[bot, dog, log]。
- 20出队 bot(标记为正在访问),看它能一步变成哪些还没访问过的词。
- 21bot 改一个字母可得 bog(走这条边),它的 dist = bot 的层 +1 = ④,入队。队列:[dog, log, bog]。
- 22出队 dog(标记为正在访问),看它能一步变成哪些还没访问过的词。
- 23dog 改一个字母可得 cog(走这条边),它的 dist = dog 的层 +1 = ⑤,入队。队列:[log, bog, cog]。
- 24出队 log(标记为正在访问),看它能一步变成哪些还没访问过的词。
- 25出队 bog(标记为正在访问),看它能一步变成哪些还没访问过的词。
- 26出队 cog —— 正是终点!它的 dist=⑤,即最短序列共 5 个词。BFS 第一次到达就是最短,搜索结束。
- 27最短转换链:hit → hot → dot → dog → cog,共 5 个词。每相邻两词只差一个字母(一条边),这就是 hit 变到 cog 的最少步数。
⚠️ 容易写错的地方
✗ 错:忘了判 end 在词表里
✓ 对:不在直接返回 0
end 不在词表则永远到不了
✗ 错:没记 visited 重复入队
✓ 对:入队即标记已访问
否则环里反复扩、可能超时甚至死循环
✗ 错:用 DFS 找最短
✓ 对:无权最短路必须 BFS
DFS 找到的不一定最短
完整代码(Python / C++ / Java)
Python
from collections import deque
def ladderLength(begin, end, wordList):
ws = set(wordList)
if end not in ws: return 0
q = deque([(begin, 1)])
seen = {begin}
while q:
w, d = q.popleft()
if w == end: return d
for i in range(len(w)):
for c in 'abcdefghijklmnopqrstuvwxyz':
nx = w[:i] + c + w[i+1:]
if nx in ws and nx not in seen:
seen.add(nx); q.append((nx, d + 1))
return 0C++
int ladderLength(string begin, string end, vector<string>& wl){
unordered_set<string> ws(wl.begin(), wl.end());
if(!ws.count(end)) return 0;
queue<pair<string,int>> q; q.push({begin,1});
unordered_set<string> seen{begin};
while(!q.empty()){
auto [w,d]=q.front(); q.pop();
if(w==end) return d;
for(int i=0;i<(int)w.size();i++){
string nx=w;
for(char c='a';c<='z';c++){ nx[i]=c;
if(ws.count(nx)&&!seen.count(nx)){ seen.insert(nx); q.push({nx,d+1}); } }
}
}
return 0;
}Java
public int ladderLength(String begin, String end, List<String> wl){
Set<String> ws = new HashSet<>(wl);
if(!ws.contains(end)) return 0;
Queue<String> q = new LinkedList<>(); q.add(begin);
Set<String> seen = new HashSet<>(); seen.add(begin);
int d = 1;
while(!q.isEmpty()){
int sz = q.size();
for(int k=0;k<sz;k++){
String w = q.poll();
if(w.equals(end)) return d;
char[] arr = w.toCharArray();
for(int i=0;i<arr.length;i++){
char old = arr[i];
for(char c='a';c<='z';c++){ arr[i]=c;
String nx = new String(arr);
if(ws.contains(nx)&&!seen.contains(nx)){ seen.add(nx); q.add(nx); } }
arr[i]=old;
}
}
d++;
}
return 0;
}复杂度
时间
O(N·L·26)
N 个词 × 每词 L 位 × 26 字母
空间
O(N)
词集 + 队列 + 已访问
看不够?换成动画再走一遍
上面的推演每一步都对应一帧动画。点开交互动画版,能一步步看着 单词接龙 的数据怎么变、指针怎么走,还能切 Python / Java / C++ 跟着练。
面试官可能追问
如何加速到双向 BFS?+
从 begin 和 end 两头同时 BFS,每次扩较小的一侧,相遇即可,层数相加。规模大时显著更快。
LC126 要输出所有最短路径怎么办?+
BFS 分层记录每个词的所有前驱,再从 end 回溯 DFS 还原全部最短路径。
想听吴师兄把这道题讲给你听?
文字版和动画都随便看。开通图解算法年卡,可以听吴师兄把 单词接龙 一步步讲透(全站已上线 905 份讲透,持续扩充), 卡住的地方还有 AI 私教小欧就着动画帮你拆到懂。
把这道题真正拿下
本平台为独立第三方培训机构,与华为技术有限公司无任何关联;课程的服务内容与权益以购买协议为准,学习效果因个人情况而异。「华为 OD」「华为可信」等仅为对岗位与考试方向的客观描述,相关商标归各自权利人所有。