我需要存储有向图(不一定是非循环的),以便尽可能快地删除节点。我不介意存储额外的数据,以便确切地知道删除节点时必须经过哪些边缘。
如果我存储边缘列表(作为节点索引对),那么当杀死某个节点时,我必须在整个列表中搜索源或目标为n的边。这对我的申请来说太贵了。通过在节点中存储一些额外的数据可以避免这种搜索吗?
一个想法是让每个节点存储自己的源和目标,作为两个单独的列表。当节点n被杀死时,它的列表也被杀死。但是,与节点n相关联的所有目标/来源如何知道如何更新自己的列表(即,从其列表中消除已失效的节点)?这需要一些昂贵的搜索......
可以避免吗?
THX。
答案 0 :(得分:4)
你有两个选择,而不是过于花哨的邻接列表和邻接矩阵。前者可能是你正在做的最好的。要删除节点,只需删除该节点的所有外边缘列表。对于in-edge,您可以考虑为O(1)查找的每个列表保留一个哈希表。
这是一个很好的概述 http://www.algorithmist.com/index.php/Graph_data_structures
答案 1 :(得分: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.
}
}
所以基本上每个边都由两个槽代表,彼此交叉指向。每个顶点都有一个这样的插槽列表。
当一个顶点被杀死时,它会在其槽列表中递归地发送一个“kill”信号。每个插槽都会通过销毁其other_end(从邻居列表中匆匆弹出,修复后面的上一个/下一个指针)来响应。
这样就可以删除顶点及其所有边,而无需任何搜索。我需要支付的价格是内存:而不是3个指针(普通,双向链接邻接列表的上一个,下一个和顶点),我必须保留4个指针(prev,next,vertex和other_end)。
这是基本的想法。对于有向图,我只需要以某种方式区分IN插槽和OUT插槽。可能将每个顶点的邻接列表分成两个单独的列表:IN_slot_list和OUT_slot_list。