用队列实现拓扑排序(致敬京东面试)
题目:
在一个有向无回路图G=(V,E)上,执行拓扑排序的另一种方法是重复地寻找一个入度为0的顶点,将该点输出,并将该顶点及其所有的出边从图中删除。解释如何来实现这一想法,才能使得它的运行时间为O(V+E)。如果G中包含回路的话,这个算法在运行时会发生什么?
思考:
初始时,所有入度为0的顶点入队列
while队列不为空,作以下处理:
取队列头结点,并出队列
处理以头结点为起点的所有的边,将边的终点的入度-1
若入度减为0,则入队列
代码:
#include <iostream> #include <queue> using namespace std; #define N 10 //边结点结构 struct Edge { int start;//有向图的起点 int end;//有向图的终点 Edge *next;//指向同一个起点的下一条边 int type;//边的类型 Edge(int s, int e):start(s),end(e),next(NULL){} }; //顶点结点结构 struct Vertex { int id; Edge *head;//指向以该顶点为起点的下一条边 int degree; Vertex(int i):head(NULL),degree(0),id(i){} }; //图结构 struct Graph { Vertex *V[N+1];//N个顶点 Graph() { int i; for(i = 1; i <= N; i++) V[i] = new Vertex(i); } ~Graph() { int i; for(i = 1; i <= N; i++) delete V[i]; } }; queue<int> Q; int time = 0; //插入边 void InsertEdge(Graph *G, Edge *E) { //如果没有相同起点的边 if(G->V[E->start]->head == NULL) G->V[E->start]->head =E; //如果有,加入到链表中,递增顺序排列,便于查重 else { //链表的插入,不解释 Edge *e1 = G->V[E->start]->head, *e2 = e1; while(e1 && e1->end < E->end) { e2 = e1; e1 = e1->next; } if(e1 && e1->end == E->end) return; if(e1 == e2) { E->next = e1; G->V[E->start]->head =E; } else { e2->next = E; E->next = e1; } //插入边的同时,计下每个顶点的入度 G->V[E->end]->degree++; } } //拓扑排序 void Topological(Graph *G) { //队列初始化 while(!Q.empty()) Q.pop(); int i; //将所有入度为0的点入队列 for(i = 1; i <= N; i++) { if(G->V[i]->degree == 0) Q.push(i); } //队列不为空 while(!Q.empty()) { //队列首元素 int t = Q.front(); Q.pop(); //输出 cout<<char(t+'l')<<' '; //处理以头结点为起点的所有的边 Edge *e = G->V[t]->head; while(e) { //将边的终点的入度-1 G->V[e->end]->degree--; //若入度减为0,则入队列 if(G->V[e->end]->degree == 0) Q.push(e->end); e = e->next; } } cout<<endl; } int main() { //构造一个空的图 Graph *G = new Graph; Edge *E; //输入边 int i; char start, end; for(i = 1; i <= 14; i++) { cin>>start>>end; E = new Edge(start-'p', end-'p'); InsertEdge(G, E); //无向图,要加两条边 // E = new Edge(end, start); // InsertEdge(G, E); } //拓扑排序并输出 Topological(G); return 0; }
幸运之神的降临,往往只是因为你多看了一眼,多想了一下,多走了一步。