一、图的遍历的相关定义
遍历的定义:从已给的连通图中的某一顶点出发,沿着一些边访遍图中的所有的顶点,且使每个顶点仅被访问一次,就叫做图的遍历,它是图的基本运算。
遍历的实质:找每个邻接点的过程。
图的特点:图中可能存在回路,且图中任一顶点都有可能与其它顶点相通,在访问完某个顶点之后可能会沿着某些边又回到了曾经访问过的顶点。
怎样避免重复访问?
解决思路:设置辅助数组visited[n],用来标记每个被访问过的顶点。1.初始状态visited[i]为0 ;2.顶点被访问,改visited[i]为1,防止被多次访问
二、深度优先搜索
概念
深度优先搜索遍历类似于树的先序遍历,是树的先序遍历的推广
深度优先搜索所遵循的搜索策略是尽可能“深”地搜索图。讲通俗一点,深度优先搜索其实像在走迷宫,遇到一个岔路口,先选则一条路,当发现这条路是死路时,又返回岔路口选择第二条路,直至找到出口。
过程
对于一个连通图,深度优先搜索遍历过程:
1.从图中某个顶点v出发,访问v;
2.找出刚刚访问过的顶点的第一个未被访问的邻接点,访问该顶点。以该顶点为新顶点,重复该步骤,直至刚访问过的顶点没有未被访问的邻接点为止;(递归思想)
3.返回前一个访问过的且仍有未被访问的邻接点的顶点,找出该顶点的下一个未被访问的邻接点,访问该顶点;
4.重复第2、3步骤,直至图中所有顶点都被访问过,搜索结束。
树的深度优先遍历
void PreOrder(TreeNode* R)
{
if (R != NULL)
{
visit(R);//访问根结点
while (R还有下一个子树T)
{
PreOrder(T);//先根遍历下一棵子树
}
}
}
时间复杂度分析
- 时间复杂度=访问各个结点所需时间+探索各条边所需时间
邻接矩阵存储的图:
访问个顶点需要的时间,查找每个顶点的邻接点都需要的时间,而且总共有个顶点
时间复杂度=
邻接表存储的图:
访问个顶点需要的时间,查找每个顶点的邻接点共需要的时间
时间复杂度=
基于邻接矩阵的深度优先遍历
bool visited[MAXV];//访问标记数组
void DFS(MGraph G, int v);//从第v个顶点开始,深度优先遍历
{
visit(v);//访问第v个顶点
visited[v] = true;//将第v个顶点设置为已经访问的标记
for (int w = FirstNeighbor(G, v); w > 0; w = NextNeighbor(G, v, w))
{
if (!visited[w])//第w个顶点为第v个顶点尚未访问的邻接点
{
DFS(G, w);
}
}
}
注意;
int FirstNeighbor(MGraph G, int x);//求图G中顶点x的第一个邻接点,若有则返回顶点号;
//若没有邻接点或图中不存在x,则返回-1
int NextNeighbor(MGraph G, int x, int y);
//假设图G中第y个顶点是第x个顶点的一个邻接点,
//返回除第y个顶点之外,第x个顶点的下一个邻接点的顶点号;若第y个顶点是第x个顶点的最后一个邻接点,则返回-1
2.演示
非连通图的深度优先遍历
void DFSTraverse(MGraph G)//对图G进行深度优先遍历
{
Init(G);//初始化
for (int v = 1; v <= G.n; v++)
{
if (!visited[v])//对每个连通分量调用一次DFS
{
DFS(G, v);
}
}
}
基于邻接表的深度优先遍历
int visited[MAXV] = { 0 };
void DFSA(AGraph* G, int i)//邻接表的深度优先算法,i表示从第i个顶点开始深度优先遍历
{
ArcNode* p;
visited[i] = 1;
cout << G->adjlist[i].data<<" ";//访问顶点vi
p = G->adjlist[i].firstarc;//取vi边表的头指针
while (p)//寻找vi的邻接点vj
{
if (!visited[p->adjvex])//若vj尚且未被访问,则以vj为出发点向纵深搜索
{
DFSA(G, p->adjvex);
}
p = p->nextarc;//找vi的下一个邻接点
}
}
三、广度优先搜索
广度优先搜索(Breadth - First Search)是指照广度方向搜索,它类似于树的层次遍历,是树的按层次遍历的推广。
广度优先搜索的基本思想是:
访问顶点v
依次访问顶点v的各个未被访问的邻接点
分别从这些邻接点出发,依次访问它们的邻接点,并使“先被访问的顶点的邻接点”先于”后被访问的顶点的邻接点”被访问
若此时图中尚有顶点未被访问,则任选其一为起点,重复,直至图中所有顶点都被访问到
(一)基于邻接矩阵的广度优先遍历
1. 演示
void BFS(MGraph* G, int v)//从v出发,广度优先遍历图G
{
visit(v);//访问v
visited[v] = true;//对v做已访问的标记
EnQueue(Q, v);//顶点v入队列Q
while (!isEmpty(Q))
{
DeQueue(Q, v);//顶点v出队列
for (int w = FirstNeighbor(G, v); w >= 0; w = NextNeighbor(G, v, w))
{
if (!visited[w])
{
visit(w);//访问顶点w
visited[w] = true;
EnQueue(Q, w);//顶点w出队
}
}
}
}
基于邻接表的广度优先遍历
void BFS(AGraph* G, int v);//从顶点v出发,广度优先遍历
void BFS2(AGraph* G, int i);//从第i个顶点出发,广度优先遍历
int visited[MAXV] = { 0 };
void BFS(AGraph* G, int v)//从顶点v出发,广度优先遍历
{
ArcNode* p = NULL;
int Qu[MAXV];
int front = 0, rear = 0;
int w;
cout << v << " ";
visited[v] = 1;
Qu[rear] = v;
rear = (rear + 1) % MAXSIZE;
while (front != rear)
{
w = Qu[front];
front = (front + 1) % MAXSIZE;
p = G->adjlist[w].firstarc;
while (p != NULL)
{
if (visited[p->adjvex] == 0)
{
cout << p->adjvex << " ";
visited[p->adjvex] = 1;
Qu[rear] = p->adjvex;
rear = (rear + 1) % MAXSIZE;
}
p = p->nextarc;
}
}
}
void BFS2(AGraph* G, int i)//从第i个顶点出发,广度优先遍历
{
ArcNode* p;
ElemType Qu[MAXV];//定义循环队列
int front = 0, rear = 0;//初始化
cout << G->adjlist[i].data << " ";//访问第i个顶点
visited[i] = 1;//第i个顶点标记为已访问
Qu[rear] = G->adjlist[i].data;//第i个顶点入队
rear = (rear + 1) % MAXSIZE;
while (front != rear)
{
ElemType w = Qu[front];
front = (front + 1) % MAXSIZE;
int m=0;
for (int k=1; k <= G->n; k++)
{
if (G->adjlist[k].data == w)
{
m = k;
break;
}
}
p = G->adjlist[m].firstarc;
while (p != NULL)
{
if (visited[p->adjvex] == 0)
{
cout << G->adjlist[p->adjvex].data << " ";
visited[p->adjvex] = 1;
Qu[rear] = G->adjlist[p->adjvex].data;
rear = (rear + 1) % MAXSIZE;
}
p = p->nextarc;
}
}
}