广度优先搜索
Posted 浮云神码
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了广度优先搜索相关的知识,希望对你有一定的参考价值。
本次分享的题目分别使用单向广度优先搜索和双向广度优先搜索进行实现。双向搜索基于若beginWord-->endWord存在一条转换路径,则从endWord-->beginWord也存在一条路径,双向搜索效率更高,每次都会选择当前队列节点数量更少的一方继续搜索,双方最终会在某个节点相遇。
import java.util.*;
/**
* https://leetcode-cn.com/problems/word-ladder/
* 127. 单词接龙
* 难度 困难
* 字典wordList 中从单词 beginWord和 endWord 的 转换序列 是一个按下述规格形成的序列:
*
* 序列中第一个单词是 beginWord 。
* 序列中最后一个单词是 endWord 。
* 每次转换只能改变一个字母。
* 转换过程中的中间单词必须是字典wordList 中的单词。
* 给你两个单词 beginWord和 endWord 和一个字典 wordList ,找到从beginWord 到endWord 的 最短转换序列 中的 单词数目 。如果不存在这样的转换序列,返回 0。
*
* 示例 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" 不在字典中,所以无法进行转换。
*
* 提示:
*
* 1 <= beginWord.length <= 10
* 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
*/
public class WordLadder {
public int ladderLength(String beginWord, String endWord, List<String> wordList) {
int result = 0;
Set<String> wordSet = new HashSet<>(wordList);
if (!wordSet.contains(endWord)) {
return result;
}
Queue<String> queue = new LinkedList<>();
queue.add(beginWord);
result++;
int length = beginWord.length();
while (!queue.isEmpty()) {
// 每执行一次while循环, 整个转换路径向前了一步
result++;
int queueSize = queue.size();
for (int q = 0; q < queueSize; q++) {
String currentWord = queue.remove();
/**
* 外层循环:遍历单词的每个字符位置
* 内层循环:a-z依次替换当前位置的字符, 其余字符保持不变
* 结果分两种情况:
* 1. 替换后的单词在字典wordList中不存在, 则忽略, 继续替换
* 2. 替换后的单词在字典wordList中存在, 则:
* (a). 这个单词等于endWord, 直接返回结果
* (b). 这个单词未搜索到过, 加入队列, 移出集合
*/
char[] currentCharArray = currentWord.toCharArray();
for (int i = 0; i < length; i++) {
char temp = currentCharArray[i];
for (int k = 0; k < 26; k++) {
currentCharArray[i] = (char) ('a' + k);
String ladderWord = new String(currentCharArray);
if (wordSet.contains(ladderWord)) {
// 已找到, 直接返回结果
if (endWord.equals(ladderWord)) {
return result;
} else {
wordSet.remove(ladderWord);
queue.add(ladderWord);
}
}
}
currentCharArray[i] = temp;
}
}
}
// 若未搜索到转换路径, 则不存在由beginWord到endWord的转换路径
return 0;
}
public int ladderLength2(String beginWord, String endWord, List<String> wordList) {
int result = 0;
Set<String> wordSet = new HashSet<>(wordList);
if (!wordSet.contains(endWord)) {
return result;
}
/**
* 构建start、end两个队列, 并构建已搜索节点
*/
Queue<String> startQueue = new LinkedList<>();
Set<String> startVisit = new HashSet<>();
startQueue.add(beginWord);
startVisit.add(beginWord);
Queue<String> endQueue = new LinkedList<>();
Set<String> endVisit = new HashSet<>();
endQueue.add(endWord);
endVisit.add(endWord);
result++;
int length = beginWord.length();
/**
* 任意一队列为空, 则说明beginWord到endWord不存在转换路径
*/
while (!startQueue.isEmpty() && !endQueue.isEmpty()) {
// 每执行一次while循环, 整个转换路径向前了一步
result++;
// 选择节点更少的一方进行搜索
boolean isStart = startQueue.size() <= endQueue.size();
Queue<String> queue = isStart ? startQueue : endQueue;
Set<String> visit = isStart ? startVisit : endVisit;
Set<String> otherVisit = isStart ? endVisit : startVisit;
int queueSize = queue.size();
for (int q = 0; q < queueSize; q++) {
String currentWord = queue.remove();
/**
* 外层循环:遍历单词的每个字符位置
* 内层循环:a-z依次替换当前位置的字符, 其余字符保持不变
* 结果分两种情况:
* 1. 替换后的单词在字典wordList中不存在, 则忽略, 继续替换
* 2. 替换后的单词在字典wordList中存在, 则:
* (a). 若已在另一方的搜索集合中存在, 则说明路径已通
* (b). 这个单词未搜索到过, 加入本方已搜索节点集合, 再加入本方队列
*/
char[] currentCharArray = currentWord.toCharArray();
for (int i = 0; i < length; i++) {
char temp = currentCharArray[i];
for (int k = 0; k < 26; k++) {
char c = (char) ('a' + k);
if (temp == c) {
continue;
}
currentCharArray[i] = c;
String ladderWord = new String(currentCharArray);
if (wordSet.contains(ladderWord)) {
// 已找到, 直接返回结果
if (otherVisit.contains(ladderWord)) {
return result;
} else {
if (visit.add(ladderWord)) {
queue.add(ladderWord);
}
}
}
}
currentCharArray[i] = temp;
}
}
}
// 若未搜索到转换路径, 则不存在由beginWord到endWord的转换路径
return 0;
}
}
以上是关于广度优先搜索的主要内容,如果未能解决你的问题,请参考以下文章