【算法笔记】图结构及图的 DFS 和 BFS 介绍

x33g5p2x  于2022-03-03 转载在 其他  
字(3.1k)|赞(0)|评价(0)|浏览(407)

前言: 该篇文章将介绍如何应付面试中的图结构,并且还简单介绍了图的宽度优先遍历和深度优先遍历

1. 图的基本介绍

基本概念:

  • 图由点的集合和边的集合构成
  • 虽然存在有向图和无向图的概念,但实际上都可以用有向图来表达
  • 边上可能带有权值

图的结构:

  • 邻接表法
  • 邻接矩阵法
  • 还有其它众多的方式

如何搞定图的面试题: 图的算法都不难,但是写代码时会很复杂,coding 代价比较高,因此可以通过以下的方式来应付图的面试题

  • 先用自己最熟练的方式,实现图结构的表达
  • 在自己熟悉的结构上,实现所有常用图的算法作为模板
  • 把面试题提供的图结构转化为自己熟悉的图结构,再调用模板或改写即可(做一个适配器)

2. 图的实现

实现代码:

  • 点结构的实现
  1. import java.util.ArrayList;
  2. // 点结构的描述
  3. public class Node {
  4. // 该点的值
  5. public int value;
  6. // 该点的入度数
  7. public int in;
  8. // 该点的出度数
  9. public int out;
  10. // 该点的相邻点(指该点指向的点)
  11. public ArrayList<Node> nexts;
  12. // 该点的相邻边(指该点指向的边)
  13. public ArrayList<Node> edges;
  14. public Node(int value) {
  15. this.value = value;
  16. in = 0;
  17. out = 0;
  18. nexts = new ArrayList<>();
  19. edges = new ArrayList<>();
  20. }
  21. }
  • 边结构的实现
  1. // 边结构的描述
  2. public class Edge {
  3. // 边的权重
  4. public int weight;
  5. // 入边节点
  6. public Node from;
  7. // 出边节点
  8. public Node to;
  9. public Edge(int weight, Node from, Node to) {
  10. this.weight = weight;
  11. this.from = from;
  12. this.to = to;
  13. }
  14. }
  • 图结构的实现
  1. import java.util.HashMap;
  2. import java.util.HashSet;
  3. // 图的描述
  4. public class Graph {
  5. // 点的集合,Integer 表示节点的值,先有值,再创建节点
  6. public HashMap<Integer, Node> nodes;
  7. // 边的集合
  8. public HashSet<Edge> edges;
  9. public Graph() {
  10. nodes = new HashMap<>();
  11. edges = new HashSet<>();
  12. }
  13. }

常见面试题的图结构:

  • 用一个二维数组表示,每个一维数组里面有三个值
  • 第一个值表示边的权重
  • 第二个值表示边的出发节点
  • 第三个值表示边的目的节点

假设现有一个数组表示是这样的:{{3, 0, 7}, {5, 1, 2}, {6, 2, 7}},它符合上面图的结构,那么它用图表示如下

当我们面试遇见这种结构的图时,就可以使用我们上述已经定义好的图的结构来表示,因此我们只需要再做一个适配的过程

适配代码:

  1. public class Create {
  2. public static Graph createGraph(int[][] matrix) {
  3. Graph graph=new Graph();
  4. for(int i=0;i<matrix.length;i++) {
  5. // 边的权重
  6. int weight=matrix[i][0];
  7. // 出发节点的值
  8. int from=matrix[i][1];
  9. // 目的节点的值
  10. int to=matrix[i][2];
  11. // 如果该图中还没有包含该节点,则将节点入图
  12. if(!graph.nodes.containsKey(from)) {
  13. graph.nodes.put(from, new Node(from));
  14. }
  15. if(!graph.nodes.containsKey(to)) {
  16. graph.nodes.put(to, new Node(to));
  17. }
  18. Node fromNode=graph.nodes.get(from);
  19. Node toNode=graph.nodes.get(to);
  20. Edge edge=new Edge(weight,fromNode,toNode);
  21. fromNode.out++;
  22. toNode.in++;
  23. fromNode.nexts.add(toNode);
  24. fromNode.edges.add(edge);
  25. graph.edges.add(edge);
  26. }
  27. return graph;
  28. }
  29. }

3. 图的宽度优先遍历(BFS)

图的宽度优先遍历方式:
从图中弹出最高层的节点,用一个集合 Set 注册该节点,然后将该节点入队列。当我们从队列中将它弹出时,将它的相邻节点(指向的节点)进行入队列,但是首先需要判断相邻节点是否在集合中注册,如果注册了,就不做处理;如果未注册,就进行注册,并将该节点进行入队列。然后重复刚刚的操作,对每层进行遍历

方法模板:

  1. import java.util.HashSet;
  2. import java.util.LinkedList;
  3. import java.util.Queue;
  4. public class BFS {
  5. // BFS 需要有一个头节点
  6. public static void bfs(Node start) {
  7. if (start == null) {
  8. return;
  9. }
  10. Queue<Node> queue = new LinkedList<>();
  11. HashSet<Node> set = new HashSet<>();
  12. queue.add(start);
  13. set.add(start);
  14. while (!queue.isEmpty()) {
  15. Node node = queue.poll();
  16. System.out.println(node.value);
  17. for (Node cur : node.nexts) {
  18. if (!set.contains(cur)) {
  19. set.add(cur);
  20. queue.add(cur);
  21. }
  22. }
  23. }
  24. }
  25. }

4. 图的深度优先遍历(DFS)

图的宽度优先遍历方式:
一条路走到底为止,但是不能形成环路,当到底为止后,就返回上一个节点,如果该节点没有其它路,就继续往上。当某个节点还有其它路,先判断新的节点是否已经打印果过,打印过就继续往上,直到找到新的节点且未打印过。当最终返回头节点,则深度遍历结束。其中使用集合 Set 来标记该节点是否走过或打印过,使用栈来存储当前遍历路线的节点

方法模板:

  1. import java.util.HashSet;
  2. import java.util.Stack;
  3. public class DFS {
  4. public static void dfs(Node node) {
  5. if (node == null) {
  6. return;
  7. }
  8. Stack<Node> stack = new Stack<>();
  9. HashSet<Node> set = new HashSet<>();
  10. stack.add(node);
  11. set.add(node);
  12. // 在入栈时就进行打印
  13. System.out.println(node.value);
  14. while (!stack.isEmpty()) {
  15. Node cur = stack.pop();
  16. for (Node next : cur.nexts) {
  17. if (!set.contains(next)) {
  18. stack.add(cur);
  19. stack.add(next);
  20. set.add(next);
  21. System.out.println(next.value);
  22. break;
  23. }
  24. }
  25. }
  26. }
  27. }

相关文章