AuD
Lecture 'Algorithmen und Datenstrukturen' (code examples)
aud.BinarySearchTree< Key extends Comparable Class Template Reference

Base class for a binary search tree. More...

Detailed Description

Base class for a binary search tree.

Please keep in mind that this is an educational implementation. It is not optimized for a particular application!

Nodes

The internal node (nested class aud.BinarySearchTree.Node) representation is based on aud.BinaryTree!

On the one hand, this choice enables us to reuse functionality. On the other hand, there are some disadvantages due to extra data that is stored or extra indirections for method calls.

BinarySearchTree stores head as a dummy node (member head_), which has has by definition a smaller key than any other node.

This implementation does not use dummy nodes as leaves!

Note that aud.BinaryTree#getLeft and aud.BinaryTree#getRight also update links to parents! While this is convenient in many cases, it requires special care in other cases! (Explicitly setting null references, order of operations matters "even more".)

Nodes store key-value pairs

Each aud.BinarySearchTree.Node stores a key-value pair of type aud.BinarySearchTree.Entry as node data.

BinarySearchTree is a generic class, and Key and Value are provided as type parameters.

The Key type must be comparable, i.e., extend Comparable<key>.

The rationale of storing key-value pairs is that this way the search tree can be used to implement a set or a map:

  • A set stores only comparable objects, i.e., keys. This means that value attribute is not used.
  • A map associates each key with a value. This means that only the key is used for comparison (e.g., by find or insert). A typical application for maps are dictionaries.

Keys are compared by compareKeys, which treats null keys as smallest possible keys. Then head's key equals null by definition.

Finding entries of a binary search tree

The method find searches a an entry for a given key.

Low-level implementation

The core function used for finding (find) a node by key and also for finding an insertion point (for insert) is findLowerBound). It returns a aud.BinarySearchTree.LowerBound instance (which stores the node and some additional information).

High-level implementation: cursors and iterators

The method findEntry (and similarly getMinimum and getMaximum) returns a aud.BinarySearchTree.Cursor rather than a node.

aud.BinarySearchTree.Cursor provides a reference to nodes in the tree, i.e., allows access to its key and value.

The iterator() method implements Iterable<Cursor>, and this inorder traversal iterator provides Cursor instances. (With some more work, we could alternatively provide different iterators over keys, values, and key-value pairs/entries.)

Support for balancing

Although this is not a balanced tree, BinarySearchTree provides methods for local rebalancing. These are

  • restructure based on the implementation in Goodrich and Tamassia (2nd ed, chapter 7.4, p. 269).
    The following rotations are also based on this implementation!
  • rotateLeft single rotation, right-right case
  • rotateRight single rotation, left-left case
  • rotateLeftRight double rotation, left-right case
  • rotateRightLeft double rotation, right-left case

All of these call the auxiliary method setupBalancedSubtree (as does restructure) to perform the rotations. Note that an implementation that works exclusively with rotations (not restructuring), would probably implement the double rotations as a combination of two single rotations.

Especially restructure is used for balancing by sub-classes, e.g., aud.AVLTree.

The method restructure(char,Key) is provided for testing the above methods interactively (see aud.example.BinarySearchTreeExample).

Note that we excessively use the fact that an extra reference to the node's parent is stored. In fact, this allows a simple bottom-up processing as by restructure. A more (memory) efficient implementation would avoid these references at the cost of a more complex implementation!

Notifications on insert/remove

insert provides a callback onInsert to notify a subclass of a change of the tree. Similarly, remove invokes onRemove.

These callback can be used by sub-classes for balancing and visualization/output.

See also
aud.example.BinarySearchTreeExample

Definition at line 137 of file BinarySearchTree.java.


The documentation for this class was generated from the following file: