让我重申这个问题,只是为了确保我理解正确。你有节点和两种关系 - 箭头和垂直线。然后给定一个节点 N,您希望使用以下递归规则生成节点的子集 S(N):
- 规则 0:N 在 S(N) 中。
- 规则 1:如果节点 M 通过垂直关系与 N 连接,则它也在 S(N) 中。
- 规则 2:如果节点 M 位于 S(N) 中,则任何箭头指向 M 的节点也位于 S(N) 中。
集合 S(N) 是满足这些规则的最小节点集。
你给出的N是Y的例子似乎证实了这些规则。
但是,还有另一套不同但(至少对我来说)更自然的规则,其中上面的规则1被替换为
- 规则 1':如果节点 M 在 S(N) 中,则通过垂直关系与 M 连接的任何节点也位于 S(N) 中。
在续集中,我将假设您的示例确认了规则0,1,2,但类似的方法可用于规则0,1',2或任何修改。
另外,我知道您的表格在第7行中有一个错误,应该是:
7 B2 B B1,B3 (not B2,B3)
现在来看看建议的解决方案。
我首先会稍微修改一下您的表结构:由于“id”是您的主键,因此外键的规则是指向相关条目的主键。也就是说,在你的情况下,我会用“node_name”或类似的东西替换“node_id”,只是为了不与“id”混淆,并将“node_parent_id”和“cross_ref”的条目替换为它们的“id”。例如,行号 15 将如下所示:
15 'Y' [13] [14,16]
或者,如果您出于可读性原因而更喜欢,则可以使用A,B,X,Y等作为主键,前提是它们是唯一的,当然,您的表将保持不变,除了不需要的“id”字段。我将假设第一种情况,但您可以通过简单的替换将其适应第二种情况。
就桌子而言,这就是您所需要的。
现在,您需要一个递归函数来为每个给定的节点 N 生成子图 S(N)。
我将集合 S 实现为其节点的所有 “id” $set数组。然后,我将定义两个函数 - 一个用于最初基于规则 1,2 扩展集合,另一个函数随后仅基于规则 2 扩展集合。
为简单起见,我将假设您的表作为关联数组$rows行导入内存,这样$rows[$id]表示“id”等于$id的行作为关联数组。所以
$rows[15] = array('id'=>15,
'node_name'=>'Y',
'node_parent_id'=>array(13),
'cross_ref'=>array(14,16)
);
以下是这些函数的代码:
function initial_expand_set ($node_id) {
global($rows); // to access the array from outside
$set = array($node_id); // Rule 0
$row = $rows[$node_id]; // Get current Node
$parents = $row['node_parent_id']; // Get parents of the Node
$set = $parents ? array_merge ($set, $parents) : $set; // Join parents to the set
$vert_brothers = $row['cross_ref']; // Get vertical relations
$set = $vert_brothers ? array_merge ($set, $vert_brothers) : $set;
$set = expand_set($set); // Recursive function defined below
return $set;
}
递归函数:
// iterate over nodes inside the set, expand each node, and merge all together
function expand_set (array $set) {
global($rows);
$expansion = $set; // Initially $expansion is the same as $set
foreach ($set as $node_id) {
$row = $rows[$node_id]; // Get Node
// Get the set of parents of the Node - this is our new set
$parents = $row['node_parent_id']; // Get parents of the Node
// apply the recursion
$parents_expanded = expand_set($parents);
// merge with previous expansion
$expansion = array_merge($expansion, $parents_expanded);
}
// after the loop is finished getting rid of redundant entries
// array_unique generates associative array, for which I only collect values
$expansion = array_values( array_unique($expansion) );
return $expansion;
}
希望它对您有用。:)
如果需要任何进一步的细节或解释,我将很乐意为您提供帮助。
PS. 对于读者中的学者,请注意,我在'('之前使用空格作为函数定义,而没有空间用于函数调用,正如Douglas Crokford所建议的那样。