clark
cs1713
syllabus
lecture notes
trees
Trees also support a variable number of elements, but can
provide faster search than linked lists.
programming assignments
homework
Example #1: Binary Tree
A binary tree has one information area and two pointers: left
for nodes having a key value less than the node's key and
right for nodes having a key value greater than the node's
key.
Trees have a root node (i.e., starting node), intermediate
nodes, and leaf nodes (have no children).
The depth of a tree is the length of its longest path. The
depth for the binary in example #1 is 3. For example #2, what
is the depth?
A tree is considered balanced if each node is balanced: the
absolute value of the difference in depth of its left subtree
and depth of its right subtree is <= 1.
If a tree is balanced, how many comparisons does it take to
reach a particular value?
What is the worst case?
Example #2:
another binary tree
set up
Worst Case Unbalanced Tree
The worst case of being unbalanced is when none of the
nodes has more than 1 child and the depth is greater than 2.
which nodes are balanced?
Example #3
Binary Tree Node
For pointers, we define a left pointer and a right pointer.
malloc() is used to allocate a node.
typedef struct NodeT
{
int iInfo;
// info might be several attributes or just one
struct NodeT *pLeft;
struct NodeT *pRight;
} NodeT;
NodeT *pRoot = NULL;
Searching for a value - iterative algorithm
Assume iMatch contains the value to find. If not found,
pFound should be NULL; if found, it should point to the node
with the matched value.
// iterative algorithm
p = pRoot;
pFound = NULL;
while (p != NULL)
{
Search simply follows the pointers. If the match < node's
value, follow to the left. If the match > node's value, follow to
the right.
Searching for a value - recursive algorithm
searchT() should return a pointer to the node containing
match or NULL if not found.
What are the cases?
1. Empty Tree or subtree: if the parameter, p, is NULL,
return NULL for not found.
2. Match: if our new value matches the current node's
value, return the pointer to the current node
3. Less than: if our new value < current node's value, return
the search of the left subtree.
4. Greater than: if our new value > current node's value,
return the search of the right subtree.
Count the nodes in a tree
Show the function countT(NodeT *p) which returns a count of
the nodes in the tree.
What are the termination / special cases?
Empty tree or subtree.
The corresponding iterative solution is very difficult for
visiting each node since iterative doesn't easily support
backing up.
Tree traversals
How can we traverse the entire tree touching every node?
The order with which you "visit" (or process) a tree node may
be important to certain algorithms. Three orders:
if (iMatch == p->iInfo)
{
pFound = p;
break;
}
else if (iMatch < p->iInfo)
p = p->pLeft;
else
p = p->pRight;
}
// at this point, if pFound is NULL, iMatch wasn't found
// recursive searchT function
NodeT *searchT(NodeT *p, int iMatch)
{
if (p == NULL)
return NULL;
if (iMatch == p->iInfo)
return p;
if (iMatch < p->iInfo)
??
else
??
}
int countT(NodeT *p)
{
??
}
// This is an example of how wonderful recursion is
// Simple in order algorithm for printing the contents
void printInOrder(NodeT *p)
{
if (p == NULL)
return;
printInOrder (p->pLeft);
preorder - node itself, left, right
in order - left, node itself, right
post order - left, right, node itself
With in order, what order are the values printed?
Exercise: Show a recursive printPreOrder(NodeT *p)
printf("%d\n", p->iInfo);
printInOrder (p->pRight);
}
// let's practice tracing the algorithm (show trace on the board)
With in order, the values are visited in order.
void printPreOrder(NodeT *p)
{
??
}
Exercise: Show a recursive function, prettyPrint, which
shows children indented from the parent. This helps show
the tree's shape.
For the first example, we would show:
50
What side is printed first?
45
40
30
20
10
After inserting 25:
50
45
40
30
25
20
10
After inserting 35:
50
45
40
35
30
25
20
10
After inserting 70:
70
50
45
40
35
30
25
20
10
// initial call:
prettyPrintT(pRoot, 0);
void prettyPrintT(NodeT *p, int iIndent)
{
int i;
if (p == NULL)
return;
??
}
Exercise: Show a recursive function sumTree() which sums
the contents of the iInfo values.
int sumTree(NodeT *p)
{
??
}
Recursive *insertT (NodeT *p, int iMatch)
Like with linked lists, there are two major approaches to
implement a recursive function for insertion into an ordered
binary tree:
Reconstruct - reconstruct the tree recursively
This approach adds a new node, but it
reassigns the left and right pointers for
any path we follow.
Each call returns a subtree which gets
assigned to the pointer from which the
subtree began.
When a subtree is NULL, we know we
didn't find the value. The new node
belongs on this subtree so we return an
allocated new node.
Utilize By Address - take advantage of C's by address
parameter passing
Only change the pointers that are
needed.
Java cannot use this approach since it
doesn't support by address parameter
passing
In C, this approach may look confusing
due to how C passes parameters.
We will use the reconstruct approach in this course. If you
would like to see the other approach, please refer to my
CS2123 course notes.
Notice that we only insert a new node where there is a NULL
pointer!
Cases to consider:
1. Empty Tree or subtree: The parameter, p, is NULL. This
is where the new node belongs.
2. Match: if our new value matches the current node's
value, return the pointer to the current node which
effectively returns the current subtree.
3. Less than: if our new value < current node's value, set
current node's left pointer to the result of recursing to
insertT with the left subtree.
// initial call, which assigns/reassigns the pointer to the root
pRoot = insertT(pRoot, 25);
// Function definition
NodeT *insertT(NodeT *p, int iInfo)
{
if (p == NULL)
return allocateNodeT(iInfo);
if (iInfo == p->iInfo)
return p;
else if (iInfo < p->iInfo)
p->pLeft = ??
else
p->pRight = ??
return p;
}
4. Greater than: if our new value > current node's value, set
current node's right pointer to the result of recursing to
insertT with the right subtree.
Show the allocateNodeT function which allocates a NodeT,
initializes it, and returns a pointer to it.
NodeT *allocateNodeT(int iInfo)
{
NodeT *pNew;
// to allocate a new node
pNew = (NodeT *) malloc(sizeof(NodeT));
if (pNew == NULL)
exitError("Memory allocation error", "");
pNew->iInfo = iInfo;
pNew->pLeft = NULL;
pNew->pRight = NULL;
return pNew;
}
Show code for the function equalTrees which returns TRUE if
its two trees have the same iInfo values and shape.
int equalTrees(NodeT *pA, NodeT *pB)
{
??
}
© Copyright 2026 Paperzz