Algoritmos e Estruturas de Dados,
aplicar Quicksort medio
numero[i]>p
aplicar Quicksort
Escolha do pivô:
O 1º elemento da sequência O elemento do médio e depois colocar o pivó por forma a que ini, medio e fim fiquem por ordem crescente
2007-2008 , Gladys Castillo
17
O Algoritmo Quicksort static void quickSort (int int [ ]] seq, int ini, int fim) { if (seq.length == 1 || ini > fim) return; if if (seq.length == 2) { /* sequência com 2 elementos */ if if (seq[ini] > seq[fim]) trocar trocar(seq, ini, fim); if trocar return; return; } int medio = (ini + fim) /2;
/* calcular o índice do pivô no meio */
/* colocar o pivô por forma a que ini, medio e fim fiquem por ordem crescente */
colocarPivo (seq, ini, medio, fim); if (seq.length == 3) return; if return // se sequência com 3 elementos então já está ordenada /* dividir a sequencia: na parte esquerda colocam-se os valores menores do que o pivô na parte direita, os valores maiores do que o pivô */
medio = dividirSeq dividirSeq (seq, ini, medio, fim); quickSort (seq, ini, medio-1); // invocação recursiva para a parte esquerda da sequência quickSort (seq, medio+1, fim); // invocação recursiva para a parte direita da sequência } Algoritmos e Estruturas de Dados,
2007-2008 , Gladys Castillo
18
9
O Algoritmo Quicksort
Métodos: trocar e colocarPivot private static void trocar trocar (int[ ] seq, int ind1, int ind2){ int temp = seq[ind1]; seq[ind1] = seq[ind2]; seq[ind2] = seq[temp]; } private static void colocarPivo (int[ ] seq, int ini, int medio, int fim){ /* colocar o pivô por forma a que ini , medio e fim fiquem por ordem crescente */ if (seq[ini] > seq[medio]) trocar(seq, ini, medio); if trocar if (seq[ini] > seq[fim]) trocar trocar(seq, ini, fim); if if (seq[medio] > seq[fim]) trocar(seq, medio, fim); if trocar }
Algoritmos e Estruturas de Dados,
2007-2008 , Gladys Castillo
19
O Algoritmo Quicksort Método: dividirSeq (I)
private static int dividirSeq ( int[ ] seq, int ini, int medio, int fim ){ int indEsq = ini + 1; int indDir = fim - 1; while (indEsq < indDir) { // 1º. procurar elementos na parte esquerda maiores do que pivô while (indEsq < medio){ if (seq[indEsq] > seq[medio]) break; if break indEsq++; } // 2º. procurar elementos na parte direita menores do que pivô
while (indDir > medio){ if (seq[indDir] if < seq[medio]) break; break indDir--; } Algoritmos e Estruturas de Dados,
2007-2008 , Gladys Castillo
20
10
O Algoritmo Quicksort Método: dividirSeq (II)
// 3º. trocar elementos segundo 3 casos Caso I: existe um elemento na if (indEsq != medio && indDir != medio){ parte esquerda maior do que pivô e um elemento na parte direita trocar (seq, indEsq, indDir); trocar menor do que pivô então trocar os indEsq++; indDir--; elementos } else if if (indEsq if == medio && indDir != medio){ trocar trocar (seq, medio,indDir); Caso II: existe um elemento na parte direita menor do que pivô, medio++; então trocar o elemento de índice if (medio != indDir) trocar(seq, trocar medio,indDir); if trocar indDir com o pivô e deslocar o indEsq = medio; // a parte esquerda já está OK pivô uma posição à direita } else if (indEsq if != medio && indDir == medio){ if trocar(seq, medio,indEsq); trocar Caso III: existe um elemento na medio --; parte esquerda maior do que pivô, if (medio != indEsq) trocar trocar (seq, medio, indEsq); então trocar o elemento de índice if trocar indEsq com o pivô e deslocar o indDir= medio; } // a parte direita já está Ok pivô uma posição à esquerda } // fecho while return medio; } Algoritmos e Estruturas de Dados,
2007-2008 , Gladys Castillo
21
Exemplo do Algoritmo Quicksort (VER Programação avançada
medio = 4
ini = 0 209
330 25
15
42
2
usando C, António Rocha, pag 189) indEsq = 2
fim = 9
32 8
55
145
42 55 25
15
indDir = 7 145
Trocar 145 com 8
Como 209, 42 e 145 não estão ordenados ⇒ Trocar 209 com 42 e 209 com 145 medio = 4 ini = 0 42
330 25
15
145
2
32
42 55 25 fim = 9 8
55
209
pivô 145: na parte esquerda colocam-se os valores menores do que o pivô e na parte direita, os valores maiores do que o pivô
executar dividirSeq(seq, 0, 4, 9) 42
330
25
15
145
2
32
8
55
209
42 55 25
25
15
8
2
32
15
8
145
15
8
145
32
145
2
32
8
330
145
330 209
2
330 209
indDir = 7 32
2
330 209
indDir = 7
medio = 6 15
8
2
145
Trocar 145 com 332 42
55
330 209
medio = 5
indEsq = 6
Trocar 330 com 55
42
8
Trocar 145 com 8, medio ++, trocar 32 com 145
indDir = 8
indEsq = 1
15
indEsq = 5 42 55 25
32
medio ++
Trocar 2 com 145
42 55 25
2
209
55 25
15
32
330 209
medio = 7 8
2
32
145
330 209
Chamar quickSort (seq, 0, 6) quickSort(seq, 8, 9)
Algoritmos e Estruturas de Dados,
2007-2008 , Gladys Castillo
22
11
Ordenação Quicksort Complexidade
Este algoritmo é considerado uns dos melhores algoritmos de ordenação, pois apesar de, no pior caso, ser um algoritmo quadrático, no caso médio tem uma complexidade linear logarítmica de comparações, ou seja de ordem O(n log 2 n) nº de comparações
pior caso: ordem quadrático caso meio: ordem linear logarítmica
WC(n) AC(n)
≈
≈
n2
1.4 n . log2 (n)
Outro algoritmo recursivo de ordenação de complexidade linear logarítmica também muito popular é o algoritmo MergeSort (ordenação por fusão). Pode ler sobre este algoritmo no livro “ Programação avançada usando C” de Antonio Rocha, pag 184
Algoritmos e Estruturas de Dados,
2007-2008 , Gladys Castillo
23
Recursividade vs. Iteração Todo problema que se resolve recursivamente pode ser resolvido iterativamente Mas como regra geral:
os algoritmos recursivos são mais simples e elegantes
se os problemas são de natureza recursivas são mais fáceis de serem implementados em linguagens de programação de alto nível
os algoritmos recursivos são menos eficientes
possuem código mais claro (legível) e mais compacto do que os correspondentes iterativos
consomem mais recursos devido à invocação sucessiva da função recursiva: gasta-se mais tempo na execução do algoritmo e muito mais memória. Porém pode valer a pena sacrificar a eficiência em beneficio da clareza
os algoritmos recursivos são mais difíceis de ser depurados
Especialmente quando for alta a profundidade da recursão, ou seja, o número máximo de chamadas simultâneas Algoritmos e Estruturas de Dados,
2007-2008 , Gladys Castillo
24
12
Recursividade vs. Iteração Conclusão
Quando devemos usar algoritmos recursivos?
Usar se:
Os problemas são de natureza recursiva. Neste caso, a solução recursiva é a mais natural, elegante e fácil de implementar
A solução recursiva não leva a uma excessiva repetição do cálculo dos mesmos valores
A solução iterativa equivalente é muito complexa (ex: problema das Torres de Hanoi)
Algoritmos e Estruturas de Dados,
2007-2008 , Gladys Castillo
25
Bibliografia
C. Thomas Wu: An introduction to Object-Oriented Programming with Java, third edition. Chapter 15: Recursive Algorithms slides online: http://highered.mcgraw-hill.com/sites/0072518847/student_view0/chapter15/powerpoint.html
Rosália Rodrigues: Introdução à Análise e Desenvolvimento de Algoritmos. Capítulo 3. Recorrência on-line em: http://www2.mat.ua.pt/rosalia/cadeiras/ADA/Cap3Recorrencia.pdf
António Adrego da Rocha: Programação Avançada usando C. Tecnologias da Informação. FCA Editora de Informática, 2006 Capítulo 1. Recursividade, Capítulo 6.6. Algoritmos de ordenação recursivos Nota: Algumas das figuras usadas nos acetatos foram extraídas destas fontes Algoritmos e Estruturas de Dados,
2007-2008 , Gladys Castillo
26
13
Caixa de Execução Cálculo de 4! true
O valor de palindromo(“somos”) é calculado no regresso das sucessivas invocações da função recursiva, pelo que palindromo( “somos” ) = (‘s’ == ‘s’) && (‘o’ == ‘o’) && true = true palindromo( “somos” ) = (‘s’ == ‘s’) && palindromo( “omo” ) = true && e u r t
palindromo( “omo” ) = (‘o’ == ‘o’) && palindromo( “m” ) = true && e u r t
palindromo( “m” ) = true
Algoritmos e Estruturas de Dados,
2007-2008 , Gladys Castillo
27
14