Operações sobre Estruturas de Dados
Ordenamento de Listas
Objectivo: ordenar listas de acordo com a relação gt(X, Y)
Ordenamento por Borbulhagem (Bubble Sort)
6 2 2 1 1 2 5 1 2 2 5 1 4 3 3 1 4 3 4 4 4 3 5 5 5 3 6 6 6 6 borbulhagem(Lista,
ListaOrd):-faz_trocas(Lista, Lista1), !, % Alguma troca? borbulhagem(Lista1, ListaOrd).
borbulhagem(ListaOrd, ListaOrd). % Se não, lista ordenada faz_trocas(Lista,
Lista1):-faz_trocas1(Lista, Lista1, T), not var(T). faz_trocas1([ ], [ ], _).
faz_trocas1([X, Y | Resto], [Y | Resto1], trocou):-gt(X, Y),
faz_trocas1([X | Resto], Resto1, _). faz_trocas1([X | Resto], [X | Resto1],
T):-© Amílcar Cardoso, DEI-UC P - 1 3 0
Ordenamento por Insersão
Para ordenar uma lista não-vazia [X | R], (i) ordenar a cauda R e (ii) inserir X numa posição da cauda que faça [X | R] ordenada
6 6 6 6 6 1 2 2 2 2 1 2 5 5 5 1 2 3 1 1 1 3 3 4 4 3 3 4 4 5 3 4 4 5 5 6 ord_insersao([ ], [ ]). ord_insersao([X | R], ListaOrd):-ord_insersao(R, ROrd),
insere_em_ordem(X, ROrd, ListaOrd). insere_em_ordem(X, [Y | R], [Y |
R1]):-gt(X, Y), !,
insere_em_ordem(X, R, R1). insere_em_ordem(X, R, [X | R]).
Quicksort
Para ordenar uma lista não-vazia L,
(i) apagar um elemento X de L e partir o resto de L em duas listas:
Pequenos, contendo os elementos de L menores ou iguais a X, e Grandes, contendo os elementos de L maiores que X;
(ii) ordenar Pequenos e Grandes, obtendo respectivamente PeqOrd e GrandOrd;
(iii) a concatenação de PeqOrd com [X | GrandOrd] resulta na lista L ordenada. 7 8 7 6 3 1 4 1 3 4 6 7 7 8 1 3 4 5 6 7 7 8 3 7 8 1 4 7 6 5 5 3 7 8 1 4 7 6 Apaga elemento Partição Ordenação Ordenação Concatenação
© Amílcar Cardoso, DEI-UC P - 1 3 2 Um programa para o quicksort:
o Partição: partir(X, R, Pequena, Grande) é verdade se a Pequena é a lista
contendo os elementos de R de valor inferior a X e Grande é a lista que contém os restantes elementos de R
partir(_, [ ], [ ], [ ]).
partir(X, [Y | R], [Y | Pequena], Grande):-gt(X, Y), !,
partir(X, R, Pequena, Grande).
partir(X, [Y | R], Pequena, [Y | Grande]):-partir(X, R, Pequena, Grande).
o Predicado central: quicksort(Lista, ListaOrd).
quicksort([ ], [ ]).
quicksort([X | R],
LOrd):-partir(X, R, Pequena, Grande), quicksort(Pequena, PeqOrd), quicksort(Grande, GrandOrd),
Recorrendo a diferença de listas: Substituições:
[ ] ý L-L PeqOrd ý P-P1 GrandOrd ý G-G1
A concatenação de PeqOrd com [X | GrandOrd] corresponde à concatenação de P-P1 com [X | G]-G1, resultando
P-G1, com P1 = [X | G] Substituindo sistematicamente: quicksort(Lista, ListaOrd):-quicksort_dl(Lista, ListaOrd-[ ]). quicksort_dl([ ], L-L). quicksort_dl([X | R], P-G1):-partir(X, R, Pequena, Grande), quicksort_dl(Pequena, P-[X | G]), quicksort_dl(Grande, G-G1).
© Amílcar Cardoso, DEI-UC P - 1 3 4
Mergesort
Para ordenar uma lista não-vazia L,
(i) dividir L em duas listas L1 e L2, de tamanhos aproximadamente
iguais;
(ii) ordenar L1 e L2, obtendo respectivamente Ord1 e Ord2;
(iii) fundir as listas ordenadas Ord1 e Ord2, obtendo a lista L ordenada.
Partição Ordenação Ordenação Fusão 5 3 7 8 1 4 7 6 5 3 7 8 1 4 7 6 3 5 7 8 1 4 6 7 1 3 4 5 6 7 7 8
Representação de Conjuntos: Árvores Binárias
Inconveniente da representação por Listas: relação de pertença ineficiente
Árvore binária:
Î
composta por uma raíz, uma sub-árvore esquerda (árvore binária) e uma sub-árvore direita (árvore binária)a d c b raíz sub-árvore direita sub-árvore esquerda
Necessário: um símbolo especial para representar a árvore vazia e um functor especial para construir uma árvore não-vazia a partir dos seus três componentes
Â
árvore vazia: átomo nilÂ
árvore não-vazia: t(E, R, D) representa a árvore de raíz R, sub-árvore esquerda E e sub-árvore direita DExemplo, para a árvore da figura:
© Amílcar Cardoso, DEI-UC P - 1 3 6 Relação de pertença - in/2:
in(X, t(_, X, _)). in(X, t(E, _,
_)):-in(X, E). in(X, t(_, _,
D)):-in(X, D).
Problema: tão ineficiente como para a representação por listas
Solução: impôr uma relação de ordem aos elementos do conjunto e representá-lo por um Dicionário Binário
Dicionário Binário
É uma árvore binária que, se fôr não-vazia, verifica as seguintes propriedades:
➎
todos os elementos da sub-árvore esquerda são menores que a raíz➎
todos os elementos da sub-árvore direita são maiores que a raíz➎
ambas as sub-árvores são dicionários bináriosVantagem:
Exemplo: 5 6 8 3 9 1 4 7
Relação de pertença - in/2:
in(X, t(_, X, _)). in(X, t(E, R, _)):-gt(R, X), in(X, E). in(X, t(_, _, D)):-gt(X, R), in(X, D).
A relação in/2 pode também ser usada para construir um dicionário binário:
?- in(5, D), in(3, D), in(8, D).
D = t( t( D1, 3, D2 ), 5, t( D3, 8, D4 ) ) 5 8 3 D1 D2 D3 D4 Árvore equilibrada
© Amílcar Cardoso, DEI-UC P - 1 3 8 Eficiência na procura:
Â
O tempo de procura de um elemento numa árvore equilibrada com n elementos é proporcional a log nÂ
O tempo de procura de um elemento numa lista com n elementos é proporcional a nmas…
Â
se a árvore não fôr equilibrada o tempo de procura aumentaÂ
na situação extrema de a árvore ser totalmente desequilibrada otempo de procura é proporcional a n
Exemplo:
?- in(3, D), in(5, D), in(8, D).
D = t( D1, 3, t( D2, 5, t( D3, 8, D4 ) ) ) 5 8 3 D1 D2 D3 D4
Interessa garantir que as árvores são construídas de forma a manterem-se aproximadamente equilibradas
Insersão e Remoção num Dicionário Binário
InsersãoA forma de insersão mais simples é aquela em que cada novo elemento passa a constituir uma folha da árvore
addleaf(D, X, D1) 5 8 3 5 6 8 3 5 6 8 3 7 5 6 8 3 4 7 Configuração inicial: D1 addleaf(D1, 6, D2) D2 addleaf(D2, 7, D3) D3 addleaf(D3, 4, D4) D4
addleaf(nil, X, t(nil, X, nil)).
addleaf(t(Esq, X, Dir), X, t(Esq, X, Dir)).
addleaf(t(Esq, Raiz, Dir), X, t(Esq1, Raiz, Dir)):-gt(Raiz, X),
addleaf(Esq, X, Esq1).
Dir1)):-© Amílcar Cardoso, DEI-UC P - 1 4 0 Remoção
o A remoção de uma folha é a operação inversa da de insersão de uma folha: delleaf(D1, X, D2) :- addleaf(D2, X, D1) o Remoção de um nó interno: A X Esq Dir remover X A ? Esq Dir Possível solução: X
Esq Dir Esq Y Dir
Y
Esq Dir1
remover X mover Y
Se uma das sub-árvores fôr vazia:
A X Esq remover X A Esq
Remoção de elemento de um dicionário binário:
del(Arvore, X, NovaArvore)
del( t(nil, X, Dir), X, Dir ). del( t(Esq, X, nil), X, Esq ).
del( t(Esq, X, Dir), X, t(Esq, Y, Dir1) ):-delmin(Dir, Y, Dir1).
del( t(Esq, Raiz, Dir), X, t(Esq1, Raiz, Dir) ):-gt(Raiz, X),
del(Esq, X, Esq1).
del( t(Esq, Raiz, Dir), X, t(Esq, Raiz, Dir1) ):-gt(X, Raiz),
del(Dir, X, Dir1).
Remoção do menor elemento de um dicionário binário:
delmin(Arvore, Y, NovaArvore)
delmin( t(nil, Y, Dir), Y, Dir ).
delmin( t(Esq, Raiz, Dir), Y, t(Esq1, Raiz, Dir) ):-delmin(Esq, Y, Esq1).
© Amílcar Cardoso, DEI-UC P - 1 4 2 Insersão não-determinística:
O novo nó é inserido num nível qualquer da árvore
Para inserir X num dicionário binário D,
(i) inserir X na raíz de D (X passa a ser a nova raíz de D)
ou
(ii) se a raíz de D é maior que X, insere X na sub-árvore esquerda de D; caso contrário, insere X na sub-árvore direita de D
Problema: insersão na raíz
¸
addroot(Arvore, X NovaArvore)Dir2 X Y Esq2 Dir inserir X, X < Y Esq1 X Y Esq Dir1 Y Esq Dir inserir X, Y < X
addroot( nil, X, t(nil, X, nil) ).
addroot( t(Esq, Y, Dir), X, t(Esq1, X, t(Esq2, Y, Dir)) ):-gt(Y, X),
addroot(Esq, X, t(Esq1, X, Esq2)).
addroot( t(Esq, Y, Dir), X, t( t(Esq, Y, Dir1), X, Dir2) ):-gt(X, Y),
Insersão não-determinística num dicionário binário:
add(Arvore, X, NovaArvore)
add(Arvore, X, NovaArvore):-addroot(Arvore, X, NovaArvore).
add( t(Esq, Raiz, Dir), X, t(Esq1, Raiz, Dir) ):-gt(Raiz, X),
add(Esq, X, Esq1).
add( t(Esq, Raiz, Dir), X, t(Esq, Raiz, Dir1) ):-gt(X, Raiz),
add(Dir, X, Dir1).
*
Notar a semelhança com del/3, para remoção de elemento de um dicionário binárioNa verdade, este predicado pode ser utilizado na direcção inversa, para apagar um elemento do dicionário
Por exemplo:
add(nil, 3, D1), add(D1, 5, D2), add(D2, 1, D), add(DD, 5, D)
constrói uma árvore D contendo os elementos 1, 3 e 5, removendo depois 5 para produzir a árvore DD