recursion, O(n), linked lists 6/14
recursion reducing the amount of data to process and processing a smaller amount of data example: process one item in a list, recursively process the rest of the list related to proof by induction related to iteration
recursion typically used for linked data structures examples of recursion on lists: process one element, recursively process the rest look at a couple elements, recursively process half of the list
how fast does this run? given some implementation, how do we know how fast it is? can just run it, time it caution: disk access way slower than memory access way slower than CPU operations compare to another implementation (if available)
empirical analysis first priority: make sure an algorithm is working correctly if it s incorrect, doesn t matter much if it s fast or slow
empirical analysis second challenge: how does the input affect things? actual data random data perverse data (ideal data)
empirical analysis actual data get to know how good/bad an algorithm/ implementation is for real usage don t always have actual data, or enough actual data gives an expectation of actual run time
empirical analysis random data gives an overall sense of the algorithm if RNG is seeded with time, time tests aren t repeatable
empirical analysis perverse data measures worst-case performance provides guarantee - the code performs no worse than this
empirical analysis ideal data considering or testing ideal data is sometimes helpful algorithms that perform better on ideal data and the same worst-case will do better on average
theory and practice algorithms vs implementation measuring runtime evaluates implementation directly, algorithm indirectly two implementations of the same algorithm may take vastly different runtimes
theory good code should: work correctly run fast be easy to debug/develop/maintain use little memory
practice tradeoffs cpu vs memory performance vs readability/maintainability common mistakes ignoring performance of alg or implementation (runs too slow) focusing too much on performance (susceptible to difficult bugs, hard to tweak for new uses)
computational complexity a (rough) way to describe the runtime of an algorithm or implementation concerned only with rate of growth i.e., how well will this work with a huge input?
computational complexity algorithms have runtime that is some function of the input array input: function of the size of the array single number input: function of the actual number we call this number n
computational complexity given some algorithm or implementation: upper bound on runtime lower bound on runtime
Big-O suppose some code takes time f(n): O(n) is true if we can say c*n > f(n) above some base value of n and some value c O(n2 ) is true if we can say c*n 2 > f(n) above some base value of n and some value c same for any other function
Big-O O(?) is an upper bound on the runtime is trivially true if you overestimate tight bound - it s the slowest-growing function for which O(?) is true
others lower bound - same idea, use omega theta - the upper and lower bounds are the same functions
notes logarithms are common for problems that reduce the size of the input by a fraction at each step assume base 2 unless otherwise specified
examples prime numbers searching an array for a value computing a logarithm
ADTs difference between an abstract data type and an implemented data type ADT specifies what it is, what sorts of functions should be defined doesn t usually define the runtime characteristics
ADTs example: ADT: List implementations: linked lists, arrays
arrays vs linked lists what s the order of insert/remove for an array? at the end? at the beginning? at the middle?
linked lists a list where the data for each item in the list isn t necessarily stored consecutively in memory each element (node) in the list has a pointer to the next node why a pointer and not a non-pointer type?
linked lists basic singly-linked list is implemented with a struct like so: struct Node { int data; // could be any data type really Node* next; };
linked lists generally, initialize the next pointer to NULL basic linked list is just a pointer to the first node how do we: insert at the beginning? insert at the end? insert in the middle?
linked lists performance of insert/remove performance of accessing an arbitrary element performance of traversing a list how to traverse a singly-linked list
dummy nodes dummy nodes or sentinel values are common: common to have the first node in the chain be a sentinel/dummy value why? removes some if statements, speeds up runtime at the cost of some memory
doubly-linked lists advantage vs singly-linked lists: easier to remove elements disadvantage vs singly-linked lists: using even more memory common to have both a front and back sentinel/dummy node