文章目录
- 1 题目理解
- 2 BFS
- 3 双向BFS
1 题目理解
给定两个单词(beginWord 和 endWord)和一个字典,找到从 beginWord 到 endWord 的最短转换序列的长度。转换需遵循如下规则:
每次转换只能改变一个字母。
转换过程中的中间单词必须是字典中的单词。
说明:
如果不存在这样的转换序列,返回 0。
所有单词具有相同的长度。
所有单词只由小写字母组成。
字典中不存在重复的单词。
你可以假设 beginWord 和 endWord 是非空的,且二者不相同。
输入:两个单词:beginWord,endWord,一个字典
规则:查找从beginWord到endWord的最短转换序列。转换中每次只能变一个字母。转换过程中的单词必须是字典中的单词。
输出:最短序列长度,没有就返回0。
输入:
beginWord = “hit”,
endWord = “cog”,
wordList = [“hot”,“dot”,“dog”,“lot”,“log”,“cog”]
输出: 5
解释: 一个最短转换序列是 “hit” -> “hot” -> “dot” -> “dog” -> “cog”,
返回它的长度 5。
2 BFS
解法内容来源于力扣。
要求最短序列,想到要用bfs。想到bfs,自然想到图。题目中的图是隐形的。我们可以抽象出图。
可以将所有单词抽象为一个节点。如果两个单词之间只有一个字母不同,则这两个单词之间有条边。边是双向的。
例子中的图是这样的。我们以beginWord为起点,endWord为终点,找到最短路径。
在建图过程中,按照普通想法,我们可以枚举每一对词语,看他们是否相差一个字符。但这样效率太低,我们可以优化图。我们可以建一个虚拟节点。对于单词hit创建3个虚拟节点:it,ht,hi*。hit与这三个节点有双向边。如果词典中有一个单词能够转为hi这种格式,那必然它也与hi有条双向边。
最后注意bfs找到最短路径之后,因为加入了虚拟节点所以路径长度需要除以2。
class Solution {public int ladderLength(String beginWord, String endWord, List<String> wordList) {if(wordList==null || !wordList.contains(endWord)){return 0;}Map<String,List<String>> allComboDict = new HashMap<String,List<String>>();addToDict(beginWord,allComboDict);for(String w : wordList){addToDict(w,allComboDict);}Queue<String> queue = new LinkedList<String>();queue.offer(beginWord);int step = 0;Map<String,Boolean> visited = new HashMap<String,Boolean>();while(!queue.isEmpty()){step++;int size = queue.size();for(int i=0;i<size;i++){String node = queue.poll();if(node.equals(endWord)){return step/2+1;}for(String toNode: allComboDict.get(node)){if(!visited.containsKey(toNode)){visited.put(toNode,true);queue.offer(toNode);}}}}return 0;}private void addToDict(String word,Map<String,List<String>> allComboDict ){char[] array = word.toCharArray();List<String> listOfWord = allComboDict.getOrDefault(word,new ArrayList<String>());for(int i=0;i<word.length();i++){char ch = array[i];array[i] = '*';String text = new String(array);List<String> list = allComboDict.getOrDefault(text,new ArrayList<String>());list.add(word);allComboDict.put(text,list);listOfWord.add(text);array[i] = ch;}allComboDict.put(word,listOfWord);}
}
时间复杂度:O(w∗l2)O(w*l^2)O(w∗l2),w是单词个数,l是每个单词的长度。
建图过程中,对于每个单词需要枚举它连接到的所有虚拟节点,复杂度为O(w),每个单词的长度为l,复杂度为O(wl)。
bfs过程中每个节点最多有l条边。所以最终时间复杂度O(wl*l)。
3 双向BFS
根据给定字典构造的图可能会很大。BFS的搜索空间会依赖每层的分支数量增加而指数级增加。如果同时使用两个广度优先搜索。一个从beginWord开始搜索,一个从endWord开始搜索。每次从两边各扩展一层节点,当发现某一时刻,两边都访问过同一节点时,就停止搜索。
class Solution {public int ladderLength(String beginWord, String endWord, List<String> wordList) {if(wordList==null || !wordList.contains(endWord)){return 0;}Map<String,List<String>> allComboDict = new HashMap<String,List<String>>();addToDict(beginWord,allComboDict);for(String w : wordList){addToDict(w,allComboDict);}Queue<String> queue = new LinkedList<String>();queue.offer(beginWord);Map<String,Integer> visited = new HashMap<String,Integer>();visited.put(beginWord,1);Queue<String> queueEnd = new LinkedList<String>();queueEnd.offer(endWord);Map<String,Integer> visitedEnd = new HashMap<String,Integer>();visitedEnd.put(endWord,1);while(!queue.isEmpty() && !queueEnd.isEmpty()){int step = visiteNode(queue,visited,visitedEnd,allComboDict);if(step>0){return step;}step = visiteNode(queueEnd,visitedEnd,visited,allComboDict);if(step>0){return step;}}return 0;}private int visiteNode(Queue<String> queue, Map<String,Integer> visited, Map<String,Integer> checkMap,Map<String,List<String>> allComboDict ){int size = queue.size();for(int i=0;i<size;i++){String node = queue.poll();int step = visited.get(node);if(checkMap.containsKey(node)){return (visited.get(node) + checkMap.get(node))/2;}for(String toNode: allComboDict.get(node)){if(!visited.containsKey(toNode)){visited.put(toNode,step+1);queue.offer(toNode);}}}return -1;}private void addToDict(String word,Map<String,List<String>> allComboDict ){char[] array = word.toCharArray();List<String> listOfWord = allComboDict.getOrDefault(word,new ArrayList<String>());for(int i=0;i<word.length();i++){char ch = array[i];array[i] = '*';String text = new String(array);List<String> list = allComboDict.getOrDefault(text,new ArrayList<String>());list.add(word);allComboDict.put(text,list);listOfWord.add(text);array[i] = ch;}allComboDict.put(word,listOfWord);}
}