图的搜索

前言

图的搜索指的是系统化地跟随图中的边来访问图中每一个结点,并不是随意地访问图中的结点。图的搜索算法可以用来发现图的结构,许多图的算法都要求先搜索全图,可以说,图的搜索是整个图算法的核心。

图的搜索有两种类型:

  • 广度优先搜索
  • 深度优先搜索

本文中将用到两个图,一个有向图,一个无向图,分别如下:

广度优先搜索

广度优先搜索是指:给定图 G = (V, E)和一个可识别的源结点s,对图中的边进行探索,找到s能到达的所有结点。

举例说明,给定结点s,v为s能到达的所有结点的集合,先寻找s能到达的所有结点,再找集合v中结点能到达的所有结点。

队列和栈有什么不同呢?它们的不同或者说精髓用法体现在图的搜索上,细细体会就能理解队列和栈的精髓。广度优先搜索需要用到队列,因为先找s能到的顶点,再找v集合能到的顶点,需要先入先出,否则就不是广度优先搜索了

邻接链表下的广度优先搜索:

  /*
 * 广度优先算法
 * 广度优先算法可以计算出到遍历起点的最短路径,因为最短路径一定是此顶点的前驱距离+1得到的
 * 有权值的后续讨论,如果无权图这个结论是对的
 */
public void bfs(Vertex vertex){
    if (vertex == null) {
        return;
    }
    for (Vertex v : mList) {
        v.color = COLOR.WHITE;
    }
    int dis = 0;
    int index = mList.indexOf(vertex);
    if (index == -1) {
        System.out.println("error vertex");
        return;
    }
    Queue<Vertex> queue = new LinkedBlockingDeque<Vertex>();
    queue.add(vertex);
    vertex.color = COLOR.GRAY;
    vertex.d = dis;
    System.out.println(vertex);
    Arc arc = null;
    Vertex temp = null;
    while (!queue.isEmpty()) {
        temp = queue.peek();
        arc = temp.firstArc;
        while (arc != null) {
            if (arc.vertex.color == COLOR.WHITE) {
                arc.vertex.color = COLOR.GRAY;
                arc.vertex.d = temp.d + 1;
                System.out.println(arc.vertex);
                queue.add(arc.vertex);
                break;
            }
            arc = arc.next;
        }
        if (arc == null) {
            temp.color = COLOR.BLACK;
            queue.remove(temp);
        }
        if (queue.isEmpty()) {
            for (Vertex vertex2 : mList) {
                if (vertex2.color == COLOR.WHITE) {
                    queue.add(vertex2);
                    vertex2.color = COLOR.GRAY;
                    System.out.println(vertex2);
                    break;
                }
            }
        }
    }
}

结点中包含颜色,通过颜色判断结点是否被访问。先将第一个节点V添加到队列,再遍历以V为起点的链表,如果链表中有某个结点未被染色,则访问并添加到队列当中,当以V为起点的链表的所有结点均已被染色,则将V从队列中移除。

特别值得一提的是,广度优先搜索的副产品非常有用,它能够计算最短路径。假设结点V的前驱结点是S,那么到某个点的最短路径V.d = S.d + 1。

δ(s,v),表示s到v的最短距离。u.π表示u的前驱,所以上述算法中的u.d就等于到搜索起点的最短距离。

再看看邻接矩阵情况下的广度优先搜索算法:

  public void bfs(Vertex vertex){
    if (vertex == null) {
        return;
    }
    int index = mList.indexOf(vertex);
    if (index == -1) {
        return;
    }
    for (Vertex v : mList) {
        v.color = COLOR.WHITE;
    }
    Queue<Vertex> queue = new LinkedBlockingDeque<>();
    queue.add(vertex);
    vertex.color = COLOR.GRAY;
    vertex.d = 0;
    System.out.println(vertex);
    Vertex temp = null;
    while (!queue.isEmpty()) {
        temp = queue.peek();
        int tempIndex = mList.indexOf(temp);
        for (int i = 0; i < maxLength; i++) {
            if (mEdges[tempIndex][i] != null && mEdges[tempIndex][i].v2.color == COLOR.WHITE) {
                mEdges[tempIndex][i].v2.color = COLOR.GRAY;
                mEdges[tempIndex][i].v2.d = temp.d + 1;
                System.out.println(mEdges[tempIndex][i].v2);
                queue.add(mEdges[tempIndex][i].v2);
            }
        }
        queue.remove(temp);
    }
}

邻接矩阵下的广度优先搜索变化也不大,主要在寻找结点v有边相连的未被访问的结点逻辑上,稍有不同。

深度优先算法

深度优先搜索,顾名思义,只要可能就在图中尽量深入。深度优先搜索,总是对最近才发现的结点v的出发边进行搜索。

深度优先搜索有两种写法,本文中采用递归方式,不使用栈方法,有兴趣的同学们自己尝试使用栈实现。

邻接链表情况下的深度优先搜索:

int time = 0;
/*
 * 深度优先算法,下面的深度优先算法是最正确的,以栈来实现深度优先算法有可能有问题
 * 下面的计算方法最容易理解,访问完自己后立即访问自己的非访问后续结点
 * 同时深度优先算法,可以计算每个结点访问顺序,最终实现拓扑排序。
 * 即f最大的,放在前面
 */
public void dfs(){
    for (Vertex vertex : mList) {
        vertex.color = COLOR.WHITE;
    }
    time = 0;
    for (Vertex vertex : mList) {
        if (vertex.color == COLOR.WHITE) {
            dfsVisit(vertex);
        }
    }
}
public void dfsVisit(Vertex vertex){
    if (vertex == null) {
        return;
    }
    time++;
    vertex.color = COLOR.GRAY;
    vertex.d = time;
    Arc arc = vertex.firstArc;
    while (arc != null) {
        if (arc.vertex.color == COLOR.WHITE) {
            dfsVisit(arc.vertex);
        }
        arc = arc.next;
    }
    time++;
    vertex.f = time;
    System.out.println(vertex);
    vertex.color = COLOR.BLACK;
}

深度优先搜索,先取顶点数组中的每一个顶点,如果顶点未被访问,则继续访问与该顶点有边相连的顶点,再递归访问新的顶点。

当一个顶点被访问时,它的颜色为灰色,只有当与它相连的所有顶点都被访问完成了,此顶点颜色被置为黑色。
颜色为灰色时,记录时间,颜色为黑色时再次记录时间,将时间打印下来,非常有意思,因为打印的结果就是拓扑排序结果。

拓扑排序是针对有向无环图的概念。举例说明,穿袜子和穿鞋子是图的两个顶点,穿袜子指向穿鞋子,日常生活中一定要先穿袜子才能穿鞋子,这就是拓扑排序干的活。

如果v1和v2有边相连,并且是由v1指向v2,那么一定要先访问v1,再来访问v2。针对前言中的有向图,拓扑排序的结果是 1 2 4 3 5,即f值最大的应该先访问。

邻接矩阵情况下的深度优先搜索:

public void dfs(){
    for (Vertex v : mList) {
        v.color = COLOR.WHITE;
    }
    for (Vertex v : mList) {
        if (v.color == COLOR.WHITE) {
            dfsVisit(v);
        }
    }
}

int time = 0;
public void dfsVisit(Vertex vertex){
    time++;
    vertex.color = COLOR.GRAY;
    vertex.d = time;
    int index = mList.indexOf(vertex);
    Vertex v2 = null;
    for (int i = 0; i < maxLength; i++) {
        v2 = mEdges[index][i] == null ? null : mEdges[index][i].v2;
        if (v2 != null && v2.color == COLOR.WHITE) {
            dfsVisit(v2);
        }
    }
    time++;
    vertex.color = COLOR.BLACK;
    vertex.f = time++;
    System.out.println(vertex);
}

和邻接链表类似,不再详述

结语

图的两种搜索比较简单,但搜索过程中的额外两个信息是非常重要的。广度优先搜索能够查找最短路径,而深度优先搜索结果中,可以知道拓扑排序结果,f值最大先访问,即是拓扑排序结果。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,117评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,328评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,839评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,007评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,384评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,629评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,880评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,593评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,313评论 1 243
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,575评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,066评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,392评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,052评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,082评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,844评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,662评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,575评论 2 270

推荐阅读更多精彩内容