【问题标题】:Data structure for directed graphs, allowing fast node deletion?有向图的数据结构,允许快速删除节点?
【发布时间】:2011-02-24 05:28:13
【问题描述】:

我需要存储一个有向图(不一定非循环),以便节点删除尽可能快。我不介意存储额外的数据,以便确切知道删除节点时必须走哪些边。

如果我存储一个边列表(作为节点索引对),那么当杀死某个节点 n 时,我必须在整个列表中搜索其源或目标为 n 的边。这对我的申请来说太昂贵了。是否可以通过在节点中存储一些额外的数据来避免这种搜索?

一个想法是让每个节点将其自己的源和目标存储为两个单独的列表。当节点 n 被杀死时,它的列表也被杀死。但是,链接到节点 n 的所有目标/源如何知道更新他们自己的列表(即,从他们的列表中删除已失效的节点)?这将需要一些昂贵的搜索...

可以避免吗?

谢谢。

【问题讨论】:

  • 每个顶点有多少条边(通常)?如果顶点度数较低,您建议的邻接列表搜索可能不会太昂贵,特别是如果邻接列表按某种顶点 ID 排序以允许二进制搜索。请记住,由于已删除的顶点有自己的源和目标列表,因此您将知道任何给定顶点的所有相邻顶点。
  • 通常,一个顶点大约有 10-30 条入边和大约 5-10 条出边。但是一些顶点可能有数百个外边。保持邻接列表排序的想法可能会奏效......不过,这意味着我必须在创建新顶点时花时间对这些列表进行排序。

标签: algorithm graph directed-graph


【解决方案1】:

邻接表和邻接矩阵有两个选择,不用太花哨。前者可能最适合您正在做的事情。要删除一个节点,只需删除该节点所有出边的列表。对于入边,您可以考虑为每个列表保留一个哈希表以进行 O(1) 次查找。

这是一个很好的概述 http://www.algorithmist.com/index.php/Graph_data_structures

【讨论】:

  • 是的,邻接表看起来更好(尽管高效的稀疏矩阵算法也可以工作)。而不是哈希表,尝试会做得更好吗? this post 中的 Verdy_p 建议...
  • 我从未听说过尝试以这种方式使用,而且我无法理解链接中的海报所指的内容。如果您有指向主要来源的链接,我有兴趣看看。
  • 不,我也不知道。我只是想找到如何优化搜索。
【解决方案2】:

我解决了!这是无向图的解法,后面加方向很容易。

在每个顶点中,我都保留了一个特殊的邻接表。它是一个列表(双链接,便于插入/删除),其元素是“槽”:

class Slot {
  Slot prev, next; // pointers to the other slots in the list
  Slot other_end; // the other end of the edge: not a vertex, but a Slot!
  Vertex other_vertex; // the actual vertex at the other end

  void kill() {
    if (next!=null) next.kill(); // recursion
    other_end.pop_out();
  }

  void pop_out() {
    if (next!=null) next.prev = prev;
    if (prev!=null) prev.next = next;
    else other_end.other_vertex.slot_list = next; // in case this slot is the
                                                  // first in its list, I need
                                                  // to adjust the vertex's
                                                  // slot_list pointer.
    // other_end.other_vertex is actually the vertex to which this slot belongs;
    // but this slot doesn't know it, so I have to go around like this.
  }

}

所以基本上每条边都由两个插槽表示,彼此交叉。每个顶点都有一个这样的槽列表。

当一个顶点被杀死时,它会递归地向其插槽列表发送一个“杀死”信号。每个插槽通过销毁其 other_end 来响应(从邻居的列表中优雅地弹出,修补后面的 prev/next 指针)。

这样一个顶点和它的所有边都会被删除而不需要任何搜索。我必须付出的代价是内存:我必须保留 4 个指针(prev、next、vertex 和 other_end),而不是 3 个指针(用于常规双链接邻接表的 prev、next 和 vertex)。

这是基本思想。对于有向图,我只需要以某种方式区分 IN 插槽和 OUT 插槽。可能通过将每个顶点的邻接列表分成两个单独的列表:IN_slot_list 和 OUT_slot_list。

【讨论】:

  • 这个例子使用了什么编程语言?对我来说它看起来像 Java - 这是正确的吗?
猜你喜欢
  • 2015-06-16
  • 2012-02-25
  • 1970-01-01
  • 1970-01-01
  • 2017-07-03
  • 2011-07-09
  • 2018-06-12
  • 2019-04-23
  • 1970-01-01
相关资源
最近更新 更多