【发布时间】:2012-04-02 15:58:36
【问题描述】:
我在 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 下午 2 点
上述算法实际上不起作用。 deroby 指出,将 9 作为孩子添加到 7 将由算法传递,但不应该传递。问题是只要节点不重复,将具有子节点的节点添加到另一个节点就会成功——它不会验证子节点。
【问题讨论】:
-
只是为了确定我理解正确:因此在示例中将 9 作为孩子添加到 7 就可以了吗?
-
将 9 作为孩子添加到 7 违反了我想要做的事情,因为 9 的所有孩子最终都会重复,但使用上面的算法并没有发现它。因此,如果您要添加叶节点,上述算法似乎可以正常工作,但如果您尝试添加具有子节点的节点,它不会验证子节点。
-
当您说“节点 2”时,您的意思是“节点表中 Id(列)值为 2 的行”吗?
-
是的,所有的数字都是指节点ID。
-
我想知道解决方案是否有问题-您要解决什么问题?
标签: sql-server tree