BFS shortest path proof and implementation

The BFS shortest path feels obvious, but the proof is quite laborious. Most of the following proofs are taken from CLRS, and it is easier to understand in the reverse order form. First, a lemma needs to be proved, that is, the d values ​​of all points in the BFS are arranged in ascending order according to the entry queue, that is, d(s) <= d(v1) <= ... <=d(vr).

 

1. What BFS gets is a path, that is, the path d(v) from the starting point s to any point v, so it must be greater than or equal to the shortest path δ(s,v), that is, d(v) >=δ(s ,v)

 

2. The following only needs to prove that the situation of d(v)>δ(s,v) does not exist, so assuming that d(v)>δ(s,v) gets a contradiction.

 

Use mathematical induction to prove d(v) = δ(s,v), and inductively prove the length of δ(s,v), when δ(s,v)=0, it is obviously true, if and only if s= v; also true when δ(s, v)=1, these v must be directly connected to s, so it can be seen from the DFS process that these d(v) are equal to 1, so it is also true; the following assumption is that the pair length is δ( s, v)<=n are all established.

When δ(s,v)=n+1, that is, the length of the shortest path from s to v is n+1, assuming that d(v) > δ(s,v) is obtained through BFS, set the shortest path from s to v u is the last node of v. According to the definition of the shortest path, δ(s,u) =δ(s,v) – 1 = n. From the above induction hypothesis, we know that δ(s,u)=d(u) , which has the following inequality:

d(v) >δ(s,v) = d(u)+ 1

 

When the point v is out of the queue, the point u has only three cases, white, gray, and black. The following proves that these three cases will lead to contradictions.

(1) The point u is white. Since the points u and v are adjacent, according to the BFS process, d(u) = d(v) + 1, which is contradictory when d(v) > d(u) + 1;

(2) If the point u is gray, it means that u is set to gray when processing a certain point w. Since w has been dequeued, according to the lemma, d(w)<=d(u), and d(v) = d (w)+1, then d(v) <= d(u) + 1, a contradiction;

(3) The point u is black, which means that the point u is out of the queue before v, so d(u)<=d(v), there is also a contradiction.

It can be seen that BFS is indeed the shortest path.

 

Lemma: In the process of BFS processing, it is assumed that the points in the queue Q are v 1 , v 2 , ..., v r , where v 1 is the head of the queue, in ascending order, that is, d (v 1 )<=d(v 2 )<= ...<=d(v r ), and d (v 1 ) +1 >= d(v r ), i.e. there are at most two values ​​of d

 

Proving using mathematical induction, when there is only point s at the beginning, the proposition is obvious. Then prove that neither of the two processes of dequeuing and enqueuing at a certain point will change these two properties.

1. In the process of dequeuing, due to the induction hypothesis d(v 1 )+ 1>=d(v r ), and it is in ascending order, the point v 1 is out of the queue, and v 2 becomes the queue head, the attribute of ascending order will not change, and d( v 2 ) + 1>=d(v 1 ) + 1>=d(v r ), so the proposition still holds;

2. In the process of entering the queue, assuming that the new entry point is v r+1 , then v r+1 must be added by the point u before v 1 , that is, d(v 1 ) >= d(u), d(v r+ 1 ) = d(u) + 1, before the point u is out of the queue, there are r+1 points in the Q queue, u, v 1 , v 2 , ..., v r , according to the induction hypothesis, d(u) + 1>=d(v r ), so d(v r+1 )>= d(v r ), that is, the monotonic ascending order property of the queue still exists. d(v 1 ) + 1>=d(u)+1= d(v r+1 ), so the lemma is proved.


The tree structure is stored using the adjacency list, using the linked list operation in linux_kernel, as follows:

#include "list.h"               /* list from Linux_kernel */

struct link_vertex {            /* vertex type */
    int vindex;
    struct list_head head;      /* linked to all edges */
    struct list_head qnode;     /* used for Queue when BFS */
};

struct link_edge {              /* edge type */
    struct list_head node;
    int vindex;
    int weight;
};

struct link_graph {
    int vcount;
    int ecount;
    struct link_vertex *v;
};

The BFS process is quite obvious, very similar to the pseudocode in CLRS

void print_path(struct link_graph *G, int s, int v, struct link_vertex **pi)
{
    if (v == s)
        printf("%d ", v);
    else {
        if (pi[v] == NULL)
            printf("no path from %d to %d exists\n", s, v);
        else {
            print_path(G, s, pi[v]->vindex, pi);
            printf("%d ", v);
        }
    }
}


int BFS(struct link_graph *G, int vindex)
{
    int *color, *d, i = 0;
    struct link_vertex **pi;
    struct list_head queue;
    struct link_vertex *v = NULL;

#define COLOR_WHITE 0
#define COLOR_GRAY  1
#define COLOR_BLACK 2
    
    if (vindex >= G->vcount)
        return -1;
    color = malloc(sizeof(int) * G->vcount);
    d = malloc(sizeof(int) * G->vcount);
    pi = malloc(sizeof(struct link_vertex *) * G->vcount);

    for (i = 0;i < G->vcount;i++) {
        v = G->v + i;
        color[i] = COLOR_WHITE;
        d[i] = -1;
        pi[i] = NULL;
    }

    color[vindex] = COLOR_GRAY;
    d[vindex] = 0;
    pi[vindex] = NULL;
    INIT_LIST_HEAD(&queue);

    v = G->v + vindex;
    list_add_tail(&v->qnode, &queue);

    while (!list_empty(&queue)) {
        struct link_edge *lv = NULL;
        
        v = list_entry(queue.next, struct link_vertex, qnode);
        list_del(&v->qnode);
        
        list_for_each_entry(lv, &v->head, node) {
            if (color[lv->vindex] == COLOR_WHITE) {
                color[lv->vindex] = COLOR_GRAY;
                pi[lv->vindex] = v;
                d[lv->vindex] = d[v->vindex] + 1;
                list_add_tail(&(G->v + lv->vindex)->qnode, &queue);
            }
        }
        color[v->vindex] = COLOR_BLACK;
    }

    printf("\nThe path from %d to %d\n", vindex, vindex + 1);
    print_path(G, vindex, (vindex + 1) % G->vcount, pi);
    printf("\n");

    free(pi);
    free(d);
    free(color);

    return 0;
}

Randomly generate a graph in the form of an adjacency matrix, and then convert the adjacency matrix into an adjacency list for storage. The following code can be copied and tested directly (note that the linked list operation list.h of linux_kernel must be included)

static int link_edge_init(struct link_graph *G, struct link_vertex *v, int vcount, int *weight)
{
    int i = 0;
    struct link_edge *lv = NULL;

    for (i = 0;i < vcount;i++) {
        if (v->vindex == i || weight[i] == 0)
            continue;
        lv = malloc(sizeof(struct link_edge));
        if (lv == NULL)
            return -1;

        lv->vindex = i;
        lv->weight = weight[i];
        list_add(&lv->node, &v->head);
        G->ecount++;
    }

    return 0;
}


int link_graph_init(struct link_graph *G, int vcount, int *weight)
{
    int i = 0;
    struct link_vertex *v = NULL;

    G->ecount = 0;
    G->vcount = vcount;
    G->v = malloc(sizeof(struct link_vertex) * vcount);
    if (G->v == NULL) {
        printf("OOM for G->v\n");
        return -1;
    }

    for (i = 0;i < vcount;i++) {
        v = G->v + i;
        v->vindex = i;
        INIT_LIST_HEAD(&v->head);
        link_edge_init(G, v, vcount, weight + i * vcount);
    }

    return 0;
}


void link_graph_exit(struct link_graph *G)
{
    int i = 0;
    struct link_vertex *v = NULL;
    struct link_edge *lv = NULL, *tmp = NULL;
    
    for (i = 0;i < G->vcount;i++) {
        v = G->v + i;
        list_for_each_entry_safe(lv, tmp, &v->head, node) {
            free(lv);
        }
    }

    free(G->v);
}


void link_graph_print(struct link_graph *G)
{
    int i = 0;
    struct link_vertex *v = NULL;
    struct link_edge *lv = NULL;

    printf("G has %d vertexes and %d edges\n", G->vcount, G->ecount);
    for (i = 0;i < G->vcount;i++) {
        v = G->v + i;        
        printf("vindex, %4d\n", v->vindex);
        list_for_each_entry(lv, &v->head, node) {
            printf("[%4d,%4d], ", lv->vindex, lv->weight);
        }
        printf("\n");
    }
}


int main(int argc, char **argv)
{
    int i, j;
    int n = 10, r = 0;
    int **matrix = NULL, *p = NULL;
    struct link_graph G;

    if (argc >= 2) {
        n = strtoul(argv[1], 0, 0);
    }

    p = malloc(n * n * sizeof(int));
    memset(p, 0, n * n * sizeof(int));
    matrix = malloc(n * sizeof(int *));
    for (i = 0;i < n;i++) {
        matrix[i] = p + i * n;
    }

#if 1
    srand(time(0));
    for (i = 0;i < n;i++) {
        for (j = 0;j < n;j++) {
            r = rand();
            if ((r & 0xf) == 0)
                matrix[i][j] = 1;
        }
    }
#else
(void)r;
    matrix[0][0] = matrix[0][1] = matrix[0][2] = matrix[0][3] = 1;
    matrix[1][2] = matrix[1][3] = 1;
    matrix[2][0] = matrix[2][1] = 1;
    matrix[3][0] = 1;
#endif

    for (i = 0;i < n;i++) {
        for (j = 0;j < n;j++) {
            printf("%4d ", matrix[i][j]);
        }
        printf("\n");
    }

    link_graph_init(&G, n, p);
    link_graph_print(&G);

    BFS(&G, 0);

    link_graph_exit(&G);
    free(matrix);
    free(p);

    return 0;
}


Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324170631&siteId=291194637