图的遍历:
与树的遍历类似,图的遍历也是从图中某一顶点出发,按照某种方法对图中所有顶点访问且仅访问一次。图的遍历算法是求解图的连通性问题、拓扑排序、关键路径等算法的基础。
然而图的遍历比树的遍历复杂,因为图的任一顶点都可能和其余顶点相邻接。为了避免同一顶点被多次访问,在遍历图的过程中要记下每个已访问过的顶点。为此设置一个辅助数组visited[n],其初始值设置为FALSE或NULL一旦访问量顶点vi,便置为TRUE 或1.
根据搜索路径的方向,通常有两条遍历图的路径:
深度优先搜索和广度优先搜索。 它们对无向图和有向图都适用
1.深度优先搜索
类似于树的先序遍历,是树的先序遍历的推广。
连通图的深度优先搜索:
- 从图中某个顶点v出发,访问v。
- 找出刚访问过的顶点的第一个未被访问的邻接点,访问该顶点。以该顶点为新顶点,重复此步骤,直至刚访问过的顶点没有未被访问的邻接点为止。
- 返回前一个访问过的且仍有未被访问的邻接点的顶点,找出该顶点的下一个未被访问的邻接点,访问该顶点。
- 重复步骤2、3,直至图中所有顶点都被访问过,搜索结束。
采用邻接矩阵表示图的深度优先搜索遍历(连通图):
#define FALSE 0
#define TRUE 1
#define MAXINT 65535
int i,
bool visited[MVNum];//辅助数组,用来确定顶点是否被访问
//初始化辅助数组
for(i=0;i<G.Vexnum;i++)
{
visited[i] = FALSE;
}
//深度优先遍历
void DFS(AMGraph G, int v)
{
int j;
cout << G.Vexs[v] << endl;
visited[v] = 1;
for ( j = 0; j < G.numNodes; j++)
{
if (G.Edges[v][j] != MAXINT && !visited[j])
{
DFS(G, j);
}
}
return;
}
采用邻接矩阵表示图的深度优先搜索遍历(非连通图):
-
对它的连通分量分别进行深度优先遍历,即在前一个顶点进行一次深度优先遍历后,若图中尚有顶点未被访问,则另选图中一个未被访问的顶点做起始点,重复上述过程,直至图中所有顶点都被访问。
-
深度遍历非连通图的算法和连通图的区别不大,主要是在调用时加一个对辅助数组的判断,以此来确定是否还有未访问的连通分量
-
测试用例1:8个结点8条边 ,两个连通分量
a b 7
b c 7
b d 7
c e 7
d e 7
f g 7
f h 7
g h 7
-测试用例2:8个结点7条边,三个连通分量
a b 7
c d 7
c e 7
d e 7
f g 7
f h 7
g h 7`
case 2:
int v,count=0;
cout << "输入起始点:" << endl;
cin >> start;
cout << endl;
v = LocateVex(G, start);
DFS(G, v);
while (1)
{
//判断是否还有未访问的结点
for (int ifempty = 0; ifempty < G.numNodes; ifempty++)
{
if (visited[ifempty] != MAXINT && visited[ifempty] != 1)
{
count++;
}
}
cout << "还有" << count << "个结点未访问" << endl;
cout << endl;
while (count != 0)
{
int choice = 0;
cout << "是否继续深度遍历其他连通分量?[是:1/否:0]" << endl;
for (int i = 0; i < G.numNodes; i++)
{
if (visited[i] != MAXINT && visited[i] != 1)
{
cout << G.Vexs[i] << " ";
}
}
cout << endl;
cin >> choice;
if (choice == 1)
{
cout << "输入起始点:" << endl;
cin >> start;
cout << endl;
v = LocateVex(G, start);
DFS(G, v);
}
else if (choice == 0)
{
break;
}
count = 0;
for (int ifempty = 0; ifempty < G.numNodes; ifempty++)
{
if (visited[ifempty] != MAXINT && visited[ifempty] != 1)
{
count++;
}
}
cout << "还有" << count << "个结点未访问" << endl;
cout << endl;
}
if(count==0)
{
cout << "图完全遍历!" << endl;
break;
}
}
2.广度优先搜索
图的广度优先搜索,类似于树的层序遍历
特点:尽可能先对横向进行搜索。设x和y是两个相继被访问的顶点,若当前是以x 为出发点进行搜索,则在访问x的所有未曾被访问过的邻接点之后,紧接着是以y为出发点进行横向搜索,并对搜索到的y的邻接点中尚未被访问的顶点进行访问。
也就是说,先访问的顶点,其邻接点也先被访问。为此实现算法时也需要一个访问标志数组。
- 从图中某个顶点v出发,访问v;
- 依次访问v的每个未曾访问过的邻接点;
- 分别从这些邻接点出发,依次访问它们的邻接点,并使“先被访问的顶点的邻接点”先于“后被访问的顶点的邻接点”被访问。重复该步骤,直至图中所有已被访问的顶点的邻接点都被访问到。
采用邻接矩阵法表示的图的广度优先遍历:
算法步骤:
-
从图中某个顶点v出发,访问v,并置
visited[v]的值为True,然后将v进队。 -
只要队列不空,重复以下操作:
1.对头顶点u出队
2.依次检查u的所有邻接点w,如果
visited[w]的值为FALSE,则访问w,并置visited[w]的值为True,然后将w进队
辅助队列
//广度优先遍历用到的辅助队列
typedef struct
{
int data[MAXSIZE];
int front; //头指针
int rear; //尾指针,若队列不空,则指向队尾元素的下一个位置
}Queue;
//初始化空队列
status InitQueue(Queue& Q)
{
Q.front = 0;
Q.rear = 0;
return OK;
}
//队列判空
status QueueEmpty(Queue& Q)
{
if (Q.rear == Q.front)
{
return True;
}
else
{
return FALSE;
}
}
//元素入队
status EnQueue(Queue& Q,int e)
{
if ((Q.rear + 1) % MAXSIZE == Q.front)//队满判断
{
return ERROR;
}
Q.data[Q.rear] = e; //元素e赋值给队尾
Q.rear = (Q.rear + 1) % MAXSIZE; //rear指针后移
return OK;
}
//元素出队
status DeQueue(Queue& Q, int& e)
{
if (Q.front == Q.rear)
{
return ERROR;
}
e = Q.data[Q.front];
Q.front = (Q.front + 1) % MAXSIZE;
return OK;
}
BFS
void BFS(AMGraph G)
{
int i,j;
Queue Q;
for(i=0;i<G.numNodes;i++)
{
visited[i] = FALSE;
}
InitQueue(Q);
for(i=0;i<G.numNodes;i++)
{
if(visited[i]==0)
{
visited[i] = True;
cout<<G.Vexs[i]<<" ";
EnQueue(Q,i);
while(!QueueEmpty(Q))
{
DeQueue (Q,i);
for(j=0;j<G.numNodes;j++)
{
if(visited[j]!=1&&G.Edges[i][j]!=MAXINT)
{
cout<<G.Vexs[j]<<" ";
EnQueue(Q,j);
}
}
}
}
}
}
- 测试用例:8个结点 9条边
a b 7
a f 7
b c 7
b d 7
c e 7
d e 7
f g 7
f h 7
g h 7