一开始是想用回溯来做,深搜过程中判断当前单词和前一个单词和上个单词相差是否为 1,为 1 则进行深搜且回溯,最后将得到的结果集中筛选出最短路径。然而在一个 case 中发现了问题,如果 endWord 出现在中间的话而且 endWord 和之前的 word 相差会大于一就直接 continue 跳过,而后面又没有 endWord,这样会出现漏解的情况。问题出在按数组顺序进行深搜。那遍历完一个元素后应该怎么获取下一个元素进行深搜呢?有没什么办法找到所有和当前 word 相差一的多个 word呢?想想哈,暴力试试。遍历 26 个字母,将每个字母设置为当前word 中每个字母,然后判断设置后的 word 是否在 list 中存在。
Like this, 这样找到他的邻居,也避免深搜中 endWord 在中间的 case。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
private ArrayList<String> getNeighbors(String node, Set<String> dict) { ArrayList<String> res = new ArrayList<String>(); char chs[] = node.toCharArray();
for (char ch = 'a'; ch <= 'z'; ch++) { for (int i = 0; i < chs.length; i++) { if (chs[i] == ch) continue; char old_ch = chs[i]; chs[i] = ch; if (dict.contains(String.valueOf(chs))) { res.add(String.valueOf(chs)); } chs[i] = old_ch; }
}
可是在数据量大时导致了超时。为什么呢。我在遍历时会遍历很多无谓的分支,比如出现比当前 res 中的 list.size 大时,就直接不需要再遍历下去了。我们可以用一个 min 值记录最小的 list.size。如果 path 超过这个长度时就直接不再遍历下去。满怀欣喜改后还是超时。
fundoubleBfs(dict: HashSet<String>, begin: Set<String>, end: Set<String>, map: HashMap<String, MutableList<String>>, isTopDown: Boolean): Boolean { //寻找每个节点的邻居节点 if (begin.isEmpty()) { //当一个方向节点数为空仍未找到 中间联通节点时,返回 false returnfalse } if (begin.size > end.size) { // 选取节点数小的方向进行遍历 return doubleBfs(dict, end, begin, map, !isTopDown) } dict.removeAll(begin) dict.removeAll(end) //去除已访问节点 var isTraversalEnd = false//是否遍历结束 val visited = HashSet<String>() //记录本层新增节点(未在 begin 和 end 中出现过) for (word in begin) { val chars = word.toCharArray() for (i in chars.indics) { val temp = chars[i] for (c in'a'..'z') { //暴力枚举可能的邻居节点 if (c == temp) { continue } chars[i] = c val neighborWord = String(chars) val key = if (isTopDown) word else neighborWord //根据访问顺序确定 key,value.确保 key 是从上之下方向中的上面,而value 是下面。 val value = if (isTopDown) neighborWord else word val neighborWords = map.getOrDefault(key, mutableListOf())