有向無環圖兩點之間的路徑數目(算法導論22.4-2)

有向無環圖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;
}

步驟(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\n");
        }
    }
    
    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;
}

步驟(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:\n", 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("\n\nThere are %d paths from %d to %d\n", P[end], s, end);
            break;
        }
    }

    link_graph_exit(GT);
    return 0;
}

發佈了72 篇原創文章 · 獲贊 28 · 訪問量 17萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章