Hoofdstuk 3. Week 5: Sorteren. 3.1 Inleiding

Vergelijkbare documenten
Tijd is geen goede maatstaf, want is afhankelijk van computer waarop algoritme wordt gedraaid.

public boolean equaldates() post: returns true iff there if the list contains at least two BirthDay objects with the same daynumber

Zevende college complexiteit. 17 maart Ondergrens sorteren, Quicksort

Datastructuren. Analyse van algoritmen. José Lagerberg. FNWI, UvA. José Lagerberg (FNWI, UvA) Datastructuren 1 / 46

Zevende college algoritmiek. 23/24 maart Verdeel en Heers

Elfde college algoritmiek. 18 mei Algoritme van Dijkstra, Heap, Heapify & Heapsort

Java Programma structuur

Datastructuren en algoritmen voor CKI

Zevende college Algoritmiek. 6 april Verdeel en Heers

Zevende college algoritmiek. 24 maart Verdeel en Heers

Zevende college complexiteit. 7 maart Mergesort, Ondergrens sorteren (Quicksort)

Elementary Data Structures 3

Programmeermethoden. Recursie. Walter Kosters. week 11: november kosterswa/pm/

Programmeermethoden. Recursie. week 11: november kosterswa/pm/

Zesde college complexiteit. 19 maart Mergesort, Ondergrens sorteren Quicksort, Shellsort

Opgaven QuickSort 3 mei 2019, Werkgroep, Datastructuren.

Modelleren en Programmeren

Datastructuren: stapels, rijen en binaire bomen

Hoofdstuk 2. Week 4: Datastructuren. 2.1 Leesopdracht. 2.2 Bomen. 2.3 Definitie

Modelleren en Programmeren

Modelleren en Programmeren

Stacks and queues. Introductie 45. Leerkern 45. Terugkoppeling 49. Uitwerking van de opgaven 49

Voortgezet Prog. voor KI

Uitgebreide uitwerking Tentamen Complexiteit, juni 2017

Datastructuren en algoritmen voor CKI

Algoritmiek. 15 februari Grafen en bomen

Lineaire data structuren. Doorlopen van een lijst

Small Basic Programmeren Text Console 2

Stacks and queues. Hoofdstuk 6

Tree traversal. Bomen zijn overal. Ferd van Odenhoven. 15 november 2011

TENTAMEN Programmeren 1 VOORBEELDUITWERKING

Vierde college complexiteit. 26 februari Beslissingsbomen en selectie Toernooimethode Adversary argument

Recursion. Introductie 37. Leerkern 37. Terugkoppeling 40. Uitwerking van de opgaven 40

Uitwerking tentamen Algoritmiek 9 juli :00 13:00

Programmeren in C++ Efficiënte zoekfunctie in een boek

Minimum Opspannende Bomen. Algoritmiek

Een eenvoudig algoritme om permutaties te genereren

public boolean egualdates() post: returns true i f f there i f the l i s t contains at least two BirthDay objects with the same daynumber

4EE11 Project Programmeren voor W. College 3, , Blok D Tom Verhoeff, Software Engineering & Technology, TU/e

Tentamen Programmeren in C (EE1400)

Tweede college algoritmiek. 12 februari Grafen en bomen

Uitgebreide uitwerking Tentamen Complexiteit, mei 2007

Uitwerking tentamen Algoritmiek 9 juni :00 17:00

Tentamen Objectgeorienteerd Programmeren TI februari Afdeling ST Faculteit EWI TU Delft

Uitwerking tentamen Analyse van Algoritmen, 29 januari

Eerste Toets Datastructuren 11 juli 2018, , Educ-α.

Hoofdstuk 7: Werken met arrays

recursie Hoofdstuk 5 Studeeraanwijzingen De studielast van deze leereenheid bedraagt circa 6 uur. Terminologie

Vierde college complexiteit. 16 februari Beslissingsbomen en selectie

Inleiding Programmeren 2

Achtste college algoritmiek. 12 april Verdeel en Heers. Dynamisch Programmeren

Inleiding Programmeren 2

Javascript oefenblad 1

Tree traversal. Ferd van Odenhoven. 15 november Fontys Hogeschool voor Techniek en Logistiek Venlo Software Engineering. Doorlopen van bomen

Elke groep van 3 leerlingen heeft een 9 setje speelkaarten nodig: 2 t/m 10, bijvoorbeeld alle schoppen, of alle harten kaarten.

Datastructuren: stapels, rijen en binaire bomen

Datastructuren en Algoritmen

Opgaven Zoekbomen Datastructuren, 20 juni 2018, Werkgroep.

Opgaven Zoekbomen Datastructuren, 15 juni 2016, Werkgroep.

Universiteit van Amsterdam FNWI. Voorbeeld van tussentoets Inleiding programmeren

Informatica: C# WPO 11

Vierde college algoritmiek. 2 maart Toestand-actie-ruimte Exhaustive Search

is eigenlijk overbodig!

REEKS I. Zaterdag 6 november 2010, 9u

10 Meer over functies

Vijfde college complexiteit. 21 februari Selectie Toernooimethode Adversary argument

Examen Programmeren 2e Bachelor Elektrotechniek en Computerwetenschappen Faculteit Ingenieurswetenschappen Academiejaar juni, 2010

Divide & Conquer: Verdeel en Heers vervolg. Algoritmiek

Voorbeeldtentamen Inleiding programmeren (IN1608WI), Oktober 2003, , Technische Universiteit Delft, Faculteit EWI, Afdeling 2.

Datastructuren Uitwerking jan

Modelleren en Programmeren

algoritmiek - antwoorden

Opgaven Abstracte Datastructuren Datastructuren, Werkgroep, 31 mei 2017.

Programmeren in Java les 3

Modelleren en Programmeren

Twaalfde college algoritmiek. 13 mei Branch & Bound Heap, Heapsort & Heapify

Tweede Toets Datastructuren 29 juni 2016, , Educ-Γ.

Vierde college complexiteit. 14 februari Beslissingsbomen

De doorsnede van twee verzamelingen vinden

Een gelinkte lijst in C#

Grafen. Indien de uitgraad van ieder punt 1 is, dan bevat de graaf een cykel. Indien de ingraad van ieder punt 1 is, dan bevat de graaf een cykel.

Datastructuren; (Zoek)bomen

Modelleren en Programmeren

Als een PSD selecties bevat, deelt de lijn van het programma zich op met de verschillende antwoorden op het vraagstuk.

Tentamen Imperatief en Object-georiënteerd programmeren in Java voor CKI

Opgaven Heaps Datastructuren, 8 juni 2018, Werkgroep.

NAAM: Programmeren 1 Examen 29/08/2012

Vijfde college algoritmiek. 2/3 maart Exhaustive search

Dynamisch Programmeren. Het Rugzakprobleem

IMP Uitwerking week 13

Tentamen Programmeren in C (EE1400)

Gegevens invullen in HOOFDLETTERS en LEESBAAR, aub. Belgische Olympiades in de Informatica (duur : maximum 1u15 )

Verslag Opdracht 4: Magische Vierkanten

2 Recurrente betrekkingen

HOOFDSTUK 3. Imperatief programmeren. 3.1 Stapsgewijs programmeren. 3.2 If Then Else. Module 4 Programmeren

De symmetrische min-max heap

Achtste college algoritmiek. 8 april Dynamisch Programmeren

Derde college complexiteit. 7 februari Zoeken

Transcriptie:

Hoofdstuk 3 Week 5: Sorteren 3.1 Inleiding Zoals al bleek in college 1 kunnen zoekalgoritmen veel sneller worden uitgevoerd, indien we weten dat de elementen in de lijst, waarin wordt gezocht, geordend voorkomen. Niet voor niets zijn telefoongidsen, adresbestanden, woordenboeken en dergelijke alfabetisch geordend. Zelfs voor relatief kleine bestanden loont het vaak al de moeite de gegevens in zekere geordende volgorde beschikbaar te hebben, zeker, als zo n bestand vaak doorzocht moet worden. Helaas worden gegevens slechts zelden geordend aangeleverd. (Een student zal zijn studiegenoten niet in alfabetische volgorde tegenkomen en dus ook niet in alfabetische volgorde in zijn agenda hun adressen kunnen noteren). We zullen dus op de één of andere manier de ongeordende gegevens moeten ordenen. 1 ) Het op volgorde zetten van gegevens wordt wel sorteren genoemd. In algemene vorm komt het bij sorteeralgoritmen neer op: Gegeven: een zekere list alsmede een ordenings relatie. De ordeningsrelatie geeft aan in welke volgorde de elementen van de lijst moeten voorkomen. Gevraagd: een algoritme, dat list sorteert volgens de gegeven ordening We zullen in dit hoofdstuk aannemen, dat de lijst een array van Comparable, zodat iedere Class die de interface Comparable implementeerd door deze methods kan worden gesorteerd. De interface Comparable definieert de ordeningsrelatie, met de method compareto: public int compareto(object o) 1 ) Een andere mogelijkheid is, elk nieuw gegeven direkt op de goede plaats (volgens de ordening) tussen te voegen. Deze methode zullen we hier niet bespreken. 1

De waarde van a.compareto(b) is kleiner dan nul als a < b volgens de ordeningsrelatie, a.compareto(b) == 0 als a = b volgens de ordeningsrelatie, en a.compareto(b) > 0 als a > b volgens de ordeningsrelatie. We zullen de gewenste eindtoestand realiseren, uitsluitend door gebruik te maken van verwisseling van elementen in de lijst list (zogenaamd sorteren in situ). Daarvoor zullen we onderstaande method gebruiken: private static void swapitems ( Comparable list[], int i, int j ) // pre: list[i] = X en list[j] = Y // post: list[i] = Y en list[j] = X Comparable hulp = list[i]; list[i] = list[j]; list[j] = hulp; 3.2 Selection sort Het algoritme voor Selection Sort is eenvoudig. Zoek het kleinste element uit de lijst en plaats het voorop de lijst. Zoek vervolgens het kleinste element in het resterende deel van de lijst en plaatst dat op de volgende plaats in de lijst en herhaal dit totdat alle elementen op de juiste plaats staan. Het algoritme heeft dan de vorm: private static int smallestitem ( Comparable list [], int left, int right ) // returns index of smallest item in list between // left and right-1 inclusive int index, result = left; for ( index = left + 1; index < right; index ++ ) if (list[index].compareto(list[result]) < 0) 2

result = index; return result; public static void selectionsort ( Comparable list [], int length ) // Sorts list int index; for ( index = 0 ; index < length - 1 ; index ++ ) swapitems( list, index, smallestitem( list, index, length ) ); 3.3 Insertion sort Bij insertion sort geldt dat het stuk list[b..index-1] al gesorteerd is, maar in het resterende deel kunnen nog kleinere waarden voorkomen. Bij iedere stap wordt het gesorteerde stuk list[b..index-1] met één element uitgebreid tot list[b..index]. Daarvoor moet het element list[index] op de juiste plaats tussengevoegd worden. Hiervoor wordt een nieuwe variabele next geïntroduceerd met als waarde index. De elementen list[next] en list[next-1] worden met elkaar vergeleken en zo nodig met Swap verwisseld. We herhalen deze stap voor next:=next-1 totdat er niet meer verwisseld hoeft te worden. Zo zakt bij wijze van spreken het element list[index] in de lijst list[0..index-1] af totdat de juiste plaats is gevonden. We kunnen de binnenste lus implementeren met: next = index; // Voeg element[index] toe aan de gesorteerde lijst while (( next > 0 ) &&!(list[next].compareto( list[next-1]) > 0)) swapitems( list, next, next-1 ); 3

next = next - 1; Dit houdt dit in, dat we net zolang het element list[next] met zijn voorganger verwisselen, tot het op de juiste plaats staat. De method ziet er als volgt uit: public static void insertionsort ( Comparable list [], int length ) // Sorts list int index, next; for (index = 1; index < length; index ++ ) // Invariant: list[0]..element[index-1] zijn // gesorteerd next = index; // Voeg list[index] toe aan de gesorteerde lijst while (( next > 0 ) &&!(list[next].compareto( list[next-1] ) > 0)) swapitems( list, next, next-1 ); next--; 3.4 Heapsort Heapsort is een efficiënte sorteer methode, zowel in geheugengebruik als in rekentijd. Net als de vorige twee methoden is het een in situ methode die niet of nauwelijks extra geheugen nodig heeft. Daarnaast is het een methode die gegarandeerd O n 2 log n efficiëntie heeft wat betreft rekentijd, tegen O n 2 voor de voorgaande methoden zie 3.7. Heapsort gebruikt een datastructuur die heap of priority-queue wordt genoemd. Een 4

heap kan worden beschouwd als een boom, waarin het root-element de hoogste waarde (of prioriteit) heeft. Verder geldt dat voor iedere knoop in de boom, beide kinderen geen hogere waarde hebben dan de knoop zelf. Dit wordt de heap conditie genoemd. Op een heap kunnen we drie acties uitvoeren: (i) initialisatie van een lege heap, (ii) invoegen van een element met willekeurige waarde, en (iii) verwijderen van het root element. Heapsort neemt nu de volgende stappen: Initialiseer de lege heap Voeg één voor één alle elementen van list aan de heap toe Verwijder één voor één alle elementen uit de heap (waarbij de grootste als eerste komt!!), en vul hiermee list van achter naar voor. Willen we deze acties kunnen uitvoeren onder handhaving van de heap conditie, dan is het nodig om van een willekeurig element zowel de ouder als de kinderen kunnen bepalen. In de bomen die we tot nu toe hebben gehad kon dit niet. We zouden iedere knoop van een boom natuurlijk uit kunnen voeren met een extra verwijzing naar de ouder, maar dit kost geheugen. De eenvoudig oplossing is de boom op te slaan in een array. We weten immers hoe groot de boom maximaal wordt: net zo groot als de te sorteren array (= n elementen)! Maar hoe zit het dan met de claim dat heapsort niet veel geheugen nodig heeft? De truc die we gebruiken is de volgende. We gebruiken het oorspronkelijke array als heap. Dit is zo te organiseren dat er als k elementen in de heap zitten, deze de eerste k elementen in list in beslag nemen, terwijl de overige n k elementen het achterste deel van list in beslag nemen. De manier waarop we ouders en kinderen bijhouden is ook simpel. De ouder van node i is node i/2 (waarbij we integerdeling bedoelen), en de kinderen van node i zijn i 2 en i 2 + 1. Daarbij moeten we wel checken of ze bestaan! dit doen we niet door te checken of de kinderen null zijn, maar door te checken of de index waarde i 2 of i 2 + 1 kleiner is dan het huidig aantal elementen in de heap. Let op: De telling van index waarden begint bij een heap bij 1 niet 0, zoals bij een array. Als 0 de root-node index zou zijn, zou een kind van de root ook index 0 hebben. Dit kan dus niet! De implementatie van heap is de volgende: public class Heap public Comparable list []; public int maxsize, size; public Heap( Comparable thelist [], int length ) list = thelist; maxsize = length; 5

size = 0; public Comparable itematheapindex( int i ) return list[i-1];... Er zijn dus drie data velden: de array list: een verwijzing naar je origineel), maxsize: de maximum grootte, en size: de huidige grootte. De constructor neemt de originele array, en laat list ernaar verwijzen. De maximum grootte wordt gelijk gesteld aan de lengte van list, en size wordt nul, wat aangeeft dat de heap leeg is. De method itematheapindex geeft het element op heap index i terug, wat overeenkomt met array index i-1. In de code zetten we deze omrekening rechtstreeks neer: element child komt overeen met list[child-1]. Het invoegen en verwijderen heeft telkens de volgende structuur: schendt de heap conditie, en repareer die daarna. We beginnen met insertinheap. In deze method wordt eerst gecontroleerd dat de maximum capaciteit niet wordt overschreden, verhoogt dan size, en plaatst het nieuwe element achter aan de heap array. Dit element staat dus in een van de bladeren van de boom. Tenzij het een element met de kleinst mogelijke waarde in het pad via de voorouders naar de root is, staat het niet op zijn plaats, en is de heap conditie geschonden. Dit wordt gerepareerd door upheap. De method ziet er als volgt uit: public void insertinheap( Comparable item) if ( size <= maxsize ) size ++; list[size-1] = item; upheap( size ); Method upheap rekent de ouder van de te verschuiven node child uit, en copieert 6

het betreffende array element naar temp. Er wordt nu iteratief gezocht naar de eerste voorouder die een waarde groter dan die van temp heeft, of dat de root-node bereikt is. Iedere keer dat een voorouder te klein is, wordt hij naar de betreffende kind positie door geschoven. Als de while-lus klaar is, wordt het kind op zijn plek gezet. De method zet er als volgt uit. Merk op dat dit zoekprocess maximaal 2 log n stappen vergt, size=n child. Immers, de parent wordt bij iedere herhaling gedeeld door twee. De lus stopt als parent == 0 in ieder geval, en dus na niet meer dan 2 log n stappen. void upheap( int child ) int parent = child / 2; Comparable temp = list[child-1]; boolean found = false; while ((parent > 0) &&!found) if ( list[parent-1].compareto(temp) < 0 ) list[child - 1] = list[parent - 1]; child = parent; parent = child / 2; else found = true; list[child - 1] = temp; Method removefirst haalt, als de heap niet leeg is, het element list[0] weg (oftewel de root). Het laatste element, op array index size - 1 wordt dan in de root geplaatst, size wordt 1 kleiner gemaakt, en de heap wordt gerepareerd met downheap. Tot slot wordt het oude root element terug gegeven met een return statement. public Comparable removefirst() if (size > 0) Comparable temp = list[0]; list[0] = list[size - 1]; size --; downheap(1); 7

return temp; else return null; Method downheap is het spiegelbeeld van upheap. Van een node met heap index parent wordt van de data een kopie gemaakt. Daarna wordt van de twee kinderen (als ze bestaan) de grootste gezocht. Als de parent kleiner is dan het grootste kind neemt het kind de positie van de parent, zo niet of als er geen kinderen zijn, dan staat parent op de juiste plaats. Net als upheap, heeft downheap een complexiteit van O 2 log n. Dit keer wordt child iedere keer verdubbeld, en na hooguit 2 log n stappen wordt child groter dan size, en stopt de lus. De methode kan als volgt worden geimplementeerd. void downheap ( int parent ) int child; Comparable temp = list[parent-1]; boolean found = false; while ((parent <= size / 2) &&!found) child = parent * 2; if ( child < size ) if ( list[child-1].compareto(list[child]) < 0 ) child ++; if ( temp.compareto(list[child-1]) < 0 ) list[parent - 1] = list[child - 1]; parent = child; else found = true; list[parent - 1] = temp; Heapsort is nu simpel: 8

static void heapsort ( Comparable list[], int length ) Heap heap = new Heap(list,length); int i; // Zorg eerst dat heap aan de heap conditie gaat voldoen for ( i = 1; i<= length; i++ ) heap.insertinheap( list[i-1] ); // heap is nu gebouwd size = length for ( i = length ; i > 1 ; i -- ) list[i-1] = heap.removefirst( ); Na initialisatie van de heap worden de elementen van de array in de heap geplaatst, waarna ze in de tweede for-lus eruit worden gehaald, en de array list van achter naar voren wordt gevuld. 3.5 Partition sort Tot slot van dit hoofdstuk willen we recursie gebruiken om een sorteer probleem op te lossen. We beschouwen een sorteer probleem, waarvan voor het hier te geven algoritme het aantal operaties (onder niet te extreme omstandigheden) evenredig is met n 2 log n als het een sortering van n objecten betreft. Veronderstel het bestaan van een method splitlist van de volgende vorm: private static int splitlist ( Comparable list [], int b, int e ) die de waarden van list[ b..e ] permuteert (verwisselt) en als functieresultaat een 9

waarde s berekent, zodanig dat (1) b s e (2) list[b..s-1] list[s] (3) list[s] list[s+1..e] (3.1) In een plaatje: b V V V s-1 s s+1 e Het (recursieve) sorteer-method is nu voor de hand liggend: public static void quicksort ( Comparable list [], int b, int e ) if ( b < e ) int pivot = splitlist( list, b, e ); quicksort( list, b, pivot - 1 ); quicksort( list, pivot + 1, e ); De eenvoud van een recursieve method wordt veelal als misleidend ervaren, omdat men zich het mechanisme tracht voor te stellen. We stellen echter met nadruk dat de gegeven tekst een getrouwe afspiegeling is van de begripsmatige eenvoud. Moeilijker is de method splitlist. Er zijn vele versies van soortgelijke method in omloop. Het erop gebaseerde sorteer algoritme is in de literatuur bekend onder de naam quicksort. Het algoritme is gebaseerd op de keuze van één van de array-elementen als vergelijkingswaarde, zeg V, die uiteindelijk op indexpositie s zal belanden. Array-elementen V worden links in het array verzameld, array-elementen V worden rechts in het array verzameld. Om er zeker van te zijn dat een permutatie van het oorspronkelijke array ontstaat, en dat er dus geen waarden verdwijnen, zullen we als enige operatie op het array de onderlinge verwisseling van twee waarden toestaan. De index s zullen we opsporen door gebruik te maken van twee wijzers (een linkerwijzer b en een rechterwijzer e) waarbij b van links naar rechts door list zal lopen en e van rechts naar links. We zullen ervoor 10

zorgen dat ten allen tijden alle elementen links van de linkerwijzer voldoen aan V en alle elementen rechts van de rechterwijzer aan V. Indien nu b=e hebben we ons doel bereikt. Alvorens de body van splitlist te geven nog enige analyse. We introduceren grootheden B en E hetgeen nodig is om in de specificatie nog iets over de beginwaarden van b en e te zeggen. We kiezen nu als hoofdinvariant: list[b..b 1] V list[e + 1..E] (3.2) (de permutatie-eis zullen we niet steeds opnieuw noemen). In een plaatje: V?? V B b e E Als we nu ervoor zorgen dat uiteindelijk: b = e V = list[b] (3.3) dan is b = e de gezochte waarde s. We zullen uiteindelijk list[b] = V bewerkstelligen door ervoor te zorgen dat altijd geldt: list[l] = V list[r] = V (3.4) Dit betekent dat we iedere keer als we de lus uitvoeren ervoor moeten zorgen dat de waarde van V in list[b] of in list[e] blijft staan. Als de waarde van V in list[b] staat, dan mogen we e veranderen zonder dat de waarde van list[b] verloren gaat. Hetzelfde geldt als de waarde van V in list[e] staat, dan mogen we b veranderen. In het eerste geval rechtvaardigt dit het naar links schuiven van e en in het tweede geval het naar rechts schuiven van b. We kunnen de body nu afmaken: private static int splitlist ( Comparable list [], int b, int e ) boolean vtoleft = true; // Invariant: list[b..b-1 ] <= V <= list[ e+1..e ] while ( b < e ) if ( list[b].compareto( list[e] ) > 0 ) 11

swapitems( list, b, e ); vtoleft =!vtoleft; if (vtoleft) // V == list[b] e --; else // V == list[e] b ++; return b; 3.6 Mergesort Als laatste sorteeralgoritme bestuderen we mergesort. Technisch zit mergesort iets anders in elkaar, zodat dit algoritme niet goed past in het voorgaande kader. Mergesort werkt niet met het verwisselen van elementen maar met het copieren van lijsten. Daarom heeft mergesort een andere interface dan we tot nog toe gewend waren. public static void mergesort ( Comparable list [], Comparable temp [], int l, int r ) int i,j,k, middle; if ( r-l > 0) middle = (r + l) / 2; mergesort(list, temp, l, middle); mergesort(list, temp, middle+1, r); for (i = l ; i <= middle ; i ++ ) temp[i] = list[i]; for (i = middle + 1 ; i <= r ; i ++ ) temp[r + middle + 1 - i] = list[i]; 12

i = l; j = r; for ( k = l ; k <= r ; k ++ ) if ( temp[i].compareto(temp[j]) < 0 ) list[k] = temp[i]; i ++; else list[k] = temp[j]; j--; Mergesort bewerkt een array. Het algoritme is heel simpel. Deel het te bewerken array in twee ongeveer gelijke delen. Sorteer het eerste en tweede deel met mergesort en voeg deze twee resultaten samen (merge). Deze recursieve methode eindigt als het te sorteren array slechts één element bevat. Het samenvoegen van twee gesorteerde lijsten is heel simpel. Vergelijk de twee eerste elementen en plaats de kleinste in het resulterende array en ga verder met het volgende element. Om een in situ algoritme te schrijven hebben we een hulp array temp nodig. Het is veel natuurlijker om mergesort voor echte lijsten en niet voor lijsten opgeslagen in array s te schrijven. We vragen jullie om tijdens het practicum mergesort voor lijsten te implementeren. 3.7 Iets over efficiëntie Bij vergelijking van een aantal algoritmen met het zelfde einddoel speelt het begrip efficientie een belangrijke rol. Een algoritme is efficiënter, naarmate deze sneller tot resultaat komt en/of daarbij minder geheugenruimte nodig heeft. Bij de meeste besproken algoritmen is het geheugengebruik (ruimte nodig voor hulpvariabelen bijvoorbeeld) ongeveer vergelijkbaar. 2 ) De uitzondering is merge sort, die een hulp array nodig heeft ter grootte n. We zullen ons hier verder beperken tot de snelheid. Een mate voor de snelheid kan (in dit geval) zijn het aantal vergelijkingen en het aantal verwisselingen van array-elementen in het algoritme. Zowel voor insertion sort als voor selection sort is het aantal vergelijkingen evenredig met het kwadraat van het aantal elementen van de lijst list. We spreken dan van O n 2 (orde grootte n 2 ). Dit houdt in, dat vier keer zoveel elementen in een lijst 16 keer zoveel 2 ) Dat is niet helemaal waar. De recursieve aanroepen vergen ook extra geheugenruimte. 13

vergelijkingen tot gevolg zal hebben. Partition sort is echter nog veel beter. In de method splitlist is het aantal vergelijkingen en het aantal verwisselingen O n (alle elementen uit het betreffende interval worden eenmaal beschouwd en zijn in principe kandidaat om verwisseld te worden). Indien elke partitie het interval middendoor deelt, zijn log n opdelingen nodig (dan is elk interval één element groot). In totaal komen we zo voor zowel het aantal verwisselingen als het aantal vergelijkingen in dit gunstige geval uit op O n log n, hetgeen beter is dan selection sort en insertion sort. Een partitie kan ook steeds slecht uitvallen en het interval opdelen in een interval ter grootte van één element en een interval ter grootte alles-min-één. In dat geval loop de efficientie terug tot O n 2. We spreken dan van een worst case-gedrag. Meestal is partition sort dus behoorlijk snel en wordt daarom ook wel quicksort genoemd. Heapsort heeft ook een O n log n complexiteit, omdat upheap en downheap een O log n complexiteit hebben en deze ieder n keer worden aangeroepen. Heapsort is gemiddeld langzamer dan quicksort, maar worst case is heapsort nog steeds O n log n, terwijl quicksort O n 2 kan worden. Ook heeft heapsort minder geheugen nodig. Het verschil komt door de heap structuur, die gegarandeerd een gebalanceerde boom is. Ook mergesort is efficiënt doordat we het array hier iedere keer in twee ongeveer gelijke stukken delen, wat ook neerkomt op een gebalanceerde boomstructuur, als we de recursieve opsplitsing bekijken. De diepte van de recursie is dus beperkt tot 2 log n. Bij iedere aanroep is het de complexiteit in de orde van het aantal elementen in het segment dat gesorteerd moet worden door die aanroep. Alle aanroepen op een bepaalde diepte van de recursie bij elkaar doen dus O n werk, de recursie heeft een maximum diepte van O 2 log n, dus de totale complexiteit is O n log n. De keuze voor één van de laatste drie methoden moet eerder op basis van geheugen gebruik, en toepassing worden gezocht, dan dat er één duidelijk de beste is. Mergesort heeft als voordeel dat ook files, of linked lists ermee gesorteerd kunnen worden. Sorteren van data in files is nodig als de data niet in het geheugen passen. Heapsort is een goede alrounder: niet zo snel als quicksort, maar zuiniger met geheugen, en gegarandeerd O n log n. 14