0% found this document useful (0 votes)
4 views42 pages

Lecture12 Handouts Proto

The document covers binary tree traversals, including pre-order, in-order, post-order, and level-order traversals, along with their algorithms and applications. It also discusses binary search trees (BSTs), their properties, operations, and search algorithms. Additionally, it includes challenges for evaluating expressions using trees and implementing functions for tree operations.

Uploaded by

Abbas Sarfraz
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
4 views42 pages

Lecture12 Handouts Proto

The document covers binary tree traversals, including pre-order, in-order, post-order, and level-order traversals, along with their algorithms and applications. It also discusses binary search trees (BSTs), their properties, operations, and search algorithms. Additionally, it includes challenges for evaluating expressions using trees and implementing functions for tree operations.

Uploaded by

Abbas Sarfraz
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 42

1

Lecture #12
• Binary Tree Traversals
• Evaluate Expressions Using

• Binary Search Trees


• Binary Search Tree Operations
– Searching for an item
– Inserting a new item
– Finding the minimum and maximum items
– Printing out the items in order
– Deleting the whole tree
Binary Trees, Cont.
2
3

Binary Tree Traversals Cont.


Ok, we just saw the pre-order traversal in action!

To avoid boredom, we'll skip a detailed trace-


through of in-order and post-order traversals.

But... you probably want to understand how the


ordering differs between the three approaches.

So here's a little trick to


help you do just that!
An Easy Way to Remember the Order of
4

Pre/In/Post Traversals

Starting just above-left of the root


node, draw a loop counter-clockwise
around all of the nodes.

Ok, got that?


5

Pre-order Traversal: Dot on the LEFT


To determine the order of nodes
1
visited in a pre-order traversal…

2 7
Draw a dot next to each node as
3 4 8 you pass by its left side.
5 6 9

The order you draw the dots is the


Pre-order: order of the pre-order traversal!
FBADCEGIH
6

In-order Traversal: Dot UNDER the node


To determine the order of nodes
visited in a in-order traversal…
6

2 7 Draw a dot next to each node as


you pass by its under-side.
1 4 9

3 5 8 The order you draw the dots is the


order of the in-order traversal!
In-order:
ABCDEFGHI
7

Post-order Traversal: Dot on the RIGHT


To determine the order of nodes
9
visited in a post-order traversal…

5 8
Draw a dot next to each node as
1 4 7 you pass by its right side.
2 3 6
The order you draw the dots is the
order of the post-order traversal!
Post-order:
ACEDBHIGF
Tree Traversal Challenge!
8

Each node below contains a single word.


A post-order traversal results in the
sentence: Hungry clock goes back four seconds

???

??? ???

void postorder(Node *p)


??? {
if (p == nullptr)
return;
postorder(p->left);
??? ??? postorder(p->right);
cout << p->value;
}

Figure out the contents of each node.


Binary Tree Traversals
9

Ok, now we know how the recursive tree-traversals work.

Now let's see the level-order traversal.

The level-order traversal doesn't use recursion!

Instead it uses a queue, and processes the nodes of our


tree much like we did with our maze search!
(in breadth-first order)

Pre-order In-order Post-order Level-order


Traversal Traversal Traversal Traversal

These traversals all use recursion, and are nearly L.O.T. uses a
identical algorithms. breadth-first-search
to visit nodes.
10

The Level Order Traversal


In a level order traversal we visit each level’s nodes, from
left to right, before visiting nodes in the next level.
temp
Here’s the algorithm:
700
1. Use a temp pointer variable and 700 root
“a”
a queue of node pointers. 720780

2. Insert the root node pointer


720 780
into the queue. “b” “c”
800760 NULL 900
3. While the queue is not empty:
A. Dequeue the top node
800 “d” 760 “e” 900 “f”
pointer and put it in temp. NULL NULL NULL NULL NULL NULL

B. Process the node.


C. Add the node’s children to
queue if they are not NULL.
a bc d e f front rear
11

Level-order Traversal: Level-by-level


To determine the order of nodes
1
visited in a level-order traversal…
2 3
Start at the top node and draw a
4 5 6
horizontal line left-to-right
7 8 9 through all nodes on that row.

Repeat for all remaining rows.


Level-order:
FBGADICEH The order you draw the lines is the
order of the level-order traversal!
Traversal Big-Os
12

What are the Big-Os of our traversals?

Each of our traversals performs three operations per node:


They process the value in the current node.
They initiate processing of its left subtree.
They initiate processing of its right subtree.

So for a tree void PreOrder(Node *cur)


with n nodes, {
that’s 3*n if (cur == nullptr)
return;
operations, or…
cout << cur->value;

O(n)
We initiate processing of the
node’s left subtree. PreOrder(cur->left);
PreOrder(cur-> right);
}
13

Traversal Team Challenge


RULES Challenge: What order will the
• The class will split into following nodes be printed out if
left and right teams we use an in-order traversal?
• One student from each
team will come up to the
“Larry” root
board
• Each student can either
– write one new item or
“Fran” “Ronda”
– fix a single error in NULL
their teammates
solution
“Danny” “Jack” “Tom”
• Then the next two people NULL NULL NULL NULL NULL
come up, etc.
• The team that completes
“Sam”
their program first wins! NULL NULL
14

Traversal Use Case: Expression Evaluation

As we learned earlier, one use


(5+6)*(3-1)
for trees is representing
arithmetic expressions. ptr

Ok, so let's say we've stored


our expression in a tree...

How can we evaluate the tree


to get the answer?

Using a traversal, of course!


Expression Evaluation
15

• Here’s a function that computes the value of an expression tree. We start by passing in a pointer to
the root of the tree.
• Step #1 is the base case. It checks for a number in a node, and if it finds one, just returns the value
of the number. All leaf nodes will be numbers.
• Steps #2 and #3 evaluate the left and right subtrees of the current node, and gets their values.
• Then step #4 applies the operator (e.g., * sign) to the two results, and returns the overall result.
• This should look familiar! It’s a post-order traversal in disguise. We process the left and right
subtrees first, and then finally process the current node (the operator).
(5+6)*(3-1)
1. If the current node is a ptr
number, return its value.
cur
2. Recursively evaluate the left
subtree and get the result.
3. Recursively evaluate the right cur
subtree and get the result.
4. Apply the operator in the
current node to the left and
right results; return the
result.
16

Expression Evaluation
Which other algorithm does this remind you of?

1. If the current node is a int eval(Node *p) {


number, return its value. if (p->type == VALUE)
return p->value;
2. Recursively evaluate the left
subtree and get the result. int left = eval(p->left);
3. Recursively evaluate the right int right = eval(p->right);
subtree and get the result. return apply(p->operator,
left, right);
4. Apply the operator in the }
current node to the left and
right results; return result.
Answer: A post-order traversal!
Tree
Challenges
Tree recursion
problems are common
on exams and in
interviews, so let's do
a few challenges!

17
Binary Tree Challenge #1
18

Fill in the blanks to create a function called


int tree_sum(Node *root) which sums up a tree's values.

struct Node {
int val;
Node *left, *right;
};

int tree_sum(Node *root) {


if (root == ______) return ____;

int sum = _________;


sum += tree_sum(__________);
sum += tree_sum(__________);

return sum;
} }
return sum;

sum += tree_sum(root->right);
sum += tree_sum(root->left);
int sum = root->value;

if (root == nullptr) return 0;


int tree_sum(Node *root) {
19

Binary Tree Challenge #2


Fill in the blanks to create a function called
int shallowest_even(Node *root) that finds the depth of the
shallowest even number in a tree of at most 100 levels deep.

If there are no even numbers, it returns 100.

int shallowest_even(Node *root) {


level 0 9 if (root == nullptr) return _____;
level 1 7 2 if (root->val % 2 == 0) return _____;
int d1 = ____________________________;
level 2 1 int d2 = ____________________________;
level 3
if (d1 == ____ && d2 == ____)
4
return ____;
e.g., return min(d1, d2) + ____;
shallowest_even() }
would return 1 since
the 2 is at level 1 in
}
return min(d1, d2) + 1;
return 100;

the array, shallower


if (d1 == 100 && d2 == 100)
int d2 = shallowest_even(root->right);
int d1 = shallowest_even(root->left);

than the 4 at level 3


if (root->val % 2 == 0) return 0;
if (root == nullptr) return 100;
int shallowest_even(Node *root) {
Binary Search Trees
20

SEARCH
^
Binary Search Trees
What’s the big picture?
A binary search tree enables fast (log2N)
searches by ordering its data in a special way.
For every node j in the tree, all children to
j’s left must be less than it, and all children
to j’s right must be greater than it. e.g.,
Mae

Ebony Tina

Betty Nate Vera


Uses:
BSTs are used when
To see if a value V is in the tree: you need to quickly
1. Start at the root node search though loads
of data, and also
2. Compare V against the node, moving keep that data
down left or right if V is less or greater alphabetized.
3. Repeat until you find V or hit a dead end
22

Binary Search Trees


BST Definition: A Binary Search Tree is a binary tree
with the following property:

For every node X in the tree:


• All nodes in X’s left sub-
tree must be less than X. “Larry” root
• All nodes in X’s right sub-
tree must be greater than X.
“Fran” “Ronda”
NULL

• The tree to the right is a valid binary search tree. Let’s verify
this.
• Every value in Larry’s subtree is less than Larry. Every value in “Danny” “Jack” “Tom”
Larry’s right subtree is greater than Larry. NULL NULL NULL NULL NULL
• Every value in Fran’s subtree is less than Fran. Every value in
Fran’s right subtree is greater than Fran.
• Every value in Rhonda’s subtree is less than Rhonda (it’s
empty). Every value in Rhonda’s right subtree is greater than “Sam”
Rhonda.
NULL NULL
• The rest of the nodes are leaf nodes.
23

Binary Search Trees


Which of the following are valid binary search trees?

“Larry” “Larry” “Manny”


NULL

“Fran”
NULL “Fran” “Ronda” “Amy” “Nick”
NULL NULL NULL NULL NULL

“Danny”
NULL “Danny” “Nick” “Maddy”
NULL NULL NULL NULL NULL NULL

“Alex”
NULL NULL
right subtree.
• Invalid BST: Maddy is less than Manny but is in Manny’s
Larry!
• Invalid BST: Nick is in Larry’s left subtree, but Nick is >
• Valid BST
From left to right: •
24

Operations on a Binary Search Tree


Here’s what we can do to a BST:

• Determine if the binary search tree is empty


• Search the binary search tree for a value
• Insert an item in the binary search tree
• Delete an item from the binary search tree
• Find the height of the binary search tree
• Find the number of nodes and leaves in the
binary search tree
• Traverse the binary search tree
• Free the memory used by the binary search tree
Searching a BST
25

Input: A value V to search for


Output: TRUE if found, FALSE otherwise

Start at the root of the tree


Keep going until we hit the NULL pointer
If V is equal to current node’s value, then found!
If V is less than current node’s value, go left
If V is greater than current node’s value, go right
If we hit a NULL pointer, not found. “Larry”

• Let’s search for Gary.


• We start by comparing Gary to Larry, and find Gary is
less than Larry, so we go left.
“Fran” “Ronda”
• We then compare Gary to Fran, and find Gary is
NULL NULL
greater than Fran, so we go right.
• We then compare Gary to Gary, and find they’re equal.
We found our value!
“Barry” “Gary”
NULL NULL NULL NULL
Searching a BST
26

Start at the root of the tree


Keep going until we hit the NULL pointer
If V is equal to current node’s value, then found!
If V is less than current node’s value, go left
If V is greater than current node’s value, go right
If we hit a NULL pointer, not found. “Larry”

What nodes would we “Fran” “Ronda”


NULL NULL
have to examine to find
Dale and Randy?
“Barry” “Khang”
NULL NULL NULL

“Dale”
NULL NULL
Randy: >Larry, <Ronda, not found
Dale: <Larry, <Fran, >Barry, ==Dale
27

Searching a BST
Here are two different BST search algorithms in C++,
one iterative and one recursive:
bool Search(int v, Node *root)
{
Node *ptr = root; bool Search(int v, Node *ptr)
while (ptr != nullptr) {
{ if (ptr == nullptr)
if (v == ptr->value) return false; // nope
return true; else if (v == ptr->value)
else if (v < ptr->value) return(true); // found!!!
ptr = ptr->left; else if (v < ptr->value)
else return Search(V,ptr->left);
ptr = ptr->right; else
} return Search(V,ptr->right);
return false; // nope }
}
28

Recursive BST Search


Lets search for 14.

pRoot true
ptr-> 13
true

7 17
bool Search(int V, Node *ptr) NULL
true
{
if (ptr == nullptr)
3 14 19
return(false); // nope NULL NULL NULL NULL NULL NULL
else if (V == ptr->value)
return(true); // found!!!
int main(void)
else if (V < ptr->value)
return(Search(V,ptr->left)); {
else bool bFnd;
return(Search(V,ptr->right)); bFnd = Search(14,pRoot);
} }
29

Big Oh of BST Search


Question:
In the average BST with N values, 50% eliminated!
50%
how many steps are required to eliminated!
find our value?
50%
Right! log2(N) steps eliminated!
50%
eliminated!
Question:
In the worst case BST with
N values, how many steps are
required find our value?

Right! N steps

Question:
If there are 4 billion nodes in a BST, how
many steps will it take to perform a search? WOW!
Now that’s PIMP!
Just 32!
Inserting A New Value Into A BST
30

To insert a new node in our BST, we must place the


new node so that the resulting tree is still a valid BST!

“Larry”
Where would we add Carly
and Kory to our tree?
“Fran” “Ronda”
NULL NULL

“Barry” “Khang”
NULL NULL NULL

“Dale”
NULL NULL
Inserting A New Value Into A BST
31

Input: A value V to insert


If the tree is empty
Allocate a new node and put V into it
Point the root pointer to our new node. DONE!
Start at the root of the tree
While we’re not done…
If V is equal to current node’s value, DONE! (nothing to do...)
If V is less than current node’s value
If there is a left child, then go left
ELSE allocate a new node and put V into it, and
set current node’s left pointer to new node. DONE!

If V is greater than current node’s value


If there is a right child, then go right
ELSE allocate a new node and put V into it,
set current node’s right pointer to new node. DONE!
Now the C++ Code!
32

struct Node • Just as with a regular binary tree, we


use a node struct to hold our items.
{ • However let’s add a constructor to our
Node(const std::string &myVal) Node so we can easily create a new one!
{ • And our constructor initializes that root
pointer to nullptr
value = myVal; when we create a new tree. (This
left = right = nullptr; indicates the tree is empty)
}

std::string value;
Node *left,*right;
};
Now the C++ Code!
33

• And here’s our Binary Search Tree class.


class BinarySearchTree • Our BST class has a single member
{struct Node variable – the root pointer to the tree.
public:
{ • And our constructor initializes that root
pointer to nullptr
Node(const std::string &myVal) when we create a new tree. (This
BinarySearchTree()
{ indicates the tree is empty)
{ value = myVal; • Now let’s see our complete insertion
m_root = nullptr; function in C++.
left = right = NULL;
}}

void insert(const
std::string value;std::string &value)
{Node *left,*right;
}; …
}

private:

Node *m_root;
};
Now the C++ Code!
34
void insert(const std::string &value) If our tree is empty, allocate
{ Just as with a regular a new node and point the
binary tree, we use a node
if (m_root == nullptr) root pointer to it – then
{ m_root = struct
new Node(value);
to hold ourreturn;
items.} we’re done!

Node *cur = m_root;


Start traversing down from the root of
struct Node
for (;;)
the tree.
{ {
for(;;) is the same as an infinite loop.
if (value == cur->value) return;
Node(const std::string &myVal)
if (value < cur->value) If our value is already in
{ { the tree, then we’re
valueif= (cur->left
myVal; != nullptr) done - just return.
left = right
cur = NULL;
cur->left;
If the value to insert is less than the
} else
current node’s value, then go left.
{
cur->left = new Node(value);
std::string value; If there is a node to our left, advance
return; to that node and continue.
Node } *left,*right;
}; }
Otherwise we’ve found the proper spot for
else if (value > cur->value) our new value! Add our value as the left
{
child of the current node.
if (cur->right != nullptr)
cur = cur->right; If the value we want to insert is greater than
else the current node’s value, then traverse/insert
{ to the right.
cur->right = new Node(value);
return;
}
}
}
}
35
void insert(const std::string &value)
{
if (m_root == NULL) m_root
{ m_root = new Node(value); return; }
Node *cur = m_root;
for (;;)
cur→ “Larry”
{
if (value == cur->value) return;
?
if (value < cur->value) “Fran” “Ronda”
{
NULL NULL
if (cur->left != NULL)
cur = cur->left; ?
else
{ “Barry” “Phil”
cur->left = new Node(value); NULL NULL NULL NULL

return;
}
}
else if (value > cur->value) void main(void)
{ {
if (cur->right != NULL)
cur = cur->right; BinarySearchTree bst;
else bst.insert(“Larry”);
{
cur->right = new Node(value); ...
return;
} bst.insert(“Phil”);
}
} }
}
• Answers:
• Random: The numbers will form a “bushy” wide binary tree.
• Ordered: The numbers will look like a linked list going either left
(ordered in descending order) or right (ordered in ascending order).
Answer: Yes! Let's see on the board!
Thinking time!
e.g., 1,2,3,4,5,6,7 vs 4,2,6,1,3,5,7
into a tree impact the tree's structure?
Does the order in which you insert values
Inserting A New Value Into A BST
36
37

Big Oh of BST Insertion


So, what’s the big-oh of BST Insertion?
Right! It’s also O(log2n)

Why? Because we have to first use a binary search to find


where to insert our node and binary search is O(log2n).

Once we’ve found the right spot, we can insert our new
node in O(1) time.

Groovy Baby!
38

Finding Min & Max of a BST


How do we find the minimum and maximum values in a BST?
The minimum value is located at the left-most node.
The maximum value is located at the right-most node.
int GetMin(node *pRoot) int GetMax(node *pRoot)
{ {
if (pRoot == NULL) if (pRoot == NULL)
return(-1); // empty return(-1); // empty

while (pRoot->left != NULL) while (pRoot->right != NULL)


pRoot = pRoot->left; pRoot = pRoot->right;

return(pRoot->value); return(pRoot->value);
} }

“Larry”
Question: What’s the big-
oh to find the minimum or
“Fran” “Ronda” maximum element?
NULL NULL

“Barry” “Phil”
NULL NULL NULL NULL
Answer: O(log2(N))
39

Finding Min & Max of a BST


And here are recursive versions for you…

int GetMin(node *pRoot) int GetMax(node *pRoot)


{ {
if (pRoot == NULL) if (pRoot == NULL)
return(-1); // empty return(-1); // empty

if (pRoot->left == NULL) if (pRoot->right == NULL)


return(pRoot->value); return(pRoot->value);

return(GetMin(pRoot->left)); return(GetMax(pRoot->right));
} }

Hopefully you’re getting the idea that most tree


functions can be done recursively…
40

Printing a BST In Alphabetical Order

Can anyone guess what algorithm we use to print



out a BST in alphabetical order? cur “jane” root
• You guessed it! Using an “in-order” traversal on a
binary search tree will print it in alphabetical
order! Neat!
cur “danny” cur “waa”
NULL NULL

“bill” “frank”
cur NULL NULL NULL NULL cur
void InOrder(Node *cur)
{ Big-oh Alert!
if (cur == nullptr) // if empty, return… Output:
return; the big-Oh of printing
So what’s
bill
all the items in the
InOrder(cur->left); tree?
// Process nodes in left sub-tree.
danny
cout << cur->value; // Process the current node. frank
Right! O(n) since we have to visit jane
InOrder(cur->right); // Process nodes in right sub-tree.
} and print all n items. waa
41

Freeing The Whole Tree


When we are done with our BST, we have to free every
node in the tree, one at a time.

What recursive traversal algorithm


could we adapt to free our tree?
void FreeTree(Node *cur)
{
if (cur == nullptr) // if empty, return…
return;
FreeTree(cur->left); // Delete nodes in left sub-tree.
FreeTree (cur->right); // Delete nodes in right sub-tree.

delete cur; // Free the current node


}
Then we delete the current node once all of its children are gone. •
Then we free the right subtree of the current node. •
First we free the left subtree of the current node. •
It is basically a post-order traversal. •
The algorithm to free the whole tree is below. •
42

Freeing The Whole Tree


cur-> “Larry”

cur-> “Fran” “Ronda”


NULL NULL

void FreeTree(Node *cur)


{ Big-oh Alert!
if (cur == nullptr)
return;
And so on…
So what’s the big-Oh of freeing
FreeTree(cur->left);
all the items in the tree?
FreeTree (cur-> right);
It’s still O(n) since we have
delete cur;
} to visit all n items.

You might also like