算法图解-广度优先搜索

本章内容:

  • 学习使用心得数据结构-图,来创建网络模型
  • 学习广度优先搜索,使用图这种算法回答“到X的最短路径是什么的问题”
  • 学习有向图和无向图
  • 学习拓扑排序,这种排序算法指出了节点之间的依赖关系

6.1图简介

  图有节点和边组成。如下图:

6.3广度优先算法

  6.3.1查找最短路径

  广度优先算法回答两类问题:

    1、从节点A出发,有前往节点B的路径么?(在你的朋友中找芒果销售商)

    2、从节点A出发,前往节点B的那条路径最短?(哪个销售商与你的关系最近)

  注意:只有按条件顺序查找时,才能实现这样的目的。否则,查找到的结果并非是最近的。队列(queue)就是这样的数据结构。

  6.3.2队列

    队列只支持两种操作:入队和出队。

6.4实现图

graph = {}
graph["you"] = ["alice", "bob", "claire"]
graph["bob"] = ["anuj", "peggy"]
graph["alice"] = ["peggy"]
graph["claire"] = ["thom", "jonny"]
graph["anuj"] = []
graph["peggy"] = []
graph["thom"] = []
graph["jonny"] = []

有向图(directed graph):有箭头,关系是单向的

无向图(undirected graph):无箭头,直接相连的节点互为邻居

如下二图是等价的:

6.5实现算法

from collections import deque

def person_is_seller(name):
      return name[-1] == 'm' #last element is m.

graph = {}
graph["you"] = ["alice", "bob", "claire"]
graph["bob"] = ["anuj", "peggy"]
graph["alice"] = ["peggy"]
graph["claire"] = ["thom", "jonny"]
graph["anuj"] = []
graph["peggy"] = []
graph["thom"] = []
graph["jonny"] = []

def search(name):
    search_queue = deque()
    search_queue += graph[name]
    # This array is how you keep track of which people you've searched before.
    searched = []
    while search_queue:
        person = search_queue.popleft()
        print person
        # Only search this person if you haven't already searched them.
        if not person in searched:
            if person_is_seller(person):
                print person + " is a mango seller!"
                return True
            else:
                search_queue += graph[person]
                # Marks this person as searched
                searched.append(person)
        else:
            print "skip repeatedly " + person #peggy is repeatedly
    return False

search("you")
search(name)

  运行时间:广度优先搜索的运行时间为O(V+E),其中V为定点(vertice)数,E为边数。

  另,树是一种特殊的图,其边是单向的。

6.6小结

  • 广度优先算法之处是否有从A到B的路径
  • 如果有,广度优先搜索将找出最短路径
  • 如果面临类似于寻找最短路径的问题,可尝试用图来创建模型,再用广度优先搜索来解决问题
  • 有向图的边为箭头,箭头的方向制定了关系的方向,如,你->我,代表你欠我钱。
  • 无向图的边有箭头,关系是双向的,如你和我约会,等同我和你约会
  • 队列是FIFO
  • 栈是LIFO
  • 搜索列表必须是有序的,否则找不到最短路径
  • 检查过的,务必不要再检查,否则可能导致无限循环

广度优先算法的C语言实现:

https://blog.csdn.net/code_mlym/article/details/52435044

  1 /*************************************************************************
  2     > File Name: graph.c
  3     > Author: jeff zhu
  4     > Mail: 908190355@qq.com 
  5     > Created Time: 2016年09月04日 星期日 18时58分08秒
  6  ************************************************************************/
  7 
  8 #include <stdio.h>
  9 #include <malloc.h>
 10 #include <limits.h>
 11 
 12 #define MAXSIZE 100    //图的最大的顶点数量为100,
 13 #define WHITE 1            //定义每个顶点的可能的颜色,白色代表没有探索,灰色代表已经探索,但是其邻接的定点没有完全探索,黑的代表其所有邻接的顶点都探索过。
 14 #define GRAY 2
 15 #define BLACK 3
 16 #define INFINITE INT_MAX    //定义一个正无穷
 17 
 18 typedef struct gra_ptr_node {    //这个地方我定义了一个数据结构,这个数据结构用来实现链表(这个链表的节点的关键字是指针,所以节省了空间,在效率上也没有损耗)
 19     struct gra_ptr_node *next;
 20     struct graph_node *ptr;
 21 }PTR_NODE;
 22 typedef struct graph_node {     //图的每个顶点,parent用来实现广度优先搜索树,nect用来实现来链表,key代表编号,d是距离搜索顶点的距离,f是为了深度优先搜索而准备<span style="white-space:pre">                            </span>//
 23     struct graph_node *parent;
 24     struct graph_node *next;
 25     int key;
 26     int d;
 27     int f;
 28     int color;
 29 }NODE;
 30 typedef struct graph {      //图的数据结构,length代表adj实际的长度
 31     PTR_NODE *adj[MAXSIZE];
 32     int length;
 33 }GRAPH;
 34 typedef struct stack_of_granode {    //在BFS中会用到的栈
 35     NODE *arr_temp[MAXSIZE];
 36     int start;
 37     int end;
 38     int length;
 39     int count;
 40 }NODEQUEUE;
 41 
 42 NODE *arr_V[MAXSIZE];  //这个数组保存所有的顶点,可以直接在这个数组中找到想要的定点
 43 void BFS (GRAPH *G , NODE *s) ;
 44 
 45 void init_graph (GRAPH *G , int len) ;
 46 NODE *create_and_init_node (int key) ;
 47 void insert_to_adj (GRAPH *G , NODE *x , int key) ;
 48 void init_queue (NODEQUEUE *Q , GRAPH *G) ;
 49 void enqueue (NODEQUEUE *Q , NODE *x) ;
 50 NODE *dequeue (NODEQUEUE *Q) ;
 51 void BFS (GRAPH *G , NODE *s) ;
 52 
 53 int main () {
 54     int i;
 55     int count = 0;
 56     GRAPH G;
 57     NODE *temp;
 58     NODE *temp1;
 59     PTR_NODE *ptr_temp;
 60     NODEQUEUE Q;  //一下两行代码放到BFS函数中更好,写注释的时候发现的懒得改了
 61     init_queue (&Q , &G);
 62 
 63     init_graph (&G , 8);   //初始化图
 64 
 65     temp = create_and_init_node (0);  //一下代码都是初始化一个图,图的逻辑结构见《算法导论》图22-3
 66     arr_V[count++] = temp;
 67     insert_to_adj (&G , temp , 1);
 68 
 69     temp = create_and_init_node (1);
 70     arr_V[count++] = temp;
 71     insert_to_adj (&G , temp , 0);
 72     insert_to_adj (&G , temp , 2);
 73 
 74     temp = create_and_init_node (2);
 75     temp1 = temp;
 76     arr_V[count++] = temp;
 77     insert_to_adj (&G , temp , 1);
 78     insert_to_adj (&G , temp , 3);
 79 
 80     temp = create_and_init_node (3);
 81     arr_V[count++] = temp;
 82     insert_to_adj (&G , temp , 2);
 83     insert_to_adj (&G , temp , 4);
 84     insert_to_adj (&G , temp , 5);
 85 
 86     temp = create_and_init_node (4);
 87     arr_V[count++] = temp;
 88     insert_to_adj (&G , temp , 3);
 89     insert_to_adj (&G , temp , 5);
 90     insert_to_adj (&G , temp , 6);
 91     insert_to_adj (&G , temp , 7);
 92 
 93     temp = create_and_init_node (5);
 94     arr_V[count++] = temp;
 95     insert_to_adj (&G , temp , 3);
 96     insert_to_adj (&G , temp , 4);
 97     insert_to_adj (&G , temp , 6);
 98 
 99     temp = create_and_init_node (6);
100     arr_V[count++] = temp;
101     insert_to_adj (&G , temp , 5);
102     insert_to_adj (&G , temp , 4);
103     insert_to_adj (&G , temp , 7);
104 
105     temp = create_and_init_node (7);
106     arr_V[count++] = temp;
107     insert_to_adj (&G , temp , 6);
108     insert_to_adj (&G , temp , 4);  //图初始化结束
109 
110     for (i = 0 ; i < 8 ; i++) {
111         ptr_temp = G.adj[i];
112         while (ptr_temp != NULL) {
113             printf ("%d " , ptr_temp->ptr->key);
114             ptr_temp = ptr_temp->next;
115         }
116         printf ("
");
117     }                           //打印一下结果,看看是否和逻辑图是一样的
118     printf ("

");
119     BFS (&G , temp1);   //执行BFS算法
120 
121     for (i = 0 ; i < count ; i++) {
122         printf ("%d  " , arr_V[i]->d);
123     }
124     printf ("
");
125 }
126 
127 void init_graph (GRAPH *G , int len) {  //初始化图的函数
128     int i;
129 
130     G->length = len;
131     for (i = 0 ; i < len ; i++) {
132         G->adj[i] = NULL;
133     }
134 }
135 
136 NODE *create_and_init_node (int key) {   //创建和初始化图
137     NODE *temp = (NODE *) malloc (sizeof (NODE));
138 
139     temp->key = key;
140     temp->d = 0;
141     temp->f = 0;
142     temp->next = NULL;
143     temp->parent = NULL;
144 
145     return temp;
146 }
147 
148 void insert_to_adj (GRAPH *G , NODE *x , int key) {  //这个函数从图论的角度来说是将边加入到一个关于顶点的便于搜索数组。
149     if (key >= G->length) {
150         printf ("the key is too big");
151         return;
152     }
153 
154     PTR_NODE *temp;
155     temp = (PTR_NODE *) malloc (sizeof (PTR_NODE));
156     temp->next = G->adj[key];
157     temp->ptr = x;
158     G->adj[key] = temp;
159 }
160 
161 void init_queue (NODEQUEUE *Q , GRAPH *G) {  //初始化BFS算法要用到的队列Q。
162     Q->start = 0;
163     Q->end = 0;
164     Q->length = G->length;
165     Q->count = 0;
166 }
167 void enqueue (NODEQUEUE *Q , NODE *x) {  //将节点x加入到队列Q中。同时,Q->count加1。
168     if (((Q->end+1) % Q->length) == Q->start)
169         return;
170     else {
171         Q->arr_temp[Q->end] = x;
172         Q->end++;
173         if (Q->end == Q->length)
174             Q->end = 0;
175         Q->count++;
176     }
177 }
178 
179 NODE *dequeue (NODEQUEUE *Q) {   //返回Q->arr[Q->start],同时将这个数组给删除。同时,Q->count减1。
180     NODE *temp;
181     if (Q->start == Q->end)
182         return;
183     else {
184         temp = Q->arr_temp[Q->start];
185         Q->start++;
186         if (Q->start == Q->length)
187             Q->start = 0;
188         Q->count--;
189     }
190     return temp;
191 }
192 
193 void BFS (GRAPH *G , NODE *s) {   //终于到了广度优先算法了,这个算法的核心思想就是,先探索和当前节点邻接的顶点,然后再探索别的定点
194     int i;
195     NODEQUEUE Q;
196     init_queue (&Q , G);
197     NODE *temp;
198     PTR_NODE *ptr_temp;
199 
200     for (i = 0 ; i < G->length ; i++) {
201         if (arr_V[i] != s) {
202             arr_V[i]->color = WHITE;
203             arr_V[i]->d = INFINITE;
204             arr_V[i]->parent = NULL;
205         }
206     }
207     s->color = GRAY;
208     s->parent = NULL;
209     s->d = 0;
210     enqueue (&Q , s);
211 
212     while (Q.count != 0) {
213         temp = dequeue (&Q);
214         ptr_temp = G->adj[temp->key];
215         while (ptr_temp != NULL) {
216             if (ptr_temp->ptr->color == WHITE) {
217                 ptr_temp->ptr->color = GRAY;
218                 ptr_temp->ptr->d = temp->d + 1;
219                 ptr_temp->ptr->parent = temp;
220                 enqueue (&Q , ptr_temp->ptr);
221             }
222             ptr_temp = ptr_temp->next;
223         }
224         temp->color = BLACK;
225     }
226 }
View Code
原文地址:https://www.cnblogs.com/mofei004/p/8889155.html