0

我有一个存储在数据库中的节点层次结构。我选择所有,将它们存储在一个数组中,然后遍历它们并在内存中创建一个嵌套数组。

输入如下所示:

[{name: A}, {name: B}, {name: X, parent: A}, {name: Y, parent: A}, {name: C}]

输出如下所示:

[{姓名:A,孩子:[{姓名:X},{姓名: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 插入其层次结构中)

4

1 回答 1

1

看起来您正在使用队列(从前面删除,添加到后面)类型的数据结构来存储未处理的节点。随着节点被插入到您的输出数据结构中,它们将从队列中删除。如果一个节点不能被添加到输出中(因为它的“父节点”还没有被移动到输出数据结构中)它被重新排队。最终队列应该变空,除非存在“父”不存在的节点(孤儿)。

我想你的算法看起来像

 Do While not QueueEmpty()
    node = Dequeue() ' Remove from the front
    if not AddNodeToTree(node) then Queue(node) 'add to the back
 end

whereAddNodeToTree是一个函数,它接受一个节点,成功地将它添加到输出并返回True。否则它返回False 导致节点回收。

您唯一需要做的就是在队列后面添加一个前哨节点和一个标志,以指示在一个完整的循环中从队列中至少消耗了一个节点。上述算法变为:

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是用于检测队列循环的标记。

您的输出数据结构看起来像是包含树木的“森林”。也就是说,它包含几个不同的树。如果给定节点有可能在两棵或多棵树之间共享,则上述算法将无法正常工作。如果一个节点最多可能出现在“森林”中的一棵树中,那么上面应该没问题。

于 2010-06-04T17:46:50.027 回答