以迭代方式复制二叉树

时间:2012-03-10 15:21:59

标签: c++ algorithm data-structures tree binary-tree

我在接受采访时被问到这个问题,这实际上让我失去了一份工作:P 面试官问道,你将获得树的根,你必须将根返回到复制的树,但副本应该以迭代的方式进行。 我在这里粘贴我的代码,我在那里写了相同的,它工作正常。 我最初使用两个堆栈来做这个,面试官说他不喜欢,然后我用以下方式做到了。 面试官对我使用另一种结构来表示不满,该结构包含指向原始和最终树的指针(参考代码)。

我想知道是否还有其他更好的方法吗?

struct node
{
   int data;
   struct node * left;
   struct node * right;
};

struct copynode
{
   node * original;
   node * final;
};

node * copy(node *root)
{
    stack <copynode*> s;
    copynode * temp=(copynode*)malloc(sizeof(copynode));
    temp->original=root;
    temp->final=(node *)malloc(sizeof(node));
    s.push(temp);
    while(s.empty()==false)
    {
       copynode * i;
       i=s.top();
       s.pop();
       i->final=i->original;
       if(i->original->left)
       {
          copynode *left=(copynode*)malloc(sizeof(copynode));
          left->original=i->original->left;
          left->final=(node *)malloc(sizeof(node));
          s.push(left);
       }
       if(i->original->right)
       {
          copynode *right=(copynode*)malloc(sizeof(copynode));
          right->original=i->original->right;
          right->final=(node *)malloc(sizeof(node));
          s.push(right);
       }
   }  
   return temp->final;
}

8 个答案:

答案 0 :(得分:17)

如果允许在每个节点中都有父指针,则甚至不需要堆栈:

将原始树和您正在创建的树平行移动。如果原始树中的当前节点具有左子节点,但您正在创建的树中的节点没有,则创建它并向左下移。与正确的孩子一样。如果两种情况都不适用,请上去。

在代码(C#)中:

public static Node Clone(Node original)
{
    if (original == null)
        return null;

    var root = new Node(original.Data, null);
    var clone = root;

    while (original != null)
    {
        if (original.Left != null && clone.Left == null)
        {
            clone.Left = new Node(original.Left.Data, clone);
            original = original.Left;
            clone = clone.Left;
        }
        else if (original.Right != null && clone.Right == null)
        {
            clone.Right = new Node(original.Right.Data, clone);
            original = original.Right;
            clone = clone.Right;
        }
        else
        {
            original = original.Parent;
            clone = clone.Parent;
        }
    }

    return root;
}

答案 1 :(得分:1)

第一个代码段是解决方案。第二个段是您可以复制,粘贴和运行的文件,以查看正在运行的解决方案。

解决方案:

public Node clone() {
    if(null == root)
        return null;
    Queue<Node> queue = new LinkedList<Node>();
    queue.add(root);
    Node n;

    Queue<Node> q2 = new LinkedList<Node>();
    Node fresh;
    Node root2 = new Node(root.data);
    q2.add(root2);

    while(!queue.isEmpty()) {
        n=queue.remove();
        fresh = q2.remove();
        if(null != n.left) {
            queue.add(n.left);
            fresh.left = new Node(n.left.data);
            q2.add(fresh.left);
        }
        if(null != n.right) {
            queue.add(n.right);
            fresh.right= new Node(n.right.data);
            q2.add(fresh.right);
        }           
    }       
    return root2;
}//

计划文件:

import java.util.LinkedList;
import java.util.Queue;

public class BST {
Node root;

public BST() {
    root = null;
}

public void insert(int el) {

    Node tmp = root, p = null;
    while (null != tmp && el != tmp.data) {
        p = tmp;
        if (el < tmp.data)
            tmp = tmp.left;
        else
            tmp = tmp.right;
    }
    if (tmp == null) {
        if (null == p)
            root = new Node(el);
        else if (el < p.data)
            p.left = new Node(el);
        else
            p.right = new Node(el);
    }
}//

public Node clone() {
    if(null == root)
        return null;
    Queue<Node> queue = new LinkedList<Node>();
    queue.add(root);
    Node n;

    Queue<Node> q2 = new LinkedList<Node>();
    Node fresh;
    Node root2 = new Node(root.data);
    q2.add(root2);

    while(!queue.isEmpty()) {
        n=queue.remove();
        fresh = q2.remove();
        if(null != n.left) {
            queue.add(n.left);
            fresh.left = new Node(n.left.data);
            q2.add(fresh.left);
        }
        if(null != n.right) {
            queue.add(n.right);
            fresh.right= new Node(n.right.data);
            q2.add(fresh.right);
        }           
    }       
    return root2;
}//

private void inOrder(Node n) {
    if(null == n) return;
    inOrder(n.left);
    System.out.format("%d;", n.data);
    inOrder(n.right);
}//

public static void main(String[] args) {
    int[] input = { 50, 25, 75, 10, 35, 60, 100, 5, 20, 30, 45, 55, 70, 90,
            102 };
    BST bst = new BST();
    for (int i : input)
        bst.insert(i);
    Node root2 = bst.clone();
    bst.inOrder(root2);
}
}

class Node {
public int data;
public Node left;
public Node right;

public Node(int el) {
    data = el;
}
}

答案 2 :(得分:1)

两个想法:

  1. 您需要堆栈或父链接来遍历输入树(afaict)。所以我们假设面试官会对其中一个感到满意。还有什么可以简化?

    在您的代码中,您还遍历副本并与原始节点并行存储其节点。相反,您可以简单地将节点添加到副本的根目录。只要您正确选择原始遍历,您就会得到相同的结构。

    并且不难看出预订遍历会这样做(假设在添加时没有重新平衡)。

    因此您可以根据预订遍历和简单添加到副本根来编写副本。这将提供更简单的代码和/或允许重复使用,代价是效率较低(你有O(nlog(n))额外的“跳”以在插入时找到副本中的正确位置。

  2. 对于不可变节点,您只需复制根(这是正常的功能样式)。

  3. 我的直觉是(1)可能是他正在寻找的东西,给出了“树的属性”。

答案 3 :(得分:0)

没有递归的一种方法是遍历每个节点,如果节点包含右分支,则将该右侧节点推入堆栈。当您沿着路径耗尽节点时(仅跟随具有左分支的节点),将顶部节点弹出堆栈并使用相同的规则跟随它(向右推,然后在左侧)。重复此循环,直到堆栈为空。这应该只需要一个堆栈。

编辑:你问过面试官他/她正在寻找什么方法吗?如果没有,你应该以一种表明你愿意学习新事物的方式,并且你应该尝试使它成为一个双向对话。我确信你确实有学习新事物的意愿(或者你不会在这里发布),但你的面试官是否知道这一点?请记住 - 面试官通常不会寻找具体的答案,而是他们希望评估您解决问题的方法,而不是纯粹以技术方式。如果你没有采用这种方式,那么面试官就不可能认为你不了解所有事情,但能够学习并可能为他们的团队做出很大的贡献。

答案 4 :(得分:0)

这是我的工作代码:

对于每个节点,先保持左推,一个完成推左节点,推右节点然后重复相同。

#include <iostream>
#include <stack>

using namespace std;

struct Node {
    Node() 
    : left(NULL), right(NULL) {}
    int val;
    Node *left;
    Node *right;
};

struct Wrap {
    Wrap()
    : oldNode(NULL), newNode(NULL), flags(0) {}
    Node *oldNode;
    Node *newNode;
    int flags;
};

void InOrder(Node *node) {
    if(node == NULL) {
        return;
    }
    InOrder(node->left);
    cout << node->val << " ";
    InOrder(node->right);
}

Node *AllocNode(int val) {
    Node *p = new Node;
    p->val = val;
    return p;
}

Wrap *AllocWrap(Node *old) {
    Wrap *wrap = new Wrap;

    Node *newNode = new Node;
    newNode->val = old->val;

    wrap->oldNode = old;
    wrap->newNode = newNode;

    return wrap;
}

Wrap* PushLeftNodes(stack<Wrap*> &stk) {

    Wrap *curWrap = stk.top();  
    while(((curWrap->flags & 1) == 0) && (curWrap->oldNode->left != NULL)) {
        curWrap->flags |= 1;
        Wrap *newWrap = AllocWrap(curWrap->oldNode->left);
        stk.push(newWrap);
        curWrap->newNode->left = newWrap->newNode;
        curWrap = newWrap;
    }
    return curWrap;
}

Node *Clone(Node *root) {

    if(root == NULL) {
        return NULL;
    }

    Node *newRoot = NULL;
    stack<Wrap*> stk;

    Wrap *wrap = AllocWrap(root);
    stk.push(wrap);

    while(!stk.empty()) {
        wrap = PushLeftNodes(stk);
        if(((wrap->flags & 2) == 0) && (wrap->oldNode->right != NULL)) {
            wrap->flags  |= 2;
            Wrap *newWrap = AllocWrap(wrap->oldNode->right);
            stk.push(newWrap);
            wrap->newNode->right = newWrap->oldNode;
            continue;
        }

        wrap = stk.top();
        newRoot = wrap->newNode;
        stk.pop();
        delete wrap;
    }
    return newRoot;
}

int main() {
    Node *root = AllocNode(10);
    Node *curNode = root;

    curNode->left = AllocNode(5);
    curNode->right = AllocNode(15);

    curNode = curNode->left;
    curNode->left = AllocNode(14);

    curNode->right = AllocNode(17);

    curNode = curNode->right;
    curNode->left = AllocNode(18);
    curNode->right = AllocNode(45);

    curNode = root->right;
    curNode->right = AllocNode(33);

    curNode = curNode->right;
    curNode->right = AllocNode(46);

    cout << "Original tree : " << endl;
    InOrder(root);

    Node *newRoot = Clone(root);
    cout << "New tree : " << endl; 
    InOrder(newRoot);

    return 0;
}

答案 5 :(得分:0)

有效的Java代码,并且有一个相对简单的方法:

static Node cloneTree(Node original){
    if(original == null) return null;

    Node temp = original;
    Node root = temp;
    if(original.left!=null)
      temp.left = original.left;
    if(original.right!=null)
      temp.right = original.right;

   cloneTree(original.left);
   cloneTree(original.right);
  return root;
}

答案 6 :(得分:0)

考虑下面的树:

                      10
                      / \
                    20   30
                    /\
                  40  50
                      /\
                     60 70

现在基于BFS,如果你创建一个节点数组(数组用于更快的访问),它将是这样的:

节点数组: 10 20 30 40 50 N N N N 60 70 N N N N

指数: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

请注意,索引为1表示更容易计算,叶子节点的子节点表示为N表示NULL。

现在,如果您注意到对于索引i处的节点,其左子节点位于索引2 * i且其右子节点位于索引2 * i + 1处。这属于一个合适的二叉树。

现在以非递归方式复制二叉树。

  1. 根据BFS从原始树创建节点数组。
  2. 分配当前= 1(当前是索引),
  3. 在当前&lt; =节点数组的大小

    时执行以下操作

    一个。创建一个新节点(父节点)

    湾从当前节点分配值parent-&gt; val。

    ℃。将当前(索引)的原始节点替换为新节点。 //这一步是为了能够遍历新节点。

    d。创建一个新节点,分配给parent-&gt; left并从Lindex = 2 * current。

    中分配值

    即将Lindex上的原始节点替换为新节点。

    F。创建一个新节点,分配给parent-&gt; right并从Rindex = 2 * current + 1中分配值。

    即用新节点替换Rindex的原始节点。

    F。增加电流1。

  4. 现在时间复杂了,

    因为在数组中我们还考虑了外部节点的NULL子节点,我们需要计算数组的总大小。

    如果原始树中的节点数是n,那么通过二叉树的属性,二叉树的外部节点数总是比内部节点数多1,

     a. Number of external nodes is (n + 1) /2.
     b. Number of children of external nodes (count of all null nodes) is 2 * (n + 1)/2 = n + 1.
     c. Thus the total size of array is number of nodes + number of null nodes = n + n + 1 = 2n + 1
    

    由于在整个算法中,我们遍历整个数组,这种方法的时间复杂度为O(n)。

答案 7 :(得分:0)

反复执行此操作非常愚蠢,除非树很大,这引出了一个问题,即为什么要首先复制它。这是C ++尝试使用复制构造函数,因此隐式地进行递归。

KeyError: 'dense_115_input'

ValueError: Input 0 of layer sequential_40 is incompatible with the layer: expected axis -1 of input shape to have value 2206 but received input with shape [None, 2206, 2]

TypeError: object of type 'NoneType' has no len()

换句话说,你有一棵这样的树

    struct Node
    {  
        Node *left, *right;
        int value;
        Node() : left(NULL), right(NULL) {}
        Node(const int &_v) : left(NULL), right(NULL), value(_v) {}
        Node(const Node &o) 
        : left(NULL), right(NULL), value(o.value)
        {
            if (o.left)
                left = new Node(*o.left);
            if (o.right)
                right = new Node(*o.right);
        }
        ~Node() { delete left; delete right; }
    };

然后您像这样复制它:

Node *root = make_a_tree();