我在SQL中有一个非常简单的树实现:
CREATE TABLE [dbo].[Nodes] (
[Id] [int] IDENTITY(1,1) NOT NULL,
[Name] [nvarchar](max) NULL
);
CREATE TABLE [dbo].[NodeNodes] (
[ParentNodeId] [int] NOT NULL,
[ChildNodeId] [int] NOT NULL
);
我的树实现是这样的,节点可以有多个父节点。这样,用户就可以创建将常用节点组合在一起的自定义树。例如:
1 8 9
/ \ / \ / \
2 3 4 7 2 6
/ \ / \ / \
4 5 6 7 4 5
Node | Parents | Children
---------------------------
1 | - | 2,3
2 | 1,9 | 4,5
3 | 1 | 6,7
4 | 2,8 | -
5 | 2 | -
6 | 3,9 | -
7 | 3,8 | -
8 | - | 4,7
9 | - | 2,6
因此有三个树由三个节点指示,没有父节点。当用户将节点添加为另一个节点的子节点时,我的问题是验证潜在的关系。我希望没有节点在同一棵树中出现两次。例如,将节点2添加为节点6的子节点应该会失败,因为这会导致节点2在1的树和9的树中出现两次。我在编写一个有效的算法时遇到了麻烦。
我的第一个想法是找到预期父母的所有根,平整根的树以获得每个树的一个节点列表,然后将这些列表与预期子项相交,并且最后仅在所有结果相交的列表为空。继续这个例子,我将采取以下步骤:
1) Trace prospective parent through all parents to roots:
6->3->1
6->9
2) Flatten trees of the roots
1: {1,2,3,4,5,6,7}
9: {2,4,5,6,9}
3) Intersect lists with the prospective child
1: {1,2,3,4,5,6,7}^{2} = {2}
9: {2,4,5,6,9}^{2} = {2}
4) Only pass if all result lists are empty
1: {2} != {} ; fail
9: {2} != {} ; fail
此过程有效,但需要将整个树放入内存中。我有一些拥有20,000多个节点的树,这需要差不多一分钟才能运行。这种表现不是100%的交易破坏者,但它非常令人沮丧。有没有更有效的算法来做到这一点?
编辑4/2 2pm
上述算法实际上并不起作用。 deroby指出,将7作为子项添加到7将由算法传递,但不应该。问题是只要不重复节点,将子节点添加到另一个节点就会成功 - 它不会验证子节点。
答案 0 :(得分:0)
一年后,我偶然发现了自己的问题,我决定加入我的解决方案。事实证明我忘记了我的基本数据结构。 我最初认为的是一棵简单的树实际上是一张有向图,而我正在测试的是一个循环。看到循环检测是一种非常常见的事情,互联网上应该有很多解决方案和讨论。有关示例,请参阅Best algorithm for detecting cycles in a directed graph。