思路:因为路径可能循环,如果采用深度优先遍历,则要考虑路径回头的判定,这个不管是时间复杂度还是空间复杂度开销巨大(每条路都要走到死且在走的过程中要记住所有曾经走过的路线)。所以采用广度优先遍历。但是不断地超时,百度了下超时的原因,原来采用的比较相邻(只差一个字母)的方法是逐位比较两个字符串,然后将结果记录在map中,如果字典(wordList)中的元素是n个,则该方法的时间复杂度是O(n^2),所以改为采取逐位替换法,对目标字符串进行逐位替换,判断该字母是否在set中,这样的时间复杂度只有O(n)。广度优先遍历尝试次数为字典元素的个数,因为最坏的情况无非是每次走一步走完整个字典。
论证如下:
设字典元素共有n个,每个长度为l;
直接比较,结果记录,总共比较的次数是(n^2)/2,每次比较的时间是l/2,所以时间复杂度是O(n^2),另外还有O(n^2)空间开销
逐位比较,每个字符串比较的次数是26*l,比较n个,所以时间复杂度是O(n)。
另外,对于该题而言,有时候思路对了依然报超时,仅仅是因为代码的BUG而非是思路真的超时了。用题例的数据检测下你的方法是否能跑出正确答案。如果能,那么才是真的超时。。
public class Solution {
public int ladderLength(String beginWord, String endWord, Set<String> wordList) {
if (beginWord==null||endWord==null||wordList==null||beginWord.equals(endWord)||beginWord.length()!=endWord.length()) {
return 0;
}
Map<String,List<String>> map=new HashMap<String, List<String>>();
int count=1;
wordList.add(beginWord);
wordList.add(endWord);
for (String str:wordList) {
count++;
List<String> list=new ArrayList<String>();
char[] cs=str.toCharArray();
for (int i = 0; i < cs.length; i++) {
char k=cs[i];
for (char j = 'a'; j <='z'; j++) {
if (j!=k) {
cs[i]=j;
String test=String.valueOf(cs);
if (wordList.contains(test)) {
list.add(test);
}
}
}
cs[i]=k;
}
map.put(str, list);
}
Map<String,Integer> test=new HashMap<String, Integer>();
test.put(beginWord, 1);
for (int i = 1; i < count; i++) {
Map<String,Integer> newTest=new HashMap<String, Integer>();
for (String str:test.keySet()) {
List<String> list=map.get(str);
for (int j = 0; j < list.size(); j++) {
String k=list.get(j);
if (k.equals(endWord)) {
return i+1;
}
if (test.get(k)==null&&newTest.get(k)==null) {
newTest.put(k, 1);
}
}
}
test=newTest;
}
return 0;
}
}