目录
场景
一般就是查起点到终点最短路径这种,这种问题可以做很多衍生——>比如最少次数,最少步数,最少替换次数等等,一般就是用于最少的问题上
框架
1.首先要用Queue队列,将头节点放入
2.如果涉及重复子问题,我们可以定义一个Set——>避免重复
3.利用while(!queue.isEmpty()) ——>作为层序遍历终结条件
4.队列queue每次会放一层的节点,知道size后进行循环,对里面的元素进行遍历,得到下一层
,并添加到队列中,深度depth一般都是最后一层for下
// 计算从起点 start 到终点 target 的最近距离
int BFS(Node start, Node target) {
Queue<Node> q; // 核心数据结构
Set<Node> visited; // 避免走回头路
q.offer(start); // 将起点加入队列
visited.add(start);
int step = 0; // 记录扩散的步数
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);
}
}
}
/* 划重点:更新步数在这里 */
step++;
}
}
例题
思路:1.首先找特点,到叶子节点说明左右节点为空
if (cur.left == null && cur.right == null)
// 到达叶子节点
2.所以我们将头节点加入到queue——>然后进行while——>进行遍历将节点往下层序扩展直至左右子树为null返回depth——>然后将左右子节点加入队列进行下层循环
class Solution {
public int minDepth(TreeNode root) {
//1.初始化条件
if(root==null) return 0;
Queue<TreeNode> q=new LinkedList<>();
//将头节点加入队列中
q.offer(root);
int depth=1;
//2.层序遍历
while(!q.isEmpty()){
//2.1首先看队列中有多少个节点(上一层的)——>决定了循环多少次
int size=q.size();
for(int i=0;i<size;i++){
TreeNode cur=q.poll();
//2.2判断是否还有下一层,如果没有了就返回depth,否则加入下一层节点,并且depth++
if(cur.left==null&&cur.right==null) return depth;
if(cur.left!=null) q.offer(cur.left);
if(cur.right!=null) q.offer(cur.right);
}
//3.每遍历一层depth++
depth++;
}
return depth;
}
}
这里注意这个 while
循环和 for
循环的配合,while
循环控制一层一层往下走,for
循环利用 siz
变量控制从左到右遍历每一层二叉树节点:
思路:1.首先分析题意确定得到最小步数,咱用BFS——>2,已知先0000开始得到target,0000为root加入队列,然后字符串有8种变化(四个位置每个位置能+-,注意0-1=9,9+1=0),所以需要两层循环,一个是队列的字符串一个是每个字符串的演变——>3.定义两个对字符串+-的方法——>4.BFS模块完成后,结合死亡字符串考虑,只有不在死亡字符串内的才能加入队列
注意:死亡字符串和层序遍历的字符串需要去重提高效率(可能出现0000-1000-0000的情况)
class Solution {
public int openLock(String[] deadends, String target) {
//1.记录需要跳过的死亡密码,进行去重
Set<String> deads=new HashSet<>();
for(String s:deadends) deads.add(s);
//2.定义一个set对穷举过的密码进行去重——>可能有0000变1000下次又变0000的
Set<String> vistited=new HashSet<>();
//3.以下求最短路径常规操作
Queue<String> q=new LinkedList<>();
int step=0;
q.offer("0000");
vistited.add("0000");
//4.
while(!q.isEmpty()){
int size=q.size();
for(int i=0;i<size;i++){
//4.1前序位置
String cur=q.poll();
//4.2如果在死亡密码中,队列这个密码直接跳过
if(deads.contains(cur)) continue;
//如果等于目标密码直接返最小步数
if(cur.equals(target)) return step;
//4.3后序位置——>将下一层放入队列中
for(int j=0;j<4;j++){
String up=plusOne(cur,j);
if(!vistited.contains(up)){
q.offer(up);
vistited.add(up);
}
String down=minusOne(cur,j);
if(!vistited.contains(down)){
q.offer(down);
vistited.add(down);
}
}
}
//4.4每一次step+1
step++;
}
return -1;
}
//1.plusOne:将字符串s某字符位置j加1
public String plusOne(String s,int j){
char[]ch=s.toCharArray();
if(ch[j]=='9') ch[j]='0';
else ch[j]+=1;
return new String(ch);
}
//2.minusOne:将字符串某位置j减1
public String minusOne(String s,int j){
char[]ch=s.toCharArray();
if(ch[j]=='0') ch[j]='9';
else ch[j]-=1;
return new String(ch);
}
// //3.BFS求最短路径框架
// public void BFS(String target){
// //1.先将头节点加入到队列中
// Queue<String>q=new LinkedList<>();
// q.offer("0000");
// //2.然后进行while层序遍历
// while(!q.isEmpty()){
// int size=q.size();
// //2.1下一层
// for(int i=0;i<size;i++){
// String cur=p.poll();
// //2.2每个串有四个位置,每个位置有两种可能一共8种
// for(int j=0;j<4;j++){
// String up=plusOne(cur,j);
// String down=minusOne(cur,j);
// //添加到队列中
// q.offer(up);
// q.offer(down);
// }
// }
// }
// }
}
总结
首先,你看 BFS 的逻辑,depth
每增加一次,队列中的所有节点都向前迈一步,这保证了第一次到达终点的时候,走的步数是最少的。
DFS 不能找最短路径吗?其实也是可以的,但是时间复杂度相对高很多。你想啊,DFS 实际上是靠递归的堆栈记录走过的路径,你要找到最短路径,肯定得把二叉树中所有树杈都探索完才能对比出最短的路径有多长对不对?而 BFS 借助队列做到一次一步「齐头并进」,是可以在不遍历完整棵树的条件下找到最短距离的。