Algoritmos de Ordenação
Jonas H. Mendonça
Observação
Quando o aluno acessa a apresentação que está disponível no site
(sites.google.com/view/jonasmendonca/home), o aluno tem acesso a
todos os códigos que são apresentados. Isso evita que o aluno perca
tempo fazendo cópias e temos mais tempo para trabalhar o uso dos
algoritmos e sugestões de otimização que são apresentadas pelos
próprios estudantes em sala.
Algoritmos de Ordenação
São algoritmos que colocam os elementos de uma dada
sequência em uma certa ordem
(ascendente/descendente).
As ordens mais usadas são a numérica e a lexicográfica
(quando ordenamos palavras ou textos).
Estabilidade
Estabilidade: relativo à manutenção da ordem original de
itens de chaves iguais.
Um método de ordenação é estável se a ordem relativa
dos itens com chaves iguais não se altera durante a
ordenação.
Critério de Avaliação
Sendo n o número de registros no arquivo, as medidas de
complexidade relevantes são:
Número de comparações C(n) entre chaves.
Número de movimentações M(n) de itens
Outras considerações
O uso econômico da memória disponível é um requisito primordial na
ordenação interna.
Métodos que utilizam listas encadeadas não são muito utilizados.
Métodos que fazem cópias dos itens a serem ordenados possuem menor
importância.
Bubble sort
Os elementos vão “borbulhando” a cada iteração do método até a posição
correta para ordenação da lista.
O método poderia parar quando nenhum elemento borbulhace/trocasse de
posição.
Como os elementos são trocados (borbulhados) frequentemente, há um
alto custo de troca de elementos.
Exemplo
Exemplo
Exemplo
Exemplo
Exemplo
Não há necessidade de troca!!!
Exemplo
Exemplo
Bubble sort
Bubble sort
Vantagens:
Algoritmo simples
Algoritmo estável
Desvantagens:
O fato de o arquivo já estar ordenado não ajuda em nada, pois o custo continua quadrático.
Adequado apenas se arquivo pequeno
Como melhorar?
Bubble sort - Exercício
X = (25 , 57 , 48 , 37 , 12 , 92 , 86 , 33)
Selection sort
Seleção do n-ésimo menor (ou maior) elemento da lista
Troca do n-ésimo menor (ou maior) elemento com a n-ésima posição da
lista
Uma única troca por vez é realizada
Selection sort
Selection sort
Selection sort
Vantagens:
Custo linear no tamanho da entrada para o número de movimentos de registros.
É o algoritmo a ser utilizado para arquivos com registros muito grandes.
É muito interessante para arquivos pequenos.
Desvantagens:
O fato de o arquivo já estar ordenado não ajuda em nada, pois o custo continua quadrático.
O algoritmo não é estável.
Selection sort - Exercício
X = (25 , 57 , 48 , 37 , 12 , 92 , 86 , 33)
Insertion sort
Algoritmo utilizado pelo jogador de cartas
As cartas são ordenadas da esquerda para direita uma por uma.
O jogador escolhe a segunda carta e verifica se ela deve ficar antes ou na posição que está.
Depois a terceira carta é classificada, deslocando-a até sua correta posição.
O jogador realiza esse procedimento até ordenar todas as cartas
Insertion sort
O número mínimo de comparações e movimentos ocorre quando os itens
estão originalmente em ordem.
O número máximo ocorre quando os itens estão originalmente na ordem
reversa.
É o método a ser utilizado quando o arquivo está “quase” ordenado.
É um bom método quando se deseja adicionar uns poucos itens a um
arquivo ordenado, pois o custo é linear.
O algoritmo de ordenação por inserção é estável.
Insertion sort
Insertion sort
Insertion sort - Exercício
X = (25 , 57 , 48 , 37 , 12 , 92 , 86 , 33)
Uma breve pausa no algoritmos de ordenação
Dividir para conquistar
•Dividir para Conquistar: tática de guerra aplicada ao
projeto de algoritmos
•Um algoritmo de divisão e conquista é aquele que resolve o
problema desejado combinando as soluções parciais de (um
ou mais) subproblemas, obtidas recursivamente.
Projeto de Algoritmos por Divisão e Conquista
•Seja H um problema algorítmico. O método da divisão e
conquista consiste em tentar resolver H, através dos
seguintes passos:
•Decompor H em subproblemas H1, H2, ..., Hk
Projeto de Algoritmos por Divisão e Conquista
•Seja H um problema algorítmico. O método da divisão e conquista consiste em
tentar resolver H, através dos seguintes passos:
•Decompor H em subproblemas H1, H2, ..., Hk
•Resolver cada subproblema Hi, através de uma aplicação recursiva desse
método
Projeto de Algoritmos por Divisão e Conquista
•Seja H um problema algorítmico. O método da divisão e conquista consiste em
tentar resolver H, através dos seguintes passos:
•Decompor H em subproblemas H1, H2, ..., Hk
•Resolver cada subproblema Hi, através de uma aplicação recursiva desse
método
•Compor a solução de H, a partir das soluções de H1, H2, ..., Hk
Algoritmo Genérico - Dividir para Conquistar
DivConq(x) Entrada: A instância x
Saída: Solução y da
instância x
Algoritmo Genérico - Dividir para Conquistar
DivConq(x) Entrada: A instância x
Saída: Solução y da
instância x
Se x é suficientemente pequeno então
Algoritmo Genérico - Dividir para Conquistar
DivConq(x) Entrada: A instância x
Saída: Solução y da
instância x
Se x é suficientemente pequeno então
Retorne Solução(x) (dada pelo algoritmo para peq. Instâncias)
Algoritmo Genérico - Dividir para Conquistar
DivConq(x) Entrada: A instância x
Saída: Solução y da
instância x
Se x é suficientemente pequeno então
Retorne Solução(x) (dada pelo algoritmo para peq. Instâncias)
Senão
Algoritmo Genérico - Dividir para Conquistar
DivConq(x) Entrada: A instância x
Saída: Solução y da
instância x
Se x é suficientemente pequeno então
Retorne Solução(x) (dada pelo algoritmo para peq. Instâncias)
Senão
Divisão
Algoritmo Genérico - Dividir para Conquistar
DivConq(x) Entrada: A instância x
Saída: Solução y da
instância x
Se x é suficientemente pequeno então
Retorne Solução(x) (dada pelo algoritmo para peq. Instâncias)
Senão
Divisão
Decomponha x em instâncias menores x1, ..., xk
Para i=1 até k faça yi=DivConq(xi)
Algoritmo Genérico - Dividir para Conquistar
DivConq(x) Entrada: A instância x
Saída: Solução y da
instância x
Se x é suficientemente pequeno então
Retorne Solução(x) (dada pelo algoritmo para peq. Instâncias)
Senão
Divisão
Decomponha x em instâncias menores x1, ..., xk
Para i=1 até k faça yi=DivConq(xi)
Conquista
Algoritmo Genérico - Dividir para Conquistar
DivConq(x) Entrada: A instância x
Saída: Solução y da
instância x
Se x é suficientemente pequeno então
Retorne Solução(x) (dada pelo algoritmo para peq. Instâncias)
Senão
Divisão
Decomponha x em instâncias menores x1, ..., xk
Para i=1 até k faça yi=DivConq(xi)
Conquista
Combine as soluções yi para obter a solução y de x
Retorne(y)
Exemplo 1
Exponenciação
Problema: Calcular na, para todo real a e inteiro n>=0.
Exemplo 1
Exponenciação
Problema: Calcular na, para todo real a e inteiro n>=0.
1a. Solução: (Indução Fraca):
Caso base: n=0; a0 = 1
Exemplo 1
Exponenciação
Problema: Calcular na, para todo real a e inteiro n0.
1a. Solução: (Indução Fraca):
Caso base: n=0; a0 = 1
Hipótese de Indução: Suponha que, para qualquer inteiro n>0 e real a, sei
calcular an-1
Exemplo 1
Exponenciação
Problema: Calcular na, para todo real a e inteiro n0.
1a. Solução: (Indução Fraca):
Caso base: n=0; a0 = 1
Hipótese de Indução: Suponha que, para qualquer inteiro n>0 e real a, sei
calcular an-1
Passo da Indução: Queremos provar que conseguimos calcular an, para n>0.
Exemplo 1
Exponenciação
Problema: Calcular na, para todo real a e inteiro n0.
1a. Solução: (Indução Fraca):
Caso base: n=0; a0 = 1
Hipótese de Indução: Suponha que, para qualquer inteiro n>0 e real a, sei
calcular an-1
Passo da Indução: Queremos provar que conseguimos calcular an, para n>0.
Sei calcular an-1. Então, calculo an multiplicando an-1 por a
Exemplo 1 - Algoritmo Expon(a, n)
Entrada: A base a e o expoente n
Saída: O valor de an
Se n = 0 então
Retorne(1) {caso base}
Senão
an’ := Expon(a, n-1)
an := an’*a
Retorne(an)
Exemplo 1 - Complexidade
T(n): nº de operações realizadas pelo algoritmo para calcular an
1, n=0
T(n) =
T(n-1)+ 1, n>0
Exemplo 1 - Complexidade
T(n): nº de operações realizadas pelo algoritmo para calcular an
1, n=0
T(n) =
T(n-1)+ 1, n>0
Exemplo 1 - Complexidade
T(n): nº de operações realizadas pelo algoritmo para calcular an
1, n=0
T(n) =
T(n-1)+ 1, n>0 T(n)= O(n)
Voltando aos algoritmos de ordenação
Merge sort
O Merge sort, ou ordenação por intercalação, é um exemplo de algoritmo de
ordenação do tipo dividir-para-conquistar.
Sua idéia básica é criar uma sequência ordenada a partir de duas outras
também ordenadas. Para isso, ele divide a sequência original em pares de
dados, ordena-as; depois as agrupa em sequências de quatro elementos, e
assim por diante, até ter toda a sequência dividida em apenas duas partes.
Estas 2 partes são então combinadas para se chegar ao resultado final.
Merge sort
Portanto, os 3 passos seguidos pelo MergeSort são:
1. Dividir: Dividir os dados em subsequências pequenas;
2. Conquistar: Classificar as duas metades recursivamente aplicando o
merge sort;
3. Combinar: Juntar as duas metades em um único conjunto já ordenado.
Exemplo
Exemplo
Exemplo
Exemplo
Exemplo
Exemplo
Exemplo
Exemplo
Exemplo
Exemplo
Merge sort
void merge(int vetor[], int inicio, int meio, int fim){
int tamanho = fim - inicio + 1;
int i, j, k, posicao;
int temp[tamanho];
for (i=inicio; i<=fim; i++) {
temp[i] = vetor[i];
}
for (i=inicio, j=meio+1, posicao=inicio; (i <= meio) && (j <= fim); posicao++) {
if(temp[i]<temp[j]){
vetor[posicao] = temp[i];
i ++;
}
else{
vetor[posicao] = temp[j];
j++;
}
}
Merge sort
if(i == meio+1){
for(k=j; k<=fim; k++){
vetor[posicao] = temp[k];
posicao++;
}
}
else {
for(k=i; k<=meio; k++){
vetor[posicao] = temp[k];
posicao++;
}
}
}
Merge sort
Exercício
Dada a sequência de números: 12 3 4 13 9 2 6 11 5 1 8 7 10. Ordene em
ordem crescente utilizando o algoritmo aprendido em sala (MergeSort),
apresentando a sequência dos números a cada passo (Teste de Mesa).
Quick sort
Proposto por Hoare em 1960 e publicado em 1962.
É o algoritmo de ordenação interna mais rápido que se conhece para uma
ampla variedade de situações.
Provavelmente é o mais utilizado.
A idéia básica é dividir o problema de ordenar um conjunto com n itens em
dois problemas menores.
Os problemas menores são ordenados independentemente.
Os resultados são combinados para produzir a solução final.
Quick sort
A parte mais delicada do método é o processo de partição.
O vetor A [Esq..Dir] é rearranjado por meio da escolha arbitrária de um pivô
x.
O vetor A é particionado em duas partes:
Parte esquerda: chaves ≤ x.
Parte direita: chaves > x.
Partição
1. Escolha arbitrariamente um pivô x.
2. Percorra o vetor a partir da esquerda até que A[i] > x.
3. Percorra o vetor a partir da direita até que A[j] ≤ x.
4. Troque A[i] com A[j].
5. Continue este processo até os apontadores i e j se cruzarem.
Partição
1. Escolha arbitrariamente um pivô x.
2. Percorra o vetor a partir da esquerda até que A[i] > x.
3. Percorra o vetor a partir da direita até que A[j] ≤ x.
4. Troque A[i] com A[j].
5. Continue este processo até os apontadores i e j se cruzarem.
Após a partição
O vetor A[Esq..Dir] está particionado de tal forma que:
Os itens em A[Esq], A[Esq + 1], ..., A[j] são menores ou iguais a x;
Os itens em A[i], A[i + 1], ..., A[Dir] são maiores ou iguais a x.
Exemplo 1
O pivô x é escolhido como sendo:
O elemento central: A[(i + j) / 2].
Exemplo:
3 6 4 5 1 7 2
Exemplo 1
Exemplo 1
Quick sort
void Particao(int Esq, int Dir, int *i, int *j, Item *A){
Item x, aux;
*i = Esq; *j = Dir;
x = A[(*i + *j)/2]; /* obtem o pivo x */
do{
while (x.Chave > A[*i].Chave) (*i)++;
while (x.Chave < A[*j].Chave) (*j)--;
if (*i <= *j){
aux = A[*i]; A[*i] = A[*j]; A[*j] = aux;
(*i)++; (*j)--;
}
} while (*i <= *j);
}
Quick sort
void Ordena(int Esq, int Dir, Item *A)
{
int i,j;
Particao(Esq, Dir, &i, &j, A);
if (Esq < j) Ordena(Esq, j, A);
if (i < Dir) Ordena(i, Dir, A);
}
Quick sort
void QuickSort(Item *A, int n)
{
Ordena(0, n-1, A);
//Ordena(1, *n, A);
}
Quick sort
Vantagens:
É extremamente eficiente para ordenar arquivos de dados.
Necessita de apenas uma pequena pilha como memória auxiliar.
Requer cerca de n log n comparações em média para ordenar n itens.
Desvantagens:
Sua implementação é muito delicada e difícil:
Um pequeno engano pode levar a efeitos inesperados para algumas entradas de dados.
O método não é estável.
Quicksort
Melhor caso: particionamento produz segmentos com
mesmo tamanho.
Pior caso: Ocorrerá sempre que o vetor já estiver
ordenado ou em ordem inversa e escolhermos a menor
(ou maior) chave como particionadora.
Exercício
Dada a sequência de números: 12 3 4 13 9 2 6 11 5 1 8 7 10. Ordene em
ordem crescente utilizando o algoritmo Quick sort.
Heap Sort
Definição de heap - árvore binária completa: tem todos os nós possíveis em
cada nível; exemplos:
Heap sort
O heapsort utiliza uma estrutura de dados chamada heap para ordenar os elementos a medida
que os insere na estrutura. Assim, ao final das inserções, os elementos podem ser
sucessivamente removidos da raiz da heap, na ordem desejada.
Um heap é uma estrutura de dados baseada em árvore binária que segue um critério (ou
condição) bem-definido(a).
Estruturalmente, deve ser uma árvore quase completa:
o último nível pode não conter os nós mais à direita.
A heap pode ser representada como uma árvore ou como um vetor. Para uma ordenação
crescente, deve ser construído um heap máximo (o maior elemento fica na raiz). Para uma
ordenação decrescente, deve ser construído um heap mínimo (o menor elemento fica na raiz).
Condição do heap
Os dados armazenados em um heap devem satisfazer a seguinte condição:
Todo nó deve ter valor maior ou igual com relação aos seus filhos (Heap Máximo).
A condição não determina nenhuma relação entre os filhos de um nó (não confundir com
árvore binária de pesquisa).
As folhas de nível mais alto estão o mais à esquerda possível
Como representar heaps?
Podemos representar heaps como árvores binárias ou vetores.
A idéia é linearizar a árvore por níveis.
Relacionando os nós do Heap
A representação em vetores permite relacionar os nós do heap da seguinte
forma:
raiz da árvore: primeira posição do vetor
filhos de um nó na posição i: posições 2i e 2i+1
pai de um nó na posição i: posição [i / 2]
Fila de Prioridades
É um heap em que o valor de um nó é sempre menor ou igual aos de seus
eventuais filhos.
Deletando elementos de uma fila de prioridade
Inicialmente
Deletando elementos de uma fila de prioridade
Retira-se a
raiz e
coloca-se ali
o último
elemento
Deletando elementos de uma fila de prioridade
Retira-se a
raiz e
coloca-se ali
o último
elemento
Deletando elementos de uma fila de prioridade
Inserir um elemento numa fila de prioridades
Coloca-se o elemento a ser inserido na última posição
Heap sort
O método consiste em duas partes principais:
1. Inserir elemento por elemento do vetor numa fila de prioridades
2. Retirar pela raiz todos os elementos da fila de prioridades, retornando-os
ao vetor
Heap sort
void HeapSort (int n, vetor V) {
FormarFilaPrioridades (n, V);
OrdenarFilaPrioridades (n, V);
}
Heap sort
void FormarFilaPrioridades (int n, vetor V) {
int i, j, temp;
for (i = n-2; i >= 0; i--) {
j = i;
while (j < n-1 && V[j] < V[n-(n-j)/2]) {
temp = V[j];
V[j] = V[n-(n-j)/2];
V[n-(n-j)/2] = temp; j = n-(n-j)/2;
}
}
}
Heap sort
void OrdenarFilaPrioridades (int n, vetor V) {
int i, j, k, temp, min; bool parou;
for (i = 0; i < n-1; i++) {
min = V[n-1]; V[n-1] = V[i]; V[i] = min;
parou = false; j = n-1;
while (! parou && 2*j-n > i) {
if (2*j-n == i+1 || V[2*j-n] < V[2*j-n-1])
k = 2*j-n;
else k = 2*j-n-1;
if (V[j] > V[k]) {
temp = V[j]; V[j] = V[k];
V[k] = temp; j = k;
}
else parou = true;
}
}
}
Exercício
Dada a sequência de números: 12 3 4 13 9 2 6 11 5 1 8 7 10. Ordene em
ordem crescente utilizando o algoritmo Heap sort.
Exercício
Alterar os códigos de todos os métodos de ordenação e medir o tempo gasto
para ordenar vetores com tamanhos 10, 100, 1000, 10000 e 100000
elementos aleatoriamente gerados.
Analisar a curva de crescimento do tempo gasto para executar os métodos
implementados.
Fazer a mesma operação com vetores ordenados em ordem crescente e com
vetores ordenados em ordem decrescente.

Algoritmos de ordenação

  • 1.
  • 2.
    Observação Quando o alunoacessa a apresentação que está disponível no site (sites.google.com/view/jonasmendonca/home), o aluno tem acesso a todos os códigos que são apresentados. Isso evita que o aluno perca tempo fazendo cópias e temos mais tempo para trabalhar o uso dos algoritmos e sugestões de otimização que são apresentadas pelos próprios estudantes em sala.
  • 3.
    Algoritmos de Ordenação Sãoalgoritmos que colocam os elementos de uma dada sequência em uma certa ordem (ascendente/descendente). As ordens mais usadas são a numérica e a lexicográfica (quando ordenamos palavras ou textos).
  • 4.
    Estabilidade Estabilidade: relativo àmanutenção da ordem original de itens de chaves iguais. Um método de ordenação é estável se a ordem relativa dos itens com chaves iguais não se altera durante a ordenação.
  • 5.
    Critério de Avaliação Sendon o número de registros no arquivo, as medidas de complexidade relevantes são: Número de comparações C(n) entre chaves. Número de movimentações M(n) de itens
  • 6.
    Outras considerações O usoeconômico da memória disponível é um requisito primordial na ordenação interna. Métodos que utilizam listas encadeadas não são muito utilizados. Métodos que fazem cópias dos itens a serem ordenados possuem menor importância.
  • 7.
    Bubble sort Os elementosvão “borbulhando” a cada iteração do método até a posição correta para ordenação da lista. O método poderia parar quando nenhum elemento borbulhace/trocasse de posição. Como os elementos são trocados (borbulhados) frequentemente, há um alto custo de troca de elementos.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
    Bubble sort Vantagens: Algoritmo simples Algoritmoestável Desvantagens: O fato de o arquivo já estar ordenado não ajuda em nada, pois o custo continua quadrático. Adequado apenas se arquivo pequeno Como melhorar?
  • 17.
    Bubble sort -Exercício X = (25 , 57 , 48 , 37 , 12 , 92 , 86 , 33)
  • 18.
    Selection sort Seleção don-ésimo menor (ou maior) elemento da lista Troca do n-ésimo menor (ou maior) elemento com a n-ésima posição da lista Uma única troca por vez é realizada
  • 19.
  • 20.
  • 21.
    Selection sort Vantagens: Custo linearno tamanho da entrada para o número de movimentos de registros. É o algoritmo a ser utilizado para arquivos com registros muito grandes. É muito interessante para arquivos pequenos. Desvantagens: O fato de o arquivo já estar ordenado não ajuda em nada, pois o custo continua quadrático. O algoritmo não é estável.
  • 22.
    Selection sort -Exercício X = (25 , 57 , 48 , 37 , 12 , 92 , 86 , 33)
  • 23.
    Insertion sort Algoritmo utilizadopelo jogador de cartas As cartas são ordenadas da esquerda para direita uma por uma. O jogador escolhe a segunda carta e verifica se ela deve ficar antes ou na posição que está. Depois a terceira carta é classificada, deslocando-a até sua correta posição. O jogador realiza esse procedimento até ordenar todas as cartas
  • 24.
    Insertion sort O númeromínimo de comparações e movimentos ocorre quando os itens estão originalmente em ordem. O número máximo ocorre quando os itens estão originalmente na ordem reversa. É o método a ser utilizado quando o arquivo está “quase” ordenado. É um bom método quando se deseja adicionar uns poucos itens a um arquivo ordenado, pois o custo é linear. O algoritmo de ordenação por inserção é estável.
  • 25.
  • 26.
  • 27.
    Insertion sort -Exercício X = (25 , 57 , 48 , 37 , 12 , 92 , 86 , 33)
  • 28.
    Uma breve pausano algoritmos de ordenação
  • 29.
    Dividir para conquistar •Dividirpara Conquistar: tática de guerra aplicada ao projeto de algoritmos •Um algoritmo de divisão e conquista é aquele que resolve o problema desejado combinando as soluções parciais de (um ou mais) subproblemas, obtidas recursivamente.
  • 30.
    Projeto de Algoritmospor Divisão e Conquista •Seja H um problema algorítmico. O método da divisão e conquista consiste em tentar resolver H, através dos seguintes passos: •Decompor H em subproblemas H1, H2, ..., Hk
  • 31.
    Projeto de Algoritmospor Divisão e Conquista •Seja H um problema algorítmico. O método da divisão e conquista consiste em tentar resolver H, através dos seguintes passos: •Decompor H em subproblemas H1, H2, ..., Hk •Resolver cada subproblema Hi, através de uma aplicação recursiva desse método
  • 32.
    Projeto de Algoritmospor Divisão e Conquista •Seja H um problema algorítmico. O método da divisão e conquista consiste em tentar resolver H, através dos seguintes passos: •Decompor H em subproblemas H1, H2, ..., Hk •Resolver cada subproblema Hi, através de uma aplicação recursiva desse método •Compor a solução de H, a partir das soluções de H1, H2, ..., Hk
  • 33.
    Algoritmo Genérico -Dividir para Conquistar DivConq(x) Entrada: A instância x Saída: Solução y da instância x
  • 34.
    Algoritmo Genérico -Dividir para Conquistar DivConq(x) Entrada: A instância x Saída: Solução y da instância x Se x é suficientemente pequeno então
  • 35.
    Algoritmo Genérico -Dividir para Conquistar DivConq(x) Entrada: A instância x Saída: Solução y da instância x Se x é suficientemente pequeno então Retorne Solução(x) (dada pelo algoritmo para peq. Instâncias)
  • 36.
    Algoritmo Genérico -Dividir para Conquistar DivConq(x) Entrada: A instância x Saída: Solução y da instância x Se x é suficientemente pequeno então Retorne Solução(x) (dada pelo algoritmo para peq. Instâncias) Senão
  • 37.
    Algoritmo Genérico -Dividir para Conquistar DivConq(x) Entrada: A instância x Saída: Solução y da instância x Se x é suficientemente pequeno então Retorne Solução(x) (dada pelo algoritmo para peq. Instâncias) Senão Divisão
  • 38.
    Algoritmo Genérico -Dividir para Conquistar DivConq(x) Entrada: A instância x Saída: Solução y da instância x Se x é suficientemente pequeno então Retorne Solução(x) (dada pelo algoritmo para peq. Instâncias) Senão Divisão Decomponha x em instâncias menores x1, ..., xk Para i=1 até k faça yi=DivConq(xi)
  • 39.
    Algoritmo Genérico -Dividir para Conquistar DivConq(x) Entrada: A instância x Saída: Solução y da instância x Se x é suficientemente pequeno então Retorne Solução(x) (dada pelo algoritmo para peq. Instâncias) Senão Divisão Decomponha x em instâncias menores x1, ..., xk Para i=1 até k faça yi=DivConq(xi) Conquista
  • 40.
    Algoritmo Genérico -Dividir para Conquistar DivConq(x) Entrada: A instância x Saída: Solução y da instância x Se x é suficientemente pequeno então Retorne Solução(x) (dada pelo algoritmo para peq. Instâncias) Senão Divisão Decomponha x em instâncias menores x1, ..., xk Para i=1 até k faça yi=DivConq(xi) Conquista Combine as soluções yi para obter a solução y de x Retorne(y)
  • 41.
    Exemplo 1 Exponenciação Problema: Calcularna, para todo real a e inteiro n>=0.
  • 42.
    Exemplo 1 Exponenciação Problema: Calcularna, para todo real a e inteiro n>=0. 1a. Solução: (Indução Fraca): Caso base: n=0; a0 = 1
  • 43.
    Exemplo 1 Exponenciação Problema: Calcularna, para todo real a e inteiro n0. 1a. Solução: (Indução Fraca): Caso base: n=0; a0 = 1 Hipótese de Indução: Suponha que, para qualquer inteiro n>0 e real a, sei calcular an-1
  • 44.
    Exemplo 1 Exponenciação Problema: Calcularna, para todo real a e inteiro n0. 1a. Solução: (Indução Fraca): Caso base: n=0; a0 = 1 Hipótese de Indução: Suponha que, para qualquer inteiro n>0 e real a, sei calcular an-1 Passo da Indução: Queremos provar que conseguimos calcular an, para n>0.
  • 45.
    Exemplo 1 Exponenciação Problema: Calcularna, para todo real a e inteiro n0. 1a. Solução: (Indução Fraca): Caso base: n=0; a0 = 1 Hipótese de Indução: Suponha que, para qualquer inteiro n>0 e real a, sei calcular an-1 Passo da Indução: Queremos provar que conseguimos calcular an, para n>0. Sei calcular an-1. Então, calculo an multiplicando an-1 por a
  • 46.
    Exemplo 1 -Algoritmo Expon(a, n) Entrada: A base a e o expoente n Saída: O valor de an Se n = 0 então Retorne(1) {caso base} Senão an’ := Expon(a, n-1) an := an’*a Retorne(an)
  • 47.
    Exemplo 1 -Complexidade T(n): nº de operações realizadas pelo algoritmo para calcular an 1, n=0 T(n) = T(n-1)+ 1, n>0
  • 48.
    Exemplo 1 -Complexidade T(n): nº de operações realizadas pelo algoritmo para calcular an 1, n=0 T(n) = T(n-1)+ 1, n>0
  • 49.
    Exemplo 1 -Complexidade T(n): nº de operações realizadas pelo algoritmo para calcular an 1, n=0 T(n) = T(n-1)+ 1, n>0 T(n)= O(n)
  • 50.
  • 51.
    Merge sort O Mergesort, ou ordenação por intercalação, é um exemplo de algoritmo de ordenação do tipo dividir-para-conquistar. Sua idéia básica é criar uma sequência ordenada a partir de duas outras também ordenadas. Para isso, ele divide a sequência original em pares de dados, ordena-as; depois as agrupa em sequências de quatro elementos, e assim por diante, até ter toda a sequência dividida em apenas duas partes. Estas 2 partes são então combinadas para se chegar ao resultado final.
  • 52.
    Merge sort Portanto, os3 passos seguidos pelo MergeSort são: 1. Dividir: Dividir os dados em subsequências pequenas; 2. Conquistar: Classificar as duas metades recursivamente aplicando o merge sort; 3. Combinar: Juntar as duas metades em um único conjunto já ordenado.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
    Merge sort void merge(intvetor[], int inicio, int meio, int fim){ int tamanho = fim - inicio + 1; int i, j, k, posicao; int temp[tamanho]; for (i=inicio; i<=fim; i++) { temp[i] = vetor[i]; } for (i=inicio, j=meio+1, posicao=inicio; (i <= meio) && (j <= fim); posicao++) { if(temp[i]<temp[j]){ vetor[posicao] = temp[i]; i ++; } else{ vetor[posicao] = temp[j]; j++; } }
  • 64.
    Merge sort if(i ==meio+1){ for(k=j; k<=fim; k++){ vetor[posicao] = temp[k]; posicao++; } } else { for(k=i; k<=meio; k++){ vetor[posicao] = temp[k]; posicao++; } } }
  • 65.
  • 66.
    Exercício Dada a sequênciade números: 12 3 4 13 9 2 6 11 5 1 8 7 10. Ordene em ordem crescente utilizando o algoritmo aprendido em sala (MergeSort), apresentando a sequência dos números a cada passo (Teste de Mesa).
  • 67.
    Quick sort Proposto porHoare em 1960 e publicado em 1962. É o algoritmo de ordenação interna mais rápido que se conhece para uma ampla variedade de situações. Provavelmente é o mais utilizado. A idéia básica é dividir o problema de ordenar um conjunto com n itens em dois problemas menores. Os problemas menores são ordenados independentemente. Os resultados são combinados para produzir a solução final.
  • 68.
    Quick sort A partemais delicada do método é o processo de partição. O vetor A [Esq..Dir] é rearranjado por meio da escolha arbitrária de um pivô x. O vetor A é particionado em duas partes: Parte esquerda: chaves ≤ x. Parte direita: chaves > x.
  • 69.
    Partição 1. Escolha arbitrariamenteum pivô x. 2. Percorra o vetor a partir da esquerda até que A[i] > x. 3. Percorra o vetor a partir da direita até que A[j] ≤ x. 4. Troque A[i] com A[j]. 5. Continue este processo até os apontadores i e j se cruzarem.
  • 70.
    Partição 1. Escolha arbitrariamenteum pivô x. 2. Percorra o vetor a partir da esquerda até que A[i] > x. 3. Percorra o vetor a partir da direita até que A[j] ≤ x. 4. Troque A[i] com A[j]. 5. Continue este processo até os apontadores i e j se cruzarem.
  • 71.
    Após a partição Ovetor A[Esq..Dir] está particionado de tal forma que: Os itens em A[Esq], A[Esq + 1], ..., A[j] são menores ou iguais a x; Os itens em A[i], A[i + 1], ..., A[Dir] são maiores ou iguais a x.
  • 72.
    Exemplo 1 O pivôx é escolhido como sendo: O elemento central: A[(i + j) / 2]. Exemplo: 3 6 4 5 1 7 2
  • 73.
  • 74.
  • 75.
    Quick sort void Particao(intEsq, int Dir, int *i, int *j, Item *A){ Item x, aux; *i = Esq; *j = Dir; x = A[(*i + *j)/2]; /* obtem o pivo x */ do{ while (x.Chave > A[*i].Chave) (*i)++; while (x.Chave < A[*j].Chave) (*j)--; if (*i <= *j){ aux = A[*i]; A[*i] = A[*j]; A[*j] = aux; (*i)++; (*j)--; } } while (*i <= *j); }
  • 76.
    Quick sort void Ordena(intEsq, int Dir, Item *A) { int i,j; Particao(Esq, Dir, &i, &j, A); if (Esq < j) Ordena(Esq, j, A); if (i < Dir) Ordena(i, Dir, A); }
  • 77.
    Quick sort void QuickSort(Item*A, int n) { Ordena(0, n-1, A); //Ordena(1, *n, A); }
  • 78.
    Quick sort Vantagens: É extremamenteeficiente para ordenar arquivos de dados. Necessita de apenas uma pequena pilha como memória auxiliar. Requer cerca de n log n comparações em média para ordenar n itens. Desvantagens: Sua implementação é muito delicada e difícil: Um pequeno engano pode levar a efeitos inesperados para algumas entradas de dados. O método não é estável.
  • 79.
    Quicksort Melhor caso: particionamentoproduz segmentos com mesmo tamanho. Pior caso: Ocorrerá sempre que o vetor já estiver ordenado ou em ordem inversa e escolhermos a menor (ou maior) chave como particionadora.
  • 80.
    Exercício Dada a sequênciade números: 12 3 4 13 9 2 6 11 5 1 8 7 10. Ordene em ordem crescente utilizando o algoritmo Quick sort.
  • 81.
    Heap Sort Definição deheap - árvore binária completa: tem todos os nós possíveis em cada nível; exemplos:
  • 82.
    Heap sort O heapsortutiliza uma estrutura de dados chamada heap para ordenar os elementos a medida que os insere na estrutura. Assim, ao final das inserções, os elementos podem ser sucessivamente removidos da raiz da heap, na ordem desejada. Um heap é uma estrutura de dados baseada em árvore binária que segue um critério (ou condição) bem-definido(a). Estruturalmente, deve ser uma árvore quase completa: o último nível pode não conter os nós mais à direita. A heap pode ser representada como uma árvore ou como um vetor. Para uma ordenação crescente, deve ser construído um heap máximo (o maior elemento fica na raiz). Para uma ordenação decrescente, deve ser construído um heap mínimo (o menor elemento fica na raiz).
  • 83.
    Condição do heap Osdados armazenados em um heap devem satisfazer a seguinte condição: Todo nó deve ter valor maior ou igual com relação aos seus filhos (Heap Máximo). A condição não determina nenhuma relação entre os filhos de um nó (não confundir com árvore binária de pesquisa). As folhas de nível mais alto estão o mais à esquerda possível
  • 84.
    Como representar heaps? Podemosrepresentar heaps como árvores binárias ou vetores. A idéia é linearizar a árvore por níveis.
  • 85.
    Relacionando os nósdo Heap A representação em vetores permite relacionar os nós do heap da seguinte forma: raiz da árvore: primeira posição do vetor filhos de um nó na posição i: posições 2i e 2i+1 pai de um nó na posição i: posição [i / 2]
  • 87.
    Fila de Prioridades Éum heap em que o valor de um nó é sempre menor ou igual aos de seus eventuais filhos.
  • 88.
    Deletando elementos deuma fila de prioridade Inicialmente
  • 89.
    Deletando elementos deuma fila de prioridade Retira-se a raiz e coloca-se ali o último elemento
  • 90.
    Deletando elementos deuma fila de prioridade Retira-se a raiz e coloca-se ali o último elemento
  • 91.
    Deletando elementos deuma fila de prioridade
  • 94.
    Inserir um elementonuma fila de prioridades
  • 95.
    Coloca-se o elementoa ser inserido na última posição
  • 99.
    Heap sort O métodoconsiste em duas partes principais: 1. Inserir elemento por elemento do vetor numa fila de prioridades 2. Retirar pela raiz todos os elementos da fila de prioridades, retornando-os ao vetor
  • 100.
    Heap sort void HeapSort(int n, vetor V) { FormarFilaPrioridades (n, V); OrdenarFilaPrioridades (n, V); }
  • 101.
    Heap sort void FormarFilaPrioridades(int n, vetor V) { int i, j, temp; for (i = n-2; i >= 0; i--) { j = i; while (j < n-1 && V[j] < V[n-(n-j)/2]) { temp = V[j]; V[j] = V[n-(n-j)/2]; V[n-(n-j)/2] = temp; j = n-(n-j)/2; } } }
  • 102.
    Heap sort void OrdenarFilaPrioridades(int n, vetor V) { int i, j, k, temp, min; bool parou; for (i = 0; i < n-1; i++) { min = V[n-1]; V[n-1] = V[i]; V[i] = min; parou = false; j = n-1; while (! parou && 2*j-n > i) { if (2*j-n == i+1 || V[2*j-n] < V[2*j-n-1]) k = 2*j-n; else k = 2*j-n-1; if (V[j] > V[k]) { temp = V[j]; V[j] = V[k]; V[k] = temp; j = k; } else parou = true; } } }
  • 103.
    Exercício Dada a sequênciade números: 12 3 4 13 9 2 6 11 5 1 8 7 10. Ordene em ordem crescente utilizando o algoritmo Heap sort.
  • 104.
    Exercício Alterar os códigosde todos os métodos de ordenação e medir o tempo gasto para ordenar vetores com tamanhos 10, 100, 1000, 10000 e 100000 elementos aleatoriamente gerados. Analisar a curva de crescimento do tempo gasto para executar os métodos implementados. Fazer a mesma operação com vetores ordenados em ordem crescente e com vetores ordenados em ordem decrescente.