图的遍历

用深度优先搜索(DFS)法遍历图

深度优先搜索:每次都是沿着路径到不能再前进时才退到最近的岔道口。
以一个有向图进行DFS遍历:
从V0 开始进行遍历,黑色表示结点未访问,白色表示结点已访问,虚线边表示当前遍历路径
在这里插入图片描述

  1. 访问V0 ,发现从V0 出发可以到达两个未访问顶点:V1 和V2 ,因此准备访问V1 和V2 这两个顶点。
    在这里插入图片描述
  2. 从V0 出发访问V1 ,发现从V1 出发可以到达两个未访问顶点:V3 和V4 ,因此准备访问V3和V4这两个顶点。
    在这里插入图片描述
  3. 从V1出发访问V3,但是从V3出发不能到达任何未访问顶点,因此退回到当前路径上距离V3最近的仍有未访问分支顶点的岔道口V1
    在这里插入图片描述
  4. 从V1出发访问V4,发现从V4出发可以到达一个未访问顶点:V5,因此准备前往访问V5
    在这里插入图片描述
  5. 从V4出发访问V5,发现从V5出发不能到达任何未访问顶点,因此退回到当前路径上距离V5最近的仍有未访问分支顶点的岔道口V0
    在这里插入图片描述
  6. 从V0出发访问V2,发现从V2出发不能到达任何未访问顶点,因此退回到当前路径上距离V5最近的仍有未访问分支顶点的岔道口。但是此时路径上所有顶点的分支顶点都已被访问,因此DFS算法结束。
    在这里插入图片描述

DFS的具体实现

连通分量:在无向图中,如果两个顶点之间可以相互到达(可以是通过一定路径间接到达),那么就称这两个顶点连通。如果图G(V, E)的任意两个顶点都连通,则称图G为连通图;否则,称图G为非连通图,且称其中的极大连通子图为连通分量。

强连通分量:在有向图中,如果两个顶点可以各自通过一条有向路径到达另一个顶点,就称这两个顶点强连通。如果图G(V, E)的任意两个顶点都强连通,则称图G为强连通图;否则,称图G为非强连通图,且称其中的极大强连通子图为强连通分量。

例如:无向图,V1V2V3、V4V5V6V7、V8V9形成了三个连通分量;有向图,V1V2V3、V4、V5V6V7V8形成了三个强连通分量。
在这里插入图片描述
遍历图的伪代码

DFS(u) //访问顶点u
{
	vis[u] = true; //设置u已被访问
	for(从u出发能到达的所有顶点v) //枚举从u出发可以到达的所有顶点v
	{
		if(vis[v] == false) //如果v未被访问
		{
			DFS(v); //递归访问v
		}
	}
}

DFSTrave(G) //遍历图G
{
	for(G的所有顶点u) //对G的所有顶点u
	{
		if(vis[u] == false) //如果u未被访问
		{
			DFS(u); //访问u所在的连通块
		}
	}
}

定义MAXV为最大顶点数、INF为一个很大的数字

constint MAXV = 1000; //最大顶点数
constint INF = 100000000; //设INF为一个很大的数
  1. 邻接矩阵版
int n, G[MAXV][MAXV]; //n为顶点数,MAXV为最大顶点数
bool vis[MAXV] = {false}; //如果顶点i已被访问,则vis[i] == true。初值为false
void DFS(int u, int depth) //u为当前访问的顶点标号,depth为深度
{
	vis[u] = true; //设置u已被访问
	//如果需要对u进行一些操作,可以在这里进行
	//下面对所有从u出发能到达的分支顶点进行枚举
	for(int v = 0; v < n; v++) //对每个顶点v
	{
		if(vis[v] == false && G[u][v] != INF) //如果v未被访问,且u可到达v
		{
			DFS(v, depth + 1); //访问v,深度加1
		}
	}
}

void DFSTrave() //遍历图G
{
	for(int u = 0; u < n; u++) //对每个顶点u
	{
		if(vis[u] == false) //如果u未被访问
		{
			DFS(u, 1); //访问u和u所在的连通块,1表示初始为第一层
		}
	}
}
  1. 邻接表版
vector<int> Adj[MAXV]; //图G的邻接表
int n; //n为顶点数,MAXV为最大顶点数
bool vis{MAXV] = {false}; //如果顶点i已被访问,则vis[i] == true。初值为false

void DFS(int u, int depth) //u为当前访问的顶点标号,depth为深度
{
	vis[u] = true; //设置u已被访问
	/*如果需要对u进行一些操作,可以在此处进行*/
	for(int i = 0; i < Adj[u].size(); i++)  //对从u出发可以到达的所有顶点v
	{
		int v = Adj[u][j];
		if(vis[v] == false) //如果v未被访问
		{
			DFS(v, depth + 1); //访问v,深度加1
		}
	}
}

void DFSTrave() //遍历图G
{
	for(int u = 0; u < n; u++//对每个顶点u
	{
		if(vis[u] == false) //如果u未被访问
		{
			DFS(u, 1); //访问u和u所在的连通块,1表示初始为第一层
		}
	}
}

采用广度优先搜索法(BFS)遍历图

广度优先搜索:需要使用一个队列,通过反复出队首顶点,将该顶点可到达的未曾加入过队列的顶点全部入队,直到队列为空时遍历结束。
以一个有向图进行BFS遍历:
从V0 开始进行遍历,黑色表示结点未访问,白色表示结点已访问,虚线边表示当前遍历路径
在这里插入图片描述

  1. 当前队列内元素为{V0}进行访问。之后将从V0出发能够到达的两个未曾加入过队列的顶点V1、V2加入队列。
    在这里插入图片描述
  2. 当前队列内元素为{V1, V2},取出队首元素V1进行访问。之后,将从V1出发能够到达的两个未曾加入过队列的顶点V3、V4加入队列。
    在这里插入图片描述
  3. 当前队列内元素为{V2, V3, V4},取出队首元素V2进行访问。由于从V2出发无法找到未曾加入过队列的顶点(V1、V4均已加入过队列),因此不予处理。
    在这里插入图片描述
  4. 当前队列内元素为{V3, V4},取出队首元素V3进行访问。由于V3出发无法找到未曾加入过入过队列的顶点,因此不予处理。
    在这里插入图片描述
  5. 当前队列内元素为{V4},取出队首元素V4进行访问。之后,将从V4出发能够到达的一个未曾加入过队列的顶点V5加入队列。
    在这里插入图片描述
  6. 当前队列内元素为{V5},取出队首元素V5进行访问。由于从V5出发无法找到未曾加入过队列的顶点,因此不予处理。
    在这里插入图片描述
  7. 当前队列为空,BFS遍历结束。

BFS的具体实现

BFS遍历伪代码

BFS(u) //遍历u所在的连通块
{
	queue q; //定义队列q
	将u入队;
	inq[u] = true; //设置u已被加入过队列
	while(q非空) //只要队列非空
	{
		取出q的队首元素u进行访问;
		for(从u出发可达的所有顶点v) //枚举从u能直接到达的顶点v
		{
			if(inq[v] == false) //如果v未曾加入过队列
			{
				将v入队;
				inq[v] = true; //设置v已被加入过队列
			}
		}
	}
}

BFSTrave(G) //遍历图G
{
	for(G的所有顶点u) //枚举G的所有顶点u
	{
		if(inq[u] == false) //如果u未曾加入过队列
		{
			BFS(u); //遍历u所在的连通块
		}
	}
}
  1. 邻接矩阵版
int n, G[MAXV][MAXV]; //n为顶点数,MAXV为最大顶点数
bool inq[MAXV] = {false}; //若顶点i曾入过队列,则inq[i]==true。初值为false

void BFS(int u) //遍历u所在的连通块
{
	queue<int> q; //定义队列q
	q.push(u); //将初始点u入队
	inq[u] = true; //设置u已被加入过队列
	while(!q.empty()) //只要队列非空
	{
		int u = q.front(); //取出队首元素
		q.pop(); //将队首元素出队
		for(int v = 0; v < n; v++)
		{
		    //如果u的邻接点v未曾加入过队列
			if(inq[v] == false && G[u][v] != INF)
			{
				q.push(v); //将v入队
				inq[v] = true; //标记v为已被加入过队列
			}
		}
	}
}

void BFSTrave() //遍历图G
{
	for(int u = 0; u < n; u++) //枚举所有顶点
	{
		if(inq[u] == false) //如果u未曾加入过队列
		{
			BFS(q); //遍历u所在的连通块
		}
	}
}
  1. 邻接表版
vector<int> Adj[MAXV]; //图G,Adj[u]存放从顶点u出发可以到达的所有顶点
int n; //n为顶点数,MAXV为最大顶点数
bool inq[MAXV] = {false}; //若顶点i曾入过队列,则inq[i] == true.初值为false

void BFS(int u) //遍历单个连通块
{
	queue<int> q; //定义队列q
	q.push(u); //将初始点u入队
	inq[u] = true; //设置u已被加入过队列
	while(!q.empty()) //只要队列非空
	{
		int u = q.front(); //取出队首元素
		q.pop(); //将队首元素出队
		for(int i = 0; i < Adj[u].size(); i++) //枚举从u出发能到达的所有顶点
		{
			int v = Adj[u][i];
			if(inq[v] == false) //如果v未曾加入过队列
			{
				q.push(v); //将v入队
				inq[v] = true; //标记v为已被加入过队列
			}
		}
	}
}

void BFSTrave() //遍历图G
{
	for(int u = 0; u < n; u++) //枚举所有顶点
	{
		if(inq[u] == false) //如果u未曾加入过队列
		{
			BFS(q); //遍历u所在的连通块
		}
	}
}

与树的BFS遍历一样,在给定BFS初始点的情况下,可能需要输出该连通块内所有其他顶点的层号。
首先,由于需要存放顶点层号。需要定义结构体Node,并在其中存放顶点编号和层号

struct Node
{
	int v; //顶点编号
	int layer; //顶点层号
}

vector邻接表中的元素就不再是int,而变为Node

vector<Node> Adj[N];

考虑层号的传递关系,当前顶点的层号为L,那么它所有出边的终点的层号都是L+1。

void BFS(int s) //s为起始顶点编号
{
	queue<Node> q; //BFS队列
	Node start; //起始顶点
	start.v = s; //起始顶点编号
	start.layer = 0; //起始顶点层号0
	q.push(start); //将起始顶点压入队列
	inq[start.v] = true; //起始顶点的编号设为已被加入过队列
	while(!q.empty())
	{
		Node topNode = q.front(); //取出队首顶点
		q.pop(); //队首顶点出队
		int u = topNode.v; //队首顶点的编号
		for(int i = 0; i < Adj[u].size(); i++)
		{
			Node next = Adj[u]pj]; //从u出发能到达的顶点next
			next.layer = topNode.layer + 1; //next层号等于当前顶点层号加1
			//如果next的编号未被加入过队列
			if(inq[next.v] == false)
			{
				q.push(next); //将next入队
				inq[next.v] = true; //next的编号设为已被加入过队列
			}
		}
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

阳光开朗男孩

你的鼓励是我最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值