470 likes | 626 Views
Datastructuren Lijstjes (Stacks & Queues). Onderwerp 7. Dit onderwerp. Objectverwijzingen en objecten ADT versus Datastructuur Datastructuren voor dynamische verzamelingen ADT: Stack en Queue Simpele datastructuren voor verzamelingen Gelinkte Lijsten Implementatie van Stacks en Queues.
E N D
DatastructurenLijstjes (Stacks & Queues) Onderwerp 7
Dit onderwerp • Objectverwijzingen en objecten • ADT versus Datastructuur • Datastructuren voor dynamische verzamelingen • ADT: Stack en Queue • Simpele datastructuren voor verzamelingen • Gelinkte Lijsten • Implementatie van Stacks en Queues
1 Inleiding:Objectverwijzingen en Objecten Datastructuren en Abstracte Datatypes (ADT’s)
Objecten en Objectverwijzingen • In C# (bijv.) implementeer je verwijzingen naar objecten vaak met ingesloten objecten • Objectverwijzing: pijl/verwijzing naar ander (of hetzelfde) object (ik gebruik soms ook woord “pijl”) • Oude terminogie: pointer (bijv. in C, net anders gedrag...) • (Als objecten in geheugen verplaatsen dan wordt de verwijzing gecorrigeerd) • Class Lijst • Integer x • Lijst volgende 4 6 2 Datastructuren
ADT versus Datastructuur(Herhaling) • Datastructuur • is een systematische manier van organiseren van data en toegang verlenen tot diezelfde data. • Abstract data type • is een model van een datastructuur waarin gespecificeerd is: • type van de data • operaties ter ondersteuning van de datastructuur • de types van de parameters van deze operaties • Een abstract data type concentreert zich op functionaliteit, niet op tijd. Datastructuren
ADT: Priority Queue Operaties: Insert(S,x) Maximum(S) Extract-Max(S) Increase-Key(S,x,k) Datastructuur: Heap Implementeert al deze operaties efficient Insert: O(lg n) mits array groot genoeg Maximum: O(1) Extract-Max: O(lg n) Increase-Key: O(lg n) Maar er kunnen ook andere datastructuren gebruikt worden Voorbeeld: Priority Queue vs Heap
ADT: Priority Queue Operaties: Insert(S,x) Maximum(S) Extract-Max(S) Increase-Key(S,x,k) Datastructuur: Array Insert: Zolang array groot genoeg is, voeg element achteraan aan, en zet PQ-Size(S)++ O(1) Extract-Max: O(n) max = S[1]; pm = 1 fori=2 toPQ-Size(S) do ifS[i] > max then max = S[i]; pm=1 Verwissel S[pm] en S[PQ-Size(S)] PQ-Size(S)--; Return max; Maximum: O(n) Increase-Key(S,x,k): O(1) (als je weet waar x staat) Priority queue met gewoon array
Moraal • Van een ADT zijn verschillende implementaties door een datastructuur mogelijk • Soms is de ene datastructuur met de ene soort operaties sneller en de andere datastructuur met de andere soort operaties sneller • Welke je neemt hangt dus af van gebruik
2 Dynamische verzamelingen
Dynamische Verzamelingen • Verzameling elementen waar we operaties op uitvoeren, bijvoorbeeld: • Invoegen van nieuw element • Verwijderen van element • Vragen aan stellen, bijvoorbeeld: is er een element met keywaarde x? • Java: • Set Interface • C#: Collections
Verschillende datastructuren voor dynamische verzamelingen • Hangt af wat welke operaties we doen willen (en hoe snel welke operatie) • Invoegen • Weglaten • Zoeken van element op key • Element met kleinste/grootste key • Weglaten van eerst/laatst ingevoegde element • …
Allerlei soorten dynamische datastructuren • Wat wil je erop doen? • Dynamische verzameling (Set): • Invoegen van element • Weglaten van element • Testen of element in verzameling zit • Of “Map”-variant: geef extra gegevens van element • Vaak gekozen: Hashtabel (komt hierna) • Opleveren van langst geleden ingevoegde element (Queue) • Opleveren van laatst ingevoegde element (Stack) • Kleinste, grootste element, “next”: element dat net groter / kleiner is, …: (gebalanceerde) bomen
3 Stack
Stack • Stapel, vergelijkbaar met “stapel borden” • Wat je er ‘t laatst opzet haal je er ‘t eerst vanaf • LIFO: “Last In First Out”
Stack (stapel) • De Stack is een Abstract Data Type • “Dynamic Set” (dynamische verzameling): verzameling die verandert doordat er elementen in en uit gaan op een bepaalde manier • Gedefinieerd door een drietal operaties: • STACK-EMPTY(S) • Geeft een Boolean: true als de stack geen elementen bevat, false als er wel elementen in de stack zitten • PUSH(S,x) • Zet het element x “bovenop” de stack • POP(S) • Levert het “bovenste” element van de stack, en haalt dat element ervanaf. • D.w.z., verwijdert en levert dat element van alle elementen in de stack dat als laatste toegevoegd werd en nog niet ge-pop-ed is. Datastructuren
Toepassingen Stack • In veel algoritmen gebruikt • Onder andere: implementatie van recursie
Met array als je maximum grootte weet STACK-EMPTY(S) if top(S) == 0 then return true else return false PUSH(S,x) top(S)++; S[top(S)]=x {Kan fout gaan als array te klein…} POP(S) if STACK-EMPTY(S) then return “fout: stack leeg” else top(S)--; Return S[top(S)+1] Implementatie 1 van STACK
Implementatie 2 van STACK • Met Objectverwijzingen (Gelinkte lijst) • Zie volgende sheets • Nadelen: wat lastiger (en slechtere constante factor) • Voordelen: geen maximum formaat en kleine stack heeft weinig geheugen nodig
Object met verwijzing • Class Stackelement • int gegeven; • String nogeengegeven; • … • Stackelement next;
De stack heeft eenverwijzing die naar de topvan de stapel wijst top(S) 4 8 3 2
POP(S) top top 4 8 8 3 3 2 2
Push(S,4) top top 4 8 8 3 3 2 2
Commando uit programmeertaal dat test of verwijzing wijst naar niet-bestaand object In Boek-pseudocode: NIL I.e.: top(S)==NIL Sommige implementaties gebruiken een “dummy” object als eind van de stack IsEmpty(S) top top 8 3
4 Queue
Queue • Rij, vergelijk met rij voor loket • Wie het eerst komt, is ook het eerst weer weg • FIFO: “First In First Out”
Operaties op Queue • Enqueue(Q,x) • Voeg element toe aan Queue • Dequeue(Q) • Is-Empty(Q)
Queue heeft drie variabelen: lengte(Q): grootte array (1 … lengte(Q)) kop(Q) (head; begin) staart(Q) (tail; eind) Code hier test niet op lege queue’s (werkcollege) Kan fout gaan als array te klein is… EnQueue(Q,x) Q[staart(Q)] = x if (staart(Q) == lengte(Q)) then staart(Q) = 1 else staart(Q)++ Dequeue(Q) x = Q[kop(Q)] if (kop(Q)== lengte(Q)) then kop(Q) = 1 else kop(Q) ++ Implementatie van Queue op array
staart kop 5 2 8 6 3 1
Implementatie 2 van Queue: met verwijzingen • Voordelen en nadelen (tov array implementatie) net als voor Stacks • Vanwege voordelen worden vaak verwijzingen gebruikt
Queue als gelinkte lijst staart kop 8 7 3 12 5
Pseudocode • Class Queue • QueueElement kop; • QueueElement staart • Class QueueElement • Allerlei gegevens …; • QueueElement volgende
Dequeue(Q) staart kop 8 7 3 12 5 staart kop 7 3 12 5 Returnwaarde 8
Enqueue(Q,5) kop staart 8 7 3 12 staart kop 8 7 3 12 5
Implementatie met groter wordende arrays • Vaak ook gebruikt: • Neem een initiele grootte van de array • Als die te klein blijkt te worden: • Neem een grotere array, en kopieer alles naar die grotere array • Bijvoorbeeld: twee keer zo grote array
Dynamische array • size = 64 (bijvoorbeeld) • Neem een array A[1 ... size] • Invoegen: • Als we het 65e element willen invoegen: • Maak een array B[1 ... 2*size] • Kopieer A naar B; A=B; • size = 2*size • Tijd voor n invoegingen totaal ... O(n) • 1 operatie: Worst case: O(n); gemiddeld O(1) (“geamortiseerd”) Datastructuren
5 Nog even: enkel- en dubbelgelinkte lijsten
Gelinkte lijst en dubbel gelinkte lijst 8 7 3 12 1 6 0 2
Dubbelgelinktelijstobject • Class DGLO • Allerlei interessante gegevens, bijvoorbeeld int key; • DGLO vorige; (prev) • Ontbreekt in Enkelgelinkte lijst • DGLO volgende; (next) • De lijst zelf is een object met in elk geval • DGLO kop; (head) • DGLO staart; (tail)
Zoeken in gelinkte lijst List-Search(L,k) • x = kop(L) • while (x != NIL and key(x) != k) do • x = volgende(x); • return x; • Geeft object met k als dat bestaat, anders NIL
List-Insert(L,x) volgende(x)=kop(L); if kop(L) != NIL then vorige(kop(L))=x; kop(L) = x; vorige(x) = NIL Hier code om object x aan het begin van de lijst in te voegen In sommige toepassingen (bijv. als de lijst gesorteerd gehouden wordt) wil je ook middenin invoegen Invoegen in dubbelgelinkte lijst
Weglaten List-Delete(L,x) • if vorige(x)!= NIL • then volgende(vorige(x)) = volgende(x); • else kop(L)= volgende(x) • if volgende(x)!= NIL • then vorige(volgende(x)) = vorige(x); 1 6 0 2 1 0 2
Voordeel van dubbelgelinkte lijsten • Met name: het is makkelijk om een object middenin de lijst weg te laten • Nadeel: meer objectverwijzingen
Sentinels (stootblokken) • Speciale objecten aan kop en staart van de dubbelgelinkte lijst kunnen soms handig zijn ! 6 0 2
7 Combinaties van datastructuren
Datastructuren aan elkaar plakken • Voor veel toepassingen gebruik je combinaties van datastructuren • Voorbeeldje: leger(tjes) in strategisch spel • Er is een kaart: iedere locatie heeft paar coordinaten (x,y) • Er zijn legers: die hebben eigenschappen als: land, sterkte, naam, ervaring, functie, etc. • Elk leger staat op een locatie • Er kunnen meer legers op dezelfde locatie staan • Functies: • Speler kan al zijn legers bekijken op volgorde waarop ze ontstaan zijn • Speler kan locatie bekijken en daar al zijn legers zien • Leger kan verdwijnen (door “disband”-actie of verlies in veldslag) • Legers bewegen naar andere locaties
Objecten voor voorbeeld • Kaart is array [0 … ?, 0 … ?] van locatie • Locatie is Object met: • Eigenschappen als soort gebied, etc • Pijl naar dubbelgelinkte lijst van legers • Dubbelgelinkt maakt makkelijk om legers te laten verdwijnen • Leger is object met • Eigenschappen als sterkte, naam, etc. • Pijlen voor dubbelgelinkte lijst van locatie • Pijlen voor dubbelgelinkte lijst van legers van speler • Dus 4 pointers (verwijzingen naar Leger-objecten) • Elke actie is een flink, maar constant aantal operaties…
Conclusies • Sommige programmeertalen hebben dit soort datastructuren voorgebakken • Verschilt per taal • Soms (niet altijd) beter dan zelf maken… • Datastructuren voor verzamelingen • Lijsten, queues en stacks • Wat komt: • Datastructuren die ons snel laten zoeken of een element voorkomt en invoegen en weglaten: • Bomen • Hash-tabellen