Lecture 12 Slides - Lecture Slides

1 of 78

Lecture 12

Trees!

2 of 78

Tree structures (root at the bottom)

3 of 78

Tree structures (root at the top)

4 of 78

Tree structures

  • Branching structures
    • Circles are called nodes or vertices
    • Connections are called edges
  • Typically start with a single node called the root
    • Traditionally drawn at the top of the tree
    • Branches downward
    • Merges upward

5 of 78

File systems are trees

  • Modern operating systems allow files to be placed in directories (folders)
    • And directories to be placed in other directories
    • And so on and so on
  • Creating a branching structure called a tree
  • The start of the tree is called the root directory

root

a

b

d

e

f

g

c

6 of 78

The DNS is a tree

  • The DNS is the Domain Name System
  • System used to keep track of internet host names
  • Namespace is tree structured
    • Each part of the name is a node in the tree
    • Full name is a path through the tree

root

edu

com

northwestern

berkeley

cnn

amazon

gov

it

cs

7 of 78

Data flow diagrams are (often) trees

8 of 78

Expressions are trees

(+ a

(- b)

(* c d)))

( )

+

a

( )

( )

-

b

*

c

d

9 of 78

Type hierarchies are trees

  • Computers have a taxonomy of types of data objects
  • Types can have subtypes
    • Which can have subsubtypes

object

number

integer

float

procedure

posn

color

picture

string

boolean

exception

contract-violation

arity-mismatch

10 of 78

Even English sentences can be thought of as trees

11 of 78

Relationships between nodes

  • Nodes directly below a node are its children
  • The node directly above is its parent

12 of 78

Relationships between nodes

  • Nodes above are its ancestors
  • Nodes below a node are its descendants

13 of 78

Kinds of nodes

  • The only node with no parent is the root

14 of 78

Kinds of nodes

  • Nodes with no children are called leaves
  • Other nodes are called interior nodes

15 of 78

Storing information in trees

  • Most of the trees we care about have additional information stored in the nodes or edges
  • These are sometimes called labeled trees
    • And the data stored in the node is called a label

( )

a

b

( )

( )

c

d

e

( )

f

( )

g

16 of 78

Trees are recursive

  • A tree node can have children
    • which can have children
      • which can have children
        • which can have children
          • which can have children

  • So trees are also inductively defined (aka recursively defined like linked lists!)
  • We define them in many different ways depending on what kinds of trees we want to use
  • Let’s think about ways of defining trees of numbers
    • i.e. trees where nodes are labeled with numbers

17 of 78

Representing�binary trees

Important: this is a somewhat different binary tree representation than the ones used in the tutorial and assignment; there are lots of ways to represent trees!

18 of 78

Binary Trees

  • A binary tree is a tree where every node has at most two children
    • Usually called the left- and right-child
  • Binary trees came up in lecture, the tutorial, and are in Exercise 5
    • Each defines them slightly differently because a “tree” is a concept or data structure – not a particular implementation
    • This is why the Inductive Data Definition is so important

19 of 78

Example: binary trees

(define-struct binary-tree (data left right))

  • Here:
    • data is the data stored in the node
    • left and right are the nodes for the left- and right-child
  • A leaf (a node with no children) is just a node whose left and right fields are empty
  • Inductive definition: a binary tree is either:
    1. The value empty (i.e. '()), or
    2. (make-binary-tree any binary-tree binary-tree)

20 of 78

Example: binary trees

So the following are all valid binary trees:

  • empty ; (by rule 1)
  • (make-binary-tree 1 empty empty) ; by (a) and rule 2
  • (make-binary-tree 2

(make-binary-tree 1 empty empty)

empty) ; by (a), (b) and rule 2

  • (make-binary-tree 2

(make-binary-tree 1 empty empty)

(make-binary-tree 0 empty empty)) ; by (a), (b) and rule 2

  • (make-binary-tree 2

(make-binary-tree 1

empty

(make-binary-tree 1 empty empty))

(make-binary-tree 1 empty empty)) ; by (a), (b) and rule 2

21 of 78

Tree Diagrams to Check Your Understanding

22 of 78

Writing a recursion

(define-struct binary-tree (data left right))

  • In this variant, a binary tree is either:
    • The value empty (i.e. ‘()), or
    • (make-binary-tree any binary-tree binary-tree)
  • So our base case will be the value empty
    • “The empty tree”
  • And our recursive case will recurse on both (tree recursion):
    • The left child, and
    • The right child

23 of 78

Example: Counting the nodes in a tree

(define (node-count tree)

(if (empty? tree)

0

(+ 1

(node-count (binary-tree-left tree))

(node-count (binary-tree-right tree)))))

  • Check if it’s empty (base case)
    • Then there are no nodes (return zero)
  • Otherwise recursive case
    • Count the nodes on the left
    • Count the nodes on the right
    • Return the sum

24 of 78

More general trees

25 of 78

Another tree example

  • Binary tree nodes can only have two children.
  • Why if we want to allow nodes to have arbitrary numbers of children?
  • Put the children in a list!

(define-struct tree (data child-list))

  • Inductive definition:
    • A tree is (make-tree any (listof tree))
  • Note that in this representation,
    • There’s no either/or in the definition
    • There’s no representation for an empty tree
    • A leaf is just a node with an empty child-list

26 of 78

Examples

  • (make-tree 1 '()) ; is a tree
  • (make-tree 1� (list (make-tree 2 '()))) ; is a tree
  • (make-tree 1� (list (make-tree 2 '())� (make-tree 3 '()))) ; is a tree
  • (make-tree 1� (list (make-tree 2 '())� (make-tree 3 '())� (make-tree 4 '()))) ; is a tree
  • (make-tree 1� (list (make-tree 2 '())� (make-tree 3 (list (make-tree 5 '())))� (make-tree 4 '()))) ; is a tree

27 of 78

Tree Diagrams to Check Your Understanding

28 of 78

Counting the nodes in the new type of tree

(define (new-count-nodes tree)� (+ 1� (foldl + 0� (map new-count-nodes� (tree-child-list tree)))))

Or just:

(define (new-count-nodes tree)� (foldl + 1� (map new-count-nodes� (tree-child-list tree))))

29 of 78

Counting the nodes in the tree

(define (new-count-nodes tree)� (+ 1� (foldl + 0� (map new-count-nodes� (tree-child-list tree)))))

  • Wait!
  • Where’s the base case?
  • Won’t this recurse infinitely?

30 of 78

Nope!

Let’s run it on a leaf, using the substitution model:

(new-count-nodes (make-tree 1 '()))

  • (+ 1� (foldl + 0� (map new-count-nodes� (tree-child-list (make-tree 1 '())))))
  • (+ 1� (foldl + 0� (map new-count-nodes� '())))
  • (+ 1� (foldl + 0 '()))
  • (+ 1 0)
  • 1

31 of 78

Counting the nodes in the tree

(define (new-count-nodes tree)� (+ 1� (foldl + 0� (map new-count-nodes� (tree-child-list tree)))))

  • New-count-nodes is recursive
  • But it uses map to call itself
    • Rather than calling itself directly
  • If the child list is empty (a leaf)
    • Then map doesn’t have anything to call it on
  • So the recursion stops naturally

32 of 78

Binary Trees

(define-struct binary-tree (data left right))

; A binary tree is...

; - an empty list or

; - (make-binary-tree any binary-tree binary-tree)

Early Example

33 of 78

Binary Trees

(define-struct binary-tree (data left right))

; A binary tree is...

; - an empty list or

; - (make-binary-tree any binary-tree binary-tree)

(define-struct branch (data left right))

; A binary tree is...

; - a number or

; - (make-branch number binary-tree binary-tree)

Earlier Example

Tutorial

34 of 78

Binary Trees

(define-struct binary-tree (data left right))

; A binary tree is...

; - an empty list or

; - (make-binary-tree any binary-tree binary-tree)

(define-struct branch (data left right))

; A binary tree is...

; - a number or

; - (make-branch number binary-tree binary-tree)

(define-struct human (name parentA parentB))

; An ancestry-tree is either

; - an empty list or

; - (make-human string ancestry-tree ancestry-tree)

Earlier Example

Tutorial

Exercise

35 of 78

Tree Recursion

(define (func args …) � (if easy-case?solve-easy-case (combine (func one-part) (func other-part)))))

  • You can often simplify the problem by splitting it
  • Then the fixup step consists of combining the answers to the two problems (for binary trees) or many (n-ary trees)
  • This is called tree recursion
    • “I can solve this problem by running this same function on the left branch of this binary tree, running this same function on the right branch of this tree…and then combining the answers”

36 of 78

Binary

SEARCH

Trees

37 of 78

Binary Search Tree

  • A tree is a binary tree iff [if and only if] each node has at most two children. A binary tree is a binary search tree if it has the following invariant:
    • All nodes in the left sub-tree have a smaller value than the parent node.
    • All nodes in the right sub-tree have a larger value than the parent node.

  • An invariant is just a constraint that always holds for a given data type.

38 of 78

An Example

Why is this a binary search tree?

  • All nodes in the left sub-tree have a smaller value than its parent node.
  • All nodes in the right sub-tree have a larger value than its parent node.

39 of 78

An Example

40 of 78

Bigger Example

41 of 78

Bigger Example

This is the root. It has left and right sub-trees.

42 of 78

Bigger Example

This is the root. It has left and right sub-trees.

Right sub-tree has value greater than parent.

43 of 78

Bigger Example

This is the root. It has left and right sub-trees.

Right sub-tree has value greater than parent.

Left sub-tree has value less than parent.

44 of 78

Bigger Example

This is the root. It has left and right sub-trees.

Right sub-tree has value greater than parent.

Left sub-tree has value less than parent.

This value can be greater than 7 and 9…but can't be greater than 12!

45 of 78

Bigger Example

This is the root. It has left and right sub-trees.

Right sub-tree has value greater than parent.

Left sub-tree has value less than parent.

This value can be greater than 7 and 9…but can't be greater than 12!

This one can't be 8, 9, or 10 because then it would be greater than 7!

46 of 78

Wait…why would this matter?

Why do we care about keeping our data structured?

Why not just smoosh it all together?

Who cares what order it's in.

This is bogus.

Just put it in a list.

Why are we talking about this on a friday of all days.

47 of 78

You're Running the Tech Stack for HR at Walmart

  • You have over 2 million employees in the main employee database.
  • That doesn't even include your supply chain employees; your delivery employees; your tech contractors; etc.
  • You need to maintain a "database" (a collection of data) about all of them.
  • Someone asks you to get one particular employee's information out of this database.
  • How do you do it?

48 of 78

Some Simplifications

  • The only data you store about your employees is: their social security number (an identification number that is unique) and their name.
  • Step 1. Figure out a way of representing each employee.
  • Step 2. Figure out a way of storing a bunch of those employees.
  • Step 3. Write a function to lookup an employee given some social security number.

49 of 78

Using struct to create an employee

; an employee is…

; - (make-employee number string)

(define-struct employee (ssn name))

Remember, this gives us:

; constructor: …

; predicate: …

; accessors/selectors: …, …

50 of 78

Woo. Step 1 is done!

(make-employee 1 "ava")

(make-employee 2 "george")

(make-employee 3 "jessica")

(make-employee 4 "steve")

Now how do we store a bunch of these things…

51 of 78

Version 1 - Inductive Data Definition

An employee database is simply a list of people.

In other words, a database is either:

  • empty
  • (cons employee database)

52 of 78

Adding a person to the database

An employee database is simply a list of people.

In other words, a database is either:

  • empty
  • (cons employee database)

Write a function (add employee database) that returns a database with that employee added to the database.

53 of 78

Adding a person to the database

(define (add employee db)

…)

(define ava-e (make-employee 1 "ava"))

(define jessica-e (make-employee 3 "jessica"))

(check-expect (add ava-e '()) (list ava-e))

(check-expect (add ava-e (list jessica-e)) (list ava-e jessica-e))

54 of 78

Adding a person to the database

(define (add employee db)

(cons employee db))

(define ava-e (make-employee 1 "ava"))

(define jessica-e (make-employee 3 "jessica"))

(check-expect (add ava-e '()) (list ava-e))

(check-expect (add ava-e (list jessica-e)) (list ava-e jessica-e))

55 of 78

Looking up a person to the database

; lookup-v1: number database -> employee or false

; find the person in the database via their ssn

; false if not found

56 of 78

Looking up a person to the database

; lookup-v1: number database -> employee or false

; find the person in the database via their ssn

; false if not found

(define (lookup-v1 n db)

...)

(define test-db (list jessica-e ava-e george-e steve-e))

(check-expect (lookup-v1 2 test-db)

george-e)

(check-expect (lookup-v1 2 empty)

false)

(check-expect (lookup-v1 5 test-db)

false)

57 of 78

Looking up a person to the database

; lookup-v1: number database -> employee or false

; find the person in the database via their ssn

; false if not found

(define (lookup-v1 n db)

(cond [(empty? db) false]

[(= n (employee-ssn (first db))) (first db)]

[else (lookup-v1 n (rest db))]))

58 of 78

Visualization of Mushroom Kingdom

59 of 78

Looking up a person to the database

; lookup-v1: number database -> employee or false

; find the person in the database via their ssn

; false if not found

(define (lookup-v1 n db)

(cond [(empty? db) false]

[(= n (employee-ssn (first db))) (first db)]

[else (lookup-v1 n (rest db))]))

If we had 100 employees:

  • Best Case: We find the employee right at the beginning of our db – 1 lookup
  • Worst Case: We go through the entire db and don't find them – 101 lookups (n + 1).
  • Average Case: The employee is in the middle of the list - 50 lookups (n / 2).

60 of 78

Can we do better?

What if we kept this whole list sorted by ssn…

61 of 78

Version 2

An employee database is a sorted list of people.

In other words, a database is either:

  • empty
  • (cons employee database) ; such that the list is sorted

62 of 78

Version 2 - Inductive Data Definition

An employee database is a sorted list of people.

A database is either:

  • empty
  • (cons employee[e] sorted-list[l])

And has the invariant: each ssn number in l is larger than the ssn of employee e

63 of 78

Looking up a person to the database

; lookup-v2: number database -> employee or false

; find the person in the database via their ssn

; false if not found

64 of 78

Visualization of Mushroom Kingdom

65 of 78

Looking up a person to the database

; lookup-v2: number database -> employee or false

; find the person in the database via their ssn

; false if not found

(define (lookup-v2 n db)

...)

(check-expect (lookup-v2 2 (list ava-e george-e jessica-e steve-e))

george-e)

(check-expect (lookup-v2 2 empty))

(check-expect (lookup-v2 5 (list ava-e george-e jessica-e steve-e))

false)

(check-expect (lookup-v2 1 (list george-p jessica-p steve-p))

false)

66 of 78

Looking up a person to the database

(define (lookup-v2 n db)

(cond [(empty? db) false]

[(< n (employee-ssn (first db))) false]

[(= n (employee-ssn (first db))) (first db)]

[else (lookup-v2 n (rest db))]))

67 of 78

Looking up a person to the database

(define (lookup-v2 n db)

(cond [(empty? db) false]

[(< n (employee-ssn (first db))) false]

[(= n (employee-ssn (first db))) (first db)]

[else (lookup-v2 n (rest db))]))

If we have 100 employees…

  • Best Case: We find the employee right at the beginning of our db – 1 lookup or the ssn is smaller than the one first.
  • Worst Case: We go through the entire db and don't find them – 101 lookups (n + 1).
  • Average Case: The employee is in the middle of the list - 50 lookups (n / 2).

Oh wait…WHAT. All that work and no benefit?!?!?

68 of 78

Can we do better?

What if we put this in that whole binary search tree thing you talked about a little while ago…

69 of 78

Version 3

An employee database is a sorted tree of people.

In other words, a database is either:

  • empty
  • (make-db-node employee[e] db-left db-right)

(define-struct db-node (employee left right))

  • Invariant: every person in 'left' has a smaller ssn than person 'e' and every person in 'right' has a larger ssn than person 'e'

70 of 78

Visualization of Mushroom Kingdom

71 of 78

Looking up a person to the database

; lookup-v3: number database -> person or false

; find the person in the database via their ssn

; false if not found

(define (lookup-v3 n db)

; what do you do if the db is empty?

; what do you do if the person at the root (db-node-employee db)

; has the ssn that we're looking for?

; what do you do if the number we are looking for is less than

; the person's at the root?

; what do you do if the number we are looking for is greater than

; the person's at the root?

)

72 of 78

Mega Mushroom Kingdom

73 of 78

Looking up a person to the database

; lookup-v3: number database -> person or false

; find the person in the database via their ssn

; false if not found

(define (lookup-v3 n db)

(cond [(empty? db) false]

[(= n (employee-ssn (db-node-employee db))) (db-node-employee db)]

[(< n (employee-ssn (db-node-employee db))) (lookup-v3 n (db-node-left db))]

[(> n (employee-ssn (db-node-employee db))) (lookup-v3 n (db-node-right db))]))

74 of 78

Looking up a person to the database

; lookup-v3: number database -> person or false

; find the person in the database via their ssn

; false if not found

(define (lookup-v3 n db)

(cond [(empty? db) false]

[(= n (employee-ssn (db-node-employee db))) (db-node-employee db)]

[(< n (employee-ssn (db-node-employee db))) (lookup-v3 n (db-node-left db))]

[(> n (employee-ssn (db-node-employee db))) (lookup-v3 n (db-node-right db))]))

  • Best Case: The employee we want is the root of the tree – 1 lookup.
  • Worst Case: The employee we want is at the bottom of the tree - how many times do we need to divide by 2? (lg n) (this is log base 2). (lg 128) = 7, (lg 256) = 8, (lg 512) = 9…
  • Average Case: The math here is harder…we'll save it for CS 212 and 214!

75 of 78

Linear Search vs. Binary Search

76 of 78

Mathematical Notation

Class Name

When the input doubles...

Constant

Runtime is unchanged ( ... x 1 )

Logarithmic

Runtime increases by a constant

Linear

Runtime is doubled ( ... x 2 )

Quasilinear

Runtime is approximately doubled

Quadratic

Runtime is quadrupled ( ... x 4 )

Cubic

Runtime is octupled ( ... x 8 )

Exponential

Runtime gets...really big

Common Big O Classes

77 of 78

Mathematical Notation

Class Name

When the input doubles...

Constant

Runtime is unchanged ( ... x 1 )

Logarithmic

Runtime increases by a constant

Linear

Runtime is doubled ( ... x 2 )

Quasilinear

Runtime is approximately doubled

Quadratic

Runtime is quadrupled ( ... x 4 )

Cubic

Runtime is octupled ( ... x 8 )

Exponential

Runtime gets...really big

Common Big O Classes

Linear Search

78 of 78

Mathematical Notation

Class Name

When the input doubles...

Constant

Runtime is unchanged ( ... x 1 )

Logarithmic

Runtime increases by a constant

Linear

Runtime is doubled ( ... x 2 )

Quasilinear

Runtime is approximately doubled

Quadratic

Runtime is quadrupled ( ... x 4 )

Cubic

Runtime is octupled ( ... x 8 )

Exponential

Runtime gets...really big

Common Big O Classes

Binary Search