22 CompSci 105 What is a ? A tree is an abstraction for a hierarchical structure. Lecture 31 and 32 Content It is defined as a of points called nodes and a set of lines called edges where an edge connects two distinct nodes. Trees – a non‐linear data structure root A tree has three properties: Textbook: Chapter 6  One node is distinguished leaves and called the root.  Every node n other than the root is connected by an edge to exactly one other node p closer to the root.  A tree is connected in the sense that if we start at any node n other than the root and move to the parent of n, continue to the parent of the parent of n, and so on, we eventually reach the root.

Applications of Trees 33 Applications of Trees (Cont’d) 44

Examples and Applications of trees include: File Systems  Family trees  Directory structures (file system) A file system can be represented as a tree, with the top-most directory as  Arithmetic expressions the root  Game trees (finding winning positions in a game)  Binary (BSP) trees Desktop (finding visible objects in a scene)  Search trees (finding all the paths back out of a maze)  (for efficient terrain rendering) My My …  Octree (for fast collision detection, ray tracing etc.) Documents Computer  Constructed Solid Geometry (CSG) objects  Joint hierarchies (for skeletal animation of characters)  etc. A: C: D: E: …

CSi107 1 Applications of Trees (Cont’d) 55 Applications of Trees (Cont’d) 66

Arithmetic Expressions Organisational Charts and Family Trees

An arithmetic expression can be represented by a tree  the leave nodes are the variables/values  the internal nodes are the operations

+

(a+b) * (a-b) + 2 * 2

+ -

a b a b

Applications of Trees (Cont’d) 77 Applications of Trees (Cont’d) 88

Bone Hierarchies for Skeletal Animation Quadtrees for Terrain Rendering Moving a bone around a joint (e.g. the upper arm around the elbow) moves all child bones at the same time (e.g. the lower arm, hand and fingers) The terrain is divided into squares of different size: large squares for parts of the terrain far away from the viewer, and small squares for parts of the terrain close to the viewer. (Note: squares are divided into triangles in order to achieve continuity and faster rendering)

CSi107 2 Applications of Trees (Cont’d) 99 Applications of Trees (Cont’d) 1010 Octrees for Space Constructed Solid Geometry (CSG) Objects Partitioning A CSG object is defined by applying set  Divide 3d into cubes. operations (intersection, union, set  Subdivide cube if the object(s) it difference) to simpler objects, which contains are too complex (e.g. can be again CSG objects or can be for collision detection, primitive objects such as spheres, rendering, simulation) squares and cylinders.

1111 1212 Terminology Terminology (cont’d) Relationships Parts of a tree A – Data objects (the circles) in a tree are called node –A is a parent of B and B is a child nodes. A of A, if an edge points from A to B B C In the textbook a node has a – A node can have only one parent key (used to identify item) and a – If two nodes have the same payload (the actual data stored) B C parent they are siblings D E F – Links between nodes are called edges (always pointing edge A k-node tree is a tree downwards) where every node has D E F at most k children An empty tree is a tree without any nodes.

CSi107 3 1313 1414 Terminology (cont’d) Terminology (cont’d) A node a is an ancestor of a node n if we can reach n by starting from a and going to its parent, its parent's parent, Names for different Tree nodes root and so on. A node is regarded as its own ancestor unless –A node without parent is called root. we are talking about the proper ancestors. x – A node without child(ren) is called Internal A y z node A node d is a descendant of a node n if and only if n is an leaf. ancestor of d. A node is regarded as its own descendant – The non‐leave node are also called subtree uv B C unless we are talking about the proper descendants. internal nodes. w – A node and all of its descendants A path is a sequence of nodes n , n ,..., n such that n is the 1 2 k 1 form a subtree. parent of n2, n2 is the parent of n3, and so on. The length D E F of a path with k nodes is k‐1 (the number of edges (i.e. The tree is recursive in nature: forming the path). a tree is either empty or it is a node A path with a single node (k = 1) has a length of 0. whose children are trees) leaves

1515 1616 Terminology (cont’d) Terminology (cont’d)

The level (or depth) of a node n is Level of The height of a Tree is equal to the A is 0 equal to the number of edges maximum level of any node in the tree Height of the from the root to that node Level of – If a tree consist of only the root its tree is 2 B is 1 A height is 0 –0 if n is the root of the tree A – Otherwise the height is 1 greater than – 1+(level of parent of n), if n B C the height of its tallest subtrees is not the root. B C height(Tree) = 1 + max

Level of { height(subTree1), D is 2 D E F height(subTree ), … } 2 D E F

CSi107 4 1717 18 Fun with trees!!  Binary trees 2

7 5 • Draw as many different trees with A is one where the exactly four nodes as you can. maximum number of children from 1 6 9 any node is two. The tree on the • What is the maximum height right is an example of a binary tree. 5 11 4 of a tree with n nodes? What is its minimum height? • What tree has only leaf nodes and no internal nodes? What do we want to do with a tree object? • What is the maximum height of a 2-node tree with 7 • create a tree object nodes? What is its minimum height? • insert nodes • • Construct an Arithmetic Tree for the expression remove nodes • (2+4+7)*(2+8). traverse the tree

Binary trees 19 20 2 Binary trees – How to implement BinaryTree() – create a tree with one node Implementation 1: Use a list of lists to store the tree: 7 5 get_left_subtree() – return the binary • the first element is the value in the node, tree which is the left child. 1 6 9 • the second element is the list representing the left subtree, get_right_subtree() – return the binary • the third element is the list representing the right subtree tree which is the right child. 5 11 4 For example, 6 get_value() – return the object stored in the current tree node. 5 11 set_value(val) – store the object, val, in the current node. insert_left(val) – create a new binary tree and insert it as [ 6, the left child of the current node. tree [ 5, None, None], [11, None, None] insert_right(val) – create a new binary tree and insert it ] as the right child of the current node.

CSi107 5 Binary trees – List implementation 21 Binary trees – List implementation 22 class ListBinaryTree: def main(): def __init__(self, value): class ListBinaryTree: tree = ListBinaryTree(55) self.node = [data, None, None] def __init__(self, value): tree.insert_left(4) tree.insert_right(6) self.node = [value, None, None] def insert_left(self, value): new_node = ListBinaryTree(value, main() self.node[1], None) from ListBinaryTree import ListBinaryTree self.node[1] = new_node def main(): [ 55, def insert_right(self, value): tree [4, None, None], tree = ListBinaryTree(55) [6, None, None] new_node = ListBinaryTree(value, ] main() None, self.node[2] tree 55 self.node[2] = new_node tree 55 tree [ 55, None, None ] 4 6

Binary trees – List implementation 23 Binary trees – List implementation 24 class ListBinaryTree: class ListBinaryTree: def main(): def main(): def __init__(self, value): def __init__(self, value): tree = ListBinaryTree(55) tree = ListBinaryTree(55) self.node = [value,None, None] self.node = [value, None, None] tree.insert_left(4) tree.insert_left(4) tree.insert_right(6) right = ListBinaryTree(34) def insert_left(self, value): # see previous slides for more tree.set_value(16) tree.insert_tree_right(right) # see previous slide main() def get_left_subtree(self): def insert_right(self, value): return self.node[1] main() # see previous slide tree 16 def get_right_subtree(self): def set_value(self, new_value): return self.node[2] self.node[0] = new_value 55 4 6 tree def insert_tree_left(self, tree): def get_value(self): [ 16, self.node[1] = tree 4 34 return self.node[0] tree [4, None, None], [6, None, None] def insert_tree_right(self, tree): ] self.node[2] = tree

CSi107 6 Binary trees – __str__() 25 Show the tree and the output 26 from ListBinaryTree import ListBinaryTree class ListBinaryTree: def main(): 59 def __init__(self, value): def main(): tree = ListBinaryTree(55) self.node = [value,None, None] tree = ListBinaryTree(55) tree.insert_left(4) tree.insert_left(4) 4 6 tree.insert_right(6) def insert_left(self, value):# tree.insert_right(6) tree.set_value(tree.get_value() + 4) def insert_right(self, value):# print(tree) 55 print("1.", tree.get_value()) 59 def set_value(self, new_value):# right = tree.get_right_subtree() def get_value(self):# main() 4 6 left = tree.get_left_subtree() 4 6 def get_left_subtree(self):# left.insert_left(7) def get_right_subtree(self):# right.insert_right(2) [55, [4, None, None], 7 5 2 [6, None, None] ] right.insert_left(5) print("2.", tree.get_right_subtree(). def __str__(self): get_left_subtree().get_value()) n = self.node return "[" + n[0] + ", " + str(n[1]) + ", " + str(n[2]) + "]" print(tree) [59, [4, [7, None, None], None], main() [6, [5, None, None], [2, None, None]]]

Draw the tree 27 ListBinaryTree – readability 28 The output when the following code (just the skeleton class ListBinaryTree: #many methods are missing is shown here) is executed: DATA = 0 #constants forreadability tree = ListBinaryTree(…) LEFT = 1 The constructor, … RIGHT = 2 __init__() is not print(tree) def set_value(self, new_value): shown here self.node[self.DATA] = new_value is: [4, [11, None, None], [7, [8, None, None], [9, None, None]]] def get_left_subtree(self): 4 return self.node[self.LEFT] def get_right_subtree(self): Draw the tree: 11 7 return self.node[self.RIGHT] def __str__(self): return '['+str(self.node[self.DATA])+', ' + str(self.node[self.LEFT])\ 8 9 + ', ' + str(self.node[self.RIGHT]) + ']'

CSi107 7 Traversals of a Binary Tree 2929 Pre‐order Traversal of Binary Tree 3030

Visit each node in the tree def preorder(tree): Recursive structure of a Binary Tree if(tree!=None): print(tree.get_value(), end=" ") root, left subtree, right subtree preorder(tree.get_left_subtree()) Order of traversal preorder(tree.get_right_subtree()) Pre‐order traversal: root, traverse TL , traverse TR 1 In‐order traversal: traverse TL , root, traverse TR 60 Post‐order traversal: traverse TL , traverse TR, root 2 7 20 70 root 3 4 10 40 Preorder: 60, 20, 10, 40, 30, 50, 70 T T L R 5 30 6 50

In‐order Traversal of Binary Tree 3131 Post‐order Traversal of Binary Tree 32

def inorder(tree): def postorder(tree): if(tree!=None): if(tree!=None): inorder(tree.get_left_subtree()) postorder(tree.get_left_subtree()) print(tree.get_value(), end=" ") postorder(tree.get_right_subtree()) inorder(tree.get_right_subtree()) print(tree.get_value(), end=" ") 6 7 60 60 2 7 5 6 20 70 20 70 1 4 10 40 1 4 Inorder: 10, 20, 30, 40, 50, 60, 70 Postorder: 10, 30, 50, 40, 20, 70, 60 10 40 3 5 30 50 2 30 3 50

CSi107 8 Binary trees – Reference implement.33 Binary trees – Reference implement.34 class RefBinaryTree: class RefBinaryTree: def insert_right(self, data): def __init__(self, data): def __init__(self, data): t = RefBinaryTree(data) self.data = data self.data = data if self.right == None: self.left = None # RefBinaryTree, None if empty self.left = None self.right = t self.right = None # RefBinaryTree, None if empty self.right = None else: t.right = self.right from RefBinaryTree import RefBinaryTree def insert_left(self, data): self.right = t def main(): t = RefBinaryTree(data) tree tree = RefBinaryTree(55) 55 if self.left == None: self.left = t main() else: tree data 55 t.left = self.left left None self.left = t right None

Binary trees – Reference implement.35 Binary trees – using nodes 36 class RefBinaryTree: def get_left_subtree(self): from RefBinaryTree import RefBinaryTree def __init__(self, data): return self.left self.data = data tree def main(): 55 self.left = None def get_right_subtree(self): tree = RefBinaryTree(55) self.right = None return self.right tree.insert_left(4) 47 6 def insert_left(self, data):# tree.insert_right(6) def main(): 4 def insert_right(self, data):# tree = RefBinaryTree(55) tree.insert_left(7) tree.insert_left(4) main() tree data 55 def set_value(self, val): tree.insert_right(6) self.data = val left tree data 7 right data 6 55 r = tree.get_right_subtree() left None def get_value(self): print(r.get_value()) left 4 6 data 4 right None right None return self.data left None main() right None

CSi107 9 Binary trees – printing nodes 37 Binary trees – printing nodes 38

tree 55 from RefBinaryTree import RefBinaryTree def create_string(self, indent): def main(): tree 55 s=str(self.data) + "‐‐‐+" 4 6 if self.left != None: tree = RefBinaryTree(55) s=s+"\n(l)"+indent+self.left.create_string(indent+" ") tree.insert_left(4) 4 6 if self.right != None: tree.insert_right(6) s=s+"\n(r)"+indent+self.right.create_string(indent+" ") right = tree.get_right_subtree() 7 5 2 return s 55---+ left = tree.get_left_subtree() 55---+ (l) 4---+ left.insert_left(7) def __str__(self): (l) 4---+ representation = self.create_string(" ") (r) 6---+ right.insert_right(2) (l) 7---+ return representation Resulting output right.insert_left(5) print(tree) (r) 6---+ (l) 5---+ "55---+\n(l) 4---+\n(r) 6---+" main() Returned string (r) 2---+

Testing if an object is not None 39 Checking if the child is not None 40

def main(): number = 5 def create_string(self, indent): if number: s=str(self.data) + "‐‐‐+" print(1, "Number:", number) if self.left: variable returns if 4: s=s+"\n(l)"+indent+self.left.create_string(indent+" ") True if the object print(2, "Four") if self.right: is not None. value = None s=s+"\n(r)"+indent+self.right.create_string(indent+" ") if value: 1 Number: 5 return s print(3, "True:", value) 2 Four else: 3 Not True: None print(3, "Not True:", value) main()

CSi107 10 Exercise ‐ Draw the tree structure 41 Binary trees – Exercise 42 from RefBinaryTree import RefBinaryTree TASK: Use the class def get_node_sum(self): def main(): RefBinaryTree to # You do sum = self.data tree = RefBinaryTree(5) compute the sum of if self.left: tree.insert_left(8) all values stored in the sum += self.left.get_node_sum() nodes of the tree. tree.insert_right(2) if self.right: (You can assume that sum += self.right.get_node_sum() right = tree.get_right_subtree() all nodes are real return sum left = tree.get_left_subtree() integer values) left.insert_left(7) 5---+ value = left.get_value() (l) 12---+ def main(): tree tree = RefBinaryTree(55) left.set_value(value + 4) (l) 7---+ 55 # code to construct tree right.insert_right(3) (r) 2---+ # in picture on the left is omitted right.insert_left(6) (l) 6---+ 4 6 print(tree.get_node_sum()) (r) 3---+ 7 5 2 79 main() main()

43 44 Named arguments Python allows Binary trees – Reference Impl. (v2) function arguments def print_result(num, amt, spaces=3, to have default class RefBinaryTree: extra=None): values. If the function def __init__(self, value, l=None, r=None): message = str(num) + "‐" * spaces + str(amt) is called without the self.data = value if extra: argument, self.left = l message += " (" + str(extra) + ")" the argument gets its print(message) self.right = r default value. def insert_left(self, value): def main(): Also, arguments can self.left = RefBinaryTree(value, l=self.left) print_result(1, 34) be specified in any print_result(2, 34, 7) def insert_right(self, value): order by using print_result(3, 34, extra = 6) named arguments. self.right = RefBinaryTree(value, r=self.right) print_result(4, 34, 7, 10) 1---34 def set_value(self, val): 2------34 main() 3---34 (6) self.data = val 4------34 (10)

CSi107 11 46 Binary trees – Reference Impl. (v2) 45 Exercise class RefBinaryTree: Using the RefBinaryTree class RefBinaryTree: def __init__(self, value, l=None, r=None): class, write the code #continued from the previous slide self.data = value which creates the tree self.left = l self.right = r below. def get_value(self): … tree return self.data 9 from RefBinaryTree import RefBinaryTree 3 6 def get_left_subtree(self): def main(): return self.left tree = ??? 7 2

def get_right_subtree(self): 9---+ (l) 3---+ return self.right print(tree) (r) 7---+ (r) 6---+ main() (r) 2---+

CSi107 12