一、Problem
Given two words (beginWord and endWord), and a dictionary’s word list, find all shortest transformation sequence(s) from beginWord to endWord, such that:
Only one letter can be changed at a time
Each transformed word must exist in the word list. Note that beginWord is not a transformed word.
Note:
- Return an empty list if there is no such transformation sequence.
- All words have the same length.
- All words contain only lowercase alphabetic characters.
- You may assume no duplicates in the word list.
- You may assume beginWord and endWord are non-empty and are not the same.
Input:
beginWord = "hit",
endWord = "cog",
wordList = ["hot","dot","dog","lot","log","cog"]
Output:
[
["hit","hot","dot","dog","cog"],
["hit","hot","lot","log","cog"]
]
二、Solution
方法一:bfs + 回溯
- bfs 構建從 beginWord 變換得到的合法新單詞 newWord 的鄰接表。
- 因爲是 bfs,所以當找到 newWord 爲 endWord 時,遍歷完當前層得到的路徑就是最短的了,退出 bfs。
Q:爲什麼 bfs 完還需要回溯求出所有路徑?
A:其實也可以在 bfs 的過程中記錄遍歷得到的路徑,但這樣做會比較低效,因爲 bfs 不一定就是可以得到合法路徑的,而無效的路徑中的結點的添加卻是無用的。
Q:爲什麼要遍歷完一層纔將訪問過的結點標記爲 visited?
A:因爲有可能當前層的某個單詞 w1 的下一個單詞 nw1 和當前層的別的單詞 w2 的下一個單詞 nw2 是相同的(nw1 == nw2),而 w1 和 w2 都可以到達 endWord,所以不能提前設置狀態。
class Solution {
Map<String, Set<String>> e;
Set<String> st;
void bfs(String bW, String eW) {
Queue<String> q = new LinkedList<>();
Set<String> vis = new HashSet<>();
q.add(bW);
vis.add(bW);
int N = eW.length();
Set<String> nextLevel = new HashSet<>();
while (!q.isEmpty()) {
int sz = q.size();
boolean found = false;
for (int i = 0; i < sz; i++) {
String cW = q.poll();
char[] cs = cW.toCharArray();
for (int j = 0; j < N; j++) { //將每一個字符都嘗試變換
char oriChar = cs[j];
for (char k = 'a'; k <= 'z'; k++) {
if (k == oriChar)
continue;
cs[j] = k;
String nW = new String(cs);
if (!st.contains(nW) || vis.contains(nW))
continue;
if (eW.equals(nW))
found = true;
q.add(nW);
e.computeIfAbsent(cW, v -> new HashSet<>()).add(nW);
nextLevel.add(nW);
}
cs[j] = oriChar;
}
}
if (found)
break;
vis.addAll(nextLevel);
nextLevel.clear();
}
}
List<List<String>> ps;
LinkedList<String> p;
void dfs(String cur, String eW) {
if (cur.equals(eW)) {
ps.add(new LinkedList<>(p));
return;
}
if (!e.containsKey(cur))
return;
for (String to : e.get(cur)) {
p.add(to);
dfs(to, eW);
p.pollLast();
}
}
public List<List<String>> findLadders(String bW, String eW, List<String> wL) {
ps = new LinkedList<>();
if (wL == null || wL.size() == 0)
return ps;
p = new LinkedList<>();
e = new HashMap<>();
st = new HashSet<>(wL);
bfs(bW, eW);
// Set<String> nei = e.getOrDefault(bW, new HashSet<String>());
// for (String to : nei) System.out.print(to);
p.add(bW);
dfs(bW, eW);
return ps;
}
}
複雜度分析
- 時間複雜度:,
- 空間複雜度:,
方法二:雙向 bfs
別人的思路:從前到後和從後到前,同時遍歷,兩個遍歷遇到相同的結點就表明該路徑爲合法路徑。因爲兩個方向的遍歷會有兩個容器保存結點,爲了方便,遍歷時可以設定一個規則:從 size 小的集合開始遍歷,當 size 小的集合變爲更大者的時候,就要交換集合,同時邊的添加方向也需要改變。
class Solution {
Map<String, Set<String>> e;
Set<String> st;
boolean bfs(String bW, String eW) {
Set<String> b2e = new HashSet<>(), e2b = new HashSet<>(), vis = new HashSet<>();
b2e.add(bW); e2b.add(eW);
vis.add(bW); vis.add(eW);
boolean found = false, isB2E = true;
while (!b2e.isEmpty() && !e2b.isEmpty()) {
if (b2e.size() > e2b.size()) {
Set<String> t = b2e;
b2e = e2b;
e2b = t;
isB2E = !isB2E;
}
Set<String> nextLevel = new HashSet<>();
for (String cW : b2e) {
char[] cs = cW.toCharArray();
for (int j = 0; j < cs.length; j++) { //將每一個字符都嘗試變換
char oriChar = cs[j];
for (char k = 'a'; k <= 'z'; k++) {
if (k == oriChar)
continue;
cs[j] = k;
String nW = new String(cs);
if (!st.contains(nW))
continue;
if (e2b.contains(nW)) {
found = true;
addEdge(cW, nW, isB2E);
}
if (!vis.contains(nW)) {
nextLevel.add(nW);
addEdge(cW, nW, isB2E);
}
}
cs[j] = oriChar;
}
}
if (found)
return true;
b2e = nextLevel;
vis.addAll(nextLevel);
}
return false; // 注
}
void addEdge(String cur, String nex, boolean isB2E) {
if (!isB2E) {
String t = cur;
cur = nex;
nex = t;
}
e.computeIfAbsent(cur, v -> new HashSet<>()).add(nex);
}
List<List<String>> ps;
LinkedList<String> p;
void dfs(String cur, String eW) {
if (cur.equals(eW)) {
ps.add(new LinkedList<>(p));
return;
}
if (!e.containsKey(cur))
return;
for (String to : e.get(cur)) {
p.add(to);
dfs(to, eW);
p.pollLast();
}
}
public List<List<String>> findLadders(String bW, String eW, List<String> wL) {
ps = new LinkedList<>();
st = new HashSet<>(wL);
if (wL == null || !st.contains(eW)) // 注
return ps;
p = new LinkedList<>();
e = new HashMap<>();
boolean found = bfs(bW, eW);
if (!found)
return ps;
p.add(bW);
dfs(bW, eW);
return ps;
}
}
複雜度分析
- 時間複雜度:,n 爲單詞數