Data Structures and Algorithms:
Annotated Reference with Examples
First Edition
Copyright Granville Barnett, and Luca Del Tongo 2008.
Available at: dotnetslackers.com
In my last post, I left off without covering the tree traversals. There are four different tree traversals I plan to cover: Inorder, Preorder, Postorder and Breadth First. The first three (Inorder, Preorder, Postorder) are considered depth first traversals, while the last one is a breadth first (as the name implies). These algorithms are not too complex and writing out the code for them isn't too bad either.
Starting with the Inorder traversal, let's go over how it works. Inorder just gives you the tree in order of smallest to largest (dependent on what your value system is). This is accomplished by getting the value from the left node, then it's parent, then the right node. This is also done recursively so, we start at the left most node of the tree and work our way to the right most node. Let's take this tree for example:
The Inorder traversal for this tree would be as follows: 1, 3, 4, 6, 7, 8, 10, 13, 14. This might make more sense once you see the code:
void GetInOrder(TreeNode* parentNode) {
if (parentNode != NULL) {
GetInOrder(parentNode->left); // We continue to check the left node of whatever the parent node is
std::cout << parentNode->data << std::endl; // At the left most node we print out (get/compare/whatever needs to be done) the value
GetInOrder(parentNode->right); // Then we check the right most node.
}
}
Once you understand how that function is working, Preorder and Postorder are quite similar (writing the functions for them) and easy to grasp as well. Preorder gives you the tree starting at the root and continuing left to the the left most node, then the right nodes follow. That is, we check the root, head to the left subtree, check the root, head to the left subtree and continue this process until we have reached the left most node. Once at the left most subtree, we begin to check the right subtrees.
Postorder gives you the tree starting at the left most subtree, then right child, left child and root. From there it works it's way up, back at the root to the right child and its left most subtree. Using our example tree above, this will look like:
Preorder: 8, 3, 1, 6, 4, 7, 10, 14, 13
Postorder: 1, 4, 7, 6, 3, 13, 14, 10, 8
Here is the code for these two functions:
void GetPreOrder(TreeNode* parentNode) {
if (parentNode != NULL) {
std::cout << parentNode->data << std::endl;
GetPreOrder(parentNode->left);
GetPreOrder(parentNode->right);
}
}
and void GetPostOrder(TreeNode* parentNode) {
if (parentNode != NULL) {
GetPostOrder(parentNode->left);
GetPostOrder(parentNode->right);
std::cout << parentNode->data << std::endl;
}
}
Lastly, let's go over the Breadth First traversal. Breadth first means we traverse the tree by level, where we visit every node on a level before going to a lower level from left to right. I suppose we could also go from right to left but, it seems to be more common to do left to right so that's how I wrote my implementation. Using the example tree above we get:Breadth First: 8, 3, 10, 1, 6, 14, 4, 7, 13
The code for this function is a little more difficult than the others because it requires that we keep track of which node we are currently visiting.
void GetBreadthFirstOrder(TreeNode* parentNode) {
std::queue q; //Create queue to keep track of tree.
while (parentNode != NULL) {
std::cout << parentNode->data << std::endl; //print which node we're on
if (parentNode->left != NULL) {
q.push(parentNode->left); // store the left node
}
if (parentNode->right != NULL) {
q.push(parentNode->right); // store the right node
}
if (q.empty()) { // if we have gone queued all the nodes, we're done
parentNode = NULL;
} else {
parentNode = q.front(); // take node out of queue for printing (First In, First Out)
q.pop(); // remove the node
}
}
}
These traversals generally run in linear time ($O(n)$) so, while they aren't as quick as the searching, inserting and deleting algorithms, they are not terrible. I left out a few algorithms the book covers and possibly others on purpose. Some, I think, are simple enough and some I may not understand well enough. For now that's all I intend to cover on binary search trees.
Any questions, comments and/or suggestions are appreciated.
