我在接受采访时被问到这个问题,这实际上让我失去了一份工作: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;
}
答案 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)
两个想法:
您需要堆栈或父链接来遍历输入树(afaict)。所以我们假设面试官会对其中一个感到满意。还有什么可以简化?
在您的代码中,您还遍历副本并与原始节点并行存储其节点。相反,您可以简单地将节点添加到副本的根目录。只要您正确选择原始遍历,您就会得到相同的结构。
并且不难看出预订遍历会这样做(假设在添加时没有重新平衡)。
因此您可以根据预订遍历和简单添加到副本根来编写副本。这将提供更简单的代码和/或允许重复使用,代价是效率较低(你有O(nlog(n))额外的“跳”以在插入时找到副本中的正确位置。
对于不可变节点,您只需复制根(这是正常的功能样式)。
我的直觉是(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处。这属于一个合适的二叉树。
现在以非递归方式复制二叉树。
在当前&lt; =节点数组的大小
时执行以下操作一个。创建一个新节点(父节点)
湾从当前节点分配值parent-&gt; val。
℃。将当前(索引)的原始节点替换为新节点。 //这一步是为了能够遍历新节点。
d。创建一个新节点,分配给parent-&gt; left并从Lindex = 2 * current。
中分配值即将Lindex上的原始节点替换为新节点。
F。创建一个新节点,分配给parent-&gt; right并从Rindex = 2 * current + 1中分配值。
即用新节点替换Rindex的原始节点。
F。增加电流1。
现在时间复杂了,
因为在数组中我们还考虑了外部节点的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();