CS 270 Algorithms Week 7 Oliver Kullmann Binary search Arrays, lists, pointers and rooted trees Lists Pointers Trees Binary search 1 Implementing rooted trees Tutorial Lists 2 Pointers 3 Trees 4 Implementing rooted trees 5 Tutorial 6
CS 270 General remarks Algorithms Oliver Kullmann Binary search Lists Pointers We conclude elementary data structures by discussing and Trees implementing arrays, lists, pointers and trees. Implementing rooted trees We also consider binary search. Tutorial Reading from CLRS for week 7 1 Chapter 10, Sections 10.2, 10.3, 10.4.
CS 270 Arrays Algorithms Oliver Kullmann Arrays are the most fundamental data structure: Binary search An array A is a static data-structure, with a fixed length Lists n ∈ N 0 , holding n objects of the same type. Pointers Access to elements happens via A [ i ] for indices i , typically Trees 0-based (C-based languages), that is, i ∈ { 0 , . . . , n − 1 } , or Implementing rooted trees 1-based, that is, i ∈ { 1 , . . . , n } . Tutorial This access, called random access , happens in constant time, and can be used for reading and writing. Due to the fixed length of arrays, one cannot really speak of “insertion” and “deletion” for arrays. Search in general is slow (one has to run through all elements in the worst case), however fast in sorted arrays, via “binary search”.
CS 270 Vectors Algorithms Oliver Kullmann The dynamic form of an array (i.e., it can grow) can be called a vector (as for C++; or “dynamic array”): Binary search Lists The growth of the vector happens by internally holding an Pointers array, and when the need arises, to allocate a new, bigger Trees array, copy the old content, and delete the old array. Implementing rooted trees When done “infrequently”, insertions (and deletions) at the Tutorial end of the vector require only amortised constant time; see the tutorial. However insertions and deletions at the beginning of the vector (or somewhere else) needs time linear in the current size of the vector, since the elements need to be shifted. A vector with additional structure, where also insertions and deletions at the beginning happens in amortised constant time, is typically called a deque (a “double-ended queue”).
CS 270 Searching in sorted vectors Algorithms Searching in general vectors takes linear time (running through Oliver Kullmann all elements): Binary 1 However, if the vector is sorted (we assume, as it is the search Lists default, ascending order), then it can be done in Pointers logarithmic time (in the length n of the vector). Trees 2 We present the Java-function binary search , which Implementing searches for an element x in an array A . rooted trees Tutorial 3 Instead of just returning true or false (for found or not), it is more informative to return an index i with A [ i ] = x , if found, and to return − 1 otherwise. 4 Since it might not be so easy to (efficiently) form sub-arrays, our version of binary search allows to specify a sub-array by its indices begin and end . 5 As it is usually best, this so-called “range” is right-open, i.e., the beginning is included, but the ending excluded . 6 The role model for that is begin = 0 and end = n .
CS 270 Binary search Algorithms Oliver Kullmann class BinarySearch { public s t a t i c int b i n a r y s e a r c h ( f i n a l int [ ] Binary search A, int begin , int end , f i n a l int x ) { Lists i f (A == n u l l ) return − 1; Pointers i f ( begin == end ) return − 1; Trees while ( true ) { Implementing f i n a l int mid = ( begin+end ) /2; rooted trees Tutorial i f (A[ mid ] == x ) return mid ; i f ( begin+1 == end ) return − 1; i f (A[ mid ] < x ) { begin = mid+1; i f ( begin == end ) return − 1; } else end = mid ; } }
CS 270 Binary search (cont.) Algorithms Oliver Kullmann Binary search Lists public s t a t i c int b i n a r y s e a r c h ( f i n a l int [ ] Pointers A, f i n a l int x ) { Trees i f (A == n u l l ) return − 1; Implementing rooted trees return b i n a r y s e a r c h (A, 0 , A. length , x ) ; Tutorial } }
CS 270 Binary search with assertions Algorithms public s t a t i c int b i n a r y s e a r c h ( f i n a l int [ ] A Oliver Kullmann , int begin , int end , f i n a l int x ) { Binary i f (A == n u l l ) return − 1; search a s s e r t (0 < = begin < = end < = A. length ) ; Lists i f ( begin == end ) return − 1; Pointers while ( true ) { Trees a s s e r t (0 < = begin < end < = A. length ) ; Implementing rooted trees f i n a l int mid = ( begin+end ) /2; Tutorial a s s e r t ( begin < = mid < end ) ; i f (A[ mid ] == x ) return mid ; i f ( begin+1 == end ) return − 1; a s s e r t ( begin < mid ) ; i f (A[ mid ] < x ) { begin = mid+1; i f ( begin == end ) return − 1; } else end = mid ; } }
CS 270 Analysing binary search Algorithms Oliver Kullmann Binary We have a divide-and-conquer algorithm, with the characteristic search recurrence Lists T ( n ) = T ( n / 2) + 1 . Pointers Trees Implementing rooted trees That’s because we divide the array into two (nearly) equal Tutorial parts, i.e., b = 2 in the standard form of the recurrence for the Master Theorem. While we only need to investigate one of the two parts (due to the sorting!), i.e., a = 1 for the Master Theorem. Finally the work done for splitting happens in constant time, and thus c = 0 for the Master Theorem.
CS 270 Analysing binary search (cont.) Algorithms Oliver Kullmann We obtain the second case of the Master Theorem Binary search (log 2 (1) = 0), whence Lists Pointers T ( n ) = Θ(lg n ) . Trees Implementing Recall that this actually only implies an upper bound for the rooted trees run-time of binary search — the lower bound implied by the Tutorial implicit Ω holds only for the recurrence, but not necessarily for the run-time. However, it is not too hard to see that also for the algorithm, and actually for every possible search algorithm , we need at least lg( n ) comparisons.
CS 270 Removing random access from vectors, gaining fast Algorithms general insertion and deletion: Linked lists Oliver Kullmann Binary search With vectors we obtain random access — via indices, which are Lists just natural numbers, and thus arbitrary arithmetic can be Pointers performed with them — due to the contiguous and uniform Trees storage scheme: underlying is an array, which is stored as one Implementing rooted trees contiguous block of memory cells, all of the same size. Tutorial But to maintain contiguity, only deletions and insertions at the end of the vector are efficient (amortised constant-time) — if we give up contiguity, then we loose random access, but we gain efficient arbitrary deletions and insertions: (linked) lists . Lists formally implement a dictionary (search, insertion, deletion), but, different from “real” dictionaries, search is slow, while insertion and deletion is very fast, i.e., constant-time.
CS 270 Pointers to next and previous elements Algorithms Like a vector, the elements of a list are arranged in a linear order. Oliver Kullmann Binary The basic idea here is that search each elements contains a pointer Lists to the next and the previous element of the list. Pointers Trees Implementing So a list-object x is a triple: rooted trees Tutorial x.prev is a pointer to the previous element in the list; x.next is a pointer to the next element in the list; x.key contains the key (or the data, if there is no “key”). For the first element of the list, x.prev is NIL , and for the last element, x.next is NIL . The whole list is represented by a pointer L to the first element (as usual, NIL if the list is empty).
CS 270 Searching Algorithms Oliver Kullmann Binary search The SEARCH -function in Java-like code, using List as the Lists pointer-type (recall — nearly everything in Java is a pointer!): Pointers Trees s t a t i c L i s t search ( L i s t L , f i n a l Key k ) { Implementing while (L != n u l l && L . key != k ) rooted trees L = L . next ; Tutorial return L ; } Note that if x is not found, then L will automatically finally become NIL (that is, null for Java).
CS 270 Excursion: Searching, in C++ Algorithms Oliver Kullmann For comparison, the same code in C++: Binary const L i s t ∗ search ( const L i s t ∗ L , const Key k ) { search while (L != n u l l p t r and ∗ L . key != k ) Lists L = ∗ L . next ; Pointers return L ; Trees } Implementing rooted trees We see that in C/C++ we not only have pointers, but also Tutorial values (as the int s!), and thus one can distinguish between pointers and values: The *-operator makes pointer-types from value-types, and dereferences pointers (to values). Further remarks: “ const List ∗ ” means that we do not change the values . More idiomatic would be the use of the − > operator, which makes for example L − > key instead of ∗ L.key.
CS 270 Insertion Algorithms Oliver Kullmann Binary Inserting a list-object x into list L, at the beginning, again as search Java-code: Lists Pointers s t a t i c L i s t i n s e r t ( L i s t L , f i n a l L i s t x ) { Trees a s s e r t ( x != n u l l ) ; Implementing rooted trees x . next = L ; Tutorial x . prev = n u l l ; i f (L != n u l l ) L . prev = x ; L = x ; return L ; } Note that the return-value is the new list.
Recommend
More recommend