题目:
给定两个单词(beginWord 和 endWord)和一个字典,找到从 beginWord 到 endWord 的最短转换序列的长度。转换需遵循如下规则:
1.每次转换只能改变一个字母。
2.转换过程中的中间单词必须是字典中的单词。
说明:
如果不存在这样的转换序列,返回 0。
所有单词具有相同的长度。
所有单词只由小写字母组成。
字典中不存在重复的单词。
你可以假设 beginWord 和 endWord 是非空的,且二者不相同。
示例1:
输入:
beginWord = "hit",
endWord = "cog",
wordList = ["hot","dot","dog","lot","log","cog"]
输出: 5
解释: 一个最短转换序列是 "hit" -> "hot" -> "dot" -> "dog" -> "cog", 返回它的长度 5。
示例2:
输入:
beginWord = "hit"
endWord = "cog"
wordList = ["hot","dot","dog","lot","log"]
输出: 0
解释: endWord "cog" 不在字典中,所以无法进行转换。
基本思想:
图的构建和广度优先遍历(广度优先遍历一般用在树的层序遍历和图的遍历如拓扑排序)
如何建图?
对第一层遍历(只有hit),从hit开始,对其中每个单词进行a-z的替换,即ait、bit、…、hat、hbt、…。每次替换都检查数组中是否存在这个词并且这个词之前没有被访问过,如果有(即hot)放入第二层,当前层结束后进入第二层,即对hot逐一替换,结束后的第三层则是dot、lot,这时当我们对dot字符逐一替换时,lot也是其中之一,但我们之前已经标记了lot被访问过,所以不会加入到下一层,只将dog加入第四层,同样如此操作lot,将log加入第四层;所以第四层就是dog、log,对dog逐一替换时,我们注意到出现cog目标单词,返回当前层数加一即可结束。
可以看出,其实并不是建好图后进行层次遍历的,而是在遍历的时候将过程抽象,图是对遍历过程的抽象。遍历的过程就是建图的过程。
代码:
class Solution {
public int ladderLength(String beginWord, String endWord, List<String> wordList) {
Set<String> wordSet=new HashSet<>(wordList);
//如果list中没有目标单词,直接返回0
if(!wordList.contains(endWord))
return 0;
//加入集合,方便查询
wordSet.remove(beginWord);
//队列进行层次遍历
Queue<String> queue=new LinkedList<>();
queue.offer(beginWord);//第一层
Set<String> visited=new HashSet<>();//记录被访问过的单词
visited.add(beginWord);
int step=1;//记录层数
while(!queue.isEmpty()){
int size=queue.size();//记录当前层的单词个数,进行逐个访问
for(int i=0;i<size;i++){
String word=queue.poll();
if (changeWordEveryOneLetter(word, endWord, queue, visited, wordSet)){
//如果遍历当前层时,下一层出现目标单词(函数返回true)
return step+1;//返回当前层数加一
}
}
step++;//遍历完当前层,加一
}
return 0;
}
public boolean changeWordEveryOneLetter(String currentWord,String endWord,Queue queue,Set visited,Set wordSet){
char[] charArray=currentWord.toCharArray();
for(int i=0;i<currentWord.length();i++){//对当前单词每个字母逐一替换
char curChar=charArray[i];
for(char k = 'a'; k <= 'z'; k++){//尝试a-z
if(k==charArray[i])//表示原单词,跳过
continue;
charArray[i]=k;//替换完成
String nextWord=String.valueOf(charArray);
if(wordSet.contains(nextWord)){//查询在单词集合中是否存在
if(nextWord.equals(endWord))//就是目标单词,直接返回true
return true;
if(!visited.contains(nextWord)){//没被访问过才能加入
visited.add(nextWord);
queue.offer(nextWord);//加入队列,为下一层遍历做准备
}
}
}
charArray[i]=curChar;//恢复当前位,对下一位字母逐一替换
}
return false;
}
}