Java Reference
In-Depth Information
figure 18.14
Result of a naive
merge
operation:
Subtrees are shared.
root
x
t1.root
t2.root
the
root
, after verifying that the tree is not empty. An alternative traversal
strategy that can be implemented is level-order traversal. We discuss these tra-
versal routines in Section 18.4. Routines to make an empty tree and test for
emptiness are given, with their inline implementations, at lines 35 to 38, as
are routines to compute the tree's size and height. Note that, as
size
and
height
are static methods in
BinaryNode
, we can call them by simply using
BinaryNode.size
and
BinaryNode.height
.
The last method in the class is the
merge
routine, which uses two trees—
t1
and
t2
—and an element to create a new tree, with the element at the root and
the two existing trees as left and right subtrees. In principle, it is a one-liner:
The
merge
routine is
a one-liner in prin-
ciple. However, we
must also handle
aliasing, ensure
that a node is not in
two trees, and
check for errors.
We set the original
trees'
root
to
null
so that each node
is in one tree.
root = new BinaryNode<AnyType>( rootItem, t1.root, t2.root );
If things were always this simple, programmers would be unemployed. Fortu-
nately for our careers, there are a host of complications. Figure 18.14 shows
the result of the simple one-line
merge
. A problem becomes apparent: Nodes in
t1
and
t2
's trees are now in two trees (their original trees and the merged
result). This sharing is a problem if we want to remove or otherwise alter sub-
trees (because multiple subtrees may be removed or altered unintentionally).
The solution is simple in principle. We can ensure that nodes do not
appear in two trees by setting
t1.root
and
t2.root
to
null
after the
merge
.
Complications ensue when we consider some possible calls that contain
aliasing:
If the two input
trees are aliases,
we should disallow
the operation
unless the trees
are empty.
t1.merge( x, t1, t2 );
t2.merge( x, t1, t2 );
t1.merge( x, t3, t3 );
The first two cases are similar, so we consider only the first one. A diagram of
the situation is shown in Figure 18.15. Because
t1
is an alias for the current
object,
t1.root
and
root
are aliases. Thus, after the call to
new
, if we execute
t1.root=null
, we change
root
to the
null
reference, too. Consequently, we
need to be very careful with the aliases for these cases.
Search WWH ::
Custom Search