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,即使已经有一条边了。谢谢。它工作得很好,而且看起来很简单。你能给我一些关于如何处理拓扑排序的指导吗?