简体   繁体   English

C ++模板图类,递归

[英]C++ Template Graph Class, Recursion

I'm trying to code a graph class in C++, implemented as an adjacency list. 我正在尝试用C ++编写一个图形类,实现为邻接表。 I am trying to make this class a template, so that node and edges can contain an arbitrary payload. 我试图使此类成为模板,以便节点和边缘可以包含任意有效负载。 It is a simple idea, but I am getting confused about how the language might support this. 这是一个简单的想法,但是我对语言可能如何支持这一点感到困惑。

I want to be able to declare a graph as follows: 我希望能够声明如下图:
graph<int, int> or graph<int, int>
graph<string,double> et.c. graph<string,double>

First Idea, working backwards from a graph declaration: 第一个想法,从图声明中倒退:

template<class ntype, class etype>
class node {

    class edge {
    etype payload;
        node<ntype, etype>* to;
        node<ntype, etype>* from;
    }   

    ntype payload;
    vector< edge<> > incoming; // How to avoid recursion ???
    vector< edge<> > outgoing; // ????
}

template<class ntype, class etype>
class graph {

    vector< node<ntype, etype> > nodes;
}

I am getting terribly confused about the recursion involved, the scope of template arguments etc. I've tried looking at nested classes, typename vs. template, other questions, but it is not making it any clearer. 我对涉及的递归,模板参数的范围等感到非常困惑。我尝试查看嵌套类,类型名与模板,其他问题,但并没有使其更加清晰。 It would seem that C and void pointers are the absolute best way to code this now. 似乎C和void指针是现在对此进行编码的绝对最佳方法。 Any help or references are greatly appreciated. 任何帮助或参考,不胜感激。

I would first start by declaring the edge class outside the node class. 首先,我将在节点类之外声明边缘类。 I do not see an advantage for making it a nested class in this case. 在这种情况下,我看不出使它成为嵌套类的优势。 Actually, nested classes bring more cons than pros, in general. 实际上,一般来讲,嵌套类带来的弊端要大于优点。 This question have good explanations on why. 这个问题对为什么有很好的解释。

As for the design of your graph class, I would suggest using one template parameter to represent the payload (any arbitrary data type you may want the graph nodes to carry), and a second one to represent the weight (any numerical value, such as int , float , double , long , etc). 至于图形类的设计,我建议使用一个模板参数表示有效负载(您可能希望图形节点携带的任意数据类型),并使用第二个模板参数表示权重(任何数字值,例如intfloatdoublelong等)。

Some graph use-cases might not care whether the graph is weighted or not, so in those cases you could just ignore the weight field, and do not use it (leaving it to some default value, like 0, would be a good practice). 一些图形用例可能不在乎图形是否加权,因此在这些情况下,您可以忽略权重字段,而不使用它(将其保留为默认值,例如0,这是一个好习惯) 。 I also recommend using an std::list instead of an std::vector to hold the nodes, so that in case you need to add many nodes to your graph, no memory reallocation would happen. 我还建议使用std::list而不是std::vector来保存节点,以便在需要向图中添加许多节点的情况下,不会发生内存重新分配。

Considering the above, the graph class would look something like the following. 考虑到上述情况,图类将类似于以下内容。 Note I use D for the data field (payload), and W for the weight field. 注意我将D用作数据字段(有效负载),将W用作权重字段。

DirectedGraph.h DirectedGraph.h

template <class D, class W> class DirectedGraph{

public: 

    DirectedGraph(){/*...*/}
    ~DirectedGraph(){/*...*/}

    /*Pushes a node into this graph, which will have no connections initially.*/
    void push(GraphNode<D, W> *node){/*...*/}

    /*Erases a node from this graph.*/
    void erase(GraphNode<D, W> *node){/*...*/}

    std::list<GraphNode<D, W>> &getNodes(){/*...*/}

private:

    /*This is the master list holding all nodes of this graph.*/
    std::list<GraphNode<D, W>> nodes;

};

The node and edge (which I called 'Neighbor) classes would then look something like this: 节点和边缘(我称为“邻居”)类将如下所示:

GraphNode.h GraphNode.h

/*Forward declaration of the edge structure (see below).*/
template <class D, class W> struct Neighbor;

template <class D, class W> struct GraphNode{

public:

    GraphNode(D data) : data(data) {}
    ~GraphNode(){}

    /*Some data this node element will hold*/
    D data;

    /*Adds an outgoing connection.*/
    void addConnection(Neighbor<D, W> neighbor){ /*...*/}

    /*You may also want to develop a 'removeConnectoin' method here.*/
    /*...*/

    /*Nodes that this one is connected to. Since this actually represents
    a connection (an edge), the struct 'Neighbor' is used, which encapsulates
    a pointer to a 'GraphNode' as well as the weight of the edge.*/
    std::list<Neighbor<D, W>> neighbors;

    /*Nodes that connect to this one. These are the incoming connections. Note
    that this is not of type 'Neighbor' because it does not represente an edge.
    It is just a record of which nodes have an outgoing connection to this one.*/
    std::list<GraphNode<D, W>*> parents;
};


/*This struct represents an edge*/
template <class D, class W> struct Neighbor{
    Neighbor<D, W>(GraphNode<D, W> *node, W weight) : node(node), weight(weight){}
    GraphNode<D, W> *node;
    W weight;
};

One thing to be noticed here is that the only reason for using the Neighbor class is so that we can represent a weight for the edge. 这里要注意的一件事是,使用Neighbor类的唯一原因是可以表示边缘的权重。 If the graph you are going to use will always be unweighted, you could just replace the std::list of Neighbor<D, W> for an std::list of GraphNode<D, W>* . 如果您图形要使用将永远不加权的,你可以只更换std::listNeighbor<D, W>一个std::listGraphNode<D, W>* You would also be able to remove the template parameter W from your implementation. 您还可以从实现中删除模板参数W

Oh, and of course the graph header file has to include the node one. 哦,当然,图形头文件必须包含节点一。

I hope this serves as a good start point. 我希望这可以作为一个良好的起点。 Any questions, please let me know. 有任何疑问,请让我知道。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM