数构之图的遍历

图的遍历

从图中某一个顶点出发访遍图中其余顶点,且使每一个顶点仅被访问一次,这一过程叫做图的遍历

  • 深度优先搜索
  • 广度优先搜索
    在这里插入图片描述
深度优先搜索(DFS):

图的深度优先搜索类似于二叉树的先序遍历。

在深度优先搜索中,对于最新发现的顶点,如果它还有以此为起点而未探测到的边,就沿此边继续探测下去。当顶点 v
的所有边都已被探寻过后,搜索将回溯到发现顶点 v
有起始点的那些边。这一过程一直进行到已发现从源顶点可达的所有顶点为止。实际上深度优先搜索最初的探究也是为了解决迷宫问题。
在这里插入图片描述

深度优先搜索的思想:

  1. 首先访问出发点v,并将其标记成已访问过;
  2. 然后选取与v邻接的未被访问的任意一个顶点w,并访问它;
  3. 再选取与w邻接的未被访问的任意一个顶点并访问;
  4. 重复进行;
  5. 当一个顶点所有的邻接顶点都被访问过,则依次退回最近被访问过的顶点;
  6. 若该顶点还有其他邻接顶点未被访问,则从这些未被访问的顶点中取一个并重复上述过程;
  7. 直至图中所有的顶点都被访问过为止;

无向图的深度优先遍历:
在这里插入图片描述

  • 首先访问出发点A,并将其标记为已访问。
  • 访问(A的邻接顶点)C.
    由图可知A的邻接顶点有C.D.F。
  • 访问(C的邻接顶点)B.
  • 访问(C的邻接顶点)D.
  • 访问(A的邻接顶点)F.

前面已经访问了A,并且访问完了A的邻接点C的所有邻接点(包括递归的邻接点在内);因此,此时返回到访问A的另一个邻接点F。

由图可知F的邻接顶点是A和G,A已访问。

  • 访问(F的邻接顶点)G.
    G的邻接顶点为F和E,F已访问。
  • 访问(G的邻接顶点)E.在这里插入图片描述
    因此访问顺序是:A -> C -> B -> D -> F -> G -> E

有向图的深度优先遍历:
在这里插入图片描述

  • 访问A。
  • 访问(A的出边顶点)B
  • 访问(B的出边顶点)C
    在访问了B之后,接下来应该访问的是B的出边顶点,即顶点C,E,F。
  • 访问(B的出边顶点)E
    接下来访问C的出边的另一个顶点,即顶点E。
  • 访问(E的出边顶点)D
    接下来访问E的出边的另一个顶点,即顶点B,D。顶点B已访问,因此访问顶点D。
  • 访问(B的出边顶点)F
  • 访问(F的出边顶点)G
    在这里插入图片描述

因此访问顺序是:A -> B -> C -> E -> D -> F -> G

算法执行过程:任取一个顶点,访问,然后检查这个顶点的所有邻接顶点,递归访问其中未被访问过的顶点。

图的遍历结果取决于图的存储结构

图的邻接矩阵是唯一的,但对于邻接表来说,如果边的输入次序不同,邻接表也不同,

  1. 因此对于同一个图,基于邻接矩阵的遍历所得的DFS序列和BFS序列是唯一的;
  2. 基于邻接表的遍历所得到的DFS序列和BFS序列是不唯一的 。
    深度优先的算法分析
    在遍历过程中,一个顶点至多调用一次DFS函数,如果一旦某个顶点标志为已访问,就不再从它出发进行搜索

⑴ 利用邻接矩阵作为图的存储结构时, 查找每个顶点的
邻接点所需要时间为O(n²),其中为n顶点数。
⑵ 利用邻接表作为图的存储结构时, 找邻接点所需要的时间为O(e);因此,总的时间复杂度为O(n+e)

算法过程:

Boolean visited[MAX];                //  访问标志数组
Status (*VisitFunc)(int v);//  指向函数的指针
void  DFSTraverse(Graph G,Status(*Visit)(int V))
{  //  对图G作深度优先遍历
	VisitFunc=Visit; //  使用全局变量VisitFunc,使DFS不必设函数指针参数
		for(v=0;v<G.vexnum;++v)
		visited[v]=FALSE;                 //  访问标志数组初始化
		for(v=0;v<G.vexnum;++v)    // 本代码从v=0开始遍历
		if(!visited[v])
		DFS(G,v);        //  对未访问的结点调用DFS
}
void   DFS(Graph G,int V)
{
	//  从第v个顶点出发递归地深度优先遍历图G
	visited[v]=TRUE;
		VisitFunc(v);   //  访问第v个顶点
		for(w=FirstAdjVex(G,v); w>=0;w=NextAdjVex(G,v,w))
		if(!visited[w])
		DFS(G,w);    //  对v的未访问的邻接顶点w递归调用DFS
}
广度优先搜索遍历(BFS)

图的广度优先搜索遍历类似于树的层次遍历。

层序遍历
概念:先遍历第一层,再遍历第二层,…
特点:思想简单,实现复杂

在这里插入图片描述
访问过程:

  • 首先A入列。
    访问A,A已访问,A出列

  • (A的邻接顶点)B、F入列
    访问B,B已访问,B出列

  • (B的邻接顶点)C、I、G入列
    访问F,F已访问,F出列

  • (F的邻接顶点)E入列
    访问C,C已访问,C出列

  • (C的邻接顶点)D入列
    访问I,I已访问,I出列
    访问G,G已访问,G出列
    访问E,E已访问,E出列

  • (E的邻接顶点)H入列
    访问H,H已访问,H出列

void BFSTraverse(Graph G,Status(*Visit)(int v))
{//  使用访问标志数组visited和辅助队列Q

	for(v=0;v<G.vexnum;++v) 
		visited[v]=FALSE;//  访问标志数组初始化
	InitQueue(Q); //  置空的辅助队列Q
	for(v=0;v<G.wexnum;++v)//从0号顶点开始遍历
		if(!visited[v])
		{
			visited[v]=TRUE;
			Visit(v);
			EnQueue(Q,v);
			while(!QueueEmpty(Q))
			{
				DeQueue(Q,u); //  队头元素出队并置为u
				for(w=FirstAdjVex(G,v);
					w>=0;w=NextAdjVex(G,u,w))
					if(!visited[w]) //  w为v的尚未访问的邻接顶点
					{
						visited[w]=TRUE;
						Visit(w);
						EnQueue(Q,w);
					}
			}
		}
}

好啦,下次等我做了习题再来更新。留给赞吧~
在这里插入图片描述

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
C++创建可以使用邻接矩阵或邻接表两种方式。接下来我会简单介绍一下使用邻接矩阵和邻接表创建,并使用深度优先遍历算法和广度优先遍历算法进行遍历。 1. 邻接矩阵创建 邻接矩阵是一个二维数组,其中每个元素表示两个顶点之间是否有边相连。如果有,则为1,否则为0。 首先需要定义一个类,其中包含一个邻接矩阵和的顶点数。 ```C++ #define MAX_VERTEX_NUM 100 typedef char VertexType; typedef int EdgeType; class Graph { public: Graph(int vertexNum); // 构造函数 ~Graph(); // 析构函数 void addEdge(int v1, int v2); // 添加边 void DFS(int start); // 深度优先遍历 void BFS(int start); // 广度优先遍历 private: int vertexNum; // 的顶点数 VertexType vertex[MAX_VERTEX_NUM]; // 顶点集合 EdgeType edge[MAX_VERTEX_NUM][MAX_VERTEX_NUM]; // 邻接矩阵 bool visited[MAX_VERTEX_NUM]; // 记录节点是否被遍历过 }; ``` 接下来实现构造函数和添加边的函数。 ```C++ Graph::Graph(int vertexNum) { this->vertexNum = vertexNum; for(int i = 0; i < vertexNum; i++) { for(int j = 0; j < vertexNum; j++) { edge[i][j] = 0; // 初始化邻接矩阵 } } } Graph::~Graph() {} void Graph::addEdge(int v1, int v2) { edge[v1][v2] = 1; edge[v2][v1] = 1; // 因为是无向,所以需要对称存储 } ``` 接下来实现深度优先遍历和广度优先遍历。这里使用递归实现深度优先遍历,使用队列实现广度优先遍历。 ```C++ void Graph::DFS(int start) { visited[start] = true; // 标记节点为已遍历 cout << vertex[start] << " "; // 输出节点 for(int i = 0; i < vertexNum; i++) { if(edge[start][i] == 1 && !visited[i]) { // 如果与当前节点相邻的节点未被遍历过,则递归遍历这个节点 DFS(i); } } } void Graph::BFS(int start) { queue<int> q; visited[start] = true; // 标记节点为已遍历 q.push(start); // 将节点加入队列 while(!q.empty()) { int cur = q.front(); // 取出队列首节点 q.pop(); cout << vertex[cur] << " "; // 输出节点 for(int i = 0; i < vertexNum; i++) { if(edge[cur][i] == 1 && !visited[i]) { // 如果与当前节点相邻的节点未被遍历过,则将其标记为已遍历,并加入队列 visited[i] = true; q.push(i); } } } } ``` 2. 邻接表创建 邻接表是一个链表数组,其中每个链表表示一个顶点的邻接点。链表中的每个节点存储了一个邻接点的编号和指向下一个节点的指针。 同样需要定义一个类,其中包含一个邻接表和的顶点数。 ```C++ typedef struct ArcNode { int adjvex; // 邻接点编号 struct ArcNode* next; // 指向下一个邻接点的指针 } ArcNode; typedef struct VNode { VertexType data; // 顶点数据 ArcNode* firstArc; // 指向第一个邻接点的指针 } VNode; class Graph { public: Graph(int vertexNum); // 构造函数 ~Graph(); // 析构函数 void addEdge(int v1, int v2); // 添加边 void DFS(int start); // 深度优先遍历 void BFS(int start); // 广度优先遍历 private: int vertexNum; // 的顶点数 VNode vertex[MAX_VERTEX_NUM]; // 顶点集合 bool visited[MAX_VERTEX_NUM]; // 记录节点是否被遍历过 }; ``` 接下来实现构造函数和添加边的函数。 ```C++ Graph::Graph(int vertexNum) { this->vertexNum = vertexNum; for(int i = 0; i < vertexNum; i++) { vertex[i].data = 'A' + i; // 初始化顶点数据 vertex[i].firstArc = NULL; // 初始化邻接表 } } Graph::~Graph() {} void Graph::addEdge(int v1, int v2) { ArcNode* node1 = new ArcNode; node1->adjvex = v2; node1->next = vertex[v1].firstArc; vertex[v1].firstArc = node1; // 将v2加入v1的邻接表 ArcNode* node2 = new ArcNode; node2->adjvex = v1; node2->next = vertex[v2].firstArc; vertex[v2].firstArc = node2; // 将v1加入v2的邻接表 } ``` 接下来实现深度优先遍历和广度优先遍历。这里同样使用递归实现深度优先遍历,使用队列实现广度优先遍历。 ```C++ void Graph::DFS(int start) { visited[start] = true; // 标记节点为已遍历 cout << vertex[start].data << " "; // 输出节点 for(ArcNode* node = vertex[start].firstArc; node != NULL; node = node->next) { if(!visited[node->adjvex]) { // 如果与当前节点相邻的节点未被遍历过,则递归遍历这个节点 DFS(node->adjvex); } } } void Graph::BFS(int start) { queue<int> q; visited[start] = true; // 标记节点为已遍历 q.push(start); // 将节点加入队列 while(!q.empty()) { int cur = q.front(); // 取出队列首节点 q.pop(); cout << vertex[cur].data << " "; // 输出节点 for(ArcNode* node = vertex[cur].firstArc; node != NULL; node = node->next) { if(!visited[node->adjvex]) { // 如果与当前节点相邻的节点未被遍历过,则将其标记为已遍历,并加入队列 visited[node->adjvex] = true; q.push(node->adjvex); } } } } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值