Рекурсивное глубокое клонирование Java

Добрый вечер.

Я пытаюсь реализовать дерево AVL и столкнулся с проблемой копирования узлов в процессе вращения:

  • Если я сделаю это с поверхностной копией, получится довольно предсказуемый хаос.
  • Если я делаю это с ручной глубокой копией, я могу копировать только непосредственные атрибуты объекта (узла), но не его более глубокие уровни. Чтобы уточнить, если у меня есть узел с двумя дочерними элементами, которые сами имеют 1-2 дочерних элемента и чьи дочерние элементы могут иметь свои собственные дочерние элементы... Я могу только скопировать исходный узел и его прямые дочерние элементы, но потерять внуков и далее потомки.

Итак, я хотел бы знать, есть ли способ обойти это ограничение.
Вот полный код класса (main — это просто вызов 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();
}
}

person Malcolm Tucker    schedule 07.04.2015    source источник


Ответы (2)


Вы должны просто избегать копирования узлов; вы можете сбалансировать поддеревья, просто назначив их указатели left и right.

Функции поворота должны принимать в качестве параметра корень поддерева и возвращать преобразованное поддерево; вращение деревьев «на месте» является серьезным ненужным усложнением.

person Lorenzo Gatti    schedule 07.04.2015
comment
Разве это не то, что я назвал поверхностным копированием? Как бы вы это реализовали? Алгоритм, на котором я основываюсь, выполняет поворот влево следующим образом: tempNode=root; корень = корень.право; tempNode.right = root.left; Вторым шагом в этой реализации будет this=right; чего нельзя сделать на Java. - person Malcolm Tucker; 07.04.2015
comment
Вот почему я предлагаю чередовать произвольный NodeQ, переданный в качестве параметра, а не этот - person Lorenzo Gatti; 07.04.2015
comment
Ага. Вернуть новый корень, а не пытаться присвоить его this; возвращаемый узел может быть this или right. - person Louis Wasserman; 07.04.2015
comment
Хорошо, да, я подумал о переключении на параметр/возврат, который действительно снял бы проблему этой аффектации... но за счет перемещения его в вызывающую функцию. Тем не менее, я думаю, я мог бы изменить всю иерархию функций, чтобы избежать этого. Но это не меняет проблему неглубокого копирования, по крайней мере, в используемом алгоритме: первая линия назначает NodeQ, указанный в параметре, временному NodeQ, затем любая модификация одного (например, изменение их левого или правого NodeQ) влияет на Другой. Возможно, следует использовать другой алгоритм вращения. - person Malcolm Tucker; 07.04.2015
comment
Пробовал другой алгоритм ротации (использованный здесь), который также работает на бумаге, но создает свои проблемы. Я в растерянности. - person Malcolm Tucker; 07.04.2015
comment
Я начинаю думать, что это невозможно сделать с помощью одного класса в Java. Думаю, мне придется попробовать 2-классную версию. - person Malcolm Tucker; 08.04.2015

Очень простой способ глубокого клонирования — реализовать Serializable, а затем сериализовать/десериализовать объект (например, в ByteArrayOutputStream).

person lance-java    schedule 07.04.2015
comment
И это будет рекурсивно включать всех потомков, независимо от того, насколько глубоко? - person Malcolm Tucker; 07.04.2015
comment
Помимо того, что она сама по себе медленная, такая копия создаст много мусора ArrayList‹Segment› и объектов NodeQ. - person Lorenzo Gatti; 07.04.2015