Você está na página 1de 667

Algoritmos Elementares

de Ordenação

Sedgewick: Capítulo 6

IAED, 2014/2015
Algoritmos Elementares de Ordenação

•  Selection Sort
•  Insertion Sort
•  Bubble Sort
•  Shell Sort

5 IAED, 2014/2015
Motivação

•  Porquê estudar algoritmos elementares ?

•  Razões de ordem prática


–  Fáceis de codificar e por vezes suficientes
–  Rápidos/eficientes para problemas de dimensão média e por
vezes os melhores em certas situações

•  Razões pedagógicas
–  Bom exemplo para aprender terminologia e contexto dos
problemas a codificar e por vezes suficiente
–  Alguns são fáceis de generalizar para algoritmos mais eficientes
ou para melhorar o desempenho de outros algoritmos

6 IAED, 2014/2015
Análise de Desempenho

•  Parâmetro de interesse - tempo de execução


2
–  regra: tempo O( N ) para ordenar N items
–  mas se N for pequeno podem ser os melhores

•  Mas desempenho em memória também interessa


–  ordenação in-place
–  utilizando memória adicional

7 IAED, 2014/2015
Algoritmo Estável

•  Um algoritmo de ordenação é dito estável se preserva a


ordem relativa dos items com chaves repetidas
–  ex: ordenar lista de alunos, já previamente ordenada por nome,
por ano de graduação

•  Algoritmos elementares são normalmente estáveis, mas


poucos algoritmos avançados o são

8 IAED, 2014/2015
Algoritmo Interno

•  Um algoritmo de ordenação é dito interno se o conjunto


de todos os dados a ordenar couber em memória RAM;
caso contrário é dito externo

•  Distinção muito importante:


–  ordenação interna pode aceder a qualquer dado com um custo
muito pequeno
–  ordenação externa tem de aceder aos dados de forma
sequencial (ou em blocos)

•  Vamos estudar apenas algoritmos de ordenação interna

9 IAED, 2014/2015
Algoritmos de Ordenação - Implementação

•  Função sort() implementa o algoritmo de ordenação


•  Devolve tabela de int ordenada, entre as posições l e r

void sort(int a[], int l, int r);

int a[]
9 16 10
1 15
2 3 8
4 5 1
7 7 10
8 13 11 13
4 15
2 18 6

l r

10 IAED, 2014/2015
Algoritmos de Ordenação - Implementação

int main() {
int i, a[]={10,9,8,7,6,5,4,3,2,1};

sort(a, 0, 9);

for (i = 0; i < 10; i++)


printf("%3d ", a[i]);
printf("\n");

return 0;
}

11 IAED, 2014/2015
Algoritmos de Ordenação – outro exemplo

#define N 10000
Número aleatório Número aleatório
int main() { (int) entre 0 e 999 (float) entre 0 e 1
int i, a[N];

for (i = 0; i < N; i++)


a[i] = 1000*(1.0*rand()/RAND_MAX);

sort(a, 0, N-1);
Resta-nos definir a
for (i = 0; i < N; i++) função sort 
printf("%3d ", a[i]);
printf("\n");
return 0;
}

12 IAED, 2014/2015
Selection Sort
l
•  Para cada elemento i entre as
posições l e r
–  Procura o menor elemento entre i e r
–  Se o menor valor — guardado na
posição min — for menor que o valor
guardado na posição i,
•  troca o a[i] com o a[min]

13 IAED, 2014/2015
Selection Sort

•  A cada passo, escolher o menor entre os r−l+1−i


maiores elementos
•  Para cada i, os primeiros i elementos ficam já na sua
posição final
•  Para cada valor de i do primeiro ciclo, segundo ciclo é
executado r−i vezes
r−l+1−i

9 16 1 2 3 8 5 10 7 13 11 4 15 18 6

l i min r
troca o a[i] com o a[min]

14 IAED, 2014/2015
Selection Sort

•  A cada passo, escolher o menor entre os r−l+1−i


maiores elementos
•  Para cada i, os primeiros i elementos ficam já na sua
posição final
•  Para cada valor de i do primeiro ciclo, segundo ciclo é
executado r−i vezes
r−l+1−i

9 16 1 2 3 4 5 10 7 13 11 8 15 18 6

l i min r
troca o a[i] com o a[min]

15 IAED, 2014/2015
Selection Sort

•  A cada passo, escolher o menor entre os r−l+1−i


maiores elementos
•  Para cada i, os primeiros i elementos ficam já na sua
posição final
•  Para cada valor de i do primeiro ciclo, segundo ciclo é
executado r−i vezes
r−l+1−i

9 16 1 2 3 4 5 10 7 13 11 8 15 18 6

l i r

16 IAED, 2014/2015
Exercício
•  Considere o seguinte vector
v[ ] = {72, 29, 38, 22, 60, 2}
Indique o conteúdo de v no final de cada passo do algoritmo
selection sort.

Inicio: 72 29 38 22 60 2
Passo 1: 2 29 38 22 60 72
Passo 2: 2 22 38 29 60 72
Passo 3: 2 22 29 38 60 72
Passo 4: 2 22 29 38 60 72

Final: 2 22 29 38 60 72

17 IAED, 2014/2015
Selection Sort

void SelectionSort(int a[], int l, int r)


{
int i, j;
for (i = l; i < r; i++) Procura o mínimo
{
int aux,min = i;
for (j = i+1; j <= r; j++)
if (a[j] < a[min])
min = j; Troca elementos

aux = a[i]; a[i] = a[min]; a[min] = aux;


}
}
18 IAED, 2014/2015
Selection Sort

void SelectionSort(int a[], int l, int r)


{ Estas variáveis
int i, j; apenas existem
for (i = l; i < r; i++) dentro deste bloco
{
int aux,min = i;
for (j = i+1; j <= r; j++)
if (a[j] < a[min])
min = j;

aux = a[i]; a[i] = a[min]; a[min] = aux;


}
}
19 IAED, 2014/2015
Algoritmos de Ordenação – Abstrações Úteis

•  Items ordenados por uma “chave”


•  Características específicas de cada item ou chave
•  No entanto cada algoritmo tem comportamento igual :
Logo, vamos usar abstrações!!
A característica do “A”
que será utilizada para a
ordenação será, neste
typedef int Item; caso, o próprio A
#define key(A) (A)
#define less(A, B) (key(A) < key(B))
#define exch(A, B) { Item t = A; A = B; B = t; }
#define compexch(A, B) if (less(B, A)) exch(A, B)

20 IAED, 2014/2015
Algoritmos de Ordenação – Abstrações Úteis

•  Função sort() implementa o algoritmo de ordenação


•  Devolve tabela de Item ordenada, entre as posições l e r

void sort(Item a[], int l, int r);

Item a[]
9 16 10
1 15
2 3 8
4 5 1
7 7 10
8 13 11 13
4 15
2 18 6

l r

21 IAED, 2014/2015
Selection Sort

void selection(Item a[], int l, int r)


{
int i, j;
for (i = l; i < r; i++) {
int min = i;
for (j = i+1; j <= r; j++)
if (less(a[j], a[min]))
min = j;
exch(a[i], a[min]);
}
}

22 IAED, 2014/2015
Algoritmos de Ordenação – Abstrações Úteis
#define N 10000
typedef int Item;
#define key(A) (A)
#define less(A, B) (key(A) < key(B))
#define exch(A, B) { Item t = A; A = B; B = t; }
#define compexch(A, B) if (less(B, A)) exch(A, B)

void selection(Item a[], int l, int r);

int main() {
int i, a[N];

for (i = 0; i < N; i++)


a[i] = 1000*(1.0*rand()/RAND_MAX);

selection(a, 0, N-1);
(...)
}
23 IAED, 2014/2015
Exercício
•  Considere o seguinte vector
a[ ] = {22, 33, 1, 10, 11, 2}
Indique o conteúdo de a depois de 3 iterações do algoritmo
selection sort.

Inicio: 22 33 1 10 11 2
1 33 22 10 11 2
1 2 22 10 11 33
R: 1 2 10 22 11 33

24 IAED, 2014/2015
Selection Sort

•  Tempo de execução:
–  Comparações: N 2 / 2 , trocas: N
2
–  No pior caso é O( N ) , com N = r−l
2
–  No melhor caso, é O( N ) , com N = r−l

•  O algoritmo é estável?
–  Será que a ordem relativa de chaves duplicadas é mantida?

{ 2 2 1 3}
{ 1 2 2 3}
Alterámos a ordem relativa de
25 chaves duplicadas! IAED, 2014/2015
Selection Sort

•  Tempo de execução:
–  Comparações: N 2 / 2 , trocas: N
2
–  No pior caso é O( N ) , com N = r−l
2
–  No melhor caso, é O( N ) , com N = r−l

•  O algoritmo não é estável


–  i.e. ordem relativa de chaves duplicadas não é mantida
–  Podemos alterar o algoritmo para que este fique estável.

26 IAED, 2014/2015
Insertion Sort (ideia geral)

l r

27 IAED, 2014/2015
Insertion Sort (algoritmo)

•  Para cada i, os primeiros i elementos ficam ordenados,


embora possam ainda não ficar na sua posição final
•  Isso significa que, se o vector já estiver ordenado,
fazemos apenas N-1 comparações!

9 16 1 3 8 15 5 10 7 13 11 4 2 18 6

l i r

9 16 1 3 5 8 15 10 7 13 11 4 2 18 6

l r
28 IAED, 2014/2015
Exercício
•  Considere o seguinte vector
v[ ] = {72, 29, 38, 22, 60, 2}
Indique o conteúdo de no final de cada passo do algoritmo
insertion sort.

Início:
* 72 29 38 22 60 2

*
Passo 1: 29 72 38 22 60 2

*
Passo 2: 29 38 72 22 60 2
Passo 3: 22 29 38 72 60 2
*
Passo 4: 22 29 38 60 72 2
*
Final: 2 22 29 38 60 72

29 IAED, 2014/2015
Insertion Sort

void insertion(Item a[], int l, int r)


Uso uma variável
auxiliar v onde guardo o
{
valor de a[i]
int i,j;
for (i = l+1; i <= r; i++) {
Item v = a[i]; O j vai percorrer o vector a
partir da posição i-1 até
j = i-1; encontrar um elemento
while ( j >= l && less(v, a[j])) { menor que v
a[j+1] = a[j];
j--;
}
Enquanto v<a[j] vou
a[j+1] = v; puxando os valores
} para a direita
} Quando deixar de ser,
significa que encontrei
o “slot” para o v

30 IAED, 2014/2015
Insertion Sort

•  Tempo de execução:
2
–  No pior caso é O( N ) , com N = r−l, i.e. vector já ordenado por
ordem inversa
–  No melhor caso é O(N ) , com N = r−l, i.e. vector já ordenado

•  Algoritmo é estável
–  i.e. ordem relativa de chaves duplicadas é mantida

31 IAED, 2014/2015
Exercício (Verdadeiro ou Falso)

•  O tempo de execução do InsertionSort é Ω(N).

•  O tempo de execução do InsertionSort é Θ(N2).

•  O número de trocas no algoritmo InsertionSort é O(N).

•  O número de trocas no algoritmo SelectionSort é O(N).

32 IAED, 2014/2015
Exercício (Verdadeiro ou Falso)

•  O tempo de execução do InsertionSort é Ω(N).


Verdadeiro.
O tempo de execução no melhor caso é Ω(N), logo o Insertion Sort é Ω(N).

•  O tempo de execução do InsertionSort é Θ(N2).


Falso.
Θ(N2) sse Ω(N2) e O(N2). Como o insertionSort não é Ω(N2) não pode ser Θ(N2).

•  O número de trocas no algoritmo InsertionSort é O(N).


Falso.
O número de trocas é O(N2).

•  O número de trocas no algoritmo SelectionSort é O(N).


Verdadeiro.
33 IAED, 2014/2015
Bubble Sort

l r

Em cada iteração i há sempre um elemento


(o r-i)
que fica na sua posição final

34 IAED, 2014/2015
Bubble Sort

•  Para cada valor de i no primeiro ciclo, o segundo ciclo é


executado r−i vezes
•  Para cada i, os últimos i elementos ficam já na sua
posição final
•  Só são efectuadas trocas entre elementos adjacentes

35 IAED, 2014/2015
Bubble Sort

void bubble(Item a[], int l, int r)


{
int i, j;
for (i = l; i < r; i++)
for (j = l; j < r-i; j++)
compexch(a[j], a[j+1]);
}

36 IAED, 2014/2015
Bubble Sort (direita para a esquerda)

•  Para cada valor de i no primeiro ciclo, o segundo ciclo é


executado r−i vezes
•  Para cada i, os primeiros i elementos ficam já na sua
posição final
•  Só são efectuadas trocas entre elementos adjacentes
i
9 16 1 2 3 10 15 4 8 5 7 11 13 18 6

9 16 1 2 3 10 15 4 5 8 7 11 13 18 6

9 16 1 2 3 10 4 15 5 8 7 11 13 18 6

9 16 1 2 3 4 10 15 5 8 7 11 13 18 6

move o menor número para posição i


37 IAED, 2014/2015
Bubble Sort (direita para a esquerda)

void bubble(Item a[], int l, int r)


{
int i, j;
for (i = l; i < r; i++)
for (j = r; j > i; j--)
compexch(a[j-1], a[j]);
}

Exercício: Será que não haverá maneira de reduzir o número de


iterações, em particular quando o vector já está parcialmente
ordenado?

38 IAED, 2014/2015
Bubble Sort (direita para a esquerda
+ condição de paragem)
void bubble(Item a[], int l, int r)
{
int i, j, done;
for (i = l; i < r; i++){
done=1;
for (j = r; j > i; j--)
if (less(a[j], a[j-1])){
exch(a[j-1], a[j]);
XXXXX ;
}
YYYYY;
}
}
39 IAED, 2014/2015
Bubble Sort (direita para a esquerda
+ condição de paragem)
void bubble(Item a[], int l, int r)
{
int i, j, done;
for (i = l; i < r; i++){
done=1;
for (j = r; j > i; j--)
if (less(a[j], a[j-1])){
exch(a[j-1], a[j]);
done=0;
}
YYYYY;
}
}
40 IAED, 2014/2015
Bubble Sort (direita para a esquerda
+ condição de paragem)
void bubble(Item a[], int l, int r)
{
int i, j, done;
for (i = l; i < r; i++){
done=1;
for (j = r; j > i; j--)
if (less(a[j], a[j-1])){
exch(a[j-1], a[j]);
done=0;
}
if (done) break;
}
}
41 IAED, 2014/2015
Exercício
•  Considere o seguinte vector
v[ ] = {72, 29, 38, 22, 60, 2}
Indique o conteúdo de v no final 2 passagens do algoritmo
bubble sort (esquerda para a direita).

Primeira passagem: 72 29 38 22 60 2
29 72 38 22 60 2
29 38 72 22 60 2
29 38 22 72 60 2
29 38 22 60 72 2
29 38 22 60 2 72

42 IAED, 2014/2015
Exercício
•  Considere o seguinte vector
v[ ] = {72, 29, 38, 22, 60, 2}
Indique o conteúdo de v no final 2 passagens do algoritmo
bubble sort (esquerda para a direita).

Segunda passagem: 29 38 22 60 2 72
29 38 22 60 2 72
29 22 38 60 2 72
29 22 38 60 2 72
29 22 38 2 60 72
29 38 22 60 2 72

43 IAED, 2014/2015
Comparação

•  Pior Caso
Selection Insertion Bubble
Comparações N2 / 2 N2 / 2 N2 / 2
Trocas Chaves N N2 / 2 N2 / 2

•  Caso Médio
Selection Insertion Bubble
Comparações N2 / 2 N2 / 4 N2 / 2
Trocas Chaves N N2 / 4 N2 / 2

44 IAED, 2014/2015
Comparação

•  Tabelas com poucos elementos fora de ordem


–  Insertion e Bubble Sort são quase lineares
–  os melhores algoritmos de ordenação podem ser quadráticos
•  Contexto onde elementos são grandes e chaves
pequenas
–  Selection Sort é linear no número de dados
–  N dados com tamanho M palavras
–  custo comparação: 1; custo troca: M
–  N 2 / 2 comparações e NM custo de trocas
–  termo NM domina: custo proporcional ao tempo necessário
para mover os dados
•  Alternativa: uso de ponteiros (que tb vamos aprender!)
45 IAED, 2014/2015
Avaliação Experimental
N Selection Insertion Bubble
1000 5 4 11
2000 21 15 45
4000 85 62 182

•  Bubble/Insertion Sort são lentos: trocas ocorrem apenas


entre items adjacentes
–  se o menor item está no final da tabela, serão precisos N
passos para o colocar na posição correcta
•  Shellsort:
–  acelerar o algoritmo permitindo trocas entre elementos que
estão afastados
–  bubble/insertion sort, mas com elementos distanciados de h
46 IAED, 2014/2015
Shell Sort - Definições

•  Vector diz-se h-ordenado se qualquer sequência de


números separados por h posições está ordenada

1 15 2 16 3 17 4 18 5 19 11 22 18 2-ordenado

•  Vector é equivalente a h sequências ordenadas


entrelaçadas

1 2 3 4 5 11 18
15 16 17 18 19 22

•  O resultado de h-ordenar um vector que está k-ordenado,


é um vector que está h-ordenado e k-ordenado
47 IAED, 2014/2015
Shell Sort - Ideia
•  Rearranjar os dados de forma a que estejam h-ordenados
•  Usando valores de h grandes é possível mover elementos
na tabela distâncias grandes

•  Ordenar primeiro para valores de h grandes e depois para


valores de h pequenos
–  facilita as últimas ordenações, para valores de h pequenos

•  Utilizando este procedimento para qualquer sequência de


h’s que termine em 1 produz no final uma tabela ordenada

•  Cada passo torna o próximo mais simples


48 IAED, 2014/2015
Shell Sort
void shellsort(Item a[], int l, int r) gera sequência
{ h: 1 4 13 40 121
int i, j, h; 364 1093 3280 ...
for (h = 1; h <= (r-l)/9; h = 3*h+1)
;
executado para
for ( ; h > 0; h /= 3)
valores de h, por
for (i = l+h; i <= r; i++) {
ordem inversa
int j = i;
Item v = a[i];
while (j >= l+h && less(v, a[j-h])) {
a[j] = a[j-h]; InsertionSort
j -= h; com saltos de
} tamanho h
a[j] = v; (para h=1, obtemos o
} método original)
}

49 IAED, 2014/2015
Shell Sort
h=3
19 2 15 18 4 11 17 18 5 1 3 22 16
i
18 2 15 19 4 11 17 18 5 1 3 22 16
i
18 2 15 19 4 11 17 18 5 1 3 22 16
i
18 2 11 19 4 15 17 18 5 1 3 22 16
i
17 2 11 18 4 15 19 18 5 1 3 22 16
i
17 2 11 18 4 15 19 18 5 1 3 22 16
i
50 IAED, 2014/2015
Shell Sort
h=3
17 2 5 18 4 11 19 18 15 1 3 22 16
i
1 2 5 17 4 11 18 18 15 19 3 22 16
i
1 2 5 17 3 11 18 4 15 19 18 22 16
i
1 2 5 17 3 11 18 4 15 19 18 22 16
i
1 2 5 16 3 11 17 4 15 18 18 22 19
i
1 16 17 18 19 3-ordenado
2 3 4 18
5 11 15 22
51 IAED, 2014/2015
Shell Sort - Funcionamento

•  Operação (vector com tamanho 150):


–  Para cada valor de h, 40, 13, 4, 1:
•  Utilizar Insertion sort para criar h sub-vectores ordenados dentro de
vector com tamanho 150
–  Vector fica h-ordenado
–  Para h = 40 existem 40 sub-vectores ordenados, cada um com
3/4 elementos
–  Para h = 13 existem 13 sub-vectores ordenados, cada um com
11/12 elementos
–  ...
–  Para h = 1 existe 1 (sub-)vector ordenado, com 150 elementos

52 IAED, 2014/2015
Escolha da Sequência de Ordenação
elementos nas posições
pares não são comparados
•  Questão difícil de responder com elementos nas
•  Propriedades de muitas sequênciasposições
já foramímpares
estudadas
até ao
passo final
•  Possível provar que umas melhores que outras
–  1, 4, 13, 40, 121, 364, 1093, 3280, ... (Knuth, 3 N + 1 )
–  melhor que 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, ...(Shell, 2i )
Porquê?
–  mas pior (20%) que 1, 8, 23, 77, 281, 1073, ... ( 4i +1 + 3 × 2i + 1 )
•  Na prática utilizam-se sequências que decrescem
geometricamente para que o número de incrementos
seja logarítmico
•  A sequência óptima ainda não foi descoberta

53 IAED, 2014/2015
Shell Sort - Complexidade

•  Análise rigorosa da complexidade do algoritmo é


desconhecida

•  Complexidade depende da sequência de valores h utilizada

–  Sequência 1, 4, 13, 40, 121, 364, ... ~ O( N 3/2 ) comparações

–  Sequência 1, 8, 23, 77, 281, 1073, ... ~ O( N 4/3 ) comparações

–  Sequência 1, 2, 3, 4, 6, 9, 8, 12, 18, ... ~ O( N (log N ) 2 )


comparações

54 IAED, 2014/2015
Shell Sort - Avaliação Experimental

N O K G S P I
12500 16 6 6 5 6 6
25000 37 13 11 12 15 10
50000 102 31 30 27 38 26
100000 303 77 60 63 81 58
200000 817 178 137 139 180 126

–  O: 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, ...


–  K: 1, 4, 13, 40, 121, 364, ...
–  G: 1, 2, 4, 10, 23, 51, 113, 249, 548, ...
–  S: 1, 8, 23, 77, 281, ...
–  P: 1, 7, 8, 49, 56, 64, 343, 392, 448, 512, ...
–  I: 1, 5, 19, 41, 109, 209, 505, 929, ...
55 IAED, 2014/2015
Exercício
void shellsort(Item a[], int l, int r) Suponha que a função shellsort é
{ invocada com os seguintes
int i, j, h; argumentos:
for (h = 1; h <= (r-l)/9; h = 3*h+1)
;
for ( ; h > 0; h /= 3) a = { 16, 8, 4, 19, 20, 5,
for (i = l+h; i <= r; i++) { 13, 11, 6, 12 },
int j = i; l = 0, r = 9
Item v = a[i];
while (j >= l+h && less(v, a[j-h])) Indique qual o conteúdo do vector a
{ durante a execução da função
a[j] = a[j-h]; shellsort após o primeiro valor de h ter
j -= h; sido considerado.
}
a[j] = v;
}
}

56 IAED, 2014/2015
Começa com h=1,
gera sequência h: 1 4
depois vai para h=4 e
Exercício 13 40 121 364 1093
pára... Logo o primeiro
3280 ...
valor de h será 4
void shellsort(Item a[], int l, int r) Suponha que a função shellsort é
{ invocada com os seguintes
int i, j, h; argumentos:
for (h=1; h <= (r-l)/9; h = 3*h+1)
;
a = { 16, 8, 4, 19, 20, 5,
for ( ; h > 0; h /= 3)
for (i = l+h; i <= r; i++) {
13, 11, 6, 12 },
int j = i;
l = 0, r = 9
Item v = a[i];
while (j >= l+h && less(v, a[j-h])) Indique qual o conteúdo do vector a
{ durante a execução da função
a[j] = a[j-h]; shellsort após o primeiro valor de h ter
j -= h; sido considerado.
}
a[j] = v;
}
} Agora resta-me aplicar
o insertion sort com
57 h=4. Como faço isso? IAED, 2014/2015
Exercício

a = { 16, 8, 4, 19, 20, 5, 13, 11, 6, 12 }

h=4

16, 8, 4, 19, 20, 5, 13, 11, 6, 12

Resultado:
6, 5, 4, 11, 16, 8, 13, 19, 20, 12

O Shell Sort é estável? NÃO!!

58 IAED, 2014/2015
Algoritmos Eficientes
de Ordenação

Sedgewick: Capítulo 6

IAED, 2014/2015
Algoritmos Eficientes de Ordenação

•  Quick Sort
•  Merge Sort
•  Heap Sort

Utilizar informação das chaves:


•  Counting Sort
•  Radix Sort

2 IAED, 2014/2015
Quick Sort

IAED, 2014/2015
Quick Sort - Introdução

•  Inventado nos anos 60 por A.R. Hoare


•  Vantagens
–  muito estudado e analisado
–  popular devido à facilidade de implementação e eficiência
–  O( N lg N ) , em média, para ordenar N objectos
–  ciclo interno muito simples e conciso
•  Inconvenientes
2
–  não é estável; O( N ) no pior caso!
–  frágil: qualquer pequeno erro de concretização pode não ser
detectado mas levar a ineficiência
•  Biblioteca C fornece uma concretização: qsort()

4 IAED, 2014/2015
Quick Sort - Introdução

•  Aplica método dividir para conquistar para ordenar

•  Ideia chave: efectuar partição dos dados e ordenar as


várias partes independentemente (de forma recursiva)
–  particionar os dados: menores para um lado, maiores para outro
–  usar recursão e aplicar algoritmo a cada uma das partes
–  processo de partição é crítico para evitar partições degeneradas

5 IAED, 2014/2015
Quick Sort - ideia

3 5 10 8 11 1 2 4 14 13 7

X X X X X P O O O O O
i

Todos os X’s < P e


Todos os O’s > P
void quicksort(Item a[], int l, int r)
P fica na posição final
{
int i; •  Volto a aplicar o mesmo
if (r <= l) return; algoritmo aos XX e aos
i = partition(a, l, r); OO’s
quicksort(a, l, i-1); •  Quando não pudermos
quicksort(a, i+1, r); dividir mais paramos
}

6 IAED, 2014/2015
Quick Sort - Partição
pivot

9 16 3 5 10 8 11 1 2 4 14 13 7 18 6
i j

•  2 iteradores: o i (esquerda) e o j (direita, mas antes do pivot)


•  Avanço com i (para a direita) até encontrar um elemento maior
que o pivot, ou seja, que deveria estar no lado direito do vector.
•  Avanço com j (para a esquerda) até encontrar um elemento
menor que o pivot, ou seja, que deveria estar no lado
esquerdo do vector.
•  Troco a[i] com a[j]
•  Repito estes passos até que i cruze com j.
•  Nessa altura, troco o pivot com o a[i], colocando-o na divisão
entre o “lado” esquerdo e o lado “direito” do vector.
7 IAED, 2014/2015
Quick Sort - Partição
pivot

9 16 3 5 10 8 11 1 2 4 14 13 7 18 6
i j

9 16 3 5 4 8 11 1 2 10 14 13 7 18 6
i j
9 16 3 5 4 2 11 1 8 10 14 13 7 18 6
i j

9 16 3 5 4 2 1 11 8 10 14 13 7 18 6
i j

9 16 3 5 4 2 1 7 8 10 14 13 11 18 6

8 IAED, 2014/2015
Quick Sort - Partição

•  Recebe vector a e os limites da parte a ordenar, l e r


•  Rearranja os elementos do vector de forma a que as quatro
condições seguintes sejam válidas
–  o elemento a[i], para algum i, fica na sua posição final (i.e., o
pivot)
–  nenhum dos elementos de a[l] a a[i − 1] é maior do que a[i]
–  nenhum dos elementos de a[i + 1] a a[r] é menor do que a[i]
–  processo coloca pelo menos um elemento na sua posição final

•  Após partição, a tabela fica sub-dividida em duas partes que


podem ser ordenadas independentemente aplicando o
mesmo processo
9 IAED, 2014/2015
Enquanto o iterador
Quick Sort - Partição da esquerda i for
menor que o iterador
v = a[r] é o pivot!! da direita j....

int partition(Item a[], int l, int r) { Enquanto a[i]<v


int i = l-1; avança com o i para
int j = r; a direita.
Item v = a[r];
Enquanto v<a[j]
while (i < j) {
avança com o j para
while (less(a[++i], v)); a esquerda.
while (less(v, a[--j]))
if (j == l) Protege do caso em
break; que o elemento onde
se faz a partição está
if (i < j)
na 1ª posição
exch(a[i], a[j]);
} Faz a troca...
exch(a[i], a[r]);
return i; Coloca o pivot na
} posição i, de forma a
Retorna o ponto onde partiu que pelo menos este
o vector, para que esta elemento fique na pos
10
informação seja usada na final
IAED, 2014/2015
função quicksort
Quick Sort - Recursão

•  Ordenação realizada através de partição e aplicação


recursiva do algoritmo aos dois subconjuntos de dados
daí resultantes
void quicksort(Item a[], int l, int r)
{
int i;

if (r <= l)
return;

i = partition(a, l, r);
quicksort(a, l, i-1);
quicksort(a, i+1, r);
}

11 IAED, 2014/2015
Quick Sort - Recursão
9 16 3 5 4 2 1 7 8 10 14 13 11 18 6

9 16 1
3 5 4 2 1
3 7 8 10 14 13 11 18 6

9 16 1 5
2 4
3 2
5 3
4 7 8 10 14 13 11 18 6

9 16 1 2 3 5
4 4
5 7 8 10 14 13 11 18 6

9 16 1 2 3 4 5 7 8 10 14
11 13 14
11 18 6

9 16 1 2 3 4 5 7 8 10 11 13 14 18 6

9 16 1 2 3 4 5 7 8 10 11 13 14 18 6

9 16 1 2 3 4 5 7 8 10 11 13 14 18 6

12 IAED, 2014/2015
Quick Sort | função partição | Exercício (5 min!!)
Considere a função int partition (Item a[], int l, int r) usada no algoritmo quicksort.

int partition(Item a[], int l, int r)


{
int i = l-1
int j = r;
Item v = a[r]; Suponha que a função partition é invocada com
while (i < j) { os seguintes argumentos:
while (less(a[++i], v));
while (less(v, a[--j])) a = { 10, 2, -2, 13, 14, -1, 7, 5, 0, 6 },
if (j == l) l = 0, r = 9
break; Indique qual o conteúdo do vector a após a execução da
if (i < j) função partition.
exch(a[i], a[j]);
}
exch(a[i], a[r]);
return i;
}

13 IAED, 2014/2015
Quick Sort | função partição | Exercício
•  Pivot = v = a[r] = 6
10, 2, -2, 13, 14, -1, 7, 5, 0, 6
i j
•  Troco o 10 com o 0
0, 2, -2, 13, 14, -1, 7, 5, 10, 6
i j

•  Troco o 13 com o 5

0, 2, -2, 5, 14, -1, 7, 13, 10, 6


i j

•  Troco o 14 com o -1
14 IAED, 2014/2015
Quick Sort | função partição | Exercício

0, 2, -2, 5, -1, 14, 7, 13, 10, 6


i
j
•  Como i>=j, nada acontece e saímos do ciclo
while. Agora trocamos o elemento na posição i (o
14) com o pivot (exch(a[i], a[r])), ficando

0, 2, -2, 5, -1, 6, 7, 13, 10, 14


i

•  Retorno o i (neste caso o índice l+5)


15 IAED, 2014/2015
Quick Sort - Complexidade

•  Pior caso: vector já está ordenado


–  Cerca de N 2 / 2 comparações
–  Se o vector já estiver ordenado, todas as partições degeneram e
a função chama-se a si própria N vezes;
–  O número de comparações é
( N + 1) N
N + ( N − 1) + ( N − 2) + K + 2 + 1 =
2
–  Mesma situação se o vector estiver ordenado por ordem inversa

•  Não apenas o tempo necessário para a execução do


algoritmo cresce quadraticamente como o espaço
necessário para o processo recursivo é de cerca de N o
que é inaceitável para vectores grandes
16 IAED, 2014/2015
Quick Sort - Complexidade

•  Melhor caso: quando cada partição divide o vector de


entrada em duas metades iguais
–  Número de comparações usadas por quicksort satisfaz a
recursão de dividir para conquistar: CN=2CN/2+N
–  Solução: CN ≈ N lg N

•  Propriedade: QuickSort efectua cerca de CN ≈ 2N lg N


comparações em média

17 IAED, 2014/2015
Quick Sort - Melhorias

•  Algoritmo pode ainda ser melhorado com alterações triviais


•  Ordenação de sub-vectores de pequenas dimensões pode
ser efectuada de forma mais eficiente
–  Natureza recursiva de QuickSort garante que uma fracção grande
dos sub-vectores terão tamanho pequeno
•  Como escolher correctamente o elemento de partição?
–  Aleatoriamente
–  Média de vários elementos
•  Como melhorar o desempenho se os dados tiverem um
grande número de chaves repetidas?

menor que v igual a v maior que v


l j i r
18 IAED, 2014/2015
Quick Sort - Melhorias

•  QuickSort é garantido instanciar-se a si próprio múltiplas


vezes para vectores pequenos!
•  Conveniente utilizar o melhor método possível nesta
situação: insertion sort
void quicksort(Item a[], int l, int r)
{
int i; if(r-l <= M) {
insertion(a, l, r)
if (r <= l)
return; return;
}
i = partition(a, l, r);
quicksort(a, l, i-1);
quicksort(a, i+1, r);
}

19 IAED, 2014/2015
III.c) Considere a função int partition (Item a[], int l, int r) usada no algoritmo
quicksort.
int partition(Item a[], int l, int r) {
int i = l-1, j = r;
Item v = a[r];
while (i < j) {
while (less(a[++i], v)); Exercício
while (less(v, a[--j]))
if (j == l)
break;
if (i < j)
exch(a[i], a[j]);
}
exch(a[i], a[r]);
return i;
}
Esta função recebe o vector a e as posições l e r que definem, respectivamente, os ı́ndices
limite esquerdo e direito do vector a considerar na função. Suponha que a função partition
é invocada com os seguintes argumentos:
a = {8, 1, -2, 10, 12, -7, 7, 11, 0, 5}, l = 0, r = 9
Indique qual o conteúdo do vector a após a execução da função partition e indique o valor
IAED, 2014/2015
de i retornado na ultima linha da função.
8, 1, -2, 10, 12, -7, 7, 11, 0, 5
(o pivot é o 5!)
o iterador i começa por parar no 8 e o j no 0 :
troco o 8 com o 0
0, 1, -2, 10, 12, -7, 7, 11, 8, 5
o iterador i pára no 10 e o j no -7: troco o 10 com o -7
0, 1, -2, -7, 12, 10, 7, 11, 8, 5
o iterador i pára no 12 e o j no -7: como o i>j já não troco
j i
0, 1, -2, -7, 12, 10, 7, 11, 8, 5
Resta-me portanto trocar o a[i] com o pivot, ficando:
resultado : 0, 1, -2, -7, 5, 10, 7, 11, 8, 12

...e a função partição deverá retornar o inteiro 4


IAED, 2014/2015
Merge Sort

IAED, 2014/2015
Merge Sort - ideia

Partir sucessivamente ao
meio o vector de
elementos a ordenar,
até obtermos vectores
com apenas um elemento

Aplicar sucessivamente o
procedimento de Merge,
para gerar um vector
ordenado a partir de dois
vectores ordenados

23 IAED, 2014/2015
Merge Sort - Merge

•  Gerar um vector ordenado a partir de dois vectores


ordenados é simples!

ordenado ordenado

9 16 1 3
2 3
5 5 11
8 10
4 7 28 10 7 13 14 18 6
4 11
l m r
Uso um vector
auxiliar + 2 iteradores:

aux 1 3 5 8 10 11 14 13 7 4 2
i j

24 IAED, 2014/2015
Devolve um vector ordenado,
Merge Sort - Merge em a[l..r], dados dois
vectores ordenados em
a[l..m] e a[m+1..r]
Item aux[maxN];

void merge(Item a[], int l, int m, int r)


{
int i, j, k;
for (i = m+1; i > l; i--)
aux[i-1] = a[i-1];
for (j = m; j < r; j++)
aux[r+m-j] = a[j+1];
for (k = l; k <= r; k++)
if (less(aux[j], aux[i]))
a[k] = aux[j--];
else
a[k] = aux[i++];
}

25 IAED, 2014/2015
Merge Sort - Merge

Item aux[maxN];

void merge(Item a[], int l, int m, int r)


{
int i, j, k; Constroi o vector
for (i = m+1; i > l; i--) auxiliar
aux[i-1] = a[i-1];
for (j = m; j < r; j++)
aux[r+m-j] = a[j+1];
for (k = l; k <= r; k++)
if (less(aux[j], aux[i]))
a[k] = aux[j--];
else
a[k] = aux[i++];
}

26 IAED, 2014/2015
Merge Sort - Merge

Item aux[maxN];

void merge(Item a[], int l, int m, int r)


{
int i, j, k;
for (i = m+1; i > l; i--)
aux[i-1] = a[i-1];
for (j = m; j < r; j++)
aux[r+m-j] = a[j+1];
Vai escolhendo os
for (k = l; k <= r; k++) elementos das pontas
if (less(aux[j], aux[i])) de forma a ordenar o
a[k] = aux[j--]; vector a[ ]
else
a[k] = aux[i++];
}

27 IAED, 2014/2015
Merge Sort (top-down version)

void mergesort(Item a[], int l, int r) {


int m = (r+l)/2;

if (r <= l)
return;

mergesort(a, l, m);
mergesort(a, m+1, r);
merge(a, l, m, r);
}

28 IAED, 2014/2015
Merge Sort
mergesort(a,0,10)
mergesort(a,0,5)
... void mergesort(Item a[],
mergesort(a,6,10)
... int l, int r)
{
int m = (r+l)/2;

if (r <= l)
return;

mergesort(a, l, m);
mergesort(a, m+1, r);
merge(a, l, m, r);
}

29 IAED, 2014/2015
Merge Sort
mergesort(a,0,10)
mergesort(a,0,5)
mergesort(a,0,2) void mergesort(Item a[],
...
mergesort(a,3,5) int l, int r)
...
mergesort(a,6,10) {
mergesort(a,6,8) int m = (r+l)/2;
...
mergesort(a,9,10)
... if (r <= l)
return;

mergesort(a, l, m);
mergesort(a, m+1, r);
merge(a, l, m, r);
}

31 IAED, 2014/2015
Merge Sort
mergesort(a,0,10)
mergesort(a,0,5)
mergesort(a,0,2) void mergesort(Item a[],
mergesort(a,0,1)
mergesort(a,0,0) int l, int r)
mergesort(a,1,1)
mergesort(a,2,2) {
mergesort(a,3,5) int m = (r+l)/2;
mergesort(a,3,4)
mergesort(a,3,3)
mergesort(a,4,4) if (r <= l)
mergesort(a,5,5)
mergesort(a,6,10) return;
mergesort(a,6,8)
mergesort(a,6,7)
mergesort(a,6,6) mergesort(a, l, m);
mergesort(a,7,7) mergesort(a, m+1, r);
mergesort(a,8,8)
mergesort(a,9,10) merge(a, l, m, r);
mergesort(a,9,9)
mergesort(a,10,10) }

32 IAED, 2014/2015
Merge Sort
mergesort(a,0,10)
mergesort(a,0,5)
mergesort(a,0,2) void mergesort(Item a[],
mergesort(a,0,1)
mergesort(a,0,0) int l, int r)
mergesort(a,1,1)
mergesort(a,2,2) {
mergesort(a,3,5) int m = (r+l)/2;
mergesort(a,3,4)
mergesort(a,3,3)
mergesort(a,4,4) if (r <= l)
mergesort(a,5,5)
mergesort(a,6,10) return;
mergesort(a,6,8)
mergesort(a,6,7)
mergesort(a,6,6) mergesort(a, l, m);
mergesort(a,7,7) mergesort(a, m+1, r);
mergesort(a,8,8)
mergesort(a,9,10) merge(a, l, m, r);
mergesort(a,9,9)
mergesort(a,10,10) }

33 IAED, 2014/2015
Merge Sort
0 1 2 3 4 5 6 7 8 9 10

9 16 3 5 10 8 11 1 2 4 14 13 7 18 6
9 16
mergesort(a,0,1) 3 5 10 8 11 1 2 4 14 13 7 18 6
9 16
mergesort(a,0,2) 3 5 10 8 11 1 2 4 14 13 7 18 6
9 16
mergesort(a,3,4) 3 5 10 8 11 1 2 4 14 13 7 18 6
9 16
mergesort(a,3,5) 3 5 10 1 8 11 2 4 14 13 7 18 6
9 16
mergesort(a,0,5) 1 3 5 8 10 11 2 4 14 13 7 18 6
9 16
mergesort(a,6,7) 1 3 5 8 10 11 2 4 14 13 7 18 6
9 16
mergesort(a,6,8) 1 3 5 8 10 11 2 4 14 13 7 18 6
9 16
mergesort(a,9,10) 1 3 5 8 10 11 2 4 14 7 13 18 6
9 16
mergesort(a,6,10) 1 3 5 8 10 11 2 4 7 13 14 18 6
9 16
mergesort(a,0,10)
34
1 2 3 4 5 7 8 10 11 IAED,
13 2014/2015
14 18 6
Merge Sort - Complexidade

•  Tempo de execução:

TN = T⎢⎣ N /2⎥⎦ + T⎡⎢ N /2⎤⎥ + O( N ) = O( N lg N )

•  Fácil de verificar quando N é potência de 2, e no caso


geral recorrendo a indução

•  Complexidade do pior caso é O( N lg N )

•  É estável

35 IAED, 2014/2015
II.b) Considere o seguinte função na linguagem C:
Exercício
void merge(int a[], int l, int m, int r)
{
int i, j, k;
int aux[MAXN];

for (i = m+1; i > l; i--)


aux[i-1] = a[i-1];

for (j = m; j < r; j++)


aux[r+m-j] = a[j+1];

for (k = l; k <= r; k++)


if (XXXXXXXX)
a[k] = aux[j--];
else
a[k] = aux[i++];
}

Qual a expressão que deve ser coloca em XXXXXXXX? Note que esta função ordena um vector a em que
inicialmente os valores nas posições de 0 a m estão ordenados por ordem crescente e os valores nas
posições de m+1 a r estão também ordenados por ordem crescente Ou seja, dadas duas sequências
de valores
36 ordenados, esta função realiza a união ordenada. IAED, 2014/2015
Qual a expressão
II.b) Considere o seguinte função na linguagem C:
que deve ser
Exercício
void merge(int a[], int l, int m, int inicialmente
r) os valores nas po
{
int i, j, k; posições de m+1 a r estão tam
int aux[MAXN];
de valores ordenados, esta fun
for (i = m+1; i > l; i--)
aux[i-1] = a[i-1];

for (j = m; j < r; j++)


Solução: aux[j] < aux[i]
aux[r+m-j] = a[j+1];

for (k = l; k <= r; k++)


if (XXXXXXXX)
a[k] = aux[j--];
II.c) Considere a seguinte fun
else
a[k] = aux[i++];
elemento r entre as posições l
}

int search(int v[], int k


Qual a expressão que deve ser coloca em XXXXXXXX? Note que esta função ordena um vector a em que
inicialmente os valores nas posições de 0 a m estão ordenados por ordem crescente e os valores nas

37
{
posições de m+1 a r estão também ordenados por ordem crescente Ou seja, dadas duas sequências
de valores ordenados, esta função realiza a união ordenada. IAED, 2014/2015
Heap Sort

IAED, 2014/2015
HeapSort (ideia base)
•  Podemos pensar no heapsort como um selection sort
mais eficiente.

•  o heapsort mantém os dados organizados


numa estrutura de dados (chamada heap).

•  A raiz dessa estrutura, contém sempre


o maior elemento.

•  Os elementos podem ser sucessivamente removidos


da raiz da heap, na ordem desejada, tendo o cuidado
de manter as propriedades dessa estrutura

IAED, 2014/2015
Das árvores aos amontoados (heaps)

•  Um vector de elementos que pode ser visto como uma


árvore binária

20 16 12 11 15 7 6 10 2

40 IAED, 2014/2015
Das árvores aos amontoados (heaps)

•  Um vector de elementos que pode ser visto como uma


árvore binária
20

16 12

11 15 7 6

10 2

20 16 12 11 15 7 6 10 2

41 IAED, 2014/2015
Amontoado (heap): definição

1.  Nenhum nó tem uma chave superior à raiz


2.  Heap-condition: a chave de cada nó é sempre maior
ou igual que as chaves de ambos os filhos

Será que este vector 20


é um amontoado?
16 12

11 15 7 6

10 2
42 IAED, 2014/2015
Amontoado (heap): definição | Exercício

Lab. 6. Ex. 4:
Quais dos seguintes vectores corresponde a um
amontoado (heap)?

•  a. <50, 25, 30, 27, 24, 21, 28>


•  b. <50, 30, 25, 27, 24, 28, 21>
•  c. <60, 50, 9, 40, 41, 10, 8>
•  d. <40, 15, 18, 13, 11, 14, 16>
•  e. <60, 30, 80, 10, 35, 70, 40>

1.  Nenhum nó tem uma chave superior à raiz


2.  Heap-condition: a chave de cada nó é
sempre maior ou igual que as chaves de
IAED, 2014/2015
43
ambos os filhos
Amontoado (heap): definição | Exercício

Lab. 6. Ex. 4:
Quais dos seguintes vectores corresponde a um
amontoado (heap)?

•  a. <50, 25, 30, 27, 24, 21, 28>


•  b. <50, 30, 25, 27, 24, 28, 21>
•  c. <60, 50, 9, 40, 41, 10, 8>
•  d. <40, 15, 18, 13, 11, 14, 16>
•  e. <60, 30, 80, 10, 35, 70, 40>

1.  Nenhum nó tem uma chave superior à raiz


2.  Heap-condition: a chave de cada nó é
sempre maior ou igual que as chaves de
IAED, 2014/2015
44
ambos os filhos
Amontoado: Índices Filho esquerdo

Filho direito

0
20
1
2
16 12

3 4 5 6
11 15 7 6

7 8
10 2 20 16 12 11 15 7 6 10 2
0 1 2 3 4 5 6 7 8
45 IAED, 2014/2015
Amontoado: Índices

int parent(int k) {
return ((k+1)/2)-1;
}

int left(int k) {
return 2*k+1;
}

int right(int k) {
return 2*(k+1);
}

46 IAED, 2014/2015
Operações em Amontoados: fixDown
Fixdown (i):
•  chamada quando se pretende diminuir a chave de um nó
•  confirma se ambos os filhos são menores do que “ele”;
•  se não for o caso, troca de posição com o filho maior e
volta a chamar-se para a posição onde o filho maior
estava.

Ideia do código em Linguagem Coloquial:

Fixdown (indice i)
verifica se i tem um filho maior que ele
SE i tem um filho maior:
troca i com o filho_maior
Função Fixdown (pos_do_filho_maior)
recursiva

47 IAED, 2014/2015
Operações em Amontoados: fixDown(em C)
void fixDown(Item a[], int l, int r, int k)
{
int ileft, iright, largest=k;

ileft=l+left(k-l);
iright=l+right(k-l);

if (ileft<=r && less(a[largest],a[ileft]))


largest=ileft;
if (iright<=r && less(a[largest],a[iright]))
largest=iright;

if (largest!=k){
exch(a[k],a[largest]);
fixDown(a, l, r, largest);
}
}

48 IAED, 2014/2015
Operações em Amontoados: fixDown

•  Se tivermos um amontoado...

20

16 12

11 15 7 6

10 2

49 IAED, 2014/2015
Operações em Amontoados: fixDown

•  Se tivermos um amontoado... e alterarmos a raiz.


•  Podemos voltar a arrumar o vector num “amontoado”,
aplicando o fixDown na raiz

16 12

11 15 7 6

10 2

50 IAED, 2014/2015
Operações em Amontoados: fixDown

•  Se tivermos um amontoado... e alterarmos a raiz.


•  Podemos voltar a arrumar o vector num “amontoado”,
aplicando o fixDown na raiz

16

5 12

11 15 7 6

10 2

51 IAED, 2014/2015
Operações em Amontoados: fixDown

•  Se tivermos um amontoado... e alterarmos a raiz.


•  Podemos voltar a arrumar o vector num “amontoado”,
aplicando o fixDown na raiz

16
Será que já é
um
15 12 amontoado?

11 5 7 6

10 2

52 IAED, 2014/2015
Operações em Amontoados: fixDown

•  Se tivermos um amontoado... e alterarmos a raiz.


•  Podemos voltar a arrumar o vector num “amontoado”,
aplicando o fixDown na raiz

16
Será que já é
um
15 12 amontoado?
SIM!!!!

11 5 7 6

10 2

53 IAED, 2014/2015
Operações em Amontoados: buildheap
•  Mas como transformar um vector arbitrário num
amontoado?
20

16 12

11 15 7 6

10 2

20 16 12 11 15 7 6 10 2

54 IAED, 2014/2015
Operações em Amontoados: buildheap

buildheap:
•  Chama fixDown do último até ao primeiro elemento do
vector até todos os elementos cumprirem a heap condition.
•  Uma forma mais rápida de o fazer é começar por chamar o
fixDown ao pai com o índice mais elevado
(K=heapsize/2-1), e a todos os índices<K

void buildheap(Item a[], int l, int r){


int k, heapsize = r-l+1;
for (k = heapsize/2-1; k >= l; k--)
fixDown(a, l, r, l+k);
}

55 IAED, 2014/2015
Heapsort

•  Começo por transformar o vector numa árvore.

16 12

11 15 7 6

10 20

2 16 12 11 15 7 6 10 20

56 IAED, 2014/2015
void buildheap(Item a[], int l, int r){
int k, heapsize = r-l+1;

Heapsort for (k = heapsize/2-1; k >= l; k--)


fixDown(a, l, r, l+k);
}

•  Começo por transformar o vector num amontoado.

FD 16 12

11 15 7 6

10 20

2 16 12 11 15 7 6 10 20

57 IAED, 2014/2015
void buildheap(Item a[], int l, int r){
int k, heapsize = r-l+1;

Heapsort for (k = heapsize/2-1; k >= l; k--)


fixDown(a, l, r, l+k);
}

•  Começo por transformar o vector num amontoado.

16 12

20 FD 15 7 6

10 11

2 16 12 20 15 7 6 10 11

58 IAED, 2014/2015
void buildheap(Item a[], int l, int r){
int k, heapsize = r-l+1;

Heapsort for (k = heapsize/2-1; k >= l; k--)


fixDown(a, l, r, l+k);
}

•  Começo por transformar o vector num amontoado.


FD
2

16 12

20 15 7 6

10 11

2 16 12 20 15 7 6 10 11

59 IAED, 2014/2015
void buildheap(Item a[], int l, int r){
int k, heapsize = r-l+1;

Heapsort for (k = heapsize/2-1; k >= l; k--)


fixDown(a, l, r, l+k);
}

•  Começo por transformar o vector num amontoado.


FD
2

16 12

20 15 7 6

10 11

2 16 12 20 15 7 6 10 11

60 IAED, 2014/2015
void buildheap(Item a[], int l, int r){
int k, heapsize = r-l+1;

Heapsort for (k = heapsize/2-1; k >= l; k--)


fixDown(a, l, r, l+k);
}

•  Começo por transformar o vector num amontoado.

FD 20 12

16 15 7 6

10 11

2 20 12 16 15 7 6 10 11

61 IAED, 2014/2015
void buildheap(Item a[], int l, int r){
int k, heapsize = r-l+1;

Heapsort for (k = heapsize/2-1; k >= l; k--)


fixDown(a, l, r, l+k);
}
FD
•  Começo por transformar o vector num amontoado.

20 12

16 15 7 6

10 11

2 20 12 16 15 7 6 10 11

62 IAED, 2014/2015
void buildheap(Item a[], int l, int r){
int k, heapsize = r-l+1;

Heapsort for (k = heapsize/2-1; k >= l; k--)


fixDown(a, l, r, l+k);
}

•  Começo por transformar o vector num amontoado.


FD
20

2 12

16 15 7 6

10 11

20 2 12 16 15 7 6 10 11

63 IAED, 2014/2015
void buildheap(Item a[], int l, int r){
int k, heapsize = r-l+1;

Heapsort for (k = heapsize/2-1; k >= l; k--)


fixDown(a, l, r, l+k);
}

•  Começo por transformar o vector num amontoado.

20

FD 16 12

2 15 7 6

10 11

20 16 12 2 15 7 6 10 11

64 IAED, 2014/2015
void buildheap(Item a[], int l, int r){
int k, heapsize = r-l+1;

Heapsort for (k = heapsize/2-1; k >= l; k--)


fixDown(a, l, r, l+k);
}

•  Começo por transformar o vector num amontoado.

20

16 12

11 FD 15 7 6

10 2

20 16 12 11 15 7 6 10 2

65 IAED, 2014/2015
void buildheap(Item a[], int l, int r){
int k, heapsize = r-l+1;

Heapsort for (k = heapsize/2-1; k >= l; k--)


fixDown(a, l, r, l+k);
}

•  Começo por transformar o vector num amontoado:


Já está!
20

16 12

11 15 7 6

10 2

20 16 12 11 15 7 6 10 2

66 IAED, 2014/2015
Heapsort

•  Trocar o primeiro elemento (raiz) com o último

20

16 12

11 15 7 6

10 2

20 16 12 11 15 7 6 10 2

67 IAED, 2014/2015
Heapsort

•  Trocar o primeiro elemento (raiz) com o último

16 12

11 15 7 6

10 20

2 16 12 11 15 7 6 10 20

68 IAED, 2014/2015
Heapsort

•  Reduzir a dimensão do amontoado em uma unidade

16 12

11 15 7 6

10 20

2 16 12 11 15 7 6 10 20

69 IAED, 2014/2015
Heapsort

•  Reduzir a dimensão do amontoado em uma unidade

16 12

11 15 7 6

Encontrámos o
maior elemento
10 20

2 16 12 11 15 7 6 10 20

70 IAED, 2014/2015
Heapsort

•  Correr fixDown sobre a nova raiz para reparar o


amontoado
2

16 12

11 15 7 6

10 20

2 16 12 11 15 7 6 10 20

71 IAED, 2014/2015
Heapsort

•  Correr fixDown sobre a nova raiz para reparar o


amontoado
16

2 12

11 15 7 6

10 20

16 2 12 11 15 7 6 10 20

72 IAED, 2014/2015
Heapsort

•  Correr fixDown sobre a nova raiz para reparar o


amontoado
16

15 12

11 2 7 6

10 20

16 15 12 11 2 7 6 10 20

73 IAED, 2014/2015
Heapsort

•  Repetir

16

15 12

11 2 7 6

10 20

16 15 12 11 2 7 6 10 20

74 IAED, 2014/2015
Heapsort

•  Repetir

10

15 12

11 2 7 6

16 20

10 15 12 11 2 7 6 16 20

75 IAED, 2014/2015
Heapsort

•  Repetir

10

15 12

11 2 7 6

Encontrámos o
2º maior elemento
16 20

10 15 12 11 2 7 6 16 20

76 IAED, 2014/2015
Heapsort

•  Repetir

15

11 12

10 2 7 6

16 20

15 11 12 10 2 7 6 16 20

77 IAED, 2014/2015
Heapsort

•  Repetir

11 12

10 2 7 15

16 20

6 11 12 10 2 7 15 16 20

78 IAED, 2014/2015
Heapsort

•  Repetir

11 12

10 2 7 15

16 20

6 11 12 10 2 7 15 16 20

79 IAED, 2014/2015
Heapsort

•  Repetir ...

12

11 7

10 2 6 15

16 20

12 11 7 10 2 6 15 16 20

80 IAED, 2014/2015
Heapsort

•  No final

6 7

10 11 12 15

16 20

2 6 7 10 11 12 15 16 20

81 IAED, 2014/2015
Heapsort Começo por transformar o
vector num amontoado

Troco o primeiro com o


void heapsort(Item a[], int l, int r) último elemento do
vector (i.e., troco a raiz
{ com o último elemento da
buildheap(a,l,r); árvore)
while (r-l > 0) {
exch(a[l], a[r]);
fixDown(a, l, --r, l);
}
} Reduzo a dimensão do
meu amontoado

Executo estes 3 últimos ...e chamo o fixDown à raiz, de


passos até ordenar todos forma a voltar a transformar o
os elementos (sub-)vector num amontoado
82 IAED, 2014/2015
Exercício
•  Considere que se pretende ordenar o vector em baixo,
utilizando o algoritmo de ordenação heapsort. Qual será
a configuração do vector após 2 iterações do heapsort?
a = { 10, 2, 17, 14, -1, 4, 8, 0, 7 }

•  Receita:
1.  Transformamos a[] num amontoado (ver buildheap)
2.  Trocamos o ultimo elemento com a raiz, e extraímos esse
elemento do amontoado.
3.  Aplicamos o fixDown à raiz
4.  Voltamos a 2.

83 IAED, 2014/2015
Exercício

•  Começo por transformar o vector num amontoado.


FD FD
10

FD 2 17

14 -1 4 8

0 7

84 IAED, 2014/2015
Exercício

•  Começo por transformar o vector num amontoado.


FD
10

2 17

14 -1 4 8

0 7

85 IAED, 2014/2015
Exercício

•  Começo por transformar o vector num amontoado.


FD
10

FD 14 17

2 -1 4 8

0 7

86 IAED, 2014/2015
Exercício

•  Começo por transformar o vector num amontoado.


FD
10

14 17

7 -1 4 8

0 2

87 IAED, 2014/2015
Exercício
FD
•  Começo por transformar o vector num amontoado.

10

14 17

7 -1 4 8

0 2

88 IAED, 2014/2015
Exercício
FD
•  Começo por transformar o vector num amontoado.
FD
17

14 10

7 -1 4 8

0 2

89 IAED, 2014/2015
Exercício

•  Agora já tenho um amontoado!!

17

14 10

7 -1 4 8
1.  Trocamos o ultimo elemento
0 2 com a raiz, e extraímos esse
elemento do amontoado.
2.  Aplicamos o fixDown à raiz
3.  Voltamos a 1.

90 IAED, 2014/2015
Exercício

14 10

7 -1 4 8
1.  Trocamos o ultimo elemento
0 17 com a raiz, e extraímos esse
elemento do amontoado.
2.  Aplicamos o fixDown à raiz
3.  Voltamos a 1.

91 IAED, 2014/2015
Exercício

14

7 10

2 -1 4 8
1.  Trocamos o ultimo elemento
0 17 com a raiz, e extraímos esse
elemento do amontoado.
2.  Aplicamos o fixDown à raiz
3.  Voltamos a 1.

92 IAED, 2014/2015
Exercício

7 10

2 -1 4 8
1.  Trocamos o ultimo elemento
14 17 com a raiz, e extraímos esse
elemento do amontoado.
2.  Aplicamos o fixDown à raiz
3.  Voltamos a 1.

93 IAED, 2014/2015
Exercício

10

7 8

2 -1 4 0

14 17

94 IAED, 2014/2015
Exercício

10

7 8

2 -1 4 0

14 17

a = { 10, 7, 8, 2, -1, 4, 0, 14, 17 }

95 IAED, 2014/2015
Heapsort - Complexidade

•  Construção do amontoado (ciclo for):


–  O ( N lg N ) no pior caso
–  Pode ser O(N ) Porquê ?

•  Colocação das chaves (ciclo while):


–  O ( N lg N ) no pior & melhor caso
Porquê ?

•  Complexidade no pior caso é O ( N lg N )

•  Não é estável

96 IAED, 2014/2015
Avaliação Experimental

N Quick Merge Heap


12500 2 5 3
25000 7 11 8
50000 13 24 18
100000 27 52 42
200000 58 111 100
400000 122 238 232
800000 261 520 542

97 IAED, 2014/2015
Ordenação por Comparação

•  Algoritmos de ordenação baseados em comparações


são pelo menos O(N lg N )
–  Para N chaves existem N ! ordenações possíveis das chaves
–  Algoritmo de ordenação por comparação utiliza comparações de
pares de chaves para selecionar uma das N ! ordenações
•  Escolher uma folha em árvore com N ! folhas
•  Altura da árvore é não inferior lg(N!) ≈ N lg N
•  É possível obter algoritmos mais eficientes desde
que não sejam apenas baseados em comparações
•  Alternativa: Utilizar informação quanto às chaves
utilizadas
–  Counting Sort
–  Radix Sort
98 IAED, 2014/2015
Ponteiros e Tabelas

K&R: Capítulo 5

IAED, 2014/2015
Ponteiros e Tabelas

•  Ponteiros e endereços
•  Ponteiros e argumentos de funções
•  Ponteiros e tabelas
•  Alocação dinâmica de memória
•  Aritmética de ponteiros
•  Tabelas de ponteiros e ponteiros para ponteiros
•  Tabelas multi-dimensionais
•  Inicialização de tabelas de ponteiros
•  Argumentos da linha de comandos

2 IAED, 2014/2015
Ponteiros e Endereços

•  Na memória do computador cada posição é


referenciada por um endereço, atribuído de forma
sequencial
•  Posições adjacentes têm endereços consecutivos
•  Um ponteiro é uma variável que contém um endereço
de outra variável.

a
3245434
3245435
3245436
3245437

3 IAED, 2014/2015
Ponteiros e Endereços: exemplo

int x = 10;
int *px = &x;
px

x 3245434
3245435
3245436
3245437

Inicializo px com
Declaração de um o endereço de x
ponteiro (operador &)

4 IAED, 2014/2015
O que é
um ponteiro em C ?

5 IAED, 2014/2015
Um ponteiro em C
é
um endereço de memória

6 IAED, 2014/2015
Declaração de ponteiros
•  Na sua declaração temos de indicar ao compilador para
que tipo de variável estamos a endereçar.

•  Declaração de um ponteiro para tipo <tipo>


<tipo> *<variável>;

•  Exemplos
char *cptr; /* ponteiro para caracter */

int *iptr; /* ponteiro para inteiro */

double *dptr; /* ponteiro para double */


7 IAED, 2014/2015
Declaração de ponteiros
•  Na sua declaração temos de indicar ao compilador para
que tipo de variável estamos a endereçar.

•  Declaração de um ponteiro para tipo <tipo>


<tipo> *<variável>;

•  Exemplos
/* apenas a é um ponteiro*/
int* a, b;

/* c e d são ponteiros para floats */


float *c, *d;
8 IAED, 2014/2015
Operador &

•  O endereço de uma variável é obtido através do


operador &.

•  Exemplos

int a = 43; /* um inteiro inicializado a 43 */


int* iptr; /* ponteiro para inteiro */
iptr = &a; /* iptr passa a guardar o endereço de a */

9 IAED, 2014/2015
Operador &

•  O endereço de uma variável é obtido através do


operador &.

•  Exemplos

int a = 43; /* um inteiro inicializado a 43 */


int *iptr; /* ponteiro para inteiro */
iptr = &a; /* iptr passa a guardar o endereço de a */

10 IAED, 2014/2015
Operador &

•  O endereço de uma variável é obtido através do


operador &.

•  Exemplos (também poderia inicializar iptr na mesma linha):

int a = 43; /* um inteiro inicializado a 43 */


int *iptr = &a;
/* declaro um ponteiro para inteiro e esse ponteiro passa
a guardar o endereço de a */

11 IAED, 2014/2015
Operador *

•  O operador * permite aceder ao conteúdo de uma posição


de memória endereçada pelo ponteiro
(i.e., o conteúdo para onde um ponteiro “aponta”).

•  Exemplo:
int a = 43; /* um inteiro inicializado a 43 */
int *iptr; /* ponteiro para inteiro */
int b;

iptr = &a; /* iptr passa a guardar o endereço de a */


b = *iptr; /* b passa a guardar o valor apontado por
iptr*/ IAED, 2014/2015
12
Operadores & e *
x y
•  Outro exemplo:

#include <stdio.h>
0
1 1
int main() {
int y, x = 1;
int *px; px
px = &x;
y = *px;
*px = 0;
printf("%d %d\n", x, y);

return 0;
}
13 IAED, 2014/2015
Operadores & e * Declaro dois inteiros,
sendo x=1

•  Outro exemplo:

#include <stdio.h> px é um ponteiro para


inteiros... Mas ainda não
guarda o endereço de nenhum
int main() {
int y, x = 1; Agora sim... px fica a guardar
int *px; o endereço de x
y toma o valor guardado no
px = &x; endereço de memória
y = *px; guardado em px, i.e., o valor 1
*px = 0; Alteramos o conteúdo da
printf("%d %d\n", x, y); posição de memória para
onde px aponta, ou seja,
return 0; vamos alterar o valor de x
} Output: 0 1
14 IAED, 2014/2015
Confusão habitual: utilizações do * (asterisco)

•  Declaração do ponteiro

int *x;
–  x é um ponteiro para um inteiro

•  Conteúdo da posição de memória apontada pelo ponteiro

*x = 4;
–  o valor 4 é atribuído ao conteúdo da posição de memória apontada
por x
15 IAED, 2014/2015
Utilização de Ponteiros

•  O valor de retorno de uma função pode ser um ponteiro


int* xpto();

•  O argumento de uma função pode ser um ponteiro


int abcd(char *a, int *b);

16 IAED, 2014/2015
Passagem de Parâmetros para Funções

•  Em C os parâmetros são passados por valor

void swap(int a, int b) {


int aux;

aux = a;
a = b;
b = aux;
}

•  Chamada swap(x, y);


•  A troca não é efectuada: Não funciona como necessário !
18 IAED, 2014/2015
Passagem de Parâmetros para Funções

•  Passagem por referência consegue-se enviando ponteiros

void swap(int *a, int *b) {


int aux;

aux = *a;
*a = *b;
*b = aux;
}

•  Chamada deverá ser swap(&x, &y)

19 IAED, 2014/2015
Ponteiro Nulo / Endereço Zero

•  Ponteiro especial para representar o endereço 0


int *ptr = NULL;

•  Definido em stdlib.h
–  Necessário #include <stdlib.h>

•  Utilizado para indicar situações especiais

•  Na realidade NULL == 0

20 IAED, 2014/2015
Ponteiros e Tabelas

•  Em C existe uma relação entre ponteiros e tabelas


#include <stdio.h>
Os apontadores têm uma
int main() { aritmética própria
int a[6] = {1, 2, 7, 0, 11, 6};
int *pa = a;

printf("%d %d %d\n", a[2], *(a+2), *(pa+2));

return 0;
}
•  a é um ponteiro para a primeira posição da tabela

21 IAED, 2014/2015
Ponteiros e Tabelas

•  É possível efectuar + e - com ponteiros

int x[10]; px
int *px = x;
X[0]

22 IAED, 2014/2015
Ponteiros e Tabelas

•  É possível efectuar + e - com ponteiros

int x[10]; px
int *px = x;

px++;
X[0]
•  Incrementa/decrementa na dimensão
do tipo para o qual aponta
–  sizeof(int) neste caso

23 IAED, 2014/2015
Ponteiros e Tabelas

•  Em C existe uma relação entre ponteiros e tabelas


#include <stdio.h>

int main() {
int a[6] = {1, 2, 7, 0, 11, 6};
int *pa = a;

printf("%d %d %d\n", a[2], *(a+2), *(pa+2));

return 0;
}
•  a é um ponteiro para a primeira posição da tabela

24 IAED, 2014/2015
Ponteiros e Tabelas

•  A declaração int *p1; declara o mesmo que int p2[];


–  p1 pode ser alterado
–  p2 não pode ser alterado
–  int p2[]; só pode ser utilizado em certos casos

•  A declaração int p3[100]; declara uma tabela com 100


inteiros e aloca memória na quantidade necessária
–  p3 não pode ser alterado

•  A declaração char *text; não aloca qualquer memória


–  no entanto char *text = "ola"; aloca;

25 IAED, 2014/2015
Ponteiros e Tabelas

•  Qual a diferença entre as duas declarações seguintes ?

char t1[] = "ola";


char *t2 = "ola";

•  Ambas alocam 4 bytes e copiam para essa posição de


memória a sequência de caracteres 'o','l','a','\0'
•  Em ambos os casos é possível modificar o conteúdo da
memória alocada
•  Não é possível alterar o valor de t1, ou seja não é possível
pôr t1 a endereçar outra posição de memória
•  É possível alterar o valor de t2
26 IAED, 2014/2015
Exercício
•  Seja float a[100];
float *p=a;

•  então
a[i] é equivalente a *(a+i) V
&a[i] é equivalente a a+i V
a[i] é equivalente a p[i] V
p[i] é equivalente a *(p+i) V
*a ? a é equivalente a p[0] F
a[0] ?
27 IAED, 2014/2015
Ponteiros e Tabelas

•  Exemplo: cópia de strings


void strcpy(char s[], char t[])
{
int i = 0;
while ((s[i] = t[i]) != ´\0´)
i++;
}

28 IAED, 2014/2015
Ponteiros e Tabelas

•  Exemplo: cópia de strings


void strcpy(char *s, char *t)
{
int i = 0;
while ((s[i] = t[i]) != ´\0´)
i++;
}

void strcpy(char *s, char *t)


{
while ((*(s+i) = *(t+i)) != ´\0´)
i++;
}

29 IAED, 2014/2015
Ponteiros e Tabelas

•  Exemplo: cópia de strings

void strcpy(char *s, char *t)


{
while ((*(s+i) = *(t+i)) != ´\0´)
i++;
}

void strcpy(char *s, char *t)


{
while ((*s = *t) != ´\0´) {
s++;
t++;
}
}

30 IAED, 2014/2015
Ponteiros e Tabelas

•  Exemplo: cópia de strings


void strcpy(char *s, char *t)
{
while ((*s = *t) != ’\0’) {
s++;
t++;
}
}

void strcpy(char *s, char *t)


{
while ((*s++ = *t++) != ’\0’);
}

31 IAED, 2014/2015
Ponteiros e Tabelas

•  Exemplo: cópia de strings


void strcpy(char *s, char *t)
{
while ((*s++ = *t++) != ’\0’);
}

void strcpy(char *s, char *t)


{
while ((*s++ = *t++));
}

Porquê?

32 IAED, 2014/2015
Passagem de Parâmetros para Funções II

•  Quando fazemos

int a;
scanf(“%d”,&a);

o que estamos a passar ao scanf ?

char s[100];
scanf(“%s”,s);

•  Porque não precisamos do & ?

33 IAED, 2014/2015
Passagem de Parâmetros para Funções II

•  Passagem por referência consegue-se enviando ponteiros

void leVector(int *v, int tamanho) {


int i;
for (i=0 ; i<tamanho ; i++)
scanf(“%d”,&v[i]}
}

•  Podemos escrever o argumento como int*v ou int v[]


•  Como v já é um endereço podemos alterar o v dentro da
função.

34 IAED, 2014/2015
Endereços de ponteiros

•  É possível declarar um ponteiro para um ponteiro


#include <stdio.h>

int main() {
int x = 10;
int *px = &x;
int **ppx = &px;

printf("%d %d %d\n", x, *px, **ppx);

return 0;
}
35 IAED, 2014/2015
Argumentos da Linha de Comandos

•  argv[0] é o nome o programa


•  argv[i] é i-ésimo argumento
•  Programa ”escreve"
•  $ escreve hello world gera hello world
int main(int argc, char *argv[]) {
int i;
array de pointers
for(i=1; i < argc; i++)
printf("%s ", argv[i]); Tamanho do array
(número de argumentos introduzidos)
printf("\n");
return 0;
}

36 IAED, 2014/2015
1ª nota

•  Ao fazer
int *a;

apenas estamos a reservar memória para 1 endereço de


memória e não para um inteiro.
•  Por esta razão, não devemos inicializar o conteúdo de um
ponteiro sem que saibamos exactamente onde ele está a
escrever. Ex.:

int *a;
*a=12; /* A evitar!!! */

37 IAED, 2014/2015
2ª nota: Ponteiros para Funções

•  É possível ter ponteiros para funções


•  O nome de uma função é um ponteiro para essa função
int soma(int a, int b) { return a+b; }

int main() {
int (*ptr)(int, int);

ptr = soma;

printf("%d\n", (*ptr)(3,4));
return 0;
}

38 IAED, 2014/2015
2ª nota: Ponteiros para Funções (2º exemplo)
•  É possível ter ponteiros para funções
•  O nome de uma função é um ponteiro para essa função
int modulo(int a) { return a < 0 ? –a : a; }
int dobro(int a) { return a*2; }

void escreve(int (*func)(int), int valor){


printf(“%d\n”,(*func)(valor));
}
int main() {
int x = -10;
int (*f)(int);
f = modulo;
escreve(f,x);
return 0;
}
39 IAED, 2014/2015
Ponteiros & tabelas (cont.)

K&R: Capítulo 5

IAED, 2014/2015
Sinopse da aula de hoje

•  Pointers in a nutshell & alocação dinâmica de memória


•  Estruturas, funções e apontadores
•  Estruturas auto-referenciadas
•  Exemplo de aplicação:
–  Listas ligadas
–  uma Pilha

2 IAED, 2014/2015
3245434
Pointers in a nutshell
3245435

•  Na memória do computador cada 3245436


posição é referenciada por um 3245437
“endereço”
•  Um apontador é uma variável que contém um endereço
de outra variável.

3 IAED, 2014/2015
3245434
Pointers in a nutshell
3245435

•  Na memória do computador cada 3245436


posição é referenciada por um 3245437
“endereço”
•  Um apontador é uma variável que contém um endereço
de outra variável.
•  Na sua declaração temos de indicar ao compilador para
que tipo de variável estamos a apontar.

char *cptr; /* ponteiro para caracter */


int *iptr; /* ponteiro para inteiro */
double *dptr; /* ponteiro para double */

4 IAED, 2014/2015
3245434
Pointers in a nutshell
3245435

•  Na memória do computador cada 3245436


posição é referenciada por um 3245437
“endereço”
•  Um apontador é uma variável que contém um endereço
de outra variável.
•  O endereço de uma variável é dado pelo operador &

int a=43; /* um inteiro inicializado a 43 */


int *iptr; /* ponteiro para inteiro */
iptr=&a; /* iptr passa a guardar o endereço de a */

5 IAED, 2014/2015
3245434
Pointers in a nutshell
3245435

•  Na memória do computador cada 3245436


posição é referenciada por um 3245437
“endereço”
•  Um apontador é uma variável que contém um endereço
de outra variável.
•  O valor guardado num determinado endereço é dado
por pelo operador *
int b;
int a=43; /* um inteiro inicializado a 43 */
int *iptr; /* ponteiro para inteiro */
iptr=&a; /* iptr passa a guardar o endereço de a */
b=*iptr; /* b passa a guardar o valor 43*/ */
6 IAED, 2014/2015
O que é
um ponteiro em C ?

7 IAED, 2014/2015
IAED, 2012/2013
Um ponteiro em C
é
um endereço de memória

8 IAED, 2014/2015
IAED, 2012/2013
Pointers in a nutshell

•  Em C os parâmetros são passados por valor

void swap(int a, int b) {


int aux;

aux = a;
a = b;
b = aux;
}

•  swap(x, y) não funciona como necessário !

9 IAED, 2014/2015
Pointers in a nutshell

•  Passagem por referência consegue-se enviando ponteiros

void swap(int *a, int *b) {


int aux;

aux = *a;
*a = *b;
*b = aux;
}

•  Chamada deverá ser swap(&x, &y)

10 IAED, 2014/2015
Ponteiro Nulo / Endereço Zero

•  Ponteiro especial para representar o endereço 0


int *ptr = NULL;

•  Definido em stdlib.h
–  Necessário #include <stdlib.h>

•  Utilizado para indicar situações especiais

•  Na realidade NULL == 0

11 IAED, 2014/2015
Pointers in a nutshell

•  Em C existe uma relação entre ponteiros e tabelas


#include <stdio.h>
Os apontadores têm uma
int main() { aritmética própria
int a[6] = {1, 2, 3, 4, 5, 6 };
int *pa = a;

printf("%d %d %d\n", a[2], *(a+2), *(pa+2));

return 0;
}
•  a é um ponteiro para a primeira posição da tabela
•  pa pode ser alterado; a não pode!
12 IAED, 2014/2015
Pointers in a nutshell

•  É possível efectuar + e - com ponteiros

x
int x = 10; px
int *px = &x;

13 IAED, 2014/2015
Pointers in a nutshell

•  É possível efectuar + e - com ponteiros

x
int x = 10; px
int *px = &x;

px++;

•  Incrementa/decrementa na dimensão
do tipo para o qual aponta
–  sizeof(int) neste caso
14 IAED, 2014/2015
Passagem de Parâmetros para Funções

•  Quando fazemos

int a;
scanf(“%d”,&a);

o que estamos a passar ao scanf ?

char s[100];
scanf(“%s”,s);

•  Por que não precisamos do & ?

15 IAED, 2014/2015
Passagem de Parâmetros para Funções

•  Passagem por referência consegue-se enviando ponteiros

void leVector(int v[], int tamanho) {


int i;
for (i=0 ; i<tamanho ; i++)
scanf(“%d”,&v[i]}
}

•  Podemos escrever o argumento como int*v ou int v[]


•  Como v já é um endereço podemos alterar o v dentro da
função.

16 IAED, 2014/2015
Passagem de Parâmetros para Funções

•  Passagem por referência consegue-se enviando ponteiros

void leVector(int *v, int tamanho) {


int i;
for (i=0 ; i<tamanho ; i++)
scanf(“%d”,v++}
}

•  Podemos escrever o argumento como int*v ou int v[]


•  Como v já é um endereço podemos alterar o v dentro da
função.

17 IAED, 2014/2015
Endereços de ponteiros

•  É possível declarar um ponteiro para um ponteiro


#include <stdio.h>

int main() {
int x = 10;
int *px = &x;
int **ppx = &px;

printf("%d %d %d\n", x, *px, **ppx);

return 0;
}
18 IAED, 2014/2015
Argumentos da Linha de Comandos

•  argv[0] é o nome o programa


•  argv[i] é i-ésimo argumento
•  Programa ”escreve"
•  $ ./escreve hello world gera hello world
int main(int argc, char *argv[]) {
int i;
array de pointers
for(i=1; i < argc; i++)
printf("%s ", argv[i]); Tamanho do array
(número de argumentos introduzidos)
printf("\n");
return 0;
}

19 IAED, 2014/2015
ATENÇÃO!

•  Ao fazer
int *a;

apenas estamos a reservar memória para 1 endereço de


memória e não para um inteiro.
•  Por esta razão, não devemos inicializar o conteúdo de um
ponteiro sem que saibamos exactamente onde ele está a
escrever. Ex.:

int *a;
*a=12; /* A evitar!!!*/

20 IAED, 2014/2015
Ponteiros para Funções

•  É possível ter ponteiros para funções


•  O nome de uma função é um ponteiro para essa função
int soma(int a, int b) { return a+b; }

int main() {
int (*ptr)(int, int);

ptr = soma;

printf("%d\n", (*ptr)(3,4));
return 0;
}

21 IAED, 2014/2015
Ponteiros para Funções (2º exemplo)
•  É possível ter ponteiros para funções
•  O nome de uma função é um ponteiro para essa função
int modulo(int a) { return a < 0 ? –a : a; }
int dobro(int a) { return a*2; }

void escreve(int (*func)(int), int valor){


printf(“%d\n”,(*func)(valor));
}
int main() {
int x=-10;
int (*f)(int);
f=modulo;
escreve(f,x);
return 0;
}
22 IAED, 2014/2015
Alocação dinâmica de memória

IAED, 2014/2015
Alocação Dinâmica de Memória

•  Alocação estática
int tab[100];

–  Memória alocada durante o scope da variável


–  Não é possível libertar quando não necessária
–  Não é possivel utilizar fora do scope

•  Solução: alocação dinâmica !

24 IAED, 2014/2015
Alocação Dinâmica de Memória

•  Função malloc
void *malloc(size_t size);
•  Recebe como argumento o número de bytes
–  Tipo size_t representa uma dimensão em bytes
•  Devolve um ponteiro (endereço) para o primeiro byte do
bloco de memória contígua alocada
–  void * indica um ponteiro para um tipo não especificado
–  permite utilização com qualquer tipo de dados
–  posteriormente faz-se conversão para o tipo correcto por type cast

int *vec;
vec = (int*) malloc(sizeof(int)*100);
25 IAED, 2014/2015
Alocação Dinâmica de Memória

•  Função realloc
void *realloc(void *ptr, size_t size);

•  Recebe ponteiro ptr para bloco de memória antigo e


dimensão size do novo bloco de memória
•  Devolve ponteiro para novo bloco de memória
•  Copia valores do bloco antigo para o novo
–  Se novo for mais pequeno, só copia até caber
–  Se novo for maior, copia tudo e deixa o resto sem ser inicializado

vec = (int*) realloc(vec, sizeof(int)*250);


26 IAED, 2014/2015
Alocação Dinâmica de Memória

•  Libertação de memória é efectuada com a função free


void free(void *ptr);

•  Recebe como argumento o ponteiro para a primeira


posição do bloco de memória contígua a libertar
•  Não devolve nada
•  Como libertar a memória reservada com o malloc anterior?
free(vec);

•  Tanto malloc como free estão definidas em stdlib.h


–  Necessário #include <stdlib.h>
27
–  Sugestão: Usem o valgrind IAED, 2014/2015
Sinopse da aula de hoje

•  Pointers in a nutshell: Dúvidas?


•  Estruturas, Funções e apontadores
•  Estruturas Auto-Referenciadas
•  Exemplo de aplicação:
–  Listas ligadas
–  uma Pilha

28 IAED, 2014/2015
Ponteiros para estruturas

IAED, 2014/2015
Sinopse da aula de hoje

•  Pointers in a nutshell
•  Estruturas, Funções e apontadores
•  Estruturas Auto-Referenciadas
•  Exemplo de aplicação:
–  Listas ligadas
–  uma Pilha

30 IAED, 2014/2015
Declaração de Estruturas (recapitulação)

•  Declaração de variável do tipo estrutura:


typedef struct ponto {
double x;
double y;
} Ponto

Ponto centro;

•  Manipulação : <variavel>.<membro>
centro.x = 12.3;
centro.y = 5.2;

31 IAED, 2014/2015
Declaração de Estruturas
•  Declaração de variável do tipo estrutura:
typedef struct ponto {
double x;
double y;
} Ponto

Ponto centro;
Ponto *pcentro = &centro;

•  Manipulação : (*<variavel>).<membro>
(*pcentro).x = 12.3;
(*pcentro).y = 5.2;

Mas isto é
32 IAED, 2014/2015
aborrecido!
Declaração de Estruturas (operador ->)
•  Declaração de variável do tipo estrutura:
typedef struct ponto {
double x;
double y;
} Ponto

Ponto centro;
Ponto *pcentro = &centro;

(*<ponteiro>).<membro> é equiv. a
•  Manipulação : <ponteiro>-><membro>
pcentro->x = 12.3;
pcentro->y = 5.2;

33 IAED, 2014/2015
Ponteiros para Estruturas

•  Declaração de ponteiro para uma estrutura:


Ponto *pcentro;

•  A declaração de um ponteiro não aloca memória!!


–  Se quisermos alocar memória de forma explícita fazemos:

pcentro = (Ponto*) malloc(sizeof(Ponto));

34 IAED, 2014/2015
Estruturas e Funções

•  Funções podem receber e retornar estruturas


Ponto adicionaPonto(Ponto p1, Ponto p2) {
Ponto res;
res.x = p1.x + p2.x;
res.y = p1.y + p2.y;
return res;
}

•  Função retorna cópia da estrutura res


•  Passagem de argumentos feita por valor
–  Chamada adicionaPonto(pa, pb) não altera valores de pa
nem pb.
Adicionaponto cria
cópias de pa e pb que só Será eficiente?
35
existem dentro da função! IAED, 2014/2015
Estruturas, Funções e Ponteiros

•  Passagem de estruturas grandes como parâmetros é


ineficiente
–  É necessário efectuar a cópia de todos os campos
•  Utilizam-se normalmente ponteiros para estruturas
•  Podemos alterar o conteúdo dos argumentos!

void adicionaPonto(Ponto *p1, Ponto *p2, Ponto *res) {


res->x = p1->x + p2->x;
res->y = p1->y + p2->y;
}

36 IAED, 2014/2015
Estruturas, Funções e Ponteiros

•  Também podemos reservar memória para uma estrutura


que será utilizada fora da função
–  Reservamos memória com o malloc e retornamos o pointer para
a memória alocada.

Ponto *adicionaPonto(Ponto *p1, Ponto *p2) {


Ponto *res;

res = (Ponto *) malloc(sizeof(Ponto));


res->x = p1->x + p2->x;
res->y = p1->y + p2->y;

return res;
}
37 IAED, 2014/2015
Estruturas, Funções e Ponteiros

•  Também podemos reservar memória para uma estrutura


que será utilizada fora da função
–  Reservamos memória com o malloc e retornamos o pointer para
a memória alocada.

Ponto *adicionaPonto(Ponto *p1, Ponto *p2) {


Ponto *res;
ATENÇÃO:
em geral, para cada
res = (Ponto *) malloc(sizeof(Ponto)); malloc tem de haver um
res->x = p1->x + p2->x; free!!
res->y = p1->y + p2->y;
Uma “memory leak”
ocorre sempre que
return res;
“perdemos” o endereço
} de memória do objecto
38 alocado.
IAED, 2014/2015
Estruturas, Funções e Ponteiros

•  Exemplo de fuga de memória (ERRO!!):

void printSOMA(Ponto *p1, Ponto *p2) {


Ponto *res;
ATENÇÃO:
em geral, para cada
res = (Ponto *) malloc(sizeof(Ponto)); malloc tem de haver um
res->x = p1->x + p2->x; free!!
res->y = p1->y + p2->y;
Uma “memory leak”
ocorre sempre que
printf(“(%d, %d)\n”,res->x,res->y);
“perdemos” o endereço
de memória do objecto
}
39 alocado.
IAED, 2014/2015
Estruturas, Funções e Ponteiros

•  Exemplo (corrigido):

void printSOMA(Ponto *p1, Ponto *p2) {


Ponto *res;
ATENÇÃO:
em geral, para cada
res = (Ponto *) malloc(sizeof(Ponto)); malloc tem de haver um
res->x = p1->x + p2->x; free!!
res->y = p1->y + p2->y;
Uma “memory leak”
ocorre sempre que
printf(“(%d, %d)\n”,res->x,res->y);
“perdemos” o endereço
free (res); de memória do objecto
}
40 alocado.
IAED, 2014/2015
Estruturas auto-referenciadas

IAED, 2014/2015
Sinopse

•  Pointers in a nutshell
•  Estruturas, Funções e apontadores
•  Estruturas Auto-Referenciadas: das tabelas às listas
•  Exemplo de aplicação:
–  Listas ligadas
–  uma Pilha

42 IAED, 2014/2015
Tabelas/Vectores

•  Colecção de items
–  Inteiros, reais, caracteres
–  Estruturas
–  Tabelas, Ponteiros

•  Guardados em posições consecutivas de memória

int tab[N];

•  Programador é responsável por respeitar limites


43 IAED, 2014/2015
Tabelas/Vectores

•  Em C tabelas podem ser


–  De dimensão fixa
–  Alocadas dinamicamente

#define N 100
int tab1[N];
int *tab2 = (int *) malloc(N*sizeof(int));

•  Acesso alternativo a tabelas


x = tab2[i];
y = *(tab2+i);

44 IAED, 2014/2015
Tabelas/Vectores

•  Vantagens
–  Manipulação simples
–  Facilidade de acesso ao n-ésimo elemento
•  Desvantagens
–  Tamanho limitado
–  Necessidade de realocar e copiar todos os elementos se
desejar aumentar dimensão da tabela
–  Desperdicio de memória

•  Alternativa: Listas

head NULL
45 IAED, 2014/2015
O que é uma estrutura auto-referenciada?

•  Estrutura em que um campo da estrutura é um


apontador para outra estrutura do mesmo tipo
typedef struct ponto {
double x;
double y;
struct ponto *origem;
} Ponto;

•  As estruturas auto-referenciadas permitem criar


estruturas de dados dinâmicas, utilizando ponteiros:
–  Listas (simplesmente e duplamente ligadas)
–  Árvores
–  etc.
47 IAED, 2014/2015
Estruturas auto-referenciadas
& Listas
K&R: Capítulo 6

1 2 3 4 5 6
NULL

head
IAED, 2014/2015
Ponto da situação e plano da aula

•  Última aula: Alocação dinâmica de memória


•  Última aula: Estruturas Auto-Referenciadas: das tabelas às listas
•  Desafios de hoje:
–  Listas de inteiros
•  Pilhas de inteiros (push, pop, etc.)
–  Listas de strings
•  Criar as seguintes funções:
–  Criar novo elemento (função NEW)
–  Inserir novo elemento (insertBegin e insertEnd)
–  Remover um elemento (delete)
–  Procura elemento (lookup)
–  Imprime no stdout todos os elementos da lista (print)

2 IAED, 2014/2015
Ponto da situação e plano da aula

•  Última aula: Alocação dinâmica de memória


•  Última aula: Estruturas Auto-Referenciadas: das tabelas às listas
•  Desafios de hoje:
–  Listas de inteiros
•  Pilhas de inteiros (push, pop, etc.)
–  Listas de strings
•  Criar as seguintes funções:
–  Criar novo elemento (função NEW)
–  Inserir novo elemento (insertBegin e insertEnd)
–  Remover um elemento (delete)
–  Procura elemento (lookup)
–  Imprime no stdout todos os elementos da lista (print)

3 IAED, 2014/2015
Ponto da situação e plano da aula

•  Última aula: Alocação dinâmica de memória


•  Última aula: Estruturas Auto-Referenciadas: das tabelas às listas
•  Desafios de hoje:
–  Listas de inteiros
•  Pilhas de inteiros (push, pop, etc.)
–  Listas de strings
•  Criar as seguintes funções:
–  Criar novo elemento (função NEW)
–  Inserir novo elemento (insertBegin e insertEnd)
–  Remover um elemento (delete)
–  Procura elemento (lookup)
–  Imprime no stdout todos os elementos da lista (print)
Ver: http://web.ist.utl.pt/aplf/aed/
wiki.cgi/Lesson14
4 IAED, 2014/2015
Alocação Dinâmica de Memória

•  Função malloc
void *malloc(size_t size);
•  Recebe como argumento o número de bytes
–  Tipo size_t representa uma dimensão em bytes
•  Devolve um ponteiro (endereço) para o primeiro byte do
bloco de memória contígua alocada
–  void * indica um ponteiro para um tipo não especificado
–  permite utilização com qualquer tipo de dados
–  posteriormente faz-se conversão para o tipo correcto por type cast

int *vec;
vec = (int*) malloc(sizeof(int)*100);
5 IAED, 2014/2015
Alocação Dinâmica de Memória

•  Libertação de memória é efectuada com a função free


void free(void *ptr);

•  Recebe como argumento o ponteiro para a primeira


posição do bloco de memória contígua a libertar
•  Não devolve nada
•  Como libertar a memória reservada com o malloc anterior?
free(vec);

•  Tanto malloc como free estão definidas em stdlib.h


–  Necessário #include <stdlib.h>
6 IAED, 2014/2015
Estruturas & pointers
•  Declaração de variável do tipo estrutura:
typedef struct ponto {
double x;
double y;
} Ponto

Ponto centro;
Ponto *pcentro = &centro;

•  Manipulação : (*<variavel>).<membro>
(*pcentro).x = 12.3;
(*pcentro).y = 5.2;

Mas isto é
7 IAED, 2014/2015
aborrecido!
Estruturas & pointers (operador ->)
•  Declaração de variável do tipo estrutura:
typedef struct ponto {
double x;
double y;
} Ponto

Ponto centro;
Ponto *pcentro = &centro;

(*<ponteiro>).<membro> é equiv. a
•  Manipulação : <ponteiro>-><membro>
pcentro->x = 12.3;
pcentro->y = 5.2;

8 IAED, 2014/2015
malloc & estruturas

•  Declaração de ponteiro para uma estrutura:


Ponto *pcentro;

•  A declaração de um ponteiro não aloca memória!!


–  Se quisermos alocar memória de forma explícita fazemos:

pcentro = (Ponto*) malloc(sizeof(Ponto));

9 IAED, 2014/2015
Estruturas, Funções e Ponteiros

•  Passagem de estruturas grandes como parâmetros é


ineficiente
–  É necessário efectuar a cópia de todos os campos
•  Utilizam-se normalmente ponteiros para estruturas
•  Podemos alterar o conteúdo dos argumentos!

void adicionaPonto(Ponto *p1, Ponto *p2, Ponto *res) {


res->x = p1->x + p2->x;
res->y = p1->y + p2->y;
}

10 IAED, 2014/2015
Tabelas/Vectores

•  Vantagens
–  Manipulação simples
–  Facilidade de acesso ao n-ésimo elemento
•  Desvantagens
–  Tamanho limitado
–  Necessidade de realocar e copiar todos os elementos se
desejar aumentar dimensão da tabela
–  Desperdicio de memória

•  Alternativa: Listas

head NULL
11 IAED, 2014/2015
Lista Simplesmente Ligada

•  Conjunto de nós
head
NULL

•  Cada nó contém
–  Informação útil
–  Ponteiro para o próximo nó

struct node {
int valor;
struct node *next;
valor next
};

12 IAED, 2014/2015
Lista Simplesmente Ligada

•  Conjunto de nós
head
NULL

•  Cada nó contém
–  Informação útil
–  Ponteiro para o próximo nó

struct node {
int valor;
struct node *next;
valor next
};

13 IAED, 2014/2015
Lista Simplesmente Ligada

•  Conjunto de nós
head
NULL

•  Cada nó contém
–  Informação útil
–  Ponteiro para o próximo nó

struct node {
char nome[256];
struct node *next;
nome next
};

14 IAED, 2014/2015
Lista Simplesmente Ligada

•  Conjunto de nós
head
NULL

•  Cada nó contém
–  Informação útil T O
H R ‘\0’
–  Ponteiro para o próximo nó

struct node {
char *nome;
struct node *next;
nome next
};

15 IAED, 2014/2015
Lista Simplesmente Ligada

•  Conjunto de nós
head
NULL

•  Cada nó contém
–  Informação útil
–  Ponteiro para o próximo nó

struct node {
Livro *livro;
struct node *next;
livro next
};

16 IAED, 2014/2015
Lista Simplesmente Ligada

•  Conjunto de nós
head
NULL

•  Cada nó contém A nossa


–  Informação útil abstração
habitual
–  Ponteiro para o próximo nó

struct node {
Item item;
struct node *next;
item next
};

17 IAED, 2014/2015
Listas

•  Vantagens
–  Tamanho ilimitado (limite na memória disponível na máquina)
–  Alocação de memória apenas para os elementos que queremos
representar
–  Inserção e remoção simples

•  Desvantagens
–  Mais difícil o acesso ao n-ésimo elemento

18 IAED, 2014/2015
Inserção na Lista de um elemento *t depois de *x

t->next = x->next;
x->next = t;

19 IAED, 2014/2015
Inserção na Lista

t->next = x->next;
x->next = t;

20 IAED, 2014/2015
Remoção do elemento depois de x da Lista

t = x->next;
x->next = t->next;
free(t);

t
free (t)

21 IAED, 2014/2015
Lista Duplamente Ligada

•  Conjunto de nós

head NULL
NULL
•  Cada nó contém
–  Informação útil
–  Ponteiro para o próximo nó e para o nó anterior

struct node {
Item item;
struct node *prev, *next;
Item prev next
};

22 IAED, 2014/2015
Exemplo: Pilha Dinâmica de Inteiros

6
top

4
top

top 6
2 top 4
2
NULL A única coisa que
não podemos perder
é o pointer para o
topo da pilha
23 IAED, 2014/2015
Exemplo: Pilha Dinâmica de Inteiros

#include <stdio.h>
#include <stdlib.h>

struct node{
int value;
struct node *next;
}; Variável global

static struct node *top;

void init() /* inicializa a pilha */


{
top = NULL;
}

24 IAED, 2014/2015
Exemplo: Pilha Dinâmica de Inteiros

void push(int value) /* introduz novo elemento no topo */


{
struct node *new;

new = (struct node *) malloc(sizeof(struct node));


new->value = value;
new->next = top; new
top 6
top = new;
} 4
top

NULL

25 IAED, 2014/2015
Exemplo: Pilha Dinâmica de Inteiros

int is_empty() /* pergunta se está vazia */


{
return top == NULL;
}

26 IAED, 2014/2015
Exemplo: Pilha Dinâmica de Inteiros
int pop() /* apaga o topo e retorna o valor apagado */
{
int value;
struct node *old;

if (!is_empty()) {
value = top->value; old top
old 6
old = top;
top = top->next; 4
top
free(old);
return value;
2
}
else
return -1; NULL
}
27 IAED, 2014/2015
Exemplo: Pilha Dinâmica de Inteiros

Problema:
•  Como obter o número de elementos da Pilha?

Solução:
•  Necessário percorrer toda a Pilha!!

28 IAED, 2014/2015
Utilização de typedef

•  É usual utilizar typedef na manipulação de estruturas


auto-referenciadas
struct node{
int value;
struct node *next;
};

typedef struct node Node;


typedef struct node* Node_ptr;

29 IAED, 2014/2015
Utilização de typedef

•  É usual utilizar typedef na manipulação de estruturas


auto-referenciadas
struct node{
int value;
struct node *next;
};

typedef struct node Node;


typedef struct node* link;

30 IAED, 2014/2015
Ponto da situação e plano da aula

•  Última aula: Alocação dinâmica de memória


•  Última aula: Estruturas Auto-Referenciadas: das tabelas às listas
•  Desafios de hoje:
–  Listas de inteiros
•  Pilhas de inteiros (push, pop, etc.)
–  Listas de strings
•  Criar as seguintes funções:
–  Criar novo elemento (função NEW)
–  Inserir novo elemento (insertBegin e insertEnd)
–  Remover um elemento (delete)
–  Procura elemento (lookup)
–  Imprime no stdout todos os elementos da lista (print)
Ver: http://web.ist.utl.pt/aplf/aed/
wiki.cgi/Lesson14
31 IAED, 2014/2015
Lista de Strings

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct node {


char *text;
struct node *next;
}Node;

typedef Node* link;

32 IAED, 2014/2015
Lista de Strings

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct node {


char *text;
struct node *next;
} *link;

33 IAED, 2014/2015
Criar lista com Argumentos da linha de
comandos

$ ./myprogram bolo1 bolo2 bolo3 bolo4 bolo5

int main(int argc, char* argv[]){ argc = 6


... argv[0] = “myprogram”
} argv[1] = “bolo1”
argv[2] = “bolo2”
argv[3] = “bolo3”
...
head
NULL

34 IAED, 2014/2015
Criar lista com Argumentos da linha de
comandos
int main(int argc, char* argv[])
{
int i;
link head = NULL;
/* inserir todos os elementos na lista*/
for(i = 1; i < argc; i++)
head = insertEnd(head, argv[i]);
print(head); /* imprime toda a lista*/
/* remover um elemento (lido do stdin) */
scanf("%d", &i);
head = delete(head, argv[i]);
print(head); /* voltamos a imprimir toda a lista */
return 0;
}

35 IAED, 2014/2015
Novo Elemento
Função auxiliar NEW responsável pela alocação de memória de tudo o
que é necessário para um novo nó, i.e., a estrutura e a string.

link NEW(char* text) B O L O ‘\0’


{
B O L O ‘\0’

text next
} x

36 IAED, 2014/2015
Novo Elemento
Função auxiliar NEW responsável pela alocação de memória de tudo o
que é necessário para um novo nó, i.e., a estrutura e a string.

Reservar memória
link NEW(char* text) para o novo nó
{
link x = (link) malloc(sizeof(struct
? node));
x->text =
(char*) malloc(sizeof(char)*(strlen(text)+1));
?
strcpy(x->text, text);
x->next = NULL;
return x;
}

37 IAED, 2014/2015
Novo Elemento
Função auxiliar NEW responsável pela alocação de memória de tudo o
que é necessário para um novo nó, i.e., a estrutura e a string.

Reservar memória
link NEW(char* text) para o novo nó
{
link x = (link) malloc(sizeof(struct node));
x->text =
(char*) malloc(sizeof(char)*(strlen(text)+1));
?
strcpy(x->text, text);
x->next = NULL;
return x;
}

38 text IAED, 2014/2015


Novo Elemento
Função auxiliar NEW responsável pela alocação de memória de tudo o
que é necessário para um novo nó, i.e., a estrutura e a string.

link NEW(char* text) Reservar memória


para a string
{
link x = (link) malloc(sizeof(struct node));
x->text =
(char*) malloc(sizeof(char)*(strlen(text)+1));
?
strcpy(x->text, text);
x->next = NULL;
return x;
}

39 text IAED, 2014/2015


Novo Elemento
Função auxiliar NEW responsável pela alocação de memória de tudo o
que é necessário para um novo nó, i.e., a estrutura e a string.

link NEW(char* text) Reservar memória


para a string
{
link x = (link) malloc(sizeof(struct node));
x->text =
(char*) malloc(sizeof(char)*(strlen(text)+1));
strcpy(x->text, text);
x->next = NULL; X X X X X
return x;
}

40 text IAED, 2014/2015


Novo Elemento
Função auxiliar NEW responsável pela alocação de memória de tudo o
que é necessário para um novo nó, i.e., a estrutura e a string.

link NEW(char* text)


{
link x = (link) malloc(sizeof(struct node));
x->text =
(char*) malloc(sizeof(char)*(strlen(text)+1));
strcpy(x->text, text);
x->next = NULL; B O L O ‘\0’
return x;
}

41 text IAED, 2014/2015


Novo Elemento
Função auxiliar NEW responsável pela alocação de memória de tudo o
que é necessário para um novo nó, i.e., a estrutura e a string.

link NEW(char* text)


{
link x = (link) malloc(sizeof(struct node));
x->text =
(char*) malloc(sizeof(char)*(strlen(text)+1));
strcpy(x->text, text);
x->next = NULL; B O L O ‘\0’
return x;
}

NULL
42 text IAED, 2014/2015
Inserção na Lista

t->next = x->next;
x->next = t;

43 IAED, 2014/2015
Inserção na Lista

t->next = x->next;
x->next = t;

44 IAED, 2014/2015
Inserção no Início (mais fácil)

link insertBegin(link head, char* text)


{
link x = NEW(text);
x->next = head;
?
return x;
}

45 IAED, 2014/2015
Inserção no Início

link insertBegin(link head, char* text)


{
link x = NEW(text);
x->next = head;
return x;
}

int main(){
(...)
head = insertBegin(head, “Bolo”);
(...)
}

46 IAED, 2014/2015
Inserção no Fim

link insertEnd(link head, char* text)


{
link x; Como chego ao
fim da lista?
if(head == NULL)
return NEW(text);
for(x = head; x->next !=
? NULL; x = x->next)
;
x->next = NEW(text);
return head;
}

47 IAED, 2014/2015
Inserção no Fim

link insertEnd(link head, char* text)


{
link x;
if(head == NULL)
return NEW(text);
for(x = head; x->next != NULL; x = x->next)
;
x->next = NEW(text);
return head;
}

48 IAED, 2014/2015
Procura na Lista

link lookup(link head, char* text)


{
link t;
for(t = head; t != NULL; t = t->next)
if(strcmp(t->text,
? text) == 0)
return t;
return NULL;
}

49 IAED, 2014/2015
Procura na Lista

link lookup(link head, char* text)


{
link t;
for(t = head; t != NULL; t = t->next)
if(strcmp(t->text, text) == 0)
return t;
return NULL;
}

50 IAED, 2014/2015
Remoção da Lista

t = prev->next;
prev->next = t->next;

prev

51 IAED, 2014/2015
Remoção da Lista com Procura

prev

52 IAED, 2014/2015
Remoção da Lista com Procura
link delete(link head, char* text)
{
link t, prev;
for(t = head, prev = NULL; t != NULL;
prev = t, t = t->next) {
if(strcmp(t->text, text) == 0) {
if(t == head)
head = ?t->next;
else
prev->next = t->next;
free(t->text);
free(t);
}
}
return head;
}
53 IAED, 2014/2015
Remoção da Lista com Procura
link delete(link head, char* text)
{
link t, prev;
for(t = head, prev = NULL; t != NULL;
prev = t, t = t->next) {
if(strcmp(t->text, text) == 0) {
if(t == head)
head = t->next;
else
prev->next = t->next;
free(t->text);
free(t);
}
}
return head;
}
54 IAED, 2014/2015
Remoção da Lista com Procura
link delete(link head, char* text)
{
link t, prev;
for(t = head, prev = NULL; t != NULL;
prev = t, t = t->next) {
if(strcmp(t->text, text) == 0) {
if(t == head)
head = t->next;
else
prev->next = t->next;
FREEnode(t);
} void FREEnode(link t)
} {
return head; free(t->text);
} free(t);
}
55 IAED, 2014/2015
Inserção no Início – Sem Valor de Retorno

link insertBegin(link head, char* text)


{
link x = NEW(text);
x->next = head;
return x;
}

void insertBegin(link *headptr, char *text) /*alternativa*/


{
link x = NEW(text);
x->next = *headptr;
*headptr = x;
}

56 IAED, 2014/2015
Próxima aula: Tipos Abstractos de Dados

•  Motivação
•  Objectos
•  Pilhas
•  Exemplos de clientes
•  ADTs para FIFOs e filas

•  Tipos Abstractos de Dados = Abstract Data Types = ADTs

57 IAED, 2014/2015
ADTs (Abstract Data Types): Motivação

•  Mesmas estruturas são usadas com vários tipos de dados

Listas Inteiros
Pilhas Reais
Amontoado Strings
FIFOs Estruturas
•  O procedimento para inserir um inteiro, real, uma string ou
uma estrutura numa lista é similar
•  O código pode (e deve) ser re-utilizado
•  Abstracção dos detalhes de implementação

1 IAED, 2014/2015
ADT e Colecções de Objectos

•  ADTs são úteis para manipular colecções de objectos

•  Operações típicas
–  Comparações entre objectos
–  Operações de entrada e saída (leitura e escrita)
–  Inserção em colecções
–  Apagamento de colecções
–  Alteração de propriedades (e.g., prioridade)

•  ADTs deste tipo são denominados filas generalizadas

2 IAED, 2014/2015
Vantagens do Uso de ADTs

•  Solução elegante
•  Separa os problemas:
–  Alto nível: interface de operações sobre tipo de dados
–  Baixo nível: como manter as estruturas de dados
•  Permite comparar diferentes implementações
•  Permite re-utilizar o código

•  Para utilizarmos ADTs, vamos aprender um pouco mais


de C…

3 IAED, 2014/2015
Regras de Scope

IAED, 2014/2015
Organização de Programas

•  Programas normalmente dividos em vários ficheiros


•  Cada ficheiro permite implementar conjunto de
funcionalidades relacionadas

calculadora.c
estatistica.c
trigonom.c
aritmetica.c
5 IAED, 2014/2015
Regras de Scope

•  Em C todas as variáveis têm um scope, i.e., um âmbito


de acessibilidade & visibilidade, que podem tomar 1 de 4
(na realidade 3) estados:
–  Bloco (variáveis locais)
–  Função
–  Ficheiro (variáveis globais apenas visíveis dentro
do ficheiro onde são declaradas)
–  Programa (variáveis globais visíveis em múltiplos
ficheiros)

6 IAED, 2014/2015
Block scope

•  Em C todas as variáveis têm um scope, i.e., um âmbito


de acessibilidade & visibilidade
–  Bloco

int soma(int v[], int n) {


int i, soma=0;
for (i=0 ; i<n; i++)
soma+=v[i];
return soma;
}

7 IAED, 2014/2015
Block scope

•  Em C todas as variaveis tem um scope, i.e., um âmbito


de acessibilidade & visibilidade
–  Bloco
•  A utilização do qualificador static permite manter o
valor da variável entre chamadas à função

int soma(int v[], int n) {


int i,
static int soma=0;
for (i=0 ; i<n; i++)
soma+=v[i];
return soma;
} IAED, 2014/2015
8
Block scope

•  Em C todas as variaveis tem um scope, i.e., um âmbito


de acessibilidade & visibilidade
–  Bloco

void bubble(int a[], int l, int r){


int i, j;
for (i = l; i < r; i++)
for (j = r; j > i; j--)
if (a[j-1]>a[j]){
int t = a[j-1];
a[j-1]=a[j];
a[j]=t;
};
}
9 IAED, 2014/2015
File & Program scope: “variáveis globais”

•  Um programa C pode ser composto por um conjunto de


objectos externos, que podem ser variáveis ou funções
•  Podem ser utilizadas por qualquer função, ao contrário
de variáveis internas/locais, que apenas podem ser
utilizadas dentro da uma função ou bloco

int acumulador;

void soma(int valor) {


acumulador += valor;
}

10 IAED, 2014/2015
File scope: “variáveis globais estáticas”

•  Variáveis globais definidas como estáticas permitem


limitar o seu scope ao ficheiro em que são definidas
•  Funções também podem ser definidas como estáticas:
–  Limita scope da função entre ponto da definição e o fim do
ficheiro onde definição ocorre

static int acumulador;

void soma(int valor) {


acumulador += valor;
}

11 IAED, 2014/2015
Program scope: “variáveis externas”

•  Uma variável externa é definida quando são indicadas as


propriedades da variável, e quando são especificados os
seus requisitos em termos de memória
int a;
•  Uma variável externa é declarada quando apenas são
indicadas as suas propriedades
•  extern
externintint
b; a;
•  Uma variável apenas pode ter uma definição, embora
possa ser declarada várias vezes
–  Dimensão de um array obrigatória na definição do array, mas
opcional na declaração
–  Inicialização de uma variável externa apenas pode ter lugar na
definição da variável IAED, 2014/2015
12
De uma forma geral, temos que…

•  Os programas são divididos em vários módulos

•  Cada módulo providencia um conjunto de


funcionalidades bem definido e coerente, incluindo a
especificação, a documentação e os testes de validação
necessários.

•  No caso da programação em C, cada um desses


módulos é em geral implementado em ficheiros
diferentes e, de facto, cada módulo pode ser subdividido
também por vários ficheiros.

IAED, 2014/2015
De uma forma geral, temos que…

•  Existem em geral dois tipos de ficheiros:

–  header files, ou ficheiros de cabeçalho, onde devem


ser incluídas todas as declarações partilhadas para
um dado módulo, incluindo o cabeçalho das funções
e os tipos de dados;

–  source files, ou ficheiros fonte, onde devem ser


implementadas todas as funções.

IAED, 2014/2015
Header Files (Ficheiros de Cabeçalho)

•  São utilizados para incluirem todas as declarações


partilhadas por mais de um ficheiro.

calculadora.c
estatistica.c
trigonom.c
aritmetica.c
calculadora.h
15 IAED, 2014/2015
Exemplo 1: “variáveis externas”

•  Variáveis externas
–  utilizadas antes de serem definidas, ou definidas noutro ficheiro,
deverão ser declaradas com a palavra-chave extern

#include “stuff.h” #ifndef _STUFF_


#define _STUFF_
int x;
#include <stdio.h>
#include “stuff.h”
int main()
{ void print();
void print()
x=2; {
print(); extern int x;
printf(“%d\n”,x);
return 0; #endif }
}

stuff.h stuff.c
main.c

16 IAED, 2014/2015
Exemplo 1: “variáveis externas”

•  Variáveis externas
–  utilizadas antes de serem definidas, ou definidas noutro ficheiro,
deverão ser declaradas com a palavra-chave extern
Aqui não estou a
reservar memória.
#include “stuff.h” #ifndef _STUFF_
#define _STUFF_
int x;
#include <stdio.h>
#include “stuff.h”
int main()
{ void print();
void print()
x=2; {
print(); extern int x;
printf(“%d\n”,x);
return 0; #endif }
}

stuff.h stuff.c
main.c
Aqui estou a definir x e
a reservar memoria
17
para um inteiro IAED, 2014/2015
Exemplo 2: Módulo para números complexos

•  complexos.h
Garante que este
ficheiro é apenas
incluído uma vez.
#ifndef COMPLEXOS_H
#define COMPLEXOS_H

typedef struct {
float real, img;
} complexo;

complexo soma(complexo a, complexo b);


complexo le_complexo();
void escreve_complexo(complexo a);

#endif

IAED, 2014/2015
Exemplo 2: Módulo para números complexos
•  complexos.c #include <stdio.h>
#include "complexos.h"

complexo soma(complexo a, complexo b)


{
a.real += b.real;
a.img += b.img;
return a;
}
complexo le_complexo()
{
complexo a;
char sign;
scanf("%f%c%fi", &a.real, &sign, &a.img);
if (sign == '-’) a.img *= -1;
return a;
}
void escreve_complexo(complexo a)
{
if (a.img >= 0) printf("%f+%fi", a.real, a.img);
else printf("%f%fi", a.real, a.img);
IAED, 2014/2015
}
Exemplo 2: Módulo para números complexos
•  main.c #include <stdio.h>
#include "complexos.h"

int main()
{
complexo x, y, z;
x = le_complexo();
y = le_complexo();
z = soma(x, y);

escreve_complexo(x);
printf(" + ");
escreve_complexo(y);
printf(" = ");
escreve_complexo(z);
printf("\n");

return 0;
Compilação!
}

$ gcc -Wall -ansi -pedantic -o complexos main.c complexos.c IAED, 2014/2015


I/O
Operações sobre ficheiros

IAED, 2014/2015
Operações sobre ficheiros

•  Até este momento fizemos sempre leituras do stdin e


escrevemos sempre para o stdout. Vamos ver agora
como realizar estas operações sobre ficheiros.
•  Como abrir um ficheiro? Modo de abertura do
ficheiro. Neste caso
Ponteiro para estamos a abrir o
ficheiro ficheiro em modo de
FILE *fp; leitura

fp=fopen(”tests.txt", "r");

24 IAED, 2014/2015
Operações sobre ficheiros

•  Até este momento fizemos sempre leituras do stdin e


escrevemos sempre para o stdout. Vamos ver agora
como realizar estas operações sobre ficheiros.
•  Como abrir um ficheiro? Modos de
abertura
r – abre para leitura (read)
w – abre um ficheiro vazio para escrita (o ficheiro não precisa de existir)
a – abre para acrescentar no fim (“append” ; ficheiro não precisa de existir)
r+ – abre para escrita e leitura; começa no início; o ficheiro tem de existir
w+ – abre para escrita e leitura (tal como o “w” ignora qualquer ficheiro que
exista com o mesmo nome, criando um novo ficheiro)
a+ – abre para escrita e leitura (output é sempre colocado no fim)
…mas há mais

25 IAED, 2014/2015
Exemplo
#include <stdio.h>
#include <stdlib.h>

int main()
{
FILE *fp; Se não conseguir
abrir fp fica igual a
fp = fopen("teste.txt", "r"); NULL
if (fp == NULL) {
printf(“teste.txt: No such file or directory\n”);
exit(1);
}

return 0;
}

26 IAED, 2014/2015
Exemplo
#include <stdio.h>
#include <stdlib.h>

int main()
{
FILE *fp;

fp = fopen("teste.txt", "r");
if (fp == NULL) {
perror(“teste.txt”); Escreve a mesma
exit(1); mensagem de erro.
}

return 0;
}

27 IAED, 2014/2015
Exemplo 2
#include <stdio.h>
#include <stdlib.h>

int main()
{
FILE *fp;

fp = fopen("teste.txt", "r");
if (fp == NULL) {
perror(“teste.txt”);
exit(1); Fecha o ficheiro
}

fclose(fp);

return 0;
}
28 IAED, 2014/2015
Exemplo 3
#include <stdio.h>
#include <stdlib.h>

int main()
{
FILE *fp;

fp = fopen("teste.txt", ”w");
if (fp == NULL) {
perror(“teste.txt”); Escreve para um
exit(1); ficheiro
}

fprintf(fp, "Hi file!\n");

fclose(fp);

return 0; IAED, 2014/2015


29
}
Exemplo 3
#include <stdio.h>
#include <stdlib.h>

int main()
{
FILE *fp;

fp = fopen("teste.txt", ”w");
if (fp == NULL) {
perror(“teste.txt”); Escreve para um
exit(1); ficheiro
} (alternativa)

fputs("Hi file!”, fp);

fclose(fp);

return 0; IAED, 2014/2015


30
}
Exemplo 4
#include <stdio.h>
#include <stdlib.h>

int main()
{
FILE *myfile; int i;
float mydata[100];
myfile = fopen(”info.dat", ”r");
Lê um conjunto
if (myfile== NULL) {
de 100 floats
perror(“info.dat”);
guardados num
exit(1);
}
ficheiro
for (i=0;i<100;i++)
fscanf(myfile,”%f”,&mydata[i]);

fclose(myfile);
return 0;
} IAED, 2014/2015
31
32 IAED, 2014/2015
Tipos Abstractos de Dados

Sedgewick: Capítulo 4

IAED, 2014/2015
Outro exemplo: filas de espera (FIFO)
Interface do ADT Queue
QUEUE.h
void QUEUEinit(int);
int QUEUEempty();
void QUEUEput(Item);
Item QUEUEget();

Mais uma vez, temos (pelo menos) 2 formas de implementar


uma fila de espera ... com o mesmo interface!

Tabelas / Vectores Listas

20 IAED, 2014/2015
Implementação do ADT FIFO (Listas)
QUEUE.c
#include <stdlib.h>

#include "Item.h"
#include "QUEUE.h"

typedef struct QUEUEnode* link;

struct QUEUEnode {
Item item;
link next;
};

static link head, tail;

21 IAED, 2014/2015
Implementação do ADT FIFO (Listas)
QUEUE.c
void QUEUEinit(int maxN) {
head = NULL;
Inicio a head e a tail a
tail = NULL; NULL
}

int QUEUEempty() {
return head == NULL;
}

link NEW(Item item, link next) {


link x = (link) malloc(sizeof(struct QUEUEnode));

x->item = item;
x->next = next;
return x;
}

22 IAED, 2014/2015
Implementação do ADT FIFO (Listas)
QUEUE.c (cont)
void QUEUEput(Item item) { Quando a fila está
if (head == NULL) { vazia...
head = (tail = NEW(item, head));
return;
Nos outros casos,
}
insiro no fim.
tail->next = NEW(item, tail->next);
tail = tail->next;
}
Actualizo a tail.
Item QUEUEget() {
Item item = head->item; Guardo o conteudo do
link t = head->next; primeiro elemento numa
variavel auxiliar.
free(head);
head = t;
return item; Liberto a memória e
retorno o elemento
}
removido
23 IAED, 2014/2015
Implementação do ADT FIFO (Tabelas)
QUEUE.c
#include <stdlib.h>
0
#include "Item.h " N
#include "QUEUE.h"
Circular buffer
static Item *q;
static int N, head, tail;

void QUEUEinit(int maxN) {


q = (Item *) malloc((maxN+1)*sizeof(Item));
N = maxN+1;
head = N;
tail = 0;
}
Truque para podermos
distinguir entre full &
empty queue...

24 IAED, 2014/2015
Implementação do ADT FIFO (Tabelas)
QUEUE.c (cont)
int QUEUEempty() {
return head % N == tail; 0
} N

void QUEUEput(Item item) { Circular buffer


q[tail++] = item;
tail = tail % N;
}

Item QUEUEget() {
head = head % N;
return q[head++];
}

25 IAED, 2014/2015
ADTs de 1a Ordem

•  Nos ADTs estudados só é possível ter uma instância da


estrutura de dados
–  A informação da estrutura de dados é guardada em variáveis
globais
–  As funções operam sobre uma única estrutura de dados

•  Como fazer quando pretendemos ter várias instâncias


do mesmo ADT ?
–  Exemplo: múltiplas FIFOS

26 IAED, 2014/2015
ADTs de 1a Ordem
•  Solução: em vez da informação da estrutura de dados
ser guardada em variáveis globais, é guardada numa
estrutura que é passada como argumento a cada função
QUEUE.h
typedef struct queue *Q;
typedef struct QUEUEnode *link;
struct QUEUEnode { Item item; link next; };
struct queue { link head; link tail; };
QUEUE.h (original)
typedef struct QUEUEnode* link;
Q QUEUEinit(int maxN); QUEUE.h (original)

int QUEUEempty(Q); struct QUEUEnodevoid


{ QUEUEinit(int);
Item int
item; link QUEUEempty();
next; };
void
27
QUEUEput(Q, Item); void QUEUEput(Item);
static Item
link head, IAED,QUEUEget();
tail;2014/2015
Item QUEUEget(Q);
Implementação do ADT FIFO de 1a Ordem
QUEUE.c (cont)
#include <stdlib.h>

#include "Item.h"
#include "QUEUE.h"

link NEW(Item item, link next) {


link x = (link) malloc(sizeof(struct QUEUEnode));

x->item = item;
QUEUE.c (original)
x->next = next;
link NEW(Item item, link next) {
return x; link x = (link) malloc(sizeof(struct QUEUEnode));
}
x->item = item;
x->next = next;
return x;
28 } IAED, 2014/2015
Implementação do ADT FIFO de 1a Ordem
QUEUE.c (cont)
Q QUEUEinit(int maxN) {
Q q = (Q)malloc(sizeof(struct queue));
q->head = NULL;
q->tail = NULL;
return q;
}

int QUEUEempty(Q q) {
return q->head == NULL;
}
QUEUE.c (original)
void QUEUEinit(int maxN) {
head = NULL;
tail = NULL;
}

int QUEUEempty() {
return head == NULL;
29 } IAED, 2014/2015
Implementação do ADT FIFO de 1a Ordem
QUEUE.c (cont)

void QUEUEput(Q q, Item item) {


if (q->head == NULL) {
q->tail = NEW(item, q->head);
q->head = q->tail;
return;
}
q->tail->next = NEW(item, q->tail->next);
q->tail = q->tail->next;
}
QUEUE.c (original)
void QUEUEput(Item item) {
if (head == NULL) {
head = (tail = NEW(item, head));
return;
}
tail->next = NEW(item, tail->next);
tail = tail->next;
30 } IAED, 2014/2015
Implementação do ADT FIFO de 1a Ordem
QUEUE.c (cont)

Item QUEUEget(Q q) {
Item item = q->head->item;
link t = q->head->next;
free(q->head);
q->head = t;
return item;
}

QUEUE.c (original)
Item QUEUEget() {
Item item = head->item;
link t = head->next;
free(head);
head = t;
return item;
31 } IAED, 2014/2015
Implementação do ADT FIFO de 1a Ordem
QUEUE.c (cont)

Item QUEUEget(Q q) {
Item item = q->head->item;
link t = q->head->next;
free(q->head);
q->head = t;
return item; /* esta função retorna o item de forma a que a sua
informação seja processada (se for o caso) e, se necessário, a
sua memoria é libertada posteriormente */
}

32 IAED, 2014/2015
Cliente do ADT FIFO de 1a Ordem
#include <stdio.h>
#include <stdlib.h>
#include "Item.h"
#include "QUEUE.h"
#define M 10

int main(int argc, char *argv[]) {


int i, j, N = atoi(argv[1]);
Q queues[M];

for (i = 0; i < M; i++)


queues[i] = QUEUEinit(N);
for (i = 0; i < N; i++)
QUEUEput(queues[rand() % M], i);
for (i = 0; i < M; i++, printf("\n"))
for (j = 0; !QUEUEempty(queues[i]); j++)
printf("%3d ", QUEUEget(queues[i]));
return 0;
}

33 IAED, 2014/2015
Conseguem imaginar uma forma alternativa
de implementar uma queue FIFO?

•  Usando listas duplamente ligadas


•  Guardando apenas a head (i.e., não temos a tail)
•  Inserção e remoção: O(1)

head
prev next

NULL
NULL

34 IAED, 2014/2015
Conseguem imaginar uma forma alternativa
de implementar uma queue FIFO?

•  Usando listas duplamente ligadas


•  Guardando apenas a head (i.e., não temos a tail)
•  Inserção e remoção: O(1)
•  A tail é sempre a head->prev
head
prev next

35 IAED, 2014/2015
Tabelas de Dispersão

Sedgewick: Capítulo 14

IAED, 2014/2015
Tabelas de Dispersão (Hash Tables)

•  Funções de dispersão
•  Encadeamento externo
•  Procura linear
•  Double hashing (dispersão dupla)
•  Eficiência da procura

37 IAED, 2014/2015
Tabelas de Dispersão - Motivação

•  Procurar um dado elemento numa estrutura de dados

–  Vector

–  Lista

–  Compara a chave de cada elemento com a chave pretendida

38 IAED, 2014/2015
Tabelas de Dispersão

•  Guardar

função de
dispersão índice
•  Procurar

transforma
uma chave
num índice

39 IAED, 2014/2015
Função de Dispersão (Hashing)

•  Transforma a chave num índice da tabela, ou seja num


inteiro [0, M − 1], para uma tabela de dimensão M

•  Colisão: ocorre quando a função de dispersão devolve


o mesmo valor para chaves distintas

•  Função de dispersão ideal: a probabilidade de ocorrer


uma colisão para duas chaves distintas é 1/ M
–  Distribui os elementos de forma uniforme

40 IAED, 2014/2015
Função de Dispersão

•  Deve distribuir as chaves de forma uniforme e quase


aleatória
•  Deve ser rápida de calcular
•  Deve envolver todos os bits da chave
•  Diferentes funções devem ser usadas para diferentes
tipos de dados
–  Cadeias de caracteres
–  Inteiros
–  Reais

41 IAED, 2014/2015
Função de Dispersão – Números

•  hash(k ) = k mod M

int hash(int value, int M)


{
return (value%M);
}

43 IAED, 2014/2015
Função de Dispersão – Strings

•  Calcula uma soma ponderada dos caracteres


•  Soma é feita módulo M
•  M deve ser um primo, para evitar colisões

int hash(char *v, int M)


{
int h = 0, a = 127;

for (; *v != ’\0’; v++)


h = (a*h + *v) % M;
return h;
}

44 IAED, 2014/2015
Função de Dispersão – Strings v2.0

•  Recalcula a base em cada iteração


•  Evita anomalias com chaves altamente regulares

int hashU(char *v, int M)


{
int h, a = 31415, b = 27183;

for (h = 0; *v != ’\0’; v++, a = a*b % (M-1))


h = (a*h + *v) % M;
return h;
}

45 IAED, 2014/2015
Colisões

•  Guardar / Procurar
colisão !

função de
dispersão

•  Como resolver colisões ?

46 IAED, 2014/2015
Resolução por Encadeamento Externo

•  Cada posição da tabela tem um ponteiro para uma lista


•  Colisões resolvidas juntando o elemento ao início da lista
•  Remoções resolvidas removendo o elemento da lista

0 707
1
2 72
3 Ordem de inserção
4 707, 72, 14, 70, 76, 20
5
6
hash(k ) = k mod 7
47 IAED, 2014/2015
Resolução por Encadeamento Externo

•  Cada posição da tabela tem um ponteiro para uma lista


•  Colisões resolvidas juntando o elemento ao início da lista
•  Remoções resolvidas removendo o elemento da lista

0 14 707
1
2 72
3 Ordem de inserção
4 707, 72, 14, 70, 76, 20
5
6
hash(k ) = k mod 7
48 IAED, 2014/2015
Resolução por Encadeamento Externo

•  Cada posição da tabela tem um ponteiro para uma lista


•  Colisões resolvidas juntando o elemento ao início da lista
•  Remoções resolvidas removendo o elemento da lista

0 70 14 707
1
2 72
3 Ordem de inserção
4 707, 72, 14, 70, 76, 20
5
6
hash(k ) = k mod 7
49 IAED, 2014/2015
Resolução por Encadeamento Externo

•  Cada posição da tabela tem um ponteiro para uma lista


•  Colisões resolvidas juntando o elemento ao início da lista
•  Remoções resolvidas removendo o elemento da lista

0 70 14 707
1
2 72
3 Ordem de inserção
4 707, 72, 14, 70, 76, 20
5
6 76 hash(k ) = k mod 7
50 IAED, 2014/2015
Resolução por Encadeamento Externo

•  Cada posição da tabela tem um ponteiro para uma lista


•  Colisões resolvidas juntando o elemento ao início da lista
•  Remoções resolvidas removendo o elemento da lista

0 70 14 707
1
2 72
3 Ordem de inserção
4 707, 72, 14, 70, 76, 20
5
6 20 76 hash(k ) = k mod 7
51 IAED, 2014/2015
Resolução por Encadeamento Externo
static link *heads;
static int M;
void STinit(int max) {
int i;
M = max;
heads = (link*)malloc(M*sizeof(link));
for (i = 0; i < M; i++) heads[i] = NULL;
}
void STinsert(Item item) {
int i = hash(key(item), M);
heads[i] = insertBeginList(heads[i], item);
}
void STdelete(Item item) {
int i = hash(key(item), M);
heads[i] = removeItemList(heads[i], item);
}
Item STsearch(Key v) {
int i = hash(v, M);
return searchList(heads[i], v);
}

52 IAED, 2014/2015
Resolução por Encadeamento Externo

•  Comprimento médio das listas é N / M

•  A probabilidade de o número de chaves numa lista ser


t
maior ou igual a tα é de (α e / t ) e−α onde α = N / M

•  Se α = 20, a probabilidade de uma lista ter mais de 40


items é 0.0000016.

53 IAED, 2014/2015
Exercício

Considere a seguinte função de dispersão:

int hash(int value, int M) {


return value % M;
}

Usando uma tabela de dispersão por encadeamento externo (external


chaining) para guardar elementos com as seguintes chaves

V={13, 11, 10, 2, 22, 35, 45, 1, 143, 333, 6, 99998}

e a função de dispersão definida em cima, e sabendo que M = 10, qual ou


quais são as chaves dos elementos guardados na posição de índice igual a 3
da tabela? Indique o número de colisões?

54 IAED, 2014/2015
Exercício V={13, 11, 10, 2, 22, 35, 45, 1, 143, 333, 6, 99998}

0 10

1 11

2 2

3 13

4
5
6
7
8
9
55 IAED, 2014/2015
Exercício V={13, 11, 10, 2, 22, 35, 45, 1, 143, 333, 6, 99998}

0 10

1 11

2 22 2

3 13

4
5 35

6
7
8
9
56 IAED, 2014/2015
Exercício V={13, 11, 10, 2, 22, 35, 45, 1, 143, 333, 6, 99998}

0 10

1 11

2 22 2

3 13

4
5 45 35

6
7
8
9
57 IAED, 2014/2015
Exercício V={13, 11, 10, 2, 22, 35, 45, 1, 143, 333, 6, 99998}

0 10

1 1 11

2 22 2

3 13

4
5 45 35

6
7
8
9
58 IAED, 2014/2015
Exercício V={13, 11, 10, 2, 22, 35, 45, 1, 143, 333, 6, 99998}

0 10

1 1 11

2 22 2

3 333 143 13

4 Resposta:
Posição 3: 333, 143, 13
5 45 35
Colisões: 5
6 6

7
8 99998

9
59 IAED, 2014/2015
Resolução por Procura Linear

•  Cada posição da tabela guarda um elemento


•  Necessário conhecer número de elementos à priori
•  Se a posição k estiver ocupada, guarda o item na posição
seguinte livre (linear probing).

0 707 14 !!
1
2 72
Ordem de inserção
3
707, 72, 14, 70, 76, 20
4
5
6
hash(k ) = k mod 7
61 IAED, 2014/2015
Resolução por Procura Linear

•  Cada posição da tabela guarda um elemento


•  Necessário conhecer número de elementos à priori
•  Se a posição k estiver ocupada, guarda o item na posição
seguinte livre (linear probing).

0 707
1 14
2 72
Ordem de inserção
3
707, 72, 14, 70, 76, 20
4
5
6
hash(k ) = k mod 7
62 IAED, 2014/2015
Resolução por Procura Linear

•  Cada posição da tabela guarda um elemento


•  Necessário conhecer número de elementos à priori
•  Se a posição k estiver ocupada, guarda o item na posição
seguinte livre (linear probing).

0 707 70 !!
1 14
2 72
Ordem de inserção
3
707, 72, 14, 70, 76, 20
4
5
6
hash(k ) = k mod 7
63 IAED, 2014/2015
Resolução por Procura Linear

•  Cada posição da tabela guarda um elemento


•  Necessário conhecer número de elementos à priori
•  Se a posição k estiver ocupada, guarda o item na posição
seguinte livre (linear probing).

0 707
1 14
2 72
Ordem de inserção
3 70
707, 72, 14, 70, 76, 20
4
5
6 76
hash(k ) = k mod 7
64 IAED, 2014/2015
Resolução por Procura Linear

•  Cada posição da tabela guarda um elemento


•  Necessário conhecer número de elementos à priori
•  Se a posição k estiver ocupada, guarda o item na posição
seguinte livre (linear probing).

0 707
1 14
2 72
Ordem de inserção
3 70
707, 72, 14, 70, 76, 20
4
5
20 6 76
hash(k ) = k mod 7
65 IAED, 2014/2015
Resolução por Procura Linear

•  Cada posição da tabela guarda um elemento


•  Necessário conhecer número de elementos à priori
•  Se a posição k estiver ocupada, guarda o item na posição
seguinte livre (linear probing).

0 707
1 14
2 72
Ordem de inserção
3 70
707, 72, 14, 70, 76, 20
4 20
5
6 76
hash(k ) = k mod 7
66 IAED, 2014/2015
Resolução por Procura Linear: Init & Insert
#include <stdlib.h>
#include "Item.h"
#define null(A) (key(st[A]) == key(NULLitem))
static int N, M;
static Item *st;
void STinit(int max) {
int i;
N = 0;
M = 2*max;
st = (Item*)malloc(M*sizeof(Item));
for (i = 0; i < M; i++)
st[i] = NULLitem;
}
void STinsert(Item item) {
Key v = key(item);
int i = hash(v, M);
while (!null(i)) i = (i+1) % M;
st[i] = item;
N++;
}

67 IAED, 2014/2015
Resolução por Procura Linear: search

•  Resolução de conflitos: se a posição correspondente ao


índice devolvido pela função de dispersão estiver
ocupada, incrementar o índice até encontrar primeira
posição livre.
Item STsearch(Key v)
{
int i = hash(v, M);
while (!null(i))
if (eq(v, key(st[i]))
return st[i];
else
i = (i+1) % M;
return NULLitem;
}

68 IAED, 2014/2015
Remoção de Items em Procura Linear

•  Após achar o item, coloca-se a posição a NULL


•  Reinserem-se todos os items que se seguem, até à próxima
posição vazia k
hash(k ) = k mod 7 Ordem de inserção
707, 72, 14, 70, 76, 20
0 707
1 14 Remover(14)
2 72 14%7 à k=0
3 70
k++ à encontrei! à remove
4 20
5
6 76

69 IAED, 2014/2015
Remoção de Items em Procura Linear

•  Após achar o item, coloca-se a posição a NULL


•  Reinserem-se todos os items que se seguem, até à próxima
posição vazia k
hash(k ) = k mod 7 Ordem de inserção
707, 72, 14, 70, 76, 20
0 707
1 Remover(14)
2 72 14%7 à k=0
3 70
k++ à encontrei! à remove
4 20
à re-introduza todos os
5
elementos seguintes até
6 76 encontrar uma caixa vazia
70 IAED, 2014/2015
Remoção de Items em Procura Linear

•  Após achar o item, coloca-se a posição a NULL


•  Reinserem-se todos os items que se seguem, até à próxima
posição vazia k
hash(k ) = k mod 7 Ordem de inserção
707, 72, 14, 70, 76, 20
0 707
1 70 Remover(14)
2 72 14%7 à k=0
3 20
k++ à encontrei! à remove
4
à re-introduza todos os
5
elementos seguintes até
6 76 encontrar uma caixa vazia
71 IAED, 2014/2015
Remoção de Items em Procura Linear
•  Após achar o item, coloca-se a posição a NULL
•  Reinserem-se todos os items que se seguem, até à
próxima posição vazia
void STdelete(Item item)
{
int j, i = hash(key(item), M); Item v;
while (!null(i))
if (eq(key(item), key(st[i])) break;
else i = (i+1) % M;
if (null(i)) return;
st[i] = NULLitem;
N--;
for (j = (i+1) % M; !null(j); j = (j+1) % M, N--) {
v = st[j];
st[j] = NULLitem;
STinsert(v);
}
}

72 IAED, 2014/2015
Resolução por Procura Linear

•  α = N / M tem de ser menor que 1.


•  Número de operações necessário para achar um item é:
1 ⎛ 1 ⎞
–  Hits: ⎜ 1 + ⎟
2 ⎝ 1 − α ⎠
1 ⎛ 1 ⎞
–  Misses: ⎜1 + ⎟
2 ⎝ (1 − α ) 2 ⎠

•  Número de operações cresce rapidamente quando α → 1.0


α 0.5 0.667 0.75 0.9
hit 1.5 2.0 3.0 5.5
miss 2.5 5.0 8.5 55.5

73 IAED, 2014/2015
Exercício

Considere a seguinte função de dispersão:

int hash(int value, int M) {


return value % M;
}

Usando uma tabela de dispersão por encadeamento linear (linear probing)


para guardar elementos com as seguintes chaves

w={13, 11, 10, 2, 22, 34, 45, 1}


e a função de dispersão definida em cima, e sabendo que M = 10, qual ou
indice da entrada da tabela do último elemento?

74 IAED, 2014/2015
Exercício V={13, 11, 10, 2, 22, 34, 45, 1}

0 10

1 11

2 2

3 13

4 22

5 34

6 45

7 1

8
9
75 IAED, 2014/2015
Exercício

Considere a seguinte função de dispersão:

int hash(int value, int M) {


return value % M;
}

Usando uma tabela de dispersão por encadeamento linear (linear probing)


para guardar elementos com as seguintes chaves

w={13, 11, 10, 2, 22, 34, 45, 1}


e a função de dispersão definida em cima, e sabendo que M = 10, qual ou
indice da entrada da tabela do último elemento? Qual a distribuição das
chaves na tabela se removermos o elemento 22?

76 IAED, 2014/2015
Exercício V={13, 11, 10, 2, 22, 34, 45, 1}

0 10

1 11

2 2

3 13

4 22

5 34

6 45

7 1

8
9
77 IAED, 2014/2015
Exercício V={13, 11, 10, 2, 22, 34, 45, 1}

0 10

1 11

2 2

3 13

4
5 34

6 45

7 1

8
9
78 IAED, 2014/2015
Exercício V={13, 11, 10, 2, 22, 34, 45, 1}

0 10

1 11

2 2

3 13

4 34

5 45

6 1

7
8
9
79 IAED, 2014/2015
Resolução por Double Hashing

•  Guarda elementos na tabela, como na procura linear

•  Usa outra técnica para resolver conflitos


–  Em vez de procurar em sequência, usa uma segunda função de
hashing para determinar o incremento
–  Incremento deve ser maior que 0 e primo relativamente ao
tamanho da tabela
–  Diminui o número de operações relativamente à procura linear
–  Operações só se tornam demasiado lentas quando o factor de
carga atinge o valor 90% - 95%

80 IAED, 2014/2015
Resolução por Double Hashing

void STinsert(Item item) { Procura Linear


Key v = key(item);
void STinsert(Item item) {
int i = hash(v, M); Key v = key(item);
int k = hashtwo(v, M); int i = hash(v, M);
while (!null(i)) i = (i+k) % M; while (!null(i)) i = (i+1) % M;
st[i] = item; N++; st[i] = item; N++;
} }

Item STsearch(Key v) {
Item STsearch(Key v) {
int i = hash(v, M);
int i = hash(v, M); while (!null(i))
int k = hashtwo(v, M); if (eq(v, key(st[i]))
while (!null(i)) return st[i];
if eq(v, key(st[i])) else
return st[i]; i = (i+1) % M;
return NULLitem;
else i = (i+k) % M;
}
return NULLitem;
}
81 IAED, 2014/2015
Resolução por Double Hashing

•  α = N / M tem de ser menor que 1.


•  Número de operações necessário para achar um item é:
1
⎛ 1 ⎞
–  Hits: ln
α ⎜⎝ 1 − α ⎟⎠
1
–  Misses:
1−α

•  Número de operações cresce rapidamente quando α → 1.0


α 0.5 0.667 0.75 0.9
hit 1.4 1.6 2.8 2.6
miss 1.5 2.0 3.0 5.5

82 IAED, 2014/2015
Hash Tables: Exercício (Double Hashing)
Considere uma tabela de dispersão de dimensão M=7 com
resolução por double hashing e funções de dispersão:

H1(k)=k mod M
H2(k)=1+3k
Qual o conteúdo da tabela depois de inserirmos (por esta ordem)
os seguintes valores
9, 7, 8, 1, 3, 0

0: 7 0
1: 8 1
2: 9
3: 3
4:
5:
83 6: IAED, 2014/2015
Hash Tables: Exercício (Double Hashing)
Considere uma tabela de dispersão de dimensão M=7 com
resolução por double hashing e funções de dispersão:

h1(h)=k mod M
h2(h)=1+3k
Qual o conteúdo da tabela depois de inserirmos (por esta ordem)
os seguintes valores
9, 7, 8, 1, 3, 0

0: 7
1: 8
2: 9
3: 3
4: 0
5: 1
84 6: IAED, 2014/2015
Tabelas de Dispersão Dinâmicas

•  Carga da tabela aumenta - custo de inserção e procura


aumenta
–  tabela esparsa (ou encadeamento externo) - aumento é gradual
–  tabela não esparsa - aumento incomportável
–  procura linear e dispersão dupla - carga máxima 1

•  Solução: duplicar tamanho da tabela quando esta fica


meio cheia
–  No entanto, duplicação tem custo alto
–  mas, é pouco frequente

85 IAED, 2014/2015
Solução Dinâmica para Procura Linear
void expand();
void STinsert(Item item) {
Key v = key(item);
int i = hash(v, M);
while (!null(i))
i = (i+1) % M;
st[i] = item;
if (N++ > M/2)
expand();
} Aloca Memória;
void expand() { Cria um novo st;
int i; Duplica o M
Item *t = st;
init(M+M);
for (i = 0; i < M/2; i++)
if (key(t[i]) != key(NULLitem))
STinsert(t[i]);
free(t);
}

86 IAED, 2014/2015
Tabelas de Dispersão - Conclusão

•  Vantagens
–  Concretizam operações de inserção e procura em tempo
constante (caso médio)
•  procura linear - a mais rápida (tabela esparsa)
•  dispersão dupla - melhor compromisso tempo/memória
•  encadeamento externo - mais fácil de concretizar, maior carga mas
pior uso de memória
•  Inconvenientes
–  Não há garantia de desempenho
–  Custo de função de dispersão alto se chaves longas
–  Ocupam mais memória do que necessário
–  Não suportam eficientemente as operações de ordenação e
selecção

87 IAED, 2014/2015
Tabelas de Dispersão

Sedgewick: Capítulo 14

IAED, 2014/2015
Resolução por Double Hashing

•  Guarda elementos na tabela, como na procura linear

•  Usa outra técnica para resolver conflitos


–  Em vez de procurar em sequência, usa uma segunda função de
hashing para determinar o incremento
–  Incremento deve ser maior que 0 e primo relativamente ao
tamanho da tabela
–  Diminui o número de operações relativamente à procura linear
–  Operações só se tornam demasiado lentas quando o factor de
carga atinge o valor 90% - 95%

76 IAED, 2014/2015
Resolução por Double Hashing

void STinsert(Item item) { Procura Linear


Key v = key(item);
void STinsert(Item item) {
int i = hash(v, M); Key v = key(item);
int k = hashtwo(v, M); int i = hash(v, M);
while (!null(i)) i = (i+k) % M; while (!null(i)) i = (i+1) % M;
st[i] = item; N++; st[i] = item; N++;
} }

Item STsearch(Key v) {
Item STsearch(Key v) {
int i = hash(v, M);
int i = hash(v, M); while (!null(i))
int k = hashtwo(v, M); if (eq(v, key(st[i]))
while (!null(i)) return st[i];
if eq(v, key(st[i])) else
return st[i]; i = (i+1) % M;
return NULLitem;
else i = (i+k) % M;
}
return NULLitem;
}
77 IAED, 2014/2015
Hash Tables: Exercício (Double Hashing)
Considere uma tabela de dispersão de dimensão M=7 com
resolução por double hashing e funções de dispersão:

H1(k)=k mod M
H2(k)=1+3k
Qual o conteúdo da tabela depois de inserirmos (por esta ordem)
os seguintes valores
9, 7, 8, 1, 3, 0

0: 7 0
1: 8 1
2: 9
3: 3
4:
5:
79 6: IAED, 2014/2015
Hash Tables: Exercício (Double Hashing)
Considere uma tabela de dispersão de dimensão M=7 com
resolução por double hashing e funções de dispersão:

h1(h)=k mod M
h2(h)=1+3k
Qual o conteúdo da tabela depois de inserirmos (por esta ordem)
os seguintes valores
9, 7, 8, 1, 3, 0

0: 7
1: 8
2: 9
3: 3
4: 0
5: 1
80 6: IAED, 2014/2015
Tabelas de Dispersão Dinâmicas

•  Carga da tabela aumenta - custo de inserção e procura


aumenta
–  tabela esparsa (ou encadeamento externo) - aumento é gradual
–  tabela não esparsa - aumento incomportável
–  procura linear e dispersão dupla - carga máxima 1

•  Solução: duplicar tamanho da tabela quando esta fica


meio cheia
–  No entanto, duplicação tem custo alto
–  mas, é pouco frequente

81 IAED, 2014/2015
Solução Dinâmica para Procura Linear
void expand();
void STinsert(Item item) {
Key v = key(item);
int i = hash(v, M);
while (!null(i))
i = (i+1) % M;
st[i] = item;
if (N++ > M/2)
expand();
} Aloca Memória;
void expand() { Cria um novo st;
int i; Duplica o M
Item *t = st;
init(M+M);
for (i = 0; i < M/2; i++)
if (key(t[i]) != key(NULLitem))
STinsert(t[i]);
free(t);
}

82 IAED, 2014/2015
Tabelas de Dispersão - Conclusão

•  Vantagens
–  Concretizam operações de inserção e procura em tempo
constante (caso médio)
•  procura linear - a mais rápida (tabela esparsa)
•  dispersão dupla - melhor compromisso tempo/memória
•  encadeamento externo - mais fácil de concretizar, maior carga mas
pior uso de memória
•  Inconvenientes
–  Não há garantia de desempenho
–  Custo de função de dispersão alto se chaves longas
–  Ocupam mais memória do que necessário
–  Não suportam eficientemente as operações de ordenação e
selecção

83 IAED, 2014/2015
Árvores AVL

IAED, 2014/2015
Resumo da aula de hoje

•  Recapitular algumas ideias da última aula


–  Pesquisa, inserção, remoção & travessias em árvores binárias
de pesquisa.

•  Árvores binárias equilibradas


–  Exemplo: Rotações, pesquisa, inserção e remoção em
AVL trees

•  Exercícios

2 IAED, 2014/2015
Estrutura de uma Árvore Binária

#include "Item.h"

typedef struct node { l r


Item item;
struct node *l;
struct node *r;
} *link;
l r l r

3 IAED, 2014/2015
Árvores de Procura Binárias (BST)

•  Nós na sub-árvore esquerda tem chaves menores ou


iguais que a raíz
•  Nós na sub-árvore direita tem chaves maiores ou iguais
que a raíz
20

12 32

8 18 23 45

2 9

4 IAED, 2014/2015
Árvores de Procura Binárias (BST)

•  Vamos procurar o 2
•  No pior caso, a pesquisa escala com a profundidade da
árvore.

20

12 32

8 18 23 45

2 9

5 IAED, 2014/2015
Inserção de um novo elemento

•  Vamos inserir o elemento “9”

20

12 32

8 18 23 45

2 9

6 IAED, 2014/2015
Remoção de um elemento
•  Mais difícil: 3 possibilidades.
20
1. 5 2.

22
7
45
3.

IAED, 2014/2015
Exemplos: Remoção de um elemento

1.  Se o nó não tiver filhos à fácil... Basta apagar.

2.  Se o nó tiver um só filho... Também é fácil.

3.  Remoção de um nó interno:


1.  substituímos o elemento a remover pelo maior dos elementos à
esquerda do elemento a ser removido.
2.  removemos esse elemento (que estará necessariamente nas
condições 1 ou 2)

12 IAED, 2014/2015
Travessia em Pre-Order

•  Visita raíz antes dos filhos:


Output: 20, 12, 8, 2, 9, 18, 32, 23, 45

void traverse(link h)
20
{
if (h == NULL) 12 32
return;
visit(h->item);
traverse(h->l); 8 18 23 45
traverse(h->r);
} 2 9

13 IAED, 2014/2015
Travessia em In-Order (sorted!!)

•  Visita a raíz depois de visitar o filho esquerdo e antes de


visitar o direito
Output: 2, 8, 9, 12, 18, 20, 23, 32, 45
void traverse(link h)
20
{
if (h == NULL) 12 32
return;
traverse(h->l);
visit(h); 8 18 23 45
traverse(h->r);
} 2 9

14 IAED, 2014/2015
Travessia em Post-Order

•  Apenas visita a raíz depois de visitar o filho direito e o


esquerdo
•  Output: 2, 9, 8, 18, 12, 23, 45, 32, 20
•  Exemplo
void de aplicação: avaliação
traverse(link h) de expressões posfixadas
20
{
if (h == NULL) 12 32
return;
traverse(h->l);
traverse(h->r); 8 18 23 45
visit(h);
} 2 9

15 IAED, 2014/2015
Pesquisas em BST

•  Geralmente eficiente: O(log N )


•  No pior caso, O( N ) para uma árvore desequilibrada
–  Ordem de inserção: 1,2,3,4,5,6,7,8
•  No caso de chaves aleatórias, O(log N )

•  Comparação com pesquisa binária em tabelas:


–  Tempo de pesquisa comparável
–  Tempo de inserção muito mais rápido O( N )

16 IAED, 2014/2015
Árvores Binárias Equilibradas

•  Evitam o pior caso de O( N )


•  Algum overhead na construção
•  Alternativa: 5
F
–  Reequilibrar uma árvore, depois de construída
–  Usar aleatoriedade 4
E
–  Usar técnicas especiais de construção
(AVL, Red-Black, etc) 3
D
2
C
1
B
0
A
17 IAED, 2014/2015
Árvores AVL (Adelson-Velskii e Landis)

•  BST em que, para cada nó interno, a profundidade


(height) de ambos os filhos difere, no máximo, em 1
3
20
Height
2 1
12 32

1 0 0 0
8 18 23 45

0 0
2 9

Nota: Estamos a considerar que a profundidade de uma folha = 0


18 IAED, 2014/2015
Árvores AVL (Adelson-Velskii e Landis)

•  Será que esta árvore esta “equilibrada”?

3
20
2 1
12 32

1 0 0 0
8 18 23 45

0 0
2 9

19 IAED, 2014/2015
Inserção em Árvores AVL

•  Inserção de um elemento pode desequilibrar a árvore

3
20
2 1
12 32

1 0 0 0
8 18 23 45

0 0
2 9

21 IAED, 2014/2015
Inserção em Árvores AVL

•  Inserção de um elemento pode desequilibrar a árvore


Vamos inserir o “1”!
3
20
Height
2 1
12 32

1 0 0 0
8 18 23 45

0 0
2 9

22 IAED, 2014/2015
Inserção em Árvores AVL

•  Inserção de um elemento pode desequilibrar a árvore

4
20
Height
3 1
12 32

2 0 0 0
8 18 23 45

1 0
2 9

0
1
23 IAED, 2014/2015
Inserção em Árvores AVL

•  Inserção de um elemento pode desequilibrar a árvore

4
20
Height
árvore 3 1
desequilibrada ! 12 32

2 0 0 0
8 18 23 45

1 0
2 9

0
1
24 IAED, 2014/2015
Inserção em Árvores AVL

•  Pode ser necessário equilibrar após a inserção

4
20
Height
árvore 3 1
desequilibrada ! 12 32

2 0 0 0
8 18 23 45

1 0
2 9

0
1
25 IAED, 2014/2015
20

Inserção em Árvores AVL 12 32

8 18 23 45

2 9

1
3
20 Height
árvore 2
1
equilibrada ! 8 32

1 1 0 0
2 12 23 45

0 0 0
1 9 18

26 IAED, 2014/2015
Inserção em Árvores AVL

•  Em vez da altura (height) podemos analisar


directamente o balance factor = height (left) – height (right)
3
20 Height
árvore 2
1
equilibrada ! 8 32

1 1 0 0
2 12 23 45

0 0 0
1 9 18

27 IAED, 2014/2015
Inserção em Árvores AVL

•  Em vez da altura (height) podemos analisar


directamente o balance factor = height (left) – height (right)
1
20 Balance factor
árvore 0
0
equilibrada ! 8 32

1 0 0 0
2 12 23 45

0 0 0
1 9 18

28 IAED, 2014/2015
Operações de Rotação - Esquerda

link rotL(link h) {
link x = h->r;
h->r = x->l;
x->l = h;
return x;
}

x
H
h h
F F
x I
D H D
G
B E G I B E

A C A C
29 IAED, 2014/2015
Operações de Rotação - Direita

link rotR(link h) {
link x = h->l;
h->l = x->r;
x->r = h;
return x;
}

D D x
h
B H B F
x h
A C F I A C E H

E G G I

30 IAED, 2014/2015
Inserção em Árvores AVL

•  Inserir novo elemento, numa folha, como numa BST


normal
•  Percorrer o caminho desde a nova folha até à raíz
–  Para cada nó encontrado, verificar se as alturas dos dois filhos
(esquerdo e direito) não diferem em mais do que 1
–  Se diferirem, equilibrar a sub-árvore com raíz nesse nó,
efectuando uma rotação (simples ou dupla)
•  Após equilibrar a sub-árvore com raíz num determinado
nó x, não será necessário equilibrar as sub-árvores com
raíz nos antepassados de x
–  Terminar após uma equilibragem, ou quando atingirmos a raíz

31 IAED, 2014/2015
Equilibragem de Árvores AVL

•  Rotação Simples I (left)

A B
B A C
C

rotação
esquerda
32 IAED, 2014/2015
Equilibragem de Árvores AVL

•  Rotação Simples II (right)

C B
B A C
A

rotação
direita
33 IAED, 2014/2015
AVL Ex. 1: Inserção com rotações simples
•  Considere a árvore em baixo. Os números ao lado correspondem
ao balance factor = height (left) – height (right)
•  Vamos inserir o elemento 18
0
45
Balance factor
0 0
36 63

0 0 0 0
27 39 54 72

34 IAED, 2014/2015
AVL Ex. 1: Inserção com rotações simples
•  Considere a árvore em baixo. Os números ao lado correspondem
ao balance factor = height (left) – height (right)
•  Vamos inserir o elemento 18
•  Esta BST está equilibrada?
1
45
Balance factor
1 0
36 63

1 0 0 0
27 39 54 72

0
18

35 IAED, 2014/2015
AVL Ex. 1: Inserção com rotações simples
•  Agora vamos introduzir o elemento 9... Está equilibrada?
•  NÃO!!!!
•  Vamos subir do 9 em direção à raiz até encontrar um elemento
desequilibrado. 2
45
Balance factor
2 0
36 63

2 0 0 0
27 39 54 72

1
18

0
36
9 IAED, 2014/2015
AVL Ex. 1: Inserção com rotações simples
•  Para equilibrar, basta fazer uma rotação simples para a direita
(rotR) aplicada ao elemento 27

2
45
2 0
36 63

2 0 0 0
27 39 54 72

1
18

0
37
9 IAED, 2014/2015
AVL Ex. 1: Inserção com rotações simples
•  Para equilibrar, basta fazer uma rotação simples para a direita
(rotR) aplicada ao elemento 27
•  ...e já está!!!
1
45
1 0
36 63

0 0 0 0
18 39 54 72

0 0
9 27

IAED, 2014/2015
AVL Ex. 1: Inserção com rotações simples

2
45
2 0
36 63

2 0 0 0
27 39 54 72

1
18 RECEITA 1: Quando o novo nó é inserido
na sub-árvore da esquerda, da sub-
0
árvore da esquerda do elemento
9 desequilibrado, basta-nos aplicar uma
IAED, 2014/2015
39
rotR a esse elemento.
AVL Ex. 2: Inserção com rotações simples
•  Considere a árvore em baixo. Os números ao lado correspondem
ao balance factor = height (left) – height (right)
•  Vamos inserir o elemento 91
-1
45
0 -1
36 63

0 0 0 -1
27 39 54 72

0
89

40 IAED, 2014/2015
AVL Ex. 2: Inserção com rotações simples
•  Considere a árvore em baixo. Os números ao lado correspondem
ao balance factor = height (left) – height (right)
•  Vamos inserir o elemento 91
-2
45
0 -2
36 63

0 0 0 -2
27 39 54 72

-1
89

41 IAED, 2014/2015
91
AVL Ex. 2: Inserção com rotações simples
•  Considere a árvore em baixo. Os números ao lado correspondem
ao balance factor = height (left) – height (right)
•  Vamos inserir o elemento 91
-2
45
0 -2
36 63

0 0 0 -2
27 39 54 72

-1

RECEITA 2: Quando o novo nó é inserido 89


na sub-árvore da direita, da sub-árvore
da direita, do elemento desequilibrado, 0
basta-nos aplicar uma rotL a esse IAED, 2014/2015
91
42
elemento.
AVL Ex. 2: Inserção com rotações simples
•  Considere a árvore em baixo. Os números ao lado correspondem
ao balance factor = height (left) – height (right)

•  RESULTADO FINAL:
-1
45
0 -1
36 63

0 0 0 0
27 39 54 89

0
0
72 91

43 IAED, 2014/2015
AVL Ex. 3: Inserção com rotações duplas
•  Considere a árvore em baixo. Os números ao lado correspondem
ao balance factor = height (left) – height (right)
•  Vamos inserir o elemento 56
-1
45
0 1
36 63

0
50

44 IAED, 2014/2015
AVL Ex. 3: Inserção com rotações duplas
•  Considere a árvore em baixo. Os números ao lado correspondem
ao balance factor = height (left) – height (right)
•  Vamos inserir o elemento 56

Receita 3: O novo nó é -2
inserido na sub-árvore 45
da direita, da sub-árvore 0 2
da esquerda do elemento 36 63
desequilibrado!!
O que fazer ? Rotação -1
dupla Esquerda-Direita 50
0
56

45 IAED, 2014/2015
Equilibragem de Árvores AVL

•  Rotação Dupla I Esquerda-Direita

C C
A B
B A

rotação
esquerda
46 IAED, 2014/2015
Equilibragem de Árvores AVL

•  Rotação Dupla I (cont) Esquerda-Direita

C B
A A C
B

rotação
esquerda, direita
47 IAED, 2014/2015
Equilibragem de Árvores AVL

•  Rotação Dupla II Direita-Esquerda

A A
C B
B C

rotação
direita
48 IAED, 2014/2015
Equilibragem de Árvores AVL

•  Rotação Dupla II (cont) Direita-Esquerda

A B
C A C
B

rotação
direita, esquerda
49 IAED, 2014/2015
AVL Ex. 3: Inserção com rotações duplas
•  Considere a árvore em baixo. Os números ao lado correspondem
ao balance factor = height (left) – height (right)
•  Vamos inserir o elemento 56

Receita 3: O novo nó é -2
inserido na sub-árvore 45
da direita, da sub-árvore 0 2
da esquerda do elemento 36 63
desequilibrado!!
O que fazer ? Rotação -1
dupla Esquerda-Direita 50 rotL

0
56

50 IAED, 2014/2015
AVL Ex. 3: Inserção com rotações duplas
•  Considere a árvore em baixo. Os números ao lado correspondem
ao balance factor = height (left) – height (right)
•  Vamos inserir o elemento 56

Receita 3: O novo nó é -2
inserido na sub-árvore 45
da direita, da sub-árvore 0 2
da esquerda do elemento 36 63
desequilibrado!!
O que fazer ? Rotação 1
dupla Esquerda-Direita 56 rotL

0
50

51 IAED, 2014/2015
AVL Ex. 3: Inserção com rotações duplas
•  Considere a árvore em baixo. Os números ao lado correspondem
ao balance factor = height (left) – height (right)
•  Vamos inserir o elemento 56

Receita 3: O novo nó é -2
inserido na sub-árvore 45
da direita, da sub-árvore 0 2
da esquerda do elemento 36 63 rotR
desequilibrado!!
O que fazer ? Rotação 1
dupla Esquerda-Direita 56

0
50

52 IAED, 2014/2015
AVL Ex. 3: Inserção com rotações duplas
•  Considere a árvore em baixo. Os números ao lado correspondem
ao balance factor = height (left) – height (right)
•  Vamos inserir o elemento 56

Receita 3: O novo nó é -1
inserido na sub-árvore 45
da direita, da sub-árvore 0 0
da esquerda do elemento 36 56 rotR
desequilibrado!!
O que fazer ? Rotação 0 0
dupla Esquerda-Direita 50 63

53 IAED, 2014/2015
AVL Ex. 3: Inserção com rotações duplas
•  Considere a árvore em baixo. Os números ao lado correspondem
ao balance factor = height (left) – height (right)
•  Vamos inserir o elemento 56
-1
•  RESULTADO FINAL: 45
0 0
36 56

0 0
50 63

54 IAED, 2014/2015
AVL Ex. 4: Inserção com rotações duplas
•  Considere a árvore em baixo. Os números ao lado correspondem
ao balance factor = height (left) – height (right)
•  Depois de inserir o elemento 65 temos:
-1
45
0 1
36 63

0
70

55 IAED, 2014/2015
AVL Ex. 4: Inserção com rotações duplas
•  Considere a árvore em baixo. Os números ao lado correspondem
ao balance factor = height (left) – height (right)
•  Depois de inserir o elemento 65 temos:
-2
45
0 -2
36 63

1
Receita 4: O novo nó é 70 rotR
inserido na sub-árvore da
esquerda, da sub-árvore da 0
direita do elemento 65
desequilibrado!!
O que fazer ? Rotação dupla
Direita-Esquerda IAED, 2014/2015
56
AVL Ex. 4: Inserção com rotações duplas
•  Considere a árvore em baixo. Os números ao lado correspondem
ao balance factor = height (left) – height (right)
•  Depois de inserir o elemento 65 temos:
-2
45
0 -2
36 63

-1
Receita 4: O novo nó é 65 rotR
inserido na sub-árvore da
esquerda, da sub-árvore da
direita do elemento 70
desequilibrado!!
O que fazer ? Rotação dupla
Direita-Esquerda IAED, 2014/2015
57
AVL Ex. 4: Inserção com rotações duplas
•  Considere a árvore em baixo. Os números ao lado correspondem
ao balance factor = height (left) – height (right)
•  Depois de inserir o elemento 65 temos:
-2
45
0 -2
36 63 rotL

-1
Receita 4: O novo nó é 65
inserido na sub-árvore da
esquerda, da sub-árvore da
direita do elemento 70
desequilibrado!!
O que fazer ? Rotação dupla
Direita-Esquerda IAED, 2014/2015
58
AVL Ex. 4: Inserção com rotações duplas
•  Considere a árvore em baixo. Os números ao lado correspondem
ao balance factor = height (left) – height (right)
•  Depois de inserir o elemento 65 temos :
-1
45
0 0
36 65 rotL

0
0
Receita 4: O novo nó é 63 70
inserido na sub-árvore da
esquerda, da sub-árvore da
direita do elemento
desequilibrado!!
O que fazer ? Rotação dupla
Direita-Esquerda IAED, 2014/2015
59
AVL Ex. 4: Inserção com rotações duplas
•  Considere a árvore em baixo. Os números ao lado correspondem
ao balance factor = height (left) – height (right)
•  Depois de inserir o elemento 65 temos (resultado final):
-1
45
0 0
36 65

0
0
63 70

60 IAED, 2014/2015
Resumo

Caso 1: Se o novo nó é inserido na sub-árvore da esquerda, da sub-


árvore da esquerda do elemento desequilibrado, basta-nos aplicar uma
rotação simples para a direita aplicada a esse elemento.

Caso 2: Se o novo nó é inserido na sub-árvore da direita, da sub-árvore


da direita do elemento desequilibrado, basta-nos aplicar uma
rotação simples para a esquerda aplicada a esse elemento.

Caso 3: Se o novo nó é inserido na sub-árvore da direita, da sub-árvore da


esquerda do elemento desequilibrado, fazemos uma
rotação dupla Esquerda-Direita

Caso 4: Se o novo nó é inserido na sub-árvore da esquerda, da sub-


árvore da direita do elemento desequilibrado, fazemos uma
rotação dupla Direita-Esquerda

61 IAED, 2014/2015
Remoção em Árvores AVL

•  Remoção de um elemento pode desequilibrar a árvore

1
20
Balance factor
1 -1
12 32

0 0 0
8 18 45

0 0
2 9

62 IAED, 2014/2015
Remoção em Árvores AVL

•  Remoção de um elemento pode desequilibrar a árvore

2
árvore 20
desequilibrada !
1 0
12 32

0 0
8 18

0 0
2 9

63 IAED, 2014/2015
Remoção em Árvores AVL

•  Remover um nó como numa BST normal


•  Percorrer o caminho desde o nó removido até à raíz
–  Para cada nó encontrado, verificar se as alturas dos dois filhos
(esquerdo e direito) não diferem em mais do que 1
–  Se diferirem, equilibrar a sub-árvore com raíz nesse nó,
efectuando uma rotação simples ou uma rotação dupla
•  Após equilibrar a sub-árvore com raíz num determinado
nó x, poderá ser necessário equilibrar as sub-árvores
com raízes nos antepassados de x
–  Terminar apenas quando atingirmos a raíz

64 IAED, 2014/2015
AVL Ex. 5: Remoção de nós
•  Considere a árvore em baixo. Os números ao lado correspondem
ao balance factor = height (left) – height (right)
•  Vamos remover o elemento 72:
1
45
0 -1
36 63

1 -1 0
27 39 72

0
0
18 40

65 IAED, 2014/2015
AVL Ex. 5: Remoção de nós
•  Considere a árvore em baixo. Os números ao lado correspondem
ao balance factor = height (left) – height (right)
•  Vamos remover o elemento 72:
2
45 rotR
0 0
36 63

1 -1
27 39

0
0
18 40

66 IAED, 2014/2015
AVL Ex. 5: Remoção de nós
•  Considere a árvore em baixo. Os números ao lado correspondem
ao balance factor = height (left) – height (right)
•  Vamos remover o elemento 72:
-1
36
1 1
27 45
1 0
0
18 39 63
0
40

67 IAED, 2014/2015
Pesquisa em Árvores AVL

•  Pesquisa é realizada como numa árvore BST normal

•  Pesquisa não altera a árvore

68 IAED, 2014/2015
Desempenho de Árvores AVL

•  Uma equilibragem (rotação dupla ou simples): O (1)

•  Pesquisa: O (log N)
–  Profundidade é O(log N ) , não é necessário equilibrar
•  Inserção: O (log N)
–  Procurar a posição para inserir é O (log N)
–  Manter equilibrada é O (log N) : subir na árvore e equilibrar 1 vez
•  Remoção: O (log N)
–  Procurar o elemento a remover é O (log N)
–  Manter equilibrada é O (log N) : subir na árvore e equilibrar no
máximo O (log N) vezes

69 IAED, 2014/2015
Exercício 1

Construa uma árvore AVL inserindo os seguintes elementos


(por esta ordem):

63, 9, 19, 27, 18, 108, 99 e 81

Desenhe a árvore equilibrada resultante, e escreva o


resultado de uma travessia pre-order sobre essa árvore.

70 IAED, 2014/2015
Exercício 1
63, 9, 19, 27, 18, 108, 99 e 81

63 19

9 63
9 RotL(9) + RotR(63)
à
18 27 108 <-RotR
19

99

81

71 IAED, 2014/2015
Exercício 1
Balance factors
-1
19
-1 -1
9 63

0 0 0
18 27 99

0 0
81 108

Travessia pre-order: 19, 9, 18, 63, 27, 99, 81, 108

72 IAED, 2014/2015
Exercício 2
Balance factors
-1
19
-1 -1
9 63

0 0 0
18 27 99

0 0
81 108

Vamos agora remover o elemento 18 !!

73 IAED, 2014/2015
Exercício 2
nó desequilibrado!
-2
19
0 -1
9 63

0 0
27 99

0 0
81 108

Vamos agora remover o elemento 18 !!

74 IAED, 2014/2015
Exercício 2
nó desequilibrado!
-2
19
0 -1
9 63

0 0
27 99

0 0
81 108

O que fazer?

75 IAED, 2014/2015
Exercício 2
nó desequilibrado!
-2
19
0 -1
9 63

0 0
27 99

0 0
81 108

O que fazer?
Rodar o elemento desequilibrado para a esquerda...
76 IAED, 2014/2015
Exercício 2

0
63
0 0
19 99

0 0
9 27 81 108

Já está!

77 IAED, 2014/2015
ADTs + AVLs: Alterações ao código anterior
ST.h
#ifndef _ST_
#define _ST_

#include <stdlib.h>
#include <stdio.h>
#include "Item.h"

typedef struct STnode* link;

void STinit(link*);
int STcount(link);
Item STsearch(link,Key);
void STinsert(link*,Item);
void STdelete(link*,Key);
void STsort(link,void (*visit)(Item));
void
78 STfree(link*);
#endif
IAED, 2014/2015
Height vai guardando
ST.c (alterações a verde) a altura do nó
#include "ST.h”

struct STnode { Item item; link l, r; int height};

link NEW(Item item, link l, link r)


{
link x = (link)malloc(sizeof(struct STnode));
x->item = item;
x->l = l;
x->r = r;
x->height=1;
return x;
}

int height(link h){


79 if (h == NULL) return 0;
return h->height;
}
IAED, 2014/2015
ST.c (alterações a verde)
link rotL(link h)
{
Actualizar a altura
int height_left, height_right; dos nós
link x = h->r; envolvidos
h->r = x->l;
x->l = h;

height_left = height(h->l);
height_right = height(h->r);
h->height =
height_left > height_right ? height_left + 1 : height_right + 1;

height_left = height(x->l);
height_right = height(x->r);
x->height =
height_left > height_right ? height_left + 1 : height_right + 1;

return x;
80
}

IAED, 2014/2015
ST.c (alterações a verde)
link rotR(link h)
{
Actualizar a altura
int height_left, height_right; dos nós
link x = h->l; envolvidos
h->l = x->r;
x->r = h;

height_left = height(h->l);
height_right = height(h->r);
h->height =
height_left > height_right ? height_left + 1 : height_right + 1;

height_left = height(x->l);
height_right = height(x->r);
x->height =
height_left > height_right ? height_left + 1 : height_right + 1;

return x;
81
}

IAED, 2014/2015
ST.c (alterações a verde)

link rotLR(link h) /*rotação dupla esquerda direita*/


{
if (h==NULL) return h;
h->l = rotL(h->l);
return rotR(h);
}

link rotRL(link h) /*rotação dupla direita esquerda*/


{
if (h==NULL) return h;
h->r = rotR(h->r);
return rotL(h);
}

int Balance(link h) {/*Balance factor*/


if(h == NULL) return 0;
return height(h->l) - height(h->r);
82
}

IAED, 2014/2015
ST.c (alterações a verde)
link AVLbalance(link h) {
int balanceFactor;
if (h==NULL) return h;

balanceFactor= Balance(h);
if(balanceFactor>1) {
if (Balance(h->l)>0) h=rotR(h);
else h=rotLR(h);
}
else if(balanceFactor<-1){
if (Balance(h->r)<0) h = rotL(h);
else h = rotRL(h);
}
else{
int height_left = height(h->l);
int height_right = height(h->r);
h->height =
height_left > height_right ? height_left + 1 : height_right + 1;
83
}
return h;
} IAED, 2014/2015
ST.c (alterações a verde)

link insertR(link h, Item item)


{
if (h == NULL)
return NEW(item, NULL, NULL);
if (less(key(item), key(h->item)))
h->l = insertR(h->l, item);
else
h->r = insertR(h->r, item);
h = AVLbalance(h);
return h;
}

84

IAED, 2014/2015
ST.c (alterações a verde)
link deleteR(link h, Key k) {
if (h == NULL) return h;
else if (less(k, key(h->item))) h->l = deleteR(h->l,k);
else if (less(key(h->item), k)) h->r = deleteR(h->r,k) ;
else{
if (h->l !=NULL && h->r !=NULL){
link aux = max(h->l);
{Item x; x = h->item; h->item = aux->item; aux->item = x;}
h->l = deleteR(h->l, key(aux->item));
}
else {
link aux = h;
if (h->l == NULL && h->r == NULL) h=NULL;
else if (h->l == NULL) h = h->r;
else h = h->l;
deleteItem(aux->item);
free(aux);
}
85
}
h = AVLbalance(h);
return h; IAED, 2014/2015
}
Árvores Binárias (1st round)

IAED, 2014/2015
Árvores Binárias

•  Estrutura de dados elementar


•  Inserção, procura e remoção de elementos em árvores.
•  Métodos de travessia de árvores
•  Exemplos
•  ADTs para árvores binárias de pesquisa
•  Arvores binárias balanciadas (AVL trees)
•  Exemplos.

2 IAED, 2014/2015
Estrutura de uma Árvore Binária

#include "Item.h"

typedef struct node { l r


Item item;
struct node *l;
struct node *r;
} *link;
l r l r

3 IAED, 2014/2015
Estrutura de uma Árvore Binária

#include "Item.h"

typedef struct node { l r


void* item;
struct node *l;
struct node *r;
} *link;
l r l r

4 IAED, 2014/2015
Árvores de Procura Binárias (BST)

•  Nós na sub-árvore esquerda têm chaves menores ou


iguais que a raíz
•  Nós na sub-árvore direita têm chaves maiores ou iguais
que a raíz
20

12 32

8 18 23 45

2 9

5 IAED, 2014/2015
Basics...

#include <stdlib.h>
#include "Item.h"

typedef struct node* link;


struct node { Item item; link l, r;};
static link head;

void init() {
head = NULL;
}

link NEW(Item item, link l, link r) {


link x = (link)malloc(sizeof(struct node));
x->item = item;
x->l = l;
x->r = r;
return x;
}

6 IAED, 2014/2015
Pesquisa de um item v: Função search

•  A pesquisa começa na raiz = head


•  Verifico se é igual ao item da raiz. Se for retorno o “link”
onde estou.
•  Caso contrário chamo a função search para o filho da
esquerda ou para o filho da direita dependendo se v é
menor ou maior que head->item 20

12 32

8 18 23 45

2 9 IAED, 2014/2015
7
Pesquisa de um item v: Função search
link search(link h, Item v) {
if (h == NULL)
return NULL;
if (eq(v, h->item))
return h;
if (less(v, h->item))
return search(h->l, v);
else
20
return search(h->r, v);
}
12 32

8 18 23 45

2 9
8 IAED, 2014/2015
Inserção de um novo elemento: insert

•  Vamos inserir o elemento “9”

20

12 32

8 18 23 45

2 9

9 IAED, 2014/2015
Inserção de um novo elemento: insert

•  Começo na raiz
•  Vou percorrendo a árvore de cima para baixo até
encontrar o lugar onde introduzir o novo elemento
•  Quando encontrar o lugar vazio (NULL) para o novo
elemento (respeitando as regras da binary search tree)
crio um novo elemento.
•  Mais uma vez, podemos fazer isto recursivamente...

10 IAED, 2014/2015
Inserção de um novo elemento: insert

link insert(link h, Item item) {


Item v = item;
if (h == NULL)
return NEW(item, NULL, NULL);
if (less(v, h->item))
h->l = insert(h->l, item);
else
h->r = insert(h->r, item);
return h;
}

exemplo de utilização por parte do cliente:


head = insert(head,10);

11 IAED, 2014/2015
Como encontrar o menor elemento abaixo de
um determinado nó?
•  Como fazer?
•  Descemos a árvore sempre pelo filho da esquerda até
encontrarmos um nó sem filho esquerdo

20

12 32

8 18 23 45

12 IAED, 2014/2015
Como encontrar o maior elemento abaixo de
um determinado nó?
•  Descemos a árvore sempre pelo filho da direita até
encontrarmos um nó sem filho na direita

20

12 32

8 18 23 45

13 IAED, 2014/2015
Max & Min

link max(link h) {
if (h==NULL || h->r==NULL) return h;
else return max(h->r);
}

link min(link h) {
if (h==NULL || h->l==NULL) return h;
else return min(h->l);
}

14 IAED, 2014/2015
Max & Min (alternativa)

link max(link h) {
while(h!=NULL && h->r!=NULL)
h=h->r;
return h;
}

link min(link h) {
while(h!=NULL && h->l!=NULL)
h=h->l;
return h;
}

15 IAED, 2014/2015
Remoção de um elemento
•  Mais difícil: 3 possibilidades.
1.  Se o nó não tiver filhos à fácil... Basta apagar.
2.  Se o nó tiver um único filho... Também é fácil.
ex: apagar o 18. Redirecionamos o filho direito do
12 para o 19 e apagamos o 18.

20

12 32

8 18 23 45

16 2 9 19 IAED, 2014/2015
Remoção de um elemento
•  Mais difícil: 3 possibilidades.
1.  Se o nó não tiver filhos à fácil... Basta apagar.
2.  Se o nó tiver um só filho... Também é fácil.
ex: apagar o 18. Redirecionamos o filho direito do
12 para o 19 e apagamos o 18.

20

12 32

8 19 23 45

17 2 9 19 IAED, 2014/2015
Remoção de um elemento
•  Mais difícil: 3 possibilidades.
1.  Se o nó não tiver filhos à fácil... Basta apagar.
2.  Se o nó tiver um só filho... Também é fácil.
3.  Remoção de um nó interno: i) substituímos o elemento
a remover pelo maior dos elementos à esquerda do
elemento a ser removido.

20

12 32

8 18 23 45

18 2 9 19 IAED, 2014/2015
Remoção de um elemento
•  Mais difícil: 3 possibilidades.
1.  Se o nodo não tiver filhos à fácil... Basta apagar.
2.  Se o nodo tiver um só filho... Também é fácil.
3.  Remoção de um nó interno: i) substituímos o elemento
a remover pelo maior dos elementos à esquerda do
elemento a ser removido. ii) removemos esse elemento
(que estará nas condições 1 ou 2)
20

9 32

8 18 23 45

19 2 9 19 IAED, 2014/2015
Delete
link delete(link h, Item item) {
link aux ;
if (h==NULL) return h;
else if (less(item, h->item)) h->l=delete(h->l,item);
else if (less(h->item, item)) h->r=delete(h->r,item);
else{
if ( h->l !=NULL && h->r !=NULL ) { /*caso 3*/
aux=max(h->l);
h->item = aux->item;
h->l=delete(h->l, aux->item);
}
else { /*casos 1 e 2*/
aux=h;
if ( h->l == NULL && h->r == NULL ) h=NULL;
else if (h->l==NULL) h=h->r;
else h=h->l;
deleteItem(aux->item);
free(aux);
}
20
}
return h;
IAED, 2014/2015
}
Número de Elementos e Profundidade

int count(link h) {
if (h==NULL) return 0;
else
return count(h->r) + count(h->l) + 1;
}

int height(link h)
{
int u, v;
if (h == NULL) return 0;
u = height(h->l);
v = height(h->r);
if (u > v) return u+1;
else return v+1;
}
Nota: Aqui estamos considerar que a profundidade de uma folha é igual a 1.
Também é comum considerar que esta é igual a 0. Nesse caso, quando
h==NULL, a função deverá retornar -1, ou seja, ...
IAED, 2014/2015
21
Número de Elementos e Profundidade

int count(link h) {
if (h==NULL) return 0;
else
return count(h->r) + count(h->l) + 1;
}

int height(link h)
{
int u, v;
if (h == NULL) return -1;
u = height(h->l);
v = height(h->r);
if (u > v) return u+1;
else return v+1;
}

22 IAED, 2014/2015
Funçao visit

/* exemplo para inteiros */


void visit (Item i)
{
printf(“%d\n”,i);
}

23 IAED, 2014/2015
Travessia em Pre-Order

•  Visita raíz antes dos filhos:


Output: 20, 12, 8, 2, 9, 18, 32, 23, 45

void traverse(link h)
20
{
if (h == NULL) 12 32
return;
visit(h->item);
traverse(h->l); 8 18 23 45
traverse(h->r);
} 2 9

24 IAED, 2014/2015
Outra alternativa…

•  Visita a raíz depois do filho esquerdo e antes do direito

void traverse(link h)
20
{
if (h == NULL) 12 32
return;
traverse(h->l);
visit(h); 8 18 23 45
traverse(h->r);
} 2 9

25 IAED, 2014/2015
Travessia em In-Order (sorted!!)

•  Visita a raíz depois do filho esquerdo e antes do direito


Output: 2, 8, 9, 12, 18, 20, 23, 32, 45

void traverse(link h)
20
{
if (h == NULL) 12 32
return;
traverse(h->l);
visit(h); 8 18 23 45
traverse(h->r);
} 2 9

26 IAED, 2014/2015
Travessia em Post-Order

•  Visita a raíz depois dos filhos


•  Output: 2, 9, 8, 18, 12, 23, 45, 32, 20
•  Exemplo de aplicação: avaliação de expressões posfixas
void traverse(link h)
20
{
if (h == NULL) 12 32
return;
traverse(h->l);
traverse(h->r); 8 18 23 45
visit(h);
} 2 9

27 IAED, 2014/2015
Pesquisas em BST
Porquê?
•  Geralmente eficiente: O(log N )
•  No pior caso, O( N ) para uma árvore desequilibrada
–  Ordem de inserção: 1,2,3,4,5,6,7,8
•  No caso de chaves aleatórias, O(log N )
Porquê?
•  Comparação com pesquisa binária em tabelas:
–  Tempo de pesquisa comparável
–  Tempo de inserção muito mais rápido
•  Tempo de pesquisa e inserção são O( N ) no pior caso
(árvore degenerada)

28 IAED, 2014/2015
Árvores Binárias Equilibradas

•  Evitam o pior caso de O( N )


•  Algum overhead na construção
•  Alternativa:
–  Reequilibrar uma árvore, depois de construída
–  Usar aleatoriedade
–  Usar técnicas especiais de construção (AVL, Red-Black, etc)

29 IAED, 2014/2015
Exercício de exame: árvores binárias
III.b) Considere a seguinte árvore binária de pesquisa:

Indique qual a sequência de elementos obtida se


Indique qual a sequência de elementos obtida se percorrer a àrvore em post-order.

Solução: -6 1 0 4 15 26 50 52 37 24 12
Solução: -6 1 0 4 15 26 50 52 37 24 12

IAED, 2014/2015
Exercício de exame: árvores binárias
Considere uma árvore binária de pesquisa t, que permite guardar
número inteiros.
Assumindo que a árvore está inicialmente vazia, indique
i)  o resultado de uma travessia pre-order depois de inserir os
seguintes elementos: {11, 15, 8, 5, 9, 1, 18}.
ii)  Qual o resultadado de uma travessia post-order depois de
remover o elemento 11 ?

IAED, 2014/2015
Exercício de exame: árvores binárias
Considere uma árvore binária de pesquisa t, que permite guardar
número inteiros.
Assumindo que a árvore está inicialmente vazia, indique
i)  o resultado de uma travessia pre-order depois de inserir os
seguintes elementos: {11, 15, 8, 5, 9, 1, 18}.
ii)  Qual o resultadado de uma travessia post-order depois de
remover o elemento 11 ?
11

8 15

5 9 18

1 i) {11, 8, 5, 1, 9, 15, 18}


IAED, 2014/2015
Exercício de exame: árvores binárias
Considere uma árvore binária de pesquisa t, que permite guardar
número inteiros.
Assumindo que a árvore está inicialmente vazia, indique
i)  o resultado de uma travessia pre-order depois de inserir os
seguintes elementos: {11, 15, 8, 5, 9, 1, 18}.
ii)  Qual o resultadado de uma travessia post-order depois de
remover o elemento 11 ?
11 3º caso!

8 15

5 9 18

1
IAED, 2014/2015
Exercício de exame: árvores binárias
Considere uma árvore binária de pesquisa t, que permite guardar
número inteiros.
Assumindo que a árvore está inicialmente vazia, indique
i)  o resultado de uma travessia pre-order depois de inserir os
seguintes elementos: {11, 15, 8, 5, 9, 1, 18}.
ii)  Qual o resultadado de uma travessia post-order depois de
remover o elemento 11 ?
9

8 15

5 9 18

1
IAED, 2014/2015
Exercício de exame: árvores binárias
Considere uma árvore binária de pesquisa t, que permite guardar
número inteiros.
Assumindo que a árvore está inicialmente vazia, indique
i)  o resultado de uma travessia pre-order depois de inserir os
seguintes elementos: {11, 15, 8, 5, 9, 1, 18}.
ii)  Qual o resultadado de uma travessia post-order depois de
remover o elemento 11 ?
9

8 15

5 18

ii) {1, 5, 8, 18, 15, 9}


1
IAED, 2014/2015
Alternativa (olhar para a árvore esquerda)
Considere uma árvore binária de pesquisa t, que permite guardar
número inteiros.
Assumindo que a árvore está inicialmente vazia, indique
i)  o resultado de uma travessia pre-order depois de inserir os
seguintes elementos: {11, 15, 8, 5, 9, 1, 18}.
ii)  Qual o resultadado de uma travessia post-order depois de
remover o elemento 11 ?
11 3º caso!

8 15

5 9 18

1
IAED, 2014/2015
Alternativa (olhar para a árvore esquerda)
Considere uma árvore binária de pesquisa t, que permite guardar
número inteiros.
Assumindo que a árvore está inicialmente vazia, indique
i)  o resultado de uma travessia pre-order depois de inserir os
seguintes elementos: {11, 15, 8, 5, 9, 1, 18}.
ii)  Qual o resultadado de uma travessia post-order depois de
remover o elemento 11 ?
15

8 18

5 9

ii) {1, 5, 9, 8, 18, 15}


1
IAED, 2014/2015
Exemplo de aplicação de ADTs
Crie um programa que permita guardar, numa árvore binária, todos os alunos de IAED, onde
cada aluno é caracterizado pelo seu nome, nota final e um inteiro identificativo do curso.
Estruture o seu programa através de ADTs. A chave (a key) utilizada para a organização da
árvore deverá ser dada pelo nome do aluno.

Cada nó da árvore binária deverá ser representado por uma estrutura do tipo:

typedef struct node { O tipo Item


Item item; ficará definido
struct node *l; em Item.h
struct node *r;
} *link;

38 IAED, 2014/2015
Item.h
#ifndef _ITEM_ a chave pela
#define _ITEM_
qual vamos arrumar
#include <stdio.h> os elementos é o
#include <stdlib.h> nome
#include <string.h>

#define key(a) (a != NULL ? a->nome : "")


#define less(a,b) (strcmp(a,b)<0)
#define eq(a,b) (strcmp(a,b)==0)
#define NULLitem NULL

typedef char* Key;


typedef struct estudante {
O Item
char* nome; int notaIAED; int curso;
vai ser um ponteiro
}* Item;
para esta estrutura
39
Item newItem(char*nome, int nota, int curso);
void deleteItem(Item a);
void visitItem(Item a);

#endif IAED, 2014/2015


Item.c
#include "Item.h"

Item newItem(char*nome, int nota, int curso)


{
Item x = (Item)malloc(sizeof(struct estudante));
x->nome = strdup(nome);
x->notaIAED = nota;
x->curso = curso;
return x;
}

void deleteItem(Item a)
{
free(a->nome);
free(a);
}

void visitItem(Item a)
40
{
printf("nome: %s\n",a->nome);
printf("notaIAED: %d\n",a->notaIAED);
printf("curso: %d\n\n",a->curso);
}
IAED, 2014/2015
Interface do ADT Tabela de Símbolos
ST.h
#ifndef _ST_
#define _ST_

#include <stdlib.h>
#include <stdio.h>
#include "Item.h"

typedef struct STnode* link;


struct STnode { Item item; link l, r;};

void STinit(link*);
int STcount(link);
Item STsearch(link, Key);
void STinsert(link*, Item);
void STdelete(link*, Key);
void
41 STsort(link, void (*visit)(Item));
void STfree(link*);
IAED, 2014/2015
#endif
ST.c

#include "ST.h"

link NEW(Item item, link l, link r)


{
link x = (link)malloc(sizeof(struct STnode));
x->item = item;
x->l = l;
x->r = r;
return x;
}

void STinit(link*head)
{
*head = NULL;
}
42

IAED, 2014/2015
ST.c

link insertR(link h, Item item) {


if (h == NULL)
return NEW(item, NULL, NULL);
if (less(key(item), key(h->item)))
h->l = insertR(h->l, item);
else
h->r = insertR(h->r, item);
return h;
}

void STinsert(link*head, Item item)


{
*head = insertR(*head, item);
}
43

IAED, 2014/2015
ST.c

Item searchR(link h, Key v)


{
if (h == NULL)
return NULLitem;
if (eq(v, key(h->item)))
return h->item;
if (less(v, key(h->item)))
return searchR(h->l, v);
else
return searchR(h->r, v);
}

Item STsearch(link head, Key v)


{
44 return searchR(head, v);
}

IAED, 2014/2015
ST.c
link deleteR(link h, Key k)
{
if (h==NULL) return h;
else if (less(k, key(h->item))) h->l=deleteR(h->l,k);
else if (less(key(h->item), k)) h->r=deleteR(h->r,k) ;
else {
if (h->l !=NULL && h->r !=NULL){/*caso 3*/
link aux=max(h->l);
{Item x; x=h->item; h->item=aux->item; aux->item=x; }
h->l= deleteR(h->l, key(aux->item));
}
else { /*casos 1 e 2*/
link aux=h;
if ( h->l == NULL && h->r == NULL ) h=NULL;
else if (h->l==NULL) h=h->r; Esta função
else h=h->l; também
deleteItem(aux->item); poderia ter sido
45
free(aux); passada como
}
}
argumento
return h;
}
IAED, 2014/2015
ST.c
link max(link h) {
if (h==NULL || h->r==NULL) return h;
else return max(h->r);
}

int count(link h){


if (h==NULL) return 0;
else return count(h->r) + count(h->l) + 1;
}

int STcount(link head){


return count(head);
}

46
void STdelete(link*head, Key k){
*head = deleteR(*head, k);
}
IAED, 2014/2015
ST.c

void sortR(link h, void (*visit)(Item))


{
if (h == NULL)
return;
sortR(h->l, visit);
visit(h->item);
sortR(h->r, visit);
}

void STsort(link head, void (*visit)(Item))


{
sortR(head, visit);
}

47

IAED, 2014/2015
ST.c

link freeR(link h)
{
if (h==NULL)
return h;
h->l=freeR(h->l);
h->r=freeR(h->r);
return deleteR(h,key(h->item));
}

void STfree(link*head)
{
*head=freeR(*head);
}

48

IAED, 2014/2015
main.c
#include <stdio.h>
#include "ST.h"

int main(){
link turma1;
STinit(&turma1);

STinsert(&turma1, newItem("Mikhail Gorbachev", 10, 0));


STinsert(&turma1, newItem("Jimmy Carter", 10, 0));
STinsert(&turma1, newItem("Barack Obama", 12, 0));

STsort(turma1,visitItem);

printf("numero de alunos: %d\n",STcount(turma1));


STdelete(&turma1,"Barack Obama");
49 printf("numero de alunos: %d\n",STcount(turma1));

STfree(&turma1);

return 0;
IAED, 2014/2015
}
Próxima aula

•  Árvores binárias equilibradas


•  AVL trees

50 IAED, 2014/2015
Grafos (I)

CLRS: Capítulo 22

IAED, 2014/2015
Grafos

•  Definição e representação
–  Matriz de adjacências
–  Listas de Adjacências
•  Interface do ADT Grafo
•  Implementação do ADT Grafo usando
–  Matriz de adjacências
–  Listas de Adjacências
•  Matrizes adjacências vs Listas de adjacências

3 IAED, 2014/2015
Grafo - Definição

•  Constituído por um conjunto V (vértices) e E (arcos/edges)


–  Arco liga dois vértices
–  Vértice pode estar ligado a qualquer número de outros vertices

vértice / nó
arco / link / aresta

4 IAED, 2014/2015
Grau de um nó
•  O grau de um vértice (d, degree) contabiliza o número de
ligações/arcos de um vértice (ou nodo).
•  O grau médio de um grafo (z), é a média dos graus de
todos os vértices
d=1
vértice / nó
arco / link / aresta
d=1
d=2
d=1 d=0

d=1
d=3
d=3
d=1
d=4

d=2 d=1 IAED, 2014/2015


5
Passamos a ter
Outros Conceitos um in-degree e um
out-degree

•  Grafo Dirigido
–  Os arcos podem ter direcção

•  Grafo Pesado
2
–  Os arcos podem ter peso (custo)
3
4
5 3
2 3
3 5

1 4

6 IAED, 2014/2015
Outros Conceitos

•  Grafos acíclicos dirigidos


–  para qualquer vértice v, não há
nenhum caminho começando e
acabando em v.

•  Grafo conexo
–  Para quaisquer vértices v e u, há
sempre o caminho a ligar u e v

7 IAED, 2014/2015
Outros Conceitos

•  Bi-connected graphs
–  para qualquer vértice v, se
removermos v, o grafo continua
conexo

•  Grafo conexo
–  Para quaisquer vértices v e u, há
sempre o caminho a ligar u e v

8 IAED, 2014/2015
Exemplos de grafos

IAED, 2014/2015
Francisco C. Santos, IST, 2012
Exemplos de grafos o sistema nervoso do planeta

vértices arestas
routers linhas telefónicas
satélites cabos de TV
computadores ondas EM

IAED, 2014/2015
#1 Eric Roberts (I)

#411

Kevin Bacon

#2 Michael Madsen

#3 Harvey Keitel IAED, 2014/2015


Distâncias médias, diâmetro, caminhos
mais curtos, etc.

caminho mais curto entre C e E, pode ser o CBFE ou CAHE,


que possuem a mesma distância, i.e., temos de fazer o
mesmo número de saltos para chegar de um ao outro.

Como desenvolver um algoritmo que calcule eficientemente


caminhos mais curtos J?
IAED, 2014/2015
Francisco C. Santos, IST, 2012
Representação: matriz adjacente

IAED, 2014/2015
Representação: listas

Listas Matriz de Adjacências


não dirigido 1 2 3 4
1 2 4 /
1 3 1 0 1 0 1
2 1 3 4 /
2 1 0 1 1
3 2 /
4 1 2 /
3 0 1 0 0
2 4 4 1 1 0 0

dirigido 1 2 3 4
1 3 1 0 1 0 1
2 0 0 1 0
3 0 0 0 0
2 4 4 0 1 0 0

15 IAED, 2014/2015
Matriz de Adjacências - Vantagens

•  Representação mais adequada quando:


–  Há espaço disponível
–  Grafos são densos
–  Algoritmos requerem mais de V 2 operações

•  Adição e remoção de arcos é feita de forma eficiente

•  Fácil evitar existência de arcos paralelos (repetidos)

•  Fácil determinar se dois vértices estão ou não ligados

16 IAED, 2014/2015
Matriz de Adjacências - Inconvenientes

•  Grafos esparsos de grande dimensão requerem espaço


de memória proporcional a V 2

•  Neste caso, a simples inicialização do grafo


(proporcional a V 2) pode dominar o tempo de execução
global do algoritmo

•  Para o caso de grafos muito esparsos, mas com um


número muito elevado de vértices, pode nem sequer
existir memória suficiente para armazenar a matriz

17 IAED, 2014/2015
Listas de Adjacências - Vantagens

•  Inicialização é proporcional a V

•  Utiliza sempre espaço proporcional a V+E


–  Adequado para grafos esparsos
–  Algoritmos que assentem na análise de arcos em grafos esparsos.

•  Adição de arcos é feita de forma eficiente

18 IAED, 2014/2015
Listas de Adjacências - Inconvenientes

•  Arcos paralelos e adjacência entre vértices


–  Requer que se pesquise as listas de adjacências, o que pode levar
um tempo proporcional a V

•  Remoção de arcos
–  Pode levar um tempo proporcional a V (este problema pode ser
contornado).

•  Não aconselhável para


–  Grafos de grande dimensão que não podem ter arcos paralelos;
–  Grande utilização de remoção de arcos

19 IAED, 2014/2015
Representações Alternativas

•  Três mecanismos básicos de representação de grafos


–  Vector de arcos (pouco comum)
–  Matriz de adjacências
–  Listas de adjacências

•  Produzem diferentes desempenhos ao nível das


operações de manipulação

•  Escolha deverá depender do problema a resolver

20 IAED, 2014/2015
Grafos - Definição e Representação

•  Grafo definido por um conjunto V de vértices e um


conjunto E de arcos, G = (V, E)
–  Arcos representam ligações entre pares de vértices
•  E ⊆ V × V
–  Grafo esparso: |E| << |V × V|
–  Representação dos arcos
•  Matriz de adjacências: arcos representados por matriz
–  Para grafos densos
•  Listas de adjacências: arcos representados por listas
–  Para grafos esparsos
•  Grafos podem ser dirigidos ou não dirigidos
–  Existência (ou não) da noção de direcção nos arcos

21 IAED, 2014/2015
Grafos - Definição e Representação

•  Listas de adjacências
–  Grafos não dirigidos
•  Tamanho das listas é 2 ⋅⏐E⏐
–  Grafos dirigidos
•  Tamanho das listas é⏐E⏐
∴  Tamanho total das listas de adjacências é O(V+E)
–  Matriz de adjacências: Θ(V 2 ) para qualquer grafo

•  Grafos pesados
–  Função de pesos: w: E → R
•  Permite associar um peso a cada arco

22 IAED, 2014/2015
Interface do ADT Grafo
GRAPH.h Estrutura auxiliar
typedef struct { Edge
int v;
int w; Devolve um Edge
} Edge; ligando x e y

Edge EDGE(int x, int y); Estrutura genérica


typedef struct graph *Graph; Graph

Graph GRAPHinit(int);
void GRAPHinsertE(Graph, Edge);
void GRAPHremoveE(Graph, Edge);
int GRAPHedges(Edge a[], Graph G);
Graph GRAPHcopy(Graph);
void GRAPHdestroy(Graph);

23 IAED, 2014/2015
Implementação do ADT Grafo - Matriz
GRAPHmat.c
#include <stdlib.h>
#include “GRAPH.h” número de vértices
e arcos
typedef struct graph {
int V, E;
int **adj;
matriz de inteiros
} * Graph;

Graph GRAPHinit(int V){


Graph G = malloc(sizeof(struct graph)); no início não
G->V = V; tenho arcos
G->E = 0;
G->adj = MATRIXint(V, V, 0);
return G; reserva memória
} para V 2 inteiros e
inicializa-os a 0
24 IAED, 2014/2015
Implementação do ADT Grafo - Matriz
GRAPHmat.c (cont)
void GRAPHinsertE(Graph G, Edge e) {
int v = e.v, w = e.w;
if (G->adj[v][w] == 0)
G->E++; recebe um arco (edge)
G->adj[v][w] = 1; e introduz informação
G->adj[w][v] = 1; na matriz
}

void GRAPHremoveE(Graph G, Edge e) {


int v = e.v, w = e.w;
if (G->adj[v][w] == 1)
G->E--;
G->adj[v][w] = 0;
G->adj[w][v] = 0;
}

25 IAED, 2014/2015
Implementação do ADT Grafo - Matriz
GRAPHmat.c (cont)

int GRAPHedges(Edge a[], Graph G){


ciclo sobre todos os
int v, w, E = 0;
for (v = 0; v < G->V; v++)
elementos da matriz
for (w = v+1; w < G->V; w++)
if (G->adj[v][w] == 1)
a[E++] = EDGE(v, w); ... contudo, como
return E; a matriz é simétrica,
} basta-me olhar
devolve um para metade
vector de arcos

26 IAED, 2014/2015
Implementação do ADT Grafo - Listas
GRAPHlst.c
#include <stdlib.h>
#include “GRAPH.h”

typedef struct node *link;

struct node { cada elemento


int v; das listas, guardará o
link next;
índice de um nó
};

struct graph {
int V;
int E; vector de
link *adj; listas ligadas
};

27 IAED, 2014/2015
Implementação do ADT Grafo - Listas
GRAPHlst.c (cont)
link NEW(int v, link next) { função chamada
link x = malloc(sizeof(struct node)); quando adiciono um
x->v = v; vértice
x ->next = next; cada novo elemento
return x;
é inserido no início
}
Graph GRAPHinit(int V) {
int v; reservo memória
G = malloc(sizeof(struct graph)); para V links, ou seja,
G->V = V;
para V nós
G->E = 0;
G->adj = malloc(V * sizeof(link));
for (v = 0; v < V; v++)
G->adj[v] = NULL;
inicializo todos
return G;
} a NULL

28 IAED, 2014/2015
Implementação do ADT Grafo - Listas
GRAPHlst.c (cont)

void GRAPHinsertE(Graph G, Edge e) {


int v = e.v, w = e.w;
G->adj[v] = NEW(w, G->adj[v]);
G->adj[w] = NEW(v, G->adj[w]);
G->E++;
}
insere
no início
void GRAPHremoveE(Graph G, Edge e){
tenho que encontrar
o elemento e.w na lista
de adjacentes do e.v e
/* Fica como exercício */ removê-lo; fazer o mesmo
para e.v

} ver aula de
listas ligadas
29 IAED, 2014/2015
Implementação do ADT Grafo - Listas
GRAPHlst.c (cont) Função que recebe um G e retorna um
vector e um nº de Edges
ciclo sobre
int GRAPHedges(Edge a[], Graph G) {
todos os vértices
int v, E = 0;
link t;
for (v = 0; v < G->V; v++) percorre a lista
for (t = G->adj[v]; t != NULL; t = t->next)
de adjacentes de v
if (v < t->v )
a[E++] = EDGE(v, t->v); para
return E; evitar elementos
} repetidos só adiciono
devolve lista pares onde
com todos os v < t->v
arcos do grafo

30 IAED, 2014/2015
Desempenho das Várias Representações
E = nº de arestas ; V = nº de vértices

Vector de Arcos Matriz de Adj. Listas de Adj.


Espaço O(E) O(V2) O(V+E)

Inicialização O(1) O(V2) O(V)

Cópia O(E) O(V2) O(E)

Destruição O(1) O(V) O(E)

Inserir Arco O(1) O(1) O(1)

Encontrar Arco O(E) O(1) O(V)

Remover Arco O(E) O(1) O(V)

31 IAED, 2014/2015
Desempenho das Várias Representações
E = nº de arestas ; V = nº de vértices

Vector de Arcos Matriz de Adj. Listas de Adj.


Espaço O(E) O(V2) O(V+E)

Inicialização O(1) O(V2) O(V)

Cópia O(E) O(V2) O(E)

Destruição O(1) O(V) O(E)

Inserir Arco O(1) O(1) O(1)

Encontrar Arco O(E) O(1) O(V)

Remover Arco O(E) O(1) O(V)

32 IAED, 2014/2015
Procura

•  Algumas propriedades simples em grafos são fáceis de


determinar, independentemente da ordem pela qual se
examinam os arcos
–  Ex: grau de todos os vértices.
•  Outras propriedades estão associadas a caminhos, pelo
que se torna necessário identificá-las através de
pesquisa feita de vértice em vértice ao longo dos arcos
•  A maioria dos algoritmos em grafos que vamos estudar
utilizam este modelo abstracto básico
•  Torna-se então necessário analisar o essencial dos
algoritmos de procura em grafos e suas propriedades
estruturais
33 IAED, 2014/2015
Procura

•  Procurar em grafos é equivalente a percorrer labirintos


–  Necessário marcar pontos já visitado
–  Ser-se capaz de recuar, num caminho efectuado, até ao ponto
de partida
•  Os vários algoritmos de procura em grafos limitam-se a
executar uma determinada estratégia de procura em
labirintos
–  Procura em profundidade primeiro (DFS – “Depth-first-search”)
•  Admite duas implementações: recursiva e com uso de pilha
explícita
–  Substituindo a pilha por uma fila FIFO, transforma-se em
procura em largura primeiro (BFS – “Breadth-first-search”)

34 IAED, 2014/2015
Procura

•  Dado um vértice origem/fonte


•  Visitar todos os vértices atingíveis a partir da origem
–  Todos os vértices que estão em qualquer caminho do grafo que
comece na origem

origem

•  A ordem pela qual os vértices são visitados depende do


tipo de procura.
35 IAED, 2014/2015
Procura em Grafos

•  Procura em Largura Primeiro (BFS)


–  Implementação de BFS - Matriz de Adjacências
•  Procura em Profundidade Primeiro (DFS)
–  Implementação de DFS - Matriz de Adjacências

36 IAED, 2014/2015
Procura em Largura Primeiro (BFS)
Breadth-First
Search
•  Visita os vértices por ordem da sua distância à origem
•  Vértices mais próximos são visitados em primeiro lugar
–  Vértices não atingíveis a partir da origem, não são visitados

origem

37 IAED, 2014/2015
Procura em Largura Primeiro (BFS)
Breadth-First
Search
•  Visita os vértices por ordem da sua distância à origem
•  Vértices mais próximos são visitados em primeiro lugar
–  Vértices não atingíveis a partir da origem, não são visitados

origem
origem

38 IAED, 2014/2015
Procura em Largura Primeiro (BFS)
Breadth-First
Search
•  Dados G = (V, E) e vértice fonte s, BFS explora
sistematicamente vértices de G para descobrir todos os
vértices atingíveis a partir de s
–  Cálculo da distância: menor número de arcos de s para cada vértice
atingível
–  Identificação de árvore BF: caminho mais curto de s para cada
vértice atingível v
•  Fronteira entre nós descobertos e não descobertos é
expandida uniformemente
–  Vértices à distância k descobertos antes de qualquer vértice à
distância k+1

39 IAED, 2014/2015
Procura em Largura Primeiro (BFS) O BFS depende
da ordem das listas
•  Exemplo 1:
Lista adjacente:

A A: B, C, D
B: E
B C D C: B, G
D: C, G
E: C, F
E F G F: C, H
G: F, H, I
H: E, I
I: F
H I
Sequência: A B C D E G F H I
Distância: 0 1 1 1 2 2 3 3 3

IAED, 2014/2015
40
Procura em Largura Primeiro (BFS trees)
•  Exemplo 1:
Lista adjacente:

A A
A: B, C, D
B: E
B C D C:BB, G C D
D: C, G
E: C, F
E F G F:E C, H G
G: F, H, I
H: E, I
I: F
F H I
H I
Sequência: A B C D E G F H I
Distância: 0 1 1 1 2 2 3 3 3
Predecessores: NIL A A A B C E G G
(ou pais) IAED, 2014/2015
41
Procura em Largura Primeiro (BFS)

•  Aplicações
–  Encontrar as componentes conexas de um grafo.
–  Encontrar todos os nós de uma componente conexa.
–  Encontrar todos os caminhos mais curtos entre 2 nós, u e v,
de um grafo sem pesos ou com pesos (grafo pesado).

42 IAED, 2014/2015
Procura em Largura Primeiro (BFS)

•  Implementação
–  color[v]: cor do vértice v, branco, cinzento e preto
•  branco: não visitado
•  cinzento: já visitado mas algum dos adjacentes não visitado ou
procura em algum dos adjacentes não terminada
•  preto: já visitado e procura nos adjacentes já terminada
–  π[v]: predecessor de v na árvore BF
–  d[v]: tempo de descoberta de v

•  Outras definições
–  δ (s,v): menor distância de s a v - menor número de arcos em
qualquer caminho de s para v

43 IAED, 2014/2015
Procura em Largura Primeiro (BFS)
Inicializa cores, d’s e
predecessores de todos
os nós

Inicializa a “source”
Function BFS (G, src)
Quando testamos,
for each u ∈ V[G]
Q guarda sempre os color[u] = white; d[u] = ∞; π[u] = NIL inicializações
cinzentos
color[src] = gray; d[src] = 0; π[src] = NIL
Q = { src } Inicializa a fila de espera
while Q ≠ ∅
Vai buscar o 1º elem.
u = Dequeue (Q)
ciclo for each v ∈ Adj[u] Para todos
principal os parceiros v de u
if color[v] = white then
color[v] = gray; d[v] = d[u] + 1; π[v] = u
Enqueue (Q, v)
color[u] = black Se forem brancos,
pinta de cinzento,
...no fim, tornamos u Coloca cada v, na fila incrementa d e coloca
44
“preto” (=processado) de espera u como “pai”
IAED, 2014/2015
Exemplo BFS

d[r]=∞ d[s]=0 d[t]=∞ d[u]=∞


π[r]=NIL π[s]=NIL π[t]=NIL π[u]=NIL Q
r s t u s

v w x y
d[v]=∞ d[w]=∞ d[x]=∞ d[y]=∞
π[v]=NIL π[w]=NIL π[x]=NIL π[y]=NIL

45 IAED, 2014/2015
Exemplo BFS

d[r]=1 d[s]=0 d[t]=∞ d[u]=∞


π[r]=s π[s]=NIL π[t]=NIL π[u]=NIL Q
r s t u w
r

v w x y
d[v]=∞ d[w]=1 d[x]=∞ d[y]=∞
π[v]=NIL π[w]=s π[x]=NIL π[y]=NIL

46 IAED, 2014/2015
Exemplo BFS

d[r]=1 d[s]=0 d[t]=2 d[u]=∞


π[r]=s π[s]=NIL π[t]=w π[u]=NIL Q
r s t u r
t
x
v w x y
d[v]=∞ d[w]=1 d[x]=2 d[y]=∞
π[v]=NIL π[w]=s π[x]=w π[y]=NIL

47 IAED, 2014/2015
Exemplo BFS

d[r]=1 d[s]=0 d[t]=2 d[u]=∞


π[r]=s π[s]=NIL π[t]=w π[u]=NIL Q
r s t u t
x
v
v w x y
d[v]=2 d[w]=1 d[x]=2 d[y]=∞
π[v]=r π[w]=s π[x]=w π[y]=NIL

48 IAED, 2014/2015
Exemplo BFS

d[r]=1 d[s]=0 d[t]=2 d[u]=3


π[r]=s π[s]=NIL π[t]=w π[u]=t Q
r s t u x
v
u
v w x y
d[v]=2 d[w]=1 d[x]=2 d[y]=∞
π[v]=r π[w]=s π[x]=w π[y]=NIL

49 IAED, 2014/2015
Exemplo BFS

d[r]=1 d[s]=0 d[t]=2 d[u]=3


π[r]=s π[s]=NIL π[t]=w π[u]=t Q
r s t u v
u
y
v w x y
d[v]=2 d[w]=1 d[x]=2 d[y]=3
π[v]=r π[w]=s π[x]=w π[y]=x

50 IAED, 2014/2015
Exemplo BFS

d[r]=1 d[s]=0 d[t]=2 d[u]=3


π[r]=s π[s]=NIL π[t]=w π[u]=t Q
r s t u u
y

v w x y
d[v]=2 d[w]=1 d[x]=2 d[y]=3
π[v]=r π[w]=s π[x]=w π[y]=x

51 IAED, 2014/2015
Exemplo BFS

d[r]=1 d[s]=0 d[t]=2 d[u]=3


π[r]=s π[s]=NIL π[t]=w π[u]=t Q
r s t u y

v w x y
d[v]=2 d[w]=1 d[x]=2 d[y]=3
π[v]=r π[w]=s π[x]=w π[y]=x

52 IAED, 2014/2015
Exemplo BFS

d[r]=1 d[s]=0 d[t]=2 d[u]=3


π[r]=s π[s]=NIL π[t]=w π[u]=t Q
r s t u

v w x y
d[v]=2 d[w]=1 d[x]=2 d[y]=3
π[v]=r π[w]=s π[x]=w π[y]=x

53 IAED, 2014/2015
Exemplo BFS

•  Árvore BF: pai na árvore é o predecessor π

s
d[r]=1 d[s]=0 d[t]=2 d[u]=3
π[r]=s π[s]=NIL π[t]=w π[u]=t
r s t u r w

v w x y v t x
d[v]=2 d[w]=1 d[x]=2 d[y]=3
π[v]=r π[w]=s π[x]=w π[y]=x

u y
54 IAED, 2014/2015
Implementação de BFS - Matriz de Adj.

void bfs(Graph G, int s) ...


{
int u, v; while (!QUEUEempty()) {
u = QUEUEget();
for (u = 0; u < G->V; u++) { for (v = 0; v < G->V; v++)
color[u] = WHITE; if (G->adj[u][v] == 1) {
d[u] = -1; if (color[v] == WHITE) {
p[u] = -1; color[v] = GRAY;
} d[v] = d[u] + 1;
p[v] = u;
color[s] = GRAY; QUEUEput(v);
d[s] = 0; }
QUEUEput(s); }
color[u] = BLACK;
... }

55 IAED, 2014/2015
Exercício: Travessia Largura Primeiro (BFS)

•  Qual a sequência de vértices visitados numa travessia em largura


primeiro (BFS) sobre o grafo abaixo, com origem no vértice A?
Considere que os vértices são visitados por ordem alfabética e que
os vértices adjacentes de um vértice também são visitados por
ordem alfabética.

56 A B C G D E H F IAED, 2014/2015
Procura em Largura Primeiro (BFS)

•  Tempo de execução

–  Matriz de adjacências: O(V 2)


•  Inicialização: O(V)
•  Cada vértice colocado na fila apenas 1 vez: O(V)
•  Linha da matriz visitada 1 vez por cada vértice: O(V 2)

–  Listas de adjacências: O(V+E)


•  Inicialização: O(V)
•  Cada vértice colocado na fila apenas 1 vez: O(V)
•  Lista de adjacências visitada 1 vez por cada vértice: O(E)

57 IAED, 2014/2015
Procura em Largura Primeiro (BFS)

•  Resultado
( ) ( )
–  Para qualquer arco (u,v): δ s ,v ≤ δ s , u + 1
•  Se u atingível, então v atingível
–  Caminho mais curto para v não pode ser superior a caminho
mais curto para u mais o arco (u,v)
•  Se u não atingível, então resultado é válido (independentemente de
v ser atingível)

•  No final da BFS
–  d[u] = δ(s,u), para todo o vértice u de V

58 IAED, 2014/2015
Procura em Profundidade Primeiro (DFS)
Depth-First
Search
•  Visita primeiro os arcos dos vértices mais recentemente
visitados

origem

59 IAED, 2014/2015
Procura em Profundidade Primeiro (DFS)
Depth-First
Search
•  Visita primeiro os arcos dos vértices mais recentemente
visitados

origem origem

60 IAED, 2014/2015
Procura em Profundidade Primeiro (DFS)

•  Aplicações
–  Encontrar um caminho entre 2 nodos específicos, u e v, num
grafo sem pesos ou com pesos (grafo pesado).
–  Saber se um grafo é conexo ou não.
–  Útil para saber a ordenação topológica de um grafo

61 IAED, 2014/2015
Procura em Profundidade Primeiro (DFS)

•  Grafo pesquisado dando prioridade aos arcos dos


vértices mais recentemente visitados
•  Resultado da procura:
–  Floresta DF: Gπ = (V , Eπ ) E π = {(π [v], v) : v ∈V ∧ π [v] ≠ NIL}
–  Floresta DF composta por várias árvores DF

•  Implementação:
–  color[u]: cor do vértice (branco, cinzento, preto)
–  d[u]: tempo de início (de visita do vértice)
–  f[u]: tempo de fim (de visita do vértice)
–  π[u]: predecessor

62 IAED, 2014/2015
Procura em Profundidade Primeiro (DFS)
Inicialização
Pinta de cinzento

function DFS (G) function DFS-Visit (u)


Tempo
for each vertex u ∈ V[G] time = time + 1 de
color[u] = white color[u] = gray descoberta
π [u] = NIL d[u] = time
time = 0 for each v ∈ Adj[u]
for each vertex u ∈ V[G] if color[v] = white
if color[u] = white DFS-visit todos os π [v] = u
DFS-Visit (u) vizinhos brancos DFS-Visit (v)
color[u] = black
Para todos os
vértices u “brancos” time = time + 1
f[u] = time
DFS-visitar u Pinta de preto
Atribui o tempo e incrementa o
de saída time
63 IAED, 2014/2015
Exemplo DFS

d[x]=∞ d[v]=∞ d[w]=∞


f[x]=∞ f[v]=∞ f[w]=∞
π[x]=NIL π[v]=NIL π[w]=NIL

u v w
Time=0

x y z
d[x]=∞ d[y]=∞ d[z]=∞
f[x]=∞ f[y]=∞ f[z]=∞
π[x]=NIL π[y]=NIL π[z]=NIL

64 IAED, 2014/2015
Exemplo DFS

d[u]=1 d[v]=∞ d[w]=∞


f[u]=∞ f[v]=∞ f[w]=∞
π[u]=NIL π[v]=NIL π[w]=NIL

u v w
Time=1

x y z
d[x]=∞ d[y]=∞ d[z]=∞
f[x]=∞ f[y]=∞ f[z]=∞
π[x]=NIL π[y]=NIL π[z]=NIL

65 IAED, 2014/2015
Exemplo DFS

d[u]=1 d[v]=2 d[w]=∞


f[u]=∞ f[v]=∞ f[w]=∞
π[u]=NIL π[v]=u π[w]=NIL

u v w
Time=2

x y z
d[x]=∞ d[y]=∞ d[z]=∞
f[x]=∞ f[y]=∞ f[z]=∞
π[x]=NIL π[y]=NIL π[z]=NIL

66 IAED, 2014/2015
Exemplo DFS

d[u]=1 d[v]=2 d[w]=∞


f[u]=∞ f[v]=∞ f[w]=∞
π[u]=NIL π[v]=u π[w]=NIL

u v w
Time=3

x y z
d[x]=∞ d[y]=3 d[z]=∞
f[x]=∞ f[y]=∞ f[z]=∞
π[x]=NIL π[y]=v π[z]=NIL

67 IAED, 2014/2015
Exemplo DFS

d[u]=1 d[v]=2 d[w]=∞


f[u]=∞ f[v]=∞ f[w]=∞
π[u]=NIL π[v]=u π[w]=NIL

u v w
Time=4

x y z
d[x]=4 d[y]=3 d[z]=∞
f[x]=∞ f[y]=∞ f[z]=∞
π[x]=y π[y]=v π[z]=NIL

68 IAED, 2014/2015
Exemplo DFS

d[u]=1 d[v]=2 d[w]=∞


f[u]=∞ f[v]=∞ f[w]=∞
π[u]=NIL π[v]=u π[w]=NIL

u v w
B
Time=5

x y z
d[x]=4 d[y]=3 d[z]=∞
f[x]=∞ f[y]=∞ f[z]=∞
π[x]=y π[y]=v π[z]=NIL

69 IAED, 2014/2015
Exemplo DFS

d[u]=1 d[v]=2 d[w]=∞


f[u]=∞ f[v]=∞ f[w]=∞
π[u]=NIL π[v]=u π[w]=NIL

u v w
B
Time=5

x y z
d[x]=4 d[y]=3 d[z]=∞
f[x]=5 f[y]=∞ f[z]=∞
π[x]=y π[y]=v π[z]=NIL

70 IAED, 2014/2015
Exemplo DFS

d[u]=1 d[v]=2 d[w]=∞


f[u]=∞ f[v]=∞ f[w]=∞
π[u]=NIL π[v]=u π[w]=NIL

u v w
B
Time=6

x y z
d[x]=4 d[y]=3 d[z]=∞
f[x]=5 f[y]=6 f[z]=∞
π[x]=y π[y]=v π[z]=NIL

71 IAED, 2014/2015
Exemplo DFS

d[u]=1 d[v]=2 d[w]=∞


f[u]=∞ f[v]=7 f[w]=∞
π[u]=NIL π[v]=u π[w]=NIL

u v w
B
Time=7

x y z
d[x]=4 d[y]=3 d[z]=∞
f[x]=5 f[y]=6 f[z]=∞
π[x]=y π[y]=v π[z]=NIL

72 IAED, 2014/2015
Exemplo DFS

d[u]=1 d[v]=2 d[w]=∞


f[u]=8 f[v]=7 f[w]=∞
π[u]=NIL π[v]=u π[w]=NIL

u v w
B
Time=8 F

x y z
d[x]=4 d[y]=3 d[z]=∞
f[x]=5 f[y]=6 f[z]=∞
π[x]=y π[y]=v π[z]=NIL

73 IAED, 2014/2015
Exemplo DFS

d[u]=1 d[v]=2 d[w]=9


f[u]=8 f[v]=7 f[w]=∞
π[u]=NIL π[v]=u π[w]=NIL

u v w
B
Time=9 F

x y z
d[x]=4 d[y]=3 d[z]=∞
f[x]=5 f[y]=6 f[z]=∞
π[x]=y π[y]=v π[z]=NIL

74 IAED, 2014/2015
Exemplo DFS

d[u]=1 d[v]=2 d[w]=9


f[u]=8 f[v]=7 f[w]=∞
π[u]=NIL π[v]=u π[w]=NIL

u v w
B C
Time=9 F

x y z
d[x]=4 d[y]=3 d[z]=∞
f[x]=5 f[y]=6 f[z]=∞
π[x]=y π[y]=v π[z]=NIL

75 IAED, 2014/2015
Exemplo DFS

d[u]=1 d[v]=2 d[w]=9


f[u]=8 f[v]=7 f[w]=∞
π[u]=NIL π[v]=u π[w]=NIL

u v w
B C
Time=10 F

x y z
d[x]=4 d[y]=3 d[z]=10
f[x]=5 f[y]=6 f[z]=∞
π[x]=y π[y]=v π[z]=w

76 IAED, 2014/2015
Exemplo DFS

d[u]=1 d[v]=2 d[w]=9


f[u]=8 f[v]=7 f[w]=∞
π[u]=NIL π[v]=u π[w]=NIL

u v w
B C
Time=11 F

x y z B

d[x]=4 d[y]=3 d[z]=10


f[x]=5 f[y]=6 f[z]=11
π[x]=y π[y]=v π[z]=w

77 IAED, 2014/2015
Exemplo DFS

d[u]=1 d[v]=2 d[w]=9


f[u]=8 f[v]=7 f[w]=12
π[u]=NIL π[v]=u π[w]=NIL

u v w
B C
Time=12 F

x y z B

d[x]=4 d[y]=3 d[z]=10


f[x]=5 f[y]=6 f[z]=11
π[x]=y π[y]=v π[z]=w

78 IAED, 2014/2015
Exemplo DFS

d[u]=1 d[v]=2 d[w]=9


f[u]=8 f[v]=7 f[w]=12
π[u]=NIL π[v]=u π[w]=NIL

u v w
B C
Time=12 F

x y z B

d[x]=4 d[y]=3 d[z]=10


f[x]=5 f[y]=6 f[z]=11
π[x]=y π[y]=v π[z]=w

79 IAED, 2014/2015
Exemplo DFS

•  Floresta DF: pai numa árvore é o predecessor π


–  Floresta porque pode conter várias árvores

u w

v z

x
80 IAED, 2014/2015
Procura em Profundidade Primeiro (DFS)
•  Exemplo (origem: ‘A’):
Lista adjacente (ordem alfab.):
A A: B, C
B: E O DFS depende
B C D C: B, F da ordem das listas
D: C, G
E: F
E F G F:
G: F
A D

B C G
Sequência: A B E F C D G
Predecessor: nil A B E A nil D E
F
IAED, 2014/2015
81
Procura em Profundidade Primeiro (DFS)
•  Exemplo (origem: ‘A’):
Lista adjacente (ordem alfab.):
1/ 10 A A: B, C
8/ 9 11/ 14 B: E O DFS depende
2/ 7
B C D C: B, F da ordem das listas
D: C, G
3/ 6 4/ 5 12/ 13
E: F
E F G F:
G: F

Sequência: A B E F C D G
Tempo de descoberta: 1 2 3 4 8 11 12
Tempo de fim: 10 7 6 5 9 14 13
IAED, 2014/2015
82
Implementação de DFS

void dfs(Graph G) void dfs_visit(Graph G, int u)


{ {
int u; int v;
for (u = 0; u < G->V; u++) { color[u] = GRAY;d[u] = time;
color[u] = WHITE; time++;
p[u] = -1; for (v = 0; v < G->V; v++)
} if (G->adj[u][v] == 1) {
time = 1; if (color[v] == WHITE) {
for (u = 0; u < G->V; u++) p[v] = u;
if(color[u] == WHITE) dfs_visit(G, v);
dfs_visit(G, u); }
} }
color[u] = BLACK;
f[u] = time;
time++;
}

83 IAED, 2014/2015
Exercício: Travessia Profundidade Primeiro (DFS)

•  Qual a sequência de vértices visitados numa travessia em


profundidade primeiro (DFS) sobre o grafo abaixo, com origem no
vértice A? Considere que os vértices são visitados por ordem
alfabética e que os vértices adjacentes de um vértice também são
visitados por ordem alfabética.

ACBFEDG
84 IAED, 2014/2015
Procura em Profundidade Primeiro (DFS)

•  Tempo de execução: O(V+E)


–  Inicialização: O(V)
–  Chamadas a DFS-Visit dentro de DFS: O(V)
–  Arcos analisados em DFS-Visit: Θ(E)
•  Chamadas a DFS-Visit dentro de DFS-Visit: O(V)
•  Mas

∑ | Adj[v] |= Θ( E )
v∈V

85 IAED, 2014/2015
Grafos (II)

CLRS: Capítulos 22 e 23

IAED, 2014/2015
Mais uns conceitos úteis

•  Dado grafo G = (V, E), um caminho p é uma sequência

<v0, v1, …, vk>

tal que para todo o i, 0 ≤ i ≤ k-1, (vi, vi+1)∈E


v0 v1 v3 v4

v2

v5

87 IAED, 2014/2015
Mais uns conceitos úteis

•  Se existe um caminho p de u para v, então v diz-se


atingível a partir de u via p

88 IAED, 2014/2015
Mais uns conceitos úteis
–  No grafo em baixo existem 2 ciclos (azul e verde)

•  Um grafo dirigido G = (V,E) é acíclico se não tem ciclos


–  Directed Acyclic Graph (DAG)
–  Para qualquer vértice v de um DAG, não há nenhum caminho que
89 comece e acabe em v
IAED, 2014/2015
Ordenação topológica
•  Grafos como representação de um problema de planeamento de
tarefas

•  Se um grafo tem um arco de X para Y, então a tarefa X deve ser


efectuada antes de Y.
•  Uma ordenação topológica de um grafo é uma sequência de
“tarefas” onde esta condição é verificada.

90 IAED, 2014/2015
Ordenação Topológica: Exemplo

undershorts socks
watch
pants shoes
shirt

belt
tie

jacket

Podem existir várias ordenações válidas!

91 IAED, 2014/2015
Exercício: ordenação topológica

•  Qual das sequências de vértices corresponde a uma ordenação


topológica válida do grafo abaixo ?

a. <A,B,C,D,E,F,G>
b. <B,A,D,C,G,F,E>
c. <B,A,D,F,E,G,C>
d. <B,F,A,E,D,C,G>
e. <C,G,E,B,A,D,F>
f. <F,E,G,D,C,B,A>
g. <F,G,E,B,A,D,C>

92 IAED, 2014/2015
Exercício: ordenação topológica

•  Qual das sequências de vértices corresponde a uma ordenação


topológica válida do grafo abaixo ?

a. <A,B,C,D,E,F,G>
b. <B,A,D,C,G,F,E>
c. <B,A,D,F,E,G,C>
d. <B,F,A,E,D,C,G>
e. <C,G,E,B,A,D,F>
f. <F,E,G,D,C,B,A>
g. <F,G,E,B,A,D,C>

93 IAED, 2014/2015
Exercício: ordenação topológica

•  Qual das sequências de vértices corresponde a uma ordenação


topológica válida do grafo abaixo ?

a. <A,B,C,D,E,F,G>
b. <B,A,D,C,G,F,E>
c. <B,A,D,F,E,G,C>
d. <B,F,A,E,D,C,G>
e. <C,G,E,B,A,D,F>
f. <F,E,G,D,C,B,A>
g. <F,G,E,B,A,D,C>

94 IAED, 2014/2015
Exercício: ordenação topológica

•  Qual das sequências de vértices corresponde a uma ordenação


topológica válida do grafo abaixo ?

a. <A,B,C,D,E,F,G>
b. <B,A,D,C,G,F,E>
c. <B,A,D,F,E,G,C>
d. <B,F,A,E,D,C,G>
e. <C,G,E,B,A,D,F>
f. <F,E,G,D,C,B,A>
g. <F,G,E,B,A,D,C>

95 IAED, 2014/2015
Exercício: ordenação topológica

•  Qual das sequências de vértices corresponde a uma ordenação


topológica válida do grafo abaixo ?

a. <A,B,C,D,E,F,G>
b. <B,A,D,C,G,F,E>
c. <B,A,D,F,E,G,C>
d. <B,F,A,E,D,C,G>
e. <C,G,E,B,A,D,F>
f. <F,E,G,D,C,B,A>
g. <F,G,E,B,A,D,C>

96 IAED, 2014/2015
Exercício: ordenação topológica

•  Qual das sequências de vértices corresponde a uma ordenação


topológica válida do grafo abaixo ?

a. <A,B,C,D,E,F,G>
b. <B,A,D,C,G,F,E>
c. <B,A,D,F,E,G,C>
d. <B,F,A,E,D,C,G>
e. <C,G,E,B,A,D,F>
f. <F,E,G,D,C,B,A>
g. <F,G,E,B,A,D,C>

97 IAED, 2014/2015
Exercício: ordenação topológica

•  Qual das sequências de vértices corresponde a uma ordenação


topológica válida do grafo abaixo ?

a. <A,B,C,D,E,F,G>
b. <B,A,D,C,G,F,E>
c. <B,A,D,F,E,G,C>
d. <B,F,A,E,D,C,G>
e. <C,G,E,B,A,D,F>
f. <F,E,G,D,C,B,A>
g. <F,G,E,B,A,D,C>

98 IAED, 2014/2015
Exercício: ordenação topológica

•  Qual das sequências de vértices corresponde a uma ordenação


topológica válida do grafo abaixo ?

a. <A,B,C,D,E,F,G>
b. <B,A,D,C,G,F,E>
c. <B,A,D,F,E,G,C>
d. <B,F,A,E,D,C,G>
e. <C,G,E,B,A,D,F>
f. <F,E,G,D,C,B,A>
g. <F,G,E,B,A,D,C>

99 IAED, 2014/2015
Ordenação Topológica

•  Uma ordenação topológica de um DAG G = (V,E) é uma


ordenação de todos os vértices tal que se (u,v) ∈E então
u aparece antes de v na ordenação

•  Por outras palavras, se um grafo tem um arco dirigido


u→v, então u vem antes de v na ordenação topológica

•  Algoritmos
–  Utilizando informação de DFS
–  Eliminação de vértices (não vamos estudar)

100 IAED, 2014/2015


Ordenação topológica (implementação)
Topological-Sort(G)
1. Inicializo as cores, os predecessores e os tempos
d e f de todos os nós, tal como no DFS.
2. Chama o DFS-visit a todos os nós com in-
degree=0, e vou calculando o finishing-time (f) para
cada nó.
3. sempre que um vértice é processado (passa a
preto) e recebe um finishing-time, insiro o vértice no
início de uma lista ligada.
4. Retorno a lista ligada

101 IAED, 2014/2015


Ordenação Topológica: Exemplo & algoritmo

11/16 undershorts 17/18


socks
watch 9/10
12/15 pants shoes 13/
14
shirt 1/ 8

belt
6/7 tie 2/ 5

jacket 3/ 4

socks undershorts pants shoes watch shirt belt tie jacket

18 16 15 14 10 8 7 5 4

102 IAED, 2014/2015


Exercício: ordenação topológica
IV.b) Considere o seguinte grafo dirigido:
IV.b) Considere o seguinte grafo dirigido:
B
1/ 8
B

5/ 6
2/ 7 A C
A C

3/ D
4 D

Indique
Indique uma
uma ordenação
ordenação topológica
topológica dos vértices
dos vértices do grafo.do grafo.

Solução:
Solução:

⟨B, A,A,
C, D⟩
B A C D
⟨B, C, D⟩ IAED, 2014/2015
104
Considere o seguinte grafo orientado.
Exercício:
IV.b) DFS &
IV.b)Considere
ordenação
Considereooseguinte
seguintegrafo
topológica
grafoorientado.
orientado.
IV.b) Considere o seguinte grafo orientado.
1/ 10 5/D
8
A AC
A CC D D
A C 4/ D
9

BB EE
2/ 3 B E
B E 6/ 7

Indique os tempos de descoberta d e de finalização f , para cada um dos v


Indique osIndique
tempos osde tempos de ddescoberta
descoberta d e de
e de finalização finalização
f , para cada um f ,dospara cada um
vértices, após dos
a v
execuçãoexecução
execução
de dedeuma
uma procura emprocura
uma procuraem emprofundidade
profundidade profundidade
primeiro (DFS), primeiro
onde os(DFS),
primeiro (DFS),onde
vértices onde
são os
osvértices
vérticessã
considerados s
e os tempos
por ordem por
por ordem
deordem lexicográfica
descoberta
lexicográfica d(ou
lexicográfica
(ou seja, A, (ou seja,
e C,
B, seja,
de A, B,
B,C,
A,Qual é a. .ordenaçao
C,
. . finalização
. ). . .).).
f Qual
,Qual
para ééaacada
ordenaçaoumtopológ
ordenaçao
topológica obtida topológ
dos
pela
DFS
DFS para DFS para
para
o grafo o
acima? grafo acima?
o grafo acima?
ão de uma procura em profundidade primeiro (DFS), onde os vértices s
dem Solução:
lexicográfica
Solução:(ou seja, A, B, C, . . . ). Qual é a ordenaçao topológ
Solução:
A B CAA DBBE CC DD Ordenação
EE topológica
Ordenação
Ordenaçãotopológica
topológica
ara o dgrafo acima?
1 2d d 41 1 5 2 2 6 44 55 66 A, C, D, E, BA,
A,C,C,D,
D,E,
E,BB
f 10 3 9 8 7
f f 1010 3 3 99 88 77 A C D E B
ão:
B105 C D E Ordenação topológica IAED, 2014/2015
Componentes Fortemente Ligados (SCC)
Strongly Connected Components

• Um grafo (ou sub-grafo) diz-se fortemente ligado se para


cada par de vértices u e v existe um caminho de u para v e
de v para u

• Os componentes fortemente ligados de um grafo são os


sub-grafos de tamanho máximo que são fortemente
ligados

• Todos os vértices pertencem a uma componente


fortemente ligada, mesmo que esta só contenha o próprio
elemento
106 IAED, 2014/2015
Componentes Fortemente Ligados (SCC)

•  Definição:
–  Dado o grafo dirigido G = (V, E) um componente fortemente
ligado (SCC) é um conjunto máximo de vértices U ⊆ V, tal que
para u,v∈U, u é atingível a partir de v, E v é atingível a partir de
u
•  Obs: um vértice simples é um SCC

•  Outras definições:
–  Grafo transposto de G = (V, E)
•  GT = (V, ET) tal que: ET = {(u, v ) : ( v, u ) ∈ E}

–  OBS: G e GT têm os mesmos SCCs

107 IAED, 2014/2015


Componentes Fortemente Ligados (SCC)
1 2 3

1 2-4-5 3-6

4 6
5

7 8
7-8-9-10-11-12

10
11

12

108 IAED, 2014/2015


Componentes Fortemente Ligados
(Implementação)
Strongly-Connected-Components(G):
1. Calcula DFS(G), e o finishing-time (f) de cada nó.
2. Calcula GT
3. Inicializa as cores, os predecessores e os tempos d e f
de todos os nós de GT, tal como no DFS.
4. Chama o DFS-visit para os nós de GT por ordem inversa
do finishing-time (f) obtido em 1.
5. Retorna a floresta-DF obtida em 4. Cada uma das
árvores obtida é uma Componente Fortemente Ligada.

109 IAED, 2014/2015


Componentes Fortemente Ligados (SCC)

•  Ex. 1: Quantos SCCs ?

110 IAED, 2014/2015


Componentes Fortemente Ligados (SCC)

•  Ex. 1: Quantos SCCs ?

•  Resposta: 3

111 IAED, 2014/2015


Componentes Fortemente Ligados (SCC)

•  Ex. 2: Quantos SCCs ?

112 IAED, 2014/2015


Componentes Fortemente Ligados (SCC)

•  Ex. 2: Quantos SCCs ?

•  Resposta: 2

113 IAED, 2014/2015


Componentes Fortemente Ligados (SCC)
Grupo IV (1.5+1.5 = 3.0 val.)
Grupo IV (1.5+1.5 = 3.0 val.)
IV.a) Considere o grafo representado pela seguinte matriz de adjacências:
IV.a) Considere o grafo representado pela seguinte matriz de adjac
A B C D E F A
A 0 1 1A 0B 0C 0D
E F
B 0 0A 10 01 11 00
0 0
B C
C 1 0B 00 00 01 00
1 0
D 0 0C 01 00 10 00
0 0
E 0 0D 00 10 00 00
1 0 D E F
F 0 0E 00 00 10 01
0 0
F 0 0 0 0 1 0
Quantos componentes fortemente ligados tem o grafo?
Quantos
Solução: 3 componentes fortemente ligados tem o grafo?
Solução: 3

114 IAED, 2014/2015


Componentes Fortemente Ligados (SCC)

•  Qual o número de componentes fortemente ligados do


grafo dirigido da figura abaixo?

115 IAED, 2014/2015


Componentes Fortemente Ligados (SCC)

•  Qual o número de componentes fortemente ligados do


grafo dirigido da figura abaixo?

Solução: 1

116 IAED, 2014/2015


Desafio!

•  Algoritmo eficiente para determinar se grafo G = (V, E)


é bipartido ?
–  Grafo G é bipartido se V pode ser dividido em L e R, tal que
todos os arcos de G são incidentes em 1 vértice de L e 1
vértice de R

L R

grafo bipartido
117 IAED, 2014/2015
Desafio!

•  Algoritmo eficiente para determinar se grafo G = (V, E)


é bipartido ?
–  Grafo G é bipartido se V pode ser dividido em L e R, tal que
todos os arcos de G são incidentes em 1 vértice de L e 1
vértice de R
–  Solução: realizar uma BFS (largura
L R primeiro) em cada componente ligado
do grafo e verificar se os vértices
adjacentes já visitados têm paridade
do tempo de descoberta diferente da
do vértice actual
•  se sim, para todos os vértices,
então o grafo é bipartido
grafo bipartido •  se não, para algum vértice, então o
118 grafo não é bipartido
IAED, 2014/2015
Desafio!

•  Algoritmo eficiente para determinar se grafo G = (V, E)


é bipartido ?
–  Grafo G é bipartido se V pode ser dividido em L e R, tal que
todos os arcos de G são incidentes em 1 vértice de L e 1
vértice de R
–  Solução: realizar uma BFS (largura
L R primeiro) em cada componente ligado
0 1
do grafo e verificar se os vértices
adjacentes já visitados têm paridade
BFS 2 1 do tempo de descoberta diferente da
do vértice actual
2 3 •  se sim, para todos os vértices,
então o grafo é bipartido
grafo bipartido •  se não, para algum vértice, então o
119 grafo não é bipartido
IAED, 2014/2015
Desafio!

•  Algoritmo eficiente para determinar se grafo G = (V, E)


é bipartido ?
–  Grafo G é bipartido se V pode ser dividido em L e R, tal que
todos os arcos de G são incidentes em 1 vértice de L e 1
vértice de R
–  Solução: realizar uma BFS (largura
L R primeiro) em cada componente ligado
0 1
do grafo e verificar se os vértices
adjacentes já visitados têm paridade
BFS 2 1 do tempo de descoberta diferente da
do vértice actual
2 3 •  se sim, para todos os vértices,
então o grafo é bipartido
grafo bipartido •  se não, para algum vértice, então o
120 grafo não é bipartido
IAED, 2014/2015
Problema

•  O grafo abaixo é bipartido?

BFS ?

121 IAED, 2014/2015


Problema

•  O grafo abaixo é bipartido?

3
1 BFS ?
0
2
5
2
3
4

6
3 5

122 IAED, 2014/2015


Problema
Sim!
•  O grafo abaixo é bipartido?

3
1 BFS
0
2
5
2
3
4

6
3 5

123 IAED, 2014/2015


Problema

•  O grafo abaixo é bipartido?

BFS ?

124 IAED, 2014/2015


Problema

•  O grafo abaixo é bipartido?

3
1 BFS
0
2

2
3
4

3 4

125 IAED, 2014/2015


Problema
Não
•  O grafo abaixo é bipartido?

3
1 BFS
0
2

2
3
4

3 4

126 IAED, 2014/2015


Grafos:
mais exercícios

IAED, 2014/2015
Grafos: BFS (travessia
Grupo IV (1.0+1.5+1.5 = 4.0 val.) em largura primeiro)
IV.a) Considere uma travessia em largura primeiro (BFS) sobre o grafo G = (V, E) abaixo, com
origem no vértice A. Considere ainda que os vértices são visitados por ordem alfabética e que os
vértices adjacentes de um vértice são também visitados por ordem alfabética. Quais os valores de
d[v] para cada um dos vértices v ∈ V no final da travessia?

C B A

D E F

G H

Solução:
A B C D E F G H
0 1 2 3 4 1 4 2
IAED, 2014/2015
C B A
Grafos: BFS (travessia
Grupo IV (1.0+1.5+1.5 = 4.0 val.) em largura primeiro)
IV.a) Considere uma travessia em largura primeiro (BFS) sobre o grafo G = (V, E) abaixo, com
origem no vértice A. Considere ainda que os vértices são visitados por ordem alfabética e que os
vértices adjacentes de um vértice são também visitados por ordem alfabética. Quais os valores de
d[v] para cada um dos vértices v ∈ V no final da travessia?

2 1 0
C BD A E F

4 1
3
D E F

G H
4
G H
2

Solução:
Solução:
A B C D E
A0 1 2 3
B C 4
DF1 G H
E
4 2
F G H
IAED, 2014/2015
0 1 2 3 4 1 4 2
Grafos:
) Considere Ordenação
o grafo da figura em topológica
baixo. Indique uma (c/DFS)
sequência de vértice
ordenação topológica
IV.c) Considere o grafo dado grafo.
figura em baixo. Indique uma sequência de vértices que corresponda a
uma ordenação topológica do grafo.

A A B B E E

C D F

C D F

G H

Solução:
Existem várias ordenações topológicas possı́veis.
G A, C >.
Uma solução seria a seguinte sequência: < D, E, F, H, G, B, H
IAED, 2014/2015
Outra solução poderia ser a sequência: < D, E, B, A, F, H, G, C >.
IV.c) Considere o grafo da figura em baixo. Indique uma sequência de vértices que corresp
uma ordenação topológica do grafo.
Grafos:
) Considere Ordenação
o grafo da figura em topológica
baixo. Indique uma (c/DFS)
sequência de vértice
ordenação topológica
IV.c) Considere o grafo dado grafo.
figura A
em baixo. IndiqueBuma sequência de
E vértices que corresponda a
uma ordenação topológica do grafo.
2/ 7 8/ 15
3/ 6
A A B B E E

C D F

C D F
4/ 5 1/16 9/ 14
C D F
G H

G H
Solução:
Existem várias ordenações topológicas possı́veis.
Uma solução seria a seguinte sequência: < D, E, F, H, G, B, A, C >.
Solução: 12/ 13
Existem
Outra váriaspoderia
solução ordenações
sertopológicas possı́veis.
a sequência: < D, E, 10/B,
11 A, F, H, G, C >.
DE F H G B A C G A, C >.
Uma solução seria a seguinte sequência: < D, E, F, H, G, B,
Outra solução poderia ser a sequência: < D, E, B, A, F, H, G, C >.
H
IAED, 2014/2015
SCCs

IV.b) Indique o número de componentes fortemente ligados do grafo.

A C F

E H

B D G

Solução: 3

IAED, 2014/2015
SCCs

IV.b) Indique o número de componentes fortemente ligados do grafo.

A C F

E H

B
B D G

Solução: 3

Solução: 3 IAED, 2014/2015


III.b) ToDo Considere o seguinte grafo dirigido.

A B E F

D G

Indique os tempos de descoberta d e de finalização f , para cada um dos vértices, após a execução
de uma procura em profundidade primeiro (DFS), onde os vértices são considerados por ordem
lexicográfica (ou seja, A, B, C, . . .). Qual é a ordenaçao topológica obtida pela DFS para o grafo
acima?

Solução:

Vértice A B C D E F G
d 1 2 3 9 4 5 10
f 14 13 8 12 7 6 11
Ord. Topológica A B D G C E F
IAED, 2014/2015
III.b) ToDo Considere o seguinte grafo dirigido.
3/ 8
C

1/ 14 2/ 13 4/ 7 5/
6
A B E F

D G
9/12
10/11

Indique os tempos de descoberta d e de finalização f , para cada um dos vértices, após a execução
de uma procura em profundidade primeiro (DFS), onde os vértices são considerados por ordem
lexicográfica (ou seja, A, B, C, . . .). Qual é a ordenaçao topológica obtida pela DFS para o grafo
acima?

Solução:

Vértice A B C D E F G
d 1 2 3 9 4 5 10 A
f 14 13 8 12 7 6 11
B D G C E F
Ord. Topológica A B D G C E F
IAED, 2014/2015
III.b) ToDo Considere o seguinte grafo dirigido.
3/ 8 C

A
2/ 13 B 4/ 7 E5/ F
1/ 14 6
A B E F

D G
D G
9/12
10/ 11 um dos vértices, após a ex
Indique os tempos de descoberta d e de finalização f , para cada
de uma procura em profundidade primeiro (DFS), onde os vértices são considerados por
Indique os tempos de descoberta d e de finalização f , para cada um dos vértices, após a execução
lexicográfica
de uma procura (ou seja, A,primeiro
em profundidade .). Qualonde
B, C, . . (DFS), é a os
ordenaçao topológica
vértices são obtidapor
considerados pela DFS para
ordem
acima?
lexicográfica (ou seja, A, B, C, . . .). Qual é a ordenaçao topológica obtida pela DFS para o grafo
acima?
Solução:
Solução:
Vértice A B C D E F G
Vértice A dB C 1 D 2 E 3 F 9G 4 5 10
d 1 f 2 314 9 13 4 8 5 1210 7 6 11
f 14 13 8 12 7 6 11
Ord. Topológica A B D G C E F
Ord. Topológica A B D G C E F
IAED, 2014/2015
Grupo IV (1.5+1.5 = 3.0 val.)
IV.a) Suponha que lhe éGrupo IV implementar
pedido para (1.5+1.5 = uma3.0 val.) de dados de suporte à
estrutura
gestão da rede social de estudantes do IST. Sabendo que esta rede se trata de um grafo
IV.a) Suponha que lhe é pedido para implementar uma
esparso, indique qual a representação de grafos que escolheria. Indique também a expressão
gestão
da complexidade assimptótica maisda rede social
apertada de estudantes
para o espaço do essa
necessário para IST.estrutura
Sabendo de que
dados em termos do númeroesparso,
vérticesindique qual aderepresentação
n e do número arcos m. de grafos que escolhe
da complexidade assimptótica mais apertada para o espaço
Solução: Lista de adjacências com O(n + m) espaço.
dados em termos do número vértices n e do número de arco

Solução: Lista de adjacências com O(n + m) espaço.

IAED, 2014/2015
IV.c) Considere as seguintes afirmações, relativas a um grafo dirigido G = (V, E), com |V | vértices
e |E| arcos. Indique se cada uma das afirmações é verdadeira (V) ou falsa (F). Cada resposta errada
desconta uma resposta certa. A cotação mı́nima que é possı́vel obter nesta pergunta é 0.

1. O espaço de memória utilizado pela matriz de adjacências de G é O(|V | + |E|). F


2. A inserção de um arco em G pode ser O(1), independentemente da adopção de uma matriz de
adjacências ou de listas de adjacências.
V
3. Se G for um grafo de grandes dimensões, cujo grau médio z é muito inferior ao número de
vértices (z << |V |), então devemos representar G através de uma matriz de adjacências.
F
4. Se G é um grafo acı́clico, então admite uma única ordenação topológica válida. F

Solução:

1. F

2. V

3. F

4. F
IAED, 2014/2015
Indique as expressões erradas:

Matriz de Adj. Listas de Adj.


Espaço O(V) O(V+E)

Inserir Arco O(1) O(E)

Encontrar Arco O(1) O(V)

Remover Arco O(V) O(V)

139 IAED, 2014/2015


Indique as expressões erradas:

Matriz de Adj. Listas de Adj.


Espaço O(V) O(V+E)

Inserir Arco O(1) O(E)

Encontrar Arco O(1) O(V)

Remover Arco O(V) O(V)

140 IAED, 2014/2015


Indique as expressões erradas:

Matriz de Adj. Listas de Adj.


Espaço O(V2) O(V+E)

Inserir Arco O(1) O(1)

Encontrar Arco O(1) O(E)

Remover Arco O(1) O(E)

141 IAED, 2014/2015


3
+ 2,0 + 2,0 = 6,0 val.)
Do Indique o onúmero
Indique decomponentes
número de componentes fortemente
fortemente ligadas
ligadas do grafo em(SCC)
baixo do seguinte grafo di

F A

C E

B D

IAED, 2014/2015

Você também pode gostar