删除使用智能指针的有向加权图中节点的边

时间:2016-09-17 14:50:13

标签: c++ shared-ptr directed-graph edges stdset

我是共享指针的新手,我正试图从图中删除一个节点。删除节点时,将删除存储在该节点中的传入和传出边缘。但是,我还需要删除先前连接到已删除节点的节点的传出和传入边缘(分别是已删除节点的传入和传出边缘) - 我称之为链接节点。

下面的代码显示了我的Graph类声明:

template <typename N, typename E> class Graph {

    private:
        struct Node;
        struct Edge;

        struct Node {
            N val_;
            int numEdges_;
            int numIncomingEdges_;
            std::set<std::shared_ptr<Edge>> edges_;
            std::set<std::shared_ptr<Edge>> incomingEdges_;
            Node() {}
            Node(const N x) : val_{x} { numEdges_=0; }
            void printNode(N n);
            ~Node();
            void update();
        };

        struct Edge {
            std::weak_ptr<Node> orig;
            std::weak_ptr<Node> dest;
            E val_;
            Edge(std::shared_ptr<Node> o, std::shared_ptr<Node> d, E x);
            Edge() {};
            void printEdge();
            ~Edge();
        };
..... Some code for node and edge iterators

private:
        std::map< N, std::shared_ptr<Node> > nodes_;

};

要删除节点的类:

template <typename N, typename E>
void Graph<N, E>::deleteNode(const N& node) noexcept {
    auto findNode = nodes_.find(node);
    if (findNode != nodes_.end()) {

        // find the node which has incoming edges into the deleted node and delete its edges
        for (auto edge: findNode->second->incomingEdges_) { // for each edge in node's incomingEdges_

            // get the origin node of incoming edge to deleted node through dest.lock()
            auto originNodeOfIncomingEdge = edge->dest.lock();

            auto nodeVal1 = originNodeOfIncomingEdge->val_;
            std::cout << "Print out value of origin node of incoming edge to deleted node: " << nodeVal1 << std::endl;

            auto findLinkingNode1 = nodes_.find(nodeVal1);
            std::cout << "size of edges_ in linking node before deleting its outgoing edge (which is the incoming edge of deleted node): " << findLinkingNode1->second->edges_.size() << std::endl;

            auto findEdge = findLinkingNode1->second->edges_.find(edge);
            findLinkingNode1->second->edges_.erase(findEdge);

            std::cout << "size of edges_ in linking node after deleting its outgoing edge (which is the incoming edge of deleted node): " << findLinkingNode1->second->edges_.size() << std::endl;
            --findLinkingNode1->second->numEdges_;

        }

... similar code to above for deleting the node that has outgoing edges from deleted node going into it

        findNode->second.reset(); // deletes managed object of the shared_ptr
        nodes_.erase(findNode); // removes the node from the map container

    }
}

所以令我困惑的主要是这部分,我试图从for循环中删除边缘以删除链接节点中的边缘。

            auto findEdge = findLinkingNode1->second->edges_.find(edge);
            findLinkingNode1->second->edges_.erase(findEdge);

但是我一直在收到与shared_ptr相关的错误。首先与指针有关:

test8c(2863,0x7fff78df2000) malloc: *** error for object 0x7ffda2403350: pointer being freed was not allocated
*** set a breakpoint in malloc_error_break to debug
Abort trap: 6

以前,我的代码是findLinkingNode1->second->edges_.erase(edge);而没有findEdge。我能够编译没有任何错误,但边缘没有被删除。

有人可以指导我如何成功删除edge_的边缘? edges_声明为std::set<std::shared_ptr<Edge>> edges_;,如Graph类所示。

1 个答案:

答案 0 :(得分:0)

事物的结构方式,效率不高。你的Node析构函数需要:

  1. 对正在销毁的Edge中的每个Node进行迭代。

  2. 对于每个Edge get()其他Node

  3. 在其他Edge列表中找到相同的Node,然后将其删除。

  4. 如果这是一个频繁的操作,我建议进行以下重构:

    1. Edge暂挂shared_ptr Node s。

    2. Node暂挂weak_ptr Edge s。

    3. 因此,要删除Node,请迭代节点的Edge,然后删除它们。删除所有Edge后,shared_ptr的{​​{1}}超出范围,节点将被销毁。

      如果这是不切实际的,那么重新设计就不那么激烈了:

      1. 为每个Node分配一种唯一的标识符。一个简单的递增计数器就可以了,并且可以自动在Edge的构造函数中处理它。

      2. 使用其唯一标识符引用您的所有Edge,而不是使用Edge std::setshared_ptr s替换它标识符Edgestd::map shared_ptr。在销毁特定Edge时,这会使从Edge删除每个Node变得微不足道。

      3. 不是实现离散标识符,而是可以使用Node的原始指针,Edge作为严格的弱排序比较器,作为每个的即兴唯一标识符。边缘。