图的广度优先搜索和深度优先搜索

广度优先搜索(BFS)

广度优先搜索(Breadth-First-Search),我们平常都简称 BFS。直观地讲,它其实就是一种“地毯式”层层推进的搜索策略,即先查找离起始顶点最近的,然后是次近的,依次往外搜索。


image.png

深度优先搜索(DFS)

深度优先搜索(Depth-First-Search),简称 DFS。最直观的例子就是“走迷宫”。

假设你站在迷宫的某个岔路口,然后想找到出口。你随意选择一个岔路口来走,走着走着发现走不通的时候,你就回退到上一个岔路口,重新选择一条路继续走,直到最终找到出口。这种走法就是一种深度优先搜索策略。

走迷宫的例子很容易能看懂,我们现在再来看下,如何在图中应用深度优先搜索,来找某个顶点到另一个顶点的路径。

你可以看下面这幅图。搜索的起始顶点是 s,终止顶点是 t,我们希望在图中寻找一条从顶点 s 到顶点 t 的路径。如果映射到迷宫那个例子,s 就是你起始所在的位置,t 就是出口。我用深度递归算法,把整个搜索的路径标记出来了。这里面实线箭头表示遍历,虚线箭头表示回退。从图中我们可以看出,深度优先搜索找出来的路径,并不是顶点 s 到顶点 t 的最短路径

image.png

代码demo

其中存储的图数据如下图所示


image.png
    /**
     * 使用邻接表存储图
     * 使用Map表示相邻顶点列表
    */
    class Graph{
      constructor(isDirected = false) {
        // 图是否有向(默认无向)
        this.isDirected = isDirected;
        // 使用数组存储所有顶点
        this.vertices = [];
        // 使用顶点名作为键,相邻顶点作为值
        this.adjList = new Map();
      }
      // 添加顶点
      addVertex(v){
        if(!this.vertices.includes(v)){
          this.vertices.push(v);
          // 顶点v的相邻顶点使用Set存储
          this.adjList.set(v, new Set());
        }
      }
      // 添加边,参数为连接边的两个顶点
      addEdge(v, w){
        if(!this.vertices.includes(v)){
          this.addVertex(v);
        }
        if(!this.vertices.includes(w)){
          this.addVertex(w);
        }
        this.adjList.get(v).add(w);
        // 如果是无向图,则再添加一条w到v的边
        if(!this.isDirected){
          this.adjList.get(w).add(v);
        }
      }
      // 返回顶点列表
      getVertices(){
        return this.vertices;
      }
      // 返回邻接表
      getAdjList(){
        this.adjList;
      }
      // 广度优先搜索(Breadth-First-Search),求出顶点s到顶点t的最短路径
      bfs(s, t){
        if(s === t){
          return;
        }
        // visited 是用来记录已经被访问的顶点,用来避免顶点被重复访问
        let visited = {s:true};
        // queue 是一个队列,用来存储已经被访问、但相连的顶点还没有被访问的顶点
        let queue = [s];
        // prev 用来记录搜索路径
        let prev = {};

        while(queue.length !== 0){
          // 出队,取队列第1个顶点
          const v = queue.shift();
          if(v === t){
            console.log('找到了', prev)
            break;
          }
          // 取顶点v的相邻顶点
          const list = this.adjList.get(v)
          for(let i of list){
            if(visited[i] !== true){
              visited[i] = true;
              // 入队
              queue.push(i);
              prev[i] = v;
            }
          }
        }

        this.print(s, t, prev);
      }

      // 深度优先搜索(Depth-First-Search),深度优先搜索找出来的路径,并不是顶点 s 到顶点 t 的最短路径。
      dfs(s, t){
        if(s === t){
          return;
        }
        // visited 是用来记录已经被访问的顶点,用来避免顶点被重复访问
        let visited = {s:true};
        // stack 是一个栈,用来存储深度优先遍历的顶点
        let stack = [s];
        // prev 用来记录搜索路径
        let prev = {};

        while(stack.length !== 0){
          // 出栈
          const v = stack.pop();
          if(v === t){
            console.log('找到了', prev)
            break;
          }
          const list = this.adjList.get(v);
          for(let i of list){
            if(visited[i] !== true){
              visited[i] = true;
              // 入栈
              stack.push(i);
              prev[i] = v;
            }
          }
        }

        this.print(s, t, prev);
      }

      print(s, t, prev){
        let q = t;
        let arr = [];
        while(true){
          arr.push(q);
          q = prev[q];
          if(q === s){
            arr.push(q);
            // 打印路径
            console.log(arr.reverse())
            break;
          }
        }
      }
    }

    const graph = new Graph();

    const myVertices = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I'];
    for(let i of myVertices){
      graph.addVertex(i);
    }
    graph.addEdge('A', 'B');
    graph.addEdge('A', 'C');
    graph.addEdge('A', 'D');
    graph.addEdge('C', 'D');
    graph.addEdge('C', 'G');
    graph.addEdge('D', 'H');
    graph.addEdge('D', 'G');
    graph.addEdge('B', 'E');
    graph.addEdge('B', 'F');
    graph.addEdge('E', 'I');

    console.log(graph)

    graph.dfs('A', 'H');
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容