Datové struktury a algoritmy Část 4 Abstraktní datové typy Petr Felkel
Data structures and algorithms Part 4 Abstract data types Petr Felkel
Abstraktní datové typy Zdůrazňují vnější chování datové struktury Input Control Output BLACK BOX Emphasize outer behavior of the data structure 3/ 82
Abstraktní datové typy Abstrahují od jak? Jak jsou data zobrazena v paměti Jak jsou prováděny operace Zdůrazňují co? Co s nimi operace s daty provádějí nezávisle na konkrétní implementaci 4/ 82
Abstract data types Abstract from: How? How is the data stored in memory? How are the operations executed? Emphasize: What? What do the operations do with the data (independent of any particular implementation)? 5/ 82
Example Queue (Fronta) Čelo Front 6/ 82
Queue (Fronta) FIFO = First-in, First-out Access to front Insert to back 7/ 82
Abstract Queue (Fronta) Operations: Variables: e element, Q queue init(), front( Q ), delfront( Q ), inslast( e, Q ) Axioms: 1. init() returns a queue 2. front( inslast( e, init() )) = e 3. delfront( inslast( e, init() ) ) = init() 4. front( inslast( e, inslast( f, Q ) )) = front( inslast( f, Q ) ) 5. delfront( inslast( e, inslast( f, Q ) )) = inslast( e, delfront( inslast( f, Q ) )) 8/ 82
Expression Abstract Queue (Fronta) is a way of construction of data object by means of a sequence of operations Ex: inslast(4, inslast(3, inslast(2, init() ))) represents a queue 4 3 2 front(inslast(4, inslast(3, inslast(2, init() )))) represents its first element (head), 2 9/ 82
1. init() Abstract Queue (Fronta) Returns a queue Is informally empty Must not be formally defined 10 / 82
Abstract Queue (Fronta) 2. front( inslast( e, init() )) = e = is a rewrite operation axiom A=B means instead A, write B Ex: front( inslast( 99, init() )) means 99 11 / 82
Abstract Queue (Fronta) 3. delfront( inslast( e, init() )) = init() Axioms allow reduction of expressions Ex. delfront( inslast( 6, init() )) means init() 12 / 82
Abstract Queue (Fronta) 4. front( inslast( e, inslast( f, Q ) )) = front( inslast( f, Q ) ) Ex: front( inslast( 11, inslast( 5, init() ))) means [according to axiom 4] front( inslast( 5, init() )) 5 means [ax. 2] 13 / 82
Abstract Queue (Fronta) 5. delfront( inslast( e, inslast( f, Q ) )) = inslast( e, delfront( inslast( f, Q ) )) Ex: delfront( inslast( 8, inslast( 6, init() ))) means [according to axiom 5] inslast( 8, delfront( inslast( 6, init() ))) means [ax. 3] inslast( 8, init() ) 14 / 82
Abstract Queue (Fronta) And what to do with: front( init() )? delfront( init() )? Behavior is in the queue undefined! Add error states 6. error_empty_queue(q) = Q error_empty_elem() = e 7. front( init() ) = error_empty_elem() Ignore 8. delfront( init() ) = init() 15 / 82
Abstract Queue (Fronta) complete init() returns a queue Variables: e element, Q queue front( init() ) = error_empty_elem() front( inslast( e, init() )) = e front( inslast( e, inslast( f, Q ) )) = front( inslast( f, Q ) ) delfront( init() ) = init() delfront( inslast( e, init() ) ) = init() delfront( inslast( e, inslast( f, Q ) )) = inslast( e, delfront( inslast( f, Q ) )) error_empty_queue(q) = Q 16 / 82
Abstract Queue - extensions empty( init() ) = true empty( inslast( e, Q )) = false Variables: e element, Q queue length( init() ) = 0 length( inslast( e, Q ) ) = succ( length( Q ) ) 17 / 82
Some theory 18 / 82
Abstraktní datový typ Abstraktní datový typ (ADT) je množina druhů a příslušných operací, které jsou přesně specifikovány. Je nezávislý na konkrétní implementaci. Abstract data type is a set of data sorts and associated operations that are precisely specified independent of any particular implementation. [PEB, nist.gov] 19 / 82
Abstraktní datový typ SYNTAXE signatura = deklarace druhů Jména oborů hodnot + deklarace operací Jména operací Druhy (a pořadí) argumentů operací Druh výsledku SÉMANTIKA = popis vlastností operací! axiomy 20 / 82
Abstract data type SYNTAX signature = declaration of sorts Names of scopes of values + declaration of operations Names of operations Sorts (and order of) arguments of operations Sort of the result SEMANTICS axioms = descritption of the operations properties! 21 / 82
Abstract Queue SIGNATURE init empty Queue Signature as a diagram Elem inslast Bool delfront front error_empty_queue 22 / 82
Abstract Queue SIGNATURE Signature in symbols init: -> Queue inslast(_,_): Elem,Queue -> Queue empty(_): Queue -> Bool front(_): Queue -> Elem delfront(_): Queue -> Queue 23 / 82
Why syntax not enough? Example: Interval {1, 2, 3, 4, 5,..., 11, 12} a) month_in_year b) natural_number_from_interval_1_až_12 BUT! Operation successor (12) a) 1 for month_in_year b) undefined for number_from_interval 24 / 82
Implementation From Abstract Data Type to Data Structure 25 / 82
Data structure Data structure = a realization of ADT 1. Choose data type B for implementation of A. [integer / interval 1..12] 2. Choose representation of objects of type A by means of objects of type B. [1..January, 2..February, ] 3. Code the operations on A by means of operations of B 26 / 82
Queue implementation In Array Linear Circular 0 1 2 3 4 5 A D B or 0 1 2 3 4 5 A D B = A D B last SIZE-1 first last SIZE-1 In Dynamic Memory front last A D B 27 / 82
Example SPECIFIC Queue Interface (Syntax): Class Queue { int _size; Coupled together with Elem elems[max_size]; public: Queue init(); Elem front(); Queue delfront(); Queue inslast( Elem elem ); bool empty(); } Implementation: = 28 / 82
Specific Queue x Abstract Queue Implementation bool empty() { return (_size == 0) }; or bool empty() { return (_front == null ) }; or bool empty() { return (_front == _last ) }; Axioms vars: Elem e; Queue q; eqns: empty( init ) = true empty( inslast( e, q )) = false 29 / 82
Specific Queue x Abstract Queue Implementation Axioms Elem front(){ if(_front == null ) error; else return ( _front->val ) }; or Elem front() { if(_frontidx == _lastidx ) error; else return( elems[_frontidx] ) }; front( init ) = error_empty_elem front( inslast( e, q )) = if( empty( q )) then e else front( q ) Or front( inslast( e, init() )) = e front( inslast( e, inslast( f, Q ) )) = front( inslast( f, Q ) ) Details later on 30 / 82
Queue in an Array Elem queue ::front(void) { if( last == 0 ) return error_empty_elem(); else return array[front]; } void queue::delfront(void) { if( last!= 0) { //!empty() { last--; for(int i=0; i<last; i++) array[i] = array[i+1]; } } 31 / 82
Queue in Dynamic Mem. Elem queue ::front(void) { if( last!= null ) return error_empty_elem(); else return front->val; } void queue::delfront(void) { if( first!= null) { //!empty() Item *delitem = first; first = first.next; delete delitem; last } } front A delitem D B E 32 / 82
Queue in Dynamic Mem. void queue::inslast( elem e ) { Item *newitem = new Item; newitem->val = e; newitem->next = null; last.next = newitem; last = newitem; } front last A D newitem B E bool queue ::empty(void) // empty(): Queue -> Bool { if( last == NULL ) return true; else return false; } 33 / 82
Fronta Shrnutí úlohy hromadné obsluhy FIFO = First-in, First-out kdo dřív přijde, ten dřív mele přístup pouze k prvnímu prvku (front) vkládání pouze na konec (back) homogenní, lineární, dynamická 34 / 82
Queue - Summary multi-user services FIFO = First-in, First-out only first element accessible (front) insert only at and (back) homogeneous, linear, dynamic 35 / 82
(Abstraktní) datové typy Počet a vzájemné uspořádání složek statické = nemění se dynamické = mění se Typ komponent homogenní = všechny stejného typu nehomogenní = různého typu Existuje bezprostřední následník lineární = existuje [např. pole, seznam, ] nelineární = neexistuje [strom, tabulka, ] 36 / 82
(Abstract) data types Number and arrangement of elements static = fixed, does not change dynamic = changes Type of the components homogeneous = all of equal type inhomogeneous = different types Direct successor existence linear = direct successor exists nonlinear = direct successor does not exist 37 / 82
Abstraktní datové typy Fronta (Queue) Zásobník (Stack) Pole (Array) Tabulka (Table) Seznam (List) Strom (Tree) Množina (Set) 38 / 82
Zásobník (Stack) Kdy? odložení informace, výběr v opačném pořadí (návrat z procedury, uzlové body cesty, náboje v pistoli,...) LIFO = Last-in, First-out poslední tam, první ven přístup pouze k prvku na vrcholu (top) vkládání pouze na vrchol (top) homogenní, lineární, dynamický 39 / 82
Stack (Zásobník) When? temporary data storage, access in reverse order (return from the procedure call), important nodes along the path, ammunition in pistol,...) LIFO = Last-in, First-out only top element accessible (top) insert on the top only homogeneous, linear, dynamic 40 / 82
Stack (Zásobník) init empty full Stack length Nat Elem push max Bool pop top 41 / 82
Stack (Zásobník) Operations: init: -> Stack empty(_): Stack -> Bool push(_,_): Elem, Stack -> Stack top(_): Stack -> Elem pop(_): Stack -> Stack length(_): Stack -> Nat max: -> Nat full(_): Stack -> Bool...omezen 42 / 82
Stack (Zásobník) empty( init ) = true empty( push( e, s )) = false top( init ) = error_elem top( push( e, s )) = e pop( init ) = init pop( push( e, s )) = s 43 / 82
Stack (Zásobník) 1. empty( init() ) = true 2. empty( push( e, s )) = false Ex: empty( push( X, push( Y, pop( push( A, init() ))))) -> false empty( pop( push( A, init() ))) ->??? Need for more axioms! 44 / 82
Stack (Zásobník) 3. top( init() ) = error_elem() 4. top( push( e, s )) = e Ex: top( push( X, push( Y, pop( push( A, init() ))))) -> X top( init() ) -> error_elem top( pop( push( A, init() ))) ->??? Need for more axioms! 45 / 82
Stack (Zásobník) 5. pop( init ) = init() 6. pop( push( e, s )) = s Ex: pop( push( A, init() )) -> init() pop( push( X, push( Y, pop( push( A, init() ))))) -> push( Y, pop( push( A, init() ))) -> push( Y, init() ) 46 / 82
Stack (Zásobník) ext. 7. length( init ) = 0 8. length( push( e, s )) = succ( length( s )) Ex: length( push( A, init() )) -> succ( length( init() )) -> succ( 0 ) -> 1 47 / 82
Stack (Zásobník) limit. 9. full( s ) = eq( length( s ), max ) 10. push( e, s ) = if full( s ) then error_stack 48 / 82
In Array Stack - implementation vals 0 1 2 3 4 5 A D B E last SIZE-1 In Dynamic Memory tos E B D A 49 / 82
Stack in Array class stack { private: elem vals[size]; int last; // array of elements // index behind (place for next item) public: void init( void ); elem top( void ); void pop( void ); void push( elem e ); void error( const char errortext[] ); }; stack(void); ~stack(void); // just calls init() 50 / 82
void stack::init(void) { last = 0; } Stack in Array // init: -> Stack elem stack::top(void){ // top(_): Stack -> Elem if( last > 0 ) return vals[last-1]; else error("stack is empty during top()"); return 0; } void stack::pop(void){ // pop(_): Stack -> Stack if( last > 0 ) last--; else error("stack is empty during pop()"); } 51 / 82
void stack::push( elem e ) { if( last < SIZE ) vals[ last ++ ] = e; } Stack in Array //push(_,_):elem, Stack -> Stack bool stack::empty(void)// empty(_): Stack -> Bool { if( last == 0 ) return true; else return false; } 52 / 82
#include "stack.h" //#include "stackdyn.h" Stack Usage void main( void ) { stack s; // OUTPUT: cout << s.empty() << endl; // 1 s.push( 2 ); s.push( 3 ); cout << s.empty() << endl; // 0 cout << s.top() << endl; // 3 s.pop(); cout << s.top() << endl; // 2 s.pop(); cout << s.top() << endl; // Stack is empty during top cout << s.empty() << endl; // 1 } 53 / 82
Stack in Dynamic Mem. class stack { private: Item * s; // head of the list of elements public: void init( void ); elem top( void ); void pop( void ); void push( elem e ); void error( const char errortext[] ); }; stack(void); ~stack(void); // just calls init() 54 / 82
Stack in Dynamic Mem. void stack::init(void) { s = NULL; } // init: -> Stack elem stack::top(void){ // top(_): Stack -> Elem if( s!= NULL ) return s->val; else error( "Stack is empty during top()" ); return 0; } void stack::pop(void){ // pop(_): Stack -> Stack if( s!= NULL ) { Item* tmp = s; s = s->next; delete( tmp ); } else error( "Stack is empty during pop()" ); } 55 / 82
Abstraktní datové typy Fronta (Queue) Zásobník (Stack) Pole (Array) Tabulka (Table) Seznam (List) Strom (Tree) Množina (Set) 56 / 82
Pole (array) j 1 2 3 i 6 A B C 7 D E F Array A of 2x3 elements Row index i {6,7} Column index j {1, 2, 3} A[6,3] -> C 57 / 82
Pole (array) 6 A B C D E F Kdy? prvky stejného typu - homogenní všechny prvky současně v paměti rychlý náhodný přístup (random-access) známý počet prvků - statické indexy uspořádány - lineární ---- dán počet dimenzí (n) a meze indexů přístup k prvkům - mapovací funkce 7 1 2 3 A[6,3] -> C 58 / 82
When? Array (pole) all elements equal - homogeneous all elements in memory fast random access fixed number of elements - static indexes ordered - linear ---- given: number of dimensions (n) and index range access to elements - map function 6 A B C 7 1 2 D E F 59 / 82 3 A[6,3] -> C
init Pole (array) Array Elem 1 2 3 6 A B C 7 D E F Idx lower upper set get 60 / 82
Pole (array) Operations init(_,_): Idx, Idx -> array upper(_): Array -> Idx lower(_): Array -> Idx set(_,_,_): Array,Idx,Elem->Array get(_,_): Array, Idx -> Elem 1-D (one dimensional) n-d -> Elem ~ (n-1)d Array 6 A B C 7 1 2 3 D E F 61 / 82
Pole (array) lower( init(m,n) ) = m lower( set(a,i,e) ) = lower( a ) upper( init(m,n) ) = n upper( set(a,i,e) ) = upper( a ) set( a, i, e ) = if( or( lt(i,lower(a)), lt(upper(a),i) )) then error_array else set( a, i, e ) 1 2 3 6 A B C 7 D E F 62 / 82
Pole (array) 1 2 3 6 A B C 7 D E F get( init(m,n), i ) = error_elem get( set( a, i 1, e ), i 2 ) = = if( eq(i 1, i 2 )) then e else get( a, i 2 ) Praxe: array[i] = e zápis set(array,i,e) e = array[i] zápis e = get(array,i) 63 / 82
Array (Pole) mapping function Logical structure Array a[6..7, 1..3] Ex: a[7,2]->e Physical location in memory Row order (po řádcích) 0 1 2 3 4 5 A B C D E F Relative address (offset) 1 2 3 6 A B C i 7 D E F Indices Column order (po sloupcích) 0 1 2 3 4 5 A D B E C F j row 6 row 7 col 1 col 2 col 3 Base address map(7,2) -> (base + 4) map(7,2) -> (base + 3) 64 / 82
Array (Pole) mapping function 0 1 2 3 4 5 A B C D E F map(i,j) = a(i min, j min ) + (i-i min ) n j + (j-j min ) Base address row order (po řádcích) 2D Column offset Row offset map(i,j) = a(0,0) + (i*n j + j) for indices from (0,0) Ex: map(7,2) = a(6,1) + (i-6)*3 + (j-1) map(7,2) = a(6,1) + (7-6)*3 + (2-1) = a(6,1) + 4 row length = Number of columns Relative address i 1 2 6 A B C 7 3 D E F 0 1 2 3 4 5 A B C D E F j 65 / 82
Array (Pole) mapping function 0 1 2 3 4 5 A B C D E F map(i,j,k) = a(i min, j min, k min ) + (i-i min ) n j n k + + (j-j min ) n k + + (k-k min ) Base address map(i,j,k) = a(0,0,0) + (i*n j + j) * n k + k for indices from (0,0) row order (po řádcích) 3D Relative address (element offset) 66 / 82
Array (Pole) mapping function 0 1 2 3 4 5 A D B E C F map(i,j) = a(i min, j min ) + (i-i min ) + column order (po sloupcích) 2D Base address + (j-j min ) n i Relative address map(i,j) = a(0,0) + j * n i + i for indices from (0,0) Ex: map(7,2) = a(6,1) + (7-6) + (2-1) * 2 = a(6,1) + 3 67 / 82
Array (Pole) mapping function 0 1 2 3 4 5 A D B E C F column order (po sloupcích) 3D map(i,j,k) = a(i min, j min, k min ) + (i-i min ) + + (j-j min ) n i + Base address + (k-k min ) n i n j Relative address map(i,j,k) = a(0,0,0) + (k*n j + j) * n i + i for indices from (0,0) 68 / 82
Array speedup by Iliff s vectors Mapping functions need multiplications -> change them to additions (0) (1) (2) 3 4 A: array [3..4, 1..3, 0..2] i (0) 1 2 3 j j (0) 1 2 3 k 0 1 2 0 1 2 0 1 2 0 1 2 0 1 2 0 1 2 A B C E F G H I J K L M N O P Q R S [Hudec93, PT] 69 / 82
Abstraktní datové typy Fronta (Queue) Zásobník (Stack) Pole (Array) Tabulka (Table) Seznam (List) Strom (Tree) Množina (Set) 70 / 82
Vyhledávací Tabulka (Look-up Table) Kartotéka, asociativní paměť, převod mezi kódy, četnost slov,... homogenní, dynamická (nejen) Obsahuje a nelineární položky jednoznačně identifikované klíčem (podle klíče se položky vyhledávají) klíč jen 1x (je jedinečný) Př.: seznam hospod, klíčem je jejich jméno. 71 / 82
Look-up Table (Vyhledávací Tabulka) Card-register, associative memory, code conversion, word count homogeneous, dynamic (not only), Contains and nonlinear items items fully qualified by the key (item are searched according to their key) key is unique Ex.: directory of bars, the bar name is its key. 72 / 82
Tabulka (Look-up Table) delete read Key Table search Bool init Elem insert 73 / 82
Tabulka (Look-up Table) Operace: init: -> Table insert(_,_,_): Key, Elem, Table -> Table read(_,_): Key, Table -> Elem delete(_,_): Key, Table -> Table search(_,_): Key, Table -> Bool 74 / 82
Tabulka (Look-up Table) search( k, init ) = false search( k 1, insert(k 2,e,t)) = if( eq(k 1, k 2 ) ) then true else search( k 1, t ) 75 / 82
Tabulka (Look-up Table) delete( k, init ) = init delete( k 1, insert(k 2,e,t)) = if( eq(k 1, k 2 ) ) then delete( k 1, t ) else insert(k 2,e,delete(k 1, t)) 76 / 82
Tabulka (Look-up Table) read( k, init ) = error_elem read( k 1, insert(k 2,e,t)) = if( eq(k 1, k 2 ) ) then e else read( k 1, t ) 77 / 82
Tabulka (Look-up Table) insert(k 1,e 1, insert(k 2,e 2,t)) = if( eq(k 1, k 2 ) ) then insert(k 1,e 1, t) else insert(k 2,e 2, insert(k 1,e 1, t)) 78 / 82
Look-up Table Implementation In Array For small interval of keys sequential search O(n) direct access (key = index) O(1) converttolatin2( char_kamenický) LUT[char_kamenický] For large universum of keys Hash tables average O(1) to O(n) Wait for a separate lecture 79 / 82
Abstraktní datové typy Fronta (Queue) Zásobník (Stack) Pole (Array) Tabulka (Table) Seznam (List) Strom (Tree) Množina (Set) 80 / 82
Prameny / References Jan Honzík: Programovací techniky, skripta, VUT Brno, 19xx Karel Richta: Datové struktury, skripta pro postgraduální studium, ČVUT Praha, 1990 Bohuslav Hudec: Programovací techniky, skripta, ČVUT Praha, 1993 81 / 82
Prameny / References Steven Skiena: The Algorithm Design Manual, Springer-Verlag New York, 1998 http://www.cs.sunysb.edu/~algorith Gang of four (Cormen, Leiserson, Rivest, Stein): Introduction to Algorithms, MIT Press, 1990 Code exapmples: M.A.Weiss: Data Structures and Problem Solving using JAVA, Addison Wesley, 2001, code web page: http://www.cs.fiu.edu/~weiss/dsj2/c ode/code.html 82 / 82