Fundamentals of Python From First Programs Through Data

  • Slides: 61
Download presentation
Fundamentals of Python: From First Programs Through Data Structures Chapter 18 Hierarchical Collections: Trees

Fundamentals of Python: From First Programs Through Data Structures Chapter 18 Hierarchical Collections: Trees

Objectives After completing this chapter, you will be able to: • Describe the difference

Objectives After completing this chapter, you will be able to: • Describe the difference between trees and other types of collections using the relevant terminology • Recognize applications for which general trees and binary trees are appropriate • Describe the behavior and use of specialized trees, such as heaps, BSTs, and expression trees • Analyze the performance of operations on binary search trees and heaps • Develop recursive algorithms to process trees Fundamentals of Python: From First Programs Through Data Structures 2

An Overview of Trees • In a tree, the ideas of predecessor and successor

An Overview of Trees • In a tree, the ideas of predecessor and successor are replaced with those of parent and child • Trees have two main characteristics: – Each item can have multiple children – All items, except a privileged item called the root, have exactly one parent Fundamentals of Python: From First Programs Through Data Structures 3

Tree Terminology Fundamentals of Python: From First Programs Through Data Structures 4

Tree Terminology Fundamentals of Python: From First Programs Through Data Structures 4

Tree Terminology (continued) Fundamentals of Python: From First Programs Through Data Structures 5

Tree Terminology (continued) Fundamentals of Python: From First Programs Through Data Structures 5

Tree Terminology (continued) Note: The height of a tree containing one node is 0

Tree Terminology (continued) Note: The height of a tree containing one node is 0 By convention, the height of an empty tree is – 1 Fundamentals of Python: From First Programs Through Data Structures 6

General Trees and Binary Trees • In a binary tree, each node has at

General Trees and Binary Trees • In a binary tree, each node has at most two children: – The left child and the right child Fundamentals of Python: From First Programs Through Data Structures 7

Recursive Definitions of Trees • A general tree is either empty or consists of

Recursive Definitions of Trees • A general tree is either empty or consists of a finite set of nodes T – Node r is called the root – The set T – {r} is partitioned into disjoint subsets, each of which is a general tree • A binary tree is either empty or consists of a root plus a left subtree and a right subtree, each of which are binary trees Fundamentals of Python: From First Programs Through Data Structures 8

Why Use a Tree? • A parse tree describes the syntactic structure of a

Why Use a Tree? • A parse tree describes the syntactic structure of a particular sentence in terms of its component parts Fundamentals of Python: From First Programs Through Data Structures 9

Why Use a Tree? (continued) • File system structures are also tree-like Fundamentals of

Why Use a Tree? (continued) • File system structures are also tree-like Fundamentals of Python: From First Programs Through Data Structures 10

Why Use a Tree? (continued) • Sorted collections can also be represented as treelike

Why Use a Tree? (continued) • Sorted collections can also be represented as treelike structures – Called a binary search tree, or BST for short • Can support logarithmic searches and insertions Fundamentals of Python: From First Programs Through Data Structures 11

The Shape of Binary Trees • The shape of a binary tree can be

The Shape of Binary Trees • The shape of a binary tree can be described more formally by specifying the relationship between its height and the number of nodes contained in it A full binary tree contains the maximum number of nodes for a given height H N nodes Height: N – 1 Fundamentals of Python: From First Programs Through Data Structures 12

The Shape of Binary Trees (continued) • The number of nodes, N, contained in

The Shape of Binary Trees (continued) • The number of nodes, N, contained in a full binary tree of height H is 2 H + 1 – 1 • The height, H, of a full binary tree with N nodes is log 2(N + 1) – 1 • The maximum amount of work that it takes to access a given node in a full binary tree is O(log N) Fundamentals of Python: From First Programs Through Data Structures 13

The Shape of Binary Trees (continued) Fundamentals of Python: From First Programs Through Data

The Shape of Binary Trees (continued) Fundamentals of Python: From First Programs Through Data Structures 14

Three Common Applications of Binary Trees • In this section, we introduce three special

Three Common Applications of Binary Trees • In this section, we introduce three special uses of binary trees that impose an ordering on their data: – Heaps – Binary search trees – Expression trees Fundamentals of Python: From First Programs Through Data Structures 15

Heaps • • In a min-heap each node is ≤ to both of its

Heaps • • In a min-heap each node is ≤ to both of its children A max-heap places larger nodes nearer to the root Heap property: Constraint on the order of nodes Heap sort builds a heap from data and repeatedly removes the root item and adds it to the end of a list • Heaps are also used to implement priority queues Fundamentals of Python: From First Programs Through Data Structures 16

Binary Search Trees • A BST imposes a sorted ordering on its nodes –

Binary Search Trees • A BST imposes a sorted ordering on its nodes – Nodes in left subtree of a node are < node – Nodes in right subtree of a node are > node • When shape approaches that of a perfectly balanced binary tree, searches and insertions are O(log n) in the worst case • Not all BSTs are perfectly balanced – In worst case, they become linear and support linear searches Fundamentals of Python: From First Programs Through Data Structures 17

Binary Search Trees (continued) Fundamentals of Python: From First Programs Through Data Structures 18

Binary Search Trees (continued) Fundamentals of Python: From First Programs Through Data Structures 18

Binary Search Trees (continued) Fundamentals of Python: From First Programs Through Data Structures 19

Binary Search Trees (continued) Fundamentals of Python: From First Programs Through Data Structures 19

Expression Trees • Another way to process expressions is to build a parse tree

Expression Trees • Another way to process expressions is to build a parse tree during parsing – Expression tree • An expression tree is never empty • An interior node represents a compound expression, consisting of an operator and its operands • Each leaf node represents a numeric operand • Operands of higher precedence usually appear near bottom of tree, unless overridden in source expression by parentheses Fundamentals of Python: From First Programs Through Data Structures 20

Expression Trees (continued) Fundamentals of Python: From First Programs Through Data Structures 21

Expression Trees (continued) Fundamentals of Python: From First Programs Through Data Structures 21

Binary Tree Traversals • Four standard types of traversals for binary trees: – Preorder

Binary Tree Traversals • Four standard types of traversals for binary trees: – Preorder traversal: Visits root node, and then traverses left subtree and right subtree in similar way – Inorder traversal: Traverses left subtree, visits root node, and traverses right subtree • Appropriate for visiting items in a BST in sorted order – Postorder traversal: Traverses left subtree, traverses right subtree, and visits root node – Level order traversal: Beginning with level 0, visits the nodes at each level in left-to-right order Fundamentals of Python: From First Programs Through Data Structures 22

Binary Tree Traversals (continued) Fundamentals of Python: From First Programs Through Data Structures 23

Binary Tree Traversals (continued) Fundamentals of Python: From First Programs Through Data Structures 23

Binary Tree Traversals (continued) Fundamentals of Python: From First Programs Through Data Structures 24

Binary Tree Traversals (continued) Fundamentals of Python: From First Programs Through Data Structures 24

Binary Tree Traversals (continued) Fundamentals of Python: From First Programs Through Data Structures 25

Binary Tree Traversals (continued) Fundamentals of Python: From First Programs Through Data Structures 25

Binary Tree Traversals (continued) Fundamentals of Python: From First Programs Through Data Structures 26

Binary Tree Traversals (continued) Fundamentals of Python: From First Programs Through Data Structures 26

A Binary Tree ADT • Provides many common operations required for building more specialized

A Binary Tree ADT • Provides many common operations required for building more specialized types of trees • Should support basic operations for creating trees, determining if a tree is empty, and traversing a tree • Remaining operations focus on accessing, replacing, or removing the component parts of a nonempty binary tree—its root, left subtree, and right subtree Fundamentals of Python: From First Programs Through Data Structures 27

The Interface for a Binary Tree ADT Fundamentals of Python: From First Programs Through

The Interface for a Binary Tree ADT Fundamentals of Python: From First Programs Through Data Structures 28

The Interface for a Binary Tree ADT (continued) Fundamentals of Python: From First Programs

The Interface for a Binary Tree ADT (continued) Fundamentals of Python: From First Programs Through Data Structures 29

Processing a Binary Tree • Many algorithms for processing binary trees follow the trees’

Processing a Binary Tree • Many algorithms for processing binary trees follow the trees’ recursive structure • Programmers are occasionally interested in the frontier, or set of leaf nodes, of a tree – Example: Frontier of parse tree for English sentence shown earlier contains the words in the sentence Fundamentals of Python: From First Programs Through Data Structures 30

Processing a Binary Tree (continued) • frontier expects a binary tree and returns a

Processing a Binary Tree (continued) • frontier expects a binary tree and returns a list – Two base cases: • Tree is empty return an empty list • Tree is a leaf node return a list containing root item Fundamentals of Python: From First Programs Through Data Structures 31

Implementing a Binary Tree Fundamentals of Python: From First Programs Through Data Structures 32

Implementing a Binary Tree Fundamentals of Python: From First Programs Through Data Structures 32

Implementing a Binary Tree (continued) Fundamentals of Python: From First Programs Through Data Structures

Implementing a Binary Tree (continued) Fundamentals of Python: From First Programs Through Data Structures 33

Implementing a Binary Tree (continued) Fundamentals of Python: From First Programs Through Data Structures

Implementing a Binary Tree (continued) Fundamentals of Python: From First Programs Through Data Structures 34

The String Representation of a Tree • __str__ can be implemented with any of

The String Representation of a Tree • __str__ can be implemented with any of the traversals Fundamentals of Python: From First Programs Through Data Structures 35

Developing a Binary Search Tree • A BST imposes a special ordering on the

Developing a Binary Search Tree • A BST imposes a special ordering on the nodes in a binary tree, so as to support logarithmic searches and insertions • In this section, we use the binary tree ADT to develop a binary search tree, and assess its performance Fundamentals of Python: From First Programs Through Data Structures 36

The Binary Search Tree Interface • The interface for a BST should include a

The Binary Search Tree Interface • The interface for a BST should include a constructor and basic methods to test a tree for emptiness, determine the number of items, add an item, remove an item, and search for an item • Another useful method is __iter__, which allows users to traverse the items in BST with a for loop Fundamentals of Python: From First Programs Through Data Structures 37

Data Structures for the Implementation of BST … Fundamentals of Python: From First Programs

Data Structures for the Implementation of BST … Fundamentals of Python: From First Programs Through Data Structures 38

Searching a Binary Search Tree • find returns the first matching item if the

Searching a Binary Search Tree • find returns the first matching item if the target item is in the tree; otherwise, it returns None – We can use a recursive strategy Fundamentals of Python: From First Programs Through Data Structures 39

Inserting an Item into a Binary Search Tree • add inserts an item in

Inserting an Item into a Binary Search Tree • add inserts an item in its proper place in the BST • Item’s proper place will be in one of three positions: – The root node, if the tree is already empty – A node in the current node’s left subtree, if new item is less than item in current node – A node in the current node’s right subtree, if new item is greater than or equal to item in current node • For options 2 and 3, add uses a recursive helper function named add. Helper • In all cases, an item is added as a leaf node Fundamentals of Python: From First Programs Through Data Structures 40

Removing an Item from a Binary Search Tree • Save a reference to root

Removing an Item from a Binary Search Tree • Save a reference to root node • Locate node to be removed, its parent, and its parent’s reference to this node • If item is not in tree, return None • Otherwise, if node has a left and right child, replace node’s value with largest value in left subtree and delete that value’s node from left subtree – Otherwise, set parent’s reference to node’s only child • Reset root node to saved reference • Decrement size and return item Fundamentals of Python: From First Programs Through Data Structures 41

Removing an Item from a Binary Search Tree (continued) • Fourth step is fairly

Removing an Item from a Binary Search Tree (continued) • Fourth step is fairly complex: Can be factored out into a helper function, which takes node to be deleted as a parameter (node containing item to be removed is referred to as the top node): – Search top node’s left subtree for node containing the largest item (rightmost node of the subtree) – Replace top node’s value with the item – If top node’s left child contained the largest item, set top node’s left child to its left child’s left child – Otherwise, set parent node’s right child to that right child’s left child Fundamentals of Python: From First Programs Through Data Structures 42

Complexity Analysis of Binary Search Trees • BSTs are set up with intent of

Complexity Analysis of Binary Search Trees • BSTs are set up with intent of replicating O(log n) behavior for the binary search of a sorted list • A BST can also provide fast insertions • Optimal behavior depends on height of tree – A perfectly balanced tree supports logarithmic searches – Worst case (items are inserted in sorted order): tree’s height is linear, as is its search behavior • Insertions in random order result in a tree with close -to-optimal search behavior Fundamentals of Python: From First Programs Through Data Structures 43

Case Study: Parsing and Expression Trees • Request: – Write a program that uses

Case Study: Parsing and Expression Trees • Request: – Write a program that uses an expression tree to evaluate expressions or convert them to alternative forms • Analysis: – Like the parser developed in Chapter 17, current program parses an input expression and prints syntax error messages if errors occur – If expression is syntactically correct, program prints its value and its prefix, infix, and postfix representations Fundamentals of Python: From First Programs Through Data Structures 44

Case Study: Parsing and Expression Trees (continued) Fundamentals of Python: From First Programs Through

Case Study: Parsing and Expression Trees (continued) Fundamentals of Python: From First Programs Through Data Structures 45

Case Study: Parsing and Expression Trees (continued) Fundamentals of Python: From First Programs Through

Case Study: Parsing and Expression Trees (continued) Fundamentals of Python: From First Programs Through Data Structures 46

Case Study: Parsing and Expression Trees (continued) • Design and Implementation of the Node

Case Study: Parsing and Expression Trees (continued) • Design and Implementation of the Node Classes: Fundamentals of Python: From First Programs Through Data Structures 47

Case Study: Parsing and Expression Trees (continued) Fundamentals of Python: From First Programs Through

Case Study: Parsing and Expression Trees (continued) Fundamentals of Python: From First Programs Through Data Structures 48

Case Study: Parsing and Expression Trees (continued) Fundamentals of Python: From First Programs Through

Case Study: Parsing and Expression Trees (continued) Fundamentals of Python: From First Programs Through Data Structures 49

Case Study: Parsing and Expression Trees (continued) • Design and Implementation of the Parser

Case Study: Parsing and Expression Trees (continued) • Design and Implementation of the Parser Class: – Easiest to build an expression tree with a parser that uses a recursive descent strategy • Borrow parser from Chapter 17 and modify it – parse should now return an expression tree to its caller, which uses that tree to obtain information about the expression – factor processes either a number or an expression nested in parentheses • Calls expression to parse nested expressions Fundamentals of Python: From First Programs Through Data Structures 50

Case Study: Parsing and Expression Trees (continued) Fundamentals of Python: From First Programs Through

Case Study: Parsing and Expression Trees (continued) Fundamentals of Python: From First Programs Through Data Structures 51

Case Study: Parsing and Expression Trees (continued) Fundamentals of Python: From First Programs Through

Case Study: Parsing and Expression Trees (continued) Fundamentals of Python: From First Programs Through Data Structures 52

An Array Implementation of Binary Trees • An array-based implementation of a binary tree

An Array Implementation of Binary Trees • An array-based implementation of a binary tree is difficult to define and practical only in some cases • For complete binary trees, there is an elegant and efficient array-based representation – Elements are stored by level • The array representation of a binary tree is pretty rare and is used mainly to implement a heap Fundamentals of Python: From First Programs Through Data Structures 53

An Array Implementation of Binary Trees (continued) Fundamentals of Python: From First Programs Through

An Array Implementation of Binary Trees (continued) Fundamentals of Python: From First Programs Through Data Structures 54

An Array Implementation of Binary Trees (continued) Fundamentals of Python: From First Programs Through

An Array Implementation of Binary Trees (continued) Fundamentals of Python: From First Programs Through Data Structures 55

An Array Implementation of Binary Trees (continued) Fundamentals of Python: From First Programs Through

An Array Implementation of Binary Trees (continued) Fundamentals of Python: From First Programs Through Data Structures 56

Implementing Heaps Fundamentals of Python: From First Programs Through Data Structures 57

Implementing Heaps Fundamentals of Python: From First Programs Through Data Structures 57

Implementing Heaps (continued) • At most, log 2 n comparisons must be made to

Implementing Heaps (continued) • At most, log 2 n comparisons must be made to walk up the tree from the bottom, so add is O(log n) • Method may trigger a doubling in the array size – O(n), but amortized over all additions, it is O(1) Fundamentals of Python: From First Programs Through Data Structures 58

Using a Heap to Implement a Priority Queue • In Ch 15, we implemented

Using a Heap to Implement a Priority Queue • In Ch 15, we implemented a priority queue with a sorted linked list; alternatively, we can use a heap Fundamentals of Python: From First Programs Through Data Structures 59

Summary • Trees are hierarchical collections – The topmost node in a tree is

Summary • Trees are hierarchical collections – The topmost node in a tree is called its root – In a general tree, each node below the root has at most one parent node, and zero child nodes – Nodes without children are called leaves – Nodes that have children are called interior nodes – The root of a tree is at level 0 • In a binary tree, nodes have at most two children – A complete binary tree fills each level of nodes before moving to next level; a full binary tree includes all the possible nodes at each level Fundamentals of Python: From First Programs Through Data Structures 60

Summary (continued) • Four standard types of tree traversals: Preorder, inorder, postorder, and level

Summary (continued) • Four standard types of tree traversals: Preorder, inorder, postorder, and level order • Expression tree: Type of binary tree in which the interior nodes contain operators and the successor nodes contain their operands • Binary search tree: Nonempty left subtree has data < datum in its parent node and a nonempty right subtree has data > datum in its parent node – Logarithmic searches/insertions if close to complete • Heap: Binary tree in which smaller data items are located near root Fundamentals of Python: From First Programs Through Data Structures 61