先看标题再看代码
#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
#define MVNum 100
//边的定义
typedef struct ArcNode
{
int adjvex;
struct ArcNode *nextarc;
//OtherInfo info;
}ArcNode, *ArcNodePtr;
//顶点定义
typedef struct VNode
{
char data;
ArcNodePtr firstarc;
}VNode, *VNodePtr, AdjList[MVNum];
//图定义
typedef struct ALGraph
{
AdjList vertices;
int vexnum, arcnum;
}ALGraph, *Graph;
void CreateUDG(Graph G);
int LocateVex(Graph G, char v);
void DFS_AL(Graph G, int v, bool visited[]);
void BFS(Graph G, int v, bool visited[]);
int main(int argc, char *argv[])
{
bool visited[MVNum] = {0};
Graph G = (Graph)malloc(sizeof(ALGraph));
CreateUDG(G);
DFS_AL(G, 0, visited);
return 0;
}
//邻接表表示法创建无向图 G
void CreateUDG(Graph G)
{
int i, j, k;
printf("输入图G的顶点数和边数:\n");
scanf("%d %d", &G->vexnum, &G->arcnum);
printf("\n");
getchar();
//将顶点信息输入到顶点表中,将表头指针域置空
for(i = 0; i < G->vexnum; ++i)
{
printf("输入各顶点值: ");
scanf(" %c", &G->vertices[i].data);
//getchar();
G->vertices[i].firstarc = NULL;
printf("\n");
//getchar();
}//of for(i)
for(i = 0; i < G->vexnum; i++)
{
printf("%c ", G->vertices[i].data);
}
//创建邻接表,依次输入每条边依附的两个顶点
//确定两顶点的序号后插入到对应的边链表的头部
for(k = 0; k < G->arcnum; ++k)
{
char v1, v2;
printf("输入一边依附的两个顶点:\n");
getchar();
scanf(" %c", &v1);
//getchar();
scanf(" %c", &v2);
i = LocateVex(G, v1);
j = LocateVex(G, v2);
//将新边节点p1插入到顶点vi的边表头部
ArcNodePtr p1 = (ArcNodePtr)malloc(sizeof(ArcNode));
p1->adjvex = j;
p1->nextarc = G->vertices[i].firstarc;
G->vertices[i].firstarc = p1;
//将新边节点p2插入到顶点vj的边表头部
ArcNodePtr p2 = (ArcNodePtr)malloc(sizeof(ArcNode));
p2->adjvex = i;
p2->nextarc = G->vertices[j].firstarc;
}//of for(k)
}//of CreateUDG()
//定位顶点位置
int LocateVex(Graph G, char v)
{
int i;
for(i = 0; i < G->vexnum; i++)
{
if(v == G->vertices[i].data)
{
return i;
}// of if
}// of for(i)
if(i >= G->vexnum)
{
printf("%c位置错误,程序退出.\n", v);
exit(-1);
}
}//of LocateVex()
//深度优先遍历
void DFS_AL(Graph G, int v, bool visited[])
{
//访问第v个顶点,并置标志数组相应分量为true
printf("%c ", G->vertices[v].data);
visited[v] = true;
int w;
ArcNodePtr p = G->vertices[v].firstarc; //p指向v的边链表第一个节点
while(p != NULL) //边界点非空
{
w = p->adjvex; //w是v的邻接点
if(!visited[w]) //如果w未访问,则递归调用DFS_AL()
{
DFS_AL(G, w, visited);
}
p = p->nextarc; //p指向下一边节点
} //while
}
void CreateUDG(Graph G)
该函数是使用邻接表法创建无向图,具体流程如下:
-
先输入图的顶点数和边数,根据顶点数创建顶点表,并把表头指针域置空。
-
然后分别输入每个顶点的值,并输出当前顶点集。
-
创建邻接矩阵,依次输入每条边依附的两个顶点,确定两个顶点的序号后插入到对应的边链表的头部。
-
将新边节点p1插入到顶点vi的边表头部,并将新边节点p2插入到顶点vj的边表头部。
分析:
需要注意的是,由于是无向图,对于每条边缘需在两个顶点之间建立连接,并且每个节点的邻接点需要存储在一个单链表中,该节点对应的单链表即为其边表。此外,需要动态申请内存来存储每个新边节点p1和p2,并保证插入操作在单链表头部。
具体代码中,使用了邻接表的结构体VertexNode和单链表中各节点的结构体ArcNode,其中VertexNode包含每个节点的值及其邻接表头指针firstarc,ArcNode则包含每个节点的邻接点序号adjvex和边表里下一个相邻节点的指针nextarc。
复杂度分析:
邻接表表示法创建无向图的时间复杂度为O(arcnum),其中arcnum为图的边数。主要原因是需要对每一条边缘进行一次插入操作,这里的插入操作是在单链表的首部进行的。
在使用邻接表表示法遍历整张图时,DFS算法的时间复杂度为O(n+m),其中n为图中的顶点数,m为图中的边数。相比于邻接矩阵表示法,邻接表表示法的空间复杂度要小得多,因为邻接表只需要存储每个节点的邻接表头指针和其相邻节点的相关信息即可,而邻接矩阵需要存储所有的节点和它们之间的连边。因此,如果图比较稀疏,邻接表表示法比邻接矩阵更适合使用。另外,在删除或添加边的时候,邻接表的效率更高,因为只需要改变一个节点的指针即可。
总的来说,邻接表表示法适合表示稀疏图,其在存储空间和修改图的边时具有优势,但在需要遍历整个图时,时间复杂度相对较高。因此,在选择图的表示法时,需要结合实际情况进行选择,根据图的大小、不同算法的需求以及算法的特点进行综合考虑。
void DFS_AL(Graph G, int v, bool visited[]) 深度优先遍历
该段代码是深度优先遍历邻接表表示的图的算法代码,其具体步骤如下:
-
调用DFS_AL函数,并设定初始访问的顶点v和表示顶点是否被访问的visited标志数组。
-
访问第v个顶点,并置标志数组相应分量为true。
-
遍历v的边链表中所有节点,假设当前访问的邻接点为w,若其未被访问(visited[w]为false),则递归调用DFS_AL()进行遍历。
-
重复步骤2和3,直到遍历完所有顶点和边。
需要注意的是,在每次递归中访问visited数组中对应的节点,以判断该节点是否被访问过。同时,需要逐一遍历每个节点的邻接点列表,所以总的时间复杂度也是O(n+m),即与遍历的图的边数和顶点数相关。
深度优先遍历算法的主要优点是其实现简单,而且对于连通图和许多无向图而言是具有线性时间复杂度的,同时也支持对连通元件的遍历。在实际应用中广泛用于图的遍历、图的连通性检测、图的单源最短路径、拓扑排序等问题的解决。
运行结果: