Implementation of Trees
Implementation with Arrays
This section provides an alternate way to implement trees in C. As described above, the purpose of showing this implementation is because it involves using arrays, which are linear, meaning all the data is in a line, to implement trees, where data is stored hierarchically.
As you can see, we will be considering only a binary tree for this example, but the same technique could be used for a tree where all nodes had 3 children, 4 children, etc. There are a few inherent limitations to this method. The first is that because it uses a static array, the fixed size of the array means that there is a fixed maximum size for the tree. In general, this method requires deciding the maximum depth of the tree beforehand. The next step is to figure out how many nodes a complete tree of that size would require. Consider first the case of a binary tree. There is one node of depth 0. That one node has two children which are at depth 1. Each of those two have two children which are at depth 2. The following table shows the progression.
etc. We can see that the number of nodes doubles with each deeper level. In general, at depth n, there will be 2n nodes. The total number of nodes in a tree of depth n is 2( n + 1) - 1 . This general sum makes sense because the number of nodes at depth n is one more than the total of all of the previous nodes.
Once you have determined the maximum number of nodes that there can be, you then need to make a type which holds an array that contains that many cells. Assume that each element in the tree is of the type data_t.
typedef data_t[MAX_NODES] tree_t;
In this example, we have stored the maximum number of nodes in a sharp defined constant. Note that this means that we need to know this number when we compile the program, as opposed to being able to calculate it at run time. If MAX_NODES can only be determined at run time, then you must allocate memory dynamically.
Now we need to figure out how we are actually going to use this array for our tree. To start with, the root of the tree is always in the zero cell.
/* We want to store the data from the left and the right children of node n * into the appropriate variables. */ data_t left_child, right_child; left_child = tree[2 * n + 1]; right_child = tree[2 * n + 2]; /* Realize that we have only copied the data value, but if we modify left * child * or right_child, we do not change the values in the tree. To do * that, we would * need to make left_child and right_child pointers to those * locations in the tree */
An inherent limitation to the array method is that cells will exist for nodes even when there is no data at those locations. For this reason you need to put some value in empty locations to indicate that they hold no data. Thus, this implementation of the array method will only work when the data is such that a sentinel value is available to indicate empty nodes. For example, if the data elements were positive integers, then a -1 might indicate empty. This could be done with a sharp define.
#define EMPTY -1
Note that this will only work when the empty value is not a possible data value, but the data_t can hold it. If the data elements could potentially be negative integers then -1 would not work.