Suggestions
Use up and down arrows to review and enter to select.Please wait while we process your payment
If you don't see it, please check your spam folder. Sometimes it can end up there.
If you don't see it, please check your spam folder. Sometimes it can end up there.
Please wait while we process your payment
By signing up you agree to our terms and privacy policy.
Don’t have an account? Subscribe now
Create Your Account
Sign up for your FREE 7-day trial
Already have an account? Log in
Your Email
Choose Your Plan
Individual
Group Discount
Save over 50% with a SparkNotes PLUS Annual Plan!
Purchasing SparkNotes PLUS for a group?
Get Annual Plans at a discount when you buy 2 or more!
Price
$24.99 $18.74 /subscription + tax
Subtotal $37.48 + tax
Save 25% on 2-49 accounts
Save 30% on 50-99 accounts
Want 100 or more? Contact us for a customized plan.
Your Plan
Payment Details
Payment Summary
SparkNotes Plus
You'll be billed after your free trial ends.
7-Day Free Trial
Not Applicable
Renews December 15, 2023 December 8, 2023
Discounts (applied to next billing)
DUE NOW
US $0.00
SNPLUSROCKS20 | 20% Discount
This is not a valid promo code.
Discount Code (one code per order)
SparkNotes PLUS Annual Plan - Group Discount
Qty: 00
SparkNotes Plus subscription is $4.99/month or $24.99/year as selected above. The free trial period is the first 7 days of your subscription. TO CANCEL YOUR SUBSCRIPTION AND AVOID BEING CHARGED, YOU MUST CANCEL BEFORE THE END OF THE FREE TRIAL PERIOD. You may cancel your subscription on your Subscription and Billing page or contact Customer Support at custserv@bn.com. Your subscription will continue automatically once the free trial period is over. Free trial is available to new customers only.
Choose Your Plan
For the next 7 days, you'll have access to awesome PLUS stuff like AP English test prep, No Fear Shakespeare translations and audio, a note-taking tool, personalized dashboard, & much more!
You’ve successfully purchased a group discount. Your group members can use the joining link below to redeem their group membership. You'll also receive an email with the link.
Members will be prompted to log in or create an account to redeem their group membership.
Thanks for creating a SparkNotes account! Continue to start your free trial.
Please wait while we process your payment
Your PLUS subscription has expired
Please wait while we process your payment
Please wait while we process your payment
Note: This guide is not intended to be an introduction to trees. If you have not yet learned about trees, please see the SparkNotes guide to trees. This section will only briefly review the basic concepts of trees.
A tree is recursive data type. What does this mean? Just as a recursive function makes calls to itself, a recursive data type has references to itself.
Think about this. You are a person. You have all the attributes of being a person. And yet the mere matter that makes you up is not all that determines who you are. For one thing, you have friends. If someone asks you who you know, you could easily rattle off a list of names of your friends. Each of those friends you name is a person in and of themselves. In other words, part of being a person is that you have references to other people, pointers if you will.
A tree is similar. It is a defined data type like any other defined data type. It is a compound data type that includes whatever information the programmer would like it to incorporate. If the tree were a tree of people, each node in the tree might contain a string for a person's name, an integer for his age, a string for his address, etc. In addition, however, each node in the tree would contain pointers to other trees. If one was creating a tree of integers, it might look like the following:
Notice the lines struct _tree_t_ *left and struct _tree_t_ *right;. The definition of a tree_t contains fields that point to instances of the same type. Why are they struct _tree_t_ *left and struct _tree_t_ *right instead of what seems to be more reasonable, tree_t *left and tree_t *right? At the point in compilation that the left and right pointers are declared, the tree_t structure has not been completely defined; the compiler doesn't know it exists, or at least doesn't know what it refers to. As such, we use the struct _tree_t_ name to refer to the structure while still inside it.
Some terminology. A single instance of a tree data structure is often referred to as a node. The nodes that a node points to are called children. A node that points to another node is referred to as the child node's parent. If a node has no parent, it is referred to as the root of the tree. A node that has children is referred to as an internal node, while a node that has no children is referred to as a leaf node.
The above data structure declares what's known as a binary tree, a tree with two branches at each node. There are many different kinds of trees, each of which has its own set of operations (such as insertion, deletion, search, etc), and each with its own rules as to how many children a node can have. A binary tree is the most common, especially in introductory computer science classes. As you take more algorithm and data structure classes, you'll probably start to learn about other data types such as red-black trees, b-trees, ternary trees, etc.
As you've probably already seen in previous aspects of your computer science courses, certain data structures and certain programming techniques go hand in hand. For example, you will very rarely find an array in a program without iteration; arrays are far more useful in combination with loops that step through their elements. Similarly, recursive data types like trees are very rarely found in an application without recursive algorithms; these too go hand in hand. The rest of this section will outline some simple examples of functions that are commonly used on trees.
As with any data structure that stores information, one of the first things you'd like to have is the ability to traverse the structure. With arrays, this could be accomplished by simple iteration with a for() loop. With trees the traversal is just as simple, but instead of iteration it uses recursion.
There are many ways one can imagine traversing a tree such as the following:
Three of most common ways to traverse a tree are known as in-order,
pre-order, and post-order. An in-order traversal is one of the easiest
to think about. Take a ruler and place it vertically left of the image
of the tree. Now slowly slide it to the right, across the image, while
holding it vertically. As it crosses a node, mark that node. An inorder
traversal visits each of the nodes in that order. If you had a tree that
stored integers and looked like the following:
Look at the above tree again, and look at the root. Take a piece of paper and cover up the other nodes. Now, if someone told you that you had to print out this tree, what would you say? Thinking recursively, you might say that you would print out the tree to the left of the root, print out the root, and then print out the tree to the right of the root. That's all there is to it. In an in-order traversal, you print out all of the nodes to the left of the one you're on, then you print yourself, and then you print out all of the ones to the right of you. It's that simple. Of course, that's just the recursive step. What's the base case? When dealing with pointers, we have a special pointer that represents a non-existent pointer, a pointer that points to nothing; this symbol tells us that we should not follow that pointer, that it is null and void. That pointer is NULL (at least in C and C++; in other languages it is something similar, such as NIL in Pascal). The nodes at the bottom of the tree will have children pointers with the value NULL, meaning they have no children. Thus, our base case is when our tree is NULL. Easy.
Isn't recursion wonderful? What about the other orders, the pre- and post- order traversals? Those are just as easy. In fact, to implement them we only need to switch the order of the function calls inside the if() statement. In a preorder traversal, we first print ourself, then we print all the nodes to the left of us, and then we print all the nodes to the right of ourself.
And the code, similar to the in-order traversal, would look something like this:
In a post-order traversal, we visit everything to the left of us, then everything to the right of us, and then finally ourself.
And the code would be something like this:
As mentioned above, there are many different classes of trees. One such class is a binary tree, a tree with two children. A well-known variety (species, if you will) of binary tree is the binary search tree. A binary search tree is a binary tree with the property that a parent node is greater than or equal to its left child, and less than or equal to its right child (in terms of the data stored in the tree; the definition of what it means to be equal, less than, or greater than is up to the programmer).
Searching a binary search tree for a certain piece of data is very simple. We start at the root of the tree and compare it to the data element we're searching for. If the node we're looking at contains that data, then we're done. Otherwise, we determine whether the search element is less than or greater than the current node. If it is less than the current node we move to the node's left child. If it is greater than the current node, we move to the node's right child. Then we repeat as necessary.
Binary search on a binary search tree is easily implemented both iteratively and recursively; which technique you choose depends on the situation in which you are using it. As you become more comfortable with recursion, you'll gain a deeper understanding of when recursion is appropriate.
The iterative binary search algorithm is stated above and could be implemented as follows:
We'll follow a slightly different algorithm to do this recursively. If the current tree is NULL, then the data isn't here, so return NULL. If the data is in this node, then return this node (so far, so good). Now, if the data is less than the current node, we return the results of doing a binary search on the left child of the current node, and if the data is greater than the current node, we return the results of doing a binary search on the right child of the current node.
The size of a tree is the number of nodes in that tree. Can we write a function to compute the size of a tree? Certainly; it only takes two lines when written recursively:
What does the above do? Well, if the tree is NULL, then there is no node in the tree; therefore the size is 0, so we return 0. Otherwise, the size of the tree is the sum of the sizes of the left child tree's size and the right child tree's size, plus 1 for the current node.
We can compute other statistics about the tree. One commonly computed value is the height of the tree, meaning the longest path from the root to a NULL child. The following function does just that; draw a tree, and trace the following algorithm to see how it does it.
Not all functions on tree's take a single argument. One could imagine a function that took two arguments, for example two trees. One common operation on two trees is the equality test, which determines whether two trees are the same in terms of the data they store and the order in which they store it.
As an equality function would have to compare two trees, it would need to take two trees as arguments. The following function determines whether or not two trees are equal:
How does it determine equality? Recursively, of course. If either of the trees is NULL, then for the trees to be equal, both need to be NULL. If neither is NULL, we move on. We now compare the data in the current nodes of the trees to determine if they contain the same data. If they don't we know that the trees are not equal. If they do contain the same data, then there still remains the possibility that the trees are equal. We need to know whether the left trees are equal and whether the right trees are equal, so we compare them for equality. Voila, a recursive tree equality algorithm.
Please wait while we process your payment