mirror of
https://github.com/Steffo99/appunti-magistrali.git
synced 2024-11-25 11:34:18 +00:00
132 lines
3.7 KiB
Markdown
132 lines
3.7 KiB
Markdown
|
# Quick sort
|
||
|
|
||
|
Il _quick sort_ è un altro approccio **ricorsivo** all'ordinamento per confronto.
|
||
|
|
||
|
## Funzionamento
|
||
|
|
||
|
Anche qui, applichiamo il **divide et impera**.
|
||
|
|
||
|
1. _Divide_: Scelgo un **pivot** qualsiasi all'interno della lista. Metto alla sua **sinistra tutti i numeri minori** e alla sua **destra tutti i numeri maggiori**.
|
||
|
2. _Impera_: Eseguo un **quick sort su entrambe le sottoliste**.
|
||
|
|
||
|
### Esempi
|
||
|
|
||
|
#### Iterazione con partizioni bilanciate
|
||
|
|
||
|
Osserviamo come si formi una partizione con tre elementi e una con quattro.
|
||
|
|
||
|
```
|
||
|
|¦ [2] 8 7 1 3 5 6 {4}
|
||
|
2 |¦ [8] 7 1 3 5 6 {4}
|
||
|
2 |8 ¦ [7] 1 3 5 6 {4}
|
||
|
2 |8 ¦ [7] 1 3 5 6 {4}
|
||
|
2 |8 7 ¦ [1] 3 5 6 {4}
|
||
|
2 1 |7 8 ¦ [3] 5 6 {4}
|
||
|
2 1 3 |8 7 ¦ [5] 6 {4}
|
||
|
2 1 3 |8 7 5 ¦ [6] {4}
|
||
|
2 1 3 |8 7 5 6 ¦ [{4}]
|
||
|
[2 1 3] {4} [8 5 6 7]
|
||
|
```
|
||
|
|
||
|
#### Iterazione con partizioni sbilanciate
|
||
|
|
||
|
Osserviamo come si formi una partizione con **zero elementi** e una con tre.
|
||
|
|
||
|
```
|
||
|
|¦ [4] 7 3 {1}
|
||
|
|4 ¦ [7] 3 {1}
|
||
|
|4 7 |¦ [3] {1}
|
||
|
|4 7 3 |¦ [{1}]
|
||
|
[] {1} [4 7 3]
|
||
|
```
|
||
|
|
||
|
## Costo computazionale
|
||
|
|
||
|
| Categoria | Upper bound | Lower bound | Tight bound |
|
||
|
|-----------|-------------|-------------|-------------|
|
||
|
| Tempo | `O(n²)` | `Ω(n log n)` | - |
|
||
|
|
||
|
Il costo della funzione è dato dalla somma del costo per **dividere in due partizioni** con il costo per realizzare il **Quick sort delle due sottopartizioni**
|
||
|
|
||
|
Possiamo applicare allora il **Master Theorem generale**:
|
||
|
|
||
|
```latex
|
||
|
T(n)\\
|
||
|
=\\
|
||
|
Θ(1) \qquad per\ n = 1\\
|
||
|
T(q) + T(dim_lista - pivot - 1) + Θ(n) \qquad per\ n > 1
|
||
|
```
|
||
|
|
||
|
### Il caso migliore
|
||
|
|
||
|
Se il pivot `q` è la **mediana della partizione** che stiamo ordinando, si vengono a creare due _sottopartizioni bilanciate_, e sostituendo otteniamo:
|
||
|
|
||
|
```latex
|
||
|
T(n)\\
|
||
|
=\\
|
||
|
Θ(1) \qquad per\ n = 1\\
|
||
|
2 T(\frac{n}{2}) + Θ(n) \qquad per\ n > 1
|
||
|
```
|
||
|
|
||
|
Possiamo allora applicare il **Master Theorem particolare**:
|
||
|
|
||
|
```latex
|
||
|
T(n)\\
|
||
|
=\\
|
||
|
Θ(1) \qquad per\ n = 1\\
|
||
|
Θ(n log n) \qquad per\ n > 1
|
||
|
```
|
||
|
|
||
|
### Il caso peggiore
|
||
|
|
||
|
Se il pivot è uno degli **estremi dell'array**, si creano due _partizioni sbilanciate_: una delle due sottoliste è sempre vuota!
|
||
|
Allora:
|
||
|
|
||
|
```latex
|
||
|
T(n) = T(n-1) + Θ(n)\\
|
||
|
= T(n-2) + Θ(n-1) + Θ(n)\\
|
||
|
= T(n-3) + Θ(n-2) + Θ(n-1) + Θ(n)\\
|
||
|
= …
|
||
|
∈ Θ(n^2)
|
||
|
```
|
||
|
|
||
|
> "Non date da mangiare sequenze ordinate al Quicksort, gli sono indigeste."
|
||
|
|
||
|
## Pseudocodice
|
||
|
|
||
|
```python
|
||
|
def partition(partizione, inizio, fine):
|
||
|
"""Dividi una partizione in due, usando l'ultimo elemento come pivot.
|
||
|
|
||
|
Note utili:
|
||
|
partizione[fine] è il pivot
|
||
|
partizione[maggiori] è il primo numero dei maggiori
|
||
|
partizione[non_iterati] è l'elemento su cui si sta iterando al momento"""
|
||
|
# Crea il primo separatore (la | pipe nell'esempio)
|
||
|
maggiori = inizio
|
||
|
# Crea il secondo separatore (la ¦ broken pipe nell'esempio)
|
||
|
non_iterati = inizio
|
||
|
# Itera su ogni numero tra inizio e fine (escluso!)
|
||
|
while non_iterati < fine:
|
||
|
# Se l'elemento su cui stiamo iterando è minore del pivot
|
||
|
if partizione[non_iterati] <= partizione[fine]:
|
||
|
# Mettilo nell'insieme dei minori, scambiandolo con il primo numero dei maggiori e incrementando il primo separatore
|
||
|
partizione[maggiori], partizione[non_iterati] = partizione[non_iterati], partizione[maggiori]
|
||
|
maggiori += 1
|
||
|
# Incrementa sempre il secondo separatore
|
||
|
non_iterati += 1
|
||
|
# Inserisci il pivot tra le due sottopartizioni create,
|
||
|
partizione[fine], partizione[non_iterati] = partizione[non_iterati], partizione[fine]
|
||
|
return maggiori
|
||
|
```
|
||
|
|
||
|
## Visualizzazione
|
||
|
|
||
|
[visualgo.net](https://visualgo.net/bn/sorting) (Nota: invece che prendere l'ultimo numero come pivot prende il primo, cambiando leggermente l'algoritmo.)
|
||
|
|
||
|
## Note per l'esame
|
||
|
|
||
|
> La domanda che fa sempre è **"Qual è la sequenza di pivot utilizzata?"**
|
||
|
|
||
|
> Elementi da soli _non_ vengono presi come pivot!
|