📅  最后修改于: 2023-12-03 15:28:41.089000             🧑  作者: Mango
这道题目是GATE 2000年计算机科学考试的一道题目,属于数据结构和算法类别。下面是该题的详细题意:
给定一个由n个结点和m条边组成的有向图。其中,表示顶点u到顶点v的边。 设从顶点1开始进行广度优先搜索(BFS)遍历,如果遍历到结点t,则停止遍历并输出1到t的最短路径。(输出的路径上包含的所有边组成的集合被称为最短路径。)
考虑设计和实现一个算法来解决这个问题。
为了实现该算法,我们需要使用以下类型的数据结构:
typedef struct node {
int vertex;
struct node *next;
}node;
typedef struct graph {
int numVertices;
node **adjLists;
}graph;
其中,graph
表示一个有向图,numVertices
表示图中结点的数目,adjLists
是一个由链表构成的数组,adjLists[i]
表示所有以结点i为起点的边所组成的链表。 node
表示一个结点,vertex
表示结点的编号,next
存储下一个结点的指针。
请编写函数 shortestPath (graph *g, int startVertex, int targetVertex)
,其中, g
是一个有向图,startVertex
是广度优先搜索的起点,targetVertex
是停止搜索的目标点。该函数应该返回从起点到目标点的最短路径。如果起点和目标点之间不存在路径,则返回一个空链表。
该题考查的是广度优先搜索算法,具体思路如下:
下面是该题的代码实现,注释详尽,方便理解。代码片段如下,完整代码请见 github。
// 创建一个新的结点
node *createNode(int v) {
node *newNode = malloc(sizeof(node));
newNode->vertex = v;
newNode->next = NULL;
return newNode;
}
// 创建一个有向图
graph *createGraph(int vertices) {
graph *g = malloc(sizeof(graph));
g->numVertices = vertices;
// 创建邻接表
g->adjLists = malloc(vertices * sizeof(node *));
int i;
for (i = 0; i < vertices; i++) {
g->adjLists[i] = NULL;
}
return g;
}
// 添加边
void addEdge(graph *g, int src, int dest) {
// 创建一个新的边
node *newNode = createNode(dest);
// 在链表头部插入新的结点
newNode->next = g->adjLists[src];
g->adjLists[src] = newNode;
}
// 释放内存
void freeGraph(graph *g) {
if (g) {
if (g->adjLists) {
int v;
for (v = 0; v < g->numVertices; v++) {
if (g->adjLists[v]) {
free(g->adjLists[v]);
}
}
free(g->adjLists);
}
free(g);
}
}
// BFS搜索函数
node *BFS(graph *g, int startVertex, int targetVertex) {
// 创建一个结构体数组用于跟踪已经访问过的结点及其父结点。
struct queueItem {
int vertex; // 结点的编号
int parentVertex; // 该结点的父结点
};
// 创建一个队列,用于存储每个节点的编号
queue *q = createQueue();
// 初始化visited[]和parents[]数组
int i;
struct queueItem visited[g->numVertices];
for (i = 0; i < g->numVertices; i++) {
visited[i].vertex = false;
visited[i].parentVertex = -1;
}
// 从起点开始,将其加入队列
visited[startVertex].vertex = true;
visited[startVertex].parentVertex = -1;
enqueue(q, startVertex);
// 执行 BFS,并在每个节点上跟踪其父结点
while (!isEmpty(q)) {
// 取出队首结点
int currentVertex = dequeue(q);
// 访问当前结点的所有邻居
node *temp = g->adjLists[currentVertex];
while (temp) {
int adjVertex = temp->vertex;
// 如果该结点未被访问过,将其加入队列,并将其父结点设置为当前结点
if (!visited[adjVertex].vertex) {
visited[adjVertex].vertex = true;
visited[adjVertex].parentVertex = currentVertex;
enqueue(q, adjVertex);
}
// 如果邻居节点是目标节点,我们就从目标节点开始回溯父结点指针直到起点
if (adjVertex == targetVertex) {
node *path = createNode(targetVertex);
int parentVertex = visited[targetVertex].parentVertex;
while (parentVertex != -1) {
node *newNode = createNode(parentVertex);
newNode->next = path;
path = newNode;
parentVertex = visited[parentVertex].parentVertex;
}
// 释放队列并返回路径
freeQueue(q);
return path;
}
// 继续遍历邻居节点
temp = temp->next;
}
}
// 释放队列,未找到目标点,返回空链表
freeQueue(q);
return createNode(-1);
}
// 寻找最短路径的函数
node *shortestPath(graph *g, int startVertex, int targetVertex) {
return BFS(g, startVertex, targetVertex);
}
该题考查的是图的广度优先搜索算法的应用。广度优先搜索是一种最基础的图搜索算法,用于寻找两个结点之间的最短路径。该算法的核心思想是从起点开始逐步向外扩展,直到找到目标结点为止。通过此题,我们可以更加深入地理解图的广度优先搜索算法,并掌握使用C语言实现该算法的技巧。