C# 有没有更好的方法迭代克隆N元树?

C# 有没有更好的方法迭代克隆N元树?,c#,data-structures,tree,tree-traversal,C#,Data Structures,Tree,Tree Traversal,我正在寻找一种更好或更优化的方法来复制(或者在实际问题中,转换)n元树,而不使用递归。下面是关于我试图解决的一般情况的一些细节 树是n元的(即,每个级别最多n个节点) 子项具有指向父项的链接,父项具有所有子项的列表 在树的任何给定级别中,任何节点都可以是叶子或分支 我想出了以下解决办法。一般方法是使用两(三)个堆栈。第一个跟踪原始树中需要处理的项,第二个跟踪新创建的副本,以便我们可以适当地分配节点之间的链接(这可以分为两个堆栈而不是元组,因此可以分为三个)。这是可行的,但它有许多不受欢迎的方

我正在寻找一种更好或更优化的方法来复制(或者在实际问题中,转换)n元树,而不使用递归。下面是关于我试图解决的一般情况的一些细节

  • 树是n元的(即,每个级别最多n个节点)
  • 子项具有指向父项的链接,父项具有所有子项的列表
  • 在树的任何给定级别中,任何节点都可以是叶子或分支
我想出了以下解决办法。一般方法是使用两(三)个堆栈。第一个跟踪原始树中需要处理的项,第二个跟踪新创建的副本,以便我们可以适当地分配节点之间的链接(这可以分为两个堆栈而不是元组,因此可以分为三个)。这是可行的,但它有许多不受欢迎的方面,首先是它感觉非常尴尬。我在想,必须有更好的方法来做到这一点,我错过了一些(或多个)明显的事情

有没有人遇到过更直接/更有效的方法

public TreeNode ConvertTree(TreeNode root)
{
    Stack<TreeNode> processingStack = new Stack<TreeNode>();
    Stack<Tuple<Int32, TreeNode>> resultStack = new Stack<Tuple<Int32, TreeNode>>();
    TreeNode result = null;

    processingStack.Push(root);
    while (processingStack.Count > 0)
    {
        var currentProcessingNode = processingStack.Pop();
        var parentNode = resultStack.Count > 0 ? resultStack.Pop() : null;

        // Copies all leaf nodes and assigns parent, if applicable.
        var newResultNode = CopyNodeData(currentProcessingNode, parentNode != null ? parentNode.Item2 : null);

        // Push sub-branch nodes onto the processing stack, and keep track of how many for
        // each level.
        var subContainerCount = 0;
        foreach (var subContainer in currentProcessingNode.Children.Where(c => !c.IsLeaf))
        {
            processingStack.Push(subContainer);
            subContainerCount++;
        }

        // If we have have not processed all children in this parent, push it back on and
        // decrement the counter to keep track of it.
        if (parentNode != null && parentNode.Item1 > 1)
        {
            resultStack.Push(new Tuple<Int32, TreeNode>(parentNode.Item1 - 1, parentNode.Item2));
        }

        // If this node has sub-branches, push the newly copied node onto the result/tracking
        // stack
        if(subContainerCount > 0)
            resultStack.Push(new Tuple<Int32, TreeNode>(subContainerCount, newResultNode));

        // The very first time a new node is created, track it to return as the result
        if (newResultNode.IsRoot)
            result = newResultNode;
    }

    return result;
} 
公共树节点转换树(树节点根)
{
堆栈处理堆栈=新堆栈();
堆栈结果堆栈=新堆栈();
TreeNode结果=空;
加工钉压(根部);
while(processingStack.Count>0)
{
var currentProcessingNode=processingStack.Pop();
var parentNode=resultStack.Count>0?resultStack.Pop():null;
//复制所有叶节点并指定父节点(如果适用)。
var newResultNode=CopyNodeData(currentProcessingNode,parentNode!=null?parentNode.Item2:null);
//将分支节点推送到处理堆栈上,并跟踪有多少分支节点
//每一级。
var SubcainerCount=0;
foreach(currentProcessingNode.Children.Where(c=>!c.IsLeaf)中的var子容器)
{
加工钉推(分包商);
SubcainerCount++;
}
//如果尚未处理此父级中的所有子级,请将其推回并重试
//减小计数器以跟踪它。
if(parentNode!=null&&parentNode.Item1>1)
{
resultStack.Push(新元组(parentNode.Item1-1,parentNode.Item2));
}
//如果此节点有分支,请将新复制的节点推送到结果/跟踪上
//堆叠
如果(分包计数>0)
resultStack.Push(新元组(subcainercount,newResultNode));
//第一次创建新节点时,跟踪它以作为结果返回
if(newResultNode.IsRoot)
结果=newResultNode;
}
返回结果;
} 

请注意,我不是在寻找递归解决方案。是的,我意识到它们在很多情况下都是可用的、简单的和合适的。这个问题更多的是关于如何以迭代的方式有效地完成这种类型的操作,而不仅仅是如何在一段时间内完成它。

我将尝试一下。这假定有一个到父节点的链接,并且您可以检索节点上的子节点数并通过索引访问子节点

static TreeNode Clone(TreeNode root)
{
    var currentOriginal = root;
    var currentCloned = Copy(root, null);
    var clonedRoot = currentCloned;
    while (currentOriginal != null)
    {
        if (currentCloned.Children.Count == currentOriginal.Children.Count)
        {
            currentOriginal = currentOriginal.Parent;
            currentCloned = currentCloned.Parent;
        }
        else
        {
            var targetChild = currentOriginal.Children[currentCloned.Children.Count];
            currentOriginal = targetChild;
            currentCloned = Copy(currentOriginal, currentCloned);
        }
    }
    return clonedRoot;
}


static TreeNode Copy(TreeNode source, TreeNode parent) { ... }
我们初始化:

  • 原始树的工作变量
  • 用于克隆树的工作变量
  • 克隆树的根(因此代码更干净,另一种方法是返回
    currentCloned
    ,并将第一个
    分支中的行更改为
    currentCloned=currentCloned.Parent??currentCloned
我们循环,直到我们没有更多的过程。有两种选择:

  • 克隆的子项数与源的子项数相同。这意味着存在叶节点或所有子节点都已处理。上移到父级
  • 克隆的子项比原始的少,这意味着应该处理一个或多个子项,使用上面的索引器技巧处理下一个子项

因为我们可以使用树本身链接到父级,所以不需要堆栈来帮助导航。

Nice!比使用多个堆栈进行导航和跟踪要优雅得多。这正是我所忽视的那种解决方案。