目录
下一篇:图的关键路径
定义:若一个有向图不存在环,则称为有向无环图(Directed Acyclic Graph)
一、描述表达式
对于这个表达式使用二叉树表示
显然红色和绿色部分冗余了
我们可以将这两个冗余部分合并,以减少存储空间
就形成了一个DAG图
其中还有可以合并的部分
全部合并后
在考研题目中
这种题目很容易漏掉某个可合并项
比较好的方法:
练习:
第一步
第二步:自底向上合并同层
二、拓扑排序
AOV网(Activity Vertex Network,用顶点表示活动的网)
用DAG图表示一个工程,顶点表示活动,有向边<Vi,Vj>表示活动Vi必须先于Vj进行,例如
拓扑排序定义:
即找到做事的先后顺序
上面的番茄炒蛋项目的排序
实现步骤:
步骤3中当网中不存在无前驱的顶点说明,该图有回路
有回路则无拓扑排序
代码(伪):
/*
基于邻接矩阵
*/
bool TopologicalSort(Graph G){
//初始化一个栈
//这里可以用队列或者数组代替
//因为只是为了存储入度为0的顶点
InitStack(S);
for(int i=0;i<G.vexnum;i++){
//这里indegree数组是用来存储每个顶点的入度的
//把当前所有入度为0的顶点入栈
if(indegree[i]==0){
push(S,i);
}
}
int count;//用来记录有多少个顶点被排序了
while(!IsEmpty(S)){
Pop(S,i);
//将栈中顶点加入打印数组print
print[count++]=i;
//将所有i指向顶点的入度-1,并将入度减为0的顶点入栈
for(p=G.vertices[i].firstarc;p;p=p->nextarc){
v=p->adjvex;//获取该边指向的顶点
if(!(--indegree[v])){
push(S,v);//入度为0则入栈
}
}
}
//如果计数器小于图中 结点数,说明有回路,排序失败
if(count<G.vexnum)return false;
else return true;
}
时间复杂度:
O(|V|+|E|)
若是邻接矩阵则需要O(|V|^2)
三、逆拓扑排序
就是删除每次都删除出度为0的顶点
代码略:大概就是将indgree数组改为出度数组,没删除一个顶点都把该顶点的入度顶点-1,并判断有没有为0的就入栈
时间复杂度:
使用邻接表会很慢,每次都要遍历整个表来找入度
可以使用逆邻接表,就是邻接表是出度作为链表,则逆邻接表就是用入度作为每个结点的链表
使用邻接矩阵就方便点
逆拓扑排序dfs实现:
利用dfs的特性很好实现,递归到最深处退栈的时候输出
//DFS实现逆拓扑排序
void DFSTraverse(Graph G){
//初始化与处理连通分量
for(v=0;v<G.vexnum;v++){
visited[v]=false;
}
for(v=0;v<G.vexnum;v++){
if(!visited[v]){
DFS(G,v);
}
}
}
void DFS(Graph G,int v){
visited[v]=true;
for(w=FirstNeighbor(G,v);w>=0;w=NextNeighbor(G,v,w)){
if(!visited[w]){
DFS(G,w);
}
}
print(v);//函数返回时(退栈)输出顶点
}
对于有环路,那么一定造成这种情况:某个顶点还在函数调用栈中等待回溯被打印,但是有另一个顶点的下一个邻接顶点是该顶点造成回路,我的解决办法是增加一个递归标记数组,当这个顶点被dfs调用处理的时候将其对应位置设置为1,退栈时还原为0,在处理每个顶点的下一个顶点前都判断一下:下一个顶点的标记数组是否为1即可
类似于如下红线部分
小结: