BFS简介
参考链接
广度优先搜索一层一层地进行遍历,每层遍历都以上一层遍历的结果作为起点,遍历一个距离能访问到的所有节点。需要注意的是,遍历过的节点不能再次被遍历。
第一层:
0->{1,2,6,5}
第二层:
6->{4}
5->{3,4}
第三层:
3->{}
4->{}
每一层遍历的节点都与根节点距离相同。设 di 表示第 i 个节点与根节点的距离,推导出一个结论:对于先遍历的节点 i 与后遍历的节点 j,有 di <= dj。利用这个结论,可以求解最短路径等 最优解 问题:第一次遍历到目的节点,其所经过的路径为最短路径。应该注意的是,使用 BFS 只能求解无权图的最短路径,无权图是指从一个节点到另一个节点的代价都记为 1。
在程序实现 BFS 时需要考虑以下问题:
- 队列:用来存储每一轮遍历得到的节点;
- 标记:对于遍历过的节点,应该将它标记,防止重复遍历。
DFS简介
深度优先遍历顾名思义就是可着一条路去遍历。以下图举例,程序会优先去走一条路,比如会先a->b->e;之后以程序语言来说就是e是叶子节点了,所以结束递归,返回b的位置,此时b会取遍历f,路径就是a->b->f,之后就是返回a,a->c,a->d。其实上述描述语言为前序遍历,当前->左->右。
在程序实现 DFS 时需要考虑以下问题:
栈:用栈来保存当前节点信息,当遍历新节点返回时能够继续遍历当前节点。可以使用递归栈。
标记:和 BFS 一样同样需要对已经遍历过的节点进行标记。
题目
690. 员工的重要性
题目链接。本题需要考虑的就是不是直系下属的重要程度也需要考虑入内。
1.dfs解法:
dfs的思路就是一条路一条路的去走。
以该例子举例
[[1,5,[2,3]],[2,3,[4]],[3,4,[]],[4,1,[]]]
1
程序的流程是:首先找到员工1,之后找到直系下属员工2,之后找到非直系下属4,最后在非直系下属4的时候结束寻找。也就是传递的始终是当前要找的员工id。
路径为:1->2->4,1->3。
/*
// Employee info
class Employee {
// It's the unique id of each node;
// unique id of this employee
public int id;
// the importance value of this employee
public int importance;
// the id of direct subordinates
public List<Integer> subordinates;
};
*/
class Solution {
//不是直系下属的也需要添加进去。
int impo;
public int getImportance(List<Employee> employees, int id) {
//还是dfs最好用我觉得
//id与对应的员工信息
Map<Integer,Employee>map = new HashMap<>();
for(Employee e:employees)
map.put(e.id,e);
dfs(map,id);
return impo;**加粗样式**
}
public void dfs(Map<Integer,Employee>map,int id)
{
Employee e = map.get(id);
impo += e.importance;
for(Integer i:e.subordinates)
{
dfs(map,i);
}
return ;
}
}
2.bfs算法
bfs算法是一层一层的遍历。
以该例子举例
[[1,5,[2,3]],[2,3,[4]],[3,4,[]],[4,1,[]]]
1
程序的流程是,一层一层的去遍历,首先遍历的是我们要找的父员工,然后去遍历父员工的所有直系下属员工,之后遍历所有直系下员工的所有直系下属员工。也就是说每一回遍历传递的是一个列表,列表中存储当前层的所有id。
路径为:
第一层:1;
第二层:2,3;
第三层:4。
/*
// Employee info
class Employee {
// It's the unique id of each node;
// unique id of this employee
public int id;
// the importance value of this employee
public int importance;
// the id of direct subordinates
public List<Integer> subordinates;
};
*/
class Solution {
//不是直系下属的也需要添加进去。
int impo;
public int getImportance(List<Employee> employees, int id) {
//还是dfs最好用我觉得
//id与对应的员工信息
Map<Integer,Employee>map = new HashMap<>();
for(Employee e:employees)
map.put(e.id,e);
List<Integer>list = new ArrayList<>();
list.add(id);
bfs(map,list);
return impo;
}
public void bfs(Map<Integer,Employee>map,List<Integer>l)
{
List<Integer>list=new ArrayList<>();
for(Integer i:l)
{
Employee e = map.get(i);
impo+=e.importance;
list.addAll(e.subordinates);
}
if(list.size()!=0)
bfs(map,list);
return ;
}
}
547.朋友圈
1.dfs解法
这道题我的思路就是深度优先遍历。其实是两层遍历,第一层是遍历每个学生,第二层是遍历每个学生和他有好友关系的学生。第一层的含义其实是循环一次就代表一个朋友圈,因为我们要做的是有记忆功能的dfs。我们使用一个boolean型数组来存储,flag【i】代表i是否已经在一个朋友圈里了。当该学生不在朋友圈中,此时进入dfs递归。在dfs中我们去把该学生的好友关系挨个寻找一遍,以此类推,直到学生的好友关系的那些好友都已经在某个朋友圈当中了。
首先要理解第一个循环走一次就代表一个朋友圈,因为我会在这一次里把所有与该A学生有关的学生全部找到(不是直接有关系,有间接关系的也会找到),这些都在一个朋友圈内。下一次去找的学生是与他们一点关系都没有的学生,然后和这个学生有关系的学生们会组成另一个朋友圈。所以第一个循环走几次就代表有几个朋友圈。
其次,dfs中会遇到两个误导。
1.是否可以从(M【i】数组)i位置往后走,因为i位置以前的学生我都看过了。
答案是否定的。因为比如有四个学生。有可能第一个学生和第二个学生没关系,和第三个学生有关系,那么我们去找第三个学生的时候,就直接去看第三个和第四个学生是否有关系,那万一第三个和第二个学生还有关系呢?
更改做法:所以我们每次都必须从0开始往后找。
2.是否可以在dfs后再将flag【j】赋值为true呢?
答案是否定的。设想第二个学生和第三个学生有关系,那么我们会在M【2】数组的第三个位置进入dfs循环,之后又会在M【3】数组的第二个位置进入上一个dfs循环,这样就会一直循环。
**更改做法:**我们在进入dfs的时候将flag【i】设为true,因为我们是从i学生走过来的,所以我们要将i学生放入我们的朋友圈。
class Solution {
int circles = 0;
//有存储记忆
boolean[]flag;
public int findCircleNum(int[][] M) {
//代表i是否已经在一个朋友圈了
flag = new boolean[M.length];
for(int i=0;i<M.length;i++){
if(flag[i])
continue;
//循环几次就是有几个朋友圈(因为能循环,就代表这个学生没有被加入到朋友圈之中)
circles++;
dfs(M,flag,i);
}
return circles;
}
public void dfs(int[][]M,boolean[]flag,int i) {
//列i之前的不用看了,肯定都看过了。
//因为i行之前的行都看过了。
//要看i学生,是否能和后面的学生继续互为朋友关系。
//上面的是错的,因为比如有四个学生
//有可能第一个学生和第二个学生没关系,和第三个学生有关系,
//那么我们去找第三个学生的时候,就直接去看第三个和第四个学生是否有关系,那万一第三个和第二个学生还有关系呢?
//我们把第i个同学加进我们的朋友圈。
flag[i]=true;
for(int j=0;j<M[0].length;j++)
{
if(flag[j]||i==j)
continue;
if(M[i][j]==1)
{
dfs(M,flag,j);
//这会导致我们一直在dfs循环中,比如第二个和第三个有关系,那么第三个也和第二个有关系。
//就会不断的循环dfs(M,flag,1),和dfs(M,flag,2),去dfs一次就应该把当前循环的那个
//学生加入到朋友圈之中,但不能再dfs之后,应该在dfs初始就加入,防止陷入死循环。
//flag[j]=false;
}
}
}
}