给定两个单词(beginWord 和 endWord)和一个字典,找到从 beginWord 到 endWord 的最短转换序列的长度。转换需遵循如下规则:
每次转换只能改变一个字母。
转换过程中的中间单词必须是字典中的单词。
说明:
如果不存在这样的转换序列,返回 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” 不在字典中,所以无法进行转换。
解法
- 深度优先遍历 重复量太大 超时
- 广度优先遍历 利用队列存储每一层数据 一层一层 其中判断是否为相差一个字母的单词 采用的是遍历wordlist分别比较的方法 此方法超时 通过29/39
class Solution {
public int ladderLength(String beginWord, String endWord, List<String> wordList) {
Queue<String> queue = new LinkedList<>();
queue.offer(beginWord);
HashSet<String> set = new HashSet<>();
set.add(beginWord);
int path = 0;
List<String> list = new ArrayList<>();
while (!queue.isEmpty()){
path++;
list.clear();
while (!queue.isEmpty()){
list.add(queue.poll());
}
String tmpStr;
for (int i = 0;i<list.size();i++){
tmpStr = list.get(i);
if(tmpStr.equals(endWord)) return path;
set.add(tmpStr);
for (String str:wordList){
if(checkOne(tmpStr,str)){
if(!set.contains(str)){
queue.offer(str);
}
}
}
}
}
return 0;
}
private boolean checkOne(String beginWord,String endWord){
int result = 0;
for (int i = 0;i<beginWord.length();i++){
if(beginWord.charAt(i)!=endWord.charAt(i)){
result++;
}
if(result>1) break;
}
return result>1?false:true;
}
}
- 解法3主要针对2中 判断是否为相差一个字母的单词 采用的是遍历wordlist进行改进
改为//采用hash的方式 用set存储单词表 替换当前节点的单词current每个字符(从a~z),然后看看替换过后的单词是否在单词表中
通过 32/39 有提高 超时
class Solution {
public int ladderLength(String beginWord, String endWord, List<String> wordList) {
Queue<String> queue = new LinkedList<>();
queue.offer(beginWord);
HashSet<String> hasVisitedSet = new HashSet<>();
hasVisitedSet.add(beginWord);
HashSet<String> wordListSet = new HashSet<>(wordList);
int path = 0;
int size;
while (!queue.isEmpty()){
path++;
size = queue.size();
String tmpStr;
for (int i = 0;i<size;i++){
tmpStr = queue.poll();
if(tmpStr.equals(endWord)) return path;
hasVisitedSet.add(tmpStr);
checkOneHash(tmpStr,wordListSet,hasVisitedSet,queue);
}
}
return 0;
}
//采用hash的方式 用set存储单词表 替换当前节点的单词current每个字符(从a~z),然后看看替换过后的单词是否在单词表中
private void checkOneHash(String beginWord,HashSet<String> wordListSet,HashSet<String> hasVisitedSet,Queue<String> queue){
char[] chars = beginWord.toCharArray();
for (int i = 0;i<beginWord.length();i++){
for (int j = 'a';j<'z'+1;j++){
chars[i] = (char) j;
String str = new String(chars);
if(wordListSet.contains(str) && !hasVisitedSet.contains(str)){
queue.offer(str);
}
chars[i] = beginWord.charAt(i);
}
}
}
}
- 解法3中使用队列来存储当前层数据 判断当前层中是否包含endWord的时候采用遍历队列中的数据 负责读为o(n) 当单词数量比较大时 这是超时的主要原因
解法4则采用set替代队列存储当前层数据 降低时间复杂度
全部通过
class Solution {
public int ladderLength(String beginWord, String endWord, List<String> wordList) {
HashSet<String> set = new HashSet<>();
set.add(beginWord);
HashSet<String> wordListSet = new HashSet<>(wordList);
int path = 0;
while (!set.contains(endWord)){
path++;
HashSet<String> tmpSet = new HashSet<>();
for (String tmpStr : set){
wordListSet.remove(tmpStr);
checkOneHash(tmpStr,wordListSet,tmpSet);
}
if(tmpSet.size()==0) return 0;
set = tmpSet;
}
return path+1;
}
//采用hash的方式 用set存储单词表 替换当前节点的单词current每个字符(从a~z),然后看看替换过后的单词是否在单词表中
private void checkOneHash(String beginWord,HashSet<String> wordListSet,HashSet<String> tmpSet){
char[] chars = beginWord.toCharArray();
for (int i = 0;i<beginWord.length();i++){
for (int j = 'a';j<'z'+1;j++){
chars[i] = (char) j;
String str = new String(chars);
if(wordListSet.contains(str)){
tmpSet.add(str);
}
chars[i] = beginWord.charAt(i);
}
}
}