12.07.2015 Views

A Practical Introduction to Data Structures and Algorithm Analysis

A Practical Introduction to Data Structures and Algorithm Analysis

A Practical Introduction to Data Structures and Algorithm Analysis

SHOW MORE
SHOW LESS
  • No tags were found...

Create successful ePaper yourself

Turn your PDF publications into a flip-book with our unique Google optimized e-Paper software.

Sec. 5.3 Binary Tree Node Implementations 167Figure 5.10 presents a sample node implementation. It includes two classesderived from class VarBinNode, named LeafNode <strong>and</strong> IntlNode. ClassIntlNode accesses its children through pointers of type VarBinNode. Functiontraverse illustrates the use of these classes. When traverse calls methodisLeaf, Java’s runtime environment determines which subclass this particular instanceof root happens <strong>to</strong> be <strong>and</strong> calls that subclass’s version of isLeaf. MethodisLeaf then provides the actual node type <strong>to</strong> its caller. The other member functionsfor the derived subclasses are accessed by type-casting the base class pointeras appropriate, as shown in function traverse.There is another approach that we can take <strong>to</strong> represent separate leaf <strong>and</strong> internalnodes, also using a virtual base class <strong>and</strong> separate node classes for the two types.This is <strong>to</strong> implement nodes using the composite design pattern. This approach isnoticeably different from the one of Figure 5.10 in that the node classes themselvesimplement the functionality of traverse. Figure 5.11 shows the implementation.Here, base class VarBinNode declares a member function traverse thateach subclass must implement. Each subclass then implements its own appropriatebehavior for its role in a traversal. The whole traversal process is called by invokingtraverse on the root node, which in turn invokes traverse on its children.When comparing the implementations of Figures 5.10 <strong>and</strong> 5.11, each has advantages<strong>and</strong> disadvantages. The first does not require that the node classes knowabout the traverse function. With this approach, it is easy <strong>to</strong> add new methods<strong>to</strong> the tree class that do other traversals or other operations on nodes of the tree.However, we see that traverse in Figure 5.10 does need <strong>to</strong> be familiar with eachnode subclass. Adding a new node subclass would therefore require modifications<strong>to</strong> the traverse function. In contrast, the approach of Figure 5.11 requires thatany new operation on the tree that requires a traversal also be implemented in thenode subclasses. On the other h<strong>and</strong>, the approach of Figure 5.11 avoids the need forthe traverse function <strong>to</strong> know anything about the distinct abilities of the nodesubclasses. Those subclasses h<strong>and</strong>le the responsibility of performing a traversal onthemselves. A secondary benefit is that there is no need for traverse <strong>to</strong> explicitlyenumerate all of the different node subclasses, directing appropriate action foreach. With only two node classes this is a minor point. But if there were manysuch subclasses, this could become a bigger problem. A disadvantage is that thetraversal operation must not be called on a null pointer, because there is no object<strong>to</strong> catch the call. This problem could be avoided by using a flyweight <strong>to</strong> implementempty nodes.Typically, the version of Figure 5.10 would be preferred in this example iftraverse is a member function of the tree class, <strong>and</strong> if the node subclasses are

Hooray! Your file is uploaded and ready to be published.

Saved successfully!

Ooh no, something went wrong!