本文主要叙述了图的遍历算法中的广度优先搜索(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<iostream> #include<list> using namespace std; /* 邻接表存储有向图 */ class Graph { int V; // 顶点的数量 list<int> *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<int>[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<int> queue; // 将当前顶点标记为已访问并压入队列 visited[v] = true; queue.push_back(v); list<int>::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<V; ++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<G.vexnum; ++i) visited[i] = false; // 初始化 for(i=0; i<G.vexnum; ++i) // 从0号顶点开始遍历 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<V; ++i) visited[i] = false; // 对每个连通分量调用一次BFSUtil(),从0号顶点开始遍历 for(int i=0; i<V; ++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最小生成树算法。
通过构建图,我们现在可以将注意力转向我们将使用的算法来找到字梯问题的最短解。我们将使用的图算法称为“宽度优先搜索”算法。宽度优先搜索(BFS)是用于搜索图的最简单的算法之一。它也作为几个其他重要的图算法的原型,我们将在以后研究。 给定图 G 和起始顶点 s,广度优先搜索通过探索图中的边以找到 G 中的所有顶点,其中存在从 s 开始的路径。通过广度优先搜索,它找到和 s 相距 k 的所有顶点,然后找
主要内容:深度优先搜索(简称“深搜”或DFS),广度优先搜索,总结前边介绍了有关图的 4 种存储方式,本节介绍如何对存储的图中的顶点进行遍历。常用的遍历方式有两种: 深度优先搜索和 广度优先搜索。 深度优先搜索(简称“深搜”或DFS) 图 1 无向图 深度优先搜索的过程类似于树的先序遍历,首先从例子中体会深度优先搜索。例如图 1 是一个无向图,采用深度优先算法遍历这个图的过程为: 首先任意找一个未被遍历过的顶点,例如从 V1 开始,由于 V1 率先访问过了,所以
本文向大家介绍python实现广度优先搜索过程解析,包括了python实现广度优先搜索过程解析的使用技巧和注意事项,需要的朋友参考一下 广度优先搜索 适用范围: 无权重的图,与深度优先搜索相比,深度优先搜索法占内存少但速度较慢,广度优先搜索算法占内存多但速度较快 复杂度: 时间复杂度为O(V+E),V为顶点数,E为边数 思路 广度优先搜索是以层为顺序,将某一层上的所有节点都搜索到了之后才向下一层搜
我正在研究BFS算法,我有一个关于如何将相邻节点插入队列的问题。 例如,假设我们正在处理一个无向图,我们希望执行BFS来输出图的内容,那么我们如何知道在从队列中取出一个初始节点后,相邻节点以什么顺序插入到队列中呢?还有,有没有办法修改邻居节点插入队列的方式? 任何帮助都将不胜感激,谢谢。
本文向大家介绍C++深度优先搜索的实现方法,包括了C++深度优先搜索的实现方法的使用技巧和注意事项,需要的朋友参考一下 本文实例讲述了图的遍历中深度优先搜索的C++实现方法,是一种非常重要的算法,具体实现方法如下: 首先,图的遍历是指从图中的某一个顶点出发,按照某种搜索方法沿着图中的边对图中的所有顶点访问一次且仅访问一次。注意到树是一种特殊的图,所以树的遍历实际上也可以看作是一种特殊的图的遍历。图
本文向大家介绍什么是广度优先搜索?相关面试题,主要包含被问及什么是广度优先搜索?时的应答技巧和注意事项,需要的朋友参考一下 类似树的按层遍历,其过程为:首先访问初始点Vi,并将其标记为已访问过,接着访问Vi的所有未被访问过可到达的邻接点Vi1、Vi2……Vit,并均标记为已访问过,然后再按照Vi1、Vi2……Vit的次序,访问每一个顶点的所有未被访问过的邻接点,并均标记为已访问过,依此类推,直到图