题目地址:
给定两个单词作为起点和终点,每一步允许从单词中改变某个位置的字母,变为另一个单词。再给定一个单词的列表,问从起点到终点的所有最短路径,要求路径中所有单词都要在列表里(起点不必在,终点必须在)。题目保证所有单词都由小写字母组成。
法1:BFS记录距离 + DFS(这是BFS找路径的标准做法)。如果列表里没有终点那直接返回空列表。接着先从起点单词开始BFS,搜的过程中用哈希表记录路径上每个单词与起点的距离。一旦搜到终点就停止搜索。设从起点到终点的最少步数是k,那么此时哈希表里已经存了所有距离起点k−1步以内的单词,以及一部分距离k步的单词(距离k步的单词我们只关心终点,虽然别的单词也可能会加进去)。接着我们从终点向起点DFS,一路永远找步数比当前位置少1的单词,直到搜到起点(之所以从终点搜,是因为如果从起点搜的话,即使每次搜的都是比自己步数多1的单词,但是有可能搜到第k步的时候搜到了别的单词。从终点开始搜的话能保证搜到第k步一定是起点)。代码如下:
import java.util.*;
public class Solution {
public List<List<String>> findLadders(String beginWord, String endWord, List<String> wordList) {
List<List<String>> res = new ArrayList<>();
Set<String> set = new HashSet<>(wordList);
// 特判一下,如果终点不在列表里则直接返回空
if (!set.contains(endWord)) {
return res;
}
// dist记录每个单词与起点的距离
Map<String, Integer> dist = new HashMap<>();
dist.put(beginWord, 0);
Queue<String> queue = new LinkedList<>();
queue.offer(beginWord);
// 为了搜到终点就退出整个while循环,我们在while循环上加个label
OUT:
while (!queue.isEmpty()) {
String cur = queue.poll();
char[] chs = cur.toCharArray();
for (int i = 0; i < chs.length; i++) {
char c = chs[i];
for (char ch = 'a'; ch <= 'z'; ch++) {
if (ch != c) {
chs[i] = ch;
String next = new String(chs);
// 注意,这里每个单词只允许加入dist一次,第一次加入的时候才是最小步数
if (set.contains(next) && !dist.containsKey(next)) {
dist.put(next, dist.get(cur) + 1);
queue.offer(next);
}
// 搜到终点了就退出整个while循环
if (next.equals(endWord)) {
break OUT;
}
}
}
chs[i] = c;
}
}
dfs(endWord, beginWord, dist, new ArrayList<>(), res);
return res;
}
private void dfs(String cur, String begin, Map<String, Integer> dist, List<String> list, List<List<String>> res) {
list.add(cur);
// 如果搜到起点了,就记录答案
if (cur.equals(begin)) {
// 由于是从终点开始搜的,加入答案的时候要反个序
Collections.reverse(list);
res.add(new ArrayList<>(list));
// 加入答案之后再翻回来
Collections.reverse(list);
// 回溯之前恢复现场
list.remove(list.size() - 1);
return;
}
char[] chs = cur.toCharArray();
for (int i = 0; i < chs.length; i++) {
char c = chs[i];
for (char ch = 'a'; ch <= 'z'; ch++) {
if (ch != c) {
chs[i] = ch;
String next = new String(chs);
// 这里要写dist.containsKey(next),因为起点不一定在set里
if (dist.containsKey(next) && dist.get(next).equals(dist.get(cur) - 1)) {
dfs(next, begin, set, dist, list, res);
}
}
}
chs[i] = c;
}
list.remove(list.size() - 1);
}
}
有关链接: