广度优先搜索
1. 简介
BFS 的核心思想应该不难理解的,就是把一些问题抽象成图,从一个点开始,向四周开始扩散。一般来说,我们写 BFS 算法都是用「队列」这种数据结构,每次将一个节点周围的所有节点加入队列。
BFS 相对 DFS 的最主要的区别是:BFS 找到的路径一定是最短的,但代价就是空间复杂度可能比 DFS 大很多。
序号 | 题目 | 难度 |
---|---|---|
1 | 752. 打开转盘锁 | 中等 |
2 | 127. 单词接龙 | 困难 |
BFS 框架模板如下:
// 计算从起点 start 到终点 target 的最近距离 int BFS(Node start, Node target) { Queue<Node> q; // 核心数据结构 Set<Node> visited; // 避免走回头路 q.offer(start); // 将起点加入队列 visited.add(start); while (q not empty) { int sz = q.size(); /* 将当前队列中的所有节点向四周扩散 */ for (int i = 0; i < sz; i++) { Node cur = q.poll(); /* 划重点:这里判断是否到达终点 */ if (cur is target) return step; /* 将 cur 的相邻节点加入队列 */ for (Node x : cur.adj()) { if (x not in visited) { q.offer(x); visited.add(x); } } } } // 如果走到这里,说明在图中没有找到目标节点 }
2. 应用
2.1. Leetcode 752. 打开转盘锁
2.1.1. 题目
你有一个带有四个圆形拨轮的转盘锁。每个拨轮都有10个数字: '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' 。每个拨轮可以自由旋转:例如把 '9' 变为 '0','0' 变为 '9' 。每次旋转都只能旋转一个拨轮的一位数字。
锁的初始数字为 '0000' ,一个代表四个拨轮的数字的字符串。
列表 deadends 包含了一组死亡数字,一旦拨轮的数字和列表里的任何一个元素相同,这个锁将会被永久锁定,无法再被旋转。
字符串 target 代表可以解锁的数字,你需要给出解锁需要的最小旋转次数,如果无论如何不能解锁,返回 -1 。
示例 1:
输入:deadends = ["0201","0101","0102","1212","2002"], target = "0202"
输出:6
解释:
可能的移动序列为 "0000" -> "1000" -> "1100" -> "1200" -> "1201" -> "1202" -> "0202"。
注意 "0000" -> "0001" -> "0002" -> "0102" -> "0202" 这样的序列是不能解锁的,
因为当拨动到 "0102" 时这个锁就会被锁定。
2.1.2. 解题思路
使用广度优先搜索的思想,从起点开始枚举邻近的数字组合,枚举过程需要跳过死亡数字和已经访问过的数字,直到找到密码为止。
2.1.3. 代码实现
class Solution { private static final String START = "0000"; private static final int SUCCESS = 0; private static final int FAIL = -1; public int openLock(String[] deadends, String target) { if (target.equals(START)) { return SUCCESS; } Set<String> deadNumbers = new HashSet<>(Arrays.asList(deadends)); if (deadNumbers.contains(START)) { return FAIL; } Set<String> visit = new HashSet<>(); Queue<String> queue = new LinkedList<>(); queue.offer(START); visit.add(START); int step = 0; while (!queue.isEmpty()) { int size = queue.size(); for (int i = 0; i < size; i++) { String currentNumber = queue.poll(); for (String number : nextNumber(currentNumber)) { if (visit.contains(number) || deadNumbers.contains(number)) { continue; } if (target.equals(number)) { return step + 1; } queue.offer(number); visit.add(number); } } step += 1; } return -1; } private List<String> nextNumber(String number) { List<String> result = new ArrayList<>(); for (int i = 0; i < number.length(); i++) { result.add(numberOperate(number.toCharArray(), i, 1)); result.add(numberOperate(number.toCharArray(), i, -1)); } return result; } private String numberOperate(char[] state, int i, int value) { int newVal = state[i] - '0' + value; if (newVal == 10) { state[i] = '0'; } else if (newVal == -1) { state[i] = '9'; } else { state[i] = (char) (newVal + '0'); } return new String(state); } }
2.2. Leetcode 127. 单词接龙
2.2.1. 题目
字典 wordList 中从单词 beginWord 和 endWord 的 转换序列 是一个按下述规格形成的序列 beginWord -> s1 -> s2 -> ... -> sk:
每一对相邻的单词只差一个字母。
对于 1 <= i <= k 时,每个 si 都在 wordList 中。注意, beginWord 不需要在 wordList 中。
sk == endWord
给你两个单词 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.2.2. 解题思路
这里,我们可以使用广度优先搜索的思路,对于每一个单词,我们都可以通过找到它的邻近单词。
即对于当前单词的每一位,都可以将其替换为
2.2.3. 代码实现
class Solution { public int ladderLength(String beginWord, String endWord, List<String> wordList) { Set<String> canUseWords = new HashSet<>(wordList); Set<String> visit = new HashSet<>(); Queue<String> queue = new LinkedList<>(); queue.offer(beginWord); visit.add(beginWord); int step = 1; while (!queue.isEmpty()) { int size = queue.size(); for (int i = 0; i < size; i++) { String candidate = queue.poll(); for (String word : nextWord(candidate)) { if (visit.contains(word) || !canUseWords.contains(word)) { continue; } if (word.equals(endWord)) { return step + 1; } queue.offer(word); visit.add(word); } } step++; } return 0; } private List<String> nextWord(String word) { List<String> result = new ArrayList<>(); for (int i = 0; i < word.length(); i++) { for (char j = 'a'; j <= 'z'; j++) { if (j == word.charAt(i)) { continue; } char[] status = word.toCharArray(); status[i] = j; result.add(new String(status)); } } return result; } }
参考:
本文作者:LARRY1024
本文链接:https://www.cnblogs.com/larry1024/p/17964294
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步