Vectores: Algoritmos de Ordenaçãoarocha/AED1/APONTC/vectorOrd.pdf · Vectores: Algoritmos de...
Transcript of Vectores: Algoritmos de Ordenaçãoarocha/AED1/APONTC/vectorOrd.pdf · Vectores: Algoritmos de...
1
Algoritmos e Estruturas de Dados2008/2009
Vectores: Algoritmos de Ordenação
AED - 2005/06 2
Ordenação
• Problema (ordenação de vector)– rearranjar os n elementos de um vector (v) por ordem crescente, ou
melhor, por ordem não decrescente, porque podem existir valores repetidos
– vector de N elementos
• Algoritmos:– Ordenação por Inserção
– Ordenação por Selecção
– Bubble Sort
– ShellSort
– MergeSort
– Ordenação por Partição (QuickSort)
– BucketSort
2
AED - 2005/06 3
Ordenação por Inserção
• N-1 passos
• Em cada passo p coloca um elemento na ordem, sabendo que elementos dos índices inferiores (entre 0 e p-1) já estão ordenados
• Algoritmo (ordenação por inserção):
– Considera-se o vector dividido em dois sub-vectores (esquerdo e direito), com o da esquerda ordenado e o da direita desordenado
– Começa-se com um elemento apenas no sub-vector da esquerda
– Move-se um elemento de cada vez do sub-vector da direita para o sub-vector da esquerda, inserindo-o na posição correcta por forma a manter o sub-vector da esquerda ordenado
– Termina-se quando o sub-vector da direita fica vazio
AED - 2005/06 4
5
ordenado desordenado
1ª iteração
v: 3 1 9 5 8
3 5 1 9 5 8
2ª iteração
1 3 5 9 5 8
3ª iteração
1 3 5 9 5 8
4ª iteração
1 3 5 5 9 8
5ª iteração
1 3 5 5 8 9desordenadoordenado
v:
Exemplo de Ordenação por Inserção
3
AED - 2005/06 5
/* Insere um valor x num array v com n elementos ordenados. Após a inserção, o array fica com n+1 elementos ordenados. Retorna a posição em que inseriu o valor (entre 0 e n). */
template <class T>int insertSorted(T v[], int n, T x){
int j;// procura posição (j) de inserção da direita (posi ção n)// para a esquerda (posição 0), e ao mesmo tempo ch ega // elementos à direita (podia usar o próprio n em v ez de j)
for (j = n; j > 0 && x < v[j - 1]; j--)v[j] = v[j-1];
v[j] = x; // insere agora
return j; // retorna a posição em que inseriu
}
Inserção de valor em vector ordenado
• Sub-problema: inserir um valor num vector ordenado (mantendo-o ordenado)
• Solução em C++ (função insertSorted) :
AED - 2005/06 6
// Ordena array v de n elementos, ficando v[0] ≤... ≤v[n-1]
template <class T> void insertionSort(T v[], int n){
// i - tamanho do sub-array esquerdo ordenado
for (unsigned int i = 1; i < n; i++)insertSorted(v, i , v[i]);
}
template <class T> void insertionSort(T v[], int n){
for (unsigned int i = 1; i < n; i++) {T x = v[i];for (unsigned int j = i; j > 0 && x < v[j - 1]; j--)
v[j] = v[j-1];v[j] = x;
}}
Implementação da Ordenação por Inserção em C++• A função em C++ para ordenar um array pelo método de inserção é agora trivial
• Juntando tudo (para ser mais eficiente) ...
4
AED - 2005/06 7
Ordenação por Inserção usando vector
// Ordena elementos do vector a// Comparable : deve conter construtor cópia, operadores// igualdade (=) e comparação(<)
template <class Comparable> void insertionSort(vector<Comparable> &a){
for (unsigned int p = 1; p < a.size(); p++){
Comparable tmp = a[p];int j;for (j = p; j > 0 && tmp < a[j-1]; j--)
a[j] = a[j-1];a[j] = tmp;
} }
AED - 2005/06 8
Eficiência da Ordenação por Inserção
• insertSorted(v, n, x) :– o nº de iterações do ciclo for é:
• 1, no melhor caso
• n, no pior caso
• n/2, em média
• insertionSort(v, n) :– faz insertSorted(,1,) , insertSorted(,2,) , ...,
insertSorted(,n-1,)
– o nº total de iterações do ciclo for de insertSorted é:• no melhor caso, 1 + 1 + ... + 1 (n-1 vezes) = n-1 ≈ n• no pior caso, 1 + 2 + ... + n-1 = (n-1)(1 + n-1)/2 = n(n-1)/2 ≈ n2/2• em média, metade do anterior, isto é, aproximadamente n2/4
⇒ T(n) = O(n2) (quadrático) (pior caso e média)
5
AED - 2005/06
Ordenação por Selecção
• Provavelmente é o algoritmo mais intuitivo:
– Encontrar o mínimo do vector
– Trocar com o primeiro elemento
– Continuar para o resto do vector (excluindo o primeiro)
• 2 ciclos encaixados, cada um pode ter N iterações :
– Complexidade O(N2)
• Variantes:
– “stableSort” – Insere mínimo na primeira posição (em vez de realizar a troca)
– “shaker Sort” – Procura máximo e mínimo em cada iteração (selecção
bidireccional)
9
AED - 2005/06
Ordenação por Selecção
Algoritmo em C++:// Ordena vector a de n elementos, ficando a[0]≤... ≤a[n-1]
template <class Comparable>
void selectionSort(vector<Comparable> &a )
{
typename vector<Comparable>::iterator it;
for(it = a.begin(); it != a.end()-1; ++it )
iter_swap(it, min_element(it, a.end()));
}
Existente em STL:• template <class ForwardIterator> ForwardIterator
ForwardIteratormin_element(ForwardIterator first, ForwardIterator last);
• template <class ForwardIterator1, class ForwardIterator2> inline void iter_swap(ForwardIterator1 a, ForwardIterator2 b);
10
6
AED - 2005/06
Bubble Sort
• Algoritmo de Ordenação “BubbleSort” (“Exchange Sort”):
– Compara elementos adjacentes. Se o segundo for menor do que o
primeiro, troca-os
– Fazer isto desde o primeiro até ao último par
– Repetir para todos os elementos excepto o último (que já está correcto)
– Repetir, usando menos um par em cada iteração até não haver mais
pares (ou não haver trocas)
• 2 ciclos encaixados, cada um pode ter N iterações:
– Complexidade O(N2)
11
AED - 2005/06
Bubble SortAlgoritmo em C++:// Ordena vector a de n elementos inteiros, ficando a[0]≤... ≤a[n-1]
template <class Comparable>
void bubbleSort(vector<Comparable> &a)
{
for(unsigned int j=a.size()-1; j>0; j--)
{
bool troca=false;
for(unsigned int i = 0; i<j; i++)
if(a[i] > a[i+1]) {
swap(a[i],a[i+1]);
troca = true;
}
if (!troca) return;
}
}
12
7
AED - 2005/06
Bubble Sort (outra implementação)
Algoritmo em C++:// Ordena vector a de n elementos inteiros, ficando a[0]≤... ≤a[n-1]
template<class T>void bubbleSort(vector<T> &a ){
typename vector<T>::reverse_iterator it1;for(it1 = a.rbegin(); it1 != a.rend(); ++it1 ) {
typename vector<T>::iterator it2;bool troca = false;for(it2 = a.begin(); &*it2 != &*it1; ++it2 ) {
if (*it2 > *(it2+1)) {iter_swap(it2, it2+1);troca = true;
}}if (!troca) return;
}}
13
AED - 2005/06 14
ShellSort
• Compara elementos distantes
• Distância entre elementos comparados vai diminuindo, até que a comparação seja sobre elementos adjacentes– Usa a sequência h1, h2, …, ht (h1=1)
– Em determinado passo, usando incremento hk, todos os elementos separados da distância hk estão ordenados, a[i] ≤ a[i+h k]
• Sequência de incrementos:– Shell: mais popular, não mais eficiente O(N2)
• ht = N/2, hk = hk+1/2
– Hibbard: incrementos consecutivos não têm factores comuns
• h = 1, 3, 7, …, 2k-1 O(N5/4)
8
AED - 2005/06 15
ShellSort ( h = {5, 3, 1} )
81 94 11 96 12 35 17 95 28 58 41 75 v:
35 17 11 28 12 41 75 95 96 58 81 94
28 12 11 35 17 41 58 81 94 75 85 96
11 12 17 28 35 41 58 75 81 94 95 96
h=
5
h=3
h=1
35 94 11 96 12 81 17 95 28 58 41 75
35 17 11 96 12 81 94 95 28 58 41 75
35 17 11 96 12 81 94 95 28 58 41 75
35 17 11 28 12 81 94 95 96 58 41 75
35 17 11 28 12 81 94 95 96 58 41 75
35 17 11 28 12 41 94 95 96 58 81 75
AED - 2005/06 16
Implementação de ShellSort em C++
// ShellSort - com incrementos de Shell
template <class Comparable>
void shellSort(vector<Comparable> &a){
int j;
for (int gap = a.size()/2; gap > 0; gap /= 2)for (unsigned int i = gap; i < a.size(); i++){
Comparable tmp = a[i];for (j = i; j >= gap && tmp < a[j-gap]; j -= gap)
a[j] = a[j-gap];
a[j] = tmp;}
}
9
AED - 2005/06 17
MergeSort
• Abordagem recursiva– Divide-se o vector ao meio
– Ordena-se cada metade (usando MergeSort recursivamente)
– Fundem-se as duas metades já ordenadas
• Divisão e conquista– problema é dividido em dois de metade do tamanho
• Análise– Tempo execução: O(N logN)
• 2 chamadas recursivas de tamanho N/2
• Operação de junção de vectores: O(N)
• Inconveniente– fusão de vectores requer espaço extra linear
AED - 2005/06 18
Implementação de MergeSort em C++template <class Comparable>void mergeSort(vector <Comparable> & a) {
vector<Comparable> tmpArray(a.size());mergeSort(a, tmpArray, 0, a.size()-1);
}
// internal method that makes recursive calls// a is an array of Comparable terms// tmpArray is an array to place the merged result// left (right) is the left(right)-most index of th e subarray
template <class Comparable>void mergeSort(vector <Comparable> & a,
vector<Comparable> & tmpArray, int left, int right){
if (left < right){
int center = (left + right) / 2;mergeSort(a, tmpArray, left, center);mergeSort(a, tmpArray, center + 1, right);merge(a, tmpArray, left, center +1, right);
}}
10
AED - 2005/06 19
Implementação de MergeSort em C++// internal method that makes recursive calls. a is an array of // Comparable terms. tmpArray is an array to place the merged result.// left ( right) is the left(right)-most index of the subarray
template <class Comparable>void merge(vector <Comparable> & a, vector<Comparab le> &tmpArray,
int leftPos, int rightPos, int rightEnd){
int leftEnd = rightPos - 1;int tmpPos = leftPos;int numElements = rightEnd - leftPos + 1;while ( leftPos <= leftEnd && rightPos <= rightEnd )
if ( a[leftPos] <= a[rightPos] )tmpArray[tmpPos++] = a[leftPos++];
elsetmpArray[tmpPos++] = a[rightPos++];
while ( leftPos <= leftEnd )tmpArray[tmpPos++] = a[leftPos++];
while ( rightPos <= rightEnd )tmpArray[tmpPos++] = a[rightPos++];
for ( int i = 0; i < numElements; i++, rightEnd-- )a[rightEnd] = tmpArray[rightEnd];
}
AED - 2005/06 20
Ordenação por Partição (Quick Sort)
• Algoritmo (ordenação por partição):1. Caso básico: Se o número (n) de elementos do vector (a) a ordenar for
muito pequeno, usar outro algoritmo (insertionSort)
2. Passo de partição:
2.1. Escolher um elemento “arbitrário” (x) do vector (chamado pivot)
2.2. Partir o vector inicial em dois sub-vectores (esquerdo e direito), com valores ≤ x no sub-vector esquerdo e valores ≥ x no sub-vector direito (podendo existir um 3º sub-vector central com valores =x)
3. Passo recursivo: Ordenar os sub-vectores esquerdo e direito, usando o mesmo método recursivamente
• Algoritmo recursivo baseado na técnica divisão e conquista
11
AED - 2005/06 21
3 1 2 5 4 6 9 8 7
8 9 2 5 4 6 1 3 7
v: 8 9 2 5 4 6 1 3 7
i jx=4
3 9 2 5 4 6 1 8 7
i j
3 1 2 5 4 6 9 8 7
i j
3 1 2 4 5 6 9 8 7
ij <
≥≥≥≥ x≤≤≤≤ x
(2.3.3)
(2.3.3)
(2.3.1+2.3.2) i j
(2.3.3)
i j(2.3.1+2.3.2)
Exemplo do Passo de Partição
AED - 2005/06 22
/* calcula a mediana entre os valores dos indices l eft, right, e centro (left+right)/2 do vector a */
template <class Comparable>const Comparable &median3(vector<Comparable> &a, int left, int right){
int center = (left+right) /2;if (a[center] < a[left])
swap(a[left], a[center]);if (a[right] < a[left])
swap(a[left], a[right]);if (a[right] < a[center])
swap(a[center], a[right]);
//coloca pivot na posicao right-1
swap(a[center], a[right-1]);return a[right-1];
}
Implementação da Ordenação por Partição em C++
12
AED - 2005/06 23
/* Ordena vector( a) entre duas posições indicadas ( left e right ). Supõe que os elementos do vector são comparáveis co m " <" e copiáveis com " =". */
template <class Comparable>void quickSort(vector<Comparable> &a, int left, int right){
if (left-right <= 10) // se vector pequeno
insertionSort(a,left,right);else {
// inicializações
Comparable x = median3(a,left,right); // x é o pivot
int i = left;int j = right-1; // passo de partição
// continua ...
Implementação da Ordenação por Partição em C++
AED - 2005/06 24
for(; ; ) {
while (a[++i] < x) ;while (x < a[--j]) ;if (i < j)
swap(a[i], a[j]);else
break;}swap(a[i], a[right-1]); //repoe pivot
// passo recursivo
quickSort(a, left, i-1);quickSort(a, i+1, right);
}}
Implementação da Ordenação por Partição em C++
13
AED - 2005/06 25
#include <iostream.h>#include <stdlib.h> // Para usar rand()// inserir aqui a função QuickSort
void imprime(const int v[], int n){
for (int i = 0; i < n; i++)cout << v[i] << ' ';
cout << '\n';}
main(){
const int SIZE = 100;int v[SIZE];for (int i = 0; i < SIZE; i++)
v[i] = rand();imprime(v, SIZE);quickSort(v, 0, SIZE - 1);imprime(v, SIZE);return 0;
}
Programa de teste
AED - 2005/06 26
Análise da Ordenação por Partição
• Escolha pivot determina eficiência– pior caso: pivot é elemento mais pequeno
O(N2)
– melhor caso: pivot é elemento médio
O(N logN)
– caso médio: pivot corta vector arbitrariamente
O(N logN)
• Escolha pivot– má escolha: extremos do vector ( O(N2) se vector ordenado )
– aleatório: envolve mais uma função pesada
– recomendado: mediana de três elementos (extremos do vector e ponto médio)
14
AED - 2005/06 27
n
n-1
n-2
1
1
n-3 1
1 1
...
n
cada rectângulo refere-se a uma
chamada recursiva n
n/2 n/2
n/4 n/4n/4 n/41+log 2 n ≈
1 1 1 1 1 1 1 1
...
Pior caso: Melhor caso:
• profundidade de recursão: ≈ 1+log 2 n (sem contar com a possibilidade de um elemento ser excluído dos sub-arrays esquerdo e direito)
• tempo de execução total (uma vez que a soma de cada linha é n):
T(n) = O[(1+log2n) n] = O(n log n)
Eficiência da Ordenação por Partição
• profundidade de recursão: n• tempo de execução total (somando
totais de linhas):
T(n) = O[n+n+(n-1) + ... +2]
= O[n+(n-1)(n + 2)/2] = O(n2)
AED - 2005/06 28
Complexidade Espacial de QuickSort
• O espaço de memória exigido por cada chamada de quickSort, sem contar com chamadas recursivas, é independente do tamanho (n) do array
• O espaço de memória total exigido pela chamada de quickSort, incluindo as chamadas recursivas, é pois proporcional à profundidade de recursão
• Assim, a complexidade espacialde quickSort é:
– O(log n) no melhor caso (e no caso médio)
– O(n) no pior caso
• Em contrapartida, a complexidade espacialde insertionSort é O(1)
15
AED - 2005/06 29
BucketSort
• Ordenação linear– usa informação adicional sobre entrada
• Algoritmo– vector de entrada: inteiros positivos inferiores a M
a = [ A1, A2, …, AN ] ; Ai < M
– inicializar um vector de M posições a 0’scount = [c1, c2, …, cM] ; cj = 0
– Ler vector entrada (a) e para cada valor incrementar a posição respectiva no vector count : count[Ai]++
– Produzir saída lendo o vector count
• Eficiência– tempo linear
AED - 2005/06 30
Cada ponto corresponde à ordenação de 100 arrays de inteiros gerados aleatoriamente
Fonte: Sahni, "Data Structures, Algorithms and Applications in C++"
Método de ordenação por partição (quickSort) é na prática o mais eficiente,excepto para arrays pequenos (até cerca 20 elementos), em que o métodode ordenação por inserção (insertionSort) é melhor!
Quick sort
Insertion sort Heap sort
Merge sort
Comparação de tempos médios de execução(observados) de diversos algoritmos de ordenação