Lineaire data structuren array: vast aantal data items die aaneensluitend gestockeerd zijn de elementen zijn bereikbaar via een index lijst: een aantal individuele elementen die met elkaar gelinkt zijn deze links geven de volgorde in de lijst aan Een bepaald element in de lijst kan alleen aangesproken worden vanuit het voorgaande element. Er zijn twee voordelen: B F L + de lengte van de lijst kan veranderen tijdens de levensduur: men kan gemakkelijk elementen toevoegen en/of verwijderen; + de flexibiliteit om de elementen te herschikken in een andere volgorde. Doorlopen van een lijst De lijst sequentieel doorlopen kan met een eenvoudige while lus: void doorloop ( Node p) while ( p!= ( Node )NULL ) printf ( %c, p >data ) ; p = p >volgend ; printf ( \n ) ; Een mogelijke oproep is doorloop();. Een knooppunt van een lijst bevat twee delen: de informatie (van type Info) en een link naar de volgende node. typedef struct snode Info data ; Node ; struct snode volgend ; Voorbeeld: data is van het type char (typedef char Info;) veld volgend : pointer naar het volgende element op de lijst aangeven hoe het eerste element van de lijst kan bereikt worden via een extra variabele, (type is Node *) aangeven dat een bepaald element het laatste element is volgend veld vh laatste element gelijkstellen aan (Node *)NULL. B F L Een lijst construeren : telkens een element vooraan toevoegen: Node voegvooraantoe (Node, Info gegeven ) Node nieuw ; nieuw = (Node ) malloc ( sizeof (Node ) ) ; nieuw >data = gegeven ; nieuw >volgend = ; return nieuw ; Bovenstaande lijst wordt geconstrueerd door volgende oproepen: Node = ( Node )NULL; = voegvooraantoe (, ) = voegvooraantoe (, L ) = voegvooraantoe (, F ) = voegvooraantoe (, B )
Het zoeken van een element met waarde gegeven in de gelinkte lijst vanaf knooppunt : Iteratief: Node zoek (Node, Info gegeven ) Node p ; p = ; while ( p!= ( Node )NULL ) i f ( p >data == gegeven ) return(p ) ; p = p >volgend ; Het omkeren van de volgorde in de gelinkte lijst: B F L Node omkeren ( Node ) Node p, q, r ; p = ; r = (Node )NULL; while ( p!= ( Node )NULL ) q = p ; return r ; p = p >volgend ; q >volgend = r ; r = q ; Recursief: Node rzoek (Node p, Info gegeven ) if ( p == (Node )NULL ) return(p ) ; i f ( p >data == gegeven ) return(p ) ; return rzoek ( p >volgend, gegeven ) ; Oproep in beide gevallen: Node v = ( r ) zoek (, ) ; Operaties: toevoegen Het toevoegen van een nieuw element na knooppunt na: Node voegtoe (Node na, Info gegeven ) Node nieuw ; nieuw = (Node ) malloc ( sizeof (Node ) ) ; nieuw >data = gegeven ; i f ( na!= ( Node )NULL ) nieuw >volgend = na >volgend ; na >volgend = nieuw else nieuw >volgend = ( Node )NULL; return nieuw ;
B F L J na >volgend = q >volgend ; free (q ) ; return na ; Mogelijke oproep: if (==NULL) =voegtoe (NULL, g) else voegtoe (na, g ) ; B F L Een oproep zou kunnen zijn: x->volgend = verwijder(x->volgend);. Operaties: verwijderen Het verwijderen van een element na knooppunt na: Node verwijder (Node na) Node q = na >volgend ; Het verwijderen van het laatste element: Hiervoor moet eerst het voorlaatste element bepaald worden. Node verwijderachteraan ( Node ) Node vorig, actueel ; if ( q == (Node )NULL ) /* er is geen volgend element */ /* een mogelijkheid is het element zelf verwjderen */ free (na ) ; /* pointer die na aanwijst is NIE aangepast in NULL!! */ else i f (!= ( Node )NULL ) i f ( >volgend == ( Node )NULL ) fr ee ( ) ; else vorig = ;
actueel = >volgend ; while ( actu eel >volgend!= ( Node )NULL ) vorig = actueel ; actueel = actueel >volgend ; free ( actueel ) ; vorig >volgend = ( Node )NULL; return ; return ; p = p >volgend ; i f ( p >volgend!= twee ) /* er moet een element voor "twee" zitten */ p >volgend = twee >volgend ; twee >volgend = een >volgend ; een >volgend = twee ; Oefening. Wat gebeurt er als element twee reeds vlak na element een zit? Operatie herschikken Het herschikken van de lijst: element twee komt vlak achter element een: B Node herschik ( Node, Node een, Node twee ) Node p = ; if ( p == (Node )NULL ) F while ( p >volgend!= ( Node )NULL ) i f ( p >volgend == twee ) break ; L Dubbel gelinkte lijsten + gelinkte lijst: het volgend element kan gemakkelijk gevonden worden + vorige element vinden : gans de lijst moet tot aan het element doorzocht worden + dit efficiënter doen: gebruik maken van een dubbel gelinkte lijst: naast een verwijzing naar het volgende element op de lijst is ook een verwijzing naar het vorige element aanwezig. typedef struct dub Info data ; struct dub volgend ; struct dub vorig ; Dnode;
B F L herschik functie: voorgaande element van twee moet niet gezocht worden: Dnode herschik ( Dnode een, Dnode twee ) twee >vorig >volgend = twee >volgend ; twee >volgend >vorig = twee >vorig ; twee >volgend = een >volgend ; twee >vorig = een ; een >volgend >vorig = twee ; een >volgend = twee ; Het probleem van Josephus: Veronderstel dat N mensen besloten hebben om collektief zelfmoord te plegen door in een cirkel te gaan staan en telkens de M-e persoon te elimineren. Wie blijft er over of wat is de volgorde van afvallen? Bijvoorbeeld bij N = 7 personen met M = 4 is de volgorde: 4 1 6 5 7 3 2. /* * telaf.c : het probleem van Josephus : met een lijst */ #include <stdio. h> #include <malloc. h> #define MAXP 25 int a f t e l l e n ( int n, int m) ; int main( int argc, char argv [ ] ) int n, m, res ; Circulaire lijsten Voorgaande functie is correct wanneer na een nog minstens één element aanwezig is en wanneer voor en na twee ook minstens één element aanwezig is. Om deze voorwaarden niet te hoeven controleren kunnen we een circulaire lijst gebruiken. Wanneer het laatste element in de lijst wijst naar het eerste element, heeft men een circulaire lijst: 1 2 3 4 5 6 7 t printf ( Aantal mensen : ) ; scanf ( %d% c, &n ) ; printf ( Hoeveel overslaan : ) ; scanf ( %d% c, &m) ; res = a f t e l l e n (n, m) ; printf ( De laatste van %d met %d overslaan : %d\n, n, m, res ) ; typedef struct node int nr ; struct node next ; Elem ; void drukaf (Elem x ) ; int a f t e l l e n ( int n, int m)
Elem v ; Elem t ; /* wijst naar persoon die revolver net heeft doorgegeven */ int i ; v = t = (Elem ) malloc ( sizeof (Elem) ) ; v >nr = 1; for ( i =2; i<=n ; i++) v >next = (Elem ) malloc ( sizeof (Elem) ) ; v = v >next ; v >nr = i ; v >next = t ; t = v ; drukaf ( t ) ; while ( t!= t >next ) printf ( %3d, t >nr ) ; t = t >next ; while ( t!= x ) ; printf ( \n ) ; for ( i =1; i<m; i++) t = t >next ; v = t >next ; t >next = t >next >next ; free (v ) ; drukaf ( t ) ; return t >nr ; void drukaf ( Elem x) Elem t ; t = x ; do Stacks In veel toepassingen is het niet nodig om op een willekeurige plaats in de lijst elementen te kunnen toevoegen of verwijderen. Ook het herschikken van de elementen in de lijst is niet altijd vereist. Een stack is een data-structuur waarop slechts twee mogelijke operaties gedefinieerd zijn: push : het toevoegen van een element in het begin van de lijst; pop : het verwijderen van het eerste element van de lijst. Een klassiek voorbeeld is het berekenen van de waarde van een eenvoudige rekenkundige uitdrukking. De intermediaire resultaten kunnen gemakkelijk op een stack bewaard worden.
De waarde van 5 (((9 + 8) (4 6)) + 7) kan berekend worden met push(5); push(9); push(8); push( pop() + pop() ); push(4); 6 push(6); 8 4 24 7 push( pop() * pop() ); 9 17 17 17 408 415 push( pop() * pop() ); push(7); push( pop() + pop() ); push( pop() * pop() ); printf( "%d\n", pop() ); 5 5 5 5 5 5 2075 int isstackle eg (void ) return! top ; int isstackvol (void ) return top==max; + variabele top : de index van de eerste vrije plaats, de plaats waar een volgend element kan gepusht worden. + in deze eenvoudige functies wordt NIE nagegaan wordt of de array volzet is (bij een push) of de array leeg is (bij een pop) Oefening. Implementeer deze stack functies met behulp van een gelinkte lijst. Wanneer de maximale lengte van de stack op voorhand gekend is, kan bij de implementatie gebruik gemaakt worden van een array: #define MAX 100 static int stack [MAX] ; static int top ; void s tackinit (void ) top = 0; /* top wijst naar eerstvolgende vrije element */ void push ( int w) stack [ top++] = w; int pop(void ) return stack[ top ] ; Queues Bij een queue zijn er ook maar twee basisoperaties mogelijk: + toevoegen van een element aan de ene kant van de lijst, + wegnemen van een element aan de andere kant. Een implementatie met behulp van een array: #define MAX 100 static int queue [MAX] ; static int ; static int staart ; void queueinit (void ) staart = = 0; void put( int w)
queue [ staart ++] = w; /* staart wijst naar lege plaats */ if ( staart == MAX ) /* waar eerstvolgende element */ staart = 0; /* moet toegevoegd worden */ int get (void ) int w = queue [ ++]; /* wijst naar eerste element */ i f ( == MAX ) /* dat kan weggenomen worden */ = 0; return w; int isqueueleeg (void ) return ==staart ; Denktaak Veronderstel dat top een pointer is naar het eerste element van een dubbel gelinkte lijst. Zo n element is van type Stack. Wat is het effect van de functieoproep top = functie(top);? Verklaar uw antwoord met een tekening. typedef struct node int waarde ; struct node volgend ; struct node vorig ; Stack ; Stack functie ( Stack top ) Stack p = top ; Stack t ; Oefening. Implementeer deze queue functies met behulp van een dubbel gelinkte lijst. staart K N I P Stack v = top ; while ( p!= NULL ) t = p >volgend ; p >volgend = p >vorig ; p >vorig = t ; v = p ; p = t ; return v ;