Lecture 12
Trees!
Tree structures (root at the bottom)
Tree structures (root at the top)
Tree structures
File systems are trees
root
a
b
d
e
f
g
c
The DNS is a tree
root
edu
com
northwestern
berkeley
cnn
amazon
gov
it
cs
Data flow diagrams are (often) trees
Expressions are trees
(+ a
(- b)
(* c d)))
( )
+
a
( )
( )
-
b
*
c
d
Type hierarchies are trees
object
number
integer
float
procedure
posn
color
picture
string
boolean
exception
contract-violation
arity-mismatch
Even English sentences can be thought of as trees
Relationships between nodes
Relationships between nodes
Kinds of nodes
Kinds of nodes
Storing information in trees
( )
a
b
( )
( )
c
d
e
( )
f
( )
g
Trees are recursive
…
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!
Binary Trees
Example: binary trees
(define-struct binary-tree (data left right))
Example: binary trees
So the following are all valid binary trees:
(make-binary-tree 1 empty empty)
empty) ; by (a), (b) and rule 2
(make-binary-tree 1 empty empty)
(make-binary-tree 0 empty empty)) ; by (a), (b) and rule 2
(make-binary-tree 1
empty
(make-binary-tree 1 empty empty))
(make-binary-tree 1 empty empty)) ; by (a), (b) and rule 2
Tree Diagrams to Check Your Understanding
Writing a recursion
(define-struct binary-tree (data left right))
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)))))
More general trees
Another tree example
(define-struct tree (data child-list))
Examples
Tree Diagrams to Check Your Understanding
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))))
Counting the nodes in the tree
(define (new-count-nodes tree)� (+ 1� (foldl + 0� (map new-count-nodes� (tree-child-list tree)))))
Nope!
Let’s run it on a leaf, using the substitution model:
(new-count-nodes (make-tree 1 '()))
Counting the nodes in the tree
(define (new-count-nodes tree)� (+ 1� (foldl + 0� (map new-count-nodes� (tree-child-list tree)))))
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
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
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
Tree Recursion
(define (func args …) � (if easy-case?� solve-easy-case� (combine (func one-part)� (func other-part)))))
Binary
SEARCH
Trees
Binary Search Tree
An Example
Why is this a binary search tree?
An Example
Bigger Example
Bigger Example
This is the root. It has left and right sub-trees.
Bigger Example
This is the root. It has left and right sub-trees.
Right sub-tree has value greater than parent.
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.
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!
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!
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.
You're Running the Tech Stack for HR at Walmart
Some Simplifications
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: …, …
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…
Version 1 - Inductive Data Definition
An employee database is simply a list of people.
In other words, a database is either:
Adding a person to the database
An employee database is simply a list of people.
In other words, a database is either:
Write a function (add employee database) that returns a database with that employee added to the database.
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))
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))
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
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)
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))]))
Visualization of Mushroom Kingdom
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:
Can we do better?
What if we kept this whole list sorted by ssn…
Version 2
An employee database is a sorted list of people.
In other words, a database is either:
Version 2 - Inductive Data Definition
An employee database is a sorted list of people.
A database is either:
And has the invariant: each ssn number in l is larger than the ssn of employee e
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
Visualization of Mushroom Kingdom
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)
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))]))
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…
Oh wait…WHAT. All that work and no benefit?!?!?
Can we do better?
What if we put this in that whole binary search tree thing you talked about a little while ago…
Version 3
An employee database is a sorted tree of people.
In other words, a database is either:
(define-struct db-node (employee left right))
Visualization of Mushroom Kingdom
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?
)
Mega Mushroom Kingdom
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))]))
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))]))
Linear Search vs. Binary Search
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
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
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