C++实现广度优先搜索实例_C 语言

本文主要叙述了图的遍历算法中的广度优先搜索(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最小生成树算法。

以上是小编为您精心准备的的内容,在的博客、问答、公众号、人物、课程等栏目也有的相关内容,欢迎继续使用右上角搜索按钮进行搜索c++
, 搜索
, 优先
广度
广度优先搜索c语言、广度优先搜索、广度优先搜索算法、广度优先搜索 实现、图的广度优先搜索,以便于您获取更多的相关知识。

时间: 2024-10-22 21:01:42

C++实现广度优先搜索实例_C 语言的相关文章

C语言实现图的遍历之深度优先搜索实例_C 语言

DFS(Depth-First-Search)深度优先搜索算法是图的遍历算法中非常常见的一类算法.分享给大家供大家参考.具体方法如下: #include <iostream> #include <algorithm> #include <iterator> using namespace std; #define MAX_VERTEX_NUM 10 struct Node { int adjvex; struct Node *next; int info; }; typ

C++归并算法实例_C 语言

本文实例讲述了C++归并算法.分享给大家供大家参考.具体如下: /* 归并算法:把两个或两个以上的线性表合并在一起,形成一个新的线性表 函数模版的基本使用 程序意图:将两个相同类型的线性表元素排好序,然后将他们组合成一个排好的线性表 */ #include <iostream> using namespace std; const int n = 5; //5个元素 //输出数据元素 template <class T1> void OutPut(T1 out[(2*n)]) {

C++实现汉诺塔算法经典实例_C 语言

本文所述为汉诺塔算法的C++代码的经典实现方法. 汉诺塔问题描述:3个柱为a.b.c,圆盘最初在a柱,借助b柱移到c柱.需要你指定圆盘数. 具体实现代码如下: #include <iostream> using namespace std; int times = 0; //全局变量,搬动次数 //第n个圆盘从x柱搬到z柱 void move(int n, char x, char z) { cout << "第" << ++times <&l

C语言输出旋转后数组中的最小数元素的算法原理与实例_C 语言

  问题描述:把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转.输入一个排好序的数组的一个旋转,输出旋转数组的最小元素.例如数组{3, 4, 5, 1, 2}为{1, 2, 3, 4, 5}的一个旋转,该数组的最小值为1.      思路:这道题最直观的解法并不难.从头到尾遍历数组一次,就能找出最小的元素,时间复杂度显然是O(n).但这个思路没有利用输入数组的特性.既然有时间复杂度更小的算法,我们容易想到二分查找,因为它的时间复杂度为O(logn).这个问题是否可以运用二分查找呢

C基础 mariadb处理的简单实例_C 语言

引言 MariaDB 是一款灰常不错开源数据库. 这里直接用它来解决业务问题. 业务需求: 现在数据库中表示按照天分表的. 突然我们需要按照月来处理数据. 例如输入一个玩家id, 查找这个玩家这个月内看了一件事几次. 我们先搭建一个环境. 操作系统: Linux version 4.4.0-22-generic (buildd@lgw01-41) (gcc version 5.3.1 20160413 (Ubuntu 5.3.1-14ubuntu2) ) #40-Ubuntu SMP Thu M

对C语言中指针的理解与其基础使用实例_C 语言

C语言的指针,关键意思在于"指". "指"是什么意思? 其实完全可以理解为指示的意思.比如,有一个物体,我们称之为A.正是这个物体,有了这么个称谓,我们才能够进行脱离这个物体的实体而进行一系列的交流.将一个物体的指示,是对这个物体的抽象.有了这种抽象能力,才有所谓的智慧和文明.所以这就是"指示"这种抽象方法的威力. 退化到C语言的指针,指针是一段数据/指令(在冯诺易曼体系中,二者是相通,在同一空间中的)的指示.这是指示,也就是这段数据/指令的起始

C++动态数组类的封装实例_C 语言

C++中的动态数组(Dynamic Array)是指动态分配的.可以根据需求动态增长占用内存的数组.为了实现一个动态数组类的封装,我们需要考虑几个问题:new/delete的使用.内存分配策略.类的四大函数(构造函数.拷贝构造函数.拷贝赋值运算符.析构函数).运算符的重载.涉及到的知识点很多,对此本文只做简单的介绍. 一.内存分配策略 当用new为一个动态数组申请一块内存时,数组中的元素是连续存储的,例如 vector和string.当向一个动态数组添加元素时,如果没有空间容纳新元素,不可能简单

重构-C++实现矩阵的简单实例_C 语言

重构-C++实现矩阵的简单实例 #include <iostream> #include <cmath> using namespace std; double cofactor(double* detPtr,int rank,int t); //代数余子式 double valDet( double *detPtr, int rank); //行列式 template <class T> void exchange(T& t1,T& t2){T tem

C语言实现去除字符串中空格的简单实例_C 语言

在网上看了些去除空格的代码,觉得都不是很简洁,就自己写代码实现它本着高效率,不使用额外存储空间的想法实现该功能去除空格一共有三种: 1.去除全部空格: 2.一种是去除左边空格: 3.去除右边空格  想去除左右两边空格,只要先去除左边再去除右边的就行了 以下是实现代码: /*去除字符串中所有空格*/ voidVS_StrTrim(char*pStr) { char *pTmp = pStr; while (*pStr != '/0') { if (*pStr != ' ') { *pTmp++ =