总:图的两种遍历方式:
1.深度优先遍历(DFS,Depth First Search)
2.广度优先遍历(BFS,Breadth First Search)
(这里的遍历方式主要是针对以邻接表为存储形式的)
- 从定义出发:
图的遍历:从图中某一顶点出发访遍图中其余顶点,且使每一个顶点仅被访问一次
因此,为防止一个顶点被访问多次,设一个辅助数组visited[n];
visited[i] = 0或FALSE //vi未被访问过
visited[i] = 1或TRUE //vi已被访问
遍历方式: | 深度优先遍历 | 广度优先遍历 |
---|---|---|
特点: | 类似于树的先根遍历 | 类似于树的层次遍历 |
具体遍历方式如下:
—深度优先遍历(DFS)
- 从图中某顶点v出发,访问该顶点;
- 依次从v的未被访问的邻接点出发继续对图进行深度优先遍历,直至图中所有和v有路径相通的顶点都被访问到;
- 若图中仍有顶点未被访问, 则另选一个未曾被访问的顶点作起始点,重复上述过程,直到图中所有顶点都被访问为止。
算法如下:(邻接表的组成结构参考上一篇图的定义结构中的邻接表组成)
//图的邻接表表示:
typedef struct adjVert{ //边
int adjvert; //存储邻接顶点的地址
int edgeInfo;
struct adjVert *next;
}Graph_AdjVert;
typedef struct vert{ //顶点
char data[128];
adjVert *firstarc;
}Graph_Vert;
typedef struct{ //图
Graph_Vert v[MAX_VERTEX_NUM];
int n;
}Graph;
Boolean visited[MAX_VERTEX_NUM]; //顶点访问标志的数组
void DFSTraverse(ALGraph G){
int i;
//初始化:
for(i = 0; i < G.n; ++i)visited[i] = FALSE;
//开始对顶点表进行查阅,看是否都已经遍历了,如果有顶点没有访问过,就顺着该顶点进行深度遍历
for(i = 0; i < G.n; ++i) //如果结点没有被访问
if(!visited[i]) DFS(G,i);
}
void DFS(ALGraph G, int i){ //顺着某一个顶点进行深度遍历
//先访问该顶点
visited[i] = TRUE;
ArcNode *p = G.v[i].firstarc;//让p指向第一个邻接顶点;
while(p){ //如果p存在,即存在邻接顶点而不是NULL,就表示可以遍历
if(!visited[p->adjvex])//如果该点没有被访问过,就递归深度遍历
DFS(G, p->adjvex);
p = p->nextarc; //若没有执行if语句,也就是已经被访问过,就将p指向下一个邻接顶点;
}
}
—广度优先遍历(BFS)
- 从图中某顶点v出发,访问该顶点,并依次访问V0的各个未曾访问过的邻接点;
- 然后分别从这些邻接点出发,直至图中所有已被访问的顶点的邻接点都被访问到;
- 若图中仍有顶点未被访问, 则另选一个未曾被访问的顶点作起始点,重复上述过程,直到图中所有顶点都被访问为止。
广度遍历后:
遍历顺序:
V1,V2,V3,V4,V5,V6,V7,V8
算法实现如下:
//前期的队列准备
//创建辅助队列的结构
typedef struct SQueue{
int v[MAX_VERTEX_NUM];
int front,rear;
}SQueue;
//队列初始化
void InitQueue(SQueue *q){
q->front = q->rear = 0;
}
//元素入队
void EnQueue(SQueue *q, int elmt){
q->v[q->rear] = elmt;
q->rear = (q->rear+1)%MAX_VERTEX_NUM;
}
//判断队列是否为空
int QueueEmpty(SQueue *q){
return (q->rear - q->front <= 0);
}
//元素出队
int DeQueue(SQueue *q){
int elmt;
if(!QueueEmpty(q)){
elmt = q->v[q->front];
q->front = (q->front+1) % MAX_VERTEX_NUM;
}
return elmt;
}
//广度优先遍历:
void BFS(Graph G){
int i;
//创建辅助队列
SQueue *Q;
Q = (SQueue *)malloc(sizeof(SQueue));
//初始化
for(i = 0 ; i < G.n; i++) visited[i] = FALSE;
InitQueue(Q);
for(i = 0; i < G.n; i++){
if(!visited[i]){ //若未被访问
visited[i] = TRUE;
EnQueue(Q, i); //将该点入队
while(!QueueEmpty(Q)){
i = DeQueue(Q); //队头元素出队并存在e中;
Graph_AdjVert *p = G.v[i].firstarc;
while(p){
if(!visited[p->adjvert]){
p->adjvert = TRUE;
EnQueue(Q,p->adjvert);
}
p = p->next;
}
}
}
}
}