However, a good programmer often seeks a bigger goal than "getting it to work". Often, the way you approach a problem is to break it down into small tasks. Each small task is easy to understand, easy to debug, easy to read. It's amazing how such a wonderful idea is often underused.
The reason this happens, I believe, is due to a fear of writing functions. Yes, some people just don't like to write functions. Oh the hassle, they exclaim! You have to write a signature (prototype) into the header file, and you have to decide what parameters you need, and you have to comment it. Why not just write one large function, and avoid dealing with all those issues?
There are many reasons for avoiding this path to code bloat. Code is harder to read. Code is harder to debug. It also discourages you from thinking about a problem by breaking it into small tasks.
This is why locate() is such a wonderful function. First, it treats the problem of adding and removing a node as having a common starting point: locating the node.
Thus, adding a node consists of locating where the node should be added, then adjusting pointers to add the node. Removing a node consists of locating the node, and then removing it.
Notice that there's two things happening. First, both add and remove rely on the same locate() function. Second, each function, add and remove, can be thought of as having two distinct parts: locate() and the operation after the locate().
To think about how profound this idea is, think about the following: would you have used a locate() function if you hadn't been told to use it? It's not an idea that would come naturally to most people. In fact, few books that explain linked lists or binary trees even use a locate(). They believe the action of adding a node is, yes, locating the correct location and placing the node, but as one contiguous whole, not as two parts.
In fact, when I first saw locate(), I felt that it was an unnecessary function, and that there was no need to separate this function out. In fact, I just didn't think about adding a node in terms of a locate().
However, since then, I've liked the idea quite a lot for the reasons stated above. Plus, the idea of a locate() works in linked lists and binary search trees. It's nice when an idea works on more than one data structure.
Let's look at a tree.
What should locate() do? Should it return NULL? That's one possibility, but it makes much more sense to return a pointer to 40. Once you have a pointer to 40, then the add() function can attach a new node containing 45 as the right child of 40.
Thus, if locate() can't find the node in the BST, it should stop at the node just before it "fell off the tree". That's the most useful node that locate() can return.
The example above is quite important, because you should notice something about locate(). It stopped at node 40. 40 is NOT a leaf. locate() does not always find a leaf. It does, on the other hand, always find a node with zero or one child. It never finds a node with two children. If it stopped at a node with two children, then it wouldn't do its job locating the nodes (because it could always look down lower in the tree to find the node).
In the picture below, you see locate() set curr and also parent. parent is always set to curr's parent. The only time when this doesn't happen is when curr stops at the root. In that case, parent is set to NULL.
Here's a version of locate() which uses loops.
// precondition: curr is at root (or root of a subtree), parent is NULL
bool BST::locate( int key, Node * & curr, Node * & parent )
{
bool inTree = true ;
while ( curr != NULL && curr->getData() != key && inTree )
{
if ( key < curr->getData() )
if ( curr->getLeft() != NULL )
{
parent = curr ;
curr = curr->getLeft() ;
}
else // about to fall off tree
inTree = false ;
else // key > curr->getData()
if ( curr->getRight() != NULL )
{
parent = curr ;
curr = curr->getRight() ;
}
else // about to fall off tree
inTree = false ;
}
return ( curr != NULL && curr->getData() == key ) ;
}
In this version,
This makes the loop much simpler to write (although you will write a recursive version).
// precondition: curr is at root (or root of a subtree), parent is NULL
bool BST::locate( int key, Node * & curr, Node * & parent )
{
while ( curr != NULL && curr->getData() != key )
{
parent = curr ; // parent trails curr
// Move curr left or right
if ( key < curr->getData() )
curr = curr->getLeft() ; // Move curr left
else // key > curr->getData()
curr = curr->getRight() ; // Move curr right
}
return ( curr != NULL && curr->getData() == key ) ;
}
This is much simpler, and you will probably want to base your
recursion on this version.