norangebit
33710b1391
All checks were successful
continuous-integration/drone/push Build is passing
- add Prim's mst - add Kruskal's mst - edit weight interface - add exercise directed cycle
89 lines
2.8 KiB
Markdown
89 lines
2.8 KiB
Markdown
---
|
|
author: Raffaele Mignone
|
|
title: Caratterizzazione della complessità di un algoritmo per la ricerca di cicli orientati in un grafo
|
|
keywords:
|
|
- Complessità
|
|
- Grafo
|
|
- Ciclo
|
|
subject: Caratterizzazione della complessità
|
|
papersize: a4
|
|
lang: it-IT
|
|
---
|
|
|
|
# Ricerca di cicli orientati
|
|
|
|
## Traccia
|
|
|
|
Scrivere un programma per la ricerca di directed cycles in un grafo orientato.
|
|
|
|
## Soluzione
|
|
|
|
Il problema può essere risolto prendendo come base la ricerca in profondità classica.
|
|
Ad essa deve essere aggiunto un modo per tenere memoria dei nodi presenti sul percorso che si sta esplorando.
|
|
Ciò può essere fatto tramite l'uso di un array così come si è fatto per tenere traccia dei nodi visitati[^vertex-info].
|
|
|
|
[^vertex-info]: Nel caso specifico si è utilizzata una data class che conserva le informazioni `isVisited`, `isOnPath` e `previously`.
|
|
|
|
```{#lst:cycle .kotlin caption="Versione modificata della ricerca in profondità"}
|
|
private fun dfs(graph: UndirectedGraph, vertex: Int) {
|
|
graphInfo[vertex].isVisited = true
|
|
graphInfo[vertex].isOnPath = true
|
|
|
|
graph.adjacentVertex(vertex)
|
|
.forEach {
|
|
when {
|
|
hasCycle() -> return@forEach
|
|
!graphInfo[it].isVisited -> exploreChild(graph, vertex, it)
|
|
graphInfo[it].isOnStack -> cycle = makeCycle(vertex, it)
|
|
}
|
|
}
|
|
|
|
graphInfo[vertex].isOnPath = false
|
|
}
|
|
```
|
|
|
|
Quando tra i vertici adiacenti a quello che si sta esplorando si trova un nodo già presente sul percorso vuol dire che è stato trovato un ciclo.
|
|
La costruzione del ciclo viene mostrata nel @lst:makeCycle
|
|
|
|
```{#lst:makeCycle .kotlin caption="Memorizzazione dei vertici che danno origine ad un ciclo"}
|
|
private fun makeCycle(
|
|
start: Int,
|
|
end: Int
|
|
): MutableCollection<Int> {
|
|
val cycle = Stack<Int>()
|
|
|
|
var currentVertex = start
|
|
|
|
while (currentVertex != end) {
|
|
cycle.add(currentVertex)
|
|
currentVertex = graphInfo[currentVertex].previously
|
|
}
|
|
|
|
cycle.add(end)
|
|
cycle.add(start)
|
|
|
|
return cycle
|
|
}
|
|
```
|
|
|
|
Infine la funzione `exploreChild` (@lst:exploreChild) semplicemente si occupa di settare il predecessore e di eseguire la ricerca in profondità sul nuovo vertice.
|
|
|
|
```{#lst:exploreChild .kotlin caption="Funzione per eseguire la ricerca in profondità su un vertice adiacente"}
|
|
private fun exploreChild(
|
|
graph: UndirectedGraph,
|
|
parent: Int,
|
|
child: Int
|
|
) {
|
|
graphInfo[child].previously = parent
|
|
dfs(graph, child)
|
|
}
|
|
```
|
|
|
|
## Complessità
|
|
|
|
L'algoritmo per l'individuazione dei grafi mostrato precedentemente è sostanzialmente una versione modificata della ricerca in profondità e nel caso peggiore deve visitare tutti i nodi percorrendo tutti gli archi, per cui ha una complessità pari a $E + V$.
|
|
|
|
## Source code
|
|
|
|
- [DirectedCycle](https://git.norangeb.it/norangebit-unisannio-computer-science/lm-tecniche-di-programmazione/src/branch/master/src/main/kotlin/it/norangeb/algorithms/graph/operations/DirectedCycle.kt)
|