diff --git a/doc/exercises/merge_ordered_list.md b/doc/exercises/merge_ordered_list.md new file mode 100644 index 0000000..d1faca6 --- /dev/null +++ b/doc/exercises/merge_ordered_list.md @@ -0,0 +1,73 @@ +--- +title: Caratterizzazione della complessità di un algoritmo per la fusione di liste ordinate +author: Raffaele Mignone +subject: Fusione di liste ordinate +keywords: + - Complessità + - Binary Heap + - Coda a priorità + - Kotlin +papersize: a4 +lang: it-IT +--- + +# Fusione di liste ordinate + +## Traccia + +Descrivete un algoritmo per fondere $k$ liste ordinate in un’unica lista ordinata e valutarne la complessità. +$N_i$, con $1 \le i \le k$, denota la lunghezza della $i$-esima lista, mentre $N = \sum_i N_i$ è il numero totale di elementi di tutte le liste. + +## Soluzione + +Per fondere le $k$ liste ordinate possiamo usare l'algoritmo di merge visto durante l'analisi del *mergesort*, a patto di adattarlo per un merge $k$-way. +Dovendo trovare di volta in volta il minimo tra $k$ elementi non è più possibile svolgere un confronto diretto, ma si rende necessario l'utilizzo di una coda a priorità minima di dimensioni $k$. + +Inoltre è stato necessario utilizzare una struttura dati d'appoggio (@lst:node) per conservare le informazioni necessarie all'aggiunta degli elementi. + +```{#lst:node .kotlin caption="Struttura dati che contiene l'elemento d'interesse, la lista da cui è stato estratto e la posizione in quest'ultima."} +data class Node(val value: T, val k: Int, val n: Int) +``` + +Nel @lst:sort viene mostrato com'è possibile risolvere il problema attraverso un binary heap[^compare-by]. + +[^compare-by]: Il binary heap utilizzato è in grado di contenere l'oggetto `Node`, ma il confronto per l'ordinamento viene svolto sul parametro `value`. + +```{#lst:sort .kotlin caption="Algoritmo di fusione mediante binary heap"} +fun > sort(vararg lists: List): List { + val heap = BinaryHeap + .createMinPriorityQueue, T> { it.value } + lists.forEachIndexed { k, list -> + heap.insert(Node(list.first(), k, 0)) + } + + val outList = ArrayList() + + while (!heap.isEmpty()) { + heap.pop().map { + outList.add(it.value) + val k = it.k + val n = it.n + 1 + + if (lists[k].size > n) + heap.insert(Node(lists[k][n], k, n)) + } + } + + return outList +} +``` + +## Caratterizzazione della complessità + +Analizzando il @lst:sort è possibile notare l'utilizzo di una struttura `while` la cui condizione di arresto è legata allo svuotamento della coda a priorità. +Visto che nella coda devono *passare* tutti gli elementi delle liste e che per ogni ciclo viene aggiunto e rimosso un elemento il `while` verrà eseguito complessivamente $N$ volte. + +All'interno del `while` viene eseguita un operazione di aggiunta di un elemento alla lista (complessità costante), un operazione di estrazione di un elemento dalla coda (avendo usato un binary heap di dimensione $k$ ha complessità $logk$) e una di aggiunta anche questa con complessità pari a $logk$. + +Per quanto visto possiamo caratterizzare la complessità totale dell'algoritmo pari a $Nlogk$. + +## Source code + +- [BinaryHeap](https://git.norangeb.it/norangebit-unisannio-computer-science/lm-tecniche-di-programmazione/src/branch/master/src/main/kotlin/it/norangeb/algorithms/datastructures/queue/priority/BinaryHeap.kt) +- [OrderedListSorter](https://git.norangeb.it/norangebit-unisannio-computer-science/lm-tecniche-di-programmazione/src/branch/master/src/main/kotlin/it/norangeb/algorithms/exercises/OrderedListSorter.kt)