BFS 和 DFS在算法中属于有举足轻重的地位,本文希望通过使用王者峡谷二位脆皮英雄对话的方式讲解动态规划,让大家在轻松愉快的氛围中搞懂BFS 和 DFS。

鲁班:安琪拉,你知道 BFS 和DFS 吗?

安琪拉:当然知道啊,BFS 全程是 广度优先搜索, DFS 指的是深度优先搜索。

鲁班:安琪拉妹妹,你这么说我还是很蒙,能跟我详细讲讲吗?

安琪拉:鲁班哥哥,看你求知欲这么强,咱俩又都是脆皮的份上,我给你讲讲BFS 和 DFS。

以下图为例,

安琪拉教鲁班学算法之BFS和DFS_java

广度优先搜索 和 深度优先搜索都是遍历图/树 节点的方式

广度优先搜索:如下图所示,广度优先搜索,例如从上图中选取一个节点A,作为起点,宽度优先搜索,遍历的方式是遍历A、 和A相连的B和C,和B相连的D,和C相连的E,和D相连的F,以这样的顺序访问图中的节点。

安琪拉教鲁班学算法之BFS和DFS_java_02广度优先搜索

深度优先搜索:还是以A作为起点,一直走到底,直到不能访问,再回退,A => B => D => F =>E => C

安琪拉教鲁班学算法之BFS和DFS_java_03深度优先搜索

表示图节点关系有二种方式:

  • 邻接矩阵 

安琪拉教鲁班学算法之BFS和DFS_java_04

邻接矩阵适用于节点比较密集,一般用二维数组表示 array[1][0] = 1 代表 1 节点和 0 节点之间有连接线

  • 邻接表

安琪拉教鲁班学算法之BFS和DFS_java_05

邻接矩阵适合节点比较稀疏,一般用MultiMap或者Map<Key, List> 表示就可以。

代码实现例子:

public static void main(String[] args) {
    //以map 存储图中节点的关系 A 相连的节点为 B、C
       //就可表示为 List<Character> aList = new LinkedList<>(Arrays.asList('B','C'));
        Map<Character, List<Character>> graph = buildGraph();

        dfs(graph, 'A');

        bfs(graph, 'A');
    }

  // 构造图
    private static Map<Character,List<Character>> buildGraph(){
        Map<Character, List<Character>> graph = new HashMap<>();
        List<Character> aList = new LinkedList<>(Arrays.asList('B','C'));
        List<Character> bList = new LinkedList<>(Arrays.asList('A','C','D'));
        List<Character> cList = new LinkedList<>(Arrays.asList('A','B','D','E'));
        List<Character> dList = new LinkedList<>(Arrays.asList('B','F','E','C'));
        List<Character> eList = new LinkedList<>(Arrays.asList('C','D'));
        List<Character> fList = new LinkedList<>(Arrays.asList('D'));
        graph.put('A',aList);
        graph.put('B',bList);
        graph.put('C',cList);
        graph.put('D',dList);
        graph.put('E',eList);
        graph.put('F',fList);
        return graph;
    }

    /**
     * 深度优先搜索  关键是使用栈来维护相邻后继节点
     * @param graph 要遍历的图
     * @param s 起始点
     */

    public static void dfs(Map<Character,List<Character>> graph, Character s){
        //走过的节点
        Set<Character> visited = new HashSet<>();
        Stack<Character> stack = new Stack<>();
        stack.push(s);

        while(!stack.empty()){
            Character accessC = stack.pop();
            if(!visited.contains(accessC)){
                //访问函数
                System.out.print("->"+accessC);
                visited.add(accessC);
            }
            graph.get(accessC).forEach(c ->{
                if(!visited.contains(c)){
                    stack.push(c);
                }
            });
        }
    }

    /**
     * 广度优先搜索  使用队列维护相邻后续节点
     * @param graph
     * @param s
     */

    public static void bfs(Map<Character,List<Character>> graph, Character s){
        //走过的节点
        Set<Character> visited = new HashSet<>();
        Queue<Character> queue = new LinkedList<>();

        queue.offer(s);

        while (!queue.isEmpty()){
            Character accessC = queue.poll();
            if(!visited.contains(accessC)){
                //访问函数
                System.out.print("->"+accessC);
                visited.add(accessC);
            }
            graph.get(accessC).forEach(c ->{
                if(!visited.contains(c)){
                    queue.offer(c);
                }
            });
        }
    }