Datastructuren 2005/06 docent: Hendrik Jan Hoogeboom kmr.162, hoogeboom@.. 071 527 7062. werkgroep: Robert Brijder rbrijder@.. prog-opgaven: Sven van Haastregt svhaastr@.. tentamen programmeeropgaven 2x, in C++ boek bundels programmeertaal eerste werkcollege www.liacs.nl/ home/hoogeboo/datastructuren.html
verwante colleges databanken concepten progtalen software engineering Algoritmiek* datastructuren FundInformatica 1 (discrete wiskunde) Complexiteit complexiteit sorteeralgoritmes
abstract programmeren While ( S.boven >= 0 ) { a = S.vakje[S.boven] ; S.boven-- ; } struct S vakje boven While ( not S.isleeg() ) { S.pop( a ) ; } S Λ
abstracte datastructuren implementatie datastructuur niet zichtbaar in gebruik (in algoritme) wat doet de datastructuur hoe is dat geimplementeerd representatie keuze typen + definities implementatie coderen methoden specificatie vastleggen interface tijd & ruimte: randvoorwaarden implementatie bibliotheken LEDA, STL standard
adt voorbeeld S : stapel van gehele getallen domein: geordende rijen elementen uit Z operaties: init: S levert de lege rij () op. isleeg: S B true bij lege rij, anders false. push: S x Z S getal wordt vooraan in rij gezet. pop: S S x Z voorste getal wordt verwijderd en teruggegeven; ongedefinieerd bij lege rij. top: S Z voorste getal wordt teruggegeven, maar niet verwijderd. R : reële getallen domein: R operaties: 0, 1, +, -, *, /, =,
abstracte datatypen elementen domein structuur operaties gebruik datastructuur specificatie representatie interface coderen implementatie
abstractie dus ook OOP voordelen preciese specificaties gescheiden ontwikkeling modulariteit apart compileren: snel te wijzigen overdraagbaar header file afschermen informatie hiding bv. 0-de element string integriteit: consistentie, correctheid overzichtelijkheid onafhankelijkheid implementatie lokaal te wijzigen encapsulation
C++ bouwstenen + specie bool pointer * char array [] int class / struct float dieptestructuur = == destructor generiek programmeren OOP object oriented programming template <datatype>
nivo s van abstractie priority queue verzameling objecten met prioriteit initialiseren, leeg-test, toevoegen, hoogste vinden, en verwijderen heap complete boom met heap-eigenschap trickle-up sift-down array 1 2 3 4 5 6 7.. 40 38 31 12 37 6 3 vader(i) = i/2
u bent gewaarschuwd! class D af te leiden van een class B is een instantie van D ook een instantie van B? vraag wordt niet gesteld onjuist en potentieel gevaarlijk In de literatuur wemelt het van de gevallen waarin overerving misbruikt wordt: een cirkel wordt van een punt afgeleid (een cirkel is geen speciaal geval van een punt); een stack wordt van een lijst afgeleid (een stack is geen speciaal geval van een lijst).
zie Programmeermethoden Object data eigenschappen methoden gedrag OOP Class type van object Inheritance overerven eigenschappen + gedrag uit te breiden, aan te passen Polymorphism een object past zijn gedrag aan
list programmeeropgave queue fifo lifo maxq minq testprogramma insert 5 8 7 3 10 4 9
List (void); initialiseert een lege lijst. ~List (void); ruimt een bestaande lijst op. int Get (void); levert de waarde van void Change (int item); verandert de waarde void Insert (int item); voegt element toe aan de lijst void Append (int item); idem, nu ná void Delete (void); verwijdert het aangewezen element bool IsEmpty (void); test of lijst leeg is. bool IsBegin (void); bool IsEnd (void); tests of cursor naar eerste, void GoToBegin (void); void GoToEnd (void); void GoToPrevious (void); void GoToNext (void); functies om de cursor door de lijst te bewegen
typedef int Coor; klasse punt class Punt { public: // constructoren Punt(){ xx=0; yy=0; }; Punt( Coor x0, Coor y0 ) { xx=x0; yy=y0; }; // methoden void Schuif( Coor dx, Coor dy ); void Teken(); private: // data Coor xx, yy; }; methoden void Punt::Schuif( Coor dx, Coor dy ) { xx += dx; yy += dy; }; void Punt::Teken() { cout << "punt(" << xx << "," << yy << ") "; }; functie void Animatie1( Punt pt ) { pt.teken(); pt.schuif( 3, 6 ); cout << " naar "; pt.teken(); cout << endl; }; main Punt pt1, pt2(3,3) ; Animatie1( pt1 ); Animatie1( pt2 ); punt(0,0) naar punt(3,6) punt(3,3) naar punt(6,9)
klasse cirkel class Cirkel: public Punt { public: Cirkel() : Punt() { straal = 0; }; Cirkel( Coor x0, Cood y0, Cood str0) : Punt( x0, y0 ) { straal = str0; }; void Teken(); private: // naast xx, yy Coor straal; }; methoden void Cirkel::Teken() { cout << "[" << straal << "]"; Punt::Teken(); }; functie // overloading void Animatie1( Cirkel ck ) { ck.teken(); ck.schuif( 3, 6 ); cout << " naar "; ck.teken(); cout << endl; }; main Cirkel ck1(3,3,2); Animatie1( ck1 ); Cirkel *ck2 = new Cirkel(3,0,3); Animatie1( *ck2 ); Punt *ck3 = new Cirkel(1,2,3); Animatie1( *ck3 ); [2]punt(3,3) naar [2]punt(6,9) [3]punt(3,0) naar [3]punt(6,6) punt(1,2) naar punt(4,8)
early vs. late binding compile time vs. run time nieuwe klassen dynamische structuren functie void Animatie2( Punt & pt ) { pt.teken(); pt.schuif( 3, 6 ); cout << " naar "; pt.teken(); cout << endl; };? by reference virtuele methoden class Punt {.. virtual void Teken();.. }; main Punt *pt = new Punt(1,2), *ck = new Cirkel(3,3,2); Animatie2( *pt ); Animatie2( *ck ); punt(1,2) naar punt(4,8) [2]punt(3,3) naar [2]punt(6,9)
List ListNode ListItr many friends iterator list may return iterator ispastend() advance() retrieve() list sentinel header isempty makeempty() zeroth()! first() insert(x,p)!after p find(x) findprevious(x)! remove(x)!first x operator= ListNode( const Object & thelt = Object(), ListNode * n = NULL ) : element( theelt ), next( n ) { } const Object & retrieve() const void insert( const Object & x, const ListItr<Object> &p ); List(); List( const List & rhs ); const List & operator= (const List & rhs ); alias test! there is no remove(p)!? hence, in makeempty() remove( first().retrieve() ) can we avoid removing the header? 3. Lists WEISS kladje bij Weiss
class Stapel { public: \\ constructor Stapel() : boven(null) { } \\ methoden void Push(Link *n) { n->next=boven; boven=n; } bool isempty() { return boven==null; } Link *Top() { return boven; } void Pop() { Link *p=boven; boven=boven->next; delete p; } bool Zoek(Link *n) { for( Link *p=boven; p; p=p->next) if (*p==*n) return 1; return 0; } private: \\ data Link *boven; }; generiek programmeren oop: overerving templates class Link { public: \\ constructoren Link() : next(null) { } Link(Link *n) : next(n) { } \\ methoden virtual bool operator== (Link *n) =0; \\ data Link *next; }; klasse Stapel, generiek
class Link { public: \\ constructoren Link() : next(null) { } Link(Link *n) : next(n) { } \\ methoden virtual bool operator== (Link *n) =0; \\ data Link *next; }; class DoubleLink : public Link { public: \\ constructor DoubleLink(double w) : dewaarde(w) { } \\ methoden bool operator== (Link *d) { return dewaarde== ((DoubleLink*)d)->deWaarde; } \\ data double dewaarde; }; Stapel destapel; destapel.push(new DoubleLink(2.7)); destapel.push(new DoubleLink(1.0)); destapel.push(new DoubleLink(3.1)); Doublelink *getal; getal=(doublelink*) destapel.top(); destapel.pop(); link voor Double-Stapel
template <class T> class Stapel { public: \\ constructor Stapel() : boven(0) { } \\ methoden void Push(T v) { boven=new Link<T>(v,boven); } bool isempty() { return boven==0; } T Top() { return boven->waarde; } void Pop() { Link<T> *p=boven; boven=boven->next; delete p; } bool Zoek(T v) { for( Link<T> *p=boven; p; p=p->next) if (v==p->waarde) return 1; return 0; } private: \\ data Link *boven; }; template <class T> class Link { public: \\ constructor Link(T v, Link *n) : waarde(v), next(n) { } \\ data Link *next; T waarde; }; Stapel<double> destapel; destapel.push( 2.717 ); destapel.push( 1.000 ); destapel.push( 3.1415 ); Doublelink *getal; getal= destapel.top(); destapel.pop(); beter: const T & klasse Stapel, template
Standard Template Library Standard Template Library template standard container sequentieel associatief empty() size() iterator vgl. pointer *p p++ == algoritme zoeken, tellen kopiëren, reverse sorteren heap - 67 c.begin() 31 c.end() 02 77 35
STL container sequential vector array, dynamisch list lijst, dubbel verbonden deque array, tweezijdige associative set verzameling map sleutel+waarde paren multimap.. meerwaardig adaptor priority_queue (niet list) queue rij (niet vector) stack stapel
#include <iostream> #include <vector> using namespace std; int main() { STL vector vector<int> v; int i; for (i=0; i<10; i++) v.push_back(i); cout << v.size() << endl; for (i=0; i<10; i++) cout << v[i] << ; cout << endl << v.front() << << v.back() << endl; vector<int>::iterator p = v.begin(); while ( p!= v.end() ) { cout << *p << ; p++; } } return 0;
STL adaptor, map #include <vector> #include <stack> spatie int main() { stack<vector<int> > st; int i; for (i=0; i<10; i++) st.push(i); while (!st.isempty() ) { cout << st.top() << ; st.pop(); } return 0; } #include <map> #include <string> map<string,int>::iterator p = mp.find( bob ); int main() { map<string,int> mp; mp[ tim ] = 3; cout << mp[ tim ] << << mp[ bob ]; return 0; }
specificatie in Alphard let stack = xi > where xi is int invariant 0 <= length(stack) initially stack = nullseq function push( s:stack, x:int ) pre 0 <= length(s) post s = s ~x pop( s:stack ) pre 0 < length(s) post s = leader(s ) top( s:stack ) returns x:int pre 0 < length(s) post x = last(s ) isempty( s:stack ) returns b:boolean post b = (s = nullseq) Alphard: mbv. abstract object rij xi >
algebraïsche specificatie type Intstack operators Create Intstack Push : Intstack * Int Intstack Pop : Intstack Intstack Top : Intstack Int Isempty : Intstack Boolean axioms Isempty( Create ) = true Isempty( Push(s,i) ) = false Pop( Create ) = Create * Pop( Push(s,i) ) = s Top( Create ) = 0 Top( Push(s,i) ) = i * end Intstack
lijsten Lijst List toevoegen en verwijderen op elke positie Stapel Stack lijst knoop / element positie top, push, pop LIFO pointers linked dubbel sentinel óók array Rij Queue FIFO enqueue, dequeue, first circulair array tweezijdig deque
representaties lijst size first last 1 2 3 4 5 6 7? insert, delete nb: kopiëren = gelijkheidstest == Λ sentinel? previous, insert before(!) simplist: linkinversie vorig deze vorig deze
^ thur 0 16 1 0 1 mon tues A 7 12 v 0 1 D fri sat wed 5 6 10 2 ζ 2 ζ 3 B C sun expressie code binaire zoek 4 2 heap ζ 1 c s a a e r tt a l trie e k expr expr * term term term* fact fact fact b a a syntax sat tues fri mon wed sun thur 2,3 boom BOMEN
bomen inhoudsopgave terminologie representatie boomwandeling» recursie, stapel» draden» linkomkering, Morris toepassing» union-find» priority queue binaire heap leftist tree, binomial,» dictionary binaire zoekboom AVL-, B-boom, red-black» compressie Huffman, LZW
bomen: abstract/wiskundig belangrijk, véél varieteiten bomen (adt) terminologie knoop, wortel, vader, kind (on)gericht (on)geordend binaire bomen (links rechts) vol, compleet beperkingen structuur aantal kinderen (binair, B-boom) hoogte subbomen (AVL-, B-boom) Compleet (heap) plaatsing sleutels zoekboom, heap
boomrepresentaties pointer template <class T> struct Knoop { T info; Knoop *links, *rechts; }; 7-air Knoop *kind[7]; cursor 16 17 18 3 18 array (complete boom) links i 2i 2 1 3 vader i i/2 4 5 6 7 eerste-kind-rechter-broer links rechts info... vgl. binair
binaire boom C++ template <class T> template struct BinKnp { T info; BinKnp<T> *links, *rechts; int tag // bij linkomkering }; constructor BinKnp ( const T& i, BinKnp<T> *l = NULL, BinKnp<T> *r = NULL ) : info(i) default { links = l; rechts = r; tag = 0; } constructor van type T
wat: systematische methode om knopen te bezoeken boomwandeling waarom: informatie halen, aanpassen structuur controleren, aanpassen uit te breiden naar grafen methodes: stapel (stack) WLR pre-orde depth-first LWR symmetrisch LRW post-orde rij (queue) nivo orde breadth-first
beoordeling: boomwandeling extra datastructuur (=ruimte) (ook recursie stapel) ruimte bv. extra bitjes tijd (stappen per knoop) verstoring structuur meerdere wandelingen mogelijk? bv. Lindstrom aanpassing representatie draden
uitgestelde verplichtingen boomwandeling elementair wandel( deze ): ): pre-bezoek( deze ); ); wandel( linkerkind ); ); symm-bezoek( deze ); ); wandel( rechterkind ); ); post-bezoek( deze ); ); Init(S); // // Stapel S 4 6 14 16 S Wortel; while ( not not IsLeeg(S) ) 7 8 15 16 do do deze S; S; if if ( deze nul nul ) 9 11 then pre-bezoek( deze ); ); S rechterkind; 10 S linkerkind; while ( deze nul ) fi fi do pre-bezoek( deze ); od od S rechterkind; deze := linkerkind; od 3 2 12 5 1 13
boomwandeling met stapel 2 1 5 3 4 6 start 7 9 8 10 11 15 12 13 14 deze=wortel deze==nul? n 16 j pre-bezoek(deze) S.push(deze) deze=ekind(deze) 2 1 5 3 4 6 7 eerste-kind, rechter-broer 9 heeft ekind? heeft rbroer? j S.isleeg()? n 8 S.pop(deze) deze=rbroer(deze) 10 11 15 12 13 14 16 stop rbroer van: deze/vader
re-draden in boom 0 wel re-kind: re li * pad naar opvolger -1 +1 X geen re-kind: draad naar opvolger vader vinden
bedrade binaire boom enum TagTp {Tak, Draad}; template <class T> struct BedradeKnp { T info; BedradeKnp<T> *links, *rechts; TagTp ltag, rtag; } template <class T> BedradeKnp<T> *LWROpvolg (BedradeKnp<T> *N) { BedradeKnp<T> *hulp; hulp = N->rechts; if (N->rtag == Tak) * hulp While (hulp->ltag == Tak) hulp = hulp->links; return hulp; }; N N * Draad Tak hulp
boomwandeling: linkomkering structuur verstoord één wandeling bezoek herkenbaar WLR, LWR, LRW mogelijk extra bitje per knoop 1 3 globale bezoekteller bezoek richting nieuw 1 links 1 2 geen l-kind 2 rechts 1 3 geen r-kind 3 omhoog 2 is l-kind 3 is r-kind link-omkering pad terug naar wortel gat tussen huidige knoop en vader bit onderscheid links/rechts 2 0 1
linkomkering vader = NULL; deze = wortel; bezoek = 1; klaar = false; while (!klaar ) switch ( bezoek ) { case 1: // pre-orde volgend = deze->links; if ( volgend==null ) bezoek = 2; else { deze->links=vader; vader=deze; deze=volgend; } break; case 2: // symmetrisch volgend = deze->rechts; if ( volgend==null ) bezoek=3; else { deze->tag=1; deze->rechts=vader; vader=deze; deze=volgend; bezoek = 1; } break; case 3: // post-orde if ( vader!= NULL ) if ( vader->tag== 0 ) { hulp=vader->links; vader->links=deze; deze=vader; vader=hulp; bezoek=2; } else { vader->tag = 0; hulp=vader->rechts; vader->rechts=deze; deze=vader; vader=hulp; } else klaar = true; }
herstellen? dan takken houden & draden herkennen boomtransformatie 1 5 4 2 8 6 9 7 3 1 5 4 2 8 6 9 7 3 methode Morris: twee visies 1 5 4 2 8 6 9 7 3 tijdelijke draden 1 5 4 2 8 6 9 7 3 1 5 4 2 8 6 9 7 3 1 5 4 2 8 6 9 7 3
methode Morris boom-wandelen in bedrade boom // zoek eerste in lwr-ordening while (heeft li-kind) do ga links od while (ongelijk NUL) do bezoek // zoek lwr-opvolger if (heeft re-kind) ga rechts while (heeft li-kind) do ga links od else // re-draad volg draad fi od 2 4 5 ga ga links links := := &leg &leg draad draad aan aan vanuit lwr-voorganger heeft heeft re-draad? := := ga ga rechts; ga ga links; bereik knoop knoop door door rechts te te volgen? 6 volg volg draad draad := := & verwijder draad draad