1. 图的遍历
定义:从给定图中任意指定的顶点出发,按照某种搜索方法沿着图的边访问图中的所有顶点,使每个顶点仅被访问一次。
图的遍历得到的顶点序列称为图的遍历序列。
图的遍历方式有两种:深度优先遍历(DFS)和广度优先遍历(BFS)。
深度优先遍历算法
过程:
(1)从图中某个初始顶点
v
v
v出发,首先访问初始顶点
v
v
v;
(2)选择一个与顶点
v
v
v相邻且没被访问过的顶点
w
w
w,再从
w
w
w出发进行深度优先搜索,直到图中与当前顶点
v
v
v邻接的所有顶点都被访问过为止。
算法设计思路:
#include <iostream>
using namespace std;
#define MaxV 100
typedef char VertexType;
typedef int InfoType;
int visited[MaxV] = {0};
//图的邻接表存储类型定义
struct ArcNode{//边节点类型
int adjvex;//边的终点编号
ArcNode* nextarc;//指向下一条边的指针
InfoType info;//边的信息,如:权
};
struct VNode{//头节点类型
VertexType data;//顶点信息
ArcNode* firstarc;//指向第一条边
};
struct ALGraph{//图邻接表类型
VNode adjlist[MaxV];//邻接表
int n,e;//顶点数和边数
};
//建立图的邻接表
void CreateAdjListGraph(ALGraph &G)
{
ArcNode *e;
cout << "输入顶点数:" << endl;
cin >> G.n; //输入当前图的顶点数
cout << "输入边数:" << endl;
cin >> G.e; //输入当前图的边数
cout << "输入顶点信息" << endl;
for(int i = 0; i < G.n; i++) //建立顶点表
{
cin >> G.adjlist[i].data; //输入顶点信息
G.adjlist[i].firstarc = NULL; //将表边指针置为空
}
cout << "输入边的两个顶点及其权重" << endl;
for(int k = 0; k < G.e; k++)
{
int i, j, w;
cin >> i >> j >> w; //输入边的两个顶点和边上的权重
e = new ArcNode; //创建一个表边节点指针
e->adjvex = j;
e->info = w;
e->nextarc = G.adjlist[i].firstarc;
G.adjlist[i].firstarc = e;
//因为是无向图,彼此相对称
e = new ArcNode; //创建一个表边节点指针
e->adjvex = i;
e->info = w;
e->nextarc = G.adjlist[j].firstarc;
G.adjlist[j].firstarc = e;
}
}
void DFS(ALGraph G, int v){//深度优先遍历
ArcNode *p;
int w;
printf("%d ",v);
visited[v] = 1;
p = G.adjlist[v].firstarc;//p指向v的第一个邻接点
while (p != nullptr){//循环访问v的所有邻接点
w = p->adjvex;//邻接点的编号
if (visited[w] == 0)
DFS(G,w);//递归遍历w
p = p->nextarc;//指向下一个邻接点
}
}
广度优先遍历算法
void BFS(ALGraph G, int v){//广度优先遍历
ArcNode *p; int w,i;
int qu[MaxV], rear, front;//循环队列
rear = front = 0;
int visited1[MaxV] = {0};
printf("%d ",v);
visited1[v] = 1;
rear = (rear +1) % MaxV;
qu[rear] = v;//v进队
while(rear != front){//队不为空时循环
front = (front + 1) % MaxV;
w = qu[front];//出队并赋给w
p = G.adjlist[w].firstarc;//找w的第一个邻接点
while(p != nullptr){//访问w的所有未访问的邻接点并进队
if(visited1[p->adjvex] == 0){
printf("%d ",p->adjvex);//访问并标记
visited1[p->adjvex] = 1;
rear = (rear + 1) % MaxV;//相邻顶点进队
qu[rear] = p->adjvex;
}
p = p->nextarc;//找下一个邻接顶点
}
}
}
int main()
{
ALGraph G;
CreateAdjListGraph(G);
cout << "深度优先遍历结果为:" << endl;
DFS(G,1);
cout << "广度优先遍历结果为:" << endl;
BFS(G,1);
return 0;
}
运行结果:
2. 图遍历的应用
基于DFS的应用
例1:图G采用邻接表存储,设计算法判断顶点u–>v是否有简单路径。
思路:从u开始进行深度优先遍历,当搜索到v时表明从u到v有简单路径。
int visited[MaxV] = {0};
bool ExistPath(ALGraph G, int u, int v){//判断u到v是否有简单路径
visited[u] = 1;
if (u == v) return true;
ArcNode *p; int w;
p = G.adjlist[u].firstarc;
if(p != nullptr){
w = p->adjvex;
if (visited[w] == 0)
ExistPath(G,w,v);
p = p->nextarc;
}
}
例2:图G采用邻接表存储,设计算法输出图G中从顶点u到v的一条简单路径。
int visited1[MaxV] = {0};
void FindPath(ALGraph G, int u, int v, int path[], int d){//输出从顶点u到v的一条简单路径
ArcNode *p; int w,i;
visited1[u] = 1;
d++; path[d] = u;
if (u == v){//找到一条路径后输出并返回
printf("一条简单路径为:");
for(i=0;i<=d;i++) printf("%d",path[i]);
printf("\n");
return;//找到一条后返回
}
p = G.adjlist[u].firstarc;//p指向顶点u的第一个相邻点
while(p!=nullptr){
w = p->adjvex;
if (visited1[w] == 0)
FindPath(G,w,v,path,d);
p = p->nextarc;
}
}
例3:图G采用邻接表存储,设计算法输出图G中从顶点u到v的长度为l的所有简单路径。
int visited[MaxV] = {0};
void FindAllPath(ALGraph G, int u, int v, int l, int path[], int d){//输出u到v的长度为l的所有简单路径
ArcNode *p; int w,i;
visited[u] = 1;
d++; path[d] = u;
if (u == v && d == l){//输出一条路径
for(i=0;i<=d;i++) printf("%d",path[i]);
printf("\n");
//return;因为要找所有的,所以不返回
}
p = G.adjlist[u].firstarc;//p指向顶点u的第一个相邻点
while(p!=nullptr){
w = p->adjvex;
if (visited[w] == 0)
FindAllPath(G,w,v,l,path,d);
p = p->nextarc;
}
visited[u] = 0;//回溯,使该顶点可以重新使用
}
基于BFS的应用
例4:设计算法求不带权无向连通图G中从u到v的一条最短路径。
void ShortPath(ALGraph G, int u, int v){//求u到v的最短路径
ArcNode* p; int w,i;
Quere qu[MaxV];//定义非循环队列
int rear = -1, front = -1;
int visited[MaxV] = {0};
rear++;//顶点u进队
qu[rear].data = u;
qu[rear].parent = -1;
while(front != rear){//队不空时循环
front++;//出队
w = qu[front].data;
//若找到v,输出逆路径
if(w == v){
i = front;
while(qu[i].parent != -1){
printf("%2d",qu[i].data);
i = qu[i].parent;
}
printf("%2d\n",qu[i].data);
break;
}
p = G.adjlist[w].firstarc;
//将w未访问过的邻接点进队
while(p != nullptr){
if(visited[p->adjvex] == 0){
visited[p->adjvex] = 1;
rear++;
qu[rear].data = p->adjvex;
qu[rear].parent = front;//进队顶点的前一顶点正是现在的出队元素
}
p = p->nextarc;
}
}
}