C++ 在我的图形实现中查找边数并执行拓扑排序
在过去的几天里,我一直在研究一个图形实现。所有这些对我来说都是全新的,我被困在实现的两个部分上。我正在从输入文件中实现课程的有向图。从文件中,我可以确定哪些课程是其他课程的预科。然后,我创建一个有向图,其中课程作为节点,边连接预先要求的课程。我还想找到节点和边的总数,并对图进行拓扑排序(稍后我将为边添加权重)。这是我的实现 有向图C++ 在我的图形实现中查找边数并执行拓扑排序,c++,graph,graph-algorithm,topological-sort,C++,Graph,Graph Algorithm,Topological Sort,在过去的几天里,我一直在研究一个图形实现。所有这些对我来说都是全新的,我被困在实现的两个部分上。我正在从输入文件中实现课程的有向图。从文件中,我可以确定哪些课程是其他课程的预科。然后,我创建一个有向图,其中课程作为节点,边连接预先要求的课程。我还想找到节点和边的总数,并对图进行拓扑排序(稍后我将为边添加权重)。这是我的实现 有向图 class vertex{ public: typedef std::pair<int, vertex*> ve; std::vecto
class vertex{
public:
typedef std::pair<int, vertex*> ve;
std::vector<ve> adjacency;
std::string course;
vertex(std::string c){
course = c;
}
};
class Digraph{
public:
void addVertex(std::string&);
void addEdge(std::string& from, std::string& to, int cost);
typedef std::map<std::string, vertex *> vmap;
vmap work;
int getNumVertices();
int getNumEdges();
void getTopoSort();
};
类顶点{
公众:
typedef-std::对ve;
向量邻接;
弦课程;
顶点(标准::字符串c){
课程=c;
}
};
类有向图{
公众:
void addVertex(std::string&);
无效追加(标准::字符串和起始,标准::字符串和起始,整数成本);
typedef std::map vmap;
vmap工作;
int getNumVertices();
int getNumEdges();
void getTopoSort();
};
有向图
void Digraph::addVertex(std::string& course){
vmap::iterator iter = work.begin();
iter = work.find(course);
if(iter == work.end()){
vertex *v;
v = new vertex(course);
work[course] = v;
return;
}
}
void Digraph::addEdge(std::string& from, std::string& to, int cost){
vertex *f = (work.find(from)->second);
vertex *t = (work.find(to)->second);
std::pair<int, vertex *> edge = std::make_pair(cost, t);
f->adjacency.push_back(edge);
}
void有向图::addVertex(std::string和course){
迭代器iter=work.begin();
iter=工作。查找(课程);
if(iter==work.end()){
顶点*v;
v=新顶点(路线);
工作[课程]=v;
返回;
}
}
void-Digraph::addEdge(std::string&from,std::string&to,int-cost){
顶点*f=(work.find(from)->秒);
顶点*t=(work.find(to)->秒);
std::pair edge=std::make_pair(成本,t);
f->邻接。向后推(边);
}
查找节点数很容易,只需返回
work.size
。我已经确认这是正常工作。我不知道如何返回图形中的边数。看起来很简单,但我尝试的一切都不起作用。其次,我完全不知道如何在这个图上执行拓扑排序。非常感谢您的帮助。一个简单的方法是遍历图形中的所有顶点,将它们的邻居计数相加,然后除以二:
int Digraph::getNumEdges(){
int count = 0;
for (const auto & v : work) {
count += v.second->adjacency.size();
}
return count / 2;
}
要使用基于范围的for循环,需要使用c++11。使用命令行上的g++将是--std=c++11
编辑:
我刚刚意识到你有一个有向图,你可能想为每个方向数一个。在这种情况下:不要被二除
int Digraph::getNumEdges(){
int count = 0;
for (const auto & v : work) {
count += v.second->adjacency.size();
}
return count;
}
首先,对于边的数量,在构建图时直接计算边会更简单(只需在有向图类中添加一个计数器,并在每次添加边时增加它…) 对于拓扑排序,首先我有一个问题:您的边是从prereqs到Dependent courses?也就是说,如果a是B的预请求,那么您有一个链接a->B?如果不是这样,则需要反转图形 您可以使用main算法来构建拓扑排序:一个基于简单的DFS(),另一个依赖于顶点的度数()(本例中的课程) 通常,您需要验证您的图形不包含任何循环,如果您的数据是一致的,通常情况下就是这样
让我们考虑基于DFS的算法:DFS遍历给定根中的每个顶点,边出现边。我们可以很容易地证明,顶点最后相遇的顺序形成了一个反向拓扑顺序。因此,我们所需要的就是在调用当前顶点的后继顶点后,将其推入堆栈
我再次使用C++11为您创建了一个快速而肮脏的实现 首先,将以下内容添加到有向图类: typedef std::unordered_set<vertex*> marks_set;
marks_set marks;
typedef std::deque<vertex*> stack;
stack topo;
void dfs(vertex* vcur);
typedef std::无序集标记集;
标记和设置标记;
typedef std::deque堆栈;
叠加地形;
无效dfs(顶点*vcur);
下面是代码:
void Digraph::dfs(vertex* vcur) {
marks.insert(vcur);
for (const auto & adj : vcur->adjacency) {
vertex* suc = adj.second;
if (marks.find(suc) == marks.end()) {
this->dfs(suc);
} // you can detect cycle in the else statement
}
topo.push_back(vcur);
}
void Digraph::getTopoSort() {
// It should be a good idea to separate this algorithm from the graph itself
// You probably don't want the inner state of it in your graph,
// but that's your part.
// Be sure marks and topo are empty
marks.clear();
topo.clear();
// Run the DFS on all connected components
for (const auto & v : work) {
if (marks.find(v.second) == marks.end()) {
this->dfs(v.second);
}
}
// Display it
for (const auto v : topo) {
std::cout << v->course << "\n";
}
}
void有向图::dfs(顶点*vcur){
标记。插入(vcur);
用于(常数自动和邻接:vcur->邻接){
顶点*suc=调整秒;
if(marks.find(suc)=marks.end()){
这->dfs(suc);
}//您可以在else语句中检测循环
}
地形推回(vcur);
}
void有向图::getTopoSort(){
//将此算法与图形本身分离应该是一个好主意
//你可能不希望它的内部状态出现在你的图表中,
//但那是你的责任。
//确保标记和拓扑为空
标记。清除();
topo.clear();
//在所有连接的组件上运行DFS
用于(施工自动和验证:工作){
if(marks.find(v.second)=marks.end()){
这->dfs(v.second);
}
}
//展示它
用于(常数自动v:topo){
std::cout当然请注意,Digraph::addEdge
可以将一条新边从a添加到B,即使已经有一条边了。谢谢。它工作得很好,而且看起来很简单。你能给我一些关于如何处理拓扑排序的指导吗?