BFS

来源:互联网 发布:中日合邦 知乎 编辑:程序博客网 时间:2024/06/06 05:56

总体描述

BFS 是最简单的图搜索算法之一。
Prim最小生成树算法和Dijkstra的单源最短路径算法都使用了类似BFS的思想。

给定图G=(V, E)和一个可以识别的源节点s, BFS能够计算出从源点s到每个可以到达的节点的距离(最少的边数),同时生成一颗“BFS生成树“。

BFS的搜索过程是沿着其广度方向扩展的,算法需要在发现了所有距离源点s为k的所有节点之后,才会发现距离源节点s为k+1的其他节点。

为了跟踪算法进展,BFS在概念上将每个节点标识好颜色(白色:开始状态; 灰色:被发现,但是其邻接点还没有更新完,即邻接点还有白色的; 黑色:被发现,邻接点完全更新完)。
BFS对灰色和黑色节点加以区别,以确保搜索按照BFS模式进行。

执行BFS的过程,我们将构造出一颗BFS优先树
最后打印BFS的路径时,我们可以根据生成树的“前驱-后继“状态来打印。

搜索过程:

假定输入图G = (V, E) 是以邻接链表所表示的,该算法为图中每个节点赋予了一些额外属性: 将每个节点u的颜色存放在属性u.color里,将u的前驱节点存放在属性u.pre之中,没有就记录为NIL。使用一个FIFO队列Queue管理灰色节点。

伪代码如下(BFS过程,构造BFS树):

BFS (G, s){    for each vertex {u} belongs to (G.V - {s})        u.color = WHITE        u.d = INF        u.pre = NIL    s.color = GRAY    s.d = 0    s.pre = NIL    Q = NULL 空队列    ENQUEUE(Q, s)         //s入队    while (Q != NULL) {        u = DEQUEUE(Q)    //出队        for each {v} belongs to (G.Adj[u])  //u的所有邻接节点        {            if v.color == WHITE {                v.color = GRAY                v.d = u.d + 1                v.pre = u                ENQUEUE(Q, v)       //入队            }        }         u.color = BLACK    } }

打印BFS路径,在BFS构造树之后:

PRINT-PATH(G, s, v){    if v == s        print s    else if v.pre == NIL        print "no path from " s " to " v "exists"    else        PRINT-PATH(G, s, v.pre)        print v}

时间复杂度分析

邻接表存储时,BFS搜索的总运行时间为 O(V + E)

实践

按照伪代码写demo,使用了邻接表存储,循环数组模拟队列即可。图是按照算法导论上的图 P345

代码如下:

bfs.h

#include <stdio.h>#include <stdlib.h>#define INF 10000#define NIL 0#define MAX 100#define DATA charenum judge{    white = 0,    gray = 1,    black = 2};struct node {    DATA name;    DATA pre_node;    size_t distance;    enum judge color;};struct G {    struct node *array;        struct vexnode *aja_table;    struct graph *g;};// ===================struct graph {    size_t nodes_number;    size_t edges_number;};struct vexnode {    DATA id;    struct arcnode *arc_head; };struct arcnode {    DATA id;    struct arcnode *next;};

bfs.c

#include "./bfs.h" struct graph *generate_graph(){    struct graph *head = NULL;    if ((head = (struct graph *)malloc(sizeof(struct graph))) == NULL)        return NULL;    head->nodes_number = 0;    head->edges_number = 0;    return head;}struct arcnode *malloc_arcnode(const DATA id){    struct arcnode *tmp = NULL;    if ((tmp = (struct arcnode *)malloc(sizeof(struct arcnode))) == NULL)        return NULL;    tmp->id = id;    tmp->next = NULL;    return tmp;}struct vexnode *malloc_vexnode(size_t length, struct graph *g){    struct vexnode *head = NULL;     if ((head = (struct vexnode *)malloc(length * sizeof(struct vexnode))) == NULL)        return NULL;    g->nodes_number = length;    return head;}void generate_vexnode(struct vexnode head[], struct graph *g){    /*     * 做测试,id就用ABCDEFGH     */    char data = 'A';    size_t length = g->nodes_number;    struct vexnode *iter = head;    for (size_t i = 1; i <= length; i++) {        iter[i].id = data;        iter[i].arc_head = NULL;        data++;    }}struct vexnode *search_vexnode_by_id(struct vexnode head[], const DATA id, struct graph *g) {    size_t length = g->nodes_number;    struct vexnode *first = head;     for (size_t i = 0; i <= length; i++) {        if (first[i].id == id)            return (first + i);    }    return NULL;}int judge_arcnode_by_i_j(struct vexnode head[], const DATA i, const DATA j, struct graph *g){    struct vexnode *result = search_vexnode_by_id(head, i, g);    if (result == NULL)        return -1;      //failure    struct arcnode *iter = result->arc_head;    while (iter != NULL) {        if (iter->id == j) {            return 0;   //success        }        iter = iter->next;    }    return -2;          //not find}int add_arcnode_by_i_j(struct vexnode head[], const DATA i, const DATA j, struct graph *g){    int judge = -3;    if ((judge = judge_arcnode_by_i_j(head, i, j, g)) == 0) {        printf("already exist.\n");        return -1;     }    struct vexnode *search = NULL;     search = search_vexnode_by_id(head, i, g);    if (search == NULL) {        printf("could not search.\n");        return -1;    }    struct arcnode *insert = malloc_arcnode(j);    insert->next = search->arc_head;    search->arc_head = insert;    g->edges_number++;    return 0;}struct node *search_node_by_name(struct G *total, const DATA name){    size_t length = total->g->nodes_number;    for (size_t i = 0; i < length; i++) {        if (name == total->array[i].name) {            struct node *position = total->array + i;            return position;        }    }    return NULL;}void BFS(struct G *total, const DATA s){    struct node *data_head = total->array;    size_t length = total->g->nodes_number;    for (size_t i = 0; i < length; i++) {        if (data_head[i].name == s) {            continue;        }        data_head[i].distance = 0;        data_head[i].pre_node = NIL;        data_head[i].color = white;    }     struct node *the_start = total->array;    while (the_start->name != s) {        the_start++;    }    if (the_start == NULL) {        printf("failed to find the start point.\n");        return ;    }    the_start->distance = 0;    the_start->pre_node = NIL;    the_start->color = gray;    //  begin    DATA Queue[MAX] = {0};    size_t top, end;                //top做入队, end做出队     top = end = 0;    Queue[top] = s;     //  enqueue    top = (top + 1) % MAX;    while (top != end) {    // Queue is not empty        DATA u = Queue[end];        //出队        end = (end + 1) % MAX;        printf("start: u = %c\n ", u);///////////////////////        struct node *search_u = NULL;        if ((search_u = search_node_by_name(total, u)) == NULL) {            printf("search u error\n");            return;        }         struct vexnode *ajacency = search_vexnode_by_id(total->aja_table, u, total->g);        struct arcnode *arc_start = ajacency->arc_head;                //printf("search vexnode by id:  find %c\n", ajacency->id);        //getchar();        while (arc_start != NULL) {            DATA v = arc_start->id;              struct node *search_v = search_node_by_name(total, v);            if (search_v == NULL) {                printf("search v error\n") ;                return ;            }            if (search_v->color == white) {                search_v->distance = search_u->distance + 1;                search_v->pre_node = search_u->name;                search_v->color = gray;                Queue[top] = v;         //enqueue                top = (top + 1) % MAX;                printf("enqueue v = %c\n", v);            }            arc_start = arc_start->next;        }        //getchar();    }}void print_bfs(struct G *total, const DATA s, const DATA v){    struct node *search_v = search_node_by_name(total, v);    if (search_v == NULL) {        printf("print_bfs: could not find v\n") ;        return;    }    if (s == v)        printf("%c ", s);    else if (search_v->pre_node == NIL)         printf("no path from %c to %c \n", s, v);    else {        print_bfs(total, s, search_v->pre_node) ;        printf("%c ", v);    }}int main(void){    struct graph *g = generate_graph();    struct vexnode *head = malloc_vexnode(8, g);    generate_vexnode(head, g);    // add data here    add_arcnode_by_i_j(head, 'A', 'B', g);    add_arcnode_by_i_j(head, 'B', 'A', g);    add_arcnode_by_i_j(head, 'B', 'C', g);    add_arcnode_by_i_j(head, 'C', 'B', g);    add_arcnode_by_i_j(head, 'A', 'D', g);    add_arcnode_by_i_j(head, 'D', 'A', g);    add_arcnode_by_i_j(head, 'D', 'E', g);    add_arcnode_by_i_j(head, 'E', 'D', g);    add_arcnode_by_i_j(head, 'D', 'F', g);    add_arcnode_by_i_j(head, 'F', 'D', g);    add_arcnode_by_i_j(head, 'E', 'F', g);    add_arcnode_by_i_j(head, 'F', 'E', g);    add_arcnode_by_i_j(head, 'E', 'G', g);    add_arcnode_by_i_j(head, 'G', 'E', g);    add_arcnode_by_i_j(head, 'F', 'G', g);    add_arcnode_by_i_j(head, 'G', 'F', g);    add_arcnode_by_i_j(head, 'F', 'H', g);    add_arcnode_by_i_j(head, 'H', 'F', g);    add_arcnode_by_i_j(head, 'H', 'G', g);    add_arcnode_by_i_j(head, 'G', 'H', g);    struct G *total = NULL;    if ((total = (struct G *)malloc(sizeof(struct G))) == NULL)        return -1;    total->aja_table = head;    total->g = g;    if ((total->array = (struct node *)malloc(g->nodes_number * sizeof(struct node))) == NULL)        return -1;    //initialize    DATA temp = 'A';    for (size_t j = 0; j < total->g->nodes_number; j++) {        total->array[j].name = temp;        temp++;    }    BFS(total, 'A');    print_bfs(total, 'A', 'G');    return 0;}

结果, 输入:A~G

A D F G

问题二:

A和B要坐飞机去某个城市,他们现在在一号城市,目标是五号城市,可是一号城市并没有直达五号的航班,B此时已经收集了很多航班信息,现在A希望找到一种乘坐方式,使得转机的次数最少,如何解决呢?

result2

样例输入:

5 7 1 51 21 32 32 43 43 54 5

第一行四个数字:节点数、边数、起始点、终止点
然后M行,代表连边


分析:
典型的BFS过程,连边之间的代价当成 1 即可。
BFS过程利用队列,逐层遍历玩去之后再往下进行。

实现:

#include <stdio.h>#include <stdlib.h>#define MAX 50#define NIL 0typedef enum {    UNKNOWN,    WHITE,    GRAY,    BLACK}COLOR;struct node {    COLOR color;    int parent;    int distance;};struct node node_accord[MAX + 1];  //记录节点的详细信息int graph[MAX + 1][MAX + 1];   //记录图的信息int book[MAX + 1];            //标记数组int N, M;    // 节点数,边数int start_node, target_node;  //起始点,终止点//模拟队列int QUEUE[MAX + 1];int head = 0;int tail = 0;//标记,是否找到目的地int flag = 0;//从begin点开始void BFS(int begin) {    for (int i = 1; i <= N; i++) {        node_accord[i].color = WHITE;        node_accord[i].parent = NIL;        node_accord[i].distance = 0;    }    node_accord[begin].color = GRAY;    node_accord[begin].parent = NIL;    node_accord[begin].distance = 0;    book[begin] = 1;    //入队    QUEUE[tail++] = begin;    while (head != tail) {        //出队        int temp = QUEUE[head];        head++;        for (int i = 1; i <= N; i++) {            if (graph[temp][i] == 1 && book[i] == 0) {                book[i] = 1;    //book数组标识节点是否遍历过                node_accord[i].parent = temp;                node_accord[i].distance = node_accord[temp].distance + 1;                node_accord[i].color = GRAY;                //入队                QUEUE[tail] = i;                tail++;                if (i == target_node) {                    flag = 1;      //已经找到了目的地,标记                    break;                }            }        }        if (flag == 1) {            break;        }        node_accord[temp].color = BLACK;    }    node_accord[begin].color = BLACK;}//递归打印路径void PRINT_PATH(int start, int end){    if (start == end)        printf(" %d ", start);    else if (node_accord[end].parent == NIL)        printf("no path from %d to %d\n", start, end);    else {        PRINT_PATH(start, node_accord[end].parent);        printf(" %d ", end);    }}int main(){    scanf("%d %d %d %d", &N, &M, &start_node, &target_node);    for (int i = 1; i <= M; i++) {        int x, y;         scanf("%d %d", &x, &y);        graph[x][y] = 1;    }    BFS(1);    if (flag == 1) {        //注意是tail - 1的位置        printf("path distance = %d\n",  node_accord[QUEUE[tail - 1]].distance);        PRINT_PATH(start_node, target_node);    //递归打印路径        printf("\n");    }    else         printf("do not find path..");    return 0;}

结果:
这里写图片描述

再测另外一组数据:

0 0