双向BFS的实现

Breadth First Search(BFS)
下面是一个简单的双向连通图,从A一直要走到L,请问最短可以走多少路
在这里插入图片描述
双向扩散,即从A向右扩散,也从L向左边扩散,当扩散的两层结点有重合的时候,第一次重合的地方就是
A和L两者之间的最短路径,把左边扩散的步数加上右边扩散的步数,和就是最后 的总的步数

127 单词接龙

给定两个单词(beginWord 和 endWord)和一个字典,找到从 beginWord 到 endWord 的最短转换序列的长度。转换需遵循如下规则:
每次转换只能改变一个字母。
转换过程中的中间单词必须是字典中的单词。
说明:
如果不存在这样的转换序列,返回 0。
所有单词具有相同的长度。
所有单词只由小写字母组成。
字典中不存在重复的单词。
你可以假设 beginWord 和 endWord 是非空的,且二者不相同。
示例 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” 不在字典中,所以无法进行转换。

方法一:广度优先搜索 (双向BFS)

利用广度优先搜索搜索从 beginWord 到 endWord 的路径。

public int ladderLength(String beginWord, String endWord, List<String> wordList) {
	// 查询一个单词是否在List里面存在,时间复杂度是O(n),如果是HashSet,时间复杂度就是O(1) 的
	Set<String> wordList = new HashSet<String>(wordList);
	// beginSet是从beginWord向外扩散,endSet是从endSet向外扩散
	Set<String> beginSet = new HashSet<String>(), endSet = new HashSet<String>();
	int len = 1;
	int strLen = beginWord.length();
	// visited指的是BFS的结点是否已经被访问过,向外面扩散到中间的时候,这个结点不能让它再往回走
	HashSet<String> visited = new HashSet<String>();
	beginSet.add(beginWord);
	endSet.add(endWord);
	// BFS starts here 这里有两个queue都应该是不为空的说明搜索已经搜索完了
	// 每次扩散的时候只扩散其中一个set,优先选择小的set,如果beginSet更小就先扩散beginSet
	
	while (!beginSet.isEmpty() && !endSet.isEmpty()) {
	  // 但是beginSet 大于endSet,就把beginSet和endSet对调
		if (beginSet.size() > endSet.size()) {
			Set<String> set = beginSet;
			beginSet = endSet;
			endSet = set;
		}
		// 这个temp表示接下来要扩散beginSet
		Set<String> temp = new HashSet<String>();
		// 扩散:循环queue(set跟queue一样)里面每一个单词,对每个单词变换其中一个字符位
		// 针对每一个字符位的位置把它变换成abcdefg一直到z里面的任何一个字符,将字符重新替换
		// 替换掉之后就得到一个新的word命名为target,如果target在endSet里面已经有了,说明找到了
		// 因为endSet是从endWord那边扩散过来的,如果我们生成了target也在endSet里面,说明
		// 从头扩散出来的set和从尾巴扩散出来的set 相交了,没有在endSet中的话,就会继续扩散出其他的
		
		// target且把target加到下一次要扩散的set里面去,同时把visited标记下
		// 另外在这里还需要把这个单词本身还原,还原之后,将temp给了beginSet,
		// 然后length++,说明又扩散了一步
		for (String word: beginSet) {
			char[] chs = word.toCharArray();

			for (int i = 0; i < chs.length; i++) {
				for (char c = 'a'; c <= 'z'; c++) {
					char old = chs[i];
					chs[i] = c;
					String target = String.valueOf(chs);
					if (endSet.contains(target)) {
						return len + 1;
					}
					if (!visited.contains(target) && wordList.contains(target)) {
						temp.add(target);
						visited.add(target);
					}
					chs[i] = old;
				}
			}
		}
		beginSet = temp;
		len++;
	}
}
class Solution(object):
	def ladderLength(self, beginWord, endWord, wordList):
		# endWord不在list里面肯定最后都找不到就不用找
		if endWord not in wordList: return 0 
		front = {beginWord}
		back = {endWord}
		dist = 1 #走的步数
		wordList = set(wordList) #定义为set方便查找
		word_len = len(beginWord)
		# BFS starts here
		while front:
			dist += 1 #每次走之前先dist加1
			next_front = set() #后面扩散出来的点就放在这里面
			for word in front: #将front里面的单词遍历一次
				for i in range(word_len):#遍历单词的每一个字符位
					for c in string.lowercase:# 将字符位取出来之后 从 'a'遍历到 -- 'z'
						if c != word[i]: #将i个位置换成新的字符c,i之前的字符不变,i+1和i+1之后的这些字符也不变,只变i下标的位置
							new_word = word[:i]+c+word[i+1:]
							if new_word in back:#如果new_word在back中说明相交了,因为从front中扩散的new_word,在back中可以找到
								return dist #返回
							#不然的话就要进行扩散, 判断new_word是否在wordList中,
							#如果不在说明扩散出来的new_word不是一个合法的,在单词列表里没有就不考虑
							
							# 如果在的话,就加到next_Front中,同时把new_word从wordlist中撤掉
							if new_word in wordList:
								next_Front.add(new_word)
								wordList.remove(new_word)
			# 一开始next_Front是空,逐渐把下一次要扩散的单词都纷纷放到next_Front里面,当把front里面全部处理完					
			#说明next_Front已经准备好了,将next_Front给了新的front  					
			front = next_front
			if len(back) < len(front): #比较front和back之间的长度,如果back长度更小的时候,互调位置
				front, back = back, front
		return 0

双向BFS和单向BFS异曲同工,都是检测queue是否为空,但也有不同,不同的地方是不用queue
而是用set来表示,好处是在判断新生成的new_word是不是在另一个哥们扩散出来的set里面比较方便
比如在front里面扩散出来的点看是否在back中直接用set判断更加方便,相撞就直接返回
其他的单向和双向BFS基本一样,还有一个地方就是back和front会进行互换,不然的话
就要在两个中取舍,看哪个小就处理谁,那还不如互换下,每次只处理front,其实是一样的.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值