有向无环图讲解及模板(C++代码)
一、有向无环图
一个无环的有向图称做有向无环图(Directed Acyclic Graph)。简称DAG 图。
在图论中,如果一个有向图无法从某个顶点出发经过若干条边回到该点,则这个图是一个有向无环图(DAG图)。
因为有向图中一个点经过两种路线到达另一个点未必形成环,因此有向无环图未必能转化成树,但任何有向树均为有向无环图。
使用有向无环图解题时,要先判断是否是有向无环题。如果任务x必须在任务y之前完成:x→y,而y→z。也就是说一般在涉及优先级限制的问题时,使用有向无环图的方法。
注意与并查集进行区分。
关于并查集模板:https://blog.csdn.net/qq_41687938/article/details/112850265
二、有向无环图求解过程
做有向无环图题的时候,一共就三步:
1.根据题目意思及输入,理清两节点间的指向关系,构建由前指向后的有向无环图。
也就是构建一个map,key为当前节点,val数组为当前节点指向的那些节点。
2.根据有向无环图,统计每个节点出现的次数,因为有的节点已经出现过,但还是可能由其他指向路径的节点再指回来,在输出的时候需要遍历其最后出现的地方,所以要记一下它出现的次数。
其中,头节点的次数记为-1,并将头节点保存起来,方便接下来的遍历。
3.因为有向无环图的输出一般都有要求按大小关系输出(本文按升序输出!),也就是构建一个优先队列来完成节点输出。每遍历一个节点就将其所指向的节点压入队列中,实现了某节点的下一层与当前节点层的其他节点的比较。并将遍历到的节点输出。直到队列中所有节点输出。
2.1 具体看题:
一个完整的软件项目往往会包含很多由代码和文档组成的源文件。编译器在编译整个项目的时候,可能需要按照依赖关系来依次编译每个源文件。比如,A.cpp 依赖 B.cpp,那么在编译的时候,编译器需要先编译 B.cpp,才能再编译 A.cpp。 假设现有 0,1,2,3 四个文件,0号文件依赖1号文件,1号文件依赖2号文件,3号文件依赖1号文件,则源文件的编译顺序为 2,1,0,3 或 2,1,3,0。现给出文件依赖关系,如 1,2,-1,1,表示0号文件依赖1号文件,1号文件依赖2号文件,2号文件没有依赖,3号文件依赖1号文件。请补充完整程序,返回正确的编译顺序。注意如有同时可以编译多个文件的情况,按数字升序返回一种情况即可,比如前述案例输出为:2,1,0,3
输入例子1:
"1,2,-1,1"
输出例子1:
"2,1,0,3"
三、C++代码模板:
class Solution {
public:
string compileSeq(string input) {
//首先完成有向无环图的构建
//统计图中各节点的个数 并标记头节点为-1
//使用优先队列 按照先小后大的顺序遍历输出节点值
int len = input.size();
/*********构建有向无环图(指向关系)*********/
map<int, vector<int>> mp;//first为先 second为后, 也就是second依赖于first
string tmp;
int idx = 0;
for(auto& s:input){
if(s != ',')
tmp += s;
else{
mp[stoi(tmp)].push_back(idx++);
string().swap(tmp);//清空string
}
}
if(!tmp.empty())
mp[stoi(tmp)].push_back(idx++);
/**********统计各节点个数 并保存头节点***********/
vector<int> indexcount(len, 0);//统计各节点个数
priority_queue<int, vector<int>, greater<>> pq;//保存节点
for(auto& m:mp){
if(m.first == -1){
for(auto& a:m.second){
pq.push(a);
indexcount[a] = -1;
}
}else{
for(auto& a:m.second)
++indexcount[a];
}
}
/************根据指向关系遍历图 并按照优先队列输出结果********/
vector<int> ans;
while(!pq.empty()){
int node = pq.top();
pq.pop();//输出了就需要从优先队列中弹出
ans.push_back(node);
for(auto& m:mp[node]){
if(--indexcount[m] == 0)//如果该节点是最后一次在图中出现 则放入队列中
pq.push(m);
}
}
/***********输出结果************/
string res;
for(auto& i:ans){
res += to_string(i);
res.push_back(',');
}
if(!res.empty())
res.pop_back();
return res;
}
};
目前已整理十万字的C/C++、嵌入式常见面试题!!!!还在持续更新中!!! 这个专栏写完了,再po上自己亲手敲的笔试编程题整理。