深度复制图形结构

时间:2012-10-14 20:06:34

标签: java data-structures clone deep-copy

我有一个带有Node的图表类,其中每个Node都可以连接到其他节点:

public class Node {
  List<Node> connections;
}

我想对整个图表进行深度复制。作为第一次尝试,我尝试制作一个复制构造函数,如:

public Node(Node other) {
  connections = new ArrayList<Node>();
  for (Node n : other.connections) { 
    connections.add(new Node(n));
  }
}

如此深度复制图形只会是:

public Graph deepCopy () {
  Graph g = new Graph();
  g.nodes = new ArrayList<Node>();
  for (Node n : nodes) { 
    g.nodes.add(new Node(n));
  }
}

但这不起作用,因为它破坏了节点之间的连接关系。我想知道是否有人建议以简单的方式做到这一点?感谢。

3 个答案:

答案 0 :(得分:13)

问题是您需要复制节点的标识,而不仅仅是它们的值。具体来说,当您复制某个节点时,您需要处理它所引用的节点的标识;这意味着复制构造函数或其他一些纯粹的本地复制机制无法完成这项工作,因为它一次只处理一个节点。我不确定这是否有意义,但我输入了它,而我的退格键不起作用。

无论如何,你可以做的是传递一些其他对象,它可以告诉哪个新节点对应哪个旧节点。如果你想要花哨(谁没有?)你可以将其称为graph isomorphism。这可以像地图一样简单。就像在这个完全未经测试的代码中一样:

// in Graph
public Graph deepCopy () {
  Graph g = new Graph();
  g.nodes = new ArrayList<Node>();
  Map<Node, Node> isomorphism = new IdentityHashMap<Node, Node>();
  for (Node n : nodes) { 
    g.nodes.add(n.deepCopy(isomorphism));
  }
  return g;
}

// in Node
public Node deepCopy(Map<Node, Node> isomorphism) {
    Node copy = isomorphism.get(this);
    if (copy == null) {
        copy = new Node();
        isomorphism.put(this, copy);
        for (Node connection: connections) {
            copy.connections.add(connection.deepCopy(isomorphism));
        }
    }
    return copy;
}

Sergii提到使用序列化;序列化在遍历对象图时实际上做了类似的事情。

答案 1 :(得分:6)

是的,可以使用内存serialization/deserialization在java中创建深层副本(不仅仅是在java中) 像这样

public static Object copy(Object orig) {
        Object obj = null;
        try {
            // Write the object out to a byte array
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            ObjectOutputStream out = new ObjectOutputStream(bos);
            out.writeObject(orig);
            out.flush();
            out.close();

            // Make an input stream from the byte array and read
            // a copy of the object back in.
            ObjectInputStream in = new ObjectInputStream(
                new ByteArrayInputStream(bos.toByteArray()));
            obj = in.readObject();
        }
        catch(IOException e) {
            e.printStackTrace();
        }
        catch(ClassNotFoundException cnfe) {
            cnfe.printStackTrace();
        }
        return obj;
    }

答案 2 :(得分:0)

Kinda晚输入。但是我有一个类似的问题,但是得出了不同的解决方案。但是,如果它具有防弹功能,请不要担心。因此,请随时发表评论,以便我学习!

我有一个称为“数字”的类型,因为我没有创造力的命名内容。 每个“数字”类型的对象都有一个内部列表,该列表可以承载其他“数字”类型的对象,其中每个对象都有一个其他“数字”的列表,每个对象...等等。

基本上,您可以创建类似于以下的树结构:

enter image description here

我通过在“ Numbers”类中使用递归复制构造函数解决了深层复制问题。

数字类:

import java.util.ArrayList;

public class Numbers {

    private ArrayList<Numbers> numbers = new ArrayList<>();
    private int number;

    public Numbers(int number) {
        this.number = number;
    }

    public Numbers(Numbers numToCopy) {
        this.number = numToCopy.getNumber();
        ArrayList<Numbers> list = numToCopy.getNumbers();
        for(int i = 0; i < list.size(); i++) {
            Numbers n = new Numbers(list.get(i));
            numbers.add(n);
        }
    }

    public void addNumber(Numbers i) {
        numbers.add(i);
    }

    public ArrayList<Numbers> getNumbers() {
        return numbers;
    }

    public void setNumber(int i) {
        this.number = i;
    }

    public int getNumber() {
        return number;
    }

    public ArrayList<Numbers> getAllNumbers(ArrayList<Numbers> list) {
        int size = numbers.size();
        list.addAll(numbers);
        for(int i = 0; i < size; i++) {
            numbers.get(i).getAllNumbers(list);
        }
        return list;
    }

}

用法:

import java.util.ArrayList;

public class NumbersTest {

    public NumbersTest() {

    }

    public static void main(String[] args) {
        Numbers num0 = new Numbers(0);
        Numbers num1 = new Numbers(1);
        Numbers num2 = new Numbers(2);
        Numbers num3 = new Numbers(3);
        Numbers num4 = new Numbers(4);
        Numbers num5 = new Numbers(5);
        Numbers num6 = new Numbers(6);

        num0.addNumber(num1);
        num0.addNumber(num2);

        num1.addNumber(num3);
        num1.addNumber(num4);

        num2.addNumber(num5);
        num2.addNumber(num6);

        num4.addNumber(num6);

        //Deep copy here!
        Numbers numCopy = new Numbers(num0);
        //Change deep down in graph of original
        num0.getNumbers().get(0).getNumbers().get(1).getNumbers().get(0).setNumber(799);
        //Printout of copy to show it was NOT affected by change in original.
        for(Numbers n : numCopy.getAllNumbers(new ArrayList<Numbers>())) {
            System.out.println(n.getNumber());
        }

    }

}

使用代码显示,更改原始num0对象的“图形”内部不会更改其副本。

图中有两个六个( 6 ),没关系,因为它们位于不同的分支上。 不利的一面是,如果相同的数字会在一条路径中重复出现,例如第一个 1 下是否有( 1 )。然后,它将最终陷入无限循环。

请发表评论! :)