两个经典的算法:图的深度优先搜索(DFS)、广度优先搜索(BFS)、和判断有向(或无向)图的连通分量个数并输出

本文介绍了图的深度优先搜索(DFS)和广度优先搜索(BFS)算法,通过走迷宫和水纹扩散的比喻解释了两种搜索方式,并给出了C++代码实现。同时,探讨了如何利用DFS判断有向或无向图的连通分量个数,并通过示例展示了遍历过程和理解递归的关键点。
摘要由CSDN通过智能技术生成

什么是深度优先搜索呢?
就是从一个切入点 切入,然后遇到符合条件的就进入递归,好比一个人走迷宫,找到路可以走就一直深入往下走,走啊走,走到死路的时候就回退到刚才的岔路口,走另外一条路,这样,人终会走出迷宫(不过走迷宫可以不走完,而这个图的遍历是要走完全部的)。
图的深度优先搜索的方法和过程举例:
比如说是下面这个图,如果从A开始出发的话

在这里插入图片描述
那么过程就如图所示:A ->D ->F ->H ->I ->C ->B ->E ->G
在这里插入图片描述

为什么会这样遍历呢? 这就和刚才的走迷宫一样,比如说,人从A 走进去,发现A是一个"岔路口"既:D,C,B都可以走,但这个人选这走D(这个跟邻接表的结构和代码有关),然后他走到了D发现D只有F可以走,然后走到了F ,F 走到H,再走到I,然后发现,I之后就没有路可以走了,就往回走(回退),回到H,发现H没有《未走过的路》可以走,走回到F,发现F也没有《未走过的路》可以走,就继续回去,回到D,发现也没有《未走过的路》,在回去A,诶?岔路口有《未走过的路》可以走啊,于是从A走到C,C 之后就没有路可以接着往下走了,就回到A,发现A那个岔路口还有B可以走!然后走 B->E->G,G同理I 时的操作,最后回到A那里,然后A发现自己也把所有的路都走完了,然后递归结束,整个图的比遍历就结束。

什么是广度优先搜索呢?
所谓广度优先搜索,就好比是在湖里滴了一滴水滴,然后,水面上产生了一层又一层的水纹,水纹所及之处,就是遍历过的地方…
刚才例图的广度优先遍历如下图所示
在这里插入图片描述

那如何实现这样的遍历方法呢?那就要需要用一个特殊的数据结构:队列(先进先出)
假如我们从A出发,A先进队列,输出A,然后标记A已经访问过,然后找和A相连的其他顶点,它们分别是D,C,B ,好,先让A出队列,然后让它们(D,C,B)都分别进入队列,此时队列里的元素为D,C,B,接着从队列中取出一个元素重复刚才类似的操作,即取出D然后D连接的那个元素进入队列,即F进入队列,输出D,标记D,然后弹出D,此时队列元素为:C,B,F,然后取C重复刚才的操作,却发现C并没有相连却没有标记为遍历过的元素,因此只需要输出C,标记C,再从队列里弹出C,接着队列里的元素只剩B,F,然后重复刚才的操作,取出B,然后发现与B连接的元素为E,让E进入队列,输出B,标记B,再从队列里弹出B,此时队列里的元素为:F,E…
接下来简略过程:
输出F,标记F,弹出F,H进队列,队列里元素为E,H
输出E,标记E,弹出E,G进队列,队列里元素为H,G
输出H,标记H,弹出H,I进队列,队列里元素为G,I
G和I同理C,分别按顺序输出G,I
因此,整个遍历结果的顺序就为:A D C B F E H G I

什么是连通分量(连通分支)?
就是说一个图是由多个连通图组成的
在这里插入图片描述
比如说,下面这个图就是由两个连通分量组成的
如果我们从A开始进行深度优先遍历,那么先走A,再走D,再走E,回退到A(先回退到D),再走B,因为E已经遍历过,所以这个深度优先搜索就结束了,那么,由此我们可以知道,深度优先搜索结束了几次,就是图中连通分量有多少个,啊,那么,这个代码就非常容易实现啦,只需要用个FOR循环,配合个标记访问的数组,FOR循环执行次数为顶点个数,那么,比如说,我们从A出发,那么在一次深度优先遍历中,会输出A D E B,因此,这个搜索过程下来,已标记访问过的顶点就已经有A D E B 了,那么,FOR循环将会跳过 这几个顶点,直接从某一个未访问过的顶点 开始下一次深度优先遍历,然后对于这个图来说,第二次深度优先遍历就可以访问完所有顶点,然后结束第二次深度优先遍历,因此可以得到连通分量为2(连通分量初始为0,然后每次深度优先遍历完成时+1)
附上这三个功能实现的代码(因为是基于之前代码实现的,因此会有一些多出来的代码,见谅见谅)

#include<iostream>
#include<cstring>
#include<math.h>
#include<stdio.h>
#include<stdlib.h>
#include<conio.h>
#include<vector>
#include<queue>
#include<stack>
using namespace std;
const int maxWeight = RAND_MAX; //无穷大的值
const int DefaultVertices = 30;	//最大顶点数不妨设为30 
template <class T, class E>
class Graph {
   
protected:
    int maxVertices=10;//图中最大顶点数
    int numVertices=0;//图中当前顶点数
    int numEdges=0;//图中当前边数
    bool direction=false;//图中边的是否有方向
    bool withCost=false;//图中的边是否带权
    //返回顶点名vertex的序号(从0开始)
	int getVertexPos (T vertex);	
public:   
	void DFS (const T& v)
	{
   
		
	} 
	
	void BFS (const T& v)
	{
   
	} 
                                     
    Graph(int sz , bool direct, bool cost); //构造函数
    ~Graph()//析构函数 
	{
   }	
			//析构函数
    bool GraphEmpty () const	//判图是否为空,因为不需要修改,因此设置为常成员函数 
    {
   
	  return numEdges == 0;  
	}
    bool GraphFull() const;         //判图是否为满
    //返回图中当前顶点数
   	int NumberOfVertices () 
	{
    
	   return numVertices;
	}
    //返回图中当前边数
	int NumberOfEdges ()
	{
    
	 return numEdges;
	}
	//取回序号为 i 的顶点值(顶点名)
	virtual T getValue (int i){
   
	}
    //取顶点序号为v1,v2的边上权值
    virtual E getWeight (int v1, int v2){
   
	}   
  	//取顶点 v 的第一个邻接顶点序号
    virtual int getFirstNeighbor (int v){
   
	}
   //返回顶点 v 和邻接顶点 w 的下一个邻接顶点序号
	virtual int getNextNeighbor (int v, int w){
   
	}
    //插入新顶点, 点名为vertex
	virtual bool insertVertex (const T vertex){
   
	}
    //插入新边(v1,v2), 权值cost
	virtual bool insertEdge (T v1, T v2, E cost){
   
	}
    //删除名为 v 的顶点和所有与它相关联的边
	virtual bool removeVertex (T v){
   
	}
    //在图中删除边(v1,v2)
	virtual bool removeEdge (T v1, T v2){
   
	}	
};


template<class T , class E>
Graph<T,E>::Graph (int sz , bool direct, bool cost)
{
   
	 sz = DefaultVertices,  direct=false,  cost=false;
}
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++


template <class T, class E> // 
class Edge_Vertices {
   		   //边结点的类定义
public:
    int dest;		   //边的邻接(终)点序号
   	E cost;		   //边上的权值
   	Edge_Vertices<T, E>* link;//下一个连接顶点 
	Edge_Vertices (int num=0, Edge_Vertices<T, E>* next=NULL, E weight=NULL) //构造函数
    : dest (num), link (next), cost (weight) {
    }
   	bool operator != (Edge_Vertices<T, E>& R) const
	{
     return dest != R.dest;  } //重载!=运算符,判边不等
}; 



template <class T, class E> 
class Vertex {
   	      //顶点的类定义
public:
    T data;		      //源顶点的名字(数据)
	Edge_Vertices<T, E>* next=NULL; //next指针用来连接顶点 
};

//邻接表图的类定义继承图类
template <class T, class E> 
class Graphlnk : public Graph<T, E>{
   
 public: 
   void Enter (void);  //输入图中顶点和边信息
   void Print (void);   //输出图中顶点和边信息
    //源顶点表 (各边链表的源顶点)
    Vertex<T, E>* NodeTable;
    //返回名为vertex的顶点在图中的序号(从0开始),
	 //若未找到,则返回-1
	int getVertexPos (const T vertx) 	//找到目标顶点所在的序号 期中 vertx 参数为string 类型 
	{
      for (int i = 0; i < this->numVertices; i++)
	        if (NodeTable[i].data == vertx) return i;
 	     return -1; 
	}             //构造函数
    Graphlnk( int sz=DefaultVertices, bool direct=false, bool cost=false ); 
   	~Graphlnk(); //析构函数
    T getValue (int i) 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

是牛大春呀

老板糊涂啊

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值