20 Trees
20 Trees
Slide 2
Announcements
Assignment 6 has been released
Slide 3
Today's Topics
Refresh on what pointer diagrams represent
Quick comment on references to pointers
Introduction to the tree data structure
You've already seen lots of tree structures
Tree terminology
How do we build trees programatically?
Writing code to traverse a tree
Slide 4
Notice that each function is passed in a reference to a pointer (and Question 7 on that part of the
assignment asks about this). The reference is necessary. If the parameter was not a reference, then the
pointer would be passed by value, and any changes to the pointer would not be reflected in the code that
called the function. Instead, the function would have to return the pointer so it could be updated in the
calling function. But, it is cleaner to call the functions so they actually change the front of the list to be
the first sorted element.
In our example from the last lecture, we didn't need to worry about this, because all of our class
functions had direct access to the front class variable. This isn't the case when we don't have a class (as
in the assignment).
See the code below that demonstrates why you need a reference to a pointer:
#include <iostream>
#include "console.h"
struct ListNode {
string data;
ListNode *next;
};
modify the
cout << ", ";
front
}
front = front->next; it tries to
}
cout << "]" << endl;
}
int main() {
ListNode *front = nullptr;
cout << "inserting cat correctly" << endl;
insertAtFrontCorrect(front, "cat");
Output:
Slide 6
Trees
We have seen trees in this class before, in the form of decision trees:
Trees can desceribe programs (we o!en call them abstract syntax trees (the following is a figure in an
academic paper written by a recent CS 106 student!)
Slide 7
from r.
(root)
-----------A--------------
↙ ↙ ↙ ↓ ↘ ↘
B C D E F-- G
↙ ↙ ↘ ↙ ↘ ↘ ↘
H I J K L M N
↙ ↘
O P
Slide 8
We can define a path from a parent to its children. The path from A to O is: A-E-J-O.
The path A-E-J-O has a length of three (the number of edges).
The depth of a node is the length from the root. The depth of node J is 2. The depth of the root is 0.
The height of a node is the longest path from the node to a leaf. The height of node F is 1. The height of
all leaves is 0.
The height of a tree is the height of the root. The height of the above tree is 3.
Slide 9
Tree nodes can only have one parent, and they cannot have cycles
X X
S N
↘ ↙
A
The following are not trees, either, because you can cycle through the tree. Nodes cannot have children
that are on the same level or above on the tree:
→→→→S
↑ ↙ ↘
↑ T A
↑↙
N
S
↙ ↘
T→→→A
↙
N
Slide 10
struct TreeNode {
string data;
TreeNode* left;
TreeNode* right;
};
We don't have to limit ourselves to binary trees, by the way: we could have a ternary tree:
struct TernaryTreeNode {
string data;
TernaryTreeNode* left;
TernaryTreeNode* middle;
TernaryTreeNode* right;
};
In fact, we could have any number of children (we may look at the Trie data structure, which can be built
this way). This is an N-ary Tree:
struct NAryTreeNode {
string data;
};
Vector<NAryTreeNode*> children;
trie
Slide 11
Traversing a Tree
O!en, we will want to do something with each node in a tree. Like linked lists, we can traverse the tree,
but it is more involved because of all the branching.
There are four di"erent ways to traverse a binary tree:
1. Pre-order
2. In-order
3. Post-order
4. Level-order
Let's write some code for each traversal to see the di"erences. We'll look at two trees:
this
↙ ↘
is written
↙ ↘ ↘
a correctly sentence
BinarySearch Tree
We'll also look at:
4
↙ ↘
2 5
↙ ↘ ↘
1 3 6
By the way: trees do not have to be complete, like heaps. Any node can have 0, 1, or 2 children.
Slide 12
Pre-order traversal
The algorithm for a pre-order traversal is as follows:
1. Do something
2. Go le!
3. Go right
05
This is an inherently recursive algorithm
For our tests, let's make the "Do something" part print the data at a particular node.
Code:
0
inOrder(tree->left);
cout<< tree->data <<" ";
inOrder(tree->right);
O
}
Slide 14
1 2 34 5 6
because we might Want
delete
to all
Post-order traversal important
The algorithm for a post-order traversal is as follows: the children before we delete their
1. Go le! parents
2. Go right
postorder
calculation
3. Do something
This is also an inherently recursive algorithm
56 t
Code:
Level-order traversal
A level order traversal is di"erent than all the others – we want to go one level at a time and print out
each node, then go to the next level.
For this tree, it would be:
this is written a correctly sentence
Can we do this recursively!
Not easily!
But, we can use an algorithm you've seen before: breadth first search!
This is going to use a queue, just like the BFS you did for your maze solving assignment.
1. Enqueue the root node
2. While the queue is not empty
dequeue a node
do something with the node
enqueue the le! child if it exists
enqueue the right child if it exists
Code:
void levelOrder(TreeNode *tree) {
Queue<TreeNode *>treeQueue;
treeQueue.enqueue(tree);
while (!treeQueue.isEmpty()) {
05
TreeNode *node = treeQueue.dequeue();
cout << node->value << " ";
if (node->left != nullptr) {
}
treeQueue.enqueue(node->left);
if (node->right != nullptr) {
O 0
treeQueue.enqueue(node->right);
}
Pjak
}
4
}
Queue
Queue
Ija 4 2
Queue
Fj 4 2 5