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:
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
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:
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
Il Merge Sort più efficiente quindi è quello che divide l'array in 2 parti.