图的BFS和DFS算法

是一种灵活的数据结构,一般作为一种模型用来定义对象之间的关系或联系。对象由顶点(V)表示,而对象之间的关系或者关联则通过图的边(E)来表示。

图可以分为有向图无向图,一般用G=(V,E)来表示图。经常用邻接矩阵或者邻接表来描述一副图。

在图的基本算法中,最初需要接触的就是图的遍历算法,根据访问节点的顺序,可分为广度优先搜索(BFS)深度优先搜索(DFS)

下文都是对该例图进行操作:
在这里插入图片描述

广度优先搜索(BFS)

广度优先搜索在进一步遍历图中顶点之前,先访问当前顶点的所有邻接结点。

  1. 首先选择一个顶点作为起始结点,并将其染成灰色,其余结点为白色。
  2. 将起始结点放入队列中。
  3. 从队列首部选出一个顶点,并找出所有与之邻接的结点,将找到的邻接结点放入队列尾部,将已访问过结点涂成黑色,没访问过的结点是白色。如果顶点的颜色是灰色,表示已经发现并且放入了队列,如果顶点的颜色是白色,表示还没有发现
  4. 按照同样的方法处理队列中的下一个结点。

就是出队的顶点变成黑色,在队列里的是灰色,还没入队的是白色。

表示顶点的数据结构

function Vertex() {
    if (!(this instanceof Vertex))
        return new Vertex();
    this.color = this.WHITE; //初始为 白色
    this.pi = null; //初始为 无前驱
    this.d = this.INFINITY; //初始为 无穷大
    this.edges = null; //由顶点发出的所有边
    this.value = null; //节点的值 默认为空
}
Vertex.prototype = {
    constructor: Vertex,
    WHITE: 'white', //白色
    GRAY: 'gray', //灰色
    BLACK: 'black', //黑色
    INFINITY: null, //d 为 null 时表示无穷大
}

表示边的数据结构

function Edge() {
    if (!(this instanceof Edge))
        return new Edge();
    this.index = null; //边所依附的节点的位置
    this.sibling = null;
}

表示图的数据结构

function Graph() {
    if (!(this instanceof Graph))
        return new Graph();
    this.graph = []; //存放顶点的数组
}
Graph.prototype = {
    constructor: Graph,
    addNode: function (node) {
        this.graph.push(node);
    },
    getNode: function (index) {
        return this.graph[index];
    }
}

构建图

//创建 顶点
var vA = Vertex();
var vB = Vertex();
var vC = Vertex();
var vD = Vertex();
var vE = Vertex();
var vF = Vertex();
vA.value = 'A';
vB.value = 'B';
vC.value = 'C';
vD.value = 'D';
vE.value = 'E';
vF.value = 'F';

//构建由 A 节点发出的边集
var eA1 = Edge();
var eA2 = Edge();
eA1.index = 1;
eA2.index = 3;
eA1.sibling = eA2;
vA.edges = eA1;

//构建有 B 节点发出的边集
var eB1 = Edge();
var eB2 = Edge();
var eB3 = Edge();
eB1.index = 0;
eB2.index = 4;
eB3.index = 2;
eB1.sibling = eB2;
eB2.sibling = eB3;
vB.edges = eB1;

//构建由 C 节点发出的边
var eC1 = Edge();
var eC2 = Edge();
var eC3 = Edge();
eC1.index = 1;
eC2.index = 4;
eC3.index = 5;
eC1.sibling = eC2;
eC2.sibling = eC3;
vC.edges = eC1;

//构建由 D 节点发出的边
var eD1 = Edge();
eD1.index = 0;
vD.edges = eD1;

//构建由 E 节点发出的边
var eE1 = Edge();
var eE2 = Edge();
var eE3 = Edge();
eE1.index = 1;
eE2.index = 2;
eE3.index = 5;
eE1.sibling = eE2;
eE2.sibling = eE3;
vE.edges = eE1;

//构建由 F 节点发出的边
var eF1 = Edge();
var eF2 = Edge();
eF1.index = 2;
eF2.index = 4;
eF1.sibling = eF2;
vF.edges = eF1;

//构建图
var g = Graph();
g.addNode(vA);
g.addNode(vB);
g.addNode(vC);
g.addNode(vD);
g.addNode(vE);
g.addNode(vF);

广度优先搜索

function BFS(g, s) {
    let queue = []; //辅助队列 Q
    s.color = s.GRAY; //首次发现s涂为灰色
    s.d = 0; //距离为0
    queue.push(s); //将s放入队列 Q
    while (queue.length > 0) { //当队列Q中有顶点时执行搜索
        let u = queue.shift(); //将Q中的第一个元素移出
        if (u.edges == null) continue; //如果从当前顶点没有发出边
        let sibling = u.edges; //获取表示邻接边的链表的头节点
        while (sibling != null) { //当链表不为空
            let index = sibling.index; //当前边所连接的顶点在队列中的位置
            let n = g.getNode(index); //获取顶点
            if (n.color == n.WHITE) { //如果没有被访问过
                n.color = n.GRAY; //涂为灰色
                n.d = u.d + 1; //距离加1
                n.pi = u; //设置前驱节点
                queue.push(n); //将 n 放入队列 Q
            }
            sibling = sibling.sibling; //下一条边
        }
        u.color = u.BLACK; //当前顶点访问结束 涂为黑色
    }
}

访问顺序

B->A->E->C->D->F

深度优先搜索(DFS)

深度优先搜索在搜索过程中访问某个顶点后,需要递归地访问此顶点的所有未访问过的相邻顶点。
深度优先搜索一般默认的源点有多个。
初始条件下所有节点为白色,选择一个作为起始顶点,按照如下步骤遍历:

  1. 选择起始顶点涂成灰色,表示还未访问
  2. 从该顶点的邻接顶点中选择一个,继续这个过程(即再寻找邻接结点的邻接结点),一直深入下去,直到一个顶点没有邻接结点了,涂黑它,表示访问过了
  3. 回溯到这个涂黑顶点的上一层顶点,再找这个上一层顶点的其余邻接结点,继续如上操作,如果所有邻接结点往下都访问过了,就把自己涂黑,再回溯到更上一层。
  4. 上一层继续做如上操作,知道所有顶点都访问过。

数据结构的表示上面,只有顶点的表示稍有不同,别的都一样。

 function Vertex() {
    if (!(this instanceof Vertex))
        return new Vertex();
    this.color = this.WHITE; //初始为 白色
    this.pi = null; //初始为 无前驱
    this.d = null; //时间戳 发现时
    this.f = null; //时间戳 邻接链表扫描完成时
    this.edges = null; //由顶点发出的所有边
    this.value = null; //节点的值 默认为空
}
Vertex.prototype = {
    constructor: Vertex,
    WHITE: 'white', //白色
    GRAY: 'gray', //灰色
    BLACK: 'black', //黑色
}

深度优先搜索

function DFS(g) {
    let t = 0; //时间戳
    for (let v of g.vertexs) { //让每个节点都作为一次源节点
        if (v.color == v.WHITE) DFSVisit(g, v);
    }
    function DFSVisit(g, v) {
        t = t + 1; //时间戳加一
        v.d = t;
        v.color = v.GRAY;
        let sibling = v.edges;
        while (sibling != null) {
            let index = sibling.index;
            let n = g.getNode(index);
            if (n.color == n.WHITE) {
                n.pi = v;
                DFSVisit(g, n); //先纵向找
            }
            sibling = sibling.sibling; //利用递归的特性来回溯
        }
        v.color = v.BLACK;
        t = t + 1; //时间戳加一
        v.f = t;
    }
}

访问顺序

F->C->E->B->D->A

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值