本文主要叙述了图的遍历算法中的广度优先搜索(Breadth-First-Search)算法,是非常经典的算法,可供C++程序员参考借鉴之用。具体如下:

首先,图的遍历是指从图中的某一个顶点出发,按照某种搜索方法沿着图中的边对图中的所有顶点访问一次且仅访问一次。注意到树是一种特殊的图,所以树的遍历实际上也可以看作是一种特殊的图的遍历。图的遍历主要有两种算法:广度优先搜索(Breadth-First-Search)和深度优先搜索(Depth-First-Search)。

一、广度优先搜索(BFS)的算法思想

广度优先搜索类似于二叉树的层序遍历,它的基本思想就是:首先访问起始顶点v,接着由v出发,依次访问v的各个未访问过的邻接顶点w1,w2,…,wi,然后再依次访问w1,w2,…,wi的所有未被访问过的邻接顶点;再从这些访问过的顶点出发,再访问它们所有未被访问过的邻接顶点……依次类推,直到图中所有顶点都被访问过为止。

广度优先搜索是一种分层的查找过程,每向前走一步可能访问一批顶点,不像深度优先搜索那样有往回退的情况,因此它不是一个递归的算法。为了实现逐层的访问,算法必须借助一个辅助队列,以记录正在访问的顶点的下一层顶点。

如上图所示,为一个有向图,从顶点2开始广度优先遍历整个图,可知结果为2,0,3,1。

二、BFS算法实现

与树相比,图的不同之处在于它存在回路/环,因此在遍历时一个顶点可能被访问多次。为了防止这种情况出现,我们使用一个访问标记数组visited[]来标记顶点是否已经被访问过。

在广度优先搜索一个图之前,我们首先要构造一个图,图的存储方式主要有两种:邻接矩阵、邻接表。这里我们使用邻接表来存储图:

简单起见,我们先假设从起始顶点可以达到其他所有顶点。以有向图为例,C++代码实现:

/*************************************************************************

> File Name: BFS.cpp

> Author: SongLee

************************************************************************/

#include

#include

using namespace std;

/* 邻接表存储有向图 */

class Graph

{

int V; // 顶点的数量

list *adj; // 邻接表

void BFSUtil(int v, bool visited[]);

public:

Graph(int V); // 构造函数

void addEdge(int v, int w); // 向图中添加一条边

void BFS(int v); // BFS遍历

};

/***** 构造函数 *****/

Graph::Graph(int V)

{

this->V = V;

adj = new list[V]; // 初始化V条链表

}

/* 添加边,构造邻接表 */

void Graph::addEdge(int v, int w)

{

adj[v].push_back(w); // 将w加到v的list

}

/* 从顶点v出发广度优先搜索 */

void Graph::BFSUtil(int v, bool visited[])

{

// BFS辅助队列

list queue;

// 将当前顶点标记为已访问并压入队列

visited[v] = true;

queue.push_back(v);

list::iterator i;

while(!queue.empty())

{

// 出队

v = queue.front();

cout << v << " ";

queue.pop_front();

// 检测已出队的顶点s的所有邻接顶点

// 若存在尚未访问的邻接点,访问它并压入队列

for(i = adj[v].begin(); i!=adj[v].end(); ++i)

{

if(!visited[*i])

{

visited[*i] = true;

queue.push_back(*i);

}

}

}

}

/** 广度优先搜索 **/

void Graph::BFS(int v)

{

// 初始化访问标记数组

bool *visited = new bool[V];

for(int i=0; i

visited[i] = false;

// 假设从给定顶点可以到达图的所有顶点

BFSUtil(v, visited);

}

/* 测试 */

int main()

{

// 创建图

Graph g(4);

g.addEdge(0, 1);

g.addEdge(0, 2);

g.addEdge(1, 2);

g.addEdge(2, 0);

g.addEdge(2, 3);

g.addEdge(3, 3);

cout << "Following is BFS Traversal (starting from vertex 2) \n";

g.BFS(2);

cout << endl;

return 0;

}

上面是假设从起始顶点开始能够访问到图的所有顶点。如果不能到达所有顶点,即存在多个连通分量呢?那么我们就要对每个连通分量都进行一次广度优先搜索。

伪代码如下:

bool visited[MAX_VERTEXT_NUM]; // 访问标记数组

void BFS(Graph G) // 设访问函数为visit()

{

for(i=0; i

visited[i] = false; // 初始化

for(i=0; i

if(!visited[i]) // 对每个连通分量调用一次BFS

BFS(G,i); // Vi未访问过,从Vi开始BFS

}

void BFSUtil(Graph G, int v)

{

visit(v); // 访问初始顶点

visited[v] = true; // v已访问

Enqueue(Q, v); // 顶点v入队列

while(!isEmpty(Q))

{

Dequeue(Q, v); // 顶点v出队列

for(w=FirstNeighbor(G,v); w>=0; w=NextNeighbor(G,v))

if(!visited[w]) // 检测v的所有邻接点

{

visit(w); // 若w未访问,访问之

visited[w]=true; // 标记

Enqueue(Q, w); // 顶点w入队列

}

}

}

根据伪代码,相信不难写出对于多个连通分量的图的广度优先搜索。我们只需要修改BFS()函数部分:

void Graph::BFS()

{

// 初始化访问标记数组

bool *visited = new bool[V];

for(int i=0; i

visited[i] = false;

// 对每个连通分量调用一次BFSUtil(),从0号顶点开始遍历

for(int i=0; i

if(!visited[i])

BFSUtil(i, visited);

}

对于无向图的广度优先搜索,只是邻接表不一样,其他的都是一样的。我们只需要修改addEdge(v, w)函数:

void Graph::addEdge(int v, int w)

{

adj[v].push_back(w); // 将w加到v的list

adj[w].push_back(v);

}

三、BFS算法性能分析

1 . 空间复杂度

无论是邻接表还是邻接矩阵的存储方式,BFS算法都需要借助一个辅助队列Q,n个顶点都需要入队一次,在最坏的情况下,空间复杂度为O(|V|)。

2 . 时间复杂度

当采用邻接表存储时,每个顶点均需搜索一次,故时间复杂度为O(|V|),在搜索任一顶点的邻接点时,每条边至少访问一次,故时间复杂度为O(|E|),算法总的时间复杂度为O(|V|+|E|)。

当采用邻接矩阵存储时,查找每个顶点的邻接点所需的时间为O(|V|),故算法总的时间复杂度为O(|V|^2)。

注:广度优先搜索(BFS)算法思想有很多应用,比如Dijkstra单源最短路径算法和Prim最小生成树算法。

广度优先搜索递归C语言代码,C++实现广度优先搜索实例相关推荐

  1. 汉诺塔递归 C语言 代码简洁

    #include<stdio.h> void hannuota(int n,char A,char B,char C) {if(1==n) printf("将编号为%d的盘子从% ...

  2. C语言过河问题主函数,c,c++_C语言踩石头过河问题,用DFS搜索递归了17万次但是没报错,请问是什么原因?,c,c++,算法 - phpStudy...

    C语言踩石头过河问题,用DFS搜索递归了17万次但是没报错,请问是什么原因? 这是原题目,后面附上我的代码,刚刚接触DFS,不是很熟练,求教育--谢谢!!!TUT 这是题目,我大概概括一下 用'※'和 ...

  3. 匈牙利算法c语言代码,漫谈匈牙利算法

    匈牙利算法是由匈牙利数学家Edmonds于1965年提出,因而得名.匈牙利算法是基于Hall定理中充分性证明的思想,它是部图匹配最常见的算法,该算法的核心就是寻找增广路径,它是一种用增广路径求二分图最 ...

  4. 利用计算机语言实现ID3算法,机器学习之决策树学习-id3算法-原理分析及c语言代码实现.pdf...

    机器学习之决策树学习-id3算法-原理分析及c语言代码实现.pdf 还剩 23页未读, 继续阅读 下载文档到电脑,马上远离加班熬夜! 亲,很抱歉,此页已超出免费预览范围啦! 如果喜欢就下载吧,价低环保 ...

  5. 无向图、深度优先搜索(无向图)、广度优先搜索(无向图)、无向图路径查找(基于深度优先搜索)

    一.无向图 1.1 图的相关术语 相邻顶点: 当两个顶点通过一条边相连时,我们称这两个顶点是相邻的,并且称这条边依附于这两个顶点. 度: 某个顶点的度就是依附于该顶点的边的个数. 子图: 是一幅图的所 ...

  6. 数据结构与算法|马踏棋盘算法(小甲鱼)C语言代码的算法分析

    马踏棋盘算法(骑士周游问题)的算法分析 C语言代码部分来自小甲鱼的<数据结构与算法> 文章目录 马踏棋盘算法(骑士周游问题)的算法分析 一.C语言代码实现 二.代码分析与算法思路 题目要求 ...

  7. 图的深度搜索c语言,求图的深度优先搜索!该怎么处理

    当前位置:我的异常网» C语言 » 求图的深度优先搜索!该怎么处理 求图的深度优先搜索!该怎么处理 www.myexceptions.net  网友分享于:2013-03-16  浏览:12次 求图的 ...

  8. 嵌入式C语言代码规范

    C语言代码规范 参考安富莱C语言编码规范 1.文件与目录 1.文件及目录的命名规定可用的字符集是[A-Z:a-z:0-9:._-]. 2.源文件名后缀用小写字母 .c 和.h. 3.文件的命名要准确清 ...

  9. java 与c 运行效率_Java语言与C语言代码运行效率的比较

    <Java语言与C语言代码运行效率的比较>由会员分享,可在线阅读,更多相关<Java语言与C语言代码运行效率的比较(2页珍藏版)>请在人人文库网上搜索. 1.Java语言与C语 ...

最新文章

  1. 设计模式笔记——Adapter
  2. 属性与内存管理(属性与内存管理都是相互关联的)
  3. android连接usb外设通讯_iOS App连接外设的几种方式
  4. 机器学习算法基础概念学习总结
  5. 《数学之美》—Google AK-47的设计者
  6. java spring log4j_配置spring的log4j日志记录
  7. java字典表_SpringBoot中实现数据字典的示例代码
  8. mw150um 驱动程序win10_win10系统版水星mw150us无线网卡驱动
  9. Overleaf 硕士毕业论文参考文献格式设置
  10. 大数据相关面试题整理-带答案
  11. 嫁给通信旺的16条理由!!!
  12. python两个自定义函数_Python自定义函数实现求两个数最大公约数、最小公倍数示例...
  13. B站后台源代码泄露,官方回应声明黑话指南
  14. 校园生存手册之Linux下锐捷认证
  15. Android中View的工作流程之measure过程
  16. Java版打飞机小游戏
  17. 适合教孩子编码的 7 款免费编程语言
  18. python中序列和列表区别细菌真菌病毒_根据16S预测微生物群落功能最全攻略
  19. css 按钮hover有光影效果
  20. 评价CommonPhotoStar(动感相册)软件的特色(真是太棒了)

热门文章

  1. 组合数学(1)——二分图
  2. 聊一聊 NVMe SSD 存储介质
  3. 刚毕业被骗去了小公司,天天“取数做表”,后悔没早点用上这工具
  4. 打官司无非是为争取民企与国企平等保护、维护契约精神之战。
  5. 数据结构和算法(DSA)的本质及其关系:从哲学辩证法和数学函数角度的分析
  6. WebGL坐标系及基础几何概念
  7. 创建双跑楼梯(Revit二次开发)
  8. 艺术名人对冰雪画家冯庆的评语
  9. 项目经理和项目成员招募
  10. 微信新功能内测曝光,再次向支付宝发起挑战