Övningsuppgifter #11, Programkonstruktion och datastrukturer Lösningsförslag Elias Castegren elias.castegren@it.uu.se Övningar 1. 1
2. 2
3. Ett binomialträd med rang n har 2 n noder. En binomial heap innehåller alltså t 0 2 0 + t 1 2 1 + t 2 2 2 + + t m 2 m noder, där t i är ett eller noll beroende på om heapen har ett träd med rang i eller inte. Ett binärt tal d m d m 1 d m 2... d 0 blir decimalt d 0 2 0 + d 1 2 1 + d 2 2 2 + + d m 2 m där d i är ett eller noll. Antalet noder i ett binomialträd är alltså detsamma som ett binärt tal enligt ovan där en etta betyder att heapen har ett träd med motsvarande rang. i) (52) 10 = (110100) 2 - rangerna hos träden i heapen är alltså 2, 4 och 5 (index från höger för ettorna i det binära talet) ii) (53) 10 = (110101) 2 - rangerna är 0, 2, 4 och 5 iii) (127) 10 = (1111111) 2 - heapen innehåller träd av alla ranger mellan 0 och 7 iv) (128) 10 = (10000000) 2 - heaper innehåller ett träd av rang 8 När antalet noder är 2 k 1 måste man slå ihop k stycken träd om man lägger in ett nytt element (se övergången från 127 till 128 noder ovan). Det tar alltså längre tid att lägga in nya element när man har många ettor i följd i den binära representationen av antalet noder. 4. heapsort(l) : int list int list i) Skapa en tom max-heap H ii) Sätt in alla element i l i H, ett och ett iii) Ta ut det största elementet ur H och lägg det först i en lista l iv) Är H tom? Gå tillbaka till steg 3, annars returnera l Antag att listan som ska sorteras har l element. Vi gör först l stycken insättningar och sen l stycken extraheringar (båda operationerna med komplexitet Θ(log n), där n är antalet noder i heapen). Den totala komplexiteten blir alltså l Θ(log l ) + l Θ(log l ) = Θ( l log l ) Med andra ord är den jämförbar med mergesort och quicksort! 3
En ML-implementation med en min-heap följer nedan. Heapimplementationen kommer från föreläsningarna och finns att hämta från kurshemsidan. (* sort(l) TYPE: int list -> int list PRE: () POST: l sorterad EXAMPLES: sort([5,3,4,2,1]) = [1,2,3,4,5] *) fun sort(l) = let (* extractall(h) TYPE: binoheap -> int list PRE: h uppfyller invarianten för binomiala min-heapar POST: en sorterad lista med alla element i h EXAMPLES: sort({en heap med elementen <3,4,5>}) = [3,4,5] *) (* VARIANT: Antalet element i heapen h *) fun extractall(h) = if null(h) then [] else let val (e, h ) = extractmin(h) in e::extractall(h ) end val H = foldr (fn(e, h) => insert(h, e)) [] l in extractall(h) end; Tänk på att heapen är definierad som: type binoheap = binotree list En tom heap är alltså detsamma som en tom lista (se foldr-anropet). Därför kan man också använda listfunktioner som null för att se om en heap är tom. 4
5. Hashvärdena för våra element: k h(k) = k mod 10 113 3 19 9 155 5 16 6 129 9 43 3 99 9 26 6 125 5 73 3 Sätt in 113 113 Sätt in 19 113 19 Sätt in 155 113 155 19 Sätt in 16 113 155 16 19 Sätt in 129 113 155 16 129 19 Sätt in 43 43 155 16 129 113 19 Sätt in 99 43 155 16 99 113 129 19 5
Sätt in 26 43 155 26 99 113 16 129 19 Sätt in 125 43 125 26 99 113 155 16 129 19 Sätt in 73 73 125 26 99 43 155 16 129 113 19 Borttagning av element går till på samma sätt som ovan. Man kan sluta leta efter element som inte finns när man når slutet av kedjan på motsvarande plats. 6. Linjär probing h(k, i) = (k + f(i)) mod 10 f(i) = i Sätt in 113 113 Sätt in 19 113 19 Sätt in 155 113 155 19 Sätt in 16 113 155 16 19 6
Sätt in 129 113 155 16 19 129 113 155 16 19 Sätt in 43 129 113 155 16 19 Sätt in 99 i = 2 129 99 113 43 155 16 19 Sätt in 26 129 99 113 43 155 16 19 129 99 113 43 155 16 26 19 Sätt in 125 129 99 113 43 155 16 26 19 129 99 113 43 155 16 26 19 i = 2 129 99 113 43 155 16 26 19 i = 3 129 99 113 43 155 16 26 125 19 7
Sätt in 73 129 99 113 43 155 16 26 125 19 129 99 113 43 155 16 26 125 19......... i = 9 129 99 73 113 43 155 16 26 125 19 Man hittar rätt plats på samma sätt när man tar bort element. Ett borttaget element ersätts med för att skilja det från en plats som aldrig har haft något element. Efter att ha tagit bort elementen 19, 26, 129 och 73 ser hashtabellen ut så här: 99 113 43 155 16 125 Letar man efter ett element som inte finns måste man proba tills man hittar ett, eller tills man har provat alla möjliga probningar (ovan, när i = 9) Kvadratisk probing h(k, i) = (k + f(i)) mod 10 f(i) = i 2 De sex första insättningarna blir identiska med de ovan (eftersom 0 = 0 2 och 1 = 1 2 ). Sätt in 99 i = 2 i = 3 129 113 43 155 16 99 19 8
Sätt in 26 129 113 43 155 16 99 19 129 113 43 155 16 26 99 19 Sätt in 125 129 113 43 155 16 26 99 19 129 113 43 155 16 26 99 19 i = 2 129 113 43 155 16 26 99 19 i = 3 129 113 43 155 16 26 99 19 i = 4 129 125 113 43 155 16 26 99 19 Sätt in 73 129 125 113 43 155 16 26 99 19 129 125 113 43 155 16 26 99 19 i = 2 129 125 113 43 155 16 26 99 19 i = 3 129 125 73 113 43 155 16 26 99 19 Samma principer gäller för borttagning som ovan. Man kan sluta leta efter ett element när man hittar ett eller när man har provat alla möjliga probningar (ovan när i = 5). 9
7. Vi skriver en funktion som går igenom en lista med en hashtabell h och för varje element e kollar om e finns i h. Om e inte finns i h behåller vi elementet i listan och lägger in det i h. Om e finns i h så betyder det att e har förekommit tidigare i listan, därför tar vi bort det ur listan. Eftersom vi bara är intresserade av att att snabbt lagra och hitta nycklar i tabellen så låter vi alla värden vi lägger in i hashtabellen vara unit. (* killduplicates l TYPE: string list -> string list PRE: () POST: l med endast en förekomst av varje element EXAMPLES: killduplicates ["a", "b", "a", "b", "c"] = ["a", "b", "c"] COMPLEXITY: Theta( l ) i genomsnittliga fall *) fun killduplicates(l) = let (* killduplicates (l,h) TYPE: string list*hasharray.hash -> string list PRE: () POST: l med endast en förekomst av varje element och utan något element som finns som nyckel i h EXAMPLES: killduplicates (["a", "b", "b", "c"], {("a"->())}) = ["a", "b", "c"] COMPLEXITY: Theta( l ) för en välbyggd hashtabell h *) fun killduplicates ([], _) = [] killduplicates (f::r, h) = if issome(hasharray.sub(h,f)) then killduplicates (r, h) else (HashArray.update(h,f,()); f::killduplicates (r, h)) val h = HashArray.hash(length l) in killduplicates (l,h) end; killduplicates skulle kunna ha typen a list -> a list om Poly/ML tillät polymorfa hashtabeller (som många andra programmeringsspråk och ML-dialekter gör). 10