我有一个存储在DB中的节点层次结构。我选择all,将它们存储在一个数组中,然后迭代它们并在内存中创建一个嵌套数组。
输入如下:
[{name:A},{name:B},{name:X,parent:A},{name:Y,parent:A},{name:C}]
输出如下:
[{name:A,children:[{name:X},{name:Y}]},{B},{C}]
嵌套的深度没有限制。
我遇到的问题是,如果其中一条记录的父引用无效,则无法将其放入层次结构中,并且脚本以无限循环结束,尝试查找父级。
我打赌有一种方法可以告诉我什么时候陷入无限循环。对于记录,当在循环中我意识到没有父项将项目插入时,我将项目推送到数组的末尾,因为父项可能存在于该行中。
我想我应该能够意识到我一遍又一遍地骑着相同的物品?
编辑1 - 代码 这是重要的一点:
$cnt = count($array);
do {
$item = array_shift($array);
if ($this->push($item)) {
$cnt--;
} else {
array_push($array, $item);
}
} while ($cnt > 0);
($ this-> push()是一种尝试查找父项的方法,如果成功,则将$ item插入其层次结构中)
答案 0 :(得分:1)
看起来您正在使用队列(从前面删除,添加到后面)类型 用于存储未处理节点的数据结构。作为节点 插入到输出数据结构中,它们将从队列中删除。如果 节点无法添加到输出中(因为它的“父”没有 已被移动到输出数据结构) 它被重新排队。最终队列应该变空 除非存在“父”不存在的节点(孤儿)。
我想你的算法看起来像
Do While not QueueEmpty()
node = Dequeue() ' Remove from the front
if not AddNodeToTree(node) then Queue(node) 'add to the back
end
其中AddNodeToTree
是成功获取节点的函数
将其添加到输出并返回True
。否则返回False
导致节点回收。
您唯一需要做的就是将一个sentinal节点添加到队列的后面 和一个标志,指示队列中至少消耗了一个节点 在一个完整的循环中通过它。上述算法变为:
set NodeProcessed to False
Queue(SentinalNode) ' marker to identify cycle through queue
Do while not QueueEmpty()
node = Dequeue()
if isSentinalNode(node) then
if NodeProcessed then
Queue(node)
set NodeProcessed to False
else
' Queue contains only orphans or is empty
end
end
if AddNodeToTree(node) then
set NodeProcessed to True
else
Queue(node)
end
end
SentinalNode
是用于检测循环的标记
通过队列。
您的输出数据结构看起来像包含树的“森林”。那是, 它包含几个不同的树。如果有可能的话 可以在两个或更多个树之间共享给定节点,如上所述 算法无法正常工作。如果节点最多可能出现 在“森林”中的一棵树,那么上面应该没问题。