题目:假设在一个工厂里,已经有一个设计图,如同给出的示意图显示的生产过程的所有步骤,来制造解药,比如说一个从“添加硝酸”到“剧烈摇晃”的剪头,意味着硝酸需要再摇晃之前加入,如果一个步骤的顺序乱了,解药的制备就失败了,甚至会更糟糕,没有任何循环引用,步骤A对应步骤B或步骤B最终需要对应步骤A,需要将这个复杂的设计图表,转变为一系列的步骤,这就是工厂里工序的顺序,一旦输入,工厂将会根据指示自我重新组装,我的机器人拥有在表中存储信息的能力,请问该给机器人下达什么指令,完成正确的顺序?
涉及的编程基础原理
这个问题考察的是拓扑排序(Topological Sorting)和图论的基本知识。
拓扑排序用于对有向无环图(DAG)进行排序,使得对于图中的每一条有向边 u→vu→v,顶点 uu 在排序中出现在顶点 vv 之前。这个问题可以用来模拟工厂中的生产步骤,以确保步骤按正确的顺序执行。
图论基础
图论是数学和计算机科学中的一个分支,研究图的性质和应用。图由顶点(节点)和边(连接顶点的线)组成。根据边的方向性,图可以分为有向图和无向图。
有向图:边有方向,从一个顶点指向另一个顶点。
无向图:边没有方向,连接两个顶点。
拓扑排序
拓扑排序是一种针对有向无环图(DAG, Directed Acyclic Graph)的排序算法,将图中的顶点排成一个线性序列,使得对于每一条有向边 (u,v)(u,v),顶点 uu 在顶点 vv 之前出现。
拓扑排序在许多应用中很有用,例如任务调度、编译器中的依赖解析等。
1,图论基础:理解有向图、无环图和拓扑排序。
2,数据结构:使用队列和邻接表来存储图和进行排序。
3,算法设计:实现拓扑排序算法(Kahn's Algorithm)。
程序编写
#include <stdio.h> // 包含标准输入输出库,用于使用printf函数输出信息
#include <stdlib.h> // 包含标准库,用于动态内存分配(如malloc)
#define MAX 100 // 定义一个宏MAX,表示图中最大顶点数为100
// 图的邻接表表示
struct Graph {
int numVertices; // 图中的顶点数
int* adjLists[MAX]; // 邻接表,用于存储每个顶点的邻接顶点
int inDegree[MAX]; // 存储每个顶点的入度
};
// 创建图
struct Graph* createGraph(int vertices) {
struct Graph* graph = (struct Graph*)malloc(sizeof(struct Graph)); // 动态分配内存给图结构
graph->numVertices = vertices; // 初始化图的顶点数
for (int i = 0; i < vertices; i++) { // 遍历每个顶点
graph->adjLists[i] = (int*)malloc(vertices * sizeof(int)); // 为每个顶点的邻接表分配内存
graph->inDegree[i] = 0; // 初始化每个顶点的入度为0
for (int j = 0; j < vertices; j++) { // 初始化邻接表
graph->adjLists[i][j] = 0; // 初始化邻接表中的每个值为0
}
}
return graph; // 返回创建的图
}
// 添加边
void addEdge(struct Graph* graph, int src, int dest) {
graph->adjLists[src][dest] = 1; // 在邻接表中添加边src -> dest
graph->inDegree[dest]++; // 增加目标顶点的入度
}
// 拓扑排序
void topologicalSort(struct Graph* graph) {
int queue[MAX], front = 0, rear = 0; // 定义队列和其前后指针,用于存储入度为0的顶点
int sortedOrder[MAX], index = 0; // 存储排序后的顶点顺序和当前索引
// 将所有入度为0的顶点加入队列
for (int i = 0; i < graph->numVertices; i++) {
if (graph->inDegree[i] == 0) { // 如果顶点的入度为0
queue[rear++] = i; // 将顶点加入队列
}
}
while (front < rear) { // 当队列不为空时
int current = queue[front++]; // 从队列中取出一个顶点
sortedOrder[index++] = current; // 将顶点加入排序结果
for (int i = 0; i < graph->numVertices; i++) { // 遍历当前顶点的邻接顶点
if (graph->adjLists[current][i] == 1) { // 如果存在边current -> i
graph->inDegree[i]--; // 减少目标顶点的入度
if (graph->inDegree[i] == 0) { // 如果目标顶点的入度变为0
queue[rear++] = i; // 将其加入队列
}
}
}
}
// 检查是否存在环
if (index != graph->numVertices) { // 如果排序结果中的顶点数不等于图中的顶点数
printf("图中存在环,无法进行拓扑排序。\n"); // 输出错误信息
return; // 结束函数
}
// 输出排序后的顺序
printf("拓扑排序的顺序为:\n"); // 输出提示信息
for (int i = 0; i < index; i++) { // 遍历排序结果
printf("%d ", sortedOrder[i]); // 输出每个顶点
}
printf("\n"); // 换行
}
int main() {
int vertices = 6; // 假设有6个步骤
struct Graph* graph = createGraph(vertices); // 创建一个包含6个顶点的图
// 根据图示添加边
addEdge(graph, 0, 2); // 添加边0 -> 2
addEdge(graph, 0, 3); // 添加边0 -> 3
addEdge(graph, 1, 3); // 添加边1 -> 3
addEdge(graph, 1, 4); // 添加边1 -> 4
addEdge(graph, 2, 5); // 添加边2 -> 5
addEdge(graph, 3, 5); // 添加边3 -> 5
addEdge(graph, 4, 5); // 添加边4 -> 5
topologicalSort(graph); // 对图进行拓扑排序
return 0; // 程序正常结束
}