Binary Trees

advertisement
trees
Trees also support a variable number of elements, but can
provide faster search than linked lists.
Example #1: Binary Tree
A binary tree has one information(aka element) 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.
Example #2:
another binary tree
If a tree is balanced, how many comparisons does it take to
reach a particular value?
What is the worst case?
which nodes are balanced?
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.
Example #3
Binary Tree Typedefs
For pointers, we define a left pointer and a right pointer.
malloc() is used to allocate a node.
typedef struct NodeT
{
Element element;
struct NodeT *pLeft;
struct NodeT *pRight;
} NodeT;
typedef struct
{
NodeT *pRoot;
} TreeImp;
typedef TreeImp *Tree;
Searching for a value - iterative algorithm
Assume match contains the value to find.
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.
If we hit NULL, there isn't a match.
Searching for a value - recursive algorithm
searchT() should return a pointer to the node containing
matchKey or NULL if not found.
What are the termination and special cases?
 Not found (hit NULL)
 Matched
Count the nodes in a tree
Show the function countT(NodeT *p) which returns a count of
the nodes in the tree.
// iterative algorithm
p = tree->pRoot;
pFound = NULL;
while (p != NULL)
{
if (matchKey == p->element.key)
{
pFound = p;
break;
}
else if (matchKey < p->element.key)
p = p->pLeft;
else
p = p->pRight;
}
// at this point, if pFound is NULL, matchKey wasn't found
// recursive searchT function
NodeT *searchT(NodeT *p, Key matchKey)
{
??
}
int countT(NodeT *p)
{
??
}
What are the termination / special cases?
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:
preorder - node itself, left, right
in order - left, node itself, right
post order - left, right, node itself
// Simple in order algorithm for printing the contents
void printInOrder(NodeT *p)
{
if (p == NULL)
return;
printInOrder (p->pLeft);
printf("%d\n", p->element.key); // visit
printInOrder (p->pRight);
}
// let's practice tracing the algorithm (show trace on the board)
With in order, what order are the values printed?
Exercise: Show a recursive printPreOrder(NodeT *p)
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
Our initial call:
prettyPrintT(tree->pRoot, 0);
void prettyPrintT(NodeT *p, int iIndent)
{
int i;
if (p == NULL)
return;
??
}
Exercise: Show a recursive function sumTree() which sums
the element.key values.
int sumTree(NodeT *p)
{
??
}
Recursive Insertion Using By Address Parameter Passing
As we did with linked lists, insertion with recursion can take
advantage of by address parameter passing.
Case 1:
Empty List
Initially, we pass &(tree->pRoot).
Case 1: Empty Tree
pp's value is the address of pRoot.
*pp is NULL
Case 2: Insert on a null left branch
Initially:
 pp's value is the address of pRoot
 *pp is the address of the node containing 30
If we traverse to the left, we want pp to point to the
address of the root's pLeft. At that point:
 pp's value is the address of pRoot->pLeft
 *pp is NULL
Case 2: Insert 20 on left branch
pp initially points to
pRoot
pp points to the root's pLeft
Recursive *insertT (NodeT **pp, Element value)
Notice that we didn't include pRoot as an additional
parameter since we are planning to pass the address of pRoot
to insertT and modify the value at the address if necessary.
Our call:
Element value = …;
NodeT *pNew;
Tree tree = newTree();
pNew = insertT(&(tree->pRoot), value);
allocateNodeT
This function allocates a binary tree node, assigns the element
value,, and initializes the pointers to NULL. It returns a
pointer to the newly allocated node.
NodeT *insertT(NodeT **pp, Element value)
{
// If *pp is null, this is where we want to insert.
if (*pp == NULL)
{
??
}
// does it match
if (value.key == (*pp)->element.key)
return *pp;
// which side should we follow
if (value.key < (*pp)->element.key)
return insertT(??, value);
else
return insertT(??, value);
}
NodeT *allocateNodeT(Element value)
{
NodeT *pNew = (NodeT *) malloc(sizeof(NodeT));
pNew->element = value;
pNew-pLeft = NULL;
pNew->pRight = NULL;
return pNew;
}
Download