Algoritmi di ordinamento
Problema dell’ordinamento
- Input: Una sequenza
di valori - Output: Una sequenza
che sia una permutazione di e tale per cui
Selection Sort
Cerco il minimo e lo metto in posizione corretta, riducendo il problema agli
Esempio di esecuzione:
/%E2%9A%99%EF%B8%8F%20Algoritmi%20e%20Strutture%20Dati/_images/SelectionSortExecution.png)
Implementazione in C++
void selectionSortvector<int>& array {
int size = array.size();
for(int i = 0; i < size; ++i) {
int minIndex = getMin(array, i, size);
std::swap(array[i], array[minIndex]);
}
}
int getMinvector<int> array, int start, int end {
int minIndex = start;
for(int i = start + 1; i < end; ++i) {
if(array[i] < array[minIndex])
minIndex = i;
}
return minIndex;
}
- Time complexity
- Caso migliore:
(ad ogni iterazione dobbiamo scorrere per forza tutti gli elementi per trovare il minimo) - Caso peggiore:
- Caso migliore:
- Space complexity:
Insertion Sort
Insertion sort is a simple sorting algorithm that works similar to the way you sort playing cards in your hands. The array is virtually split into a sorted and an unsorted part. Values from the unsorted part are picked and placed at the correct position in the sorted part.
Implementazione in C++
void insertionSortvector<int>& array {
int size = array.size();
for(int i = 1; i < size; ++i) {
int j = i;
int temp = array[j];
for(; (j > 0) && (temp < array[j - 1]); --j)
array[j] = array[j - 1];
array[j] = temp;
}
}
- Time complexity
- Caso migliore:
(se ogni subarray è ordinato non bisogna spostare niente) - Caso peggiore:
(nel caso in cui l'array sia in ordine decrescente)
- Caso migliore:
- Space complexity:
Merge Sort
Il merge sort è basato sulla tecnica divide-et-impera:
- Divide: Spezza virtualmente il vettore di
elementi in due sottovettori di elementi - Impera: Chiama Merge Sort ricorsivamente sui due sottovettori
- Combina: Unisci (merge) le due sequenze ordinate
Idea: Si sfrutta il fatto che i due sottovettori sono già ordinati per ordinare più velocemente
/%E2%9A%99%EF%B8%8F%20Algoritmi%20e%20Strutture%20Dati/_images/MergeSortExecution.png)
Implementazione in C++
void mergeSortvector<int>& array {
mergeSortHelper(array, 0, array.size() - 1);
}
void mergeSortHelpervector<int>& array, int first, int last {
if(first >= last)
return;
/* mid is the last element of the first half of the array.
mid+1 is the first element of the second half of the array. */
int mid = (first + last) / 2;
mergeSortHelper(array, first, mid);
mergeSortHelper(array, mid + 1, last);
merge(array, first, mid, last);
}
void mergevector<int>& array, int first, int mid, int last {
std::vector<int> sortedArray;
int leftSide = first;
int rightSide = mid + 1;
/* We have first to mid and mid+1 to last already sorted.
We have to merge the two parts in sortedArray. */
while(leftSide < mid + 1 && rightSide < last + 1) {
if(array.at(leftSide) > array.at(rightSide)) {
sortedArray.push_back(array.at(rightSide));
++rightSide;
}
else {
sortedArray.push_back(array.at(leftSide));
++leftSide;
}
}
/* Insert all the remaining values from i to mid into sortedArray */
for(; leftSide < mid + 1; ++leftSide)
sortedArray.push_back(array.at(leftSide));
/* Insert all the remaining values from mid+1 to last into sortedArray */
for(; rightSide < last + 1; ++rightSide)
sortedArray.push_back(array.at(rightSide));
/* Assign sorted data stored in sortedArray to array */
for(int i = first; first < last + 1; ++first)
array.at(first) = sortedArray.at(first - i);
}
- Time complexity
- Caso migliore:
(il costo computazionale di merge()èe l'array viene spezzato volte) - Caso peggiore:
- Caso migliore:
- Space complexity:
(nell'ultimo caso l'array di supporto è di elementi)
Come abbiamo ottenuto questo?
Costo computazionale di Merge Sort:
È possibile risolvere questa ricorrenza nel modo seguente:
Per maggiori informazioni leggere 1. Risolvere le ricorrenze.
Riassunto del merge sort:
/%E2%9A%99%EF%B8%8F%20Algoritmi%20e%20Strutture%20Dati/_images/MergeSortExplanation.png)
E se dividessi l'array in più di 2 parti?
Si consideri una variante di MergeSort chiamata MergeSortK che, invece di suddividere l’array da ordinare in 2 parti, lo suddivide in
Merge Sort dividendo l'array in 3 parti:
da cui
Merge Sort dividendo l'array in
da cui
Sembra quindi che più sono le parti in cui dividiamo l'array e più efficiente diventa l'algoritmo.
Ma allora perchè quello più usato è il 2-way Merge sort? 🤔
In realtà, nonostante l'algoritmo sembri più efficiente, il numero di comparazioni da fare aumenta proporzionalmente al numero di parti in cui dividiamo l'array.
Infatti, se dividessi in
da cui
/%E2%9A%99%EF%B8%8F%20Algoritmi%20e%20Strutture%20Dati/_images/MergeSortComplexity.png)
Il Merge Sort più efficiente quindi è quello che divide l'array in 2 parti.