zoukankan      html  css  js  c++  java
  • 算法导论22.4拓扑排序 练习总结 (转载)

    22.4-1 给出算法 TOPOLOGICAL-SORT 运行于图 22-8 上时所生成的结点次序。这里的所有假设和练习 22.3-2 一样。

    ANSWER:


     

    22.4-2 请给出一个线性时间的算法,算法的输入为一个有向无环图 G = (V, E) 以及两个结点 s 和 t,算法的输出是从结点 s 到结点 t 之间的简单路径的数量。例如,对于图 22-8 所示的有向无环图,从结点 p 到结点 v 一共有 4 条简单路径,分别是 pov、poryv、posryv 和 psryv。(本题仅要求计数简单路径的条数,而不要求将简单路径本身列举出来。)

    ANSWER:给结点附加一个计数属性time,初始化 t.time = 1,其它的 time 均为 0 ,以 s 为源结点进行 DFS,一旦搜索到 t,则 t 马上着黑色(即不继续搜索 t 的后代)。每当结束一个结点的搜索,则该结点的 time 属性 = 该结点所指向的所有结点的 time 之和。最后 s.time 即路径条数。

    如图 22-8 的 p 到 v,最后 v.time = 1, y.time = 1, r.time = 1, s.time = 1, o.time = 1, p.time。

     解法二

    有向无环图G=(V,E),求其点s和e之间的路径数目。此题首先要以点s开始作DFS,得到从s为起点的所有可达点的一顶DFS树,但这个路径数目需要详细参详。

    定义P(x),表示点s到点x的路径数目,P(s)=1,即s到自身有一条路径,其余的所有路径数目都初始化为0。

    路径从s到达点x,则必须到达x的上一层结点,假设以x为终点的上一层结点有n个,即a1,a2,...,an,由加法定律可知P(x)= P(a1) + P(a2) + ... + P(an),这是一个从顶向下的递推过程,有点类似于动态规划。

    综上,我们只要获取任意一点的入邻接表(也称逆邻接表),由图G获取其转置GT,其中保存着任意一点的上一层结点。然后再从顶向下递推,正好利用拓扑排序获得的序列,因为由拓扑排序的定义可以保证结点的顺序关系,因此递推有效。以下的算法步骤:

    (1) 由图G获取其转置图GT,以得到所有点的入邻接表

    (2) 以点s开始作DFS,得到从点s到达点e的拓扑序列

    (3) 以此拓扑序列为顺序,逐个获取P值,最终得到P(e),即s到e的路径数目

     

    图中实线为tree edge,曲线为forward和cross edge,从p到v的路径数目,递推过程如下:

    P(p) = 1

    P(o) = P(p) = 1

    P(s) = P(p) + P(o)= 2

    P(r) = P(o) +P(s) = 3

    P(y) = P(r) = 3

    P(v) = P(y) +P(o) = 4

    步骤(1) 代码如下 

    static int graph_add_edge(struct link_graph *G, int uindex, int vindex)
    {
        struct link_edge *e = NULL;
        struct link_vertex *u = G->v + uindex;
    
        e = malloc(sizeof(struct link_edge));
        if (!e)
            return -1;
        e->vindex = vindex;
        e->type = EDGE_NONE;
    
        list_add(&e->node, &u->head);
        return 0;
    }
    
    struct link_graph* graph_transpos(struct link_graph *G)
    {
        int i = 0;
        struct link_edge *e = NULL;
        struct link_vertex *u = NULL;
        struct link_graph *GT = NULL;
    
        GT = malloc(sizeof(struct link_graph));
        if (!GT)
            return NULL;
        GT->v = malloc(G->vcount * sizeof(struct link_vertex));
        if (!GT->v) {
            free(GT);
            return NULL;
        }
    
        GT->vcount = G->vcount;
        GT->ecount = G->ecount;
        for (i = 0;i < GT->vcount;i++) {
            u = GT->v + i;
            u->vindex = i;
            INIT_LIST_HEAD(&u->head);
        }
    
        for (i = 0;i < G->vcount;i++) {
            u = G->v + i;
            list_for_each_entry(e, &u->head, node) {
                graph_add_edge(GT, e->vindex, i);
            }
        }
    
        return GT;
    }
    View Code

    步骤(2) 代码如下 

    void DFS_visit_topo(struct link_graph *G, struct link_vertex *u, int *color, struct list_head *head)
    {
        struct link_edge *le = NULL;
        
        color[u->vindex] = COLOR_GRAY;
    
        list_for_each_entry(le, &u->head, node) {
            if (color[le->vindex] == COLOR_WHITE) {
                DFS_visit_topo(G, G->v + le->vindex, color, head);
                le->type = EDGE_TREE;
            }
            else if (color[le->vindex] == COLOR_GRAY) {
                le->type = EDGE_BACK;
                printf("G has cycle and cannot topology sort
    ");
            }
        }
        
        color[u->vindex] = COLOR_BLACK;
        list_add(&u->qnode, head);
    }
    
    int graph_topo_sort(struct link_graph *G, int s, struct list_head *thead)
    {
        int i;
        int *color;
        color = malloc(sizeof(int) * G->vcount);
        for (i = 0;i < G->vcount;i++) {
            color[i] = COLOR_WHITE;
        }
        DFS_visit_topo(G, G->v + s, color, thead);
    
        free(color);
        return 0;
    }
    View Code

    步骤(3) 代码如下 

    int graph_count_path(struct link_graph *G, int s, int end)
    {
        int *P;
        struct list_head thead;
        struct link_vertex *u = NULL, *ut = NULL;
        struct link_edge *e = NULL;
        struct link_graph *GT = NULL;
    
        GT = graph_transpos(G);
        if (GT == NULL)
            return -1;
        P = malloc(G->vcount * sizeof(int));
        memset(P, 0, G->vcount * sizeof(int));
        P[s] = 1;
    
        INIT_LIST_HEAD(&thead);
        graph_topo_sort(G, s, &thead);
    
        printf("The topology sort from %d is below:
    ", s);
        list_for_each_entry(u, &thead, qnode) {
            printf("%4d ", u->vindex);
            if (u->vindex == s)
                continue;
            ut = GT->v + u->vindex;
            list_for_each_entry(e, &ut->head, node) {
                P[u->vindex] += P[e->vindex];
            }
            if (u->vindex == end) {
                printf("
    
    There are %d paths from %d to %d
    ", P[end], s, end);
                break;
            }
        }
    
        link_graph_exit(GT);
        return 0;
    }
    View Code

    22.4-3 给出一个算法来判断给定无向图 G = (V, E) 是否包含一个环路。算法运行时间应该在 O(V) 数量级,且与 |E| 无关。

    ANSWER

    我们都知道对于一个无向图而言,如果它能表示成一棵树,那么它一定没有回路,并且有|E|=|V|-1,如果在这个树上添加一条边,那么就构成了回路,如果在这个树中去掉一个边就成了森林(注意:如果只是限定|E|<|V|-1它不一定是森林,它当中可能存在无向连通子图)。

         对于这个题目我们可以用DFS来做,每当访问当前节点的邻接表时,如果存在某个邻接的元素已被标记为访问状态,且这个元素不是当前元素的父节点,那么这个图就是存在回路的。总的时间代价是O(|E|+|V|),因为E<=|V|-1(如果|E|>|V|-1根据无向图的性质,那么这个无向图一定存在回路),所以O(|E|+|V|)=O(|V|)

    22.4-4 证明或反证下述论断:如果有向图 G 包含环路,则在算法 TOPOLOGICAL-SORT(G) 所生成的结点序列里,图 G 中与所生成序列不一致的“坏”边的条数最少。

    ANSWER:

     

    22.4-5 在有向无环图 G =(V, E) 上执行拓扑排序还有一种办法,就是重复寻找入度为 0 的结点,输出该结点,将该结点及从其发出的边从图中删除。请解释如何在 O(V+E) 的时间内实现这种思想。如果图 G 包含环路,将会发生什么情况?

    ANSWER:首先利用DFS在O(V+E)时间内记录每个结点的入度。然后在每次删除结点后更新每个结点的入度。删除结点时间为O(V),更新结点入度时间为O(E),所以总时间为O(V+E)。

    如果图G包含环路,环路中的结点的入度会均大于0,都无法删除。

  • 相关阅读:
    MyBatis 内连接association 左外连接collection
    mybatis的基本配置:实体类、配置文件、映射文件、工具类 、mapper接口
    SpringMVC 使用Form标签库制作登录表单
    SpringMVC 配置过滤器解决中文乱码问题
    用户登录模块————账户安全问题
    HashSet,TreeSet和LinkedHashSet的区别
    SpringMVC 服务器端验证
    php输出(下载)文件
    26进制(字母)转十进制算法
    PHP之可变变量
  • 原文地址:https://www.cnblogs.com/mhpp/p/7419052.html
Copyright © 2011-2022 走看看