假设我有一个表示嵌套集层次结构的节点列表(示例在伪c#中)。
class Node
{
public decimal left;
public decimal right;
public decimal id;
public void AddChild(Node child) {...}
...
}
List<Node> nodes = GetFlatNodesWithoutChildrenSetFromDatabase();
字段left
,right
和id
已填充,因为这些值存储在某个数据库中。
将此平面列表转换为层次结构的有效方法是什么,这意味着为每个父节点填充适当的子节点?
一种方法是找到每个节点的所有祖先,对它们进行排序以找到父节点并将子节点添加到该节点。
foreach (var n in nodes)
{
var parent = nodes.Where(i => i.left < n.left && i.right > n.right).OrderBy(i => i.right - n.right).FirstOrDefault();
if (parent != null)
parent.AddChild(n);
}
但这效率很低。
是否有更好(意味着更快)的方法?
修改
可能的解决方案(as suggested by Chris):
var stack = new Stack<Node>(nodes.Take(1));
foreach (var n in nodes.Skip(1))
{
while (stack.Peek().right < n.left)
stack.Pop();
stack.Peek().addChild(n);
stack.Push(n);
}
节点必须按left
排序。
答案 0 :(得分:3)
我可能考虑探索的方法是左边排序然后你可以迭代一次。
当你向左移动并将其粘贴在堆栈上时,你“打开”一个节点。
当您到达要处理的新节点时,通过确定其右侧是否小于新节点,检查堆栈顶部的节点是否应该关闭。如果它是你从堆栈中删除它(关闭它),你已经处理了它的所有孩子。然后,您可以检查堆栈的新顶部,直到找到仍处于打开状态的堆栈。然后,将当前节点作为子节点添加到堆栈顶部的节点,然后打开该节点(因此它将位于堆栈顶部)。
您链接的维基百科页面上的图表(http://en.wikipedia.org/wiki/Nested_set_model)是我的灵感来源。
我的算法基本上在中间向下移动,每当你输入其中一个集合时,我称之为打开并且正在关闭集合。很明显,你打开但未关闭的最新套装将位于堆叠的顶部,因此放置孩子的位置。
我认为由于排序,这应该是O(nlogn)的复杂性。其余部分只是O(n)。
答案 1 :(得分:0)
我知道这个问题很老(我没有找到关于这个主题的任何其他问题/信息)而且我不知道“伪C#”,但是为了防止你们中的一些人使用递归算法嵌套集列表=&gt;树算法,这是我来到的(在scala中):
def retrieveUserFolderTree(user: User): Future[List[Folder]] = {
// Get a list of user's folders orderred by left asc
val dbFoldersPromise = folderDAO.findUserFolders(user)
dbFoldersPromise.map {
case rootFolder :: tail => generateChildren(0, rootFolder.right, tail)
case Nil => Nil
}
}
private def generateChildren(currentLeft: Int, currentRight: Int, dbFolders: Seq[DBFolder]): List[Folder] = {
dbFolders match {
case dbFolder :: tail if dbFolder.left > currentRight => Nil
case dbFolder :: tail if dbFolder.left > currentLeft => Folder(dbFolder.id, dbFolder.name, generateChildren(dbFolder.left, dbFolder.right, tail)) :: generateChildren(dbFolder.right, currentRight, tail)
case dbFolder :: tail => generateChildren(currentLeft, currentRight, tail)
case Nil => Nil
}
}
希望这会对某人有所帮助。
答案 2 :(得分:0)
也许我在某个地方错过了一个步骤但是当我使用上面的逻辑工作时,我最终得到了堆栈上的一些复制元素。它们按预期在树中,但另外它们也位于根节点上方的堆栈顶部。我不得不在末尾添加一个小循环来清理堆栈。
var stack = new Stack<DvrNode>(nodes.Take(1));
foreach (var n in nodes.Skip(1))
{
while (stack.Peek().Right < n.Left)
stack.Pop();
((List<DvrNode>)stack.Peek().Children).Add(n);
stack.Push(n);
}
while (stack.Peek().Left != 1)
stack.Pop();