广度优先搜索
Posted 浮云神码
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了广度优先搜索相关的知识,希望对你有一定的参考价值。
本次只分享一个广度优先搜索的题目,借助一个辅助类,称为多路搜索树,同时增加parent指针,子节点可以反向搜索到root节点。
在构造多路搜索树时,使用的思路和狄克斯特拉(Dijkstra)算法类似。
import java.util.*;
/**
* https://leetcode-cn.com/problems/word-ladder-ii
* 126. 单词接龙 II
* 难度 困难
* 按字典wordList 完成从单词 beginWord 到单词 endWord 转化,一个表示此过程的 转换序列 是形式上像 beginWord -> s1 -> s2 -> ... -> sk 这样的单词序列,并满足:
*
* 每对相邻的单词之间仅有单个字母不同。
* 转换过程中的每个单词 si(1 <= i <= k)必须是字典wordList 中的单词。注意,beginWord 不必是字典 wordList 中的单词。
* sk == endWord
* 给你两个单词 beginWord 和 endWord ,以及一个字典 wordList 。请你找出并返回所有从 beginWord 到 endWord 的 最短转换序列 ,如果不存在这样的转换序列,返回一个空列表。每个序列都应该以单词列表 [beginWord, s1, s2, ..., sk] 的形式返回。
*
* 示例 1:
*
* 输入:beginWord = "hit", endWord = "cog", wordList = ["hot","dot","dog","lot","log","cog"]
* 输出:[["hit","hot","dot","dog","cog"],["hit","hot","lot","log","cog"]]
* 解释:存在 2 种最短的转换序列:
* "hit" -> "hot" -> "dot" -> "dog" -> "cog"
* "hit" -> "hot" -> "lot" -> "log" -> "cog"
* 示例 2:
*
* 输入:beginWord = "hit", endWord = "cog", wordList = ["hot","dot","dog","lot","log"]
* 输出:[]
* 解释:endWord "cog" 不在字典 wordList 中,所以不存在符合要求的转换序列。
*
* 提示:
*
* 1 <= beginWord.length <= 7
* endWord.length == beginWord.length
* 1 <= wordList.length <= 5000
* wordList[i].length == beginWord.length
* beginWord、endWord 和 wordList[i] 由小写英文字母组成
* beginWord != endWord
* wordList 中的所有单词 互不相同
*
* 来源:力扣(LeetCode)
* 链接:https://leetcode-cn.com/problems/word-ladder-ii
*/
public class WordLadders2 {
public List<List<String>> findLadders(String beginWord, String endWord, List<String> wordList) {
List<List<String>> result = new ArrayList<>();
// 将List转为Set, 提高查询速度
Set<String> wordSet = new HashSet<>(wordList);
if (!wordSet.contains(endWord)) {
// 如果wordList中不包含endWord, 则肯定没有转换序列
return result;
}
MultipleSearchTree root = new MultipleSearchTree(beginWord);
Queue<MultipleSearchTree> queue = new LinkedList<>();
queue.add(root);
/**
* 存储已经转换过的单词, 每个单词到beginWord的转换路径都是最短的
*/
Set<String> existWords = new HashSet<>(wordList.size());
existWords.add(beginWord);
// 因所有单词的长度都一样, 所有length取beginWord.length()
int length = beginWord.length();
//
List<MultipleSearchTree> endWordTreeList = new LinkedList<>();
while (!queue.isEmpty() && endWordTreeList.isEmpty()) {
int queueSize = queue.size();
// 存储当前层转换过的词, 是本次while循环中新增加的转换词
Set<String> currentAddWords = new HashSet<>();
for (int q = 0; q < queueSize; q++) {
MultipleSearchTree current = queue.remove();
/**
* 外层循环:遍历单词的每个字符位置
* 内层循环:a-z依次替换当前位置的字符, 其余字符保持不变
* 结果分两种情况:
* 1. 替换后的单词在字典wordList中不存在, 则忽略, 继续替换
* 2. 替换后的单词在字典wordList中存在, 则:
* (a). 这个单词尚未在多路搜索树中出现过, 即在existWords中不存在, 将此单词加入当前节点的子节点中,
* 若同时不是endWord, 则加入队列中, 存入currentAddWords
* (b). 这个单词已在多路搜索树中出现过, 忽略即可, 已经有一个不长于此路径的路径可以到达此单词
*/
for (int i = 0; i < length; i++) {
byte[] currentWordBytes = current.val.getBytes();
for (int k = 0; k < 26; k++) {
currentWordBytes[i] = (byte)('a' + k);
String ladderWord = new String(currentWordBytes);
if (wordSet.contains(ladderWord)) {
if (!existWords.contains(ladderWord)) {
// 加入当前节点的子节点
MultipleSearchTree child = new MultipleSearchTree(ladderWord, current);
current.children.add(child);
if (endWord.equals(ladderWord)) {
// endWordTreeList一旦有值, 说明找到了endWord 下次while循环就不再符合条件
// 需要注意的是, 本次while循环必须要执行完毕, 搜索相同长度转换路径的结果
endWordTreeList.add(child);
} else {
// 本次迭代新增的单词
currentAddWords.add(ladderWord);
// 加入队列
queue.add(child);
}
}
}
}
}
}
// 将本次while循环转换出的单词全部存入existWords
existWords.addAll(currentAddWords);
}
// 使用parent指针将路径反向存入栈中, 再从栈中取出
Stack<MultipleSearchTree> stack = new Stack<>();
for (MultipleSearchTree item : endWordTreeList) {
while (item != null) {
stack.push(item);
item = item.parent;
}
List<String> path = new ArrayList<>();
while (!stack.isEmpty()) {
path.add(stack.pop().val);
}
result.add(path);
}
return result;
}
}
多路搜索树定义
import java.util.LinkedList;
import java.util.List;
/**
* 多路搜索树
*/
public class MultipleSearchTree {
public String val;
public MultipleSearchTree parent;
public List<MultipleSearchTree> children;
public MultipleSearchTree(String val) {
this.val = val;
this.parent = null;
this.children = new LinkedList<>();
}
public MultipleSearchTree(String val, MultipleSearchTree parent) {
this.val = val;
this.parent = parent;
this.children = new LinkedList<>();
}
}
以上是关于广度优先搜索的主要内容,如果未能解决你的问题,请参考以下文章