Leetcode一起攻克搜索(BFS,DFS,回溯)

BFS简介

参考链接
在这里插入图片描述
广度优先搜索一层一层地进行遍历,每层遍历都以上一层遍历的结果作为起点,遍历一个距离能访问到的所有节点。需要注意的是,遍历过的节点不能再次被遍历。
第一层:
0->{1,2,6,5}
第二层:
6->{4}
5->{3,4}
第三层:
3->{}
4->{}
每一层遍历的节点都与根节点距离相同。设 di 表示第 i 个节点与根节点的距离,推导出一个结论:对于先遍历的节点 i 与后遍历的节点 j,有 di <= dj。利用这个结论,可以求解最短路径等 最优解 问题:第一次遍历到目的节点,其所经过的路径为最短路径。应该注意的是,使用 BFS 只能求解无权图的最短路径,无权图是指从一个节点到另一个节点的代价都记为 1。
在程序实现 BFS 时需要考虑以下问题:

  1. 队列:用来存储每一轮遍历得到的节点;
  2. 标记:对于遍历过的节点,应该将它标记,防止重复遍历。

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;
            }
        }
        
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章