Linked List Structure Linked List Structure A linked list is either empty or a first value and the rest of the linked list A linked list is either empty or a first value and the rest of the linked list A linked list A class attribute represents is a pair an empty linked list 3 , 4 , 5 3 , 4 , 5 Link instance Link instance Link instance Link.empty Link instance Link instance Link instance Link.empty Linked Lists first: 3 first: 4 first: 5 first: 3 first: 4 first: 5 rest: rest: rest: rest: rest: rest: The first (zeroth) The rest of the element is an elements are stored attribute value in a linked list Link(3, Link(4, Link(5, Link.empty))) Link(3, Link(4, Link(5 ))) , Link.empty ) 4 5 Linked List Class Property Methods Linked list class: attributes are passed to __init__ In some cases, we want the value of instance attributes to be computed on demand For example, if we want to access the second element of a linked list class Link: Some zero-length sequence >>> s = Link(3, Link(4, Link(5))) empty = () >>> s.second 4 def __init__(self, first, rest=empty): >>> s.second = 6 Property Methods assert rest is Link.empty or isinstance(rest, Link) No method >>> s.second self.first = first calls! 6 self.rest = rest >>> s Returns whether Link(3, Link(6, Link(5))) rest is a Link The @property decorator on a method designates that it will be called whenever it is help(isinstance): Return whether an object is an instance of a class or of a subclass thereof. looked up on an instance A @<attribute>.setter decorator on a method designates that it will be called whenever Link(3, Link(4, Link(5 ))) that attribute is assigned. <attribute> must be an existing property method. (Demo) (Demo) 6 8 Recursive Computation of the Fibonacci Sequence def fib (n): Our first example of tree recursion: if n == 0 : return 0 elif n == 1 : fib(5) return 1 else : return fib(n- 2 ) + fib(n- 1 ) Tree Recursion Efficiency Memoization fib(3) fib(4) fib(1) fib(2) fib(2) fib(3) fib(0) fib(1) 1 fib(0) fib(1) fib(1) fib(2) 0 1 fib(0) fib(1) 0 1 1 (Demo) 0 1 10 http://en.wikipedia.org/wiki/File:Fibonacci.jpg
Memoization Memoized Tree Recursion Idea: Remember the results that have been computed before Call to fib fib(5) Found in cache Skipped def memo(f): Keys are arguments that fib(3) fib(4) map to return values cache = {} Tree Class def memoized(n): fib(1) fib(2) if n not in cache: fib(2) fib(3) fib(0) fib(1) cache[n] = f(n) 1 return cache[n] fib(0) fib(1) fib(1) fib(2) 0 1 return memoized Same behavior as f, if f is a pure function 0 1 1 fib(0) fib(1) 0 1 (Demo) 12 13 Tree Abstraction (Review) Tree Class Root of the whole tree or Root Node Nodes A Tree has a label and a list of branches; each branch is a Tree Root label 3 Labels class Tree: def tree(label, branches=[]): Root of a branch def __init__(self, label, branches=[]): for branch in branches: self.label = label Branch 1 2 assert is_tree(branch) for branch in branches: (also a tree) return [label] + list(branches) assert isinstance(branch, Tree) 0 1 1 1 def label(tree): self.branches = list(branches) Measuring Efficiency return tree[0] Leaf 0 1 def branches(tree): (also a tree) Path return tree[1:] def fib_tree(n): def fib_tree(n): Recursive description (wooden trees): Relative description (family trees): if n == 0 or n == 1: if n == 0 or n == 1: A tree has a root label and a list of branches Each location in a tree is called a node return Tree (n) return tree (n) else: else: Each branch is a tree Each node has a label that can be any value left = fib_tree(n-2) left = fib_tree(n-2) A tree with zero branches is called a leaf One node can be the parent / child of another right = fib_tree(n-1) right = fib_tree(n-1) fib_n = left .label + right .label fib_n = label (left) + label (right) A tree starts at the root The top node is the root node return Tree (fib_n, [left, right]) return tree (fib_n, [left, right]) People often refer to labels by their locations: "each parent is the sum of its children" (Demo) 15 16 Recursive Computation of the Fibonacci Sequence Memoization def fib (n): Our first example of tree recursion: Idea: Remember the results that have been computed before if n == 0 : return 0 elif n == 1 : fib(5) return 1 def memo(f): Keys are arguments that else : return fib(n- 2 ) + fib(n- 1 ) map to return values cache = {} Memoization fib(3) fib(4) def memoized(n): if n not in cache: fib(1) fib(2) cache[n] = f(n) fib(2) fib(3) return cache[n] fib(0) fib(1) 1 return memoized Same behavior as f, fib(0) fib(1) fib(1) fib(2) if f is a pure function 0 1 fib(0) fib(1) 0 1 1 (Demo) (Demo) 0 1 4 6 http://en.wikipedia.org/wiki/File:Fibonacci.jpg
Memoized Tree Recursion Exponentiation Call to fib Goal: one more multiplication lets us double the problem size fib(5) Found in cache def exp(b, n): � Skipped if n == 0: 1 if n = 0 b n = return 1 fib(3) fib(4) b · b n − 1 otherwise else: return b * exp(b, n-1) Exponentiation fib(1) fib(2) fib(2) fib(3) fib(0) fib(1) def exp_fast(b, n): 1 if n == 0: fib(0) fib(1) fib(1) fib(2) return 1 0 1 elif n % 2 == 0: 1 if n = 0 return square(exp_fast(b, n//2)) 0 1 1 fib(0) fib(1) b n = else: 1 2 n ) 2 ( b if n is even return b * exp_fast(b, n-1) b · b n − 1 if n is odd 0 1 def square(x): return x * x (Demo) 7 9 Exponentiation Recursive Lists Can Change Goal: one more multiplication lets us double the problem size Attribute assignment statements can change first and rest attributes of a Link The rest of a linked list can contain the linked list as a sub-list def exp(b, n): Linear time: if n == 0: • Doubling the input return 1 >>> s = Link(1, Link(2, Link(3))) doubles the time else: >>> s.first = 5 • 1024x the input takes return b * exp(b, n-1) >>> t = s.rest Mutable Linked Lists Global frame First Rest First Rest First Rest 1024x as much time >>> t.rest = s 1 2 3 s >>> s.first 5 def exp_fast(b, n): Logarithmic time: >>> s.rest.rest.rest.rest.rest.first if n == 0: • Doubling the input 2 return 1 increases the time elif n % 2 == 0: by a constant C return square(exp_fast(b, n//2)) • 1024x the input else: increases the time return b * exp_fast(b, n-1) Global frame First Rest First Rest Note: The actual by only 10 times C 5 2 s environment diagram is def square(x): much more complicated. return x * x t 10 13 Adding to an Ordered List Adding to an Ordered List Link instance Link instance Link instance Link instance Link instance Link instance first: 1 first: 3 first: 5 first: 1 0 first: 3 first: 5 s: s: rest: rest: rest: rest: rest: rest: Linked List Mutation Example Link instance first: 1 rest: def add(s, v): def add(s, v): """Add v to an ordered list s with no repeats, returning modified s.””” """Add v to an ordered list s with no repeats, returning modified s.””” ( Note : If v is already in s, then don't modify s, but still return it.) ( Note : If v is already in s, then don't modify s, but still return it.) add(s, 0) add(s, 0) add(s, 3) add(s, 4) 15 16
Adding to an Ordered List Adding to an Ordered List Adding to a Set Represented as an Ordered List def add(s, v): """Add v to a set s, returning modified s.””” Link instance Link instance Link instance Link instance Link instance Link instance s: first: 1 0 first: 3 first: 5 4 first: 1 0 first: 3 first: 5 4 >>> s = Link(1, Link(3, Link(5))) s: s: >>> add(s, 0) rest: rest: rest: rest: rest: rest: Link(0, Link(1, Link(3, Link(5)))) >>> add(s, 3) Link(0, Link(1, Link(3, Link(5)))) >>> add(s, 4) Link instance Link instance Link instance Link instance Link(0, Link(1, Link(3, Link(4, Link(5))))) >>> add(s, 6) first: 1 first: 5 first: 1 first: 5 Link(0, Link(1, Link(3, Link(4, Link(5, Link(6)))))) """ rest: rest: rest: rest: assert s is not Link.empty if s.first > v: v Link(s.first, s.rest) def add(s, v): def add(s, v): s.first, s.rest = __________________________ , _____________________________ Link instance """Add v to an ordered list s with no repeats...””” """Add v to an ordered list s with no repeats...""" elif s.first < v and empty(s.rest): first: 6 Link(v) s.rest = ___________________________________________________________________ elif s.first < v: rest: add(s.rest, v) add(s, 0) add(s, 3) add(s, 6) add(s, 0) add(s, 3) add(s, 6) ____________________________________________________________________________ add(s, 4) add(s, 4) return s . 17 18 19 Example: Pruning Trees 3 Removing subtrees from a tree is called pruning Prune branches before 1 2 recursive processing 0 1 1 1 Tree Mutation 0 1 def prune(t, n): """Prune all sub-trees whose label is n.""" b b.label != n t.branches = [______________ for b in t.branches if _____________________] for b in t.branches: b n prune(_______________________________, _______________________________) 21
Recommend
More recommend