【问题标题】:Java recursive deep cloningJava递归深度克隆
【发布时间】:2015-04-07 15:10:49
【问题描述】:

晚上好。

我正在尝试实现 AVL 树,但在轮换过程中复制节点时遇到问题:

  • 如果我使用浅拷贝,则会出现相当可预测的混乱。
  • 如果我用手动深拷贝来做,我只能拷贝对象(节点)的直接属性,而不能拷贝它的更深层次。 澄清一下,如果我有一个节点有 2 个孩子,而他们自己有 1-2 个孩子,并且他们的孩子可能有自己的孩子,...我只能复制初始节点及其直接孩子,但会失去孙子和进一步后代。

所以,我想知道是否有办法绕过这个限制。
这是该类的完整代码(主要只是对 initialise() 的调用),其他一切都正常运行,所以如果我能设法在旋转中正确复制节点,我将拥有一个工作 AVL 树。

提前感谢您的帮助。

import java.util.ArrayList;

public class NodeQ
{
static boolean isEmpty=true;
private int h=1;
private double x, y;
private ArrayList<Segment> segmentsStarting=new ArrayList<Segment>();
private NodeQ left=null, right=null;

public NodeQ(double abs, double ord) {x=abs; y=ord;}
public NodeQ(NodeQ Q) {this.x=Q.x; this.y=Q.y; this.segmentsStarting=Q.segmentsStarting;}

public double getX() {return x;}
public double getY() {return y;}
public NodeQ getLeft() {return left;}
public NodeQ getRight() {return right;}
public ArrayList<Segment> getSegmentsStarting() {return segmentsStarting;}
public int getH(){return h;}

public void setX(double abs) {x=abs;}
public void setY(double ord) {y=ord;}
public void setLeft(NodeQ l) {left=l;}
public void setRight(NodeQ r) {right=r;}
public void addSegment(Segment s) {segmentsStarting.add(s);}

public void calcH()
{
    if (this.left!=null)
    {
        if (this.right != null)
        {
            if (this.left.h > this.right.h) this.h = this.left.h + 1;
            else this.h = this.right.h + 1;
        }
        else this.h = this.left.h + 1;
    }
    else if (this.right!=null) this.h=this.right.h+1;
}

public void initialise(Segment segment)
{
    if (NodeQ.isEmpty)
    {
        y=segment.yUpper; 
        x=segment.xUpper;
        addSegment(segment); 
        setLeft(new NodeQ(segment.xLower, segment.yLower));
        h=2;
        NodeQ.isEmpty=false;
    }
    else
    {
        insert(segment.xUpper, segment.yUpper, true, segment);
        insert(segment.xLower, segment.yLower, false, segment);
    }
}

public void deepCopy(NodeQ Q)
{
    this.x=Q.x;
    this.y=Q.y;
    this.segmentsStarting=Q.segmentsStarting;
    if (Q.left==null)this.left=null;
    else this.left=new NodeQ(Q.left);
    if (Q.right==null) this.right=null;
    else this.right=new NodeQ(Q.right);
}

public void insert(double abs, double ord, boolean isUpperEndpoint, Segment s)
{
    if (y>ord || (y==ord && x<abs))
    {
        if (left==null)
        {
            left=new NodeQ(abs, ord); 
            if (isUpperEndpoint) addSegment(s);
        }
        else left.insert(abs, ord, isUpperEndpoint, s); 
    }
    else
    {
        if (right==null)
        {
            right=new NodeQ(abs, ord);
            if (isUpperEndpoint) addSegment(s);
        }
        else right.insert(abs, ord, isUpperEndpoint, s);
    }
    balancing();
}

public void leftRotation()
{
    NodeQ tmp=new NodeQ(-1, -1);
    tmp.deepCopy(this);
    this.deepCopy(this.right);

    if (this.left==null) tmp.right=null;
    else tmp.right=new NodeQ(this.left);
    this.left=new NodeQ(tmp);

    tmp.calcH();
    this.calcH();
}

public void rightRotation()
{
    NodeQ tmp=new NodeQ(-1, -1);
    tmp.deepCopy(this);
    this.deepCopy(this.left);

    if (this.right==null) tmp.left=null;
    else tmp.left=new NodeQ(this.right);
    this.right=new NodeQ(tmp);

    tmp.calcH();
    this.calcH();
}

public int calcBalance()
{
    int bal=0;
    if (left==null)
    {
        if (right!=null) bal=right.h;
    }
    else
    {
        if (right==null) bal=-left.h;
        else bal=right.h-left.h;
    }
    return bal;
}

public void balancing()
{
    int b=calcBalance();
    if (b==2)
    {
        if (right.calcBalance()<0) right.rightRotation();
        leftRotation();
    }
    else if (b==-2)
    {
`       if (left.calcBalance()>0) left.leftRotation();
        rightRotation();
    }
    else calcH();
}
}

【问题讨论】:

    标签: java deep-copy avl-tree


    【解决方案1】:

    你应该简单地避免复制节点;您可以通过分配它们的 leftright 指针来平衡子树。

    旋转函数应以子树根为参数,返回变换后的子树; “原地”旋转树木是一个不需要的主要并发症。

    【讨论】:

    • 这不是我所谓的“浅拷贝”吗?你将如何实施呢?因此,我基于的算法会进行左旋转:“tempNode=root; root=root.right; tempNode.right=root.left;”在这个实现中,第二步是 this=right;这不能在 Java 中完成。
    • 这就是为什么我建议旋转作为参数传递的任意 NodeQ,而不是 this
    • 是的。返回新的根,而不是尝试分配给this;返回的节点可能是this,也可能是right
    • 好的,是的,我考虑过切换到参数/返回,这确实会解决“this”影响的问题......但代价是将其移动到调用函数。不过,我想我可以更改所有函数的层次结构以避免不得不这样做。但这并没有改变“浅拷贝”问题,至少在所使用的算法中:第一行将参数中给出的 NodeQ 分配给临时 NodeQ,然后对其进行任何修改(例如影响它们的左侧或右侧 NodeQ)影响对方。也许应该使用另一种旋转算法。
    • 尝试了另一种旋转算法 (the one used here),它也适用于纸面,但它会产生自己的问题。我很茫然。
    【解决方案2】:

    一种非常简单的深度克隆方法是实现 Serializable,然后对对象进行序列化/反序列化(例如到 ByteArrayOutputStream)。

    【讨论】:

    • 这将递归地包括所有后代,无论多深?
    • 这样的副本除了本身速度慢之外,还会产生大量的垃圾 ArrayList 和 NodeQ 对象。
    猜你喜欢
    • 2015-11-22
    • 2013-11-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-09-17
    • 2017-09-01
    • 2010-09-09
    • 1970-01-01
    相关资源
    最近更新 更多