Notes Data Structures continued Tyler Moore CSE 3353, SMU, Dallas, TX February 7, 2013 Portions of these slides have been adapted from the slides written by Prof. Steven Skiena at SUNY Stony Brook, author of Algorithm Design Manual. For more information see http://www.cs.sunysb.edu/~skiena/ POTD: Attempt parts (a) and (b) of Q1 Notes Before class on Thursday, please attempt problem Q1 (a) and (b) You won’t turn anything in on Thursday, but I want to know if you are able to successfully code this first part of the problem It’s OK if you can’t get a working solution. In this case, bring me your errors! If you get stuck on an error, I want to know about it, so we can discuss with the class. There is a VERY good chance some of your classmates are experiencing similar trouble. 2 / 29 Variables in Python Notes Better thought of as names or identifiers attached to an object. A nice explanation: http://python.net/~goodger/projects/pycon/2007/ idiomatic/handout.html#other-languages-have-variables 3 / 29 Key distinction: mutable vs. immutable objects Notes Immutable: objects whose value cannot change Tuples (makes sense) 1 Booleans (surprise?) 2 Numbers (surprise?) 3 Strings (surprise?) 4 Mutable: objects whose value can change Dictionaries 1 Lists 2 User-defined objects (unless defined as immutable) 3 This distinction matters because it explains seemingly contradictory behavior 4 / 29
Variable assignment in action Notes >>> #variables are really names ... c = 4 >>> d = c >>> c+=1 >>> c 5 >>> d #d does not change because numbers are immutable 4 >>> #lists are mutable ... a = [1,4,2] >>> b = a #so this assigns the name b to the object attached to name a >>> a.append(3) >>> a [1, 4, 2, 3] >>> b #b still points to the same object, its contents have just changed. [1, 4, 2, 3] 5 / 29 Im/mutablility and function calls Notes >>> #let’s try this in a function ... def increment(n): #n is a name assigned to the function argument when called ... #because numbers are immutable, the following ... #reassigns n to the number represented by n+1 ... n+=1 ... return n ... >>> a = 3 >>> increment(a) 4 >>> #a does not change ... a 3 6 / 29 Im/mutablility and function calls Notes >>> def sortfun(s): ... s.sort() ... return s ... >>> def sortfun2(s): ... l = list(s) ... l.sort() ... return l ... >>> a = [1,4,2] >>> sortfun(a) [1, 2, 4] >>> a [1, 2, 4] >>> b = [3,9,1] >>> sortfun2(b) [1, 3, 9] >>> b [3, 9, 1] 7 / 29 Im/mutablility and function calls Notes def selection_sort(s): """ Input: list s to be sorted Output: sorted list """ for i in range(len(s)): #don’t name min since reserved word minidx=i for j in range(i+1,len(s)): if s[j]<s[minidx]: minidx=j s[i],s[minidx]=s[minidx],s[i] return s >>> b [3, 9, 1] >>> selection_sort(b) [1, 3, 9] >>> b [1, 3, 9] 8 / 29
Empirically evaluating performance Notes Once you are confident that your algorithm is correct, you can evaluate its performance empirically Python’s timeit package repeatedly runs code and reports average execution time timeit arguments code to be executed in string form 1 any setup code that needs to be run before executing the code (note: 2 setup code is only run once) parameter ‘number’, which indicates the number of times to run the 3 code (default is 1000000) 9 / 29 Timeit in action: timing Python’s sort function and our Notes selection sort #store function in file called sortfun.py import random def sortfun(size): l = range(1000) random.shuffle(l) l.sort() >>> timeit.timeit("sortfun(1000)","from sortfun import sortfun",number=100) 0.0516510009765625 >>> #here is the wrong way to test the built-in sort function ... timeit.timeit("l.sort()","import random; l = range(1000); random.shuffle(l)" ,number=100) 0.0010929107666015625 >>> # WRONG way to time selection sort >>> timeit.timeit("selection_sort(l)","from selection_sort import selection_sort; import random; l = range(1000); random.shuffle(l)",number=100) 3.0629560947418213 >>> # RIGHT way to time selection sort >>> timeit.timeit("import random; l = range(1000); random.shuffle(l); selection_sort(l)","from selection_sort import selection_sort", number=100) 3.0623178482055664 10 / 29 Dynamic Arrays Notes Unfortunately we cannot adjust the size of simple arrays in the middle of a programs execution. Compensating by allocating extremely large arrays can waste a lot of space. With dynamic arrays we start with an array of size 1, and double its size from m to 2m each time we run out of space. How many times will we double for n elements? Answer: Only ⌈ lg n ⌉ . 11 / 29 How Much Total Work? Notes The apparent waste in this procedure involves the recopying of the old contents on each expansion. If half the elements move once, a quarter of the elements twice, and so on, the total number of movements M is given by lg n lg n ∞ i · n i i � � � M = = n 2 i ≤ n 2 i = 2 n 2 i i =1 i =1 i =1 Thus each of the n elements move an average of only twice, and the total work of managing the dynamic array is the same O ( n ) as a simple array. 12 / 29
Advantages of Linked Lists Notes The relative advantages of linked lists over static arrays include: 1 Overflow on linked structures can never occur unless the memory is actually full. 2 Insertions and deletions are simpler than for contiguous (array) lists. 3 With large records, moving pointers is easier and faster than moving the items themselves. Dynamic memory allocation provides us with flexibility on how and where we use our limited storage resources. 13 / 29 Question Notes Are Python lists like dynamic arrays or linked lists? 14 / 29 Implementing Linked Lists in Python Notes You would never actually want to use linked lists in Python Built-in lists are much more efficient Nonetheless, implementing linked lists serves as a nice introduction to OOP in Python Code at http://lyle.smu.edu/~tylerm/courses/cse3353/ code/linked_list.py Compare to the C code in ADM pp. 68–70. Which do you prefer? 15 / 29 Implementing Linked Lists in Python Notes 1 class Node : def i n i t ( s e l f , item=None , next=None ) : 2 s e l f . item = item 3 s e l f . next = next 4 def s t r ( s e l f ) : 5 return s t r ( s e l f . item ) 6 7 8 L i n k e d L i s t : 9 class def i n i t ( s e l f ) : 10 s e l f . length = 0 11 s e l f . head = None 12 1 #code f o r Node and L i n k e d L i s t in l i n k e d l i s t . py l i n k e d l i s t 2 import 3 l i l = l i n k e d l i s t . L i n k e d L i s t () 16 / 29
Inserting a Node Notes i n s e r t l i s t ( s e l f , item ) : def 1 node = Node ( item ) 2 node . next = s e l f . head 3 s e l f . head = node 4 s e l f . length = s e l f . length + 1 5 1 l i l . i n s e r t n o d e ( ‘ ‘ a ’ ’ ) 17 / 29 Searching the list Notes s e a r c h l i s t ( s e l f , item ) : def 1 node = s e l f . head 2 while node : 3 i f node . item==item : return node 4 node = node . next 5 return None 6 1 l i l . search ( ‘ ‘ b ’ ’ ) 18 / 29 Deleting from the list Notes def p r e d e c e s s o r l i s t ( s e l f , item ) : 1 node = s e l f . head 2 while node . next : 3 i f node . next . item==item : return node 4 node = node . next 5 return None 6 7 def d e l e t e l i s t ( s e l f , item ) : 8 p = s e l f . s e a r c h l i s t ( item ) 9 i f p : 10 pred = s e l f . p r e d e c e s s o r l i s t ( item ) 11 i f pred i s None : #i f p i s the head , then there w i l l be no p r e d e c e s s o r 12 s e l f . head = p . next 13 else : #otherwise point p r e d e c e s s o r to item ’ s next element 14 pred . next = p . next 15 19 / 29 Representing the list as a string Notes def s t r ( s e l f ) : 1 node = s e l f . head 2 l l s t r = ” [ ” 3 while node : 4 l l s t r += ” %s ”%node . item 5 node = node . next 6 l l s t r+= ” ] ” 7 return l l s t r 8 20 / 29
Recommend
More recommend