图的 DFS (深度优先搜索)
代码:
void Visit(Vertex V)
{
printf("正在访问顶点%d\n", V);
}
// Visited[] 为全局变量, 已经初始化为 false
// 以 V 为出发点对邻接表存储的图 Graph 进行 DFS 搜索
void DFS(LGraph Graph, Vertex V, void(*Visit)(Vertex))
{
PtrToAdjVNode W;
Visit(V); // 访问第 V 个顶点
Visited[V] = 1; // 标记 V 已访问
for (W = Graph->G[V].FirstEdge; W; W = W->Next) // 对 V 的每个邻接点 W->AdjV
if (!Visited[W->AdjV]) // 若 W->AdjV 为被访问
DFS(Graph, W->AdjV, Visit); // 则递归访问之
}
配套教程 --> 浙江大学陈越数据结构图部分, 教材 --> 《数据结构第二版》(陈越)
完整代码(这里的图用的是邻接表的实现):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MaxVertexNum 100 // 最大顶点数设为 100
typedef int Vertex; // 用顶点下标表示顶点, 为整型
typedef int WeightType; // 边的权值为整型
typedef char DataType; // 顶点存储的数据类型为字符型
// 全局变量 Visited[], 初始化为 false
char Visited[MaxVertexNum]; // 0 代表 false, 1 代表 true
static void init()
{
memset(Visited, 0, MaxVertexNum);
}
// 边的定义
typedef struct ENode *PtrToENode;
struct ENode
{
Vertex V1, V2; // 有向边 <V1, V2>
WeightType Weight; // 权重
};
typedef PtrToENode Edge;
// 邻接点的定义
typedef struct AdjVNode *PtrToAdjVNode; // Adjacency list --> 邻接表
struct AdjVNode
{
Vertex AdjV; // 邻接点下标
WeightType Weight; // 边权重
PtrToAdjVNode Next; // 指向下一个邻接点的指针
};
// 顶点表头节点的定义
typedef struct VNode
{
PtrToAdjVNode FirstEdge; // 边表头指针
DataType Data; // 存顶点的数据
// 注意, 很多情况下, 顶点无数据, 此时 Data 可以不用出现
} AdjList[MaxVertexNum]; // AdjList 是邻接表类型
// 图节点的定义
typedef struct GNode *PtrToGNode;
struct GNode
{
int Nv; // 顶点数
int Ne; // 边数
AdjList G; // 邻接表, 用数组方式存储每一个顶点的表头节点
};
typedef PtrToGNode LGraph; // 以邻接表的方式存储的图类型
// 初始化一个有 VertexNum 个顶点但没有边的图
LGraph CreateGraph(int VertexNum)
{
Vertex V;
LGraph Graph;
Graph = (LGraph) malloc(sizeof(struct GNode)); // 建立图
Graph->Nv = VertexNum; // 顶点数为 VertexNum
Graph->Ne = 0; // 边数为 0
// 初始化邻接表头指针
// 注意: 这里默认顶点编号从 0 开始, 到(Graph->Nv - 1)
for (V = 0; V < Graph->Nv; V++)
Graph->G[V].FirstEdge = NULL;
return Graph;
}
// 插入边
void InsertEdge(LGraph Graph, Edge E)
{
PtrToAdjVNode NewNode;
// 插入边 <V1, V2>
// 为 V2 建立新的邻接点
NewNode = (PtrToAdjVNode) malloc(sizeof(struct AdjVNode));
NewNode->AdjV = E->V2;
NewNode->Weight = E->Weight;
// 将 V2 插入 V1 的表头(插入表头比插入表尾明显容易实现)
NewNode->Next = Graph->G[E->V1].FirstEdge;
Graph->G[E->V1].FirstEdge = NewNode;
// 若是无向图, 还有插入边 <V2, V1>
// 为 V1 建立新的邻接点
/*NewNode = (PtrToAdjVNode)malloc(sizeof(struct AdjVNode));
NewNode->AdjV = E->V1;
NewNode->Weight = E->Weight;
// 将 V1 插入 V2 的表头
NewNode->Next = Graph->G[E->V2].FirstEdge;
Graph->G[E->V2].FirstEdge = NewNode;*/
}
// 建图
LGraph BuildGraph()
{
LGraph Graph;
Edge E;
// Vertex V;
int Nv, i;
scanf("%d", &Nv); // 读入顶点个数
Graph = CreateGraph(Nv); // 初始化有 Nv 个顶点但没有边的图
scanf("%d", &(Graph->Ne)); // 读入边数
if (Graph->Ne != 0)
{ // 如果有边
E = (Edge) malloc(sizeof(struct ENode));
// 读入边, 格式为 "起点, 终点, 权重", 插入邻接表
for (i = 0; i < Graph->Ne; i++)
{
scanf("%d %d %d", &E->V1, &E->V2, &E->Weight);
// 注意: 如果权重不是整型, Weight 的读入格式要改
InsertEdge(Graph, E);
}
}
// 如果顶点有数据的话, 读入数据
/*for (V = 0; V < Graph->Nv; V++)
{
scanf("%c", &(Graph->G[V].Data));
}*/
return Graph;
}
// 简单遍历图
void PrintGraph(LGraph Graph)
{
if (!Graph->G[0].FirstEdge) // 邻接表为空
return;
int i;
for (i = 0; i < Graph->Nv; i++)
{
printf("%d: | ", i);
PtrToAdjVNode tmp = Graph->G[i].FirstEdge;
while (tmp)
{
printf("%d %d | ", tmp->AdjV, tmp->Weight);
tmp = tmp->Next;
}
printf("\n");
}
}
void Visit(Vertex V)
{
printf("正在访问顶点%d\n", V);
}
// Visited[] 为全局变量, 已经初始化为 false
// 以 V 为出发点对邻接表存储的图 Graph 进行 DFS 搜索
void DFS(LGraph Graph, Vertex V, void(*Visit)(Vertex))
{
PtrToAdjVNode W;
Visit(V); // 访问第 V 个顶点
Visited[V] = 1; // 标记 V 已访问
for (W = Graph->G[V].FirstEdge; W; W = W->Next) // 对 V 的每个邻接点 W->AdjV
if (!Visited[W->AdjV]) // 若 W->AdjV 为被访问
DFS(Graph, W->AdjV, Visit); // 则递归访问之
}
// 测试一组数据, 测试的图有 5 个顶点, 8 条有向边
// <1, 0, 9> <0, 2, 6> <2, 4, 7> <4, 3, 6>
// <3, 1, 5> <1, 2, 4> <0, 3, 3> <3, 4, 8>
int main()
{
/*---------- 初始测试 ----------*/
// LGraph Graph = BuildGraph();
// PrintGraph(Graph);
/*---------- 初始测试 ----------*/
/*---------- 测试 DFS ----------*/
init();
LGraph Graph = BuildGraph();
DFS(Graph, 0, Visit);
/*---------- 测试 DFS ----------*/
return 0;
}
// 测试数据
/*
5
8
1 0 9
0 2 6
2 4 7
4 3 6
3 1 5
1 2 4
0 3 3
3 4 8
*/
输出结果:
按: 测试用的图的示意 -->