Classificação
Ordenação de Dados
Ordenação e Pesquisa de Dados
Marco Antonio Montebello Júnior
[email protected]
Ordenação dos dados




Facilitar e aumentar a eficiência das operações
de pesquisa sobre esses dados
Pode ser crescente ou decrescente
A seqüência de entrada, normalmente, é um
vetor com n elementos
Outras possibilidades de estruturas de dados
como por exemplo, uma lista encadeada
Ainda mais...






Na prática os números a serem ordenados, raramente, são
valores isolados
Normalmente, cada número é componente de um conjunto de
dados denominado registro
E o conjunto de registros forma uma tabela
Cada registro contém uma chave, que é o valor a ser ordenado,
e demais valores que sempre acompanham a chave
Numa operação de ordenação, sempre que for preciso trocar a
posição de uma chave, será necessário alterar a posição de
todos os elementos do registro
Na prática, quando os registros possuem uma grande
quantidade de dados (além da chave), a ordenação é realizada
sobre um vetor (ou lista) de ponteiros para os registros, com o
objetivo de minimizar as operações de movimentação de dados
Exemplos
Contigüidade Física


As entradas são fisicamente rearranjadas (todos os
elementos de um registro são ordenados fisicamente)
Tabela não ordenada
Reg Chave Outros campos
1
3
xxx xxx xxx
2
7
yyy yyy yyy
3
5
zzz zzz zzz
4
1
kkk kkk kkk
5
4
ttt
ttt
ttt
6
2
uuu uuu uuu

Tabela ordenada
Reg Chave Outros campos
1
1
kkk kkk kkk
2
2
uuu uuu uuu
3
3
xxx xxx xxx
4
4
ttt
ttt
ttt
5
5
zzz zzz zzz
6
7
yyy yyy yyy
Exemplo
Vetor Indireto de Ordenação

As entradas são mantidas nas posições originais. A seqüência é
dada por um vetor gerado durante o processo de classificação
(não envolve movimentação dos registros em uma tabela).

Tabela desordenada
Reg Chave
1
3
2
7
3
5
4
1
5
4
6
2
Outros campos
xxx xxx xxx
yyy yyy yyy
zzz zzz zzz
kkk kkk kkk
ttt
ttt
ttt
uuu uuu uuu

Vetor de ordenação
1
2
3
4
5
6
Índice da tabela
4
6
1
5
3
2
Exemplo
Encadeamento


As entradas são mantidas nas posições originais.
É formada uma lista encadeada com a ordenação. Utiliza-se um
campo a mais na tabela para armazenamento da lista, e não mais
o vetor adicional (vetor indireto de ordenação). É preciso utilizar
um ponteiro para o primeiro elemento
Reg. Chave
Demais Campos
Próx.
1
3
xxx xxx xxx xxx xxx xxx 5
2
7
yyy yyy yyy yyy yyy yyy 3
5
zzz zzz zzz zzz zzz zzz 2
4
1
kkk kkk kkk kkk kkk kkk 6
5
4
ttt ttt ttt ttt ttt ttt
3
6
2
uuu uuu uuu uuu uuu uuu 1
4
Ponteiro para o
primeiro elemento
da tabela (reg. 4
contém chave 1)
Ordenação Interna versus
Ordenação Externa





A ordenação interna é utilizada num conjunto de dados
pequeno
Neste caso a ordenação pode ser realizada inteiramente
(ou quase inteiramente) na memória principal
A ordenação externa é utilizada num conjunto de dados
grandes
A conseqüência é que a ordenação não pode ser
realizada na memória principal
Nesse caso a ordenação é realizada sobre dados
armazenados na memória secundária (disco, fita, etc.)
Principais métodos de
ordenação interna

Ordenação por Inserção



Ordenação por Troca



Inserção Direta
Incrementos Decrescentes (Shell Sort)
Método da Bolha (Bubble Sort)
Método da Troca e Partição (Quicksort)
Ordenação por Seleção


Seleção Direta
Seleção em Árvore (Heapsort)
Ordenação por Inserção

Nesse método os elementos são inseridos em
sua posição correta, em relação aos elementos
já classificados

Duas possibilidades :


Inserção Direta
Método Shell
Inserção Direta




É o método mais simples
Utilizado para um conjunto pequeno de dados
Possui baixa eficiência
Nesse método o vetor a ser ordenado é dividido
em dois segmentos:


O primeiro segmento contém os elementos já
ordenados;
O segundo segmento contém os elementos a serem
ordenados
Algoritmo – Inserção Direta



Primeiro elemento está no vetor ordenado e os
demais no vetor desordenado;
Retirar o primeiro elemento do vetor
desordenado e colocá-lo no vetor ordenado, na
sua posição correta;
Repetir o processo para todos os elementos do
vetor desordenado.
Algoritmo – Inserção Direta
Const
TAM = 10
Tipos
v = vetor[1..TAM] de inteiros
Var
vet: v
i, j, k, temp: inteiro
achei: lógico
+-------------------------+
| Exemplo:
|
|
|
| 5 | 2
3
4
8
7 |
| 2
5 | 3
4
8
7 |
| 2
3
5 | 4
8
7 |
| 2
3
4
5 | 8
7 |
| 2
3
4
5
8 | 7 |
| 2
3
4
5
7
8 ||
+-------------------------+
Início
Para i = 1 até TAM Faça
leia (vet[i])
Fim-Para
Para i = 2 até TAM Faça
j = 1
achei = FALSO
Enquanto (j < i) E (NÃO achei) Faça
Se vet[i] < vet[j] Então
achei = VERDADEIRO
Senão
j = j + 1
Fim-Se
Fim-Enquanto
Se achei Então
temp = vet[i]
k = i - 1
Enquanto k >= j Faça
vet[k+1] = vet[k]
k = k – 1
Fim-Enquanto
vet[j] = temp
Fim-Se
Fim-Para
Fim.
/*
/*
/*
/*
/*
Compara o */
vet[i] com */
os que estão */
à sua */
esquerda */
/* Desloca o */
/* vetor para */
/* a direita */
Algoritmo – Outra versão
Const
TAM = 10
Tipos
v = vetor[1..TAM] de inteiros
Var
vet: v
i, j, k, temp: inteiro
achei: lógico
Início
Para i = 1 até TAM Faça
leia (vet[i])
Fim-Para
Para j = 2 até TAM Faça
chave = vet[j]
i = j - 1
Enquanto (i > 0) E (vet[i] > chave) Faça
vet[i + 1] = vet[i]
i = i - 1
Fim-Enquanto
vet[i+1] = chave
Fim-Para
Fim.
Incrementos Decrescentes
(Shell Sort)





Proposto por Ronald L. Shell (1959)
É uma extensão do algoritmo de inserção direta
A diferença com relação à inserção direta é o
número de segmentos do vetor
Na inserção direta é considerado um único
segmento do vetor onde os elementos são
inseridos ordenadamente
No método do Shell são considerados diversos
segmentos
Descrição do Método Shell Sort

A ordenação é realizada em diversos passos. A
cada passo está associado um incremento I, o
qual determina os elementos que pertencem a
cada um dos segmentos:


segmento 1 - vet[1], vet[1 + I], vet[1 + 2I], ...
segmento 2 - vet[2], vet[2 + I], vet[2 + 2I], ...
...

segmento k - vet[k], vet[k + I], vet[k + 2I], ...
Descrição do Método Shell Sort




A cada passo todos os elementos (segmentos) são
ordenados isoladamente por inserção direta.
No final de cada passo o processo é repetido para um
novo incremento I igual a metade do anterior, até que
seja executado um passo com incremento I = 1.
O valor do incremento I é sempre uma potência inteira
de 2. O valor do incremento inicial é dado por 2**NP,
onde NP é o número de passos para ordenar o vetor
(fornecido pelo usuário, NP é uma aproximação inicial).
Assim, para NP = 3 o valor do incremento em cada
passo seria:




I = 2 ** 3 = 8 (23)
I = 2 ** 2 = 4 (22)
I = 2 ** 1 = 2 (21)
I = 2 ** 0 = 1 (20)
Exemplo: NP = 2
Vetor original (Desordenado)
1
2
3
4
5
6
7
15 27 40 13 19 20 45

8
35
9
50
Primeiro passo: I = 2 ** NP = 4 (22)
1 5 9
2 6 10
3 7 11
15 19 50
27 20 41
40 45 25
10
41
11 12
25 10


4 8 12
13 35 10
Aplicando inserção direta em cada segmento
15 19 50
20 27 41
 Obtém-se o vetor
1
15
2
20
3
25
4
10
5
19
6
27
25 40 45
7
40
8
13
9
50
10 13 35
10
41
11
45
12
35
Exemplo: NP = 2

Segundo passo: I = I DIV 2 = 2 (I/2)
ou NP = NP - 1, I = 2 ** NP = 2 (21)
Segmento 1
1 3 5 7 9 11
15 25 19 40 50 45

Aplicando inserção direta em cada segmento:
15 19 25 40 45 50

Segmento 2
2 4 6 8 10 12
20 10 27 13 41 35
10 13 20 27 35 41
Obtém-se o vetor
1
15
2
10
3
19
4
13
5
25
6
20
7
40
8
27
9
45
10
35
11
50
12
41
Exemplo: NP = 2



1
10
Terceiro passo: I = I DIV 2 = 1
ou: NP = NP - 1, I = 2 ** NP = 1 (20)
Nesse último passo os elementos estão próximos das
suas posições finais, o que leva a um menor número de
trocas.
Aplicando inserção direta ao vetor obtido no passo
anterior obtém-se o vetor ordenado:
2
13
3
15
4
19
5
20
6
25
7
27
8
35
9
40
10
41
11
45
12
50
Algoritmo do Método Shell
Const TAM = 12
Tipos v = vetor[1..TAM] de inteiros
Var
vet: v
np, i, j, inc : inteiro
Início
Para i = 1 até TAM Faça
Leia (vet[i])
Fim-Para
Leia (np)
Para i = np até 0 Passo -1 Faça
inc = 2 ** i //2i
Para j = 1 até inc Faça
Método_Shell (vet, inc, j, TAM)
Fim-Para
Fim-Para
Fim.
Algoritmo do Método Shell
Procedimento Método_Shell (Ref vet, r, s, n)
Início
Var
i, j, k, temp: inteiro
achei: lógico
Para i = (s + r) até n passo r Faça
j = s
achei = FALSO
Enquanto (j < i) E (NÃO achei) Faça
Se vet[i] < vet[j] Então
achei = VERDADEIRO
Senão
j = j + r
Fim-Se
Fim-Enquanto
Se achei Então
temp = vet[i]
k = i + r
Enquanto k > (j - r) Faça
vet[k + r] = vet[k]
k = k - r
Fim-Enquanto
vet[j] = temp
Fim-Se
Fim-Para
Fim-Método_Shell
Ordenação por Troca




Durante o caminhamento no vetor, se dois elementos
são encontrados fora de ordem, suas posições são
trocadas
São realizadas comparações sucessivas de pares de
elementos
A estratégia de escolha dos pares de elementos
estabelece a diferença entre os dois métodos de
ordenação por troca.
Dois métodos principais:


Método da Bolha (Bubble Sort)
Método da Partição (Quick Sort)
Método da Bolha (Bubble Sort)



É um método bastante simples, porém lento
O nome bolha se deve ao fato de que os valores
flutuam até a sua correta posição como bolhas
O algoritmo é o seguinte:



A cada passo, cada elemento é comparado com o
próximo. Se o elemento estiver fora de ordem, a troca
é realizada;
Realizam-se tantos passos quantos necessários até
que não ocorram mais trocas.
Obs.: Logo no primeiro passo o maior valor vai
para o fim
Exemplo do Mecanismo do
Método Bolha
500
Passo 1: 85
Passo
Passo
Passo
Passo
Passo
2:
3:
4:
5:
6:
85
515
60
60
515
910
170
170
910
890
890
275
650
430
910
430
430
890
890
890
890
910
910
910
910
910
910
500
85 500
60
85
60 500
60
85 170
60
85 170
60
85 170
nenhuma troca
515
170
500
275
275
170
515
275
500
430
890
275
515
430
500
910
275
275
650
430
515
515
910
650
650
430
650
650
650
Algoritmo do Método Bolha
/*Nesse algoritmo, a cada passo, a variável k contém a última posição trocada.
Após esta, todas já estão classificadas.*/
Const
TAM = 20
Tipos
v = vetor[1..TAM] de inteiros
Var
vet: v
i, temp, lim, k: inteiro
troca: lógico
Início
Para i = 1 até TAM Faça
Leia (vet[i])
Fim-Para
troca = VERDADEIRO
lim = TAM - 1
Enquanto troca Faça
troca = FALSO
Para i = 1 até lim Faça
Se vet[i] > vet[i + 1] Então
temp = vet[i]
vet[i] = vet[i + 1]
vet[i + 1] = temp
k = i
troca = VERDADEIRO
Fim-Se
Fim-para
lim = k
Fim-Enquanto
Fim.
Método da Troca e Partição
(Quick Sort)




É o mais rápido entre os métodos apresentados
até o momento
E também o mais utilizado
Foi proposto por C. A. R. Hoare em 1962
Parte do princípio que é mais rápido classificar
dois vetores com n/2 elementos cada um, do
que um com n elementos (dividir um problema
maior em dois menores)
Descrição do Método Quick Sort


A parte mais delicada do método é o
particionamento do vetor
O vetor é particionado em três segmentos:



V[1], ..., V[i - 1]
(segmento 1)
V[i]
(segmento 2)
V[i + 1], ..., V[n]
(segmento 3)
A partição é realizada através da escolha
arbitrária de um elemento (V[i]) de modo que os
elementos no segmento 1 sejam menores, e os
elementos no segmento 3 sejam maiores do que
o elemento escolhido V[i]
Descrição do Método Quick Sort

Após a ordenação dos segmentos 1 e 3, tem-se
o vetor original classificado. O processo de
partição pode ser repetido para os segmentos 1
e3

Obs.: Quando um segmento apresenta um
número de elementos menor ou igual a M (um
número pré-estabelecido), aplica-se um método
simples de ordenação
Algoritmo do Quick Sort







Escolher arbitrariamente um elemento do vetor
(normalmente o meio) e colocá-lo em uma variável
auxiliar X;
Inicializar dois ponteiros I e J (I = 1 e J = n);
Percorrer o vetor a partir da esquerda até que se
encontre um V[I] >= X (incrementando o valor de I);
Percorrer o vetor a partir da direita até que se encontre
um V[J] <= X (decrementando o valor de J);
Trocar os elementos V[I] e V[J] (estão fora de lugar) e
fazer: I = I + 1 e J = J - 1;
Continuar esse processo até que I e J se cruzem em
algum ponto do vetor;
Após obtidos os dois segmentos do vetor através do
processo de partição, cada um é ordenado
recursivamente
Quicksort – Exemplo (1)
Quicksort – Exemplo (2)
Quicksort – Exemplo (3)
Quicksort – Exemplo (4)
Quicksort – Exemplo (5)
Quicksort – Exemplo (6)
Quicksort – Animação
Algoritmo em pseudocódigo
Procedimento QuickSort (esq, dir: inteiro)
Início
Var
x, i, j, aux: inteiro
i = esq
j = dir
x = v[(i + j) DIV 2]
Repita //Faça
Enquanto x > v[i] Faça
i = i + 1
Fim-Enquanto
Enquanto x < v[i] Faça
j = j - 1
Fim-Enquanto
Se i <= j Então
aux = v[i]
v[i] = v[j]
v[j] = aux
i = i + 1
j = j - 1
Fim-Se
Até Que i > j //Enquanto i < j
Se esq < j Então
QuickSort (esq, j)
Fim-Se
Se dir > i Então
QuickSort (i, dir)
Fim-Se
Fim.
Ordenação por Seleção




É realizada uma seleção sucessiva do menor (ou
maior) valor contido no vetor
A cada passo este menor (maior) valor é
colocado na sua posição correta
Repete-se o processo para o segmento que
contém os elementos não selecionados
Duas possibilidades:


Seleção direta
Seleção em árvore
Seleção Direta




A cada passo encontra-se o menor elemento
dentro do segmento com os elementos não
selecionados;
Troca-se este elemento com o primeiro elemento
do segmento;
Atualiza-se o tamanho do segmento (menos um
elemento);
Este processo é repetido até que o segmento
fique com apenas um elemento
Exemplo
19
10
10
10
10
10
10
10
25
25
13
13
13
13
13
13
10
19
19
15
15
15
15
15
18
18
18
18
17
17
17
17
35
35
35
35
35
18
18
18
17
17
17
17
18
35
19
19
15
15
15
19
19
19
35
25
13
13
25
25
25
25
25
35
TAM = 8
TAM = 7
TAM = 6
TAM = 5
TAM = 4
TAM = 3
TAM = 2
TAM = 1
Algoritmo da Seleção Direta
Const
TAM = 15
Tipos
v = vetor[1..TAM] de inteiros
Var
vet: v
i, j, temp, pos_menor: inteiro
Início
Para i = 1 até TAM Faça
Leia (vet[i])
Fim-Para
Para i = 1 até TAM - 1 Faça
pos_menor = i
Para j = i + 1 até TAM Faça
Se vet[j] < vet[pos_menor] Então
pos_menor = j
Fim-Se
Fim-Para
temp = vet[i]
vet[i] = vet[pos_menor]
vet[pos_menor] = temp
Fim-Para
Fim.
Seleção em Árvore (Heap Sort)

Utiliza uma estrutura de árvore binária para a
ordenação

A ordenação é realizada em duas fases
Seleção em Árvore (Heap Sort)
Fase 1

Monta-se uma árvore binária (heap) contendo todos os
elementos do vetor de forma que o valor contido em
qualquer nodo seja maior do que os valores de seus
sucessores. A árvore binária é estruturada no próprio
vetor da seguinte forma:



sucessor à esquerda de i: 2i + 1
sucessor à direita de i: 2i + 2
(se 2i + 1 < n)
(se 2i + 2 < n)
Transformação da árvore num heap: é realizada do
menor nível até a raiz, trocando-se cada nodo com o
maior de seus sucessores imediatos. Repete-se este
processo até cada nodo ser maior que seus sucessores
imediatos
Seleção em Árvore (Heap Sort)
Fase 2


Após a formação do heap segue-se a fase de
classificação propriamente dita, na qual o valor
que está na raiz da árvore (maior valor contido
na árvore) é colocado na sua posição correta,
trocando-o com o elemento de maior índice da
árvore (a árvore fica com 1 elemento a menos).
Este novo elemento colocado na raiz pode violar
a propriedade do heap, de modo que deve-se
restaurar o heap novamente. Este procedimento
é repetido até que a árvore fique com um único
elemento.
Exemplos de Heaps
98
98
66
10
10
66
98
98
80
66
98
44
24
32
11
66
Exemplos de Não Heaps
98
12
44
66
24
78
11
66
98
10
Heap – Exemplo (1)
Entrada:
Heap:
Heap – Exemplo (2)
Heap – Exemplo (3)
Heap – Exemplo (4)
Heap – Exemplo (5)
Comparação entre os Métodos


São apresentados quadros comparativos do
tempo gasto na ordenação de vetores com 500,
5000, 10000 e 30000 elementos, organizados de
forma aleatória, em ordem crescente
(1, 2, 3, 4, ..., n) e em ordem
decrescente (n, n-1, n - 2, ..., 1)
Em cada tabela, o método que levou menos
tempo para realizar a ordenação recebeu o valor
1 e os demais receberam valores relativos ao
mais rápido (valor 1)
Comparação entre os Métodos
500
Inserção 11.3
Shell
1.2
Quick
1
Seleção 16.2
Heap
1.5

5000 10000 30000
87
161
1.6
1.7
2
1
1
1
124 228
1.6
1.6
1.6
Ordem Aleatória
500
Inserção
1
Shell
3.9
Quick
4.1
Seleção 128
Heap
12.2
5000 10000 30000
1
1
1
6.8
7.3
8.1
6.3
6.8
7.1
1524 3066
20.8 22.4 24.6
Ordem Ascendente
Ordem Descendente


500
Inserção 40.3
Shell
1.5
Quick
1
Seleção 29.3
Heap
2.5
5000 10000 30000
305 575
1.5
1.6
1.6
1
1
1
221 417
2.7
2.7
2.9
Recomendações




Tamanho <= 50
Inserção
Tamanho <= 5000
Shell Sort
Até 1000 elementos o Shell é mais vantajoso
Tamanho > 5000
Quick Sort


Necessita de memória adicional por ser recursivo. Evitar
chamadas recursivas para pequenos intervalos. Colocar um
teste antes da recursividade (se n <= 50 inserção; se n <=
1000, shell sort)
Heap Sort

De 2 a 3 vezes mais lento que o quick sort. Seu tempo é
sempre  n log n, não importando a ordem dos elementos. Esse
método deve ser utilizado quando as aplicações não podem
tolerar eventuais variações no tempo esperado para ordenação
Download

I = 1 - Objetivo Sorocaba