Skip to content

Latest commit

 

History

History
68 lines (50 loc) · 3 KB

c822.md

File metadata and controls

68 lines (50 loc) · 3 KB

Back to questions

Solution to c822: Problems cloning tree nodes

See code at solutions/code/tutorialquestions/questionc822

Invoking n1.clone(), where n1 is an object referenced by the following code fragment and TreeNode is the class produced in question 7041:

TreeNode<String> n1 = new TreeNode<String>(1);
n1.setKey("A");
TreeNode<String> n2 = new TreeNode<String>(1);
n2.setKey("B");
n1.setChild(0, n2);
n2.setChild(0, n1);

leads to an infinite loop. The problem is that n1 is not really a tree node, because n1 has n2 as a child, and n2 has n1 as a child. Thus the nodes form a cyclic graph:

Cyclic graph of tree nodes

The recursive clone method descends forever through this cycle, building a longer and longer corresponding tree of duplicates of n1 and n2:

Infinite tree of nodes

Practically, this leads to the program terminating with a java.lang.StackOverflowError.

If we invoke n3.clone(), where n3 is an object referenced by the following code fragment:

TreeNode<String> n3 = new TreeNode<String>(2);
n3.setKey("C");
TreeNode<String> n4 = new TreeNode<String>(1);
n4.setKey("D");
TreeNode<String> n5 = new TreeNode<String>(0);
n5.setKey("E");
n3.setChild(0, n4);
n3.setChild(1, n5);
n4.setChild(0, n5);

then clone terminates, but the structure produced does not match the original. This is because n3, n4 and n5 do not really form a tree, they actually form a directed acyclic graph (DAG) that is not a tree. This is because node n5 has both n3 and n4 as a parent:

DAG of tree nodes

The clone method will produce a tree from this DAG in which the n5 node has been duplicated:

Tree obtained from DAG via cloning

Preconditions for setChild(): When child is passed as argument to setChild, it must be the case that child does not already have a parent. This avoids the construction of a DAG that is not a tree. Also, when child is passed as an argument to setChild, it must be the case that the tree node on which setChild is being invoked is not a (direct or indirect) descendent of child. This ensures that cyclic structures are not created.

Advanced: Look at TreeNode in the precondition package. This class implements these precondition checks. The single-parent check is made by equipping each TreeNode with a numberOfParents field. The implementation of setChild is then responsible for checking and maintaining this field. Unfortunately this does lead to added complexity in setChild. The check that the child node does not reach its parent is implemented through a recursive method reaches, which performs a depth-first search for the parent, starting from the child.