我正在占用大约15,000个节点并尝试从它们构建层次结构。节点不能以任何方式排序,并且每个节点都可以拥有无限量的子节点 - 但是父母将始终在他们的孩子之前被馈送到该功能。我的代码适用于N的小值,但最终超过服务器上的最大执行时间,N> 2,000。我不确定是否有更好的方法来做到这一点,但这就是我所拥有的:
function insertNode(&$treeNode, $insertNode) {
if($insertNode['DEPTH'] <= $treeNode['DEPTH']) return false;
if($treeNode['ID'] == $insertNode['PARENT_ID']) {
$treeNode['CHILDREN'][] = $insertNode;
$treeNode['CHILD_COUNT']++;
return true;
}
else {
foreach($treeNode['CHILDREN'] as $key=>$value) {
$found = insertNode($treeNode['CHILDREN'][$key], $insertNode);
if($found) {
$treeNode['CHILD_COUNT']++;
return true;
}
}
}
}
我现在最好的解决方案是将我的递归限制为只构建几千个节点的深度,然后在Javascript中调用每个底部节点的脚本,直到树真正完成。不过,我宁愿能够一气呵成。
答案 0 :(得分:0)
我自己想通了。关键是:
父母将永远在孩子面前接受这项功能
我正在使用的SQL查询(Oracle CONNECT BY
)直接在他们的直接祖先之后打印出每个孩子(以及他们所有的孩子)。例如,数据集:
|id|parent_id|
|A-|---------|
|C-|-----A---|
|Bb|-----B---|
|B-|-----A---|
将通过查询提供:
|id|parent_id|
|A-|---------|
|B-|-----A---|
|Bb|-----B---|
|C-|-----A---|
我按顺序循环遍历查询的每一行,然后为每一行调用递归插入函数。在每个函数调用中,我使用foreach
循环对树中当前节点的每个子节点进行递归调用,直到找到该行的父节点为止:
foreach($treeNode['CHILDREN'] as $key=>$value) {
$found = insertNode($treeNode['CHILDREN'][$key], $insertNode);
if($found) {
$treeNode['CHILD_COUNT']++;
return true;
}
}
但是,因为每个孩子的父母一直是其深度的最新插入项目(因为它是我深度循环的最后一行),所以父母被保证是树的最右边分支中最右边的孩子。不仅循环是不必要的,它实际上是我可能做的最糟糕的事情 - 它是从左到右循环穿过孩子,但是父母一直是最右边分支的最右边的孩子,函数循环遍历的最后一项。 (那是什么,O(N^2)
?)难怪它超时了。
我剪切foreach
并将其替换为end()
来代替。
$lastChild = $treeNode['CHILDREN'];
end($lastChild);
$key = key($lastChild);
$found = insertNode($treeNode['CHILDREN'][$key], $insertNode);
if($found) {
$treeNode['CHILD_COUNT']++;
return true;
}
这大大缩短了执行时间,因为它以指数方式减少了函数每行递归调用自身的次数。它现在在大约6秒钟内工作N = 15,000(在它被限制在30秒之前,低至N = 2,000)。没有节点编号或通过JS延迟加载。