广度优先搜索

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<>(); }}


以上是关于广度优先搜索的主要内容,如果未能解决你的问题,请参考以下文章

Prolog中的广度优先搜索

面试题算法: 广度优先搜索

图相关算法

图解:深度优先搜索与广度优先搜索

广度优先搜索遍历图

无向图 广度优先搜索 和 深度优先搜索