图的宽度优先遍历:BFS遍历

图的宽度优先遍历:BFS遍历

提示:系列图的文章
提示:大厂笔试面试都可能不咋考的数据结构:图

由于图的结构比较难,出题的时候,很难把这个图的数据搞通顺,而且搞通顺了题目也需要耗费太多时间,故笔试面试都不会咋考
笔试大厂考的就是你的贪心取巧策略和编码能力,这完全不必用图来考你,其他的有大量的选择
面试大厂考你的是优化算法的能力,但是图没啥可以优化的,只要数据结构统一,它的算法是固定死的,所以不会在面试中考!
万一考,那可能都是大厂和图相关的业务太多,比如美团高德地图啥的,这种考的少。

但不管考不考,我们要有基础,要了解图的数据结构和算法。万一考了呢,准备以备不时之需。

图的数据结构比较难,算法是固定的套路
咱们需要统一一种自己熟悉的图的数据结构,方便套用算法时好写!!

下面是咱们得关于图的重要基础知识和重点应用:
(1)图数据结构,图的统一数据结构和非标准图的转化算法


文章目录

  • 图的宽度优先遍历:BFS遍历
    • @[TOC](文章目录)
  • 图的宽度优先遍历和二叉树的宽度优先一样,只不过多了分叉
  • 图的宽度优先遍历BFS:很简单
  • 总结

图的宽度优先遍历和二叉树的宽度优先一样,只不过多了分叉

看看二叉树的BFS:
二叉树的宽度优先遍历BFS:按层的遍历方式,请你用队列实现DFS,或者请你用栈实现BFS

用队列实现,x的左右子挨个按顺序进队列。

//复习:这样做,最开始将head加入队列
    //开始循环操作:弹出打印。
    //	然后看其左右子是否不为null,是就加入队列,否则不管。
    public static void bfsBinaryTreePrint(Node head){
        if (head == null) return;

        LinkedList<Node> queue = new LinkedList<>();
        queue.add(head);
        while (!queue.isEmpty()){
            Node cur = queue.poll();
            System.out.print(cur.value + " ");

            //按层加入左右子
            if (cur.left != null) queue.add(cur.left);
            if (cur.right != null) queue.add(cur.right);
        }
        //图也是这么干BFS,DFS就是用栈,一条道走到黑
    }

图的宽度优先遍历BFS:很简单

跟二叉树的BFS类似,也是对于节点x,让x的直接邻居,挨个进队列(当然,没进去过的,进去过的就算了)

(1)a头节点进队列,加入set(标记已经遍历过了),然后开始弹出打印!cur=a;
(2)看a的所有直接邻居,未被遍历过的节点b c,依次进队列,并加入set
(3)依次弹出队列,cur=b,打印,去(1)循环执行。
(4)直到队列为空
图的宽度优先遍历:BFS遍历_第1张图片
超级超级简单!唯一比二叉树多的就是分支多了,二叉树只有2个子,而图可能有1–N个子
另外,就是用set控制已经遍历过的节点,不可以重复遍历。

手撕代码也很简单,这个代码必须手撕会,因为有不少题目都是BFS做的

//复习图的BFS
    public static void bfsReview(Node head){
        if (head == null) return;

        //队列控制BFS,和二叉树一样
        Queue<Node> queue = new LinkedList<>();
        //集合控制已经遍历过得节点
        Set<Node> set = new HashSet<>();

        //(1)a头节点进队列,加入set(标记已经遍历过了),然后开始弹出打印!cur=a;
        queue.add(head);
        set.add(head);
        while (!queue.isEmpty()){
            Node cur = queue.poll();//弹出打印
            System.out.print(cur.value +" ");

            //(2)看a的所有直接邻居,未被遍历过的节点b c,依次进队列,并加入set
            for(Node next:cur.nexts){
                if (!set.contains(next)){
                    queue.add(next);
                    set.add(next);
                }
            }
            //(3)依次弹出队列,cur=b,打印,去(1)循环执行。
            //(4)直到队列为空
        }

    }

代码可以说是过于简单了,反正考到你是一定要会的,将来很多题目就在这上面改编你的解

下面生成一个图,然后统一图的数据结构,然后验证bfs

//基础的数据类型,得自个写
    //边--和节点是相互渗透的俩数据结构
    public static class Edge{
        public int weight;
        public Node from;
        public Node to;
        public Edge(int w, Node a, Node b){
            weight = w;
            from = a;
            to = b;
        }
    }

    //节点
    public static class Node{
        public int in;
        public int out;
        public int value;
        public ArrayList<Node> nexts;
        public ArrayList<Edge> edges;//这里是相互渗透的,既然有邻居,就右边

        public Node(int v){
            value = v;//只需要给这么一个value即可
            in = 0;
            out = 0;
            nexts = new ArrayList<>();
            edges = new ArrayList<>();
        }
    }

    //图结构玩起来,图右边,节也有边
    public static class Graph{
        public HashMap<Integer, Node> nodes;//v,Node
        public HashSet<Edge> edges;
        public Graph(){
            nodes = new HashMap<>();//一般节点有value,对应包装袋,都是用哈希表玩的,并查集就是这么玩的
            edges = new HashSet<>();
        }

        public int getNodeNum(){
            return nodes.size();
        }

        public int getEdgeNum(){
            return edges.size();
        }
    }

    //将非标准图结构转化为左神标准图结构
    public static Graph generatGrap(int[][] matrix){
        if (matrix == null || matrix.length == 0) return null;
        //matrix==
        //[1,1,2]
        //[2,2,3]
        //[3,3,1],w,f,t
        //挨个遍历行
        Graph graph = new Graph();

        for (int i = 0; i < matrix.length; i++) {
            //建节点和边,然后装图,将节点的入度,出度,边和邻居放好
            int weight = matrix[i][0];
            int from = matrix[i][1];
            int to = matrix[i][2];

            //图中没节点,建,否则不必重复搞
            if (!graph.nodes.containsKey(from)) graph.nodes.put(from, new Node(from));
            if (!graph.nodes.containsKey(to)) graph.nodes.put(to, new Node(to));

            Node fromNode = graph.nodes.get(from);
            Node toNode = graph.nodes.get(to);//有就拿出来

            Edge edge = new Edge(weight, fromNode, toNode);//建边
            graph.edges.add(edge);

            fromNode.out++;
            toNode.in++;
            fromNode.nexts.add(toNode);
            fromNode.edges.add(edge);//除了入度说终点,其余都是说原点
        }

        return graph;
    }

验证bfs

public static void test(){
        int[][] matrix = {
                {1,1,2},
                {2,2,3},
                {3,3,5},
                {4,2,5},
                {5,1,3},
                {5,1,4}
        };
        Graph graph = generatGrap(matrix);
        System.out.println("bfs:");
        bfs(graph.nodes.get(1));//第一个节点开始遍历打印
        System.out.println("\n复习bfs:");
        bfsReview(graph.nodes.get(1));//第一个节点开始遍历打印
    }

效果:
图的宽度优先遍历:BFS遍历_第2张图片

复习bfs:
1 2 3 4 5 


总结

提示:重要经验:

1)图的统一数据结构,丰富图结构表示法,要熟悉,就是一堆节点,一堆边,但是他们的属性很丰富,还有标准的构图过程
2)图的宽度优先遍历方法:BFS非常简单,就是沿着x的直接邻居,加入队列就行,弹出打印,不断加队列,很简单。
3)笔试求AC,可以不考虑空间复杂度,但是面试既要考虑时间复杂度最优,也要考虑空间复杂度最优。

你可能感兴趣的:(大厂面试高频题之数据结构与算法,宽度优先,图,图的宽度优先遍历,BFS,大厂笔试面试题)