有向无环图DAG

目录

一、描述表达式

二、拓扑排序

三、逆拓扑排序

小结:

下一篇:图的关键路径


定义:若一个有向图不存在环,则称为有向无环图(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即可

类似于如下红线部分

 

 

小结:

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
以下是一个简单的C++ DAG类的构建: ```c++ #include <iostream> #include <vector> using namespace std; class DAG { private: vector<vector<int>> adjList; // 存储图的邻接表 vector<int> inDegree; // 存储每个节点的入度 public: // 构造函数 DAG(int numNodes) { adjList.resize(numNodes); inDegree.resize(numNodes, 0); } // 添加一条有向边 void addEdge(int u, int v) { adjList[u].push_back(v); inDegree[v]++; } // 拓扑排序 vector<int> topologicalSort() { vector<int> result; vector<bool> visited(adjList.size(), false); // 找到所有入度为0的节点 for (int i = 0; i < adjList.size(); i++) { if (inDegree[i] == 0 && !visited[i]) { visited[i] = true; result.push_back(i); // 更新相邻节点的入度 for (int j = 0; j < adjList[i].size(); j++) { int neighbor = adjList[i][j]; inDegree[neighbor]--; } // 重置i,从头开始找入度为0的节点 i = -1; } } return result; } }; int main() { DAG dag(6); dag.addEdge(0, 1); dag.addEdge(1, 2); dag.addEdge(1, 3); dag.addEdge(2, 4); dag.addEdge(3, 4); dag.addEdge(4, 5); vector<int> result = dag.topologicalSort(); for (int i = 0; i < result.size(); i++) { cout << result[i] << " "; } return 0; } ``` 在上面的代码中,我们定义了一个DAG类来表示有向无环图,它包含两个成员变量:邻接表 `adjList` 和每个节点的入度 `inDegree`。我们可以使用 `addEdge` 方法向图中添加一条有向边,并使用 `topologicalSort` 方法执行拓扑排序来得到一个可行的执行顺序。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值