C++模板图类,递归

C++ Template Graph Class, Recursion

本文关键字:递归 C++      更新时间:2023-10-16

我正在尝试用C++编写一个图类,实现为邻接列表。我正试图使这个类成为一个模板,这样节点和边就可以包含任意的有效负载。这是一个简单的想法,但我对语言如何支持这一点感到困惑。

我希望能够如下声明一个图:
graph<int, int>
graph<string,double>

第一个想法,从图形声明向后工作:

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;
}

我对所涉及的递归、模板参数的范围等感到非常困惑。我试过研究嵌套类、typename与template以及其他问题,但这并没有让它变得更清楚。现在看来,C和void指针是编写代码的最佳方式。如有任何帮助或参考,我们将不胜感激。

我首先在节点类之外声明边缘类。在这种情况下,我看不出将其作为嵌套类有什么好处。实际上,通常情况下,嵌套类带来的缺点多于优点。这个问题很好地解释了为什么。

至于图类的设计,我建议使用一个模板参数来表示有效载荷(您可能希望图节点携带的任何任意数据类型),并使用第二个模板参数表示权重(任何数值,如intfloatdoublelong等)。

有些图形用例可能不关心图形是否加权,因此在这些情况下,您可以忽略权重字段,而不使用它(将其保留为一些默认值,如0,将是一种很好的做法)。我还建议使用std::list而不是std::vector来保存节点,这样在需要向图中添加许多节点的情况下,就不会发生内存重新分配。

考虑到上面的内容,图类看起来如下所示。注意,我使用D作为数据字段(有效载荷),使用W作为权重字段。

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;
};

节点和边缘(我称之为"邻居")类看起来像这样:

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;
};

这里需要注意的一点是,使用Neighbor类的唯一原因是,我们可以表示边的权重。如果要使用的图总是未加权的,则可以将Neighbor<D, W>std::list替换为GraphNode<D, W>*std::list。您还可以从实现中删除模板参数W

哦,当然,图形头文件必须包括节点一。

我希望这是一个良好的开端。有任何问题,请告诉我。