图的遍历及其应用
一、图的深度遍历及其应用
1. 图的深度遍历(Depth-First Search)
1)基本思想:递归
(1)访问顶点A;
(2)从A的未被访问的邻接点中选取一个顶点w,从w出发进行深度优先遍历;
(3)重复上面两步,直至所有顶点均被访问过。
2)辅助数组:
visited[ ]:用来记录每个顶点是否被访问过。1,访问过;0,未访问过。
3)算法实现:
//邻接矩阵的深度优先遍历
void DFS(MGraph &G, int v, int visited[]) {
int i;
printf("%c\n", G.vexs[v]);
visited[v] = 1;
for(i=0; i<G.vexNum; i++) {
if (G.arcs[v][i] != 0 && G.arcs[v][i] != INFINITY && !visited[i]) {
DFS(G, i, visited);
}
}
}
//邻接表的深度优先遍历
void DFS(ALGraph &G, int v, int visited[]) {
ArcNode *p = G.adjList[v].firstArc;
printf("%c\t", G.adjList[v].vertex);
visited[v] = 1;
while (p) {
if (!visited[p->adjvex]) {
DFS(G, p->adjvex, visited);
}
p = p->next;
}
}
2.图的深度遍历的应用
1)求一条包含所有顶点的回路(Hamilton)
- 【分析】此为汉密尔顿(Hamilton)问题。起点的选择和顶点的邻接顺序会影响该简单路径是否能被找到;
- 修改DFS算法:访问顶点时将该顶点序号加入数组path[ ],n++判断是否所有顶点已被访问;某顶点所有邻接点全部被访问后回溯,计数器n–;
- 算法实现:
void DFS(MGraph &G, int i, int path[], int visited[], int &n) {
int j;
visited[i]=1;
path[n]=i;
n++;
if (n==G.vexNum) { // 符合条件输出路径
for (j=0; j<G.vexNum; j++) {
printf("%d", path[j]);
}
putchar('\n');
//return;
}
for (j=0; j<G.vexNum; j++) {
if (G.arcs[i][j]==1 && !visited[j]) {
DFS(G, j, path, visited, n);
}
}
visited[i] = 0;
n--;
}
void Hamilton(MGraph G) {
int i, n=0; //n记录当前该路径上的顶点数
int path[G.vexNum];
int visited[G.vexNum];
for (i=0; i<G.vexNum; i++) {
visited[i] = 0;
}
for (i=0; i<G.vexNum; i++) {
path[i] = INFINITY;
}
for (i=0; i<G.vexNum; i++) {
if (!visited[i]) {
DFS(G, i, path, visited, n); //i为起始点,n为加入路径的顶点个数
}
}
}
2)判断图中是否存在环
- 【分析】在图的深度遍历过程中,若出现与某个顶点有邻接关系的所有顶点均被访问过,则一定出现了回路。
- 修改DFS算法:对顶点x做深度优先遍历时,先计算其度dx和与它有邻接关系的访问标志数v;若dx==v,则出现回路,否则继续。
- 算法实现:
int DFS(MGraph &G, int i, int visited[]) {
int j, vi, di;
visited[i] = 1;
for (di = 0, vi = 0, j = 0; j<G.vexNum; j++) {
if (G.arcs[i][j]==1) {
di++;
if (visited[j]) {
vi++;
}
}
}
if (di == vi && di!=1) {
printf("有回路!\n");
return 1;
}
for (j=0; j<G.vexNum; j++) {
if (G.arcs[i][j]==1 && !visited[j]) {
if (DFS(G, j, visited)) {
return 1;
} else {
return 0;
}
}
}
printf("无回路!\n");
return 0;
}
3)求无向图的顶点a到i之间的简单路径
- 【分析】深度遍历过程中将所有访问到的顶点下标计入数组path[ ],若找到i顶点,则path中存储的即为a到i的简单路径;若未找到,删除path[ ]中的一个下标,回退,直至找到i。
- 算法实现:
//辅助函数:加入Path[]数组和从path[]中删除
void Append(int path[], int i);
void Delete(int path[]);
void DFSearchPath(MGraph G, int v, int w, int path[], int visited[], int &found) {
int j;
Append(path, v);
visited[v] = 1;
for (j=0; j<G.vexNum && !found; j++) {
if (G.arcs[v][j]==1 && !visited[j]) {
if (j == w) {
found=1;
Append(path, j);
}
DFSearchPath(G, j, w, path, visited, found);
}
}
if (!found) {
Delete(path);
}
}
二、图的广度遍历及其应用
1. 图的广度遍历(Breadth-First Search)
1)基本思想:
(1)访问初始顶点A,并将其顶点序号入队;
(2)队列不空,则出队;依次访问它的每一个未被访问过的邻接点,并将其编号入队;
(3)重复(2)直至队列空,遍历结束。
【例】:
2)代码实现:
队列的操作:
typedef struct {
int front, reer;
int data[MAXSIZE];
} Queue;
void initQueue(Queue &Q) {
Q.front = Q.reer = 0;
for(int i=0; i<MAXSIZE; i++) {
Q.data[i] = INFINITY;
}
}
void enterQueue(Queue &Q, int v) {
if((Q.front+1)%MAXSIZE == Q.reer) {
printf("Queue full\n");
return;
}
Q.data[Q.front] = v;
Q.front = (++Q.front)%MAXSIZE;
}
int deletQueue(Queue &Q) {
int i = Q.data[Q.reer];
Q.data[Q.reer] = INFINITY;
Q.reer = (++Q.reer)%MAXSIZE;
return i;
}
int emptyQueue(Queue Q) {
if(Q.front == Q.reer) {
return 0;
}
return 1;
}
算法实现:
//邻接矩阵的广度优先遍历
void BFS(MGraph G, Queue &Q, int v, int visited[]) {
int a, i = 0;
enterQueue(Q, v);
while(emptyQueue(Q)) {
a = deletQueue(Q);
visited[a] = 1;
printf("%c\t", G.vexs[a]);
i=0;
while(i<G.vexNum) {
if(G.arcs[a][i] == 1) {
if(visited[i]==0) {
enterQueue(Q, i);
visited[i] = 1;
}
}
i++;
}
}
}
//邻接表的广度优先遍历
void BFS(ALGraph G, Queue &Q, int v, int visited[]) {
int a;
ArcNode *p=NULL;
initQueue(Q);
enterQueue(Q, v);
visited[v] = 1;
while (emptyQueue(Q)) {
a = deletQueue(Q);
printf("%c\t", G.adjList[a].vertex);
p = G.adjList[a].firstArc;
while (p) {
if(!visited[p->adjvex]) {
enterQueue(Q, p->adjvex);
visited[p->adjvex] = 1;
}
p = p->next;
}
}
}
2. 图的广度遍历的应用
求无向图的顶点A、B之间的最短路径
【分析】从源点进行BFS,让进入队列的节点既能完成按层遍历,又能记住路径;将队列节点修改为带prior指针指向该节点前驱;
对队列的操作修改:
typedef struct node{
int data;
struct node *prior, *next;
}QNode;
typedef struct {
QNode *fron, *reer;
}Queue;
void initQueue(Queue &Q) {
Q.fron = Q.reer = new QNode;
Q.fron->prior = Q.fron->next = NULL;
}
void enterQueue(Queue &Q, int data) {
Q.reer->next = new QNode;
Q.reer = Q.reer->next;
Q.reer->data = data;
Q.reer->next = NULL;
Q.reer->prior = NULL;
}
void deleQueue(Queue &Q) {
Q.fron = Q.fron->next;
}
算法实现:
void BFSearch(MGraph G, Queue &Q, int vi, int vj, int visited[]) {
int a, i;
enterQueue(Q, vi);
visited[vi] = 1;
while (Q.fron != Q.reer) {
a = Q.fron->next->data;
deleQueue(Q);
for (i=0; i<G.vexNum; i++) {
if (G.arcs[a][i] == 1 && visited[i]==0) {
enterQueue(Q, i);
Q.reer->prior = Q.fron;
visited[i] = 1;
if (i==vj) {
return;
}
}
}
}
}
void creatGraph(MGraph &G) {
int i, j, from, to;
//initial Graph
for(i=0; i<MAXSIZE; i++) {
for(j=0; j<MAXSIZE; j++) {
if(i == j) {
G.arcs[i][j] = 0;
continue;
}
G.arcs[i][j] = INFINITY;
}
}
printf("Please input verNum & arcNum\n");
scanf("%d%d", &G.vexNum, &G.arcNum);
printf("Please input vertices' name\n");
getchar();
for(i=0; i<G.vexNum; i++) {
scanf("%c", &G.vexs[i]);
}
printf("Please input edge\n");
for(i=0; i<G.arcNum; i++) {
scanf("%d%d", &from, &to);
G.arcs[from][to] = 1;
G.arcs[to][from] = 1;
}
}