Algoritmos e Estruturas de Dados

Propaganda
Vectores: Algoritmos de Ordenação (2)
Algoritmos e Estruturas de Dados
2009/2010
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
AED - 2009/10
2
1
Exemplo do Passo
de Partição
i
v:
8
9
2
5
x=4
4
j
6
1
i
8
9
2
5
4
6
9
3
1
1
1
3
7
1
8
7
6
9
8
7
6
9
8
7
6
9
x
8
7
j
2
5
4
2
2
6
j
i
3
7
j
i
3
3
5
4
i
j
5
4
j < i
3
AED - 2009/10
1
2
x
4
5
3
Implementação da Ordenação por Partição em C++
/* calcula a mediana entre os valores dos indices left, 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];
}
AED - 2009/10
4
2
Implementação da Ordenação por Partição em C++
/* Ordena vector(a) entre duas posições indicadas (left e right).
Supõe que os elementos do vector são comparáveis com "<" e
copiáveis com "=". */
template <class Comparable>
void quickSort(vector<Comparable> &a, int left, int right)
{
if (left-right <= 10)
// se vector
insertionSort(a,left,right);
else {
pequeno
// inicializações
Comparable x = median3(a,left,right);
int i = left;
int j = right-1;
// passo de partição
// x é o pivot
// continua ...
5
AED - 2009/10
Implementação da Ordenação por Partição em C++
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);
}
}
AED - 2009/10
6
3
Programa de teste
#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;
}
AED - 2009/10
7
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)
AED - 2009/10
8
4
Eficiência da Ordenação por Partição
Pior caso:
cada rectângulo
refere-se a uma
chamada recursiva
n
n-1
n-3
n/2
1
1+log 2 n 
1 1
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)
n/4
n/2
n/4
n/4
n/4
...
1
1 1 1 1 1 1 1 1
...
•
•
n
1
n-2
n
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)
9
AED - 2009/10
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 espacial de quickSort é:
– O(log n) no melhor caso (e no caso médio)
– O(n) no pior caso
•
Em contrapartida, a complexidade espacial de insertionSort é O(1)
AED - 2009/10
10
5
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’s
count = [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
11
AED - 2009/10
Comparação de tempos médios de execução
(observados) de diversos
algoritmos de ordenação
Insertion sort
Heap sort
Merge sort
Cada ponto corresponde à
ordenação de 100 arrays de
inteiros gerados aleatoriamente
Quick sort
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étodo
de ordenação por inserção (insertionSort) é melhor!
AED - 2009/10
12
6
Download