【问题标题】:Is there a better way to clone an N-ary tree iteratively?有没有更好的方法来迭代地克隆 N 叉树?
【发布时间】:2014-12-03 01:01:39
【问题描述】:

我正在寻找一种更好或更优化的方法来复制(或在实际问题中,转换)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;
} 

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

【问题讨论】:

    标签: c# data-structures tree tree-traversal


    【解决方案1】:

    我会努力解决的。这假设有一个到父节点的链接,并且您可以检索节点上的子节点数量并通过索引访问子节点。

    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并将第一个if分支中的行更改为currentCloned = currentCloned.Parent ?? currentCloned

    我们循环直到我们没有更多的东西要处理。有两种选择:

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

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

    【讨论】:

    • 不错!比使用多个堆栈进行导航和跟踪要优雅得多。这正是我认为我忽略的那种解决方案。
    猜你喜欢
    • 2020-08-24
    • 1970-01-01
    • 2011-12-22
    • 2020-12-06
    • 2021-12-01
    • 1970-01-01
    • 1970-01-01
    • 2015-07-02
    相关资源
    最近更新 更多