题目链接:https://leetcode.com/problems/word-ladder-ii/
题目:
Given two words (beginWord and endWord), and a dictionary's word list, find all shortest transformation sequence(s) from beginWord toendWord, such that:
- Only one letter can be changed at a time
- Each intermediate word must exist in the word list
For example,
Given:
beginWord = "hit"
endWord = "cog"
wordList = ["hot","dot","dog","lot","log"]
Return
[ ["hit","hot","dot","dog","cog"], ["hit","hot","lot","log","cog"] ]
Note:
- All words have the same length.
- All words contain only lowercase alphabetic characters.
思路:
在http://blog.csdn.net/yeqiuzs/article/details/51872443 解法的基础上修改:
改为需要保存遍历过程中的路径,要找到所有最优解
可以有两种方法:我的做法是用队列保存 当前遍历结点队列(即代码中的queue)每一个结点的一条追踪路径。因为都是队列,可以保证一致的顺序性;另一种是用hashmap保存每个结点的邻接表,建立邻接表后,先bfs计算出最短路径,然后dfs邻接表得到所有解。
用第一种方法,直接在word ladder 基础上修改,虽然能得到正确解,但在大数据下会超时,考虑优化。 很容易就发现,当word list集合过大的时候,越遍历到后面原来的做法仍然需要遍历整个wordlist,实际上是没有必要的。因为如果之后存在最优解,之前遍历过的结点不会出现在以后,否则会形成循环,而循环是可以删去的。 所以增加一个变量currLevel记录当前遍历到哪一层了,当进入下一层时,则将当前层次已经遍历过的结点从wordlist中删去,下一层没必要还遍历这些删去的结点了。要注意,一定要在进入下一层时,才删除(剪枝),因为在相同层次遍历时删除,可能会丢掉最优解,比如图中存在两条最优解:1->2->3->4, 1->5->3->4,其中2,5结点是同一层,如果在第一次遍历到结点3后就删除3,则第二条路径1-5就到达不了3了,所以只有当结点2、5都到了3后进入结点4之前删除3才不会丢弃解。
算法:
public List<List<String>> findLadders(String beginWord, String endWord, Set<String> wordList) {
Queue<List<String>> tracks = new LinkedList<List<String>>(); // 保存遍历过程中可能到达end结点的路径
Queue<Word> queue = new LinkedList<Word>();
List<List<String>> res = new ArrayList<List<String>>();
Set<String> remove = new HashSet<String>();
int min = Integer.MAX_VALUE;// 最短距离
int currLevel = 1; // 当前遍历的层数
queue.offer(new Word(beginWord, 1));
List<String> t = new ArrayList<String>();
t.add(beginWord);
tracks.offer(t);
wordList.add(endWord);
while (!queue.isEmpty()) {
Word w = queue.poll();
currLevel = Math.max(currLevel, w.steps);// 更新当前遍历层数
List<String> track = tracks.poll();
if (w.word.equals(endWord) && w.steps <= min) { // 如果到了目的结点,且遍历层数小于或等于min
min = w.steps;
res.add(track);
continue;
}
if (w.steps > min) // 如果大于min则之后的遍历不可能还有最短距离这么长的路径了
break;
char[] ws = w.word.toCharArray();
for (int i = 0; i < w.word.length(); i++) {
char tmp = ws[i];
for (char j = 'a'; j < 'z'; j++) {
if (tmp == j) // 不能原地循环
continue;
ws[i] = j;
String ns = new String(ws);
if (wordList.contains(ns)) {
queue.offer(new Word(ns, w.steps + 1));
List<String> nextTrack = new ArrayList<String>(track);
nextTrack.add(ns);
tracks.offer(nextTrack);
if (!ns.equals(endWord))
remove.add(ns);
}
}
ws[i] = tmp;
}
if (!queue.isEmpty() && queue.peek().steps > currLevel) {// 将进入下一层
for (String r : remove) // 则将以后不会访问的结点删去 ,剪枝
wordList.remove(r);
}
}
return res;
}
class Word {
int steps;
String word;
public Word(String word, int steps) {
this.word = word;
this.steps = steps;
}
}