BFS是广度优先搜索算法,是图的一种搜索算法,当然也可以用作其它的地方。BFS是一种盲目搜索算法,其目的是系统的展开并检查途中的节点。而且如果图是非加权的(各个边的长度相等),那么它找到的第一个解是距离根节点的边数目一定最少。但如果图是加权图,那么边数目最少并不代表路径最少,因此找到的解不一定是最佳解。而且BFS的空间复杂度是:O(V+E);时间复杂度是O(V+E);其中V是图中的节点数,V是图的边数。可以参考维基百科中所说的http://zh.wikipedia.org/wiki/广度(宽度)优先搜索法
在讨论BFS的具体实现之前,我们先回顾下图的表示方式:
1).邻接矩阵的表示方法
A. 用邻接矩阵表示顶点间的关系(即边)
B.用一个顺序表来存储顶点信息
对于加权图和非加权图,它们的邻接矩阵的表示方式不一样;对于非加权图,如果两顶点之间相连,那么它们对应的邻接矩阵的边的值为1,否则为0;而加权图的邻接矩阵中的元素的值是两顶点之间边的权值,如果不相连,则为0或 。
具两个例子来表示
对于非加权图的无向图和有向图:
对于加权图:
上面的两张图是copy别人的,对于邻接矩阵的建立就比较简单了,下面我们给出相应的代码:
#define MaxVertexNum 100 // 最大顶点个数,可以按需求定义
typedef char VertexType; // 顶点类型
typedef int EdgeType; //边的类型
typedef struct {
VertexType vertex[MaxVertexNum]; //保存顶点的有序表
EdgeType edge[MaxVertexNum][MaxVertexNum];//邻接矩阵(二维的)
int e;// 边的数量
int v;// 顶点的数量
}Mgraph;
void create_mgraph( Mgraph *G)
{
int i,j,k,w;
//memset(G, 0, sizeof(Mgraph));
scanf(“%d%d”, &G->v, &G->e);// 输入顶点数和边数
while(i =0; i < G->v; i++)
G->vertex[i] = getchar(); //输入顶点的信息
for(j =0; j < G->v; j++)
for(k =0; k< G->v; k++)
G->edge[j][k] = 0;
for( k =0; k<G->e; k++)
{
scanf(“%d%d%d”, &i,&j,&w);
G->edge[i][j] = w;
G->edge[j][i] = w; // 对于无向图i->j 和j->i的边是一样的
} // end for
} // create_mgraph
2).邻接表表示
邻接表是由两部分组成的,一是图中所有顶点的数组,而是与每个顶点相连的所有顶点组成的链表。
邻接表的形式说明及其建表算法
typedef char VertexType; // 顶点类型
typedef int ArrType; //顶点在数组中位置的类型
typedef struct node{//边表结点
int adjvex; //邻接点域
struct node *next; //链域
//若要表示边上的权,则应增加一个数据域
}EdgeNode;
typedef struct vnode{ //顶点表结点
VertexType vertex; //顶点域
ArrType num; //顶点在数组(顺序表)中的位置
EdgeNode *firstedge;//边表头指针
}VertexNode;
typedef VertexNode AdjList[MaxVertexNum];//AdjList是邻接表类型
typedef struct{
AdjList adjlist;//邻接表
int v,e; 图中当前顶点数和边数
}ALGraph; //对于简单的应用,无须定义此类型,可直接使用AdjList类型。
void create_ALGraph(ALGraph * G)
{
int i, j,k;
EdgeNode *temp = NULL;
scanf("%d%d", &G->v,&G->e);
char d;
while((d = getchar())!='\n'&&d != EOF);// 清空输入流
for(i =0; i < G->v; i++) // 输入顶点信息
{
G->adjlist[i].vertex = getchar();
G->adjlist[i].vertnum = i;
G->adjlist[i].firstedge = NULL;
}//end for
for(k = 0; k < G->e; k++) //输入邻接链表信息
{
scanf("%d%d",&i,&j);// 输入边的两个顶点
temp = (EdgeNode*)malloc(sizeof(EdgeNode));
temp->adjnum = j;
temp->next = G->adjlist[i].firstedge;//从头部插入节点
G->adjlist[i].firstedge = temp;//建立i个顶点与j个顶点的链表关系
temp = (EdgeNode*)malloc(sizeof(EdgeNode));
temp->adjnum = i;
temp->next = G->adjlist[j].firstedge;//从头部插入节点
G->adjlist[j].firstedge = temp;//建立i个顶点与j个顶点的链表关系
}//end for
}//end createALGraph
注意上面的红色部分的标注,它的意思是指邻接表中的链表采用的是头部插入的方式插入节点的。因为邻接表中的链表的节点顺序可以是任意的,因此采取头部或尾部插入节点是一样的。而且头部插入方法无需查找链表尾,效率较高。
BFS算法的分析:
BFS首先从根节点进行访问,并且记录根节点的所有邻居节点(可以用队列实现),然后再一次对其每个邻居节点进行重复的处理。对已经访问的节点进行标识,标识的方法也可以采用队列的方式或者其他的方式。
BFS的非递归C++的实现:
BFS
是为了遍历访问所有的顶点,因此用邻接表表示图更适合做
BFS
。下面就其
C++
的实现进行分析;(和上面的无向图的邻接表建立方法对应)
void visit(VertexNode *cur)
{
if(cur == NULL)
return;
cout<<cur->vertex<<endl;
}
void BFS(ALGraph *G)
{
if(NULL == G)
return;
int visited[MaxVertexNum];
memset(visited, 0, MaxVertexNum*sizeof(int));
queue<VertexNode*> unvisited;
unvisited.push(&G->adjlist[0]);
while(!unvisited.empty())
{
VertexNode *cur = unvisited.front();
if(visited[cur->vertnum] == 0)
{
visit(cur);
visited[cur->vertnum] = 1;
}//end if
EdgeNode *temp = cur->firstedge;
while(temp != NULL)
{
if(visited[temp->adjnum]== 0)
unvisited.push(&G->adjlist[temp->adjnum]);
temp = temp->next;
}//end while
unvisited.pop();
}//end while
return;
}//end BFS
int main()
{
ALGraph G;
cout<<sizeof(G)<<endl;
create_ALGraph(&G);
BFS(&G);
return 0;
}
最终的运行结果: