Heapsort Lars Vidar Magnusson 24.1.2014 Kapittel 6 Heaps Heapsort Prioritetskøer
Sorterings Problemet Sorterings problemet er et av de mest fundementalske problemene innen informatikken. Vi sorterer typisk et record i henhold til dets nøkkel i.e. nøkkel + satellittdata = record, men for klarhetens skyld så holder vi oss i dette kurset til å sortere tall. Sortering blir ofte brukt som en subrutine i en mer kompleks rutine.
Sorteringsalgoritmer Det finnes en rekke sorteringsalgoritmer som alle har sine styrker og svakheter. Vi har allerede sett på Insertion-Sort og Merge-Sort med gjennomsnitts kjøretid på henholdsvis Θ(n 2 ) og Θ(n log n). Insertion-Sort sorterer elementene in-place, mens Merge algoritmen i Merge-Sort kopierer elmenetene over i en ny array.
Sorteringsalgoritmer Algoritme Verstefall kjøretid Gjennomsnitt kjøretid Insertion sort Θ(n 2 ) Θ(n 2 ) Merge sort Θ(n log n) Θ(n log n) Heapsort O(n log n) Quicksort Θ(n 2 ) Θ(n log n) (forventet) Counting sort Θ(n + k) Θ(n + k) Radix sort Θ(d(n + k)) Θ(d(n + k)) Bucket sort Θ(n 2 ) Θ(n) (gjennomsnitt)
Heapsort Heapsort benytter seg av en heap for å sortere n elementer. Algoritmen tilhører i verstefall input O(n log n) Algoritmen sorterer in-place Algoritmen kombinerer det beste fra Insertion-Sort og Merge-Sort.
Heaps Heaps er en datastruktur som kan beskrives som en nesten komplett binært tre. NB! Begrepet benyttes ofte også som en betegnelse på et minneområde uten garbage collection. Høyden til en node er den lengste enkle stien fra en node til en bladnode Høyden til en heap = høyden til rotnoden = Θ(log n)
Heaps - Lagring Selv om heaps konseptuelt er et nesten komplett binært tre er det vanlig å lagre de som vanlige arrays. Hvis array A brukes til å representere treet så vil roten til treet lagres i A[1], og vi får følgende navigeringsmetoder. Parent(i) 1 return i/2 Left(i) 1 return 2i Right(i) 1 return 2i+1
Heaps - Lagring Hvordan heaps er lagret i en array kan sees i diagrammet under.
Heap Property En heap må til enhver tid oppfylle et krav (heap property) For max-heaps må alle noder bortsett fra rotnoden oppfylle A[Parent(i)] A[i]. For min-heaps må alle noder bortsett fra rotnoden oppfylle A[Parent(i)] A[i]. En max-heap har til enhver tid største element i rotnoden En min-heap har til enhver tid minste element i rotnoden
Max-Heapify Algoritmen Max-Heapify benyttes for å ivareta max-heap proprty. Før kjøring av Max-Heapify er A[i] enten større eller mindre enn barna. Vi antar at del-trærne i de to barna er max-heaps Etter kjøring er del-treet med rot i i en max-heap
Hvordan Max-Heapify Virker Max-Heapify virker på følgende måte. Sammenlign A[i] med både A[Left(i)] og A[Right(i)]. Hvis nødvendig byttes verdien i A[i] med den største av de to barna Fortsett rekursivt til vi kommer til en bladnode.
Max-Heapify - Kjøretid Kjøretiden til Max-Heapify avhenger av høyden til treet (Θ(log n). Da kan vi naturlig nok også si at kjøretiden til Max-Heapify er O(log n) Alternativt kan vi utnytte oss av det faktum hvis det er n noder i treet med rot i i så er det på det meste 2n/3 noder i hvert av barna. T (n) = T (2n/3) + Θ(1) Masterteoremet gir oss da en kjøretid på O(log n) (som verifiserer intuisjonen)
Build-Max-Heap Det kan bevises at i en heap representert av en array A så er nodene A[n/2 + 1... n] bladnoder. Dette utnyttes i algoritmen Build-Max-Heap som brukes til å bygge en heap fra en usortert array.
Kjøring av Build-Max-Heap
Korrektheten til Build-Max-Heap Vi kan verifisere korrektheten til Build-Max-Heap ved å benytte loop invariant analyse. Loop invariant: Ved starten av alle iterasjoner så er alle noder i + 1, i + 2..., n roten til en max-heap Initialization: Dette følger naturlig av at A[n/2 + 1... n] er bladnoder. Maintenance: Barna til node i har høyere indeks enn i, så utifra vet vi at de begge er røtter i en max-heap. Max-Heapify sørger for at i blir en max-heap. Etter å ha minket i er loop invarianten korrekt. Termination: Når i = 0 så er node 1 roten i hele max-heap
Analyse av Kjøretiden til Build-Max-Heap Vi kan finne en enkel grense. Løkken kjører O(n) ganger Hvert kall til Max-Heapify har kjøretid på O(log n) Total øvre grense O(n log n) Det er mulig å gjøre en strammere analyse ved å utnytte at hvis høyden h til en node er log n, så kan vi si at kjøretiden til Max-Heapify er lineær i henhold til høyden til noden den kjører på.
Stram Analyse av Kjøretiden til Build-Max-Heap Flesteparten av nodene i en heap har lav høyde. Kjøretiden til Max-Heapify på en en node med høyde h er O(h) Vi har n/2 h+1 noder med høyde h Høyden til heapen er log n log n h=0 n O(h) = O 2 h+1 < O n ( n ( = O n = O(n) log n h=0 h=0 Kjøretiden til Build-Max-Heap er O(n). h 2 h ) h 2 h ) 1/2 (1 1/2) 2 )
Heapsort Heapsort fungerer på følgende måte. Bygger en max-heap utifra en usortert array med elementer med Build-Max-Heap Tar det største elementet fra roten og setter det på korrekt plass ved å bytte det med siste elementet. Fjern det siste elementet fra heapen og sørg for at det nye elementet i roten ligger på korrekt plass med Max-Heapify Gjenta til det bare er et element igjen i heapen
Kjøring av Heapsort
Analyse av Heapsort Det er enkelt å finne en øvre asymptotisk grense for Heapsort Build-Max-Heap er O(n) En for-løkke n 1 Bytte elementer er O(1) Max-Heapify er O(log n) Total kjøretid for Heapsort blir da O(n log n). Heapsort er en effektiv algoritme, men en velimplementert Quicksort er typisk bedre i praksis.
Prioritetskøer En prioritetskø er en effektiv datastruktur som er skreddersydd for å finne det høyest prioriterte elementet i et et sett. En prioritetskø blir ofte implementert med heaps da de gir et godt kompromiss mellom kjøretiden for innsetting og uthenting (O log n). Høyest prioritet i en max-heap er største element Høyest prioritet i en min-heap er minste element
Prioritetskø Operasjoner Hvis vi holder oss til max-prioritetskøer har vi følgende operasjoner Max-Heap-Insert(S, x): sett inn element x i settet S Heap-Maximum(S): returnerer det største elementet Heap-Extract-Max(S): Fjerner og returnerer det største elementet Heap-Increase-Key(S, x, k): Øker verdien til element x til k En min-prioritetskø har de samme operasjonene, bare speilet.
Heap-Maximum Vi ser først på Heap-Maximum. Kjøretiden er naturlig nok konstant.
Heap-Extract-Max Under er pseudekoden for Heap-Extract-Max. Kjøretiden er O(log n).
Heap-Increase-Key Under er pseudekoden for Heap-Increase-Key. Kjøretiden er O(log n).
Max-Heap-Insert Under er pseudekoden for Max-Heap-Insert. Kjøretiden er O(log n).