Datastrukturer i Haskell

Relevanta dokument
Föreläsning Datastrukturer (DAT036)

Ett generellt träd är. Antingen det tomma trädet, eller en rekursiv struktur: rot /. \ /... \ t1... tn

Föreläsning 9 Datastrukturer (DAT037)

Självbalanserande träd AVL-träd. Koffman & Wolfgang kapitel 9, avsnitt 1 2

Abstrakta datatyper. Primitiva vektorer. Deklarera en vektor

TDDC30. Objektorienterad programmering i Java, datastrukturer och algoritmer. Föreläsning 9 Jonas Lindgren, Institutionen för Datavetenskap, LiU

Tentamen Datastrukturer (DAT036)

Föreläsning 4 Datastrukturer (DAT037)

Självbalanserande AVL-träd Weiss, avsnitt 4.4

Föreläsning 5 TDDC91,TDDE22,725G97: DALG. Föreläsning i Datastrukturer och algoritmer 18 september 2018

Föreläsning 9 Innehåll

Datastrukturer. föreläsning 10. Maps 1

Föreläsning 4 Datastrukturer (DAT037)

Lösningsförslag till tentamen Datastrukturer, DAT037 (DAT036), Tiden det tar att utföra en iteration av loopen är oberoende av värdet på

ADT Kö. Seminarium 4 Köer och Stackar Innehåll. Operationer. ADT Stack. Definition. Definition

Träd Hierarkiska strukturer

Föreläsning Datastrukturer (DAT036)

Lösningar Datastrukturer TDA

BST implementering, huvudstruktur

Föreläsning 10 Innehåll. Prioritetsköer och heapar. ADT Prioritetskö. Interface för Prioritetskö. Exempel på vad du ska kunna

Föreläsning 7. Träd och binära sökträd

Seminarium 13 Innehåll

DAI2 (TIDAL) + I2 (TKIEK)

Tentamen TEN1 HI

ADT Prioritetskö. Föreläsning 12 Innehåll. Prioritetskö. Interface för Prioritetskö. Prioritetsköer och heapar

Tentamen Datastrukturer (DAT036/DAT037/DIT960)

ADT Prioritetskö. Föreläsning 13 Innehåll. Prioritetskö vs FIFO-kö. Prioritetskö Exempel på användning. Prioritetsköer och heapar

Tentamen Datastrukturer (DAT036)

Tentamen Datastrukturer, DAT037 (DAT036)

Föreläsning 3 Datastrukturer (DAT037)

TDDC30. Objektorienterad programmering i Java, datastrukturer och algoritmer. Föreläsning 8 Erik Nilsson, Institutionen för Datavetenskap, LiU

Binära sökträd. Seminarium 9 Binära sökträd Innehåll. Traversering av binära sökträd. Binära sökträd Definition. Exempel på vad du ska kunna

Föreläsning 6. Sökträd: AVL-träd, Multi-Way -sökträd, B-träd TDDC70/91: DALG. Innehåll. Innehåll. 1 AVL-träd

Tentamen i Algoritmer & Datastrukturer i Java

Tentamen, Algoritmer och datastrukturer

Tentamen Datastrukturer D DAT 035/INN960 (med mycket kortfattade lösningsförslag)

Fredag 10 juni 2016 kl 8 12

Tentamen Datastrukturer D DAT 035/INN960

Datastrukturer, algoritmer och programkonstruktion (DVA104, HT 2014) Föreläsning 5

Upplägg. Binära träd. Träd. Binära träd. Binära träd. Antal löv på ett träd. Binära träd (9) Binära sökträd (10.1)

Föreläsning 10 Innehåll. Diskutera. Inordertraversering av binära sökträd. Binära sökträd Definition

Datastrukturer. föreläsning 9. Maps 1

Tentamen Datastrukturer D DAT 036/INN960

Föreläsning 4. Kö Implementerad med array Implementerad med länkad lista Djup kontra bredd Bredden först mha kö

Föreläsning 10 Innehåll

Tentamen Datastrukturer D DAT 036/INN960

Algoritmer och datastrukturer 2012, fo rela sning 8

TDDC74 Programmering: Abstraktion och modellering Datortenta , kl 14-18

Datastrukturer. föreläsning 3. Stacks 1

Programmering i C++ EDA623 Dynamiska datastrukturer. EDA623 (Föreläsning 11) HT / 31

Föreläsning 2. AVL-träd, Multi-Way -sökträd, B-träd TDDD71: DALG. Innehåll. Innehåll. 1 Binära sökträd

TDDC30. Objektorienterad programmering i Java, datastrukturer och algoritmer. Föreläsning 3 Jonas Lindgren, Institutionen för Datavetenskap, LiU

TDDI16 Datastrukturer och algoritmer. Prioritetsköer, heapar, Union/Find

Föreläsning 11 Innehåll. Diskutera. Binära sökträd Definition. Inordertraversering av binära sökträd

Rekursiva algoritmer sortering sökning mönstermatchning

F12 - Collections. ID1004 Objektorienterad programmering Fredrik Kilander

Föreläsning Datastrukturer (DAT036)

Linjärt minne. Sammanhängande minne är ej flexibelt. Effektivt

Anmälningskod: Lägg uppgifterna i ordning. Skriv uppgiftsnummer (gäller B-delen) och din kod överst i högra hörnet på alla papper

Föreläsning 9 Innehåll

Lösningsförslag till tentamen Datastrukturer, DAT037,

Tentamen Datastrukturer D DAT 035/INN960

BINÄRA TRÄD. (X = pekarvärdet NULL): struct int_bt_node *pivot, *ny; X X X 12 X X 12 X X -3 X X

Programmering i C++ EDAF30 Dynamiska datastrukturer. EDAF30 (Föreläsning 11) HT / 34

Kungliga Tekniska Högskolan Ämneskod 2D1370 Tentamensdag 2001-maj-31 Tentamen i Funktionell Programmering Skrivtid 4 h

Datastrukturer i kursen. Föreläsning 8 Innehåll. Träd rekursiv definition. Träd

Föreläsning Datastrukturer (DAT036)

TDDC30/725G63. Objektorienterad programmering i Java, datastrukturer och algoritmer

Namn: (Ifylles av student) Personnummer: (Ifylles av student) Tentamensdatum: Tid: Hjälpmedel: Inga hjälpmedel

Informationsteknologi Tom Smedsaas 19 augusti 2016

Länkade strukturer, parametriserade typer och undantag

Datastrukturer och algoritmer. Föreläsning 4 Test, Stack och Kö

Vad har vi pratat om i kursen?

Föreläsning 5. Träd Binära träd Binärt sökträd som ADT Implementering av binärt sökträd Travestera binärt sökträd Sökning Insättning/borttagning

Föreläsning 3 Datastrukturer (DAT037)

Föreläsning 7. Träd och binära sökträd

Hitta k största bland n element. Föreläsning 13 Innehåll. Histogramproblemet

Tentamen Datastrukturer, DAT037 (DAT036)

Tentamen i Algoritmer & Datastrukturer i Java

Länkade strukturer. (del 2)

Algoritmer och datastrukturer

Inlämningsuppgiften. Föreläsning 9 Innehåll. Träd. Datastrukturer i kursen

TENTAMEN: Algoritmer och datastrukturer. Läs detta! Uppgifterna är inte avsiktligt ordnade efter svårighetsgrad.

Föreläsning 13 Datastrukturer (DAT037)

Tentamen, EDA690 Algoritmer och Datastrukturer, Helsingborg

Programkonstruktion och. Datastrukturer

Programmering II (ID1019) :00-12:00

TDDC30 Programmering i Java, Datastrukturer och Algoritmer Lektion 2. Laboration 2 Datastrukturer En liten uppgift Frågor

TDDC74 Programmering: Abstraktion och modellering Tentamen, onsdag 9 juni 2016, kl 14 18

Tentamen TEN1 HI

TENTAMEN: Algoritmer och datastrukturer. Läs detta!

TDDC30. Objektorienterad programmering i Java, datastrukturer och algoritmer. Föreläsning 3 Jonas Lindgren, Institutionen för Datavetenskap, LiU

if (n==null) { return null; } else { return new Node(n.data, copy(n.next));

Föreläsning 10 Datastrukturer (DAT037)

Lösningsförslag för tentamen i Datastrukturer (DAT037) från

TDDC74 Programmering: Abstraktion och modellering Tentamen, lördag 27 augusti 2016, kl 8 12

Lösningsförslag till tentamen Datastrukturer, DAT037,

Datastrukturer. föreläsning 9. Maps 1

Föreläsning 13 Innehåll

TDDC30 Programmering i Java, Datastrukturer och Algoritmer Lektion 2. Länkade listor Stackar Köer MyList Iteratorer Lab 2 Exceptions Paket

Transkript:

Datastrukturer i Haskell Bror Bjerner Inst. för data- och informationsteknik Göteborgs universitet & Chalmers tekniska högskola 2010 1 Fibbonacci Om vi beräknar det n:te fibonnacitalet enligt sin dubbelrekursiva definition, så får vi en komplexitet av 0( fib(n) ), dvs exponentiellt. I det iterativa fallet löser vi det genom att göra en loop i stället. För det rekursiva fallet löser vi det genom att göra en s.k. värdeförloppsrekursion, course-of-value recursion. fib :: Int -> Int fib 0 = 0 fib 1 = 1 fib n = covfib n 0 1 where covfib 1 prev curr = curr covfib n prev curr = covfib (n-1) curr (prev+curr) // bättre att ha i istället för n i covfib-definitionen // bör förklara att covfib är Haskellversionen av en iterativ loop Denna funktion är uppenbarligen av 0( n ), precis som den iterativa lösningen. 2 Sortering Då det gäller sortering i listor i stället för fält, så har vi ingen direktaccess till olika positioner i listan, vilket vi ju har för fält. Algoritmer, som bygger på 1

swap-operationen, är därför olämpliga, att använda i funktionell programmering. Snabbsortering och urvalssortering bygger på swap-metoden och är därför olämpliga i funktionell programmering. Insättnings- och sammansmältningssortering passar däremot alldeles förträffligt. 2.1 Insättningssortering Eftersom det är svårt att backa i en lista så stoppar vi i stället in varje element i den sorterade svansen. insertionsort :: Ord a => [a] -> [a] insertionsort [] = [] insertionsort (a:as) = insert a (insertionsort as) where insert a [] = [a] insert a bbs@(b:bs) a > b = b : insert a bs otherwise = a:bbs I värsta fallet, omvänt sorterat, blir rekursionsdjupet för insert antalet element i den givna listan. Totalt får vi då n 1 i=0 i anrop, dvs komplexiteten blir av 0( n 2 ). I bästa fallet, redan sorterat, blir rekursionsdjupet för insert endast 1, dvs komplexiteten blir av 0( n ) I genomsnitt blir rekursionsdjupet för insert halva antalet element i den givna listan, varför även här komplexiteten blir av 0( n 2 ) 2.2 Sammansmältningssortering Låt oss först titta på sammansmältningen av två redan sorterade dellistor. merge :: Ord a => [a] -> [a] -> [a] merge [] bs = bs merge as [] = as merge aas@(a:as) bbs@(b:bs) a < b = a : merge as bbs otherwise = b : merge aas bs 2

För att nu sortera en godtycklig lista gör vi först varje element till en lista endast innehållande detta element. Därefter sammansmältes dessa parvis, via en generell rekursion, tills endast en lista återstår. mergesort :: Ord a => [a] -> [a] mergesort [] = [] mergesort as@[a] = as mergesort as = untilonelist [ [a] a <- as ] where untilonelist [as] = as untilonelist ass = untilonelist (mergepairwise ass) mergepairwise [] = [] mergepairwise ass@[_] = ass mergepairwise (a:b:s) = merge a b : mergepairwise s Eftersom listan av listor reduceras till hälften i varje varv och de är från början är n stycken blir det totalt 2 log n varv. Antalet element som gås igenom i varje varv är åtminstone n 2. Komplexiteten blir alltså 0( n 2 log n ) för mergesort. 3

3 Stackar För att göra en stack definierar vi en modul med föjande signatur: module Stack ( Stack, -- type of stacks emptystack, -- Stack a isempty, -- Stack a -> Bool push, -- a -> Stack a -> Stack a pop, -- Stack a -> Stack a top -- Stack a -> a ) where Implementeringen blir enkel, eftersom en lista i Haskell uppför sig precis som en stack. Vi väljer därför helt enkelt en lista som implementering och översätter stackfunktionerna med motsvarande listfunktioner. newtype Stack a = Stack [a] deriving ( Eq, Show, Read ) emptystack = Stack [] isempty (Stack []) = True isempty _ = False push x (Stack as) = Stack (x:as) pop (Stack []) = error "empty stack" pop (Stack (x:as)) = Stack as top (Stack []) = error "empty stack" top (Stack (x:as)) = x Notera att vi här använder oss av error för att fånga begreppet exception i Java. 4

4 Köer Köer är lite besvärligare att implementera om man vill beakta komplexiteten. Om kön skulle implementeras som en lista kostar det ju O( n ) att stoppa in ett element sist i kön. Däremot kan vi implementera en kö med hjälp av två listor, där vi stoppar in elementen i den ena listan och plockar ut ur den andra. Detta ger en amorterad komplexitet av O( 1 ). Varje element läggs först i bakre kön och ingår sedan i en och bara en reverse-operation. module Queue ( Queue, -- type of queues emptyqueue, -- Queue a isempty, -- Queue a -> Bool enqueue, -- a -> Queue a -> Queue a dequeue, -- Queue a -> Queue a front, -- Queue a -> a ) where data Queue a = Queue [a] [a] deriving ( Eq, Show, Read ) emptyqueue = Queue [] [] isempty (Queue [] []) = True isempty _ = False enqueue x (Queue as bs) = Queue as (x:bs) dequeue (Queue [] []) = error "empty queue" dequeue (Queue [] bs) = dequeue (Queue (reverse bs) []) dequeue (Queue (x:as) bs) = Queue as bs front (Queue [] []) = error "empty queue" front (Queue (x:as) bs) = x 5

5 Binära träd Vi skall här endast betrakta binära sökträd. Vi kommer att definiera två former: det enkla binära sökträdet och det höjdbalanserade AVL-trädet. 5.1 Enkla binära sökträd Vi definierar följande modul med sin signatur. module BinarySearchTree ( BST, -- type of binary search trees emptytree, -- BST a isempty, -- BST a -> Bool leftsub, -- BST a -> BST a rightsub, -- BST a -> BST a rootval, -- BST a -> a insert, -- Ord a => a -> BST a -> BST a remove, -- Ord a => a -> BST a -> BST a inorder, -- BST a -> [a] get -- Ord a => a -> BST a -> Maybe a ) where Om vi gör en jämförelse med Java kan vi notera: att typen ej behöver ha samma namn som modulen och dess konstruktorer, vilket krävs i Java för en klass. Att de typer som förkommer i signaturen fungerar som public i Java och att övriga definitioner fungerar som private i Java, dvs endast internt i modulen. Då det gäller träd finns ingen konkret datastruktur att bygga på, varför vi definierar en ny rekursiv datatyp. Ett träd är antingen det tomma trädet eller så är det en nod bestående av ett rotvärde, ett vänster delträd och ett höger delträd. data BST a = Empty Node a (BST a) (BST a) deriving ( Eq, Show, Read ) Notera att vi här inte har angett Empty och Node i signaturen, varför vi behöver funktioner för att skapa ett nytt tomt träd, samt att plocka sönder ett träd i dess beståndsdelar. 6

emptytree = Empty isempty Empty = True isempty _ = False leftsub Empty = error "no left subtree" leftsub (Node _ l _) = l rightsub Empty = error "no right subtree" rightsub (Node r) = r rootval Empty = error "no root value" rootval (Node a ) = a Orsaken till att vi gömmer konstruktorerna för typen BST, är att vi vill, att all modifiering av trädet skall gå via funktionerna insert och remove, så att egenskapen att vara ett binärt sökträd alltid bibehålls. För insert gör vi som i Java. Vi letar upp första lediga hål, placerar det nya elementet där och låter övriga delar av trädet vara som före insättningen. insert a Empty = Node a Empty Empty insert a (Node b l r) a < b = Node b (insert a l) r otherwise = Node b l (insert a r) Då det gäller remove, så gör vi samma knep som i det imperativa fallet. Om den nod som skall förstöras har två icke-tomma subträd, hämtar vi upp det största elementet i vänster subträd, samt plockar ut det ur det vänstra subträdet. remove a Empty = Empty remove a (Node b l r) a < b = Node b (remove a l) r a > b = Node b l (remove a r) isempty l = r isempty r = l otherwise = Node maxl (remove maxl l) r where maxl = findmax l findmax (Node a _ Empty) = a findmax (Node a _ r) = findmax r 7

Notera att findmax ej är känd utanför modulen, varför vi inte behöver definiera den för det tomma trädet. För att traversera elementen i ett träd, så genererar vi en lista där vi ger elementen i inorder, dvs en sorterad lista. inorder t = inord t [] where inord Empty as = as inord (Node a l r) bs = inord l ( a : inord r bs ) Här använder vi oss av en högre ordnigens funktion inord som pressar fram en delberäkning av svansen. Vi kunde använt oss av konkatenering enligt: inorder (Node a l r) = inorder l ++ a : inorder r Motiveringen till att välja funktionen inord, är att med den senare definitionen, får vi konkateneringar på varje rekursionsnivå, dvs en komplexitet av minst 0( n 2 log n ). Med den högre ordningens funktion får vi däremot komplexiteten 0( n ) vilket givetvis är att fördra. Slutligen har vi en funktion, som ur ett givet träd, hämtar ett element, som är lika stort som det givna argumentet. Eftersom det inte är säkert att det finns något sådant element använder vi oss av typen Maybe, jämför med att ge null som resultat i Java. Funktionen kan vara bra att ha, om man till exempel vill göra något som liknar en Map i Java. get a Empty = Nothing get a (Node a l r) a < a = get a l a > a = get a r otherwise = Just a 8

5.2 AVL träd Givetvis kan vi använda oss av balanseringar i Haskell. Vi skall här använda höjdbalanserade träd enligt AVL-algoritmerna. Modulens signatur, förutom själva typen AVL blir den samma som för BinSearchTree. module AVLTree ( AVL, -- type of AVL balanced search trees emptytree, -- AVL a isempty, -- AVL a -> Bool leftsub, -- AVL a -> AVL a rightsub, -- AVL a -> AVL a rootval, -- AVL a -> a insert, -- Ord a => a -> AVL a -> AVL a remove, -- Ord a => a -> AVL a -> AVL a inorder, -- AVL a -> [a] get -- Ord a => a -> AVL a -> Maybe a ) where För att inte behöva beräkna höjderna ideligen så introduceras, precis som i det imperativa fallet, en del av noden där höjden för trädet finns beräknat. Vi får den nya datatypen: data AVL a = Empty Node a Int (AVL a) (AVL a) deriving ( Eq, Show, Read ) Funktionerna emptytree, isempty, leftsub, rightsub, rootval, get, inorder och findmax blir desamma förutom vid mönstermatchningen av en Node där det extra integerfältet måste läggas till. Det görs med hjälp av ett wild card, eftersom höjdinformationen ej behövs för dessa funktioner. Däremot behöver vi en ny intern funktion som ger höjden för ett träd eller subträd. height Empty = 0 height (Node _ h ) = h Vid insättning eller urtagning av element i trädet kan det däremot uppstå obalans, varför denna information dels behövs för att kontrollera balansen och dels behövs för uppdatering. Vid obalans finns det fyra möjliga balanseringar att tillämpa. Vi definierar dem som egna funktioner eftersom de behövs både vid insättning och vid urtagning. Enkel rotation åt vänster eller åt höger, samt dubbel rotation åt vänster respektive höger. Vi redovisar här endast vänsterrotationerna: 9

rotateleft (Node a ah al (Node b bh bl br)) = Node b (1 + max newh (height br)) (Node a newh al bl) br where newh = 1 + max (height al) (height bl) doublerotateleft(node a ah al (Node b bh (Node c ch cl cr) br)) = Node c (1 + max newh1 newh2) (Node a newh1 al cl) (Node b newh2 cr br) where newh1 = (1+max(height al)(height cl)) newh2 = (1+max(height cr)(height br)) På varje nivå behövs en kontroll av höjderna tillsammans med en kontroll av obalans. Om obalans kan ha uppstått genom att vänster delträd kan ha blivit högre anropas checkleftheavy annars anropas checkrightheavy checkleftheavy at@(node a ah bt@(node b bh bl br) ar) bh - arh < 2 = Node a ( 1 + max bh arh ) bt ar height bl < height br = doublerotateright at otherwise = rotateright at where arh = height ar Vi är nu färdiga att definiera insättning och borttagning av element i trädet: insert x Empty = Node x 1 Empty Empty insert x (Node a ah al ar) x < a = checkleftheavy (Node a ah (insert x al) ar) otherwise = checkrightheavy (Node a ah al (insert x ar)) remove x Empty = Empty remove x (Node a ah al ar) x < a = checkrightheavy (Node a ah (remove x al) ar) x > a = checkleftheavy (Node a ah al (remove x ar)) isempty al = ar isempty ar = al otherwise = checkrightheavy (Node maxl ah (remove maxl al) ar) where maxl = findmax al 10

5.3 Bredden först för binära sökträd Vi skall betrakta ett litet exempel på att använda de moduler som vi definierat. Exemplet är att traversera ett binärt sökträd enligt bredden först. För att enkelt hålla reda på ordningen för elementen, använder vi som vanligt vid bredden först en kö. import Queue import BinarySearchTree breadthfirst :: BST a -> [a] breadthfirst t = bf t emptyqueue where bf t que BinarySearchTree.isEmpty t = checkque que otherwise = (rootval t : checkque (enqueue (rightsub t) (enqueue (leftsub t) que))) checkque que Queue.isEmpty que = [] otherwise = bf (front que) (dequeue que) Om vi vill använda oss av AVL-träd så byter vi ut alla BinSearchTree mot AVLTree. Detta är inga problem då vi använder Hugs, eftersom Hugs är textbaserat. Om vi däremot vill använda oss av färdigkompilerade delar i Haskell får vi i stället göra en class där vi definierar vad för funktioner och dess typer som utgör ett binärt sökträd. Ungefär som att göra ett typat interface i Java. Detta ingår dock ej i denna kurs. 11

6 Prioritetsköer Att implementera en heap, ser först ut att vara svårt i Haskell. En heap är ju nivåbalanserad, vilket det är svårt att ge en rekursiv definition för. Vi kan emellertid ge en något modifierad definition, där trädet är nodbalanserat: Ett heapträd är antingen det tomma trädet, eller så är rotelementet det minsta elementet i hela trädet, antalet noder i högra delträdet minus antalet noder i det vänstra delträdet är 0 eller 1 och höger och vänster delträd i sin tur är heapträd. Vi börjar med att ge en signatur för prioritetskön: module PriorityQueue ( PriQue, -- type of priority queues emptyque, -- PriQue a isempty, -- PriQue a -> Bool add, -- Ord a => a -> PriQue a -> PriQue a getmin, -- Ord a => PriQue a -> a removemin -- Ord a => PriQue a -> PriQue a ) where Typen är alltså ett träd för vilket vi definierar konstruerarna: data PriQue a = Empty Node a (PriQue a) (PriQue a) deriving ( Eq, Show, Read ) emptyque ger den tomma kön och isempty kollar om det är en tom kö: emptyque = Empty isempty Empty = True isempty _ = False Att bibehålla heapträdegenskapen vid addering av element till en kö, löser vi genom att stoppa in ett element i det vänstra delträdet och sedan byta vänster och höger delträd med varandra. Det element vi stoppar in i det vänstra delträdet, är det största av rotvärdet och det nya elementet. Det minsta blir nytt rotvärde. add a Empty = Node a Empty Empty add a (Node b l r) a < b = Node a r (add b l) otherwise = Node b r (add a l) 12

Att hämta det minsta elementet, innebär nu bara, att returnera rotvärdet för hela heapträdet. getmin Empty = error "empty priority queue" getmin (Node a ) = a removemin är lite besvärligare. Vi börjar med att ta bort och hämta det högraste elementet i trädet samtidigt som vi vänder tillbaka alla högra delträd på vägen ner i trädet, för att bibehålla kravet på antal noder i delträden. getmostright (Node a Empty Empty) = (a,empty) getmostright (Node a l r) = (b, Node a bt l) where (b,bt) = getmostright r Det bortagna elementet måste sjunka ned i trädet till en plats där det uppfyller kravet att vara det minsta värdet i subträdet. restore a Empty Empty = Node a Empty Empty restore a Empty bt@(node b Empty Empty) a < b = Node a Empty bt otherwise = Node b Empty (Node a Empty Empty) restore a bt@(node b bl br) ct@(node c cl cr) a < b && a < c = Node a bt ct b < c = Node b (restore a bl br) ct otherwise = Node c bt (restore a cl cr) Nu definieras removemin enkelt med de två hjälpfunktionerna. removemin Empty = error "empty priority queue" removemin (Node _ l r ) = restore b bt l where (b,bt) = getmostright r 13