algoritmos e estruturas de dados - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii sebenta...

193
Escola Superior de Tecnologia e de Gest˜ ao Instituto Polit´ ecnico de Bragan¸ ca SEBENTA DE ALGORITMOS E ESTRUTURAS DE DADOS Paulo Jorge Matos

Upload: doxuyen

Post on 12-Feb-2019

226 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

Escola Superior de Tecnologia e de Gestao

Instituto Politecnico de Braganca

SEBENTA DE

ALGORITMOS E

ESTRUTURAS DE DADOS

Paulo Jorge Matos

Page 2: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

Versao 2005.05.11

Paulo Jorge Matos

Page 3: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

ii

Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas de Dados, do Curso de Informatica de Gestao (ano lectivo 2004/2005, da Escola Superiorde Tecnologia e de Gestao do Instituto Politecnico de Braganca.

Paulo Matos

Page 4: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

iii

Paulo Matos

Page 5: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

INDICE

Pagina

Contra-Capa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . i

Prefacio . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ii

No inıcio ... . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . iii

Indice . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . iv

Lista de figuras . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . viii

Bibliografia . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xvii

1 Introducao 1

1.1 Nocao de Algoritmo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1

1.2 Eficiencia algorıtmica . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2

1.3 Estruturas de dados vs. Eficiencia algorıtmica . . . . . . . . . . . . . . . . . . . . . . 5

2 Notacao algorıtmica 9

2.1 Representacao algorıtmica . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10

2.2 Linguagem algorıtmica . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10

2.3 Representacao das estruturas de dados . . . . . . . . . . . . . . . . . . . . . . . . . . 12

2.3.1 Conjuntos (Set) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12

2.3.2 Saco (Bag) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15

2.3.3 Sequencias (Seq) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16

2.3.4 Dicionarios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17

2.3.5 Produtos cartesianos (tuplos, records, structs) . . . . . . . . . . . . . . . . . . . . 17

2.3.6 Stack’s (Pilhas) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18

2.3.7 Queue’s (Filas) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19

3 Programacao 21

3.1 Do algoritmo ao programa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21

iv Paulo Matos

Page 6: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

Indice v

3.2 Optimizacoes de codigo: Dicas e truques . . . . . . . . . . . . . . . . . . . . . . . . . 23

3.3 Optimizacoes de codigo: Dicas e truques para C++ . . . . . . . . . . . . . . . . . . 26

4 Estruturas de Dados Lineares 35

4.1 Listas ligadas simples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36

4.1.1 Pesquisa de elementos numa lista simples . . . . . . . . . . . . . . . . . . . . . . . 37

4.1.2 Insercao de elementos numa lista simples . . . . . . . . . . . . . . . . . . . . . . . 38

4.1.3 Remocao de elementos numa lista simples . . . . . . . . . . . . . . . . . . . . . . . 46

4.1.4 Listagem dos elementos de uma lista simples . . . . . . . . . . . . . . . . . . . . . 50

4.2 Listas duplamente ligadas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51

4.2.1 Pesquisa de elementos numa lista duplamente ligada . . . . . . . . . . . . . . . . . 51

4.2.2 Insercao de elementos numa lista duplamente ligada . . . . . . . . . . . . . . . . . 52

4.2.3 Remocao de elementos numa lista duplamente ligada . . . . . . . . . . . . . . . . . 60

4.2.4 Listagem dos elementos de uma lista duplamente ligada . . . . . . . . . . . . . . . 63

4.3 Stack’s . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65

4.3.1 Stack’s estaticas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66

4.3.2 Stack’s dinamicas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69

4.3.3 Stack’s Estaticas x Stack’s Dinamicas . . . . . . . . . . . . . . . . . . . . . . . . . 73

4.4 Queue’s . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75

4.4.1 Queue’s estaticas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76

4.4.2 Queue’s dinamicas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81

4.5 Tabelas de hash . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87

4.5.1 Funcoes de hash . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89

4.5.2 Tratamento de colisoes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91

4.5.3 Linear Probing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91

4.5.4 Listas de colisoes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95

5 Estruturas de Dados Nao Lineares 101

5.1 Introducao as estruturas em arvore . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102

Paulo Matos

Page 7: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

vi Indice

5.2 Arvores Binarias de Pesquisa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102

5.2.1 Inicializacao de um nodo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104

5.2.2 Insercao numa arvore binaria de pesquisa . . . . . . . . . . . . . . . . . . . . . . . 104

5.2.3 Pesquisa numa arvore binaria de pesquisa . . . . . . . . . . . . . . . . . . . . . . . 106

5.2.4 Remocao numa arvore binaria de pesquisa . . . . . . . . . . . . . . . . . . . . . . . 107

5.2.5 Travessias de arvores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110

5.2.6 Representacao sequencial de arvores binarias . . . . . . . . . . . . . . . . . . . . . 112

5.2.7 Peso (altura) de uma arvore binaria de pesquisa . . . . . . . . . . . . . . . . . . . 112

5.3 Arvores Balanceadas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 114

5.3.1 Insercao em arvores balanceadas . . . . . . . . . . . . . . . . . . . . . . . . . . . . 115

6 Grafos 133

6.1 Definicoes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 134

6.2 Operacoes sobre Grafos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 137

6.2.1 Uniao . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 138

6.2.2 Composicao . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 139

6.2.3 Exponenciacao . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 139

6.2.4 Fecho Transitivo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140

6.2.5 Fecho de Kleen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 141

6.3 Representacao de Grafos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 141

6.3.1 Matriz de Adjacencias . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 142

6.3.2 Listas de Adjacencias . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 146

6.4 Exemplo da definicao de grafos em linguagem C . . . . . . . . . . . . . . . . . . . . 149

6.4.1 Matriz de adjacencias . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 150

6.4.2 Lista de adjacencias . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 152

6.5 Algoritmos para grafos nao pesados . . . . . . . . . . . . . . . . . . . . . . . . . . . 153

6.5.1 Se ha caminho entre dois vertices . . . . . . . . . . . . . . . . . . . . . . . . . . . . 154

6.5.2 Qual o caminho entre dois vertices . . . . . . . . . . . . . . . . . . . . . . . . . . . 155

Paulo Matos

Page 8: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

Indice vii

6.5.3 Alcancaveis de um vertice . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 156

6.5.4 Os que alcancam um vertice . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 157

6.5.5 Fecho Transitivo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 157

6.6 Travessia de grafos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 158

6.6.1 Depth-First . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 158

6.6.2 Breadth First . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 159

6.6.3 Iterative Deepening . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 160

6.6.4 Ordem Topologica . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161

6.6.5 Arvore geradora de um grafo (Spanning Tree) . . . . . . . . . . . . . . . . . . . . . 162

6.7 Grafos Pesados . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163

6.7.1 Representacao de grafos pesados . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163

6.7.2 Algoritmos para grafos pesados . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 164

Paulo Matos

Page 9: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

LISTA DE FIGURAS

Pagina

1.1 Ordenacao com tabela de ındices. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6

2.1 Representacao de um conjunto de caracteres. . . . . . . . . . . . . . . . . . . . . . . 13

2.2 Representacao de uma sequencia de caracteres. . . . . . . . . . . . . . . . . . . . . . 16

2.3 Representacao de uma stack. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19

2.4 Representacao de uma queue. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19

3.1 Algoritmo exemplo. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22

3.2 Implementacao do algoritmo da Figura 3.1. . . . . . . . . . . . . . . . . . . . . . . . 23

3.3 Implementacao da sequencia atraves de um objecto em C++. . . . . . . . . . . . . . 23

4.1 Exemplo de uma lista ligada simples. . . . . . . . . . . . . . . . . . . . . . . . . . . . 36

4.2 Apontadores apos a localizacao da posicao onde inserir o novo nodo. . . . . . . . . . 40

4.3 Insercao numa lista vazia. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41

4.4 Apontadores apos a localizacao da posicao onde inserir o novo nodo. . . . . . . . . . 42

4.5 Insercao no topo da lista. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43

4.6 Apontadores apos a localizacao da posicao onde inserir o novo nodo. . . . . . . . . . 44

4.7 Insercao numa posicao intermedia da lista. . . . . . . . . . . . . . . . . . . . . . . . . 45

4.8 Apontadores apos a localizacao da posicao onde inserir o novo nodo. . . . . . . . . . 46

4.9 Insercao no fim da lista. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47

4.10 Remocao no inıcio da lista. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47

4.11 Remocao de uma posicao intermedia da lista. . . . . . . . . . . . . . . . . . . . . . . 48

4.12 Remocao no fim da lista. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49

4.13 Estrutura de uma lista duplamente ligada. . . . . . . . . . . . . . . . . . . . . . . . . 51

4.14 Posicao dos apontadores antes da insercao numa lista vazia. . . . . . . . . . . . . . . 53

4.15 Insercao numa lista vazia. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54

4.16 Posicao dos apontadores na insercao no topo da lista. . . . . . . . . . . . . . . . . . 55

viii Paulo Matos

Page 10: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

Lista de Figuras ix

4.17 Insercao no topo da lista. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56

4.18 Posicao dos apontadores ao inserir numa posicao intermedia da lista. . . . . . . . . . 56

4.19 Insercao numa posicao intermedia da lista. . . . . . . . . . . . . . . . . . . . . . . . . 57

4.20 Posicao dos apontadores aquando da insercao no fim da lista. . . . . . . . . . . . . . 58

4.21 Insercao no fim da lista. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59

4.22 Remocao do topo da lista. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61

4.23 Remocao de uma posicao intermedia da lista. . . . . . . . . . . . . . . . . . . . . . . 61

4.24 Remocao do fim da lista. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62

4.25 Estrutura de uma stack. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65

4.26 Ordem de remocao e insercao numa stack. . . . . . . . . . . . . . . . . . . . . . . . . 66

4.27 Iniciacao de uma stack estatica. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67

4.28 Insercao de um elemento numa stack. . . . . . . . . . . . . . . . . . . . . . . . . . . 68

4.29 Remocao de um elemento de uma stack. . . . . . . . . . . . . . . . . . . . . . . . . . 68

4.30 Implementacao de uma stack sobre listas simples. . . . . . . . . . . . . . . . . . . . . 69

4.31 Insercao numa stack dinamica vazia. . . . . . . . . . . . . . . . . . . . . . . . . . . . 71

4.32 Insercao numa stack dinamica nao vazia. . . . . . . . . . . . . . . . . . . . . . . . . . 71

4.33 Insercao numa stack dinamica nao vazia. . . . . . . . . . . . . . . . . . . . . . . . . . 72

4.34 Estrutura de uma queue. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75

4.35 Estrutura de uma queue estatica. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76

4.36 Insercao de um elemento numa queue estatica. . . . . . . . . . . . . . . . . . . . . . 77

4.37 Queue com base numa sequencia circular. . . . . . . . . . . . . . . . . . . . . . . . . 78

4.38 Representacao de uma queue estatica vazia. . . . . . . . . . . . . . . . . . . . . . . . 79

4.39 Representacao de duas situacoes em que a queue esta cheia. . . . . . . . . . . . . . . 80

4.40 Ilustracao do processo de remocao numa queue estatica. . . . . . . . . . . . . . . . . 81

4.41 Estrutura de uma queue sobre listas simples. . . . . . . . . . . . . . . . . . . . . . . 82

4.42 Insercao numa queue dinamica vazia. . . . . . . . . . . . . . . . . . . . . . . . . . . . 83

4.43 Insercao numa queue dinamica nao vazia. . . . . . . . . . . . . . . . . . . . . . . . . 84

4.44 Remocao numa queue dinamica com mais do que um elemento. . . . . . . . . . . . . 85

Paulo Matos

Page 11: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

x Lista de Figuras

4.45 Estrutura formal de uma tabela de Hash. . . . . . . . . . . . . . . . . . . . . . . . . 88

4.46 Distribuicao esperada para a procura das chaves. . . . . . . . . . . . . . . . . . . . . 90

4.47 Funcao de hash. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90

4.48 Tabela de hash com Linear Probing. . . . . . . . . . . . . . . . . . . . . . . . . . . . 92

4.49 Estrutura de uma tabela de Hash com Listas de Colisoes. . . . . . . . . . . . . . . . 96

5.1 Exemplo de uma arvore. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102

5.2 Estrutura base de uma arvore binaria. . . . . . . . . . . . . . . . . . . . . . . . . . . 103

5.3 Exemplo da remocao de um nodo de uma arvore binaria. . . . . . . . . . . . . . . . 107

5.4 Processo de remocao de um nodo com um unico descendente. . . . . . . . . . . . . . 108

5.5 Processo de remocao de um nodo com dois descendentes. . . . . . . . . . . . . . . . 108

5.6 Representacao sequencial de uma arvore binaria. . . . . . . . . . . . . . . . . . . . . 112

5.7 Exemplos de arvores balanceadas e nao balanceadas. . . . . . . . . . . . . . . . . . . 114

5.8 Insercao a direita numa (sub) arvore com balanco de 1. . . . . . . . . . . . . . . . . 116

5.9 Insercao a esquerda numa (sub) arvore com balanco de -1. . . . . . . . . . . . . . . . 116

5.10 Insercao a direita numa (sub) arvore com balanco de 0. . . . . . . . . . . . . . . . . 116

5.11 Insercao a esquerda numa (sub) arvore com balanco de 0. . . . . . . . . . . . . . . . 116

5.12 Exemplos em que uma sub-arvore balanceada pode ou nao provocar desbalancea-mento na arvore principal. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 117

5.13 Desbalanceamento esquerda-esquerda. . . . . . . . . . . . . . . . . . . . . . . . . . . 117

5.14 Desbalanceamento esquerda-direita. . . . . . . . . . . . . . . . . . . . . . . . . . . . 118

5.15 Desbalanceamento direita-direita. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 118

5.16 Desbalanceamento direita-esquerda. . . . . . . . . . . . . . . . . . . . . . . . . . . . 119

5.17 Arvore antes da insercao do novo do nodo. . . . . . . . . . . . . . . . . . . . . . . . . 119

5.18 Arvore apos a insercao do nodo. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 120

5.19 Arvore apos o balanceamento. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 120

5.20 Arvore antes da insercao do novo nodo. . . . . . . . . . . . . . . . . . . . . . . . . . 121

5.21 Arvore apos a insercao do nodo sem balanceamento. . . . . . . . . . . . . . . . . . . 122

5.22 Arvore apos o balanceamento. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 122

Paulo Matos

Page 12: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

Lista de Figuras xi

5.23 Arvore apos a insercao do nodo sem balanceamento. . . . . . . . . . . . . . . . . . . 123

5.24 Arvore apos o balanceamento. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123

5.25 Arvore antes da insercao do novo nodo. . . . . . . . . . . . . . . . . . . . . . . . . . 125

5.26 Arvore apos a insercao do nodo sem balanceamento. . . . . . . . . . . . . . . . . . . 125

5.27 Arvore apos o balanceamento. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 126

5.28 Arvore apos a insercao do nodo sem balanceamento. . . . . . . . . . . . . . . . . . . 127

5.29 Arvore apos o balanceamento. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 127

5.30 Arvore antes da insercao do novo nodo. . . . . . . . . . . . . . . . . . . . . . . . . . 128

5.31 Arvore apos a insercao do nodo sem balanceamento. . . . . . . . . . . . . . . . . . . 129

5.32 Arvore apos o balanceamento. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 129

6.1 Exemplo de um grafo. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 134

6.2 Exemplo de um grafo orientado. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 137

6.3 Representacao do grafo identidade de G. . . . . . . . . . . . . . . . . . . . . . . . . . 138

6.4 Grafos exemplo. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 138

6.5 Uniao do grafo G1 e G2. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 139

6.6 Composicao dos grafos G1 e G2. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 139

6.7 Expoentes de G1. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140

6.8 Expoente tres de G1 (G31). . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140

6.9 Fecho transitivo de G1 (G+1 ). . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 141

6.10 Fecho de Kleen de G1 (G∗1). . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 141

6.11 Matriz de adjacencias de G2. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 142

6.12 Representacao completa de G2. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143

6.13 Listas de adjacencias. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 147

6.14 Mapa a utilizar como exemplo. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 150

6.15 Exemplo de duas travessias Depth First sobre o mesmo grafo. . . . . . . . . . . . . . 159

6.16 Grafos para os exercıcios. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 160

6.17 Grafo de precedencias. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161

Paulo Matos

Page 13: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

xii Lista de Figuras

6.18 Ordem topologica do grafo da Figura 6.17. . . . . . . . . . . . . . . . . . . . . . . . 161

6.19 Grafo G e respectivas arvores geradoras por Depth-First. . . . . . . . . . . . . . . . . 162

6.20 Arvores geradoras por Breadth-First do grafo G da Figura 6.19. . . . . . . . . . . . . 162

6.21 Grafo exemplo para gerar a Spanning Tree Mınima. . . . . . . . . . . . . . . . . . . 165

6.22 Primeira iteracao do algoritmo para determinar a Spanning Tree Mınima. . . . . . . 166

6.23 Segunda iteracao do algoritmo para determinar a Spanning Tree Mınima. . . . . . . 167

6.24 Terceira iteracao do algoritmo para determinar a Spanning Tree Mınima. . . . . . . 168

6.25 Quarta iteracao do algoritmo para determinar a Spanning Tree Mınima. . . . . . . . 169

6.26 Quinta iteracao do algoritmo para determinar a Spanning Tree Mınima. . . . . . . . 170

6.27 Sexta iteracao do algoritmo para determinar a Spanning Tree Mınima. . . . . . . . . 171

6.28 Mapa do Exemplo 6. ?? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 172

Paulo Matos

Page 14: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

Apresentacao

Disciplina de Algoritmos e Estruturas de Dados

Objectivos

O programa proposto para a disciplina de Algoritmos e Estruturas de Dados, para o anolectivo de 2004/2005, visa aperfeicoar as metodologias de programacao e dotar os alunos dosconhecimentos, designadamente sobre algoritmos e estruturas de dados, necessarios ao desenvol-vimento de solucoes eficientes para os mais variados problemas de programacao. No termino dadisciplina, o aluno devera ser capaz de desenhar a estrutura de dados e desenvolver os algoritmosque melhor se adequam a cada problema.

Funcionamento da disciplina

As aulas da disciplina de Algoritmos e Estruturas de Dados, que sao de cariz teorico-pratico,dividem-se em dois tipos de perıodos:

• Perıodos de exposicao, durante os quais o docente expoe os conteudos, recorrendo deforma intercalada aos metodos expositivos e interrogativos, no sentido de levar o aluno aacompanhar o raciocınio e a perceber os conhecimentos que docente visa transmitir;

• Perıodos de desenvolvimento e implementacao, durante os quais o docente, atraves deuma metodologia activa, leva o aluno a conceber e desenvolver solucoes para os problemascolocados. Pretende-se assim estimular a capacidade do aluno para analisar, conceber eimplementar solucoes para problemas de cariz pratico, sempre supervisionado pelo docente,que encaminhara o aluno, no sentido de alcancar as solucoes mais adequadas e corrigirpossıveis erros de concepcao e implementacao.

Paulo Matos xiii

Page 15: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

xiv Apresentacao

Docentes

Prof. Paulo Jorge MatosGab. [email protected]

Prof. Reis QuarteuGab. [email protected]

Horario de atendimento

Nome Dia da semana HoraPaulo Jorge Matos Segunda-Feira 11:00-13:00

Quarta-Feira 9:00-11:00Reis Quarteu Quarta-Feira 14:00-16:00

Quinta-Feira 10:00-12:00

Avaliacao

A avaliacao da disciplina de Algoritmos e Estruturas de Dados contempla tres componentes:

TP Um conjunto de tres trabalhos distribuıdos aleatoriamente ao longo das aulas

Proj Um trabalho de projecto a realizar individualmente ou em grupos de dois elementos

PE Uma prova escrita a realizar na epoca propria para exames

A nota final e calculada da seguinte forma:

Nota Final = 30% TP + 30% Proj + 40%PE

As tres componentes tem nota mınima de 7.5 valores em 20.

Detalhes acerca da 1a componente

Para a primeira componente estao previstos cerca de meia centena de trabalhos, que seraodistribuıdos pelo docente no decorrer das aulas. E de realcar que cada aluno apenas tem querealizar tres trabalhos.

Detalhes acerca da 2a componente

Paulo Matos

Page 16: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

Notas e Avisos xv

Esta previsto que o trabalho de projecto seja atribuıdo no fim da materia sobre filas prio-ritarias, isto e, por volta da vigesima aula (meados do mes de Maio). Os alunos deverao entregaro trabalho ate ao final do semestre (meados de Junho).

A avaliacao para as epocas especiais contempla um trabalho de projecto com defesa oral(Proj) e uma prova escrita (PE). A nota final e calculada da seguinte forma:

Nota final = 30% Proj + 70% PE

Ambas componentes tem nota mınima de 7.5 valores.

Notas e Avisos

• Os alunos devem requisitar no centro de comunicacoes da ESTiG uma conta para poderemtrabalhar durante as aulas.

• Cada aluno deve comparecer as aulas do turno que lhe foi atribuıdo pela secretaria academica.

• Os alunos devem, sempre que necessario, recorrer ao apoio dos docentes, utilizando paratal as horas de atendimento disponıveis, ou colocando as questoes por email.

Paulo Matos

Page 17: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

xvi Apresentacao

Programa da disciplina

1. Introducao

(a) Nocao de Algoritmo

(b) Ordem de complexidade de um algoritmo

(c) Representacao das estruturas de dados vs. Ordem de complexidade de um algoritmo

2. Revisoes sobre templates e classes abstractas

3. Especificacao de uma linguagem algorıtmica

(a) Representacao algorıtmica

(b) Representacao das estruturas de dados

(c) Do algoritmo ao programa, boas e mas tecnicas de programacao

4. Estruturas de Dados Lineares

(a) Listas ligadas simples (revisoes)

(b) Listas duplamente ligadas

(c) Stack

(d) Queue

(e) Tabelas de Hash

5. Estruturas de Dados Nao Lineares

(a) Arvores Genericas

(b) Arvores Binarias

(c) Arvores Binarias de Pesquisa

(d) Arvores Binarias de Pesquisa Balanceadas

(e) Heaps

6. Grafos

(a) Definicoes sobre grafos

(b) Representacao de grafos

(c) Algoritmos para grafos nao pesados

(d) Algoritmos para grafos pesados

Paulo Matos

Page 18: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

Bibliografia xvii

Bibliografia

[1] Paulo Jorge Matos. Sebenta de Algoritmos e Estruturas de Dados. Instituto Politecnico deBraganca, Braganca, Portugal, Abril 2005.

[2] Pimenta Rodrigues. Programacao em C++ - Algoritmos e Estruturas de Dados. FCA.

[3] Douglas Schmidt. C++ Tips and Traps. University of Vanderbilt.

[4] Sedgewick. Algorithmics in C++. Addison-Wesley Publishing Company.

Paulo Matos

Page 19: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

xviii

Paulo Matos

Page 20: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

Capıtulo 1Introducao

Indice

1.1 Nocao de Algoritmo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1

1.2 Eficiencia algorıtmica . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2

1.3 Estruturas de dados vs. Eficiencia algorıtmica . . . . . . . . . . . . . . . . 5

1.1 Nocao de Algoritmo

Um algoritmo consiste numa sequencia de operacoes bem definidas e sem ambiguidades, quedescreve uma solucao exequıvel computacionalmente, de um problema, independentemente dalinguagem de programacao a utilizar.

A implementacao pratica de um algoritmo pode variar com a linguagem a utilizar e deprogramador para programador, no entanto para um mesmo estado inicial e conjunto de dadosde entrada (definidos como instancia inicial do problema) deve produzir sempre a mesma solucao.

Paulo Matos 1

Page 21: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

2 Capıtulo 1. Introducao

1.2 Eficiencia algorıtmica

Nem sempre a representacao algorıtmica traduz directamente a implementacao mais eficazde um problema. E funcao do programador implementar a solucao adequada considerando asrestricoes da linguagem a utilizar. E no entanto possıvel medir a qualidade da estrutura basedo algoritmo.

Uma forma de avaliacao consiste em determinar o esforco computacional do algoritmo naresolucao de determinado problema. E atraves do valor desse esforco que se pode comparar aeficiencia entre algoritmos.

O esforco computacional pode ser determinado atraves da Ordem de Complexidade doalgoritmo, que e definida da seguinte forma:

Um algoritmo tem como Ordem de Complexidade O(f(n)), quando o numero de operacoesprimitivas executadas para obter a solucao para a instancia do problema, cujo tamanho e n, naoexceder uma constante vezes f(n), para um n suficientemente grande.

Ou seja, diz-se que g(n) e de ordem de complexidade O(f(n)), se existirem duas constantesK e n′ tal que:

|g(n)| ≤ K ∗ |f(n)| ,∀n ≥ n′ (1.1)

Exemplo 1.1

Pretende-se avaliar a ordem de complexidade da solução fornecida, para a resolução deum problema de ordenação de uma sequência de n números, por comparações sucessivas.A solução, para um problema de tamanho três, é a seguinte:

Posição: 1 2 3Sequência: < 7, 9, 2 >

(1) SE seq.get(1)>seq.get(2) ENTÃO(2) seq.swap(1,2) < 7, 9, 3 >(3) FSE(4) SE seq.get(2)>seq.get(3) ENTÃO(5) seq.swap(2,3) < 7, 3, 9 >(6) FSE(7) SE seq.get(1)>seq.get(2) ENTÃO(8) seq.swap(1,2) < 3, 7, 9 >(9) FSE

Para o caso genérico (tamanho n), a solução fornecida é a seguinte:

(1) PARA i ← n ATÉ 2 SALTO -1 FAZER(2) PARA j ← 1 ATÉ i-1 FAZER

Paulo Matos

Page 22: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

1.2. Eficiencia algorıtmica 3

(3) SE seq.get(j)>seq.get(j+1) ENTÃO(4) seq.swap(j,j+1)(5) FSE(6) FPARA(7) FPARA

De onde se obtêm os seguintes valores de g(n):

n 2 3 4 5 ... ng(n) 1 1+2 1+2+3 1+2+3+4 ... 1+...+n-1

É possível converter a série da qual resulta g(n) num somatório, que por sua vez podeser convertido numa equação em ordem a n:

g(n) = τ ∗n−1∑i=1

i = τ ∗ n ∗ (n− 1)2

(1.2)

Em que τ representa o tempo de execução por ciclo. O seu valor é no entanto irrelevantepara os restantes cálculos, uma vez que pode ser absorvido pela constante K, pelo que éeliminado.

Para determinar a ordem do algoritmo é necessário encontrar um f(n), tal que:

|g(n)| ≤ K ∗ |f(n)| ,∀n ≥ n′ (1.3)

Propondo f(n) = n2, obtém-se a seguinte inequação:

∣∣∣∣n ∗ (n− 1)2

∣∣∣∣ ≤ K ∗∣∣∣n2

∣∣∣ (1.4)

de onde é possível concluir que, para valores de K ≥ 0.5, a inequação é sempre verda-deira (n = 2,3,4,...). Pelo que o f(n) proposto garante sempre que K ∗ f(n) é um majorantede g(n). Diz-se assim que a ordem do algoritmo é:

O(f(n)) = O(n2) (1.5)

Ou seja, no pior dos casos a ordem de complexidade do algoritmo é quadrática. No en-tanto uma análise mais atenta permite concluir que a solução proposta é determinística,isto é, para um sequência de tamanho n demora sempre o mesmo tempo a executar, in-dependentemente desta já estar, ou não, ordenada (ou parcialmente ordenada). Com estasolução pode-se mesmo afirmar que a Eq. 1.2 fornece o valor (muito aproximado) do tempode execução.

Paulo Matos

Page 23: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

4 Capıtulo 1. Introducao

Poder-se-a entao perguntar o porque de utilizar a Ordem de Complexidade como formade avaliacao de algoritmos. A resposta e simples, e que apesar de muitos algoritmos seremdeterminısticos, muitos outros ha em que tal nao acontece e e para estes ultimos que esta formade avaliacao e util, uma vez que permite obter uma funcao que serve de majorante para o tempode execucao.

Na proxima seccao e apresentado uma breve analise da Ordem de Complexidade deste algo-ritmo em comparacao com uma segunda solucao nao-determinıstica.

Valores tıpicos para a ordem de complexidade sao:

O(1) Constante

O(log n) Logarıtmica

O(n log n) n log n

O(n2) Quadratica

O(n3) Cubica

O(2n) Exponencial

De notar que a ordem de complexidade apenas permite comparar algoritmos de ordens dis-tintas. Caso contrario, e entao necessario ter em conta a soma dos tempos de execucao dasinstrucoes primitivas, bem como uma analise mais detalhada da estrategia do algoritmo (verex. 2).

Nem sempre um algoritmo de ordem elevada (n3, 2n), significa um mau algoritmo. E ocaso dos algoritmos para a resolucao de problemas NP-Completos, onde nao e possıvel obtersolucoes optimas num tempo de ordem polinomial, mas com a insercao de algumas heurısticas,permite ”desprezar”grande parte do espaco de solucoes, tornando a sua eficiencia comparavel aum algoritmo de ordem inferior

A Ordem de Complexidade e uma medida a priori, ou seja, permite antever a eficiencia daimplementacao pratica do algoritmo. Existem outras medidas para avaliar a qualidade de umasolucao e que se aplicam a posteriori, ou seja, medem o tempo que a implementacao do algoritmoleva a executar. A este tipo de estrategia designa-se por bench marketing.

Ocorre, por vezes, que o domınio dos valores de entrada de um algoritmo e muito grandee diversificado, nao sendo possıvel medir a eficiencia da solucao para todas as hipoteses deentrada. Torna-se entao necessario escolher um sub-domınio representativo que permita avaliara qualidade da solucao.

Exercıcios:

a) O seguinte algoritmo descreve uma solucao utilizada na pesquisa de um elemento numasequencia. Determine a ordem de complexidade para: o corpo principal do algoritmo, afuncao Pesquisar(...) e o conjunto das duas.

(1) ALGORITMO Pesquisa numa sequência

Paulo Matos

Page 24: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

1.3. Estruturas de dados vs. Eficiencia algorıtmica 5

(2) VARIÁVEL i, val, n, x :INTEIRO(3) VARIÁVEL lista :seq(INTEIRO)(4) INÍCIO(5) ESCREVER("Qual o tamanho da sequência?")(6) LER(n)(7) PARA i ← 1 ATÉ n FAZER(8) lista.set(LER(),i)(9) FPARA(10) ESCREVER("Valor a pesquisar?")(11) LER(val)(12) x ← Pesquisar(lista,n,val)(13) ESCREVER("O valor encontra-se na posição ",x)(14) FIM ALGORITMO(15)

(16) PROCEDIMENTO(17) Pesquisar(lista:seq(INTEIRO),n,val:INTEIRO):INTEIRO(18) VARIÁVEL meio, x :INTEIRO(19) INÍCIO(20) SE n = 1 ENTÃO(21) SE lista.get(n)=val ENTÃO(22) RETORNAR 1(23) SENÃO(24) RETORNAR 0(25) FSE(26) FSE(27) meio ← n/2 + n MOD 2(28) x ← Pesquisar(lista<1...meio>, meio, val)(29) SE x = 1 ENTÃO(30) RETORNAR x(31) FSE(32) RETORNAR meio + Pesquisar(lista<meio+1....n>,n-meio,val)(33) FIM

b) Determine a ordem de complexidade de um algoritmo de pesquisa linear para uma matriztridimensional.

1.3 Estruturas de dados vs. Eficiencia algorıtmica

Na representacao algorıtmica, as estruturas de dados sao normalmente apresentadas comoentidades abstractas, nomeadamente no caso das estruturas mais complexas, como por exemplo:as sequencias, conjuntos, stack’s, etc.

A implementacao do algoritmo passa por escolher as estruturas de dados que melhor seadequam a execucao do mesmo, pois depende destas grande parte da eficiencia da implementacaofinal, pode-se mesmo afirmar que existe uma dependencia directa entre a eficiencia e as estruturasde dados utilizadas. O exemplo 1. 2 ajuda a compreender a relacao entre estruturas de dadose eficiencia algorıtmica.

Paulo Matos

Page 25: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

6 Capıtulo 1. Introducao

Exemplo 1.2

O gestor do Hospital Só Saúde S.A., encomendou a um seu funcionário o desenvolvi-mento de um pequeno programa para gerir a marcação de consultas. O programa deveriaarmazenar a informação dos utentes pela ordem da marcação das consultas, de tal formaque o primeiro utente a registar-se, seria o primeiro a ter consulta. Mas o programa tam-bém deveria ser capaz de gerar uma lista com todas as consultas marcadas, ordenada pelonúmero do cartão de utente.

Para satisfazer o segundo requisito (gerar a lista ordenada de todas as marcações), oresponsável pela implementação do programa concebe duas soluções: a primeira consisteem implementar uma rotina que dinamicamente obtém a lista ordenada, ou seja, per-corre toda a base de dados, ordena os elementos e devolve o resultado. A segunda soluçãoconsiste em manter uma tabela de índices que permite manter a ordenação dos utentesconforme estes se inscrevem. Esta solução encontra-se representada na Fig. 1.1.

Figura 1.1: Ordenacao com tabela de ındices.

A primeira solução recorre a uma função genérica de ordenação, semelhante à do algo-ritmo do Exemplo 1. 1 , que é de ordem quadrática O(n2), mas para o qual é possível obteruma equação mais aproximada (Eq. 1.2).

A segunda solução não necessita de qualquer tipo de procedimento uma vez que oselementos já se encontram ordenados. Apenas é necessário garantir que os índices sãoactualizados, o que se consegue através de um processo de inserção ordenada, percorrendoa sequência de índices à procura da posição a ocupar pela nova marcação.

No pior dos casos, esta solução percorre os n − 1 elementos da lista, pelo que se tratade uma rotina de ordem linear O(n). No entanto como este procedimento é executado paracada uma das marcações, perfaz um total nxn (no pior dos casos), correspondendo assim auma solução de ordem de complexidade quadrática.

A segunda solução necessita ainda de uma sequência de índices, a qual como é óbvioocupa espaço em memória. No caso de se utilizar uma lista ligada simples para implemen-tar a sequência de índices, é necessário por cada marcação mais:

1 apontador (4 bytes) + 1 inteiro (4 bytes) = 8 bytes.

A seguinte tabela apresenta os valores comparativos entre as duas soluções:

Paulo Matos

Page 26: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

1.3. Estruturas de dados vs. Eficiencia algorıtmica 7

n (marcações) 100 1000 100001a Solução Espaço 0 0 0

Tempo 4950 4995E2 49995E32a Solução Espaço 800bytes 8Kbytes 80Kbytes

Tempo 1E4 1E6 1E8

A partir da tabela poder-se-ia concluir que a primeira solução é a melhor, uma vez queé da mesma ordem que a segunda, mas não necessita da lista de índices, permitindo assimeconomizar o espaço por esta ocupado. No entanto uma análise mais detalhada permitever que apesar de o primeiro algoritmo demorar exactamente τ ∗ n∗(n−1)

2 , o segundo ficamuito aquém desse valor. É que no pior dos casos, que ocorre quando as marcações sãoinseridas por ordem crescente da chave de ordenação, a segunda solução tem apenas quepercorrer o número de elementos existentes na lista de índices aquando da inserção, e nãon. O que perfaz para o total das marcações a seguinte soma:

Ordem de marcação 1o 2o 3o 4o ... no

Nociclos/ marcação 0 + 1 + 2 + 3 + ... + (n-1)

Total τ ∗ n∗(n−1)2

O que parecia inicialmente uma solução pior, é na realidade muito mais eficiente, umavez que, no pior dos casos, demora tanto tempo quanto a primeira solução. Mas como émuito pouco provável que as marcações se efectuem exactamente pela ordem crescente donúmero de identificação do cartão de utente, resulta que o tempo total de execução é narealidade bastante inferior. Para um cálculo mais aproximado é legítimo considerar queem média o número de elementos da sequência a percorrer, até se encontre a posição ondeinserir o novo índice, é de metade o que perfaz um tempo total de τ ∗ n∗(n−1)

4 .

O seguinte quadro mostra o tempo de execução e o espaço ocupado por ambas as so-luções, onde se pode verificar que para valores de n suficientemente elevados, a primeirasolução possui uma performance bastante inferior à segunda.

n (marcações) 100 1000 100001a Solução Espaço 0 0 0

Tempo 4950 4995E2 49995E32a Solução Espaço 800bytes 8Kbytes 80Kbytes

Tempo 2475 2497.5E2 24997.5E3

Paulo Matos

Page 27: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

8 Capıtulo 1. Introducao

Paulo Matos

Page 28: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

Capıtulo 2Notacao algorıtmica

Indice

2.1 Representacao algorıtmica . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10

2.2 Linguagem algorıtmica . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10

2.3 Representacao das estruturas de dados . . . . . . . . . . . . . . . . . . . . 12

2.3.1 Conjuntos (Set) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12

2.3.2 Saco (Bag) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15

2.3.3 Sequencias (Seq) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16

2.3.4 Dicionarios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17

2.3.5 Produtos cartesianos (tuplos, records, structs) . . . . . . . . . . . . . . . . . . 17

2.3.6 Stack’s (Pilhas) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18

2.3.7 Queue’s (Filas) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19

Paulo Matos 9

Page 29: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

10 Capıtulo 2. Notacao algorıtmica

2.1 Representacao algorıtmica

A materia ate aqui apresentada, comprova que podem existir diversas implementacoes de umalgoritmo, certamente umas melhores que as outras, comprova-se tambem que existem muitosdetalhes que dependem da linguagem utilizada, mas que sao perfeitamente dispensaveis paraa apresentacao e explicacao da maior parte dos algoritmos. Faz no entanto falta definir asintaxe para a representacao algorıtmica se faca de forma inequıvoca. De seguida apresenta-se a linguagem algorıtmica a utilizar, sempre que possıvel, nas aulas teoricas de Tecnicas deProgramacao.

Faz falta que antes da apresentacao da gramatica se estabelecam algumas convencoes para arepresentacao da sintaxe da linguagem algorıtmica, sao elas as seguintes:

MAIUSCULAS e a negrito Palavras reservadas;

[... ] Partes opcionais da gramatica;

El → Er Expressao do lado esquerdo (El) deriva na frase do lado direito (Er);

Sımbolos reservados dentro da sintaxe;

a | b Expressao opcional entre a e b.

A seguir a uma palavra reservada pode surgir, separado pelo caracter ’/’, a abreviatura pelaqual a palavra reservada pode ser substituıda, por exemplo:

expressao exemplo → PROCEDIMENTO/PROC Nome

2.2 Linguagem algorıtmica

Algoritmo → ALGORITMO Nome[Declaração_variáveis][Declaração_de_constantes][INÍCIO]

Seq_de_instruçõesFIM .

Declaração_variáveis → VARIÁVEL/VAR Lista_variáveis":"Tipo_variável[Declaração_variáveis] .

Lista_variáveis → Variável [","Lista_variáveis] .

Tipo_variável → INTEIRO/INT | REAL | ...

Paulo Matos

Page 30: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

2.2. Linguagem algorıtmica 11

Declaração_constantes → CONSTANTE/CONSTLista_atrib_const .

Lista_atrib_const → Nome_constante "←"Valor[","Lista_atrib_const] .

Seq_de_instruções → Instrução [Seq_de_instruções] .

Instrução → Instrução_simples |Instrução_Se |Instrução_Enq |Instrução_Rep |Instrução_Para .

Instrução_simples → SKIP |ABORT [mensagem] |Atribuição |Inv_de_procedimentos |RETORNAR/RET [Expressão] |Descrição_informal .

Descrição_informal → "["texto "]// texto .

Atribuição → Variável "←"Expressão

Inv_de_procedimentos → Nome_função"("Lista_valores ")"

Instrução_Se → SE Expressão ENTÃOSeq_de_ instruções

[SENÃO Seq_de_instruções]FSE .

Instrução_Enq → ENQUANTO/ENQ Expressão FAZERSeq_de_instruções

FENQ

Instrução_Rep → REPETIR/REPSeq_de_instruções

ATÉ Expressão

Instrução_Para → PARA Variável"←"Expressão [Incr] FAZER

Seq_de_instruçõesFPARA

Incr → SALTO Constante

Def_de_funções → PROCEDIMENTO/PROC Nome"("[Lista_parâmetros]")"[":"Tipo_de_retorno]

Paulo Matos

Page 31: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

12 Capıtulo 2. Notacao algorıtmica

[Declaração_variáveis][Declaração_de_constantes][INÍCIO]

Seq_de_instruçõesFIM .

Lista_parâmetros → [IN:|OUT:|INOUT:] Nome_parâmetro":"Tipo_de_parâmetro[","Lista_parâmetros]

Exemplo 2.1

(1) ALGORITMO Ordenar lista(2) VARIÁVEL i, j, n :INTEIRO(3) VARIÁVEL lista :Seq(INTEIRO)(4) INÍCIO(5) LER(n)(6) PARA i ← n-1 ATÉ 1 SALTO -1 FAZER(7) PARA j ← 1 ATÉ i FAZER(8) SE lista.get(j)>lista.get(j+1) ENTÃO(9) lista.swap(j,j+1)(10) FSE(11) FPARA(12) FPARA(13) FIM

2.3 Representacao das estruturas de dados

Para além dos tipos primitivos de dados, tal como INTEIRO, ou REAL, é também convenienteque a linguagem algorítmica permita de�nir novos tipos de dados, como é o caso das estruturasou mesmo dos tipos abstractos de dados (listas, árvores, etc). As secções que se seguem visammostrar como é que utilizando uma notação algorítmica é possível de�nir novos tipos de dados,incluindo tipos abstractos.

2.3.1 Conjuntos (Set)

Os conjuntos são estruturas abstractas de dados que permitem guardar/representar um con-junto de elementos do mesmo tipo. Não aceitam no entanto elementos duplicados e não pressupõequalquer tipo de ordenação ou organização. A representação de um conjunto encontra-se na Fi-gura 2.1.

Paulo Matos

Page 32: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

2.3. Representacao das estruturas de dados 13

Figura 2.1: Representacao de um conjunto de caracteres.

Declaracao de conjuntos

A declaração de um conjunto de inteiros, representado por conjA, faz-se da seguinte forma:

VARIÁVEL conjA :Set(INTEIRO)

Operadores de conjuntos

new: → Set(DATA) // Cria e inicializaum conjunto

free: Set(DATA) → ∅ // Destrói o conjuntoφ: → Set(DATA) // Conjunto vazio∪: Set(DATA) x Set(DATA) → Set(DATA) // União de conjuntos∩: Set(DATA) x Set(DATA) → Set(DATA) // Intersecção de conjuntos\: Set(DATA) x Set(DATA) → Set(DATA) // Diferença de conjuntos∈: Set(DATA) x DATA → BOOL // Testa se DATA

pertence a Set(DATA)⊂: Set(DATA) x Set(DATA) → BOOL // Testa se

Set(DATA)⊂Set(DATA)⊃: Set(DATA) x Set(DATA) → BOOL // Testa se

Set(DATA)⊃Set(DATA)

Definicao de uma interface para conjuntos (Set)

#ifndef ___TSET#define ___TSET// template <class T>// class Bag;// template <class T>// class Seq;#include <iostream>using namespace std;// *****************************************************************// ATENÇÃO: Os métodos que estão comentados, são métodos que:// não podem fazer parte da interface;// ou não podem ser virtuais,// ou cuja implementação apenas é recomendada, mas não obrigatória

Paulo Matos

Page 33: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

14 Capıtulo 2. Notacao algorıtmica

// *****************************************************************template <class T>class Set{private:

// T vnulo; // Valor nulo do tipo Tpublic:

// Construtores do Set// Set(const T&);// Set(const T&,const T&);// Construtor de cópia// Set(Set<T>&);// Construtores de cópia utilizando outras estruturas// Set(Bag<T>&);// Set(Seq<T>&);// void setVNull(const T&);// Destrutor// virtual ~Set();virtual const T& getVNull()const=0;// Métodos para o Setvirtual bool empty()const=0; // Testa se o Set está vaziovirtual int count()const=0; // Número de elementos do Setvirtual int remAll()=0; // Remove todos elementos do Set// Métodos entre Set vs Tvirtual bool add(const T&)=0; // Adiciona elemento ao Setvirtual bool rem(const T&)=0; // Remove elemento do Setvirtual bool has(const T&)const=0; // Testa se o Set contém um dado elemento// Métodos entre Setsvirtual Set<T> &reunion(const Set<T>&)=0; // União entre Setsvirtual Set<T> &difference(const Set<T>&)=0; // Diferença entre Setsvirtual Set<T> &interception(const Set<T>&)=0; // Intersecção de Setsvirtual bool equal(const Set<T>&)const=0; // Igualdade entre Setsvirtual bool contain(const Set<T>&)const=0; // Testa se o objecto contém todos

// os elementos de um dado Set// Redefinição de operadoresvirtual bool operator!() const=0; // Testa se o Set está vazio

// (devolve true se tal ocorrer)virtual Set<T>& operator+=(const T&)=0; // Adiciona um novo elemento ao Setvirtual Set<T>& operator-=(const T&)=0; // Remove um dado elemento do Setvirtual bool operator()(const T&)=0; // Adiciona um novo elemento ao Setvirtual bool operator[](const T&)const=0; // Testa se o Set contém

// um dado elementovirtual Set<T>& operator=(const Set<T>&)=0; // Atribuição entre Sets// virtual Set<T>& operator=(const Bag<T>&)=0; // Atribuição entre Sets// virtual Set<T>& operator=(const Seq<T>&)=0; // Atribuição entre Setsvirtual Set<T>& operator+=(const Set<T>&)=0; // União entre dois Setsvirtual Set<T>& operator-=(const Set<T>&)=0; // Diferença entre dois Setsvirtual Set<T>& operator^=(const Set<T>&)=0; // Intersecção entre dois Setsvirtual bool operator==(const Set<T>&)const=0; // Testa se dois Sets são iguaisvirtual bool operator!=(const Set<T>&)const=0; // Testa se dois Sets são diferentesvirtual bool operator&&(const Set<T>&)const=0; // Testa se dois Sets são iguais

// (estritamente iguais)

Paulo Matos

Page 34: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

2.3. Representacao das estruturas de dados 15

virtual bool operator<=(const Set<T>&)const=0; // Testa se o objecto contém todos// os elementos de um dado Set

// Métodos auxiliaresvirtual bool load(char*)=0; // Carrega os dados do Set

// a partir de um ficheirovirtual bool save(char*)=0; // Salvaguarda os dados

// do Set em ficheiro// virtual int buildQueue(Queue<T>&,int)=0;// Métodos amigáveis// friend ostream& operator<< <T>(ostream&,Set<T>&); // Lista os elementos do Set

};#endif

2.3.2 Saco (Bag)

Os sacos (bag) são estruturas abstractas de dados que permitem guardar/representar várioselementos do mesmo tipo. À semelhança dos conjuntos não pressupõe qualquer tipo de ordenaçãoou organização, aceita no entanto elementos repetidos.

Declaracao de conjuntos

A declaração de um conjunto de inteiros, representado por bagA, faz-se da seguinte forma:

VARIÁVEL bagA :Bag(INTEIRO)

Operadores dos sacos

Sobre os sacos podem ser utilizados praticamente todos os operadores de�nidos para os con-juntos. No entanto a sua aplicação poderá diferir de caso para caso e até mesmo de implemen-tação para implementação. Por exemplo, supondo que BagA e BagB são dois sacos tais que:BagA=10,12,12,12,15, BagB=12,12,15,21. Se é mais ou menos fácil de aceitar que a união destesdois sacos resultaria em 10,12,12,12,12,15,21, já não é tão simples saber o resultado da intersec-ção. Esta operação poderá levar apenas em conta os diferentes valores de cada um dos sacos,resultando assim em 12,15; ou também poderá levar em conta o número de elementos, resultandoem 12,12,15; poderá inclusive haver outras interpretações.

new: → Bag(DATA) // Cria e inicializaum saco

free: Bag(DATA) → ∅ // Destrói o sacoφ: → Bag(DATA) // Saco vazio∪: Bag(DATA) x Bag(DATA) → Bag(DATA) // União de sacos∩: Bag(DATA) x Bag(DATA) → Bag(DATA) // Intersecção de sacos\: Bag(DATA) x Bag(DATA) → Bag(DATA) // Diferença de sacos∈: Bag(DATA) x DATA → BOOL // Testa se DATA

pertence a Bag(DATA)

Paulo Matos

Page 35: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

16 Capıtulo 2. Notacao algorıtmica

⊂: Bag(DATA) x Bag(DATA) → BOOL // Testa seBag(DATA)⊂Bag(DATA)

⊃: Bag(DATA) x Bag(DATA) → BOOL // Testa seBag(DATA)⊃Bag(DATA)

2.3.3 Sequencias (Seq)

As sequências são estruturas abstractas de dados que permitem guardar/representar conjun-tos de elementos do mesmo tipo. Os elementos encontram-se organizados sequencialmente eassociados a um índice. Nada impede a existência de elementos duplicados. A representação deuma sequência encontra-se na Figura 2.2.

Figura 2.2: Representacao de uma sequencia de caracteres.

Declaracao de sequencias

A declaração de uma sequência de inteiros, representada por seqA, faz-se da seguinte forma:

VARIÁVEL seqA :Seq(INTEIRO)

Operadores de sequencias

new: → Seq(DATA) // Cria e inicializauma sequência vazia

free: Seq(DATA) → ∅ // Destrói a sequência<>: → Seq(DATA) // Sequência vazia<_._>: Seq(DATA) x DATA → Seq(DATA) // Acrescenta DATA

ao fim da sequência∧: Seq(DATA) x Seq(DATA) → Seq(DATA) // Concatenação de sequênciashead: Seq(DATA) → DATA // Devolve o elemento

do topo da sequênciatail: Seq(DATA) → Seq(DATA) // Devolve a cauda

da sequênciaget: Seq(DATA) x ÍNDICE → DATA // Devolve o elemento

assinalado por ÍNDICEset: Seq(DATA) x DATA x ÍNDICE → Seq(DATA) // Insere DATA no posição

assinalada por ÍNDICEswap: Seq(DATA) x ÍNDICE1 x ÍNDICE2 → Seq(DATA) // Faz a troca entre os

elementos de ÍNDICE1 e ÍNDICE2

Paulo Matos

Page 36: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

2.3. Representacao das estruturas de dados 17

2.3.4 Dicionarios

Os dicionários são estruturas abstractas de dados que permitem guardar/representar pares deelementos, cada um formado por uma chave e respectiva informação. O conceito de dicionárionão de�ne a organização interna dos elementos, apenas garante que quando um chave existe éúnica.

Declaracao de dicionarios

A declaração de um dicionário que relaciona um valor do tipo inteiro com uma estrutura dedados do tipo ALUNO (um tuplo), representada por dictA, faz-se da seguinte forma:

VARIÁVEL dictA :Dict(INTEIRO,ALUNO)

Operadores do dicionario

new: → Dict(T1,T2) // Cria e inicializa um dicionário

free: Dict(T1,T2) → ∅ // Destrói o dicionário∪: Dict(T1,T2) x Dict(T1,T2) → Dict(T1,T2) // União de dicionários∩: Dict(T1,T2) x Dict(T1,T2) → Dict(T1,T2) // Intersecção de dicionários\: Dict(T1,T2) x Dict(T1,T2) → Dict(T1,T2) // Diferença de dicionários∈: Dict(T1,T2) x T1 → BOOL // Testa se o valor do tipo T1

pertence a Dict(T1,T2)⊂: Dict(T1,T2) x Dict(T1,T2) → BOOL // Testa se o primeiro dicionário

está contido no segundo⊃: Dict(T1,T2) x Dict(T1,T2) → BOOL // Testa se o segundo dicionário

está contido no primeiro

2.3.5 Produtos cartesianos (tuplos, records, structs)

O produto cartesiano permite representar, de forma abstracta, estruturas compostas por várioselementos de tipos distintos.

Declaracao de um produto cartesiano

A declaração de uma variável de�nida com base num produto cartesiano, pressupõe a préviade�nição desse produto, da seguinte forma:

Nome_tuplo = A x B x C x ...

É agora possível de�nir uma variável, por exemplo tvar, do tipo Nome_tuplo:

VARIÁVEL tvar :Nome_tuplo

Paulo Matos

Page 37: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

18 Capıtulo 2. Notacao algorıtmica

Operadores dos Tuplos

Como a descrição dos operadores de um produto cartesiano é dependente do produto de�nido,utilizar-se-á um exemplo para demonstrar que tipo de operadores se podem de�nir e como é queestes devem ser de�nidos.

Arvbin = raiz x esq x dirraiz : DATAesq, dir : Arvbin

De�nindo os operadores para o produto cartesiano:

new: → ArvBin // Cria um elemento vazioraiz: Arvbin → DATA // Devolve o valor do campo raizgetEsq: Arvbin → Arvbin // Devolve o valor do campo esqsetEsq: Arvbin x Arvbin → Arvbin // Atribui um valor ao campo esqgetDir: Arvbin → Arvbin // Devolve o valor do campo dirsetDir: Arvbin x Arvbin → Arvbin // Atribui um valor ao campo dirmkarvbin: DATA x Arvbin x Arvbin → Arvbin // Cria e inicializa um elemento

NOTA: Por vezes o acesso aos componentes das estruturas através dos operadores, poderá tornaras expressões algo complicadas, daí a utilizar-se sempre que tal se justi�que o acesso directo aoselementos, por exemplo:

VAR a, b, c : Arvbina.esq = bc.dir = a

2.3.6 Stack’s (Pilhas)

As stack 's, também designadas por pilhas, são estruturas de dados abstractas que permitemguardar elementos do mesmo tipo. O que caracteriza a stack, é o seu modo de funcionamento queobedece a uma regra do tipo FILO (First In Last Out), ou LIFO (Last In First Out), isto é, oselementos são inseridos na stack por uma determinada ordem e só podem ser removidos da stackpela ordem inversa. A adaptação para português do termo stack, que é pilha, é particularmentefeliz dado que simboliza perfeitamente o funcionamento desta estrutura. Os dados entram nastack e são empilhados uns por cima dos outros (segundo a ordem pela qual são inseridos). Aremoção de um dado elemento, pressupõe que primeiro se faça a remoção de todos os elementosque foram inseridos posteriormente. A representação de uma stack encontra-se na Figura 2.3.

Declaracao de Stack’s

A declaração de uma variável tipo Stack de inteiros, com a designação stackA, faz-se daseguinte forma:

Paulo Matos

Page 38: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

2.3. Representacao das estruturas de dados 19

Figura 2.3: Representacao de uma stack.

VARIÁVEL stackA :Stack(INTEIRO)

Operadores da Stack

new: → Stack(DATA) // Cria uma stack vaziafree: Stack(DATA) → ∅ // Destroi a stacktop: Stack(DATA) → DATA // Devolve o elemento

do topo da stackpush: Stack(DATA) x DATA → Stack(DATA) // Coloca DATA

no topo da stackpop: INOUT: Stack(DATA) → DATA // Retira o elemento

que está no topo

2.3.7 Queue’s (Filas)

As queue's, também designadas por �las, são estruturas de dados abstractas que permitemguardar elementos do mesmo tipo, mas segundo uma organização do tipo FIFO (First In FirstOut), LILO (Last In Last Out). O que signi�ca que o primeiro elemento inserido na queueserá também o primeiro a ser removido, o que é o funcionamento próprio de uma �la (dai autilização deste termo na tradução para português). A representação de uma queue encontra-sena Figura 2.4.

Figura 2.4: Representacao de uma queue.

Declaracao de Queue

Para declarar uma variável do tipo Queue de inteiros, com a designação queueA, à que procederda seguinte forma:

VARIÁVEL queueA :Queue(INTEIRO)

Paulo Matos

Page 39: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

20 Capıtulo 2. Notacao algorıtmica

Operadores da Queue

new: → Queue(DATA) // Cria uma queue vaziafree: Queue(DATA) → ∅ // Destroi a queuefrente: Queue(DATA) → DATA // Retorna o elemento

da frenteqinsert: Queue(DATA) x DATA → Queue(DATA) // Coloca DATA na queueqremove: INOUT: Queue(DATA) → DATA // Retira o elemento

da frente

Paulo Matos

Page 40: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

Capıtulo 3Programacao

Indice

3.1 Do algoritmo ao programa . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21

3.2 Optimizacoes de codigo: Dicas e truques . . . . . . . . . . . . . . . . . . . 23

3.3 Optimizacoes de codigo: Dicas e truques para C++ . . . . . . . . . . . . 26

3.1 Do algoritmo ao programa

A representação algorítmica visa descrever aquilo que é a ideia base de uma determinadasolução. Como não se prende com detalhes próprios da implementação, nomeadamente da lin-guagem de programação, e como pode fazer uso de conceitos e entidades mais abstractas, resultanuma representação que é normalmente mais compacta, genérica, abrangente e facilmente inter-pretável. Isto não signi�ca no entanto que é mais importante ou que substitui a implementação,aliás a qualidade do resultado, isto é do programa, passa em grande parte pela forma como seimplementam os algoritmos e as estruturas abstractas de dados.

Paulo Matos 21

Page 41: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

22 Capıtulo 3. Programacao

Neste capítulo e fazendo uso da linguagem de programação C, mostra-se como é que asoperações descritas através da notação algorítmica devem ou podem ser convertidas em código.O algoritmo da Figura 3.1 servirá como exemplo.

(1) ALGORITMO Ordenar sequência(2) VAR i, j, n :INT(3) VAR lista :Seq(INT)(4) INÍCIO(5) LER(n)(5) ... // Preencher sequência(6) PARA i ← n-1 ATÉ 1 SALTO -1 FAZER(7) PARA j ← 1 ATÉ i FAZER(8) SE lista.get(j)>lista.get(j+1) ENTÃO(9) lista.swap(j,j+1)(10) FSE(11) FPARA(12) FPARA(13) FIM

Figura 3.1: Algoritmo exemplo.

A primeira questão que se coloca está directamente relacionada com a designação do algoritmo,que é formada por duas palavras. Em termos de implementação, a maior parte das linguagensnão admite identi�cadores formados por várias palavras. O mesmo será dizer que o identi�cadornão deve conter caracteres de espaço e tabulação (entre outros). Dai a ser necessário convertera designação "Ordenar sequência" em algo do tipo "Ordenar_sequencia" (sem qualquer tipo deacentuação). Mas no caso da linguagem C/C++, há ainda o problema de que a função principaltem que necessariamente designar-se por "main". É possível utilizar a designação do algoritmo,mas apenas se este for implementado como uma função "convencional" da linguagem C.

A linha 2 do algoritmo declara três variáveis do tipo INT, o que é facilmente convertidopara linguagem C/C++, utilizando o tipo primitivo int , conforme está representado na linha9 da Figura 3.2. Já a variável declarada na linha três do algoritmo (lista), dado que é um tipoabstracto de dados pode ser implementada de diversas formas. É possível utilizar estruturas ousoluções mais complexas, como é o caso das listas ligadas (ver Secção 4.1), ou um array alocadodinamicamente. Mas a solução mais simples, é de�nir esta variável como um array de inteiros,como está representado na linha 10 da Figura 3.2.

A operação LER(n) é convertida nas instruções 11 e 12 da Figura 3.2; as operações cíclicasdo tipo PARA são convertidas em instruções for(...;...;...). De notar no entanto que houve anecessidade de alterar os valores das variáveis associadas aos ciclos. Isto, por um lado, porqueno algoritmo considerou-se que a primeira posição da sequência é a um, dado no entanto quea sequência foi implementada sob a forma de um array, a posição inicial efectiva é a zero. Poroutro lado, as próprias condições utilizadas nas instruções for(...;...;...), requerem alguns ajustesdas variáveis. A operação SE é convertida na instrução if(...).

A função swap(...) é de�nida em termos algoritmos como fazendo parte da própria de�niçãoda sequência (ver Secção 2.3.3). No entanto, tal conceito não pode ser directamente adaptadopara a linguagem C, uma vez que esta linguagem não possui o conceito de objecto. É assimnecessário de�nir uma função que implemente o swap(...). Em termos de implementação, a formade associar a sequência, isto é o array, à função swap(...), consiste em fazer com que a função

Paulo Matos

Page 42: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

3.2. Optimizacoes de codigo: Dicas e truques 23

(1) void swap(int l[],int i, int j){(2) int t;(3) t = l[i];(4) l[i] = l[j];(5) l[j] = t;(6) }(7)

(8) void main(){(9) int i, j, n;(10) int lista[NELEM];(11) printf("N. elementos da sequência?\n");(12) scanf("%d",&n);(13) ... // Preenchimento de lista(14) for(i=n-1;i>0;i�-)(15) for(j=0;j<i;j++)(16) if(lista[j]>lista[j+1]){(17) swap(lista,j,j+1);(18) }(19) }

Figura 3.2: Implementação do algoritmo da Figura 3.1.

aceite a sequência como parâmetro, conforme está representado na Figura 3.2. Se no entanto oalgoritmo for codi�cado em C++, é perfeitamente possível criar um objecto que corresponda àsequência e incluir nesse objecto o método swap(...). Neste último caso não é necessário passara sequência como parâmetro para o método, como aliás está ilustrado na Figura 3.3.

(1) class Sequencia{(2) private:(3) int lista[NELEM];(4) public:(5) Sequencia();(6) virtual Sequencia();(7) void swap(int,int);(8) ...(9) }

Figura 3.3: Implementação da sequência através de um objecto em C++.

3.2 Optimizacoes de codigo: Dicas e truques

Uma boa solução em termos de programação não depende só do algoritmo desenvolvido,ou das estruturas de dados escolhidos para a implementação do problema. É necessário saberdistinguir uma boa tradução da linguagem algorítmica para linguagem de programação, de umamá tradução.

Paulo Matos

Page 43: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

24 Capıtulo 3. Programacao

Pretende-se neste capítulo apresentar algumas técnicas de boa programação e prestar algunsavisos para erros e maus hábitos de programação que são vulgarmente cometidos. De salientarno entanto que não são os conhecimentos aqui transmitidos que fazem um bom programador,mas sim os conhecimentos acumulados através da experiência.

Simplificacao Algebrica de Expressoes

A simpli�cação de determinadas expressões através das suas propriedades algébricas permite,por vezes, dar indicações ao compilador de melhores soluções para a geração do código �nal(binário).

i + 0 = 0 + i = i - 0 = i0 - i = -ii * 1 = 1 * i = i / 1 = ii * 0 = 0 * i = 0- (-i) = ii + (-j) = i - jb v T = T v b = Tb v F = F v b = bf << 0 = f >> 0 = ff << w = f >> w = f, Se bitlen(f) = w / n , n Ni ^ 2 = i * i2 * i = i + ii * 5 => t1 = i << 2;

t2 = t1 + i;i * 7 => t1 = i << 3;

t2 = t1 - i;

Propriedade distributiva dos operadores

Simpli�cações algébricas com base nas propriedades associativa, comutativa e distributiva,com o objectivo de compor/decompor expressões, simpli�cando as suas componentes.

(i-j) + (i-j) + (i-j) + (i-j) = 4*i - 4*j = 4 * (i-j)

Expressoes equivalentes

Supressão de expressões quando existem expressões equivalentes entre si.

Ler(i)j ← i +1k ← il ← k +1

Paulo Matos

Page 44: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

3.2. Optimizacoes de codigo: Dicas e truques 25

Propagacao de copias

Em expressões tipo x ← y, substituir as utilizações posteriores de x por y.

x ← yr ← x * 4 +2 r ← y * 4 + 2s ← y+5*x s ← y + 5 * y s ← 6*y

Propagacao de constantes

Quando ocorre uma atribuição do tipo x← constante, substituir as utilizações posteriores dex pelo valor da constante.

a ← 3b ← 4 * a b ← 12

Eliminacao de expressoes comuns

Consiste em detectar sub-expressões comuns e realizar o calculo, separadamente, de forma areutilizar o resultado.

p ← i * 4x ← a + i * 4 x ← a + py ← (i * 4)/3 y ← p / 3

Colocar expressoes comuns em evidencia

Determinar expressões que são comuns entre os possíveis caminhos de execução e colocá-lasem evidência.

(1) SE a > 0 ENTÃO SE a > 0 ENTÃO(2) a ← a - 1 a ← a - 1(3) b ← a - c SENÃO(4) SENÃO a ← a + 1(5) a ← a + 1 FSE(6) b ← a - c b ← a - c(7) SE

Paulo Matos

Page 45: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

26 Capıtulo 3. Programacao

Remocao de expressoes constantes de dentro de ciclos

Consiste em remover as expressões que se encontram dentro de estruturas de controlo cíclicase que são constantes ao longo da execução de todo o ciclo, colocando-as fora do mesmo.

(1) ENQ b > 0 FAZER c ← x + z(2) c ← x + z ENQ b > 0 FAZER(3) ... ...(4) [Ciclos sem atribuições às variáveis x e z ](5) FENQ FENQ

Reordenacao do encadeamento de estruturas

Quando existem estruturas de controlo encadeadas, é por vezes possível inverter a sua ordem,de forma a minimizar o tempo de execução.

(1) PARA i ← 1 ATÉ 100 FAZER SE x > 0 ENTÃO(1) SE x > 0 ENTÃO PARA i ← 1 ATÉ 100 FAZER(2) a(i) ← a(i) + 1 a(i) ← a(i) + 1(3) SENÃO FPARA(4) a(i) ← a(i) - 1 SENÃO(5) FSE PARA i ← 1 ATÉ 100 FAZER(6) FPARA a(i) ← a(i) - 1(7) FPARA(8) FSE

Substituicao de estruturas condicionais por estruturas de atribuicao

Consiste em substituir estruturas condicionais do tipo SE ... ENTÃO ... SENÃO ..., porinstruções simples.

(1) SE a > b ENTÃO t1 ← a > b(2) c ← a c ← (t1) a(3) SENÃO c ← (!t1) b(4) c ← b(5) FSE

3.3 Optimizacoes de codigo: Dicas e truques para C++

Esta secção foi elaborada tendo por base o documento �C++-Tips-n-Traps� da autoria doProf. Douglas C. Schmidt do Departamento de EECS da Universidade de Vanderbilt.

Paulo Matos

Page 46: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

3.3. Optimizacoes de codigo: Dicas e truques para C++ 27

Utilizacao do const (em detrimento do #define)

A de�nição de constante com recurso ao const, ao contrário do #de�ne , resulta em identi�-cadores tipados e com scope, o que permite evitar situações potencialmente graves, como ilustrao seguinte exemplo:

(1) void fx(){ void fx(){(2) #define PI 3.1415 const double PI=3.1415;(3) ... ...(4) } }(5) double gy(){ double gy(){(6) ... ...(7) return PI*...; return PI*...; // Erro potencial(8) } }

Utilizacao do inline em detrimento do #define

As macros do C, como o #de�ne, são bastante poderosas, no entanto a sua utilização porvezes pode produzir resultados inesperados, como aliás ilustra o seguinte exemplo:

(1) #define max(a,b) ((a)>=(b))?(a):(b)(2) max(x++,y++);

De notar que, a expansão da macro da linha (2) resulta na seguinte expressão: (x + +) >=(y++)?x++ : y++. Como tal, as variáveis x e y são incrementadas duas vezes, quando seria deesperar que tal operação ocorresse apenas uma única vez para cada uma das variáveis. Para estetipo de situação, em que a macro é utilizada para de�nir uma pequena função, deve-se utilizar oinline em detrimento do #de�ne. Isto porque é igualmente e�ciente e dado que não há expansãode macros, os efeitos indesejados são mais facilmente detectados.

(1) inline int max(int a,int b)return a>=b?a:b;

É inclusive possível generalizar a utilização através de templates, como ilustra o seguinteexemplo:

(1) template <class T>(2) inline int max(T a,T b)return a>=b?a:b;

Distincao entre o int e o unsigned int

Ao contrário do C, o C++ faz distinção entre o tipo int e o tipo unsigned int, o que poderápor vezes produzir resultados �inesperado�. Vejamos o seguinte exemplo:

Paulo Matos

Page 47: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

28 Capıtulo 3. Programacao

(1) #include <iostream>(2) inline void f(int){cout<<"Invocação de f(int)"<<endl;}(3) inline void f(unsigned){cout<<"Invocação de f(unsigned)"<<endl;}(4) int main(){(5) f(1); // Invoca f(int)(6) f(1U); // Invoca f(unsigned)(7) }

Passagem de parametros por referencia em vez de por endereco

A passagem de parâmetros por referência é tão ou mais e�ciente que a passagem de parâmetrospor endereço e é mais segura.

(1) // Exemplo de passagem por endereço(2) void setSize(unsigned *h,unsigned *w){...}(3) // ...(4) unsigned height, width;(5) // ...(6) setSize(&height,&width);

(1) // Exemplo de passagem por referência(2) void setSize(unsigned &h,unsigned &w){...}(3) // ...(4) unsigned height, width;(5) // ...(6) setSize(height,width);

Utilizacao do const

A utilização do const permite passar parâmetros como constantes e precaver contra alteraçõesacidentais dos valores das �variáveis�.

(1) template <class K>(2) void Node<K>::setKey(const K &c){ch=c;}

Fazer uso do overloading de funcoes

É de todo recomendado fazer uso de overloading de funções quando estas apenas diferem notipo e número dos parâmetros.

(1) // Em C // C++(2) int abs(int x){...} int abs(int x){...}

Paulo Matos

Page 48: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

3.3. Optimizacoes de codigo: Dicas e truques para C++ 29

(3) double fabs(double x){...} double abs(double x){...}(4) long labs(long x){...} long abs(long x){...}

Utilizacao do new e do delete

O new e o delete devem ser utilizados em detrimento do malloc(...) e do free(...), pois nãosó garantem a invocação do construtor e do destrutor, como são mais seguros. De notar que nomalloc(...) apenas se indica o número de bytes a alocar, enquanto no new indica-se o tipo e aquantidade de elementos a alocar. Esta informação é essencial para que o processo de libertar amemória seja correctamente executado.

(1) // Implementação em C // Implementação em C++(2) int size=100; const int size = 100;(3) int *ipa=malloc(size); int *ipa = new int[size];(4) ... ...(5) free(ipa); delete [] ipa;

Utilizacao do operador de << e de >>

Os operadores de entrada >> e saída << devem ser utilizados em detrimento dos printf(...) edo scanf(...), dado que são �tipados� e podem ser rede�nidos para cada classe.

(1) // Implementação em C // Implementação em C++(2) float x; float x;(3) scanf("%f",&x); cin >> x;(4) printf("A resposta é:%f\n",x); cout << "A resposta é:"<< x << endl;(5) fprintf(stderr,"Comando inválido\n"); cerr << "Comando inválido\n";

Utilizar metodos virtuais em detrimento de switch’s

É relativamente comum que se cometa o erro de utilizar estruturas condicionais, nomeada-mente o switch para selecionar as operações a efectuar, quando em C++ tais problemas devemser resolvidos fazendo uso de métodos virtuais e do overloading. Este tipo de problema é ilustradoatravés do seguinte exemplo:

(1) // Implementação em C(2) enum Tfigura{TRIANGULO,RECTANGULO,CIRCULO};(3) struct Triangulo{float x1,y1,x2,y2,x3,y3;};(4) struct Rectangulo{float x1,y1,x2,y2;};(5) struct Circulo{float x,y,r;};(6) struct Figura{(7) enum Tfigura tipo;

Paulo Matos

Page 49: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

30 Capıtulo 3. Programacao

(8) union {(9) struct Triangulo t;(10) struct Rectangulo r;(11) struct Circulo c;(12) } u;(13) };(14) float area(Figura *f){(15) float res=0.0F;(16) switch(f->tipo){(17) case TRIANGULO:{(18) struct Triangulo *t=&(f->u.t);(19) res=fabs((t->x1*t->y2-t->x2*t->y1)+(20) (t->x3*t->y3-t->x3*t->y2)(21) +(t->x3*t->y1-t->x1*t->y3))/2;(22) break;(23) }(24) case RECTANGULO:{(25) Rectangulo *r=&(f->u.r);(26) res=fabs((r->x1- r->x2)*( r->y1- r->y2));(27) break;(28) }(29) case CIRCULO:{(30) Circulo *c=&(f->u.c);(31) res=3.1415* c->r*c->r;(32) break;(33) }(34) default:fprintf(stderr,"Figura inválida\n");(35) }(36) return res;(37) }

Fazendo uso das potencialidades da linguagem C++, o mesmo problema deveria ser resolvidoda seguinte forma:

(1) // Implementação em C++(2) class Figura{(3) public:(4) Figura(){}(5) virtual float area(void)const=0;(6) };(7) class Triangulo:public Figura{(8) public:(9) Triangulo(float a1,float a2,float a3,float b1,float b2,float b3){(10) x1(a1),x2(a2),x3(a3),y1(b1),y2(b2),y3(b3)}(11) float area(void)const{(12) return fabs((x1*y2-x2*y1)+(x3*y3-x3*y2)+(x3*y1-x1*y3))/2;}(13) private:(14) float x1,x2,x3,y1,y2,y3;(15) };

Paulo Matos

Page 50: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

3.3. Optimizacoes de codigo: Dicas e truques para C++ 31

(16) class Rectangulo:public Figura{(17) public:(18) Rectangulo(float a1,float a2,float b1,float b2):(19) x1(a1),x2(a2),y1(b1),y2(b2){}(20) float area(void)const{return fabs((x1-x2)*(y1-y2));}(21) private:(22) float x1,x2,y1,y2;(23) };(24) class Circulo:public Figura{(25) public:(26) Circulo(float a,float b,float c):x(a),y(b),r(c){}(27) float area(void)const{return 3.1415*r*r;}(28) private:(29) float x,y,r;(30) };

Utilizacao de variaveis estaticas e definicao de enumeracoes dentro de classes

Sempre que possível as variáveis devem ser de�nidas no âmbito de uma classe, evitando-seassim a utilização de variáveis externas (globais). Sempre que seja possível, deve-se tambémde�nir as enumerações e outros tipos de variáveis dentro de classes. Ambas recomendações,permitem reduzir o �scope� das variáveis e das enumerações, evitando potenciais con�itos deutilização. O seguinte exemplo ilustra esta situação:

(1) enum Cor{AZUL,VERDE,PRETO}; #include <iostream>(2) enum Cor cr=AZUL; class MyLib{(3) public:(4) int main(){ enum Cor{AZUL,VERDE,PRETO};(5) int cr=10; static Cor cr;(6) cr=VERDE; };(7) } MyLib::Cor MyLib::cr=MyLib::AZUL;(8)

(9) int main(){(10) int cr=10;(11) MyLib::cr=MyLib::VERDE;(12) }

Inicializacao e atribuicao

O C++ �esconde� por vezes muitas operações que podem ser evitadas, no sentido de tornara execução do código mais célere. O seguinte exemplo ajuda a explicar este tipo de situação:

(1) class String{(2) public:(3) String();

Paulo Matos

Page 51: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

32 Capıtulo 3. Programacao

(4) String(const char*);(5) String(const String&);(6) String &operator=(const String&);(7) private:(8) int len;(9) char *data;(10) };(11)

(12) class Nome{(13) public:(14) Nome(const char *t){s=t;}(15) // public: Nome(const char *t):s(t){}(16) private:(17) String s;(18) };(19)

(20) int main(){Nome gato="Fifi";}

Se não for de�nido o construtor da linha 15, a instanciação da variável gato vai implicar aexecução dos seguintes procedimentos:

• Instanciação de um objecto do tipoNome utilizando o construtor por defeito (Nome::Nome());

• Instanciação de um objecto do tipo String utilizando o construtor por defeito (String::String()),o que implica a inicialização das variáveis String::len e String::data (esta última poderárequer alocação de memória e inicialização com "\0");

• Utilizar o construtor por defeito CONST CHAR* para processar a string "Fi�", o queimplica a alocação de espaço para uma variável temporária e respectiva cópia da string;

• Utilização do operador de atribuição (String::operator=(...)), o que implica libertar o es-paço alocado previamente para String::data, alocar um novo espaço e efectuar a cópia dastring. É ainda necessário libertar o espaço alocado para a variável temporária.

O que perfaz um total de três operações de alocação, duas de cópia de strings e duas operaçõespara libertar memória.

A utilização do construtor de�nido na linha 15 do exemplo anterior (Nome(const char *t):s(t){}),permite optimizar este procedimento. Dado que nestas circunstâncias é este o construtor utilizadopara criar o objecto do tipo Nome, o qual tem condições para inicializar de imediato a variávelNome::s. Consegue-se assim atingir os mesmos objectivos fazendo apenas uso de uma operaçãode alocação e de uma operação de cópia, preconizada pelo construtor String::String(const char*).

Utilizacao de parentesis na instanciacao de objectos

Um erro que é comum ao codi�car algoritmos em C++, é instanciar objectos utilizando oconstrutor por defeito, fazendo uso dos parêntesis.

Paulo Matos

Page 52: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

3.3. Optimizacoes de codigo: Dicas e truques para C++ 33

(1) class Teste{(2) public:(3) Teste(void);(4) int get(void);(5) };(6) int main(){(7) Teste f; // Cria um objecto do tipo Teste(8) Teste ff(); // Declara uma função que devolve Teste(9) f.get(); // Possível e correcto(10) f.get; // Possível, mas provavelmente incorrecto ?!?!?(11) ff.get(); // Erro(12) }

Utilizacao de parametros por omissao com metodos virtuais

A utilização de parâmetros por omissão, com métodos virtuais, pode por vezes produzirresultados �inesperados�, senão vejamos o seguinte exemplo:

(1) class Base{(2) public:(3) virtual void f(char *n="Base"){cout << "Class:" << n << endl;}(4) };(5)

(6) class Derivada:public Base{(7) public:(8) virtual void f(char *n="Derivada"){cout << "Class:" << n << endl;}(9) };(10)

(11) int main(){(12) Derivada *dc=new Derivada;(13) dc->f(); // Imprime: Class: Derivada(14) Base *bc=dc;(15) bc->f(); // Imprime: Class: Base(16) return 0;(17) }

Atencao aos espacos

É preciso dar especial atenção onde se deve, ou não, inserir espaços. Operadores formadospor dois símbolos não devem conter espaços entre esses símbolos, como se pode observar peloseguinte exemplo ∗ = é diferente de ∗ =. De notar que na linha 2, deve existir espaço entre osímbolo ∗ e o símbolo =, enquanto na linha 9 tal espaço não deverá existir.

(1) char *x="My name";(2) int foo(char * = x){

Paulo Matos

Page 53: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

34 Capıtulo 3. Programacao

(3) cout << "X:"<< x << endl;(4) return 1;(5) }(6)

(7) int main(){(8) int a = 10, b;(9) b *= foo();(10) return 0;(11) }

A não inserção de espaços poderá inclusive levar a resultados �inesperados�, por exemplo:

(1) int a=10;(2) int b = a //* dividido por 4 */ 4;(3) -a;(4) cout << "a:"<< a << "b:"<< b << endl;

Ao contrário do que eventualmente se poderia esperar, o código anterior não só é compilável,como quando devidamente contextualizado é exequível. O resultado produzido é: a : 10 b : 0.

Casos há que no entanto levam a erros de compilação, por exemplo:

(1) template <class K1,class K2,class INFO>(2) class TwoKeyDict : Dict<K1,Dict<K2,INFO>>{(3) ...(4) };

Este exemplo não é compilável dado que o compilador interpreta os símbolos >> (linha 2)como sendo o operador de deslocamento para a direita. O erro é facilmente contornável, colocandoum espaço entre os símbolos.

Existem muitas outras técnicas para optimizar código, muitas das quais típicas das própriaslinguagens. As aqui apresentadas são apenas uma amostra daquelas que são mais vulgares e quepodem mais facilmente ser utilizadas pelos compiladores e interpretadores de código, ou mesmopelos próprios programadores.

Paulo Matos

Page 54: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

Capıtulo 4Estruturas de Dados Lineares

Indice

4.1 Listas ligadas simples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36

4.1.1 Pesquisa de elementos numa lista simples . . . . . . . . . . . . . . . . . . . . 37

4.1.2 Insercao de elementos numa lista simples . . . . . . . . . . . . . . . . . . . . 38

4.1.3 Remocao de elementos numa lista simples . . . . . . . . . . . . . . . . . . . . 46

4.1.4 Listagem dos elementos de uma lista simples . . . . . . . . . . . . . . . . . . 50

4.2 Listas duplamente ligadas . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51

4.2.1 Pesquisa de elementos numa lista duplamente ligada . . . . . . . . . . . . . . 51

4.2.2 Insercao de elementos numa lista duplamente ligada . . . . . . . . . . . . . . 52

4.2.3 Remocao de elementos numa lista duplamente ligada . . . . . . . . . . . . . . 60

4.2.4 Listagem dos elementos de uma lista duplamente ligada . . . . . . . . . . . . 63

4.3 Stack’s . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65

4.3.1 Stack’s estaticas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66

4.3.2 Stack’s dinamicas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69

4.3.3 Stack’s Estaticas x Stack’s Dinamicas . . . . . . . . . . . . . . . . . . . . . . 73

Paulo Matos 35

Page 55: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

36 Capıtulo 4. Estruturas de Dados Lineares

4.4 Queue’s . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75

4.4.1 Queue’s estaticas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76

4.4.2 Queue’s dinamicas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81

4.5 Tabelas de hash . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87

4.5.1 Funcoes de hash . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89

4.5.2 Tratamento de colisoes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91

4.5.3 Linear Probing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91

4.5.4 Listas de colisoes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95

Este capítulo visa apresentar algumas estruturas abstractas de dados, do tipo linear, imple-mentadas maioritariamente com recurso a alocação dinâmica de memória. Pretende-se abordarem termos formais e práticos, estruturas como listas ligadas, stack's, queue's e tabelas de hash.

4.1 Listas ligadas simples

As listas ligadas simples são uma das estruturas abstractas de dados mais conhecidas e uti-lizadas. Talvez porque sejam a alternativa conceptualmente mais próxima dos arrays, que jápermite construir soluções e�cientes (em termos de alocação de memória), e cuja estrutura efuncionamento é relativamente simples.

Uma lista ligada é uma estrutura dinâmica composta por um conjunto de nodos, ou elementos,que se encontram interligados entre si, de forma sequencial. No caso concreto de uma listaligada simples, cada nodo encontra-se apenas ligado ao seu sucessor. Na Figura 4.1 encontra-serepresentada uma lista ligada simples.

Figura 4.1: Exemplo de uma lista ligada simples.

Cada nodo é composto por três partes distintas: a chave (ch), através da qual se efectua aordenação dos elementos da lista; a informação associada à chave (info); e o campo que faz aligação com o elemento seguinte da lista (next). Formalmente cada nodo, designado por NODOS(nodo simples), é de�nido da seguinte forma:

NODOS = ch x info x nextch : CHAVEinfo : INFOnext : NODOS

Para o qual se de�nem os seguintes operadores:

Paulo Matos

Page 56: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

4.1. Listas ligadas simples 37

new: → NODOS // Cria um nodo vaziofree: NODOS → ∅ // Destrói o nodogetKey: NODOS → CHAVE // Devolve o valor de chsetKey: NODOS x CHAVE → NODOS // Atribui CHAVE a chgetInfo: NODOS → INFO // Devolve o valor de infosetInfo: NODOS x INFO → NODOS // Atribui INFO a infogetNext: NODOS → NODOS // Devolve o valor de nextsetNext: NODOS1 x NODOS2 → NODOS // Atribui NODOS2 a next

Para além das operações ao nível do nodo, existem as operações ao nível da lista, como é ocaso da: pesquisa, inserção, remoção e listagem dos elementos.

4.1.1 Pesquisa de elementos numa lista simples

A solução mais simples para pesquisar um elemento numa lista (lst), cuja a chave a pesquisaré ch, consiste em percorrer toda a lista até encontrar o elemento pretendido, da seguinte forma:

(1) VAR q : NODOS(2) q ← lst(3) ENQ q 6= NULO E q.getKey() 6= ch FAZER(4) q ← q.getNext()(5) FENQ(6) SE q 6= NULO ENTÃO ...

No �m da execução do ciclo ENQUANTO (ENQ), a chave pode ter sido ou não encontrada.No primeiro caso a variável lst aponta para NULO. No segundo caso q aponta para o nodo quecontém a chave pesquisada. Numa lista ordenada é ainda possível optimizar o ciclo da seguinteforma:

(1) VAR q : NODOS(2) q ← lst(3) ENQ q 6= NULO E q.getKey() < ch FAZER(4) q ← q.getNext()(5) FENQ(6) SE q 6= NULO E q.getKey() = ch ENTÃO ...

Na primeira solução, se o elemento a pesquisar não pertencer à lista, a pesquisa só terminaquando se alcança o �m da lista. A segunda solução tira proveito do facto da lista estar ordenadae só a percorre enquanto as chaves dos nodos forem inferiores à chave que se está a pesquisar,quer esta faça ou não parte da lista.

O algoritmo completo para uma função de pesquisa é então o seguinte:

Paulo Matos

Page 57: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

38 Capıtulo 4. Estruturas de Dados Lineares

(1) PROC PesquisaLS(lst:NODOS,ch:CHAVE):INFO(2) VAR q : NODOS(3) INÍCIO(4) q ← lst(5) ENQ q 6= NULO E q.getKey()<ch FAZER(6) q ← q.getNext()(7) FENQ(8) SE q 6= NULO E q.getKey()=ch ENTÃO(9) RET q.getInfo()(10) SENÃO(11) RET ∅(12) FSE(13) FIM

NOTA: Ao nome de cada função será acrescentado um su�xo que permitirá identi�car o tipode estrutura a que a função se destina. Por exemplo, no caso anterior acrescentou-se à função osu�xo LS de Lista Simples, �cando o nome PesquisaLS(...).

4.1.2 Insercao de elementos numa lista simples

A inserção dos elementos numa lista faz-se segundo determinada estratégia, que pode consistirem inserir no início da lista, no �m da lista, de forma ordenada, ou obedecendo a uma qualqueroutra política de inserção. Os exemplos a seguir apresentados pressupõem que a inserção se fazde forma ordenada.

O processo de inserção contém três fases distintas: uma inicial, na qual se determina a posiçãoonde inserir o nodo, que utiliza uma solução semelhante à da rotina de pesquisa; uma segundafase (nem sempre obrigatória), na qual se aloca o espaço e se preenche o nodo a inserir; e umaúltima fase, em que os apontadores são redireccionados de forma a inserir o nodo na lista.

Posicionamento dos apontadores

A primeira fase, que consiste em determinar a posição na lista onde se vai inserir o novo nodo,faz-se utilizando um ou dois apontadores auxiliares. Quando se utiliza um único apontador, estedeverá �car posicionado no nodo que será, após a inserção, o antecessor do nodo a inserir. Asexcepções ocorrem quando a lista está vazia ou o nodo vai ser inserido à cabeça da lista (naprimeira posição da lista). Neste caso o apontador deverá apontar, respectivamente para NULOou para o primeiro elemento da lista.

Utilizando dois apontadores auxiliares, estes devem �car posicionados nos dois nodos entre osquais será inserido o novo nodo, um no nodo anterior e o outro no nodo posterior. Neste casotambém há que salvaguardar as situações em que a lista está vazia, quando se insere o elementoà cabeça da lista ou quando se insere no �m da lista.

Para o caso de se utilizar um único apontador auxiliar, aqui representado por q, o posiciona-mento faz-se da seguinte forma:

Paulo Matos

Page 58: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

4.1. Listas ligadas simples 39

(1) VAR q :NODOS(2) q ← lst(3) ENQ q6=NULO E q.getNext()6=NULO E (q.getNext()).getKey()<ch FAZER(4) q ← q.getNext()(5) FENQ(6) ...

A primeira sub-condição (q 6= NULO) do ciclo ENQUANTO (ENQ) serve para testar se alista está vazia; a segunda sub-condição (q.getNext() 6= NULO) serve de condição de paragempara o caso da chave a inserir ser a maior, ou a menor, de todas as chaves até ai inseridas (exceptopara o caso da lista vazia); e por �m, a terceira sub-condição controla o avanço do apontadorauxiliar até à posição onde o nodo deve ser inserido.

É no entanto possível simpli�car o algoritmo utilizando dois apontadores auxiliares, aquirepresentados por q e qant, da seguinte forma:

(1) VAR q, qant :NODOS(2) q ← qant ← lst(3) ENQ q 6= NULO E q.getKey()<ch FAZER(4) qant ← q(5) q ← q.getNext()(6) FENQ(7) ...

Para ilustrar o resto do processo de inserção será utilizada esta última solução.

Alocacao e preenchimento do nodo

Na segunda fase, a da alocação e preenchimento do novo nodo, são realizadas as seguintesoperações:

(1) VAR p :NODOS(2) p.new()(3) p.setKey(ch)(4) p.setInfo(inf)

Em que p representa o novo nodo, ch a chave a inserir e inf a informação associada a essachave.

Insercao do nodo

Na última fase, os apontadores são redireccionados segundo a solução escolhida para localizara posição do novo nodo. Em qualquer dos casos podem ocorrer quatro situações distintas, sãoelas as seguintes:

Paulo Matos

Page 59: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

40 Capıtulo 4. Estruturas de Dados Lineares

• Inserir numa lista vazia,

• Inserir no início,

• Inserir numa posição intermédia,

• Inserir no �m.

Insercao numa lista vazia

Inicialmente a lista deve estar vazia, o que signi�ca que o apontador inicial (lst) deve estara NULO, pelo que q e qant também se encontram a NULO, como se encontra ilustrado naFigura 4.2.

Figura 4.2: Apontadores apos a localizacao da posicao onde inserir o novo nodo.

Considerando p como o apontador para o nodo a inserir, então os apontadores são redirecci-onados da seguinte forma:

(1) p.setNext(NULO)(2) lst ← p

Ou então:

(1) p.setNext(lst)(2) lst ← p

Ou ainda:

(1) p.setNext(q)(2) lst ← p

A Figura 4.3 ilustra como são redireccionados os apontadores.

Falta ainda indicar quais os factores que permitem distinguir esta situação das restantes três.Para tal, pode-se tirar proveito do facto de que antes de se redireccionar os apontadores, oapontador inicial da lista (lst) aponta para NULO, o que permite utilizar a seguinte condição:

Paulo Matos

Page 60: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

4.1. Listas ligadas simples 41

Figura 4.3: Insercao numa lista vazia.

(1) SE lst=NULO ENTÃO(2) ...

No entanto, para evidenciar o maior número de instruções entre as quatro possíveis situaçõesde inserção e como se poderá comprovar através do próximo caso, é vantajoso tirar proveito dofacto de que q também aponta para NULO, pelo que nesta situação a condição de selecção �ca:

(1) SE lst=q ENTÃO(2) ...

Insercao no topo (posicao inicial da lista)

Caso a chave a inserir seja a menor das chaves até ai inseridas, deverá então �car à cabeçada lista, ou seja, passar a ser o primeiro elemento desta. Nesta situação ambos apontadoresauxiliares (q e qant) apontam para o primeiro nodo da lista, como se encontra ilustrado naFigura 4.4.

As operações a realizar para esta situação são as seguintes:

(1) p.setNext(lst)(2) lst ← p

Ou então:

(1) p.setNext(q)(2) lst ← p

Paulo Matos

Page 61: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

42 Capıtulo 4. Estruturas de Dados Lineares

Figura 4.4: Apontadores apos a localizacao da posicao onde inserir o novo nodo.

De notar que é necessário actualizar o valor de lst para que não se perca a lista. A Figura 4.5ilustra como redireccionar os apontadores.

Esta situação é reconhecida pelo facto de que q e lst apontarem ambos para a mesma posição.Tirando proveito disso e do facto das operações a realizar serem as mesmas da situação ante-rior (inserir numa lista vazia), pode-se então realizar o mesmo tipo de tratamento para ambassituações, de tal forma que:

(1) SE lst=q ENTÃO(2) p.setNext(q)(3) lst ← p(4) SENÃO(5) ...

Insercao numa posicao intermedia

Caso o nodo a inserir venha a �car entre dois outros nodos já pertencentes à lista, então q eqant apontam respectivamente para o nodo posterior e anterior à futura posição do novo nodo,como se encontra ilustrado na Figura 4.6.

As operações realizar são então as seguintes:

(1) p.setNext(q)(2) qant.setNext(p)

Os apontadores �cam redireccionados segundo o esquema da Figura 4.7.

Paulo Matos

Page 62: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

4.1. Listas ligadas simples 43

Figura 4.5: Insercao no topo da lista.

Em oposição às duas situações anteriores, esta distingue-se por q não apontar para a posiçãoinicial da lista (lst) e de não ser NULO. Mas como se poderá comprovar no algoritmo �nal esta,e a próximo situação, não são mais que o complemento das duas primeiras, pelo que não hánecessidade de apresentar qualquer critério de selecção.

Insercao no fim da lista

No caso da chave a inserir ser a maior de todas então o respectivo nodo é colocado no �m dalista. Nesta situação q aponta para NULO e qant para o último nodo da lista, como se encontrailustrado na Figura 4.8.

As operações a executar nesta situação são as seguintes:

(1) p.setNext(NULO)(2) qant.setNext(p)

Ou então:

(1) p.setNext(q)(2) qant.setNext(p)

A Figura 4.9 ilustra como redireccionar os apontadores.

Paulo Matos

Page 63: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

44 Capıtulo 4. Estruturas de Dados Lineares

Figura 4.6: Apontadores apos a localizacao da posicao onde inserir o novo nodo.

A particularidade desta situação, que a faz distinta das demais, é que o apontador q apontapara NULO, no entanto isto por si só poderia levar a con�itos com a primeira situação, onde alista encontra-se inicialmente vazia, pelo que q também aponta para NULO. Mas como a condiçãoproposta para esse caso foi de que q deve ser igual a lst não há qualquer tipo de con�ito. Existeno entanto uma solução melhor, que consiste em tratar este caso juntamente com o anterior,uma vez que as operações a realizar são exactamente as mesmas.

Elementos repetidos

Para além das quatro situações atrás descritas é ainda possível ocorrer que a chave a inserirjá exista na lista. Neste caso são várias as soluções possíveis, de entre as quais se destacam asseguintes: inserir a nova chave e respectiva informação, aceitando como tal chaves iguais paraelementos distintos; rejeitar por completo a nova chave (e informação); substituir o nodo anteriorpelo novo; o que corresponde a manter o nodo antigo substituindo apenas a informação. Seráesta última, a solução utilizada no algoritmo �nal.

Esta situação distingue-se das demais pelo facto de que a chave a inserir ser igual à chavecontida no nodo apontado por q, o que resulta na seguinte condição:

(1) SE q.getKey()=ch ENTÃO(2) ...

Solucao final

Reunindo todas as situações atrás descritas, obtém-se o seguinte algoritmo:

Paulo Matos

Page 64: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

4.1. Listas ligadas simples 45

Figura 4.7: Insercao numa posicao intermedia da lista.

(1) PROC InserirLS(lst:NODOS,ch:CHAVE,inf:INFO):NODOS(2) VAR qant, q, p :NODOS(3) INÍCIO(4) q ← lst(5) qant ← q(6) ENQ q 6= NULO E q.getKey()<ch FAZER(7) qant ← q(8) q ← q.getNext()(9) FENQ(10) SE q 6= NULO E q.getKey()=ch ENTÃO(11) q.setInfo(inf) // Actualizar a informação(12) SENÃO(13) p.new()(14) p.setKey(ch)(15) p.setInfo(inf)(16) p.setNext(q)(17) SE lst=q ENTÃO(18) lst ← p(19) SENÃO(20) qant.setNext(p)(21) FSE(22) FSE(23) RET lst(24) FIM

Paulo Matos

Page 65: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

46 Capıtulo 4. Estruturas de Dados Lineares

Figura 4.8: Apontadores apos a localizacao da posicao onde inserir o novo nodo.

4.1.3 Remocao de elementos numa lista simples

A rotina de remoção de uma lista decorre em duas fases: na primeira detecta-se o elemento aremover; na segunda procede-se à remoção propriamente dita, através do redireccionamento dosapontadores.

O elemento a remover é localizado utilizando as mesmas estratégias que foram apresentadaspara a inserção (ver Secção 4.1.2). No caso de se utilizar um só apontador auxiliar, este deve�car apontar para o nodo imediatamente anterior aquele que se pretende remover. No caso dese utilizarem dois apontadores auxiliares, então um �ca apontar para o nodo a remover (q) e ooutro para o nodo imediatamente anterior (qant).

É no entanto possível que o elemento a remover não faça parte da lista. Neste caso, não hánecessidade de remover qualquer nodo, �cando a lista inalterado. Mas se o elemento �zer parteda lista, pode então ocorrer uma das seguintes situações:

• Remoção do topo da lista;

• Remoção de uma posição intermédia da lista;

• Remoção do �m da lista.

Independentemente da situação concreta, q aponta sempre para o nodo a remover. Os seguin-tes exemplos, utilizam dois apontadores auxiliares, q e qant, para localizar o nodo a remover.

Remocao do topo

Nesta situação qant, q e o apontador inicial da lista (lst) apontam todos para primeiro ele-mento.

Paulo Matos

Page 66: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

4.1. Listas ligadas simples 47

Figura 4.9: Insercao no fim da lista.

Os apontadores são redireccionados segundo o esquema da Figura 4.10 e as operações a realizarsão as seguintes:

(1) lst ← q.getNext()(2) q.free()

Figura 4.10: Remocao no inıcio da lista.

Esta situação distingue-se das restantes pelo facto de que q e qant apontarem ambos para amesma posição e ainda por q apontar para primeiro elemento da lista (lst). Como tal, a condiçãode selecção pode ser:

(1) SE q = qant ENTÃO

Paulo Matos

Page 67: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

48 Capıtulo 4. Estruturas de Dados Lineares

(2) ...

Ou então:

(1) SE q = lst ENTÃO(2) ...

Remocao de uma posicao intermedia

No caso do nodo a remover encontrar-se numa posição intermédia, qant aponta para o nodoimediatamente anterior ao nodo a remover, como está ilustrado na Figura 4.11.

Figura 4.11: Remocao de uma posicao intermedia da lista.

As operações a executar nesta situação são então as seguintes:

(1) qant.setNext(q.getNext())(2) q.free()

Remocao do fim da lista

Caso o elemento a remover seja o último da lista, a única diferença em relação ao caso anterioré que q.getNext() é NULO, o que no entanto não afecta as operações a realizar. Esta situaçãoencontra-se ilustrada na Figura 4.12.

A condição a utilizar para distinguir as duas últimas situações em relação à primeira, podeser a seguinte:

Paulo Matos

Page 68: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

4.1. Listas ligadas simples 49

Figura 4.12: Remocao no fim da lista.

(1) SE q 6= lst ENTÃO(2) ...

Ou então, funcionar como complemento à condição colocada para o caso em que o nodo aremover é o primeiro da lista.

Solucao final

O algoritmo completo para a rotina de remoção é o seguinte:

(1) PROC RemoverLS(lst:NODOS,ch:CHAVE):NODOS(2) VAR q, qant :NODOS(3) INÍCIO(4) q ← lst(5) qant ← q(6) ENQ q 6= NULO E q.getKey()<ch FAZER(7) qant ← q(8) q ← q.getNext()(9) FENQ(10) SE q 6= NULO E q.getKey()=ch ENTÃO(11) SE q = lst ENTÃO(12) lst ← q.getNext()(13) SENÃO(14) qant.setNext(q.getNext())(15) FSE(16) q.free()(17) FSE(18) RET lst(19) FIM

Paulo Matos

Page 69: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

50 Capıtulo 4. Estruturas de Dados Lineares

4.1.4 Listagem dos elementos de uma lista simples

A listagem consiste em percorrer a lista, elemento a elemento, até se atingir o �m.

(1) PROC ListarLS(lst:NODOS)(2) VAR q :NODOS(3) INÍCIO(4) q ← lst(5) ENQ q 6= NULO FAZER(6) ... // Operações a executar aquando da listagem(7) q ← q.getNext()(8) FENQ(9) FIM

Exercıcios:

a) Descreva os algoritmos com as soluções recursivas para as funções de inserir, remover elistar de uma lista ligada simples.

b) Pretende-se fazer a gestão informática de um parque automóvel. Para tal estipula-se queum automóvel ao entrar no parque deve ser registado, armazenando a matrícula, nome doproprietário e hora de entrada. Quando o automóvel sai, é então dado baixa do mesmo.

Implemente em C++, um programa que permita fazer a gestão do parque, possibilitandoao responsável determinar quantos carros se encontram actualmente no parque e que carrossão esses. Utilize para tal uma lista ligada simples com algoritmos não recursivos.

c) Implemente o mesmo programa utilizando soluções recursivas.

d) Pede-se que implemente em linguagem C++ um dicionário de sinónimos, partindo doprincípio que cada palavra contida no dicionário possui no máximo 5 sinónimos diferentes.

Paulo Matos

Page 70: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

4.2. Listas duplamente ligadas 51

4.2 Listas duplamente ligadas

As listas duplamente ligadas acrescentam às listas simples a capacidade de percorrer a lista emambos sentidos. Para tal, existe mais um apontador por nodo, que serve para fazer a ligação como nodo anterior. A estrutura genérica de uma lista duplamente ligada encontra-se representadana Figura 4.13.

Figura 4.13: Estrutura de uma lista duplamente ligada.

Tratando-se de uma lista ordenada, é normalmente composta por uma chave (ch), algumainformação (info) e por dois apontadores, um para o elemento seguinte (next) e outro para oelemento anterior (prev). Formalmente a estrutura é de�nida da seguinte forma:

NODOD = ch x info x prev x nextch : CHAVEinfo : INFOprev, next : NODOD

De�ne-se ainda o seguinte conjunto de operadores:

new: → NODOD // Cria um nodo vaziofree: NODOD → ∅ // Destrói o nodogetKey: NODOD → CHAVE // Devolve chsetKey: NODOD x CHAVE → NODOD // Atribui CHAVE a chgetInfo: NODOD → INFO // Devolve infosetInfo: NODOD x INFO → NODOD // Atribui INFO a infogetPrev: NODOD → NODOD // Devolve prevsetPrev: NODOD1 x NODOD2 → NODOD // Atribui NODO2 a prevgetNext: NODOD → NODOD // Devolve nextsetNext: NODOD1 x NODOD2 → NODOD // Atribui NODO2 a next

À semelhança da lista simples, a lista duplamente ligada também possui funções de pesquisa,inserção, remoção e listagem.

4.2.1 Pesquisa de elementos numa lista duplamente ligada

Todas as soluções apresentadas para as listas ligadas simples funcionam nas listas duplamenteligadas. É no entanto possível implementar outras estratégias de pesquisa, aproveitando o facto

Paulo Matos

Page 71: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

52 Capıtulo 4. Estruturas de Dados Lineares

de ser possível percorrer a lista em ambos sentidos. A título de exemplo, pode-se utilizar umapontador auxiliar que assinale a posição intermédia da lista. Esta estratégia permite com umasimples comparação inicial reduzir o espaço de pesquisa a metade, aumentando assim a e�ciênciado algoritmo. Apresenta-se de seguida a representação algorítmica para esta situação, onde mrepresenta o apontador para o nodo que se encontra a meio da lista:

(1) SE m 6= NULO E m.getKey()<ch ENTÃO(2) ENQ m 6= NULO E m.getKey()<ch FAZER(3) m ← m.getNext()(4) FENQ(5) SENÃO(6) ENQ m 6= NULO E m.getKey()>ch FAZER(7) m ← m.getPrev()(8) FENQ(9) FSE(10) SE m 6= NULO E m.getKey()=ch ENTÃO(11) ...

Muitas outras estratégias tornam-se possíveis de implementar com a utilização de listas du-plamente ligadas. Uma das principais vantagens, em relação às listas simples, está na facilidadecom que é possível implementar as mais diversas rotinas (inserir, remover, etc) utilizando umsó apontador auxiliar, uma vez que a partir de um dado nodo é sempre possível aceder ao seusucessor e antecessor. O algoritmo de pesquisa �ca assim resumido às seguintes instruções (listaordenada):

(1) PROC PesquisaLD(lst:NODOD,ch:CHAVE):INFO(2) VAR q :NODOD(3) INÍCIO(4) q ← lst(5) ENQ q 6= NULO E q.getKey()<ch FAZER(6) q ← q.getNext()(7) FENQ(8) SE q 6= NULO E q.getKey()=ch ENTÃO(9) RET q.getInfo()(10) SENÃO(11) RET ∅(12) FSE(13) FIM

4.2.2 Insercao de elementos numa lista duplamente ligada

É possível utilizar exactamente os mesmos algoritmos de inserção que foram utilizados paraas listas simples, mas tal signi�ca desprezar as potencialidades de uma lista duplamente ligada.À semelhança das listas simples, a inserção nas listas duplamente ligadas também é feita em trêsfases: na primeira, localiza-se a posição na lista onde o novo nodo deve ser inserido; na segunda,cria-se e preenche-se o novo nodo; e por �m, na terceira fase os apontadores são redireccionados.

Paulo Matos

Page 72: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

4.2. Listas duplamente ligadas 53

Para se encontrar a posição onde inserir o novo nodo utiliza-se um algoritmo semelhante aoapresentado para a pesquisa. É no entanto acrescentando uma terceira sub-condição ao ciclo deforma a contemplar a situação em que a chave a inserir é a maior de todas até ai inseridas, poiscaso contrário o apontador auxiliar (q) tomaria valor de NULO não permitindo assim a correctalocalização.

(1) VAR q :NODOD(2) q ← lst(3) ENQ q 6= NULO E q.getKey()<ch E q.getNext() 6= NULO ENTÃO(4) q ← q.getNext()(5) FENQ

Pressupondo que a chave a inserir é representada por ch e a informação por inf , então nasegunda fase realizam-se as seguintes operações:

(1) VAR p :NODOD(2) p.new()(3) p.setKey(ch)(4) p.setInfo(inf)

Após o término da segunda fase, pode ocorrer uma das seguintes situações:

• Inserir numa lista vazia;

• Inserir no topo;

• Inserir numa posição intermédia;

• Inserir no �m.

Insercao numa lista vazia

Nesta situação o apontador inicial da lista (lst) e o apontador auxiliar (q) apontam ambospara NULO, como se encontra ilustrado na Figura 4.14.

Figura 4.14: Posicao dos apontadores antes da insercao numa lista vazia.

Pressupondo que p é o apontador para o nodo a inserir, então as operações a realizar encontram-se ilustradas na Figura 4.15 e são as seguintes:

Paulo Matos

Page 73: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

54 Capıtulo 4. Estruturas de Dados Lineares

(1) p.setNext(NULO)(2) p.setPrev(NULO)(3) lst ← p

Ou então:

(1) p.setNext(lst)(2) p.setPrev(lst)(3) lst ← p

Ou ainda:

(1) p.setNext(q)(2) p.setPrev(q)(3) lst ← p

Figura 4.15: Insercao numa lista vazia.

Esta situação distingue-se das demais por ter lst e q a apontarem simultaneamente paraNULO (antes de se redireccionar os apontadores), o que corresponde à seguinte condição:

(1) SE lst = NULO ENTÃO(2) ...

Ou em alternativa:

(1) SE q = NULO ENTÃO(2) ...

Paulo Matos

Page 74: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

4.2. Listas duplamente ligadas 55

Insercao na posicao inicial da lista

Nesta situação o apontador auxiliar (q) �ca apontar para o primeiro elemento da lista, que épara onde também aponta lst, como se encontra ilustrado na Figura 4.16.

Figura 4.16: Posicao dos apontadores na insercao no topo da lista.

As operações a executar são as seguintes:

(1) p.setNext(q)(2) p.setPrev(NULO)(3) q.setPrev(p)(4) lst ← p

Ou então:

(1) p.setNext(q)(2) p.setPrev(q.getPrev())(3) q.setPrev(p)(4) lst ← p

Os apontadores são redireccionados segundo o esquema da Figura 4.17.

Esta situação distingue-se das demais por ter q igual a lst e ambos apontadores diferentes deNULO, pelo que a condição de selecção pode ser:

(1) SE q = lst E q 6= NULO ENTÃO(2) ...

É no entanto possível juntar esta situação à seguinte, onde a inserção é feita numa posiçãointermédia da lista, como adiante se poderá ver.

Paulo Matos

Page 75: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

56 Capıtulo 4. Estruturas de Dados Lineares

Figura 4.17: Insercao no topo da lista.

Insercao numa posicao intermedia

Se o nodo for para inserir numa posição intermédia, então o apontador auxiliar �ca posicionadono nodo com a chave imediatamente superior à chave a inserir, como se encontra ilustrado naFigura 4.18.

Figura 4.18: Posicao dos apontadores ao inserir numa posicao intermedia da lista.

Os apontadores são redireccionados segundo o esquema da Figura 4.19, o que corresponde àsseguintes operações:

(1) p.setNext(q)(2) p.setPrev(q.getPrev())(3) (q.getPrev()).setNext(p)(4) q.setPrev(p)

Ou ainda:

Paulo Matos

Page 76: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

4.2. Listas duplamente ligadas 57

(1) p.setNext(q)(2) p.setPrev(q.getPrev())(3) (p.getPrev()).setNext(p)(4) q.setPrev(p)

Figura 4.19: Insercao numa posicao intermedia da lista.

De notar que neste último caso, é possível trocar a ordem das duas últimas operações, o queserá importante para associar a identi�cação deste caso com o anterior, como mais adiante severá.

Esta situação, à semelhança da anterior, distingue-se pelo facto do apontador auxiliar (q)estar a apontar para o nodo com a chave imediatamente superior à que se pretende inserir, o quecorresponde à seguinte condição:

(1) SE q.getKey() > ch ENTÃO(2) ...

No entanto as instruções a executar para ambas situações não são completamente iguais, daíser necessário considerar mais uma condição, de tal forma que:

(1) SE q.getKey() > ch ENTÃO(2) p.setNext(q)(3) p.setPrev(q.getPrev())(4) q.setPrev(p)(5) SE q = lst ENTÃO(6) lst ← p(7) SENÃO(8) (p.getPrev()).setNext(p)(9) FSE(10) FSE

Paulo Matos

Page 77: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

58 Capıtulo 4. Estruturas de Dados Lineares

Insercao no fim da lista

Quando o nodo é para ser inserido no �m da lista, o apontador auxiliar (q) �ca posicionadono último nodo, como se encontra ilustrado na Figura 4.20.

Figura 4.20: Posicao dos apontadores aquando da insercao no fim da lista.

Os apontadores são redireccionados segundo o esquema da Figura 4.21, o que corresponde àsseguintes operações:

(1) p.setNext(NULO)(2) p.setPrev(q)(3) q.setNext(p)

Ou ainda:

(1) p.setNext(q.getNext())(2) p.setPrev(q)(3) q.setNext(p)

Esta situação distingue-se das demais por: q não é nulo; e a chave contida no nodo apontadopor q é inferior à chave que se pretende inserir.

(1) SE q 6= NULO E q.getKey()<ch ENTÃO(2) ...

Insercao de um elemento repetido

Para além das quatro situações anteriores, pode ainda ocorrer que o elemento a inserir já existana lista. À semelhança das listas simples há que decidir que tipo de política seguir nestes casos.No algoritmo que se segue optou-se por não inserir o nodo, mas apenas substituir a informação,fazendo com que a rotina de inserção sirva também como rotina para alterar a informação.

Paulo Matos

Page 78: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

4.2. Listas duplamente ligadas 59

Figura 4.21: Insercao no fim da lista.

Solucao final

O algoritmo completo para a rotina de inserção, é o seguinte:

(1) PROC InserirLD(lst:NODOD,ch:CHAVE,inf:INFO):NODOD(2) VAR q, p :NODOD(3) INÍCIO(4) q ← lst(5) ENQ q 6= NULO E q.getKey()<ch E q.getNext() 6= NULO FAZER(6) q ← q.getNext()(7) FENQ(8) SE q 6= NULO E q.getKey()=ch ENTÃO(9) q.setInfo(inf)(10) SENÃO(11) p.new()(12) p.setKey(ch)(13) p.setInfo(inf)(14) SE q = NULO ENTÃO(15) p.setNext(NULO)(16) p.setPrev(NULO)(17) lst ← p(18) SENÃO(19) SE q.getKey() > ch ENTÃO(20) p.setNext(q)(21) p.setPrev(q.getPrev())(22) q.setPrev(p)(23) SE q = lst ENTÃO lst ← p(24) SENÃO (p.getPrev()).setNext(p) FSE(25) SENÃO(26) p.setNext(NULO)(27) p.setPrev(q)(28) q.setNext(p)

Paulo Matos

Page 79: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

60 Capıtulo 4. Estruturas de Dados Lineares

(29) FSE(30) FSE(31) FSE(32) RET lst(33) FIM

4.2.3 Remocao de elementos numa lista duplamente ligada

A remoção de um elemento da lista passa por detectar a posição do respectivo nodo, procedendo-se em seguida à sua remoção e ao redireccionar dos apontadores.

Para detectar a posição do nodo, utiliza-se uma solução semelhante à proposta para a pesquisa,ou seja:

(1) VAR q :NODOD(2) q ← lst(3) ENQ q 6= NULO E q.getKey()<ch ENTÃO(4) q ← q.getNext()(5) FENQ(6) SE q 6= NULO E q.getKey()=ch ENTÃO(7) ...

Após a execução da rotina anterior, se o apontador auxiliar (q) apontar para NULO, ou seapontar para um nodo cuja chave não seja a que se pretende, então é porque a chave a removernão faz parte da lista. Caso contrário ocorre uma das três seguintes situações:

• Remoção do topo da lista;

• Remoção de uma posição intermédia da lista;

• Remoção do �m da lista.

Remocao da posicao inicial da lista

Caso o nodo a remover seja o primeiro, então o apontador auxiliar (q) aponta para a mesmaposição do apontador inicial da lista (lst), como se encontra ilustrado na Figura 4.22.

Nestas circunstancias as operações a realizar são:

(1) lst ← q.getNext()(2) lst.setPrev(NULO)(3) q.free()

NOTA: A segunda operação só deve ser realizada caso o next de q não seja NULO.

Paulo Matos

Page 80: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

4.2. Listas duplamente ligadas 61

Figura 4.22: Remocao do topo da lista.

Remocao de uma posicao intermedia

O esquema da remoção de um nodo de uma posição intermédia encontra-se ilustrado naFigura 4.23, onde se pode veri�car a posição do apontador auxiliar (q) e de como se devemredireccionar os apontadores.

Figura 4.23: Remocao de uma posicao intermedia da lista.

As operações a executar nesta circunstancia são:

(1) (q.getNext()).setPrev(q.getPrev())(2) (q.getPrev( )).setNext(q.getNext( ))

Paulo Matos

Page 81: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

62 Capıtulo 4. Estruturas de Dados Lineares

(3) q.free()

Remocao do fim da lista

A remoção do �m da lista encontra-se representada na Figura 4.24.

Figura 4.24: Remocao do fim da lista.

As operações a realizar são:

(1) (q.getPrev()).setNext(q.getNext())(2) q.free()

Ou então:

(1) (q.getPrev()).setNext(NULO)(2) q.free()

Através das situações atrás analisadas pode-se concluir o seguinte:

• Só faz sentido remover um nodo caso q seja diferente de NULO e a chave contida em qigual à chave que se pretende remover;

• Que a operação (q.getNext()).setPrev(q.getPrev()), pode ser realizada sempre que nextde q são diferentes de NULO;

• No caso do nodo a remover ser o primeiro da lista, é ainda necessário actualizar o apontadorinicial da lista, isto é: lst ← q.getNext();

Paulo Matos

Page 82: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

4.2. Listas duplamente ligadas 63

• Caso contrário (se o nodo a remover não é o nodo inicial da lista) é então necessário realizara seguinte instrução: (q.getPrev()).setNext(q.getNext());

• Por �m apenas falta libertar o nodo q.

Solucao final

O algoritmo completo para a rotina de remoção é o seguinte:

(1) PROC RemoverLD(lst:NODOD,ch:CHAVE):NODOD(2) VAR q :NODOD(3) INÍCIO(4) q ← lst(5) ENQ q 6= NULO E q.getKey()<ch FAZER(6) q ← q.getNext()(7) FENQ(8) SE q 6= NULO E q.getKey()=ch ENTÃO(9) SE q.getNext()6=NULO ENTÃO(10) (q.getNext()).setPrev(q.getPrev())(11) FSE(12) SE lst = q ENTÃO(13) lst ← q.getNext()(14) SENÃO(15) (q.getPrev()).setNext(q.getNext())(16) FSE(17) q.free()(18) FSE(19) RET lst(20) FIM

4.2.4 Listagem dos elementos de uma lista duplamente ligada

A listagem é em tudo semelhante à da lista simples (ver Secção 4.1.4).

Exercıcios:

a) A direcção da ESTG considerou que existe um número excessivo de automóveis no parqueda escola pelo que decidiu passar a cobrar pelo estacionamento aos alunos e funcionários(nos quais se incluem os docentes).

Pretende-se implementar um programa que permita armazenar a informação dos arrenda-tários e respectivos lugares, utilizando uma única lista duplamente ligada. A informação areter é composta pelos seguintes campos:

Tipo de arrendatário tarrend {ALUNO, FUNCIONÁRIO}Número de identi�cação nid inteiroNome nome string

Paulo Matos

Page 83: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

64 Capıtulo 4. Estruturas de Dados Lineares

N. do lugar nlugar inteiroMatrícula do carro matricula string0

Para os alunos, o campo nid serve para armazenar o número mecanográ�co, para os fun-cionários, serve para armazenar o número do bilhete de identidade.

Por uma questão de gestão do espaço, é necessário de�nir uma política de atribuições delugares. Desta forma o programa não deve permitir introduzir mais arrendatários que onúmero de lugares existente (NLUGARESMAX).

Na lista deve surgir em primeiro lugar os funcionários ordenados pelo número do bilhetede identidade e depois os alunos ordenados pelo número mecanográ�co.

O programa deve conter rotinas de inserção, remoção, pesquisa através do número me-canográ�co, do número do bilhete de identidade ou da matrícula do automóvel, listagemde todos os elementos, listagem dos alunos, listagem dos funcionários e armazenar/ler ainformação em/de �cheiro. Deve ainda permitir determinar o número de lugares por alugar.

b) Para o problema anterior apresente uma solução que utilize duas listas duplamente ligadas,uma para os alunos e outra para os funcionários, mas garantindo sempre que os funcionáriostêm acesso a pelo menos 10% do parque automóvel.

Paulo Matos

Page 84: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

4.3. Stack’s 65

4.3 Stack’s

Como já se viu anteriormente, trata-te de uma estrutura de dados que permite inserir ouremover, os elementos apenas pelo topo da stack (pilha).

A stack funciona como uma pilha de folhas num tabuleiro, em que a única folha que estáimediatamente acessível é a do topo. Uma determinada folha que se encontre algures no meioda stack, só estará acessível após a remoção de todas as outras folhas que eventualmente seencontrem por cima, trata-se como tal de um sistema do tipo FILO (First-In-Last-Out). AFigura 4.25 mostra a estrutura de uma stack.

Figura 4.25: Estrutura de uma stack.

A especi�cação formal, a declaração de variáveis e a de�nição dos operadores para stack's,foi feita na Secção 2.3.6. É no entanto de chamar atenção que, por questões de implementação,optou-se por vezes por alterar os protótipos dos operadores (para lá das alterações impostas pelaprópria linguagem de programação). Por exemplo, os operadores pop e push, em algumas dasimplementações que posteriormente se apresentarão, serão rede�nidos da seguinte forma:

pop: INOUT: Stack(INFO) x OUT: INFO → BOOL // O valor de topo da stack// é devolvido através do// parâmetro do tipo INFO// e a função devolve// um booleano a indicar// se a operação foi,// ou não, bem sucedida.

Push: INOUT: Stack(INFO) x INFO → BOOL // Coloca o elemento do// tipo INFO no topo da// stack e devolve um// booleano a indicar se// a operação foi, ou não,// bem sucedida.

Paulo Matos

Page 85: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

66 Capıtulo 4. Estruturas de Dados Lineares

4.3.1 Stack’s estaticas

Uma stack pode ser implementada utilizando uma sequência (array ou vector), de tamanhosu�cientemente grande para conter todos os elementos nela colocados, e um apontador ou ín-dice (Stop), que marca a localização do elemento que está no topo da stack, conforme ilustra aFigura 4.26. Caso a stack se encontre vazia Stop tem o valor -1.

Figura 4.26: Ordem de remocao e insercao numa stack.

Uma stack do tipo estático pode ser de�nida através do seguinte tuplo:

STACKE = Base x Stop x tmaxBase : Seq(INFO)Stop : ÍNDICEtmax : INTEIRO

A declaração formal de uma variável do tipo STACKE de Inteiros, com nome Mystack, faz-seda seguinte forma:

(1) VAR Mystack :STACKE(INTEIROS)

Uma stack's estática pode ser vista como uma sequência, cuja a política de inserção e remoçãoestá condicionada. Como tal, pode-se utilizar praticamente todos os operadores de�nidos paraa sequência, desde que isso não contradiga o funcionamento da stack.

Para facilitar o acesso aos componentes de STACKE, são ainda de�nidos os seguintes opera-dores:

getBase: STACKE(INFO) → SEQ(INFO) // Permite aceder à// sequência da STACKE

getPTop: STACKE(INFO) → ÍNDICE // Devolve Stop

setPTop: STACKE(INFO) x ÍNDICE → STACKE(INFO) // Actualiza Stop

getTMax: STACKE(INFO) → INTEIRO // Devolve o tamanho// da stack

Paulo Matos

Page 86: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

4.3. Stack’s 67

NOTA: Não se de�ne o operador setTMax(...) uma vez que o tamanho da stack deve serde�nido aquando da sua inicialização.

Até aqui os operadores para stack's foram apenas de�nidos formalmente, sem se apresentarqualquer tipo de implementação. Pretende-se agora detalhar o funcionamento desses operadoressegundo a estrutura de�nida para STACKE.

Inicializacao da stack

Para se inicializar uma stack estática é necessário colocar o apontador do topo (Stop) comum valor não válido, isto é, que não represente nenhum índice válido. Esse valor poderá ser, porexemplo, zero se a sequência começar em 1; ou poderá ser -1 se a sequência começar em zero. AFigura 4.27 ilustra o processo de inicialização (considerando que a sequência começa na posiçãozero).

Figura 4.27: Iniciacao de uma stack estatica.

(1) PROC new(tam:INTEIRO):STACKE(INFO)(2) VAR base :Seq(INFO)(3) VAR Stop :ÍNDICE(4) VAR tmax :INTEIRO(5) INÍCIO(6) base.new()(7) Stop ← -1(8) tmax ← tam(9) RET (base,Stop,tmax)(10) FIM

Insercao de um novo elemento (Push)

A inserção de um novo elemento na stack obriga a incrementar o Stop em 1 e só depois colocaro valor na stack. Há no entanto que ter o cuidado de veri�car se a stack está ou não cheia. AFigura 4.28 ilustra o processo de inserção (Push).

(1) PROC Push(INOUT:s:STACKE(INFO),val:INFO):STATUS(2) INÍCIO(3) SE s.getPTop() = s.getTMax()-1 ENTÃO(4) RET INSUCESSO ["Stack Cheia"](5) SENÃO(6) s.setPTop(s.getPTop()+1)

Paulo Matos

Page 87: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

68 Capıtulo 4. Estruturas de Dados Lineares

Figura 4.28: Insercao de um elemento numa stack.

(7) (s.getBase()).set(val,s.getPTop())(8) RET SUCESSO(9) FSE(10) FIM

Remocao de um elemento (Pop)

A operação pop(...), remove o elemento que esta no topo da stack e decrementa o Stop em 1.Há no entanto que veri�car se a stack está, ou não, vazia antes de se tentar remover qualquerelemento. A Figura 4.29 ilustra o processo de remoção de um elemento de uma stack estática(Pop)

Figura 4.29: Remocao de um elemento de uma stack.

(1) PROC Pop(INOUT:s:STACKE(INFO),OUT:val:INFO):STATUS(2) INÍCIO(3) SE s.getPTop() = -1 ENTÃO(4) // val ← NULO(5) RET INSUCESSO ["Stack Vazia"](6) SENÃO(7) val ← (s.getBase()).get(s.getPTop())(8) s.setPTop(s.getPTop()-1)(9) RET SUCESSO(10) FSE(11) FIM

Paulo Matos

Page 88: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

4.3. Stack’s 69

Determinar o elemento de topo (Top)

É por vezes necessário saber qual o valor do elemento que se encontra no topo da stack semo remover, tal é possível de realizar à custa dos operadores atrás de�nidos, bastando para talfazer:

(1) pop(s,x)(2) ....(3) push(s,x)

É no entanto habitual de�nir esta operação de forma independente. O algoritmo é muitoparecido com o da operação Pop(...), mas não é necessário actualizar o Stop, a sua elaboração�ca como proposta de exercício.

4.3.2 Stack’s dinamicas

Uma outra forma de implementar stack's consiste em utilizar estruturas dinâmicas do tipolistas. A implementação das stack's sobre listas simples, faz-se inserindo e removendo os elemen-tos pelo topo da lista (primeiro elemento da lista). A estrutura física de uma stack sobre umalista simples encontra-se representada na Figura 4.30.

Figura 4.30: Implementacao de uma stack sobre listas simples.

Os nodos para este caso são semelhantes aos utilizados nas listas simples (ver Secção 4.1), ouseja, contêm um campo que representa a informação a reter em cada nodo (info) e um apontadorpara o elemento seguinte (next) da lista. Não há no entanto a necessidade de de�nir uma chave(mesmo que esta exista) uma vez que neste tipo de estrutura, a organização dos elementos emnada depende da chave (esta faz parte da informação, isto é, de info).

Formalmente, uma stack dinâmica é de�nida da seguinte forma:

Paulo Matos

Page 89: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

70 Capıtulo 4. Estruturas de Dados Lineares

STACKD = info x nextinfo : INFOnext : STACKD

Os operadores são em tudo semelhantes aos utilizados nas listas (simples):

new: → STACKD(INFO) // Cria e inicializa// uma nova stack

free: STACKD(INFO) → ∅ // Destrói a stackgetInfo:STACKD(INFO) → INFO // Devolve infosetInfo:STACKD(INFO) x INFO → STACKD(INFO) // Atribui o valor

// de INFO a infogetNext:STACKD(INFO) → STACKD(INFO) // Devolve nextsetNext:STACKD(INFO)1 x STACKD(INFO)2 → STACKD(INFO) // Atribui o valor

// de STACKD(INFO)2

// a next

Operacoes sobre stack’s dinamicas

Ao contrário das lista, onde é possível executar as operações do tipo pesquisa e listagem, nasstack's apenas são admitidas as seguintes funções:

• Inserir um elemento na stack (push),

• Retirar um elemento da stack (pop),

• Aceder ao valor do topo da stack (top).

Insercao de um novo elemento na stack (Push)

O processo de inserção numa stack, implementada com base numa lista simples, necessita deter em conta as seguintes situações:

• Inserir numa lista vazia

• Inserir no topo da lista

Para a primeira situação, considera-se lstack o apontador inicial da lista e novo o aponta-dor para a estrutura a inserir. A reordenação dos apontadores faz-se segundo o esquema daFigura 4.31.

Que corresponde às seguintes operações:

Paulo Matos

Page 90: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

4.3. Stack’s 71

Figura 4.31: Insercao numa stack dinamica vazia.

(1) novo.setNext(lstack)(2) lstack ← novo

Caso a stack contenha pelo menos um elemento, então a inserção faz-se à cabeça da lista.Esta situação encontra-se representada na Figura 4.32, onde se mantém a convenção para lstack

e novo.

Figura 4.32: Insercao numa stack dinamica nao vazia.

As operações a executar neste último caso são:

(1) novo.setNext(lstack)(2) lstack ← novo

É assim possível concluir que as instruções a executar para ambas as situações são iguais, oque resulta no seguinte algoritmo:

Paulo Matos

Page 91: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

72 Capıtulo 4. Estruturas de Dados Lineares

(1) PROC Push(INOUT:lstack:STACKD(INFO),val:INFO)(2) VAR novo :STACKD(INFO)(3) INÍCIO(4) novo.new()(5) novo.setInfo(val)(6) novo.setNext(lstack)(7) lstack ← novo(8) FIM

Remocao de um elemento da stack (Pop)

A remoção de um elemento da stack encontra-se representada na Figura 4.33, onde se começapor salvaguardar o valor de topo da stack para que seja posteriormente devolvido. Depois,utilizando um apontador auxiliar (qaux), salvaguarda-se o endereço do elemento do topo, o qualservirá para libertar o respectivo nodo. Por �m actualiza-se o valor de lstack e liberta-se o nododo topo. O que corresponde ao seguinte conjunto de operações:

(1) val ← lstack.getInfo()(2) qaux ← lstack

(3) lstack ← lstack.getNext()(4) qaux.free()

Onde val serve de variável de retorno do elemento do topo da stack. De notar ainda que estasoperações também funcionam para o caso em que o elemento a remover é o único da stack.

Figura 4.33: Insercao numa stack dinamica nao vazia.

Na solução �nal, o único cuidado a ter é testar, antes de se tentar remover, se a stack nãoestá vazia. A solução completa é a seguinte:

Paulo Matos

Page 92: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

4.3. Stack’s 73

(1) PROC Pop(INOUT lstack:STACKD(INFO),OUT:val:INFO):STATUS(2) VAR qaux :STACKD(INFO)(3) INÍCIO(4) SE lstack 6= NULO ENTÃO(5) val ← lstack.getInfo() // Passo 1(6) qaux ← lstack // Passo 2(7) lstack ← lstack.getNext() // Passo 3(8) qaux.free() // Passo 4(9) RET SUCESSO(10) SENÃO(11) // val ← ∅(12) RET INSUCESSO ["Stack vazia"](13) FSE(14) FIM

Ler valor do topo da stack (Top)

Mantendo a convenção para lstack, basta então executar o seguinte procedimento, para obtero valor do topo:

(1) PROC Top(lstack:STACKD(INFO),OUT:val:INFO):STATUS(2) INÍCIO(3) SE lstack 6= NULO ENTÃO(4) val ← lstack.getInfo()(5) RET SUCESSO(6) SENÃO(7) // val ← ∅(8) RET INSUCESSO(9) FSE(10) FIM

4.3.3 Stack’s Estaticas x Stack’s Dinamicas

A grande vantagem das stack estáticas em comparação com as dinâmicas está, em primeirolugar, na facilidade de implementação e, em segundo, na velocidade de execução. Esta últimavantagem deve-se ao facto de utilizarem arrays (sequências) o que faz com que o acesso seja maisrápido.

As estruturas estáticas permitem ainda economizar espaço de memória, visto que não neces-sitam de utilizar apontadores, como acontece nas estruturas dinâmicas. A sua principal desvan-tagem reside no facto de necessitarem de reservar de antemão espaço su�ciente de memória paraconter todos os elementos, sob pena de que a partir de determinado ponto �que inviabilizada autilização da stack. Por outro lado, se o espaço reservado for muito grande, pode acontecer quegrande parte dele nunca chegue a ser utilizado, havendo como tal um desperdício de recursos.

Paulo Matos

Page 93: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

74 Capıtulo 4. Estruturas de Dados Lineares

Exercıcio

a) Implemente um programa, com base numa stack, para inverter a ordem dos caracteres deuma string.

Paulo Matos

Page 94: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

4.4. Queue’s 75

4.4 Queue’s

A queue é uma estrutura que permite guardar um conjunto de elementos do mesmo tipo,organizados segundo a ordem pela qual são inseridos, ou seja, o primeiro a ser inserido é o quese encontra à cabeça da queue e como tal o primeiro a ser removido.

Servem para implementar sistemas multi-programados, em que é necessário uma gestão dotipo FIFO (First In First OUT), ou LILO (Last In Last Out). Em que o primeiro "utilizador"aentrar na queue, é o primeiro a ser atendido, o segundo será atendido em segundo e daí poradiante.

As queue's podem, como acontece no caso das stack's, ser implementadas com base em estru-turas estáticas, ou dinâmicas. Em ambos casos é necessário manter a localização do primeiro eúltimo elemento da queue.

De�ne-se, geralmente, Qrear como sendo o indicador da posição onde inserir o próximo ele-mento a entrar na queue e Qfront como o indicador do primeiro elemento a ser retirado da queue.A Figura 4.34 representa a estrutura de uma queue.

Figura 4.34: Estrutura de uma queue.

A especi�cação formal, a declaração de variáveis e a de�nição dos operadores para stack's, foifeita na Secção 2.3.7. É no entanto útil acrescentar os seguintes operadores:

QEmpty: Queue(INFO) → BOOL // Indica se a queue está vaziaQFull: Queue(INFO) → BOOL // Indica se a queue está cheia

Também nas queue's, e à semelhança do que aconteceu para as stack's, em termos de im-plementação é por vezes mais e�ciente ou simples utilizar outros protótipos para os operadoresbase. Isto acontece nomeadamente para os operadores QRemove e QInsert:

QInsert:INOUT:Queue(INFO) x INFO → BOOL // Insere o valor de INFO na// queue, e devolve um// booleano a indicar se a// operação foi, ou não,// bem sucedida.

QRemove:INOUT:Queue(INFO) x OUT:INFO → BOOL // Remove um elemento,// devolvendo a respectiva// informação através// do parâmetro INFO.

Paulo Matos

Page 95: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

76 Capıtulo 4. Estruturas de Dados Lineares

NOTA: É também comum designar os operadores QInsert e QRemove, respectivamente, porenqueue e dequeue.

4.4.1 Queue’s estaticas

À semelhança das stack's, também as queue's podem ser implementadas recorrendo a sequên-cias (arrays ou vectores) de tamanho su�cientemente grande para garantir o continuo funciona-mento da queue.

Como se encontra representado na Figura 4.35, QFront e QRear indicam, respectivamente, aposição do próximo elemento a remover e a posição onde inserir o próximo elemento a entrar naqueue.

Figura 4.35: Estrutura de uma queue estatica.

Uma queue estática é formalmente descrita da seguinte forma:

QUEUES = Base x QFront x QRear x TQueueBase : Seq(INFO)QFront, QRear : ÍNDICETQueue : INTEIRO

NOTA: A designação utilizada para representar uma queue estática (QUEUES), advém deQUEUE + Static.

A declaração de uma variável tipo QUEUES de INTEIROS, com nome MyQueue é feita daseguinte forma:

(1) VAR MyQueue : QUEUES(INTEIROS)

Para além dos operadores de sequências (estrutura formal utilizada para implementar umaqueue estática) atrás referidos, é ainda necessário de�nir os seguintes operadores para aceder aoscomponentes do tuplo QUEUES:

getBase: QUEUES(INFO) → Seq(INFO) // Devolve o valor// de Base

getPFront: QUEUES(INFO) → ÍNDICE // Devolve o valor// de QFront

setPFront: QUEUES(INFO) x ÍNDICE → QUEUES(INFO) // Atrbiui o valor de

Paulo Matos

Page 96: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

4.4. Queue’s 77

// ÍNDIDE a QFrontgetPRear: QUEUES(INFO) → ÍNDICE // Devolve o valor

// de QRearsetPRear: QUEUES(INFO) x ÍNDICE → QUEUES(INFO) // Atribui o valor de

// ÍNDICE a QReargetTQueue: QUEUES(INFO) → INTEIRO // Devolve o valor

// de TQueue

NOTA: Não se de�ne o setTQueue(...) uma vez que o tamanho de uma queue estática éde�nido aquando da sua inicialização.

Operacoes sobre queue’s estaticas

As operações típicas de uma queue estática, são:

• Veri�car se a queue está vazia (QEmpty),

• Veri�car se a queue está cheia (QFull),

• Colocar um elemento na queue (QInsert),

• Remover um elemento da queue (QRemove).

Implementacao de queue’s sobre arrays

O procedimento de inserção consiste em colocar o novo elemento no local apontado por QReare de seguida actualizar esse índice, conforme ilustra a Figura 4.36.

Figura 4.36: Insercao de um elemento numa queue estatica.

(1) PROC QInsertE(INOUT:q:QUEUES(INFO),val:INFO):STATUS(2) INÍCIO(3) SE q.getPRear()>q.getTQueue()-1 ENTÃO(4) RET INSUCESSO [Queue cheia](5) SENÃO(6) (q.getBase()).set( val,q.getPRear())(7) q.setPRear(q.getPRear()+1)

Paulo Matos

Page 97: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

78 Capıtulo 4. Estruturas de Dados Lineares

(8) RET SUCESSO(9) FSE(10) FIM

Com a inserção de novos elementos na queue, QRear vai avançando até que inevitavelmenteatinge o �m da sequência, não sendo assim possível inserir mais elementos na queue. O apontadorQRear atinge assim o seu limite, estando a queue "inutilizada".

Uma solução bastante mais e�ciente, consiste em aproveitar as posições que vão �cando livrescom a remoção dos elementos da queue para inserir os novos elementos. Para tal, é necessáriotestar quando é que QRear atinge o limite (�m da sequência) e nessa situação, redireccionarQRear para a posição inicial da sequência. Desta forma, a queue pode funcionar em continuo,desde que o número de elementos contidos na queue não ultrapasse o NMAXELEM-1, o que étotalmente dependente do tamanho de�nido para a sequência. Com esta solução, a sequênciafunciona como um circuito fechado, conforme ilustra a Figura 4.37.

Figura 4.37: Queue com base numa sequencia circular.

Há agora condições para implementar as várias rotinas/operadores de uma queue estática,com a garantia de que esta irá funcionar correctamente, desde que não se exceda o número limitede elementos (NMAXELEM-1).

Verificar se a queue esta vazia (QEmpty)

Uma queue encontra-se vazia quando QFront e QRear assinalarem a mesma posição, con-forme ilustra a Figura 4.38.

A rotina para detectar se a queue está ou não vazia é a seguinte:

(1) PROC QEmptyE(q:QUEUES(INFO)):BOOL

Paulo Matos

Page 98: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

4.4. Queue’s 79

Figura 4.38: Representacao de uma queue estatica vazia.

(2) INÍCIO(3) SE q.getPRear() = q.getPFront() ENTÃO(4) RET VERDADEIRO(5) SENÃO(6) RET FALSO(7) FSE(8) FIM

Verificar se a queue esta cheia (QFull)

Se a sequência utilizada para implementar a queue estiver completamente cheia, que QReare QFront apontam para a mesma posição. O que é a condição para se identi�car se a queueestá vazia. Isto levanta obviamente a seguinte questão: Nestas condições está a queue cheia ouvazia?

A forma de distinguir a duas situações, é não ocupar completamente a sequência, isto é, aqueue estará cheia a partir do momento em que já só há uma posição livre na sequência. Omesmo será dizer que:

• QRear é inferior ao QFront em uma unidade,

• Ou que QFront e QRear encontram-se respectivamente na primeira e última posição dasequência.

A Figura 4.39 ilustra esta situação.

Nestas condições, o algoritmo que permite determinar se a queue está, ou não, cheia, é oseguinte:

(1) PROC QFullE(q:QUEUES(INFO)):BOOL(2) INÍCIO(3) SE q.getPRear()+1 = q.getPFront() OU(4) (q.getPRear() = q.getTQueue()- 1 E q.getPFront() = 0) ENTÃO(5) RET VERDADEIRO

Paulo Matos

Page 99: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

80 Capıtulo 4. Estruturas de Dados Lineares

Figura 4.39: Representacao de duas situacoes em que a queue esta cheia.

(6) SENÃO(7) RET FALSO(8) FSE(9) FIM

Insercao de um novo elemento na queue (QInsert)

O algoritmo completo para a inserção numa queue obedece aos mesmos princípios ilustradosna Figura 4.36, mas tendo ainda em conta o facto do array funcionar de forma circular. Oalgoritmo �nal é o seguinte:

(1) PROC QInsertE(INOUT:q:QUEUES(INFO),val:INFO):STATUS(2) INÍCIO(3) SE q.QFullE() ENTÃO(4) RET INSUCESSO ["Queue Cheia"](5) SENÃO(6) (q.getBase()).set(val,q.getPRear())(7) SE q.getPRear() = q.getTQueue()-1 ENTÃO(8) q.setPRear(0)(9) SENÃO(10) q.setPRear(q.getPRear()+1)(11) FSE(12) RET SUCESSO(13) FSE(14) FIM

Remover um elemento da queue (QRemove)

A remoção de um elemento da queue, passa por retirar o elemento que se encontra na posi-ção assinalada por QFront, actualizando em seguida o apontador. Por �m devolve-se o valor

Paulo Matos

Page 100: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

4.4. Queue’s 81

removido. Esta situação encontra-se representada na Figura 4.40.

Figura 4.40: Ilustracao do processo de remocao numa queue estatica.

O processo de remoção pressupõem um teste prévio à queue para veri�car se existe algumelemento a remover. Para além disso, há que veri�car se QFront atingiu a última posição dasequência (NMAXELEM − 1), sendo então necessário redireccioná-lo para o início da mesma.O algoritmo completo é o seguinte:

(1) PROC QRemoveE(INOUT:q:QUEUES(INFO),OUT:val:INFO):STATUS(2) INÍCIO(3) SE q.QEmpty() ENTÃO(4) RET INSUCESSO ["Queue vazia"](5) SENÃO(6) val ← (q.getBase()).get(q.getPFront())(7) SE q.getPFront() = q.getTQueue()-1 ENTÃO(8) q.setPFront(0)(9) SENÃO(10) q.setPFront(q.getPFront()+1)(11) FSE(12) RET SUCESSO(13) FSE(14) FIM

4.4.2 Queue’s dinamicas

Uma forma mais e�ciente (sem desperdício de memória) de implementar queue's, é recorrendoa estruturas dinâmicas do tipo listas. A implementação de queue's utilizando listas ligadas émuito simples, basta apenas inserir no �m da lista e remover do início da lista. A estrutura deuma queue (qd) implementada sobre listas ligadas encontra-se apresentada na Figura 4.41.

Os nodos para este caso são mais uma vez semelhantes aos utilizados nas listas simples,contêm um campo que representa a informação a reter em cada nodo (info) e um apontadorpara o elemento seguinte (next) da lista.

A implementação de queue's dinâmicas, com listas ligadas simples, obriga a guardar o apon-tador inicial da lista (QFront) e o apontador para o último nodo (QRear). A inserção na queuefaz-se através do apontador QRear e a remoção através de QFront.

Paulo Matos

Page 101: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

82 Capıtulo 4. Estruturas de Dados Lineares

Figura 4.41: Estrutura de uma queue sobre listas simples.

Formalmente a estrutura é de�nida à custa da estrutura NODOS da lista ligada simples (ouduplamente ligada), da seguinte forma:

QUEUED = QFront x QRearQFront : NODOQRear : NODO

A declaração de uma variável do tipo QUEUED com o nome qd, de elementos do tipo INFO,faz-se da seguinte forma:

(1) VAR qd :QUEUED(INFO)

Neste tipo de estrutura podem ser utilizados todos os operadores previamente de�nidos paraas listas simples, desde que não contradigam o funcionamento da queue, mais o seguinte conjunto:

new: → QUEUED // Cria e inicializa um queuegetPFront: QUEUED → NODO // Devolve o valor de QFrontsetPFront: QUEUED x NODO → QUEUED // Atribui o valor de NODO a QFrontgetPRear: QUEUED → NODO // Devolve o valor de QRearsetPRear: QUEUED x NODO → QUEUED // Atribui o valor de NODO a QRear

Operacoes sobre queue’s dinamicas

As operações típicas de uma queue dinâmica são as mesmas das queue's estáticas, isto é:

• Testar se queue está vazia (QEmpty),

• Inserir um elemento na queue (QInsert),

• Retirar um elemento da queue (QRemove).

Paulo Matos

Page 102: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

4.4. Queue’s 83

Não é necessário testar se a queue está cheia, uma vez que o espaço desta apenas se encontralimitado pela quantidade de memória disponibilizada pelo sistema operativo. Excepto caso sepretenda limitar o número de elementos na queue a um valor máximo.

Testar se a queue esta vazia (QEmpty)

Para veri�car se a queue está vazia, basta testar se o apontador QRear é NULO.

(1) PROC QEmptyD(qd:QUEUED):BOOL(2) INÍCIO(3) SE qd.getPRear() = NULO ENTÃO(4) RET TRUE ["Queue vazia"](5) SENÃO(6) RET FALSE ["Queue não vazia"](7) FSE(8) FIM

Insercao de um novo elemento na queue (QInsert)

O processo de inserção de um elemento numa queue com listas ligadas é bastante simples,uma vez que só podem ocorrer duas situações distintas: a primeira é quando a lista está vaziae a segunda é quando possui pelo menos um elemento. Se a lista está vazia, com a inserçãodo primeiro nodo, QRear e QFront passam ambos a apontar para o nodo novo. A situaçãoencontra-se representada na Figura 4.42, onde qd representa a QUEUED.

Figura 4.42: Insercao numa queue dinamica vazia.

As operações a realizar caso a queue esteja vazia, são:

(1) novo.setNext(NULO)(2) qd.setPRear(novo)(3) qd.setPFront(novo)

Paulo Matos

Page 103: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

84 Capıtulo 4. Estruturas de Dados Lineares

O primeiro passo, onde se coloca o campo next de novo a apontar para NULO, pode serremovido sem prejuízos para o processo de inserção, no entanto devido aos outros processosaconselha-se a sua inclusão.

No caso da queue possuir pelo menos um elemento, então a inserção faz-se pelo �m da lista.A Figura 4.43 ilustra o processo de inserção para este caso.

Figura 4.43: Insercao numa queue dinamica nao vazia.

As operações a realizar caso a queue contenha pelo menos um elemento, são:

(1) novo.setNext(NULO)(2) (qd.getPRear()).setNext(novo)(3) qd.setPRear(novo)

A solução �nal é a seguinte:

(1) PROC QInsertD(INOUT:qd:QUEUED,inf:INFO)(2) VAR novo :NODOS(3) INÍCIO(4) novo.new()(5) novo.setInfo(inf)(6) novo.setNext(NULO)(7) SE qd.getPRear() = NULO ENTÃO(8) qd.setPRear(novo)(9) qd.setPFront(novo)(10) SENÃO(11) (qd.getPRear()).setNext(novo)(12) qd.setPRear(novo)(13) FSE(14) FIM

Paulo Matos

Page 104: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

4.4. Queue’s 85

Remover um elemento da queue

A operação de remoção de uma queue, consiste em remover da lista o elemento que se encontraà cabeça desta. É no entanto necessário testar previamente se a queue possui algum elementopara remover.

A Figura 4.44 ilustra o processo de remoção de uma queue dinâmica (qd). Para tal salvaguarda-se o conteúdo do nodo a remover em val, depois através de um apontador auxiliar (qaux) guarda-setemporariamente o endereço do elemento a remover, actualiza-se o QFront e por �m liberta-seo nodo removido através de qaux.

Figura 4.44: Remocao numa queue dinamica com mais do que um elemento.

As operações a realizar para se proceder a remoção de um elemento, são as seguintes:

(1) val ← (qd.getPFront()).getInfo()(2) qaux ← qd.getPFront()(3) qd.setPFront((qd.getPFront()).getNext())(4) qaux.free()

Há ainda que salvaguardar a situação em que o elemento removido é o último da queue, nestasituação é ainda necessário actualizar QRear para NULO.

A solução �nal é a seguinte:

(1) PROC QRemoveD(INOUT:qd:QUEUED,OUT:val:INFO):STATUS(2) VAR qaux :NODOS(3) INÍCIO(4) SE qd.QEmptyD() = FALSE ENTÃO(5) val ← (qd.getPFront()).getInfo()(6) qaux ← qd.getPFront()(7) SE qd.getPFront() = qd.getPRear() ENTÃO(8) qd.setPFront(NULO)(9) qd.setPRear(NULO)(10) SENÃO

Paulo Matos

Page 105: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

86 Capıtulo 4. Estruturas de Dados Lineares

(11) qd.setPFront((qd.getPFront()).getNext())(12) FSE(13) qaux.free()(14) RET SUCESSO(15) SENÃO(16) // val ← ∅(17) RET INSUCESSO(18) FSE(19) FIM

Exercıcios:

a) Na clínica médica de São Bentinho, os clientes ao chegarem, dirigem-se à recepção, ondepreenchem um questionário, que para além de conter a informação referente ao paciente,serve para determinar a ordem de atendimento.

A clínica possui vários médicos ao serviço que assim que, terminam uma consulta dirigem-seà secretaria onde se informam sobre qual é o próximo paciente a atender.

O director da clínica pretende informatizar o sistema de forma a aumentar a e�ciência.Para tal, pede que implemente um programa com base numa queue estática.

b) A responsável pelo serviços de bar da Escola Superior de Tecnologia e Gestão, perante agrande a�uência de alunos e funcionários ao bar e de forma a evitar complicações, decidiuencomendar um programa para a gestão de atendimentos.

Pretende-se que todo o sistema funcione da seguinte forma: o aluno, ou funcionário, devedirigir-se à caixa para efectuar a sua encomenda, sendo de imediato emitido um pedido parauma �la de espera. Nesse pedido consta um código, a data e os itens da encomenda. Aoaluno/funcionário é entregue um talão com o mesmo código do pedido. Cabe às funcionáriasaceder aos pedidos pela ordem de chegada, ou seja, pela ordem que foram inseridos na �lade espera. Para tal preparam os itens pedidos, sendo estes entregues ao aluno/funcionárioque tenha o talão com o mesmo código que consta no pedido.

Paulo Matos

Page 106: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

4.5. Tabelas de hash 87

4.5 Tabelas de hash

As tabelas de hash são estruturas de dados que minimizam alguns inconvenientes inerentesàs estruturas sequenciais (e outras) e são especialmente adequadas para problemas de grandedimensão, isto é, problemas com façam uso de grandes quantidades de dados.

O contexto de aplicação das tabelas de hash, pressupõe a existência de um conjunto de chaves,um conjunto de informação e uma função, designada por função de hash, que faz corresponder acada chave a respectiva informação.

Este tipo de estrutura apresenta francas vantagens quando utilizada em situações em queo cardinal do conjunto das chaves é potencialmente muito superior ao número de elementosefectivos (em média) da estrutura de dados. Serve de exemplo a gestão de contas correntesde um banco, onde se utilize como chave o número do bilhete de identidade. Este é o tipo deproblema em que o cardinal do domínio das chaves é muito grande quando comparado com onúmero efectivo de chaves utilizadas por um dado banco.

De notar que nesta situação a utilização de, por exemplo, um array, implicava que este fossedeclarado com uma dimensão igual à do cardinal do domínio das chaves, fazendo com que muitasposições �cassem por ocupar, havendo como tal desperdício de espaço. Poder-se-ia pensar atéem utilizar um array com um número de elementos proporcional ao cardinal do domínio daschaves, criando uma relação do tipo de n para 1, de tal forma que a n elementos do domínio,corresponderia uma única posição no array. Apesar desta solução já permitir reduzir o númerode espaços desperdiçados do array, se não for convenientemente gerida pode levar a situaçõescaóticas, de sobreposição de elementos, bem como deteriorar o tempo de acesso.

Uma outra solução para este tipo de problema consiste em utilizar listas, mantendo assimapenas o espaço das chaves que contivessem informação. Mas as listas têm uma enorme desvan-tagem, é que são muito pouco e�cientes quando o número de elementos é muito grande, ondepara realizar uma simples pesquisa pode ser necessário ter que percorrer grande parte da lista.

As tabelas de hash surgem assim como uma solução alternativa que minimiza os inconvenientesdas sequências (arrays) e das listas.

A função de hash é uma função �nita que relaciona os elementos do conjunto das chaves,designado por domínio, com os elementos do conjunto das informações, designado por contrado-mínio.

A relação entre uma chave e a respectiva informação é dada pelo conjunto formado pela funçãode hash e pelas funções de acesso à estrutura de dados (pesquisa, inserção ou remoção).

De notar que, apesar do que se disse atrás, uma sequência pode ser vista como uma tabelade hash, onde o domínio, ou conjunto das chaves, são os índices e os elementos da sequênciaformam o conjunto das informações. O índice zero (ou um) de�niria assim o primeiro elementoda sequência.

A função seria então injectiva, uma vez que para cada elemento do conjunto das chavescorresponde uma única posição da sequência.

No entanto não é esta a ideia do funcionamento das tabelas de hash. Ao contrário do exemplo,as tabelas de hash aplicam-se normalmente em situações em que a relação entre o cardinal dodomínio e o número efectivo de dados na tabela é muito grande (cardinal do conjunto de chegada).

Paulo Matos

Page 107: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

88 Capıtulo 4. Estruturas de Dados Lineares

De uma forma genérica, utilizam-se tabelas de hash nas situações em que:

• Existe uma função �nita de um conjunto de chaves para um conjunto de informações;

• O cardinal do domínio é potencialmente muito superior ao cardinal do contra domínio, semque no entanto este último seja um valor pequeno.

Uma tabela de hash é então de�nida pelo seguinte par:

• Uma sequência que armazena sequências/listas de dados (chave x informação);

• E uma função h, designada por função de hash, que tem por �nalidade relacionar as chavesch1, ..., chn com as posições da sequência (principal).

h: chj -> i, chj ∈ {ch1, ..., chn} ∧ i ∈ {Índices da sequência}

Na Figura 4.45 encontra-se representada a estrutura de uma tabela de hash.

Figura 4.45: Estrutura formal de uma tabela de Hash.

Na prática e como se pode veri�car pela de�nição, a função de hash não relaciona a chavecom a respectiva informação, mas sim, a chave com a posição da sequência onde se encontra ainformação respeitante a essa chave.

O tamanho da sequência (principal) depende do problema concreto a resolver, em qualquercaso, se for um valor grande, vai permitir dispersar melhor os dados, melhorando o tempo deacesso, mas se for muito grande pode complicar substancialmente a construção da função de hashe também levar a que a dispersão seja tão grande que não se justi�que a utilização de tabelas dehash.

Cabe assim à função de hash dispersar as chaves, de preferência uniformemente, pelas posiçõesda sequência (principal).

Como já se referiu, o cardinal do conjunto de partida é muito superior ao cardinal do contradomínio, o que permite que duas, ou mais chaves, possuam uma mesma imagem no contradomínio, ou seja:

Paulo Matos

Page 108: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

4.5. Tabelas de hash 89

h(chi) = h(chj)

Desta forma, diz-se que, h é uma função não-injectiva e que houve uma colisão entre chi echj .

A qualidade da implementação das tabelas de hash passa por minimizar o número de colisõese no caso destas ocorrerem, optimizar o seu tratamento. Pode-se assim avaliar este tipo deestrutura pelas seguintes características:

• A existência de uma função h que minimize o número de colisões, sem que isso impliqueaumentar o tamanho da sequência;

• Implementação de funções e�cientes para tratamento das colisões.

4.5.1 Funcoes de hash

Como atrás se de�niu, a função de hash é responsável por converter uma qualquer chave ch,pertencente ao conjunto das chaves, num índice da sequência das informações.

Uma forma de avaliação das funções de hash, consiste em veri�car se a função distribui deforma uniforme as chaves pelos índices, minimizando assim as colisões.

É também importante que a função seja rápida a calcular o índice da sequência, pois se fordemasiado complexa pode ocorrer que demore mais tempo a determinar o índice, do que a tratarde uma eventual colisão, que pudesse ocorrer por causa de uma função menos elaborada.

É possível determinar uma função de hash que optimize este tipo de características, recorrendoa métodos numéricos e conhecendo o comportamento da procura das chaves, através de métodosestatísticos.

De forma muito genérica, pode-se resumir a ideia ao seguinte: tentar agrupar as chaves deforma a obter n (tamanho da sequência) subconjuntos, de tal forma que a procura esperada paraos vários subconjuntos seja aproximadamente igual. Em seguida atribuir a cada um destes umafracção da sequência (1/n).

Exemplo 4.1

Suponha o seguinte caso: num conjunto com seis chaves e um contra domínio de tama-nho três, pretende-se determinar uma função de hash que permita minimizar o número decolisões. O gráfico da Figura 4.46 descreve a procura esperada para cada uma das chaves:

Conjunto das chaves { a, b, c, d, e, f}Contradomínio { 1, 2, 3}

Uma solução possível consiste em determinar uma função h que permita relacionar osdados da seguinte forma:

Paulo Matos

Page 109: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

90 Capıtulo 4. Estruturas de Dados Lineares

Figura 4.46: Distribuicao esperada para a procura das chaves.

{ a, c, e} 1 => 11/30{ b, d} 2 => 9/30{f} 3 => 10/30

Esta solução, cuja representação gráfica encontra-se na Figura 4.47, permite a melhordistribuição possível, para cada uma das chaves, segundo a procura esperada.

Figura 4.47: Funcao de hash.

Como no presente exemplo o conjunto das chaves é muito reduzido, a implementação dafunção pode ser feita de forma lógica (comparando as chaves caso a caso). Mas na maioriadas vezes o conjunto das chaves é muito grande, pelo que mesmo que se consiga determinara distribuição esperada das chaves, nem sempre é possível, ou fácil, obter a função de hash,sendo por vezes necessário recorrer a métodos numéricos.

Funcoes de hash tıpicas

Existem algumas funções relativamente simples e que mesmo sendo independentes do pro-blema a resolver, permitem uma distribuição uniforme das chaves para um grande número decasos. Uma dessas funções de hash para chaves do tipo inteiro, é a seguinte:

Paulo Matos

Page 110: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

4.5. Tabelas de hash 91

h(x) = ( x mod n) +1

em que n é o tamanho de sequência e deve ser um número primo. Mas como nem sempre talé possível, pode-se então utilizar a seguinte função:

h(x) = (( x mod p) mod n) +1

onde p é o número primo maior do que n (tamanho da sequência).

Quando as chaves são do tipo string, é conveniente convertê-las para um valor decimal. Umasolução possível, consiste em somar os valores decimais dos caracteres da string, o que por si só émá solução, uma vez que corresponde a uma função que é muito pouco injectiva. Outra solução,consiste em utilizar o valor decimal do primeiro carácter, ou então calcular a média pesada dosvalores decimais dos caracteres que compõem a string.

4.5.2 Tratamento de colisoes

Independentemente da qualidade da função de hash, existe sempre a possibilidade de ocorreruma colisão, pelo que é sempre necessário implementar mecanismos que permitam lidar com estassituações. As duas soluções a seguir propostas, Linear Probing e Listas de Colisões, pretendemelucidar o funcionamento das tabelas de hash. Não são no entanto as únicas soluções possíveis.

4.5.3 Linear Probing

O Linear Probing é uma estratégia de implementação de tabelas de hash, que apenas utilizauma estrutura estática do tipo sequência. É como tal bastante rápida nos acessos, uma vez queé fácil aceder aos elementos da sequência. Infelizmente, é necessário estabelecer um majorantepara o número de elementos que a estrutura pode conter.

Este tipo de tabela de hash é implementada sobre um array (sequência) com funcionamentocircular, semelhante ao utilizado nas queue's estáticas. O array deve ter um tamanho su�ci-entemente grande para conter todos o elementos que lá se esperam inserir e encontra-se "seg-mentado"em n áreas, em que n é valor do cardinal do conjunto de chegada da função de hash(número de índices da sequência principal, ver Figura 4.45).

A função de hash ao receber uma dada chave deve devolver o índice do array correspondenteà posição dessa chave. No processo de inserção, testa-se se a posição indicada pela função dehash está ou não livre, se estiver livre o novo elemento é imediatamente inserido, caso contrário,ocorre uma colisão.

O que a estratégia de Linear Probing propõe, é que se insira o novo elemento na primeiraposição que se encontre livre, a contar da posição indicada pela função de hash. Um elementosó não é inserido, se não existir nenhuma posição livre em toda a sequência.

A Figura 4.48 representa uma tabela de hash com Linear Probing, em que a função de hashsó devolve um dos seguintes valores 0, 10, 20, ..., n-20, n - 10. Se por exemplo n, que representao tamanho da sequência, for 120, então o cardinal do conjunto de chegada é 12, ou seja, tantosquantos os valores possíveis de devolver pela função. É este valor que representa o tamanho dasequência principal da Figura 4.45.

Paulo Matos

Page 111: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

92 Capıtulo 4. Estruturas de Dados Lineares

Figura 4.48: Tabela de hash com Linear Probing.

Concretamente, a função de hash tem por objectivo segmentar o conjunto das chaves, de talforma que a cada uma faça corresponder um dos índices que pode devolver.

O espaçamento de dez em dez entre cada um desses índices, representa o número de elementosesperados para cada um dos índices. Isso não coloca no entanto qualquer impeditivo a inserirmais do que dez elementos correspondentes a um mesmo índice, uma vez que estes podem serinseridos para além da região da sequência que lhes é destinada. Também não é obrigatórioque o espaçamento entre dois índices consecutivos devolvidos pela função de hash seja sempreconstante, tudo depende da procura esperada para cada "segmento"do conjunto das chaves.

Este tipo de tabela de hash, em comparação com uma sequência simples, permite que atravésda função hash, se consiga aceder directamente à posição do array, onde há uma grande probabi-lidade de se encontrar o elemento pretendido, acelerando assim o processo de inserção. O mesmoocorre para os restantes casos (remoção e pesquisa).

Definicao formal da estrutura e dos operadores

Formalmente a estrutura é de�nida da seguinte forma:

THASH = h() x tabh: CHAVE -> Índicestab: Seq(HELEM)HELEM = ocupado x chave x infoocupado: BOOLchave: CHAVEinfo: INFO

Paulo Matos

Page 112: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

4.5. Tabelas de hash 93

Para a implementação dos algoritmos, é ainda necessário de�nir os seguintes operadores:

getTab: THASH → Seq(HELEM) // Devolve a sequênciagetOcupado: HELEM → BOOL // Devolve o valor de ocupadosetOcupado: HELEM x BOOL → HELEM // Atribui BOOL a ocupadogetChave: HELEM → CHAVE // Devolve o valor de chavesetChave: HELEM x CHAVE → HELEM // Atribui CHAVE a chavegetInfo: HELEM → INFO // Devolve o valor de infosetInfo: HELEM x INFO → HELEM // Atribui INFO a info

Insercao de um novo elemento na tabela de hash

Pode-se considerar que o algoritmo de inserção decorre em três fases: uma primeira onde sedetermina a posição onde idealmente deveria estar, ou �car, a chave que se pretende inserir;depois numa segunda fase testa-se se a chave está realmente na posição indicada pela funçãode hash. Caso tal não aconteça, então é necessário: avançar ao longo da sequência até que seencontre uma posição que já contenha o elemento que se pretende inserir; ou até encontrar umaposição livre; ou enquanto não se tiver dado a volta completa à sequência (que funciona de formacircular). Na última fase, se a posição encontrada estiver livre insere-se o elemento. No entanto,se estiver ocupada pela mesma chave que se pretende inserir, apenas se actualiza a informação.A função de inserção é então de�nida da seguinte forma:

(1) PROC InsereHLP(INOUT:tabela:THASH,ch:CHAVE,ninf:INFO):STATUS(2) VAR posinicial, pos :Índices(3) INÍCIO(4) pos ← tabela.h(ch)(5) SE ((tabela.getTab()).get(pos)).getOcupado() = SIM E(6) ((tabela.getTab()).get(pos)).getChave() 6= ch ENTÃO(7) posinicial ← pos(8) REP(9) SE pos + 1 = NMAXELEM ENTÃO(10) pos ← 0(11) SENÃO(12) pos ← pos +1(13) FSE(14) ATÉ (((tabela.getTab()).get(pos)).getOcupado() = NÃO OU(15) ((tabela.getTab()).get(pos)).getChave() = ch OU(16) pos = posinicial )(17) FSE(18) SE ((tabela.getTab()).get(pos)).getOcupado() = NÃO OU(19) ((tabela.getTab()).get(pos)).getChave() = ch ENTÃO(20) ((tabela.getTab()).get(pos)).setOcupado(SIM)(21) ((tabela.getTab()).get(pos)).setChave(ch)(22) ((tabela.getTab()).get(pos)).setInfo(ninf)(23) RET SUCESSO(24) SENÃO(25) RET INSUCESSO // Tabela de hash completa(26) FSE

Paulo Matos

Page 113: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

94 Capıtulo 4. Estruturas de Dados Lineares

(27) FIM

Pesquisa de um elemento na tabela de hash

Utilizando este processo de inserção, pode acontecer que a posição indicada por h(chi) seencontre ocupada pelo elemento chj , tal que chi 6= chj . Pelo que na pesquisa não basta acederdirectamente à posição indicada pela função de hash, é igualmente necessário testar se nessaposição se encontra realmente o elemento procurado. Caso tal não aconteça, então há quepercorrer a sequência até se encontrar a chave pretendida, ou então até completar uma volta àsequência. O algoritmo de pesquisa é então de�nido da seguinte forma:

(1) PROC PesquisaHLP(tabela:THASH,ch:CHAVE,OUT:i:INFO):STATUS(2) VAR pos, posinicial :Índices(3) INÍCIO(4) pos ← tabela.h(ch)(5) SE ((tabela.getTab()).get(pos)).getOcupado() = NÃO OU(6) ((tabela.getTab()).get(pos)).getChave() 6= ch ENTÃO(7) posinicial ← pos(8) REP(9) SE pos + 1 = NMAXELEM ENTÃO(10) pos ← 0(11) SENÃO(12) pos ← pos +1(13) FSE(14) ATÉ (pos = posinicial OU(15) (((tabela.getTab()).get(pos)).getChave() = ch E(16) ((tabela.getTab()).get(pos)).getOcupado() = SIM))(17) FSE(18) SE ((tabela.getTab()).get(pos)).getOcupado() = SIM E(19) ((tabela.getTab()).get(pos)).getChave() = ch ENTÃO(20) i ← ((tabela.getTab()).get(pos)).getInfo()(21) RET SUCESSO(22) SENÃO(23) RET INSUCESSO // Chave não faz parte da tabela(24) FSE(25) FIM

Remocao de um elemento da tabela de hash

As mesmas considerações tomadas para a pesquisa, são aplicáveis à remoção.

(1) PROC RemoverHLP(INOUT:tabela:THASH,ch:CHAVE):STATUS(2) VAR pos, posinicial :Índices(3) INÍCIO(4) pos ← tabela.h(ch)

Paulo Matos

Page 114: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

4.5. Tabelas de hash 95

(5) SE ((tabela.getTab()).get(pos)).getOcupado() = NÃO OU(6) ((tabela.getTab()).get(pos)).getChave() 6= ch ENTÃO(7) posinicial ← pos(8) REP(9) SE pos + 1 = NMAXELEM ENTÃO(10) pos ← 0(11) SENÃO(12) pos ← pos +1(13) FSE(14) ATÉ (pos = posinicial OU(15) (((tabela.getTab()).get(pos)).getChave()=ch E(16) ((tabela.getTab()).get(pos)).getOcupado() = SIM))(17) FSE(18) SE ((tabela.getTab()).get(pos)).getOcupado() = SIM E(19) ((tabela.getTab()).get(pos)).getChave() = ch ENTÃO(20) ((tabela.getTab()).get(pos)).setOcupado(NÃO)(21) ((tabela.getTab()).get(pos)).setChave(NULO)(22) RET SUCESSO(23) SENÃO(24) RET INSUCESSO // A chave não se encontra inserida(25) FSE(26) FIM

4.5.4 Listas de colisoes

O Linear Probing é uma boa estratégia quando não nos é possível utilizar estruturas dinâmicase tem como vantagem a velocidade de acesso típica dos arrays.

As tabelas de hash com listas de colisões são estruturas que, apesar de ser possível a suaimplementação sobre uma sequência de sequências (array bidimensional), recorrem normalmentea uma solução intermédia, entre o estático e o dinâmico, a qual é de�nida formalmente comouma sequência de listas. Será esta última solução a utilizada para exempli�car esta estratégia.

O funcionamento da sequência faz-se de forma normal, ao contrário da solução anterior ondeera circular. Cada elemento da sequência não é mais do que um apontador para o primeiro nodode uma lista ligada, a qual pode ser do tipo simples, ou duplamente ligada, ou mesmo outro tipode estrutura. A estrutura da tabela de hash com listas de colisões (listas simples) encontra-serepresentada na Figura 4.49.

Definicao formal da estrutura e dos operadores

Formalmente a estrutura é de�nida da seguinte forma:

THASH = h() x tabh: CHAVE -> Índicestab: Seq(NODOS)NODOS = chave x informação x next

Paulo Matos

Page 115: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

96 Capıtulo 4. Estruturas de Dados Lineares

Figura 4.49: Estrutura de uma tabela de Hash com Listas de Colisoes.

chave: CHAVEinfo: INFOnext: NODOS

É ainda necessário de�nir os seguintes operadores:

getTab: THASH → Seq(NODOS) // Devolve a sequênciagetChave: NODOS → CHAVE // Devolve o valor de chavesetChave: NODOS x CHAVE → NODOS // Atribui CHAVE a chavegetInfo: NODOS → INFO // Devolve o valor de infosetInfo: NODOS x INFO → NODOS // Atribui INFO a infogetNext: NODOS → NODOS // Devolve o valor de nextsetNext: NODOS1 x NODOS2 → NODOS // Atribui NODOS2 a next

Pesquisa de um elemento na tabela de hash

A pesquisa de um elemento numa tabela de hash com listas de colisões, consiste em determinaro índice da sequência, correspondente à chave a pesquisar. Através do índice e da sequência daslistas, acede-se ao apontador inicial da respectiva lista, bastando depois realizar uma pesquisaem tudo semelhante às pesquisas apresentadas no capítulo sobre listas simples (ver Secção 4.1.1),ou duplamente ligadas (ver Secção 4.2.1).

Os algoritmos que se apresentam em seguida, utilizam soluções recursivas para a pesquisa,inserção e remoção sobre listas, conforme o exercício proposto aos alunos para implementar sobrelistas simples.

(1) PROC PesquisarTHLC(tabela:THASH,ch:CHAVE):INFO(2) VAR pos :Índices

Paulo Matos

Page 116: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

4.5. Tabelas de hash 97

(3) INÍCIO(4) pos ← tabela.h(ch)(5) RET PesquisaLC((tabela.getTab()).get(pos),ch)(6) FIM

PesquisaLC(...) é uma função semelhante ás funções apresentadas para pesquisa sobre listassimples. No entanto, a solução aqui utilizada será do tipo recursivo. Para tal, há que identi�caras seguintes situações, tendo em conta que se convencionou que a lista está ordenada de formacrescente:

• Lista é nula,

• Lista não é nula,

• A chave a pesquisar encontra-se no primeiro nodo da lista,

• A chave a pesquisar encontra-se no resto da lista.

(1) PROC PesquisaLC(lista:NODOS,ch:CHAVE,OUT:inf:INFO):STATUS(3) INÍCIO(4) SE lista 6= NULO ENTÃO(5) SE lista.getChave() = ch ENTÃO(6) inf ← lista.getInfo()(7) RET SUCESSO(8) SENÃO(9) SE lista.getChave() < ch ENTÃO(10) RET PesquisaLC(lista.getNext(),ch,inf)(11) FSE(12) FSE(13) FSE(14) RET INSUCESSO(15) FIM

Insercao de um novo elemento na tabela de hash

O processo de inserção é muito semelhante ao da pesquisa, ou seja, dado uma chave ch,determina-se o índice i através de h(ch). Acedendo depois à posição i da sequência de listas,obtém-se o apontador inicial da lista, onde se deve inserir o novo elemento.

(1) PROC InsereTHLC(INOUT:tabela:THASH,ch:CHAVE,inf:INFO)(2) VAR pos :Índices(3) VAR p, lst :NODOS(4) INÍCIO(5) p.new()(6) p.setChave(ch)(7) p.setInfo(inf)

Paulo Matos

Page 117: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

98 Capıtulo 4. Estruturas de Dados Lineares

(8) pos ← tabela.h(ch)(9) lst ← (tabela.getTab()).get(pos)(10) lst ← InsereLC(lst,p)(11) (tabela.getTab()).set(lst,pos)(12) FIM

A inserção na lista pode-se fazer segundo uma das estratégias de�nidas anteriormente para aslistas simples, ou duplamente ligadas. No entanto a solução aqui apresentada é do tipo recursivo,onde há que ter em conta os seguintes casos:

• Lista é nula,

• Lista não é nula,

• A chave a inserir já se encontra no primeiro nodo da lista,

• A chave a inserir é inferior à do primeiro nodo da lista,

• A chave a inserir é superior à do primeiro nodo, pelo que deve então ser inserida na restantelista.

(1) PROC InsereLC(lista,p:NODOS):NODOS(2) VAR resultado, naux :NODOS(3) INÍCIO(4) SE lista = NULO ENTÃO(5) p.setNext(NULO)(6) resultado ← p(7) SENÃO(8) SE lista.getChave() = p.getChave() ENTÃO(9) lista.setInfo(p.getInfo())(10) p.free()(11) resultado ← lista(12) SENÃO(13) SE lista.getChave() > p.getChave() ENTÃO(14) p.setNext(lista)(15) resultado ← p(16) SENÃO(17) naux ← InsereLC(lista.getNext(),p)(18) lista.setNext(naux)(19) resultado ← lista(20) FSE(21) FSE(22) FSE(23) RET resultado(24) FIM

Paulo Matos

Page 118: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

4.5. Tabelas de hash 99

Remocao de um novo elemento da tabela de hash

O algoritmo de remoção é muito semelhante aos algoritmos anteriores, há apenas que ter ocuidado de actualizar os valores da sequência, para o caso em que o elemento a remover é o queestá no início da lista.

(1) PROC RemoveTHLC(INOUT:tabela:THASH,ch:CHAVE)(2) VAR pos :Índices(3) VAR lst :NODOS(4) INÍCIO(5) pos ← tabela.h(ch)(6) lst ← (tabela.getTab()).get(pos)(7) lst ← RemoveLC(lst,ch)(8) (tabela.getTab()).set(lst,pos)(9) FIM

A remoção sobre a lista também se pode fazer segundo as estratégias de�nidas anteriormentepara as listas simples, ou duplamente ligadas. Como alternativa, apresenta-se um algoritmo dotipo recursivo, onde é necessário identi�car os seguintes casos:

• Lista é nula,

• Lista não é nula,

• A chave a remover encontra-se no primeiro nodo da lista,

• A chave a remover encontra-se na restante lista.

(1) PROC RemoveLC(lista:NODOS,ch:CHAVE):NODOS(2) VAR resultado, naux :NODOS(3) INÍCIO(4) resultado ← lista(5) SE lista 6= NULO ENTÃO(6) SE lista.getChave() = ch ENTÃO(7) resultado ← lista.getNext()(8) lista.free()(9) SENÃO(10) SE lista.getChave() < ch ENTÃO(11) naux ← RemoveLC(lista.getNext(),ch)(12) lista.setNext(naux)(13) FSE(14) FSE(15) FSE(16) RET resultado(17) FIM

Paulo Matos

Page 119: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

100 Capıtulo 4. Estruturas de Dados Lineares

Exercıcios:

a) Implemente um programa para gestão dos docentes do IPB, utilizando uma tabela de hashcom a estratégia de Linear Probing. Considere que a função de hash aceita como chave onúmero do bilhete de identidade e que o cardinal do contra domínio é cinco.

A tabela de hash deve permitir armazenar o nome do docente e escola onde este lecciona({ AGRÁRIA, EDUCAÇÃO, TECNOLOGIA}).

b) Implemente um dicionário utilizando uma tabela de hash com listas de colisões. Considereque o contra domínio é de tamanho 26 (uma posição para cada letra do abecedário).

Paulo Matos

Page 120: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

Capıtulo 5Estruturas de Dados Nao Lineares

Indice

5.1 Introducao as estruturas em arvore . . . . . . . . . . . . . . . . . . . . . . 102

5.2 Arvores Binarias de Pesquisa . . . . . . . . . . . . . . . . . . . . . . . . . . 102

5.2.1 Inicializacao de um nodo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104

5.2.2 Insercao numa arvore binaria de pesquisa . . . . . . . . . . . . . . . . . . . . 104

5.2.3 Pesquisa numa arvore binaria de pesquisa . . . . . . . . . . . . . . . . . . . . 106

5.2.4 Remocao numa arvore binaria de pesquisa . . . . . . . . . . . . . . . . . . . . 107

5.2.5 Travessias de arvores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110

5.2.6 Representacao sequencial de arvores binarias . . . . . . . . . . . . . . . . . . 112

5.2.7 Peso (altura) de uma arvore binaria de pesquisa . . . . . . . . . . . . . . . . 112

5.3 Arvores Balanceadas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 114

5.3.1 Insercao em arvores balanceadas . . . . . . . . . . . . . . . . . . . . . . . . . 115

Paulo Matos 101

Page 121: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

102 Capıtulo 5. Estruturas de Dados Nao Lineares

5.1 Introducao as estruturas em arvore

A representação de dados recorrendo a estruturas em árvore (invertida) é um processo vulgarde representação nas mais variadas áreas do conhecimento. Um exemplo concreto deste tipo derepresentação, é a classi�cação dos seres vivos, onde todo e qualquer ser vivo tem que pertencera um dos reinos: vegetal, animal, monera, fungi ou protozoa, que por sua vez se dividem em �lose estes por sua vez em classes e por ai adiante, até que por �m se obtêm as espécies.

Os reinos, �los, classes, etc, constituem os nodos intermédios da árvore, ou seja, pontosdos quais rami�cam mais nodos. As espécies encontram-se representadas pelos nodos terminaisda árvore, vulgarmente designados por folhas da árvore, uma vez que, ao contrário dos nodosanteriores, não possuem descendentes.

A Figura 5.1 representa uma árvore genérica onde cada nodo possui zero, ou mais, descen-dentes.

Figura 5.1: Exemplo de uma arvore.

Por questões de organização da própria estrutura de dados, uma vez que nem sempre é fácilimplementar o processo de classi�cação dos nodos dentro da árvore e também por questões dee�ciência computacional, recorre-se a modelos mais simples de árvores. É o caso das árvoresbinárias, onde cada nodo pode possuir no máximo dois descendentes. São estas últimas árvoresque se pretende estudar com maior detalhe no decorrer desta disciplina, principalmente a suautilização como árvores de pesquisa e como árvores de decisão.

5.2 Arvores Binarias de Pesquisa

As árvores do tipo binário caracterizam-se por possuir no máximo dois descendentes por cadanodo. Em que cada descendente não é mais do que uma sub-árvore. Como no máximo existemdois descendentes, designam-se um por sub-árvore esquerda (ou nodo descendente esquerdo) eoutro por sub-árvore direita (ou nodo descendente direito), conforme ilustra a Figura 5.2.

Para o caso das árvores binárias de pesquisa, é ainda necessário existir uma chave por cadanodo, da qual depende a organização dos mesmos na árvore. De�ne-se então uma árvore binária

Paulo Matos

Page 122: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

5.2. Arvores Binarias de Pesquisa 103

Figura 5.2: Estrutura base de uma arvore binaria.

de pesquisa da seguinte forma:

Numa árvore binária de pesquisa, a chave de todo e qualquer nodo da árvore, é sempre maiordo que qualquer chave que pertença à sub-árvore esquerda do nodo e sempre menor que qualquerchave que pertença à sub-árvore direita do nodo.

A de�nição anterior resume-se à seguinte expressão:

máx.(Sub Árvore Esquerdanodoi) < Chavenodoi

< min.(Sub Árvore Direitanodoi),

∀ nodoi ∈ {Nodos da árvore}

Definicao formal das arvores binarias de pesquisa

Formalmente uma árvore binária de pesquisa é de�nida da seguinte forma:

ArvBin = chave x info x arvesq x arvdir

chave : CHAVEinfo : INFOarvesq, arvdir : ArvBin

ArvBin representa uma árvore que contém chave, info e duas sub-árvores, a arvesq e a arvdir,que representam respectivamente a sub-árvore esquerda e a sub-árvore direita.

Operadores das arvores binarias de pesquisa

Os operadores necessários à manipulação de uma estrutura do tipo ArvBin, são:

new: → ArvBin // Cria uma árvore vaziaénula: ArvBin → BOOL // Testa se a árvore é, ou não, nula.getChave: ArvBin → CHAVE // Obtém a chave do nodo.setChave: ArvBin x CHAVE → ArvBin // Atribui a chave ao nodo.getInfo: ArvBin → INFO // Obtém a informação do nodo.setInfo: ArvBin x INFO → ArvBin // Atribui a informação ao nodo.getArvDir: ArvBin → ArvBin // Obtém a sub-árvore direita.setArvDir: ArvBin x ArvBin → ArvBin // Atribui a sub-árvore direita.getArvEsq: ArvBin → ArvBin // Obtém a sub-árvore esquerda.

Paulo Matos

Page 123: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

104 Capıtulo 5. Estruturas de Dados Nao Lineares

setArvEsq: ArvBin x ArvBin → ArvBin // Atribui a sub-árvore esquerda.

5.2.1 Inicializacao de um nodo

É conveniente garantir as seguintes condições na inicialização de um nodo tipo ArvBin:

• O valor do campo chave deve corresponder a uma CHAVE nula e deve dar indicação deque o nodo não possui chave.

• Caso a informação seja composta por apontadores, garantir que o seu valor inicial é nulo.

• Garantir que ambas as sub-árvores esquerda e direita são nulas.

O algoritmo de iniciação �ca de�nido da seguinte forma:

(1) PROC New():ArvBin(2) VAR chave :CHAVE(3) VAR info :INFO(4) VAR arvesq, arvdir :ArvBin(5) INÍCIO(6) chave ← NULO(7) arvesq ← NULO(8) arvdir ← NULO(9) // Inicialização dos campos de informação(10) ...(11) RET (chave,info,arvesq,arvdir)(12) FIM

5.2.2 Insercao numa arvore binaria de pesquisa

O processo de inserção deve garantir que a organização de uma árvore binária de pesquisa semantém para todo e qualquer nodo da árvore, ou seja, que nenhuma chave da sub-árvore esquerdade um nodo seja superior à chave do próprio nodo, nem que este seja maior que qualquer chavecontida na sub-árvore direita. Com esta de�nição não se admitem elementos repetidos. Oseguinte exemplo demonstra o processo de inserção numa árvore binária de pesquisa:

Paulo Matos

Page 124: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

5.2. Arvores Binarias de Pesquisa 105

Árvore inicial.

Árvore após inserção da chave de valor 12.

Árvore após inserção da chave de valor 85.

Árvore após inserção da chave de valor 22.

Mas antes de se realizar a inserção propriamente dita, é necessário determinar a posição ondeinserir o nodo correspondente à nova chave. O que se consegue utilizando soluções recursivas,em que se compara a chave a inserir com a chave que está no nodo raíz da árvore. Se a primeirafor maior que a segunda, a inserção prossegue na sub-árvore direita, caso contrário prosseguena sub-árvore esquerda. O processo repete-se até se alcançar uma sub-árvore nula, que é ondedeverá ser inserida a nova chave.

Existe outra condição de paragem que é quando a chave do nodo raíz é igual à chave que

Paulo Matos

Page 125: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

106 Capıtulo 5. Estruturas de Dados Nao Lineares

se pretende inserir. Neste caso não há inserção propriamente dita, dado que as árvores bináriasde pesquisa não admitem chaves duplicadas, mas poderá servir para actualizar a informação donodo.

O algoritmo identi�ca as seguintes situações:

• A árvore onde inserir é nula então inserir novo nodo,

• A árvore onde inserir não é nula então:

– Se a chave a inserir é igual à chave do nodo raíz então actualizar a informação,

– Se a chave a inserir é inferior à chave do nodo raíz, então inserir na sub-árvore esquerda,

– Se a chave a inserir é superior à chave do nodo raíz, então inserir na sub-árvore direita.

Resultando no seguinte algoritmo:

(1) PROC InserirABP(arv:ArvBin,ch:CHAVE,ninfo:INFO):ArvBin(2) VAR novo :ArvBin(3) INÍCIO(4) SE arv.énula() = VERDADE ENTÃO(5) novo.new()(6) novo.setChave(ch)(7) novo.setInfo(ninfo)(8) RET novo(9) SENÃO(10) SE arv.getChave() = ch ENTÃO(11) arv.setInfo(ninfo)(12) SENÃO(13) SE arv.getChave() > ch ENTÃO(14) arv.setArvEsq(InserirABP(arv.getArvEsq(),ch,ninfo))(15) SENÃO(16) arv.setArvDir(InserirABP(arv.getArvDir(),ch,ninfo))(17) FSE(18) FSE(19) RETORNAR arv(20) FSE(21) FIM

5.2.3 Pesquisa numa arvore binaria de pesquisa

O processo de pesquisa é muito semelhante ao processo de inserção, ou seja, é necessárioencontrar o nodo com a chave pretendida, utilizando a mesma abordagem do algoritmo de inser-ção, e depois devolver a respectiva informação. O algoritmo identi�ca as mesmas situações doalgoritmo de inserção.

(1) PROC PesquisaABP(arv:ArvBin,ch:CHAVE,OUT:inf:INFO):STATUS

Paulo Matos

Page 126: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

5.2. Arvores Binarias de Pesquisa 107

(2) VAR inf :INFO(3) INÍCIO(4) SE arv.énula() = VERDADE ENTÃO(5) inf ← VAZIO(6) RET INSUCESSO(7) SENÃO(8) SE arv.getChave() = ch ENTÃO(9) inf ← arv.getInfo()(10) RET SUCESSO(11) SENÃO(12) SE arv.getChave() > ch ENTÃO(13) RET PesquisaABP(arv.getArvEsq(),ch,inf)(14) SENÃO(15) RET PesquisaABP(arv.getArvDir(),ch,inf)(16) FSE(17) FSE(18) FSE(19) FIM

5.2.4 Remocao numa arvore binaria de pesquisa

O processo de remoção é bastante simples, se os nodos a remover forem todos folhas da árvore,mas caso não o sejam, o algoritmo complica-se substancialmente, uma vez que a remoção de umnodo intermédio, ou raíz, provoca a destruição da árvore, dando origem a duas ou três árvoresindependentes. A Figura 5.3 ilustra esta situação, em que após a remoção do nodo com chave20, existem três árvores independentes .

(a) Antes da remocao da chave 20. (b) Apos a remocao da chave 20.

Figura 5.3: Exemplo da remocao de um nodo de uma arvore binaria.

Existem várias formas de lidar com esta situação, uma consiste em pegar nos elementos decada sub-árvore do nodo removido e voltá-los a inserir na árvore principal. De preferência acomeçar pelas folhas de cada sub-árvore (ex: 12, 22, 25). No entanto a solução a propor nãorecorre a esta táctica.

Paulo Matos

Page 127: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

108 Capıtulo 5. Estruturas de Dados Nao Lineares

Nos casos em que o nodo a remover possui apenas um descendente, este pode substituir opróprio nodo. Esta situação encontra-se ilustrada na Figura 5.4, onde se removeu o nodo com achave 25, colocando no seu lugar a sua sub-árvore esquerda.

(a) Antes da remocao da chave 25. (b) Apos a remocao da chave 25.

Figura 5.4: Processo de remocao de um nodo com um unico descendente.

O problema que se coloca é o que fazer nas situações em que o nodo a remover possui osdois descendentes. A solução consiste em substituir o nodo removido, pelo nodo da sub-árvoreesquerda que possui a maior chave, ou então pelo nodo da sub-árvore-direita com a menor chave.Esta situação pode ser ilustrada, utilizando como ponto de partida a árvore da Figura 5.4(a) eremovendo o nodo com a chave 20. O resultado para a primeira solução, isto é, reutilizar a maiorchave da sub-árvore esquerda, encontra-se na Figura 5.5(a). O resultado da segunda solução(reutilizar a menor chave da sub-árvore direita do nodo a remover) encontra-se na Figura 5.5(b).

(a) Reutilizacao da maior chave da

sub-arvore esquerda.

(b) Reutilizacao da menor chave da

sub-arvore direita.

Figura 5.5: Processo de remocao de um nodo com dois descendentes.

Com este conjunto de soluções é possível garantir que após a remoção de um qualquer nodo,a árvore continua a satisfazer todas as condições necessárias para poder ser classi�cada comouma árvore binária de pesquisa. O algoritmo completo utilizando a menor chave da sub-árvoredireita, possui a seguinte estrutura:

• Se existir árvore então:

– Se o nodo raiz contiver a chave a remover então

Paulo Matos

Page 128: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

5.2. Arvores Binarias de Pesquisa 109

∗ Se a sub-árvore direita do nodo raiz é nula, substituir o nodo com a sub-árvoreesquerda,

∗ Senão então se a sub-árvore esquerda do nodo raiz é nula, substituir o nodo coma sub-árvore direita,

∗ Senão substituir o nodo pelo nodo com a menor chave da sub-árvore direita,

– Caso a chave do nodo a remover seja inferior à chave do nodo raiz, então remover onodo na sub-árvore esquerda,

– Caso a chave do nodo a remover seja superior à chave do nodo raiz, então remover onodo na sub-árvore direita.

O que resulta no seguinte algoritmo:

(1) PROC RemoverABP(arv:ArvBin,ch:CHAVE):ArvBin(2) VARIÁVEL naux :ArvBin(3) VARIÁVEL chaux :CHAVE(4) VARIÁVEL infaux :INFO(5) INÍCIO(6) SE arv.énula() = VERDADE ENTÃO(7) naux ← NULO(8) SENÃO(9) SE arv.getChave() = ch ENTÃO(10) SE (arv.getArvDir()).énula() ENTÃO(11) naux ← arv.getArvEsq()(12) arv.free()(13) SENÃO(14) SE (arv.getArvEsq()).énula() ENTÃO(15) naux ← arv.getArvDir()(16) arv.free()(17) SENÃO(18) chaux ← menorChave(arv.getArvDir())(19) PesquisaABP(arv.getArvDir(),chaux,infaux)(20) arv.setChave(chaux)(21) arv.setInfo(infaux)(22) arv.setArvDir(RemoverABP(arv.getArvDir(),chaux))(23) naux ← arv(24) FSE(25) FSE(26) SENÃO(27) SE arv.getChave() > ch ENTÃO(28) arv.setArvEsq(RemoverABP(arv.getArvEsq(),ch))(29) SENÃO(30) arv.setArvDir(RemoverABP(arv.getArvDir(),ch))(31) FSE(32) naux ← arv(33) FSE(34) FSE(35) RET naux(36) FIM

Paulo Matos

Page 129: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

110 Capıtulo 5. Estruturas de Dados Nao Lineares

De notar que a algoritmo não só é recursivo, como utiliza a função PesquisarABP (...) eainda uma função designada por menorChave(...), que recebe uma árvore binária de pesquisa edevolve a menor chave contida nessa árvore. Esta última função pode ser implementada atravésdo seguinte algoritmo:

(1) PROC menorChave(arv:ArvBin):CHAVE(2) INÍCIO(3) SE arv.énula() ENTÃO(4) RET VAZIO(5) SENÃO(6) SE (arv.getArvEsq()).énula() ENTÃO(7) RET arv.getChave()(8) SENÃO(9) RET menorChave( arv.getArvEsq())(10) FSE(11) FSE(12) FIM

A remoção pode ainda ser feita da seguinte forma, onde arv representa o nodo a remover:

• Se arv possui sub-árvore direita então substituir pelo menor nodo da sub-árvore direita,

• Senão então se arv possui sub-árvore esquerda então substituir pelo maior nodo da sub-árvore esquerda,

• Senão remover simplesmente o nodo.

O que simpli�ca consideravelmente o algoritmo anteriormente proposto, sendo apenas neces-sário implementar, a mais, a função maiorChave(...).

5.2.5 Travessias de arvores

As árvores para além das funções típicas das estruturas de dados, como o inserir, remover,ou pesquisar, possuem ainda funções para realizar travessias sobre a árvore, ou seja, funções quepermitem percorrer toda a árvore segundo determinada ordem. São três as formas de travessiamais utilizadas, a saber:

• Travessia INORDER

• Travessia PREORDER

• Travessia POSORDER

Paulo Matos

Page 130: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

5.2. Arvores Binarias de Pesquisa 111

Travessia INORDER

A travessia inorder permite visitar os nodos da árvore pela ordem crescente ou decrescenteda chave, isto é, de forma ordenada. Por exemplo, para se efectuar uma travessia inorder porordem crescente, há que primeiro visitar a sub-árvore da esquerda, depois o nodo raiz e por �m asub-árvore da direita. Desta forma, a visita começa no nodo mais à esquerda da árvore e terminano nodo mais à direita.

O algoritmo encontra-se representado em seguida, onde a função processar(...), representa otratamento a dar aos nodos conforme estes são visitados.

(1) PROC Inorder( arv :ArvBin)(2) INÍCIO(3) SE NEG arv.énula() ENTÃO(4) Inorder(arv.getArvEsq())(5) processar(arv.getChave(),arv.getInfo())(6) Inorder(arv.getArvDir())(7) FSE(8) FIM

Travessia PREORDER

Numa travessia do tipo preorder, primeiro é processado o nodo raiz, depois a sub-árvoreesquerda e por �m a sub-árvore direita, permitindo assim percorrer a árvore de cima para baixoe da esquerda para a direita.

A travessia perorder é utilizado para representar os dados contidos na árvore sob a forma deuma sequência, que permita reconstituir a árvore com a sua estrutura inicial. Um caso concretode aplicação, é o de salvaguardar a informação contida na árvore em �cheiro, de forma a serpossível voltar a reconstruir a árvore com a forma inicial. A algoritmo para a travessia preorderé o seguinte

(1) PROC Preorder( arv :ArvBin)(2) INÍCIO(3) SE NEG arv.énula() ENTÃO(4) processar(arv.getChave(),arv.getInfo())(5) Preorder(arv.getArvEsq())(6) Preorder(arv.getArvDir())(7) FSE(8) FIM

Travessia POSORDER

A travessia posorder permite percorrer a árvore visitando primeiro a sub-árvore esquerda,depois a sub-árvore direita e por �m o nodo raiz. É normalmente utilizada na destruição dasárvores, ou seja, no processo de libertar todos os nodos da árvore.

Paulo Matos

Page 131: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

112 Capıtulo 5. Estruturas de Dados Nao Lineares

Figura 5.6: Representacao sequencial de uma arvore binaria.

(1) PROC Posorder( arv :ArvBin)(2) INÍCIO(3) SE NEG arv.énula() ENTÃO(4) Posorder(arv.getArvEsq())(5) Posorder(arv.getArvDir())(6) processar(arv.getChave(),arv.getInfo())(7) FSE(8) FIM

5.2.6 Representacao sequencial de arvores binarias

A implementação de árvores pode ser feita sobre estruturas dinâmicas, como até aqui semostrou, ou sobre estruturas estáticas, como por exemplo arrays. Para se conseguir implementaruma árvore sobre uma sequência, é necessário que os descendentes directos do nodo que seencontra na posição i da sequência, estejam respectivamente na posição (2 ∗ i) e (2 ∗ i + 1), istopara todo e qualquer nodo da árvore representado na sequência. A Figura 5.6 exempli�ca comose deve organizar uma árvore sobre a forma de uma sequência.

5.2.7 Peso (altura) de uma arvore binaria de pesquisa

Uma das características de uma árvore é o seu peso, ou seja, o número máximo de camadasque a árvore possui. O cálculo do peso de uma árvore é determinado pela soma de uma unidadeao maior dos pesos das suas sub-árvores. No caso da árvore ser nula então o seu peso é zero. Oalgoritmo para determinar o peso de uma árvore é o seguinte:

(1) PROC Peso(arv:ArvBin):INTEIRO(2) VAR pesq, pdir :INTEIRO(3) INÍCIO(4) SE arv.énula() ENTÃO(5) RET 0(6) SENÃO

Paulo Matos

Page 132: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

5.2. Arvores Binarias de Pesquisa 113

(7) pesq ← Peso(arv.getArvEsq())(8) pdir ← Peso( arv.getArvDir())(9) RET 1 + máximo(pesq,pdir)(10) FSE(11) FIM

Paulo Matos

Page 133: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

114 Capıtulo 5. Estruturas de Dados Nao Lineares

5.3 Arvores Balanceadas

Através dos pesos dos ramos de uma árvore é possível determinar uma outra característicadestas, o balanceamento. Uma árvore binária de pesquisa diz-se balanceada, se todos os nodosda árvore satisfazerem a seguinte condição:

|Peso(arv.getArvEsq())− Peso(arv.getArvDir())| ≤ 1

Caso a diferença dos pesos seja igual a zero, então a árvore diz-se perfeitamente balanceada.

Uma das vantagens das árvores balanceadas, é a sua maior e�ciência nos procedimentos de pes-quisa, inserção e remoção, quando comparados com os das árvores não balanceadas. Exemplos,extremos, encontram-se representados na Figura 5.7, onde a árvore da esquerda está perfeita-mente balanceada e a árvore da direita totalmente desbalanceada (semelhante a uma lista).

(a) Arvore perfeitamente balanceada. (b) Arvore nao balanceada

Figura 5.7: Exemplos de arvores balanceadas e nao balanceadas.

Através da Figura 5.7 é fácil constatar que na melhor das situações, isto é, quando a chavea pesquisar estar na raíz da árvore, a e�ciência é semelhante para ambas as árvores. O que jánão acontece quando a chave a pesquisar corresponde à folha de maior profundidade. Na árvorebalanceada basta efectuar três comparações/invocações para se alcançar o nodo, enquanto nooutro caso são necessárias mais duas comparações/invocações (praticamente o dobro).

A ordem de complexidade do algoritmo de pesquisa numa árvore balanceada é de O(log n), emque n representa o número de nodos da árvore. Enquanto numa árvore não balanceada, a ordemde complexidade é de O(n). Signi�ca isto que para valores de n elevados, o tempo necessáriopara uma pesquisa (em termos médios) pode ser substancialmente menor caso a árvore estejabalanceada.

A desvantagem de manter as árvores balanceadas, reside na complexidade das soluções, no-meadamente para inserir e remover elementos da árvore. Soluções essas que também são menose�cientes comparativamente com as utilizadas nas árvores não binárias. Assim, a opção de man-

Paulo Matos

Page 134: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

5.3. Arvores Balanceadas 115

ter uma árvore balanceada deverá ser tomada considerando o número de elementos que a árvoredeverá comportar. Quanto maior, mais vantagens haverá em manter as árvores balanceadas.

Definicao formal das ABP balanceadas

Em relação às árvores binárias de pesquisa não balanceadas, apenas é necessário acrescentarum campo, o bal, que assinala o valor do balanço para cada nodo, isto é, a diferença entre o pesoda sub-árvore esquerda e da sub-árvore direita.

ArvBin = chave x bal x info x arvesq x arvdir

chave : CHAVEbal : INTEIROinfo : INFOarvesq, arvdir : ArvBin

Basta como tal de�nir mais dois novos operadores:

getBal: ArvBin → INTEIRO // Devolve o valor de balsetBal: ArvBin x INTEIRO → ArvBin // Atribui INTEIRO a bal

Uma árvore só é considerada como estando balanceada se o valor de bal estiver compreendidoentre -1 e 1, para todos os nodos da árvore.

5.3.1 Insercao em arvores balanceadas

A primeira questão que se levanta é: Como é possível inserir um novo elemento numa árvorede forma a que esta se mantenha balanceada?

Parte-se do princípio que a árvore está balanceada e que a nova chave pode, ou não desbalan-cear a árvore. Na primeira situação nada há a fazer, uma vez que a árvore se mantém balanceada,no segundo caso é necessário identi�car o ramo que está a provocar o desbalanceamento, ou seja,identi�car a posição onde o nodo é inserido, para que se possa proceder à reordenação dos ramosde forma a que a árvore volte a �car balanceada.

Dois casos em que a inserção de um novo nodo, não só não provoca qualquer desiquilibrio naárvore, como até reforça o balanceamento, encontram-se representados nas Figuras 5.8 e 5.9.

É ainda possível inserir um novo nodo, que apesar de localmente não provocar qualquerdesbalanceamento, o pode fazer no total da árvore, as Figuras 5.10 e 5.11 ilustram dois dessescasos. Se a árvore no seu todo corresponder às árvores iniciais dos exemplos das �guras, não hádesbalanceamento, mas se forem apenas parte de uma árvore, ou seja, uma sub-árvore, então épossível existir.

A Figura 5.12 ilustra as duas situações, utilizando para tal o caso da Figura 5.10 que quandoisolado e por si só não está desbalanceado. No entanto quando integrado numa árvore pode pro-vocar desbalanceamento, como ocorre na Figura 5.12(a), ou não (como mostra a Figura 5.12(b)).

Paulo Matos

Page 135: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

116 Capıtulo 5. Estruturas de Dados Nao Lineares

Figura 5.8: Insercao a direita numa (sub) arvore com balanco de 1.

Figura 5.9: Insercao a esquerda numa (sub) arvore com balanco de -1.

Figura 5.10: Insercao a direita numa (sub) arvore com balanco de 0.

Figura 5.11: Insercao a esquerda numa (sub) arvore com balanco de 0.

Paulo Matos

Page 136: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

5.3. Arvores Balanceadas 117

(a) Sub-arvore provoca o desbalanceamento. (b) Sub-arvore nao provoca o desbalanceamento

Figura 5.12: Exemplos em que uma sub-arvore balanceada pode ou nao provocar desbalancea-mento na arvore principal.

Figura 5.13: Desbalanceamento esquerda-esquerda.

O exemplo ilustrado na Figura 5.12(a) (árvore à esquerda), é um dos quatro casos que podedesbalancear uma árvore. Estas situações ocorrem quando o balanço desta é de 1, ou de -1, ese pretende inserir um novo nodo na sub-árvore de maior peso. É novamente de salientar que,mesmo nestas condições, a inserção de um novo nodo nem sempre provoca o desbalancear daárvore (ver exemplos das Figuras 5.8 e 5.9).

As Figuras 5.13, 5.14, 5.15 e 5.16 ilustram as quatro situações em que pode ocorrer desbalan-ceamento. As partes envoltas por um quadrado sublinhado correspondem aos casos ilustradosatravés da Figura 5.10 e da Figura 5.11.

Para cada uma das formas de desbalanceamento atrás descritas, existe um procedimentopróprio para voltar a balancear, que são os seguintes:

• (Des)Balanceamento Esquerda-Esquerda

• (Des)Balanceamento Esquerda-Direita

Paulo Matos

Page 137: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

118 Capıtulo 5. Estruturas de Dados Nao Lineares

Figura 5.14: Desbalanceamento esquerda-direita.

Figura 5.15: Desbalanceamento direita-direita.

• (Des)Balanceamento Direita-Esquerda

• (Des)Balanceamento Direita- Direita

Os processos de balanceamento são descritos de forma generalizada nas secções seguintes,após as quais se apresenta o algoritmo completo para a inserção em árvores balanceadas.

(Des)Balanceamento Esquerda-Esquerda

Esta situação caracteriza-se por:

• Nodo raiz com bal igual a 1,

• A nova chave é inserida à esquerda->esquerda do nodo raiz.

A situação encontra-se representada na Figura 5.17, onde novo representa o nodo com a chavea inserir e arv representa a árvore onde inserir.

Paulo Matos

Page 138: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

5.3. Arvores Balanceadas 119

Figura 5.16: Desbalanceamento direita-esquerda.

Figura 5.17: Arvore antes da insercao do novo do nodo.

Paulo Matos

Page 139: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

120 Capıtulo 5. Estruturas de Dados Nao Lineares

Figura 5.18: Arvore apos a insercao do nodo.

Figura 5.19: Arvore apos o balanceamento.

De notar que a nova chave, devido ao seu valor, será inserida na sub-árvore esquerda do nodocom a chave 20, ou seja, em arvEE, resultando na árvore da Figura 5.18, que como se podeveri�car está desbalanceada.

Para se voltar a balancear a árvore há que realizar a reordenação da mesma, conforme seencontra ilustrado na Figura 5.19.

O procedimento correspondente é o seguinte:

(1) PROC BalanceamentoEE(arv:ArvBin):ArvBin(2) VAR nesq :ArvBin(3) INÍCIO(4) nesq ← arv.getArvEsq()(5) arv.setArvEsq(nesq.getArvDir())(6) arv.setBal(0)

Paulo Matos

Page 140: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

5.3. Arvores Balanceadas 121

Figura 5.20: Arvore antes da insercao do novo nodo.

(7) nesq.setArvDir(arv)(8) nesq.setBal(0)(9) arv ← nesq(10) RET arv(11) FIM

(Des)Balanceamento Esquerda-Direita

Esta situação caracteriza-se por:

• Nodo raiz com bal igual a 1,

• A sub-árvore esquerda com bal igual a 0

• A nova chave é inserida à esquerda'direita do nodo raiz.

A situação encontra-se representada na Figura 5.20, onde novo representa o nodo com a chavea inserir e arv representa a árvore onde inserir.

De notar que a nova chave, devido ao seu valor, será inserida na sub-árvore esquerda do nodocom a chave 28, ou seja, em arvEDE, resultando na árvore da Figura 5.21.

A reestruturação a realizar para este caso encontra-se ilustrada na Figura 5.22.

Ainda dentro deste tipo de desbalanceamento, pode ocorrer que a chave a inserir �que nasub-árvore direita do nodo com a chave 28. Nesta situação, apesar da árvore inicial ser a mesma,o resultado é ligeiramente diferente, como se pode veri�car pela Figura 5.23.

No entanto a forma de balancear é igual para as duas situações, surgem apenas algumasdiferenças nos valores de bal, como se pode veri�car pela Figura 5.24.

Paulo Matos

Page 141: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

122 Capıtulo 5. Estruturas de Dados Nao Lineares

Figura 5.21: Arvore apos a insercao do nodo sem balanceamento.

Figura 5.22: Arvore apos o balanceamento.

Paulo Matos

Page 142: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

5.3. Arvores Balanceadas 123

Figura 5.23: Arvore apos a insercao do nodo sem balanceamento.

Figura 5.24: Arvore apos o balanceamento.

Paulo Matos

Page 143: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

124 Capıtulo 5. Estruturas de Dados Nao Lineares

Nestas condições utiliza-se o seguinte algoritmo para conseguir balancear novamente a árvore:

(1) PROC BalanceamentoED(arv:ArvBin):ArvBin(2) VAR nesq, nesqdir :ArvBin(3) INÍCIO(4) nesq ← arv.getArvEsq()(5) nesqdir ← nesq.getArvDir()(6) nesq.setArvDir(nesqdir.getArvEsq())(7) arv.setArvEsq(nesqdir.getArvDir())(8) SE nesqdir.getBal() = -1 ENTÃO(9) arv.setBal(0)(10) nesq.setBal(1)(11) SENÃO(12) SE nesqdir.getBal() = 0 ENTÃO(13) arv.setBal(0)(14) nesq.setBal(0)(15) SENÃO(16) SE nesqdir.getBal() = 1 ENTÃO(17) arv.setBal(-1)(18) nesq.setBal(0)(19) FSE(20) FSE(21) FSE(22) nesqdir.setArvEsq(nesq)(23) nesqdir.setArvDir(arv)(24) nesqdir.setBal(0)(25) arv ← nesqdir(26) RET arv(27) FIM

(Des)Balanceamento Direita-Esquerda

Esta situação caracteriza-se por:

• Nodo raiz com bal igual a -1,

• A sub-árvore direita com bal igual a 0

• A nova chave é inserida à direita->esquerda do nodo raiz.

A situação encontra-se representada na Figura 5.25, onde novo representa o nodo com a chavea inserir e arv representa a árvore onde inserir.

De notar que a nova chave, devido ao seu valor, será inserida na sub-árvore esquerda do nodocom a chave 35, ou seja, em arvDEE, resultando na árvore da Figura 5.26.

Para que a árvore �que balanceada há que realizar a reestruturação da Figura 5.27.

Paulo Matos

Page 144: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

5.3. Arvores Balanceadas 125

Figura 5.25: Arvore antes da insercao do novo nodo.

Figura 5.26: Arvore apos a insercao do nodo sem balanceamento.

Paulo Matos

Page 145: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

126 Capıtulo 5. Estruturas de Dados Nao Lineares

Figura 5.27: Arvore apos o balanceamento.

Caso a chave a inserir �que posicionada na sub-árvore direita do nodo com a chave 35, comose encontra ilustrado na Figura 5.28, então a reestruturação é muito semelhante ao exemploanterior, alterando-se apenas os valores do balanço, como se pode veri�car pela Figura 5.29.

Nestas condições utiliza-se o seguinte algoritmo para conseguir balancear novamente a árvore:

(1) PROC BalanceamentoDE(arv:ArvBin):ArvBin(2) VAR ndir, ndiresq :ArvBin(3) INÍCIO(4) ndir ← arv.getArvDir()(5) ndiresq ← ndir.getArvEsq()(6) ndir.setArvEsq(ndiresq.getArvDir())(7) arv.setArvDir(ndiresq.getArvEsq())(8) SE ndiresq.getBal() = -1 ENTÃO(9) arv.setBal(1)(10) ndir.setBal(0)(11) SENÃO(12) SE ndiresq.getBal() = 0 ENTÃO(13) arv.setBal(0)(14) ndir.setBal(0)(15) SENÃO SE ndiresq.getBal() = 1 ENTÃO(16) arv.setBal(0)(17) ndir.setBal(-1)(18) FSE(19) FSE(20) FSE(21) ndiresq.setArvDir(ndir)(22) ndiresq.setArvEsq(arv)(23) ndiresq.setBal(0)(24) arv ← ndiresq(25) RET arv

Paulo Matos

Page 146: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

5.3. Arvores Balanceadas 127

Figura 5.28: Arvore apos a insercao do nodo sem balanceamento.

Figura 5.29: Arvore apos o balanceamento.

Paulo Matos

Page 147: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

128 Capıtulo 5. Estruturas de Dados Nao Lineares

Figura 5.30: Arvore antes da insercao do novo nodo.

(26) FIM

(Des)Balanceamento Direita-Direita

O balanceamento direita-direita caracteriza-se por:

• Nodo raiz com bal igual a -1,

• A nova chave é inserida à direita'direita do nodo raiz.

A Figura 5.30 ilustra esta situação, onde novo representa o nodo com a chave a inserir e arvrepresenta a árvore onde inserir.

A nova chave, devido ao seu valor, é inserida na sub-árvore direita do nodo com a chave 40,ou seja, em arvDD, resultando na árvore da Figura 5.31.

A reestruturação da árvore encontra-se representada na Figura 5.32.

O que corresponde ao seguinte algoritmo:

(1) PROC BalanceamentoDD(arv:ArvBin):ArvBin(2) VAR ndir :ArvBin(3) INÍCIO(4) ndir ← arv.getArvDir()(5) arv.setArvDir(ndir.getArvEsq())(6) arv.setBal(0)(7) ndir.setArvEsq(arv)(8) ndir.setBal(0)(9) arv ← ndir(10) RET arv(11) FIM

Paulo Matos

Page 148: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

5.3. Arvores Balanceadas 129

Figura 5.31: Arvore apos a insercao do nodo sem balanceamento.

Figura 5.32: Arvore apos o balanceamento.

Paulo Matos

Page 149: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

130 Capıtulo 5. Estruturas de Dados Nao Lineares

Algoritmo final

O algoritmo �nal parte do princípio que a árvore está balanceada e utiliza os mesmos princípiosque o algoritmo de inserção em árvores não balanceadas. Existe no entanto mais um parâmetrode saída (alt), que permite assinalar se houve ou não, alterações em profundidade na árvore (ousub-árvores). Por defeito o seu valor é falso e só passa a verdadeiro quando se insere realmente onodo. A partir daí o valor é propagado para os nodos ascendentes, assinalando que é necessárioproceder aos ajustes dos balanços, ou eventualmente reestruturar a árvore. À semelhança dosalgoritmos até aqui apresentados, também este é do tipo recursivo.

(1) PROC InsereBalanceado(arv:ArvBin,INOUT:alt:Booleano,ch:CHAVE, ninfo:INFO)(2) :ArvBin(3) VAR novo, naux :ArvBin(4) INÍCIO(5) SE arv.énula() ENTÃO(6) novo.new()(7) novo.setChave(ch)(8) novo.setInfo(ninfo)(9) novo.setBal(0)(10) alt ← VERDADEIRO(11) arv ← novo(12) SENÃO(13) SE arv.getChave() = ch ENTÃO(14) arv.setInfo(ninfo)(15) SENÃO(16) SE arv.getChave() >ch ENTÃO(17) naux=InsereBalanceado(arv.getArvEsq(),alt,ch,ninfo)(18) arv.setArvEsq(naux)(19) SE alt = VERDADEIRO ENTÃO(20) SE arv.getBal() = 1 ENTÃO(21) arv ← BalancearEsq(arv)(22) alt ← FALSO(23) SENÃO(24) SE arv.getBal() = 0 ENTÃO(25) arv.setBal(1)(26) SENÃO(27) arv.setBal(0)(28) alt ← FALSO(29) FSE(30) FSE(31) FSE(32) SENÃO(33) naux=InsereBalanceado(arv.getArvDir(),alt,ch,ninfo)(34) arv.setArvDir(naux)(35) SE alt = VERDADEIRO ENTÃO(36) SE arv.getBal() = 1 ENTÃO(37) arv.setBal(0)(38) alt ← FALSO(39) SENÃO(40) SE arv.getBal() = 0 ENTÃO

Paulo Matos

Page 150: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

5.3. Arvores Balanceadas 131

(41) arv.setBal(-1)(42) SENÃO(43) arv ← BalancearDir(arv)(44) alt ← FALSO(45) FSE(46) FSE(47) FSE(48) FSE(49) FSE(50) FSE(51) RET arv(52) FIM

Os processos de reestruturação encontram-se agrupados mediante sejam à esquerda ou àdireita da árvore. O algoritmo correspondente ao primeiro caso é o seguinte:

(1) PROC BalancearEsq(arv :ArvBin):ArvBin(2) VAR nesq :ArvBin(3) INÍCIO(4) nesq ← arv.getArvEsq()(5) SE nesq.getBal() = -1 ENTÃO(6) arv ← BalanceamentoED(arv)(7) SENÃO(8) arv ← BalanceamentoEE(arv)(9) FSE(10) RET arv(11) FIM

No segundo caso, ou seja, se a reestruturação se realizar à direita da árvore, então o algoritmoé o seguinte:

(1) PROC BalancearDir(arv:ArvBin):ArvBin(2) VAR ndir :ArvBin(3) INÍCIO(4) ndir ← arv.getArvDir()(5) SE ndir.getBal() = -1 ENTÃO(6) arv ← BalanceamentoDD(arv)(7) SENÃO(8) arv ← BalanceamentoDE(arv)(9) FSE(10) RET arv(11) FIM

As considerações aqui feitas para o processo de inserção também devem ser utilizadas noprocesso de remoção, pois também esta operação pode desbalancear a árvore. A implementaçãoda rotina para remoção com balanceamento �ca para os alunos implementarem para casa.

Paulo Matos

Page 151: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

132 Capıtulo 5. Estruturas de Dados Nao Lineares

Paulo Matos

Page 152: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

Capıtulo 6Grafos

Indice

6.1 Definicoes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 134

6.2 Operacoes sobre Grafos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 137

6.2.1 Uniao . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 138

6.2.2 Composicao . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 139

6.2.3 Exponenciacao . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 139

6.2.4 Fecho Transitivo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140

6.2.5 Fecho de Kleen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 141

6.3 Representacao de Grafos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 141

6.3.1 Matriz de Adjacencias . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 142

6.3.2 Listas de Adjacencias . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 146

6.4 Exemplo da definicao de grafos em linguagem C . . . . . . . . . . . . . . . 149

6.4.1 Matriz de adjacencias . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 150

6.4.2 Lista de adjacencias . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 152

6.5 Algoritmos para grafos nao pesados . . . . . . . . . . . . . . . . . . . . . . 153

Paulo Matos 133

Page 153: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

134 Capıtulo 6. Grafos

6.5.1 Se ha caminho entre dois vertices . . . . . . . . . . . . . . . . . . . . . . . . . 154

6.5.2 Qual o caminho entre dois vertices . . . . . . . . . . . . . . . . . . . . . . . . 155

6.5.3 Alcancaveis de um vertice . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 156

6.5.4 Os que alcancam um vertice . . . . . . . . . . . . . . . . . . . . . . . . . . . . 157

6.5.5 Fecho Transitivo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 157

6.6 Travessia de grafos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 158

6.6.1 Depth-First . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 158

6.6.2 Breadth First . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 159

6.6.3 Iterative Deepening . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 160

6.6.4 Ordem Topologica . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161

6.6.5 Arvore geradora de um grafo (Spanning Tree) . . . . . . . . . . . . . . . . . . 162

6.7 Grafos Pesados . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163

6.7.1 Representacao de grafos pesados . . . . . . . . . . . . . . . . . . . . . . . . . 163

6.7.2 Algoritmos para grafos pesados . . . . . . . . . . . . . . . . . . . . . . . . . . 164

Os grafos são uma das estruturas mais genéricas de dados que é possível conceber. Da mesmaforma que se pode ver uma lista (simples) como um caso particular de uma árvore, esta por suavez pode ser vista como um caso particular de um grafo.

Informalmente pode-se dizer que os grafos são compostos por vértices e por ramos, em queos vértices são, à semelhança das estruturas até aqui apresentadas, uma espécie de nodos e osramos as ligações entre estes.

6.1 Definicoes

Grafo

De�ne-se então formalmente um grafo G como sendo um par (V,R), em que V é o conjunto�nito dos vértices do grafo e R uma relação binária de V para V , de tal forma que se (vi, vj) ∈ R,então é porque há um ramo com origem em vi e destino em vj . A Figura 6.1 representa o grafoG = (V,R), a utilizar para exempli�car as de�nições posteriores.

Figura 6.1: Exemplo de um grafo.

Paulo Matos

Page 154: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

6.1. Definicoes 135

Grafo Completo

Um grafo pode ser classi�cado como completo, se:

∀vx, vy ∈ V, (vx, vy) ∈ R (6.1)

O que não é o caso do grafo da �gura anterior, uma vez que não contém todos os ramospossíveis.

Subgrafo

Pode-se ainda de�nir G′ = (V ′, R′) como subgrafo de G = (V,R), caso se veri�que que:

V ′ ⊆ V ∧R′ ⊆ R (6.2)

Por exemplo:

G′ = {V ′, R′}V ′ = {v2, v3, v4, v5} e R′ = {RA, RB, RC , RD}

Caminho

Diz-se ainda que há caminho entre vx e vz e representa-se por vx ∼> vz, se e só se:

(vx, vz) ∈ R∨∃ vy ∈ V : vy 6= vx ∧ vy 6= vz ∧ (vx, vy) ∈ R ∧ vy ∼> vz

(6.3)

Caminho Simples

Um caminho vi ∼> vj diz-se simples, se se comprovar que para todos os vértices contidosnesse caminho, a seguinte expressão é verdadeira:

∀i, j, i 6= j ⇒ vi 6= vj , em que vi, vi+1, ...vj−1, vj ∈ vi ∼> vj (6.4)

Caminho Cıclico

Um caminho vi ∼> vj diz-se cíclico, se e só se:

Paulo Matos

Page 155: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

136 Capıtulo 6. Grafos

vi ∼> vj−1 e um caminho simples ∧ vi = vj , em que vi, vi+1, ...vj−1, vj ∈ vi ∼> vj (6.5)

Alcancaveis

Diz-se ainda que se existe um caminho entre os vértices vi e vj , então vi alcança vj . Aoconjunto de todos os vértices alcançáveis por vi, designa-se por alcançáveis de vi e representa-sepor Alcançáveis(vi). Formalmente o conjunto é de�nido da seguinte forma:

Alcancaveis(vi) = {vx ∈ V : vi ∼> vx} (6.6)

Por exemplo, para o grafo da Figura 6.1, os alcançáveis de v3 (Alcançáveis(v3)) são {v2, v3, v4, v5}.

Grafo Ligado

Um grafo diz-se ligado, se e só se entre dois quaisquer vértices desse grafo existir sempre umcaminho.

∀ vi, vj ∈ V, vi ∼> vj (6.7)

O grafo G da Figura 6.1, é um exemplo de um grafo não ligado, uma vez que nem sempreexiste caminho entre quaisquer dois vértices. Já o grafo G′ que se segue, de�nido com base emG, é um grafo ligado:

G′ = {V ′, R′}V ′ = {v2, v3, v4, v5} e R′ = {RA, RB, RC , RD}

Grafo Orientado

Se para todo e qualquer par de vértices vi e vj de um grafo G tal que vi 6= vj , se veri�carque (vi, vj) e (vj , vi), caso existam, são dois ramos distintos de G, então diz-se que G é um grafoorientado. Caso contrário G é um grafo não orientado.

∀ vi, vj ∈ V, vi 6= vj ⇒ (vi, vj) 6= (vj , vi) (6.8)

Nesta situação, diz-se que um qualquer ramo (vi, vj) tem início no vértice vi e �m no vérticevj . A Figura 6.2 representa um grafo orientado, o qual servirá para exempli�car as de�niçõesque se seguem.

Paulo Matos

Page 156: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

6.2. Operacoes sobre Grafos 137

Figura 6.2: Exemplo de um grafo orientado.

Antecessor e Sucessor

Se (vi, vj) pertencer ao conjunto de ramos de um grafo orientado, diz-se então que:

• vi é antecessor de vj

• vj é sucessor de vi

De�ne-se então o conjunto dos antecessores do vértice vi, que se representa por Antecessores(vi),da seguinte forma:

Antecessores(vi) = {vx ∈ V : (vx, vi) ∈ R} (6.9)

E o conjunto dos sucessores do vértice vi, que se representa por Sucessores(vi), da seguinteforma:

Sucessores(vi) = {vx ∈ V : (vi, vx) ∈ R} (6.10)

Grafo Identidade

Designa-se por grafo identidade a I = (V,R′), se para todo e qualquer ramo (vi, vj) ∈ R′ severi�car que vi = vj .

A Figura 6.3 representa o grafo identidade de G (grafo da Figura 6.2).

6.2 Operacoes sobre Grafos

Para ilustrar a execução das várias operações, utilizar-se-á os grafos G1 e G2 da Figura 6.4.

Paulo Matos

Page 157: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

138 Capıtulo 6. Grafos

Figura 6.3: Representacao do grafo identidade de G.

Figura 6.4: Grafos exemplo.

Sejam então G = (V,R), G′ = (V,R′) e G′′ = (V,R′) três grafos orientados e I o grafoidentidade.

6.2.1 Uniao

Seja G a união entre G′ e G′′, que se representa por:

G = G′ ∪G′′ (6.11)

tal que:

R = R′ ∪R′′ = {(vx, vy) : (vx, vy) ∈ R′ ∨ (vx, vy) ∈ R′′} (6.12)

A Figura 6.5, utilizando os grafos G1 e G2 anteriormente de�nidos, ilustra a união de G1 comG2 (G1 ∪G2).

Paulo Matos

Page 158: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

6.2. Operacoes sobre Grafos 139

Figura 6.5: Uniao do grafo G1 e G2.

6.2.2 Composicao

Seja G a composição entre G′ e G′′, que se representa por:

G = G′ ◦G′′ (6.13)

tal que:

R = {(vx, vy) : ∃ va ∈ V, (vx, va) ∈ R′′ ∧ (va, vy) ∈ R′} (6.14)

A Figura 6.6, utilizando os grafos G1 e G2 anteriormente de�nidos, ilustra a composição deG1 ◦G2 e de G2 ◦G1.

(a) G1 ◦ G2 (b) G2 ◦ G1

Figura 6.6: Composicao dos grafos G1 e G2.

6.2.3 Exponenciacao

Seja Gn o expoente n de G, que resulta de:

Gn =

I Se n = 0

G ◦Gn−1 Se n > 0(6.15)

Paulo Matos

Page 159: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

140 Capıtulo 6. Grafos

O mesmo é dizer que existe um ramo (vx, vy) em Gn, se e só se, existir em G um caminho devx para vy composto por n ramos.

Os expoentes G11, G2

1 e G31 estão representados, respectivamente, nas Figuras 6.7(a) , 6.7(b) e

6.8.

(a) Expoente um de G1 (G11). (b) Expoente dois de G1 (G2

1).

Figura 6.7: Expoentes de G1.

Figura 6.8: Expoente tres de G1 (G31).

Através da Figura 6.8 é possível con�rmar que a relação entre G1 e G11 é que os ramos

deste último representam todos os caminhos de G1 formados por um único ramo. Já a relaçãode G1 com G2

1 é que os ramos representam todos os caminhos de G1 formados por dois ramos.Generalizando, pode-se dizer que os ramos de Gn

1 representam todos os caminhos de G1 formadospor n ramos.

6.2.4 Fecho Transitivo

O fecho transitivo de G, que é representado por G+, resulta de:

G+ =∞⋃

n=1

Gn (6.16)

Uma vez que o fecho transitivo é a reunião de todos os expoentes de G, entre 1 e in�nito,então o simples facto de existir um ramo (vi, vj) em G+ prova, por si só, que em G existe umcaminho entre vi e vj . O fecho transitivo do grafo G1 encontra-se representado na Figura 6.9.

Paulo Matos

Page 160: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

6.3. Representacao de Grafos 141

Figura 6.9: Fecho transitivo de G1 (G+1 ).

6.2.5 Fecho de Kleen

O fecho de Kleen de G, representa-se por G∗ e resulta de:

G∗ =∞⋃

n=0

Gn = I ∪G+ (6.17)

O fecho de Kleen, ao reunir o grafo identidade ao fecho transitivo, mais não faz do que consi-derar que é sempre possível alcançar um vértice a partir dele próprio. A Figura 6.10 representao fecho de Kleen do grafo G1.

Figura 6.10: Fecho de Kleen de G1 (G∗1).

6.3 Representacao de Grafos

Mesmo com os poucos conhecimentos até aqui fornecidos, não é difícil perceber a potenci-alidade que os grafos possuem como forma de representação. Infelizmente tais potencialidadestambém acarretam custos, um dos quais encontra-se na forma de representar um grafo. É que,como já foi possível veri�car, o número de ramos que podem sair de um vértice, varia entre zeroe o número total de vértices do grafo, o que elimina a utilização de estruturas estáticas comoforma de representar cada ramo, à semelhança dos campos arvesq e arvdir das árvores binárias.Mesmo a utilização de arrays de "ramos"é bastante penosa, uma vez que seria necessário possuirpara cada vértice, um array com tantos elementos quantos os vértices do grafo. Não é como talfácil obter uma solução consensual.

De seguida, apresentam-se as duas formas de representação de grafos mais utilizadas: a Matriz

Paulo Matos

Page 161: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

142 Capıtulo 6. Grafos

de Adjacências e a Listas de Adjacências.

6.3.1 Matriz de Adjacencias

A matriz de adjacências é uma das formas mais simples de representar um grafo. Consiste emutilizar uma matriz bidimensional, onde cada um dos índices da matriz representa um vértice. Auma das dimensões da matriz estão associados os vértices de onde partem os ramos (os vérticesorigem), e à outra dimensão estão associados os vértices onde terminam os ramos (vérticesdestino).

Cada elemento da matriz é no mínimo do tipo booleano e indica se entre um vértice origeme um vértice destino existe, ou não, um ramo. Pode no entanto ser necessário guardar maisinformação referente a cada ramo, por exemplo, o peso do ramo (que pode traduzir uma distância,ou custo de execução) e nesse caso cada elemento da matriz é uma estrutura de informação.

A matriz de adjacências do grafo G2 da Figura 6.4, encontra-se representada na Figura 6.11,onde se optou por assinalar a presença dos ramos através do valor um e a sua ausência atravésdo valor zero.

Figura 6.11: Matriz de adjacencias de G2.

Este tipo de representação por si só, pode não ser su�ciente para representar toda a informaçãorelevante de um grafo, como aquela que caracteriza cada um dos vértices. Daí a ser vulgar associarà matriz de adjacências um array de tamanho n (número de vértices do grafo) com a informaçãosobre cada um dos vértices, conforme ilustra a Figura 6.12.

De notar que apesar desta forma de representação ser fácil de implementar e de simpli�car osalgoritmos tipicamente utilizados com grafos, é extremamente ine�ciente em relação ao espaçoque ocupa, uma vez que obriga a ter uma matriz de nxn elementos (em que n representa onúmero total de vértices do grafo), em que a maioria desses elementos não contém informaçãoútil.

Em compensação a implementação de determinados algoritmos é extremamente simples, porexemplo para se determinar o conjunto dos antecessores de um vértice, basta detectar os elemen-tos da matriz cujo destino é o vértice em causa e que assinalam a presença de ramos.

Antes da apresentação de alguns exemplos de utilização de matrizes de adjacências, é conve-niente de�nir formalmente o que é uma matriz bidimensional.

Paulo Matos

Page 162: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

6.3. Representacao de Grafos 143

Figura 6.12: Representacao completa de G2.

Em capítulos anteriores de�niu-se uma estrutura abstracta, designada por Sequência, quepretendia representar estruturas de dados lineares indexadas, vulgarmente designadas por vec-tores, ou matrizes unidimensionais. Para todos os efeitos pode-se considerar que uma matrizbidimensional não é mais do que uma Sequência de Sequências.

A representação formal completa de um grafo segundo uma matriz de adjacências é a seguinte:

Grafo = svert x matrizsvert : Set(Vértice)matriz : MatrizB(Ramo) ≡ Seq(Seq(Ramo))Ramo = Tuplo que permite caracterizar os ramosVértice = Tuplo que permite caracterizar os vértices

Quando se utilizam matrizes de adjacências, tipicamente, Ramo não é mais do que umavariável do tipo booleano. Para simpli�car a representação dos algoritmos de�nem-se ainda osseguintes operadores:

new: → Grafo // Cria um grafo vazioget: Grafo x Vértice1 x Vértice2 → Ramo // Devolve a informação sobre

// o ramo (Vértice1,Vértice2)set: Grafo x Ramo x Vértice1 x Vértice2 → Grafo // Insere o ramo

// (Vértice1,Vértice2)addVert: Grafo x Vértice → Grafo // Acrescenta um vérticeremVert: Grafo x Vértice → Grafo // Remove um vérticeVértices: Grafo → Set(Vértice) // Devolve o conjunto

// dos vértices do grafo

Como exemplo, apresenta-se de seguida o algoritmo que permite determinar os sucessores deum dado vértice:

(1) PROC SucessoresMA(g:Grafo,vs:Vértice):Set(Vértice)(2) VAR saux :Set(Vértice)(3) VAR vx :Vértice

Paulo Matos

Page 163: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

144 Capıtulo 6. Grafos

(4) INÍCIO(5) saux.new()(6) PARA ∀ vx ∈ Vértices(g) FAZER(7) SE g.get(vs,vx) 6= NULO ENTÃO(8) saux ← saux ∪ {vx}(9) FSE(10) FPARA(11) RET saux

(12) FIM

De forma semelhante, determina-se o conjunto dos antecessores de um vértice.

O seguinte exemplo descreve a solução para realizar a composição de dois grafos com base emmatrizes de adjacências:

(1) PROC ComposiçãoMA(g1,g2:Grafo):Grafo(2) VAR gaux :Grafo(3) VAR vx, vy, vz:Vértice(4) VAR b :BOOL // RAMO(5) INÍCIO(6) gaux.new()(7) PARA ∀ vx ∈ Vértices(g1) FAZER(8) PARA ∀ vz ∈ Vértices(g1) FAZER(9) gaux.set(FALSO,vx,vz)(10) PARA ∀ vy ∈ Vértices(g1) FAZER(11) b ← gaux.get(vx,vz) ∨ (g2.get(vx,vy) ∧ g1.get(vy,vz))(12) gaux.set(b,vx,vz)(13) FPARA(14) FPARA(15) FPARA(16) RET gaux

(17) FIM

Como é possível con�rmar pelos exemplos anteriores, a representação algorítmica sobre grafosé bastante abstracta, pelo que ao nível da implementação prática é necessário ter em atençãodeterminados aspectos tais como identi�car claramente o que é um vértice, o que designa ovértice e o que é o índice do vértice. De notar que a informação sobre um vértice pode serbastante elaborada, por exemplo se o grafo servir para descrever um mapa, onde cada vérticerepresenta uma localidade, então podemos ter como informação o nome da localidade, o númerode habitantes, área dessa localidade, etc. Caso seja necessário referenciar uma dada localidadenão é muito fácil ter que dizer por exemplo: Vila Nova de Gaia, 1200000 habitantes, 25 km2,etc, uma vez que por vezes um só campo de informação serve perfeitamente para identi�car ovértice em causa. Daí a surgir a necessidade de de�nir um tipo de dado que identi�que o campoque serve para designar o vértice, aqui representado por DVértice.

Segundo esta perspectiva um Vértice é de�nido da seguinte forma:

Vértice = dvert x ...

Paulo Matos

Page 164: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

6.3. Representacao de Grafos 145

dvert : DVertice

De onde surge o seguinte operador:

getDVert: Grafo x Vértice → DVértice // Devolve a designação do vértice

De notar no entanto que nem a designação de um vértice, nem a informação completa sobre omesmo (Vértice) permitem saber qual a sua posição relativa na matriz de adjacências ou no arraydos vértices. Para tal é necessário de�nir um terceiro tipo de dado, o IVértice, que representa oíndice (na matriz e no array) do vértice.

A relação entre o IVértice e o DVértice é de�nida pela seguinte função:

Desg2Ind: Grafo x DVértice → IVértice // Converte a designação de um// vértice no respectivo índice

A função Desg2Ind mais não faz do que percorrer o array de vértices à procura do vérticecuja designação é DVértice, devolvendo o respectivo índice da matriz.

Tendo em conta a separação entre o que é um vértice, do que é a sua designação e o seu índice,torna-se necessário rede�nir operadores base. Por exemplo para o caso do get(...):

get: Grafo x DVértice1 x DVértice2 → Ramo // Devolve o ramo// (DVértice1,DVértice2)

(1) PROC get(g:Grafo,dv1:DVértice,dv2:DVértice):Ramo(2) VAR iv1, iv2 :IVértice(3) INÍCIO(4) iv1 ← Desg2Ind(g,dv1)(5) iv2 ← Desg2Ind(g,dv2)(6) RET g.matriz.get(iv1,iv2) // matriz: representa a matriz do grafo(7) // matriz.get(...):operação de get sobre a matriz(8) FIM

Para o caso do set(...) seria necessário a seguinte rede�nição:

gset: Grafo x Ramo x DVértice1 x DVértice2 → Grafo // Insere o ramo// (DVértice1,DVértice2)

Paulo Matos

Page 165: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

146 Capıtulo 6. Grafos

(1) PROC set(g:Grafo,r:Ramo,dv1:DVértice,dv2:DVértice):Ramo(2) VAR iv1, iv2:IVértice(3) INÍCIO(4) iv1 ← Desg2Ind(g,dv1)(5) iv2 ← Desg2Ind(g,dv2)(6) RET g.matriz.set(r,iv1,iv2) // matriz: representa a matriz do grafo(7) // matriz.set(...): operação de set sobre a matriz(8) FIM

A utilização dos três tipos de dados Vértice, DVértice e IVértice pode ser ilustrada atravésdo seguinte algoritmo que permite determinar os sucessores de um dado vértice.

(1) PROC SucessoresMA(g:Grafo,dvs:DVértice):Set(DVértice)(2) VAR saux :Set(DVértice)(3) VAR vx :Vértice(4) VAR dvx :DVértice(5) INÍCIO(6) saux.new()(7) PARA ∀ vx ∈ Vértices(g) FAZER(8) dvx ← g.getDVert(vx)(9) SE g.get(dvs,dvx) 6= NULO ENTÃO(10) saux ← saux ∪ {dvx}(11) FSE(12) FPARA(13) RET saux

(14) FIM

De notar que não houve necessidade de utilizar o IVértice, uma vez que tal �cou implícito nooperador get(...).

6.3.2 Listas de Adjacencias

As listas de adjacências são uma outra forma de representação de grafos, que faz uso deestruturas mistas, estáticas e dinâmicas, para ultrapassar os defeitos inerentes às matrizes deadjacências.

A estrutura tem por base um array de listas, semelhante ao utilizado nas tabelas de hash,onde apenas a �loso�a de funcionamento é que difere, uma vez que não existe necessidade deuma função de hash (h(ch) = i).

Cada posição do array armazena a informação referente a um vértice. A esta informação estáassociada uma lista cujos elementos referenciam os vértices adjacentes (caso se trate de um grafonão orientado), ou os vértices sucessores (caso se trate de um grafo orientado).

Por exemplo o grafo G2 da Figura 6.4 é representado pela lista de adjacências da Figura 6.13.

Paulo Matos

Page 166: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

6.3. Representacao de Grafos 147

Figura 6.13: Listas de adjacencias.

De notar que a lista de adjacências, por si só, permite representar toda a informação relevantedo grafo, quer a que está associada aos ramos, quer a que está associado aos vértices.

É fácil de perceber que a implementação de determinados algoritmos sobre esta forma derepresentação é mais complicada para algumas situações, como é o caso do algoritmo para de-terminar os antecessores de um vértice. Existem no entanto outros casos em que os algoritmosse simpli�cam, como é o caso do algoritmo para determinar os sucessores de um vértice.

Mas para o caso de grafos muito grandes, em que normalmente a relação entre o númerode ramos que partem de um vértice e o número total de vértices é muito baixa, esta forma derepresentação pode tornar-se substancialmente mais compacta do que a matriz de adjacências.

Formalmente esta estrutura é de�nida como base numa Sequência de listas, ou seja:

Grafo = Seq(Vértice)Vértice = cvert x lsucccvert : VERT // Descreve a informação do vérticelsucc : NRAMO // Lista dos ramos que partem do vérticeNRAMO = info x nextinfo : RAMO // Descreve a informação do ramonext : NRAMO

Esta forma de representação acaba por ser menos formal que a anterior, pelo que se tornamesmo necessário separar nitidamente o que é o vértice, o que designa o vértice e o que é o índicedo vértice. Assim V ERT é um tipo estruturado formado pelos seguintes campos:

VERT = dvert x ...dvert : DVértice

Poderá ser necessário existir um operador, em que dada a designação de um vértice devolvea respectiva informação (incluindo lista de adjacentes):

getIVert: Grafo x DVértice → Vértice

Paulo Matos

Page 167: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

148 Capıtulo 6. Grafos

Existe também o tipo IVértice, obtido através da seguinte função:

Desg2Ind: Grafo x DVértice → IVértice // Converte a designação de um// vértice no respectivo índice

De notar ainda que a estrutura RAMO caracteriza todo um ramo, incluindo o vértice destino.Não é no entanto necessário que este salvaguarde toda a informação sobre o vértice destino,mas apenas alguma forma de o referenciar, como por exemplo, um campo do tipo IVértice ouDVértice.

RAMO = ... x vdestvdest : IVértice

Tornando-se necessário de�nir o seguinte operador:

getVDest: RAMO → IVértice // Devolve o índice do vértice destino

É assim possível de�nir, com algum detalhe, os operadores fundamentais a esta forma derepresentar grafos:

new: → Grafoget: Grafo x IVértice → Vértice // Devolve a informação

// associada a IVérticeaddVert: Grafo x Vértice → GraforemVert: Grafo x Vértice → GrafoaddRamo: Grafo x Vértice x Vértice → GraforemRamo: Grafo x Vértice x Vértice → GrafogetSucc: Grafo x DVértice → NRAMO // Devolve a lista de ramos

// que partem de DVértice

Desta forma, e apenas a título de exemplo, o algoritmo para determinar os sucessores de umvértice é o seguinte:

(1) PROC SucessoresLA(g:Grafo,dvs:DVértice):Set(IVértices)(2) VAR saux :Set(IVértice)(3) VAR naux :NRAMO(4) INÍCIO(5) saux.new()(6) naux ← g.getSucc(dvs)(7) ENQ naux 6= NULO FAZER(8) saux ← saux ∪ naux.getVDest()(9) naux ← naux.getNext()

Paulo Matos

Page 168: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

6.4. Exemplo da definicao de grafos em linguagem C 149

(10) FENQ(11) RET saux

(12) FIM

Que eventualmente pode ser resumido à seguinte forma:

(1) PROC SucessoresLA(g:Grafo,dvs:DVértice):NRAMO(2) INÍCIO(3) RET g.getSucc(dvs)(4) FIM

O algoritmo para determinar os antecessores, como já se disse, complica-se substancialmente.

(1) PROC AntecessoresLA(g:Grafo,dvd:DVértice):Set(IVértices)(2) VAR saux :Set(IVértice)(3) VAR vx :Vértice(4) VAR dvx :DVértice(5) VAR naux :NRAMO(6) VAR i :IVértice(7) INÍCIO(8) saux.new()(9) PARA i = 0 ATÉ NTOTALVERT FAZER(10) vx ← g.get(i)(11) dvx ← g.getDVert(vx)(12) naux ← g.getSucc(dvx)(13) ENQ naux 6= NULO ∧ naux.getVDest() 6= g.Desg2Ind(dvd) FAZER(14) naux ← naux.getNext()(15) FENQ(16) SE naux 6= NULO ENTÃO(17) saux ← saux ∪ {g.Desg2Ind(dvx)} // g.Desg2Ind(dvx) ≡ i(18) FSE(19) FPARA(20) RET saux

(21) FIM

6.4 Exemplo da definicao de grafos em linguagem C

A Figura 6.14 representa um mapa que servirá para ilustrar como se deve proceder paradescrever um grafo em linguagem C. Cada vértice representa uma cidade que contém dois camposde informação: o nome da cidade e a população. Cada ramo representa uma ligação entre duascidades e contém também dois campos de informação: o designação da ligação (estrada) e arespectiva distância.

Paulo Matos

Page 169: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

150 Capıtulo 6. Grafos

Figura 6.14: Mapa a utilizar como exemplo.

6.4.1 Matriz de adjacencias

A seguinte solução é puramente estática, isto é, o número de vértices do grafo tem queobrigatoriamente ser de�nido a priori.

#define NVERTICES 100 // Número de vértices do grafo#define TDVERTICE 10 // No de caracteres da designação do vértice#define TDRAMO 10 // No de caracteres da designação dos ramostypedef enumFALSE, TRUE BOOL;typedef char[TDRAMO] DRamo; // Tipo da designação do ramotypedef struct ramo{ // Definição do tipo Ramo

BOOL eramo; // Indica se existe ou não ramoDRamo dramo;int distancia;

} Ramo;typedef char[TDVERTICE] DVertice; // Tipo da designação do vérticetypedef int IVertice; // Definição do tipo IVérticetypedef struct vertice{ // Definição do tipo Vértice

DVertice dvert;int populacao;

} Vertice;typedef struct grafo{ // Definição do tipo grafo

Vertice svert[NVERTICES];Ramo matriz[NVERTICES][NVERTICES];

} Grafo;

O operador new(...), para este tipo de de�nição, deve alocar dinamicamente o grafo e preencheros ramos de forma a assinalar que por defeito não existem ramos (eramo = FALSE).

É ainda possível utilizar uma solução, onde o número de vértices do grafo pode ser de�nidodinamicamente.

#define TDVERTICE 10 // No de caracteres da designação do vértice

Paulo Matos

Page 170: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

6.4. Exemplo da definicao de grafos em linguagem C 151

#define TDRAMO 10 // No de caracteres da designação dos ramostypedef enumFALSE, TRUE BOOL;typedef char[TDRAMO] DRamo; // Tipo da designação do ramotypedef struct ramo{ // Definição do tipo Ramo

BOOL eramo; // Indica se existe ou não ramoDRamo dramo;int distancia;

} Ramo;typedef char[TDVERTICE] DVertice; // Tipo da designação do vérticetypedef int IVertice; // Definição do tipo IVérticetypedef struct vertice{ // Definição do tipo Vértice

DVertice dvert;int populacao;

} Vertice;typedef struct grafo{ // Definição do tipo grafo

int nvert; // No de vértices do grafoVertice *svert;Ramo *matriz;

} Grafo;

É de notar que a linguagem C, requer para arrays multidimensionais, como é o caso da matrizda estrutura Grafo, que se declare o tamanho de cada uma das suas dimensões (eventualmentecom excepção da última). Pelo que na de�nição anterior, expressões do tipo:

Grafo g;g.matriz[2][3] = ...;

não são exequíveis. Este problema pode ser ultrapassado de�nindo os operadores de acesso àmatriz bidimensional, nomeadamente: um para ler valores da matriz (get); e outro para escrevervalores na matriz (set).

Ramo *get(Grafo *g,IVertice ivx,IVertice ivy){// Operação equivalente a g->matriz[ivx][ivy]

int pos;if(g){

pos = ivx*g->nvert + ivy;return &(g->matriz[pos]);

}return NULL;

}void set(Grafo *g,Ramo *r,IVertice ivx,IVertice ivy){// Operação equivalente a g->matriz[ivx][ivy]

int pos;if(g){

pos = ivx*g->nvert + ivy;g->matriz[pos]= *r;

}

Paulo Matos

Page 171: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

152 Capıtulo 6. Grafos

}

O operador new(...) recebe como parâmetro o número de vértices do grafo (número máximode vértices) e devolve um apontador para um grafo pronto a utilizar:

Grafo *new(int nvert){int i;Grafo *g = (Grafo*)malloc(sizeof(Grafo));if(g){

g->nvert = nvert;g->svert = (Vertice*) malloc(sizeof(Vertice)*nvert);g->matriz = (Ramo*) malloc(sizeof(Ramo)*nvert*nvert);for(i=0;i<nvert*nvert;i++) g->matriz[i].eramo = FALSE;

}return g;

}

6.4.2 Lista de adjacencias

Apresenta-se em primeiro lugar a solução estática, isto é, a solução para a qual o número devértices do grafo tem que obrigatoriamente ser de�nido a priori.

#define NVERTICES 100 // Número de vértices do grafo#define TDVERTICE 10 // No de caracteres da designação do vértice#define TDRAMO 10 // No de caracteres da designação dos ramostypedef enumFALSE, TRUE BOOL;typedef char[TDRAMO] DRamo; // Tipo da designação do ramotypedef int IVertice; // Definição do tipo IVérticetypedef struct ramo{ // Definição do tipo Ramo

DRamo dramo;int distancia;IVertice vdest;

} Ramo;typedef struct nramo{

Ramo info;struct nramo *next;

} NRAMO;typedef char[TDVERTICE] DVertice; // Tipo da designação do vérticetypedef struct vert{

DVertice dvert;int populacao;

} VERT;typedef struct vertice{ // Definição do tipo Vértice

VERT cvert;NRAMO lsucc;

} Vertice;

Paulo Matos

Page 172: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

6.5. Algoritmos para grafos nao pesados 153

typedef Vertice Grafo[NVERTICES];

O operador new(...) é responsável por colocar o apontador lsucc de cada um dos vértices anulo.

A versão dinâmica consiste em de�nir o grafo da seguinte forma:

typedef struct grafo{int nvert;Vertice *svert;

} Grafo;

6.5 Algoritmos para grafos nao pesados

Não é certamente difícil perceber a potencialidade que os grafos têm quando comparados comas estruturas de dados mais convencionais, como as listas ou as árvores. Apesar da diversidadede situações onde é possível utilizar grafos, a verdade é que uma grande parte das operações quese realizam sobre estes, são facilmente reutilizáveis e muitos dos problemas recaem sobre um dosseguintes casos:

• Determinar se existe caminho ou qual o melhor caminho entre dois vértices;

• Determinar a sequência pela qual os vértices de um grafo devem ser processados com�nalidade de alcançar um determinado objectivo;

• Colorir os vértices do grafo segundo determinado conjunto de restrições;

• Obter o caminho que permite percorrer todos os vértices do grafo, passando uma única vezpor cada um;

• Reconhecimento de padrões em grafos;

• Reescrita de grafos por forma a optimizar determinado critério.

Existe como tal um grande número de algoritmos que são típicos dos grafos e com base nosquais é possível resolver alguns dos problemas anteriores, são eles os seguintes:

• Determinar se há caminho entre vi e vj (vi ∼> vj).

• Determinar o caminho entre dois vértices vi e vj (vi ∼> vj).

• Determinar os alcançáveis de um vértice.

• Determinar os que alcançam determinado vértice.

• Determinar o fecho transitivo de um grafo.

Paulo Matos

Page 173: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

154 Capıtulo 6. Grafos

• Determinar a ordem topológica de um grafo.

• Determinar a Spanning Tree (Mínima).

• Resolver problemas do tipo vendedor ambulante.

NOTA: As soluções a seguir apresentadas são independentes do tipo de estrutura utilizadapara representar os grafos. O tipo Vértice serve para representar e para designar vértices dografo.

6.5.1 Se ha caminho entre dois vertices

O algoritmo para determinar se existe caminho entre dois vértices vi e vj , tem por base aprópria de�nição de caminho, ou seja, existe caminho entre vi e vj (vi ∼> vj), se e só se:

(vx, vz) ∈ R∨∃ vy ∈ V : vy 6= vx ∧ vy 6= vz ∧ (vx, vy) ∈ R ∧ vy ∼> vz

O procedimento aceita como parâmetros o grafo, o vértice inicial e �nal, e devolve um booleanoa indicar se existe, ou não caminho.

Para evitar que o processo de resolução �que preso nos potenciais caminhos cíclicos do grafo,utiliza-se um quarto parâmetro, o visit, cuja �nalidade é assinalar os vértices já visitados. Asolução é a seguinte:

(1) PROC CaminhoI(g:Grafo,vi,vj:Vértice):BOOL(2) VAR visit :Set(Vértices)(3) INÍCIO(4) visit.new()(5) RET CaminhoR(g,vi,vj,visit)(6) FIM(7)

(8) PROC CaminhoR(g:Grafo,vi,vj:Vértice,INOUT:visit:Set(Vértices)):BOOL(9) VAR res :BOOL(10) VAR vx :Vértice(11) INÍCIO(12) visit ← visit ∪ {vi}(13) SE vj ∈ Sucessores(vi) ENTÃO(14) res ← VERDADEIRO(15) SENÃO(16) res ← FALSO(17) PARA ∀ vx ∈ Sucessores(vi) FAZER(18) SE vx /∈ visit FAZER(19) res ← CaminhoR(g,vx,vj,visit)(20) SE res = VERDADEIRO ENTÃO(21) RET res(22) FSE(23) FSE(24) FPARA

Paulo Matos

Page 174: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

6.5. Algoritmos para grafos nao pesados 155

(25) FSE(26) RET res(27) FIM

6.5.2 Qual o caminho entre dois vertices

Este algoritmo tem por base o algoritmo para determinar se há caminho entre dois vértices(Secção 6.5.1), só que agora pretende-se conhecer o caminho entre dois vértices e não apenas seeste existe. Para tal utiliza-se mais um parâmetro, que serve para devolver a sequência (cam)que contém os vértices a percorrer entre o vértice origem e o vértice destino.

A ideia consiste em fazer com que o algoritmo quando alcança o vértice destino comece pordevolver uma sequência formada pelo próprio vértice destino:

(1) cam ← <vf>

Como a solução é recursiva, ao voltar para a invocação anterior, constrói-se o caminho, con-catenando o vértice inicial, da instância actual da função, com a sequência devolvida:

(1) cam ← <vi, restc>

Onde restc representa o caminho devolvido pela invocação da função recursiva (CaminhoR(...)):

(1) res ← CaminhoR(g,vx,vf,visit,restc)

Pode-se por assim dizer que o caminho é construído do �m para o início. A solução completaé a seguinte:

(1) PROC CaminhoI(g:Grafo, vi,vf:Vértices,OUT: cam:Seq(Vértices)) :BOOL(1) VAR visit :Set(Vértices)(2) INÍCIO(3) visit.new()(4) cam ← <>(5) RET CaminhoR(g,vi,vf,visit,cam)(6) FIM(7)

(8) PROC CaminhoR(g:Grafo, vi,vf:Vértices,(9) INOUT: visit:Set(Vértices),OUT: cam:Seq(Vértices)) :BOOL(10) VAR res :BOOL(11) VAR vx :Vértice(12) VAR restc :Seq(Vértices)

Paulo Matos

Page 175: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

156 Capıtulo 6. Grafos

(13) INÍCIO(14) visit ← visit ∪ {vi}(15) SE vi = vf ENTÃO(16) cam ← <vf>(17) res ← TRUE(18) SENÃO(19) res ← FALSE(20) PARA ∀ vx ∈ Sucessores(vi) FAZER(21) SE vx /∈ visit ENTÃO(22) res ← CaminhoR(g,vx,vf,visit,restc)(23) SE res = TRUE ENTÃO(24) cam ← <vi,restc>(25) RET res(26) FSE(27) FSE(28) FPARA(29) FSE(30) RET res(31) FIM

6.5.3 Alcancaveis de um vertice

O conjunto dos alcançáveis é determinado sabendo que se um sucessor é um alcançável de umvértice, logo os seus sucessores também são alcançáveis desse vértice. De onde se pode obter aseguinte solução recursiva:

(1) PROC AlcançáveisI(g:Grafo,vi:Vértice):Set(Vértices)(2) VAR Alc :Set(Vértices)(3) INÍCIO(4) Alc.new()(5) RET AlcançáveisR(g,vi,Alc)(6) FIM(7)

(8) PROC AlcançáveisR(g:Grafo, vi:Vértice, Alc:Set(Vértices)) :Set(Vértices)(9) VAR vx :Vértice(10) INÍCIO(11) PARA ∀ vx ∈ Sucessores(vi) FAZER(12) SE vx /∈ Alc ENTÃO(13) Alc ← Alc ∪ {vx}(14) Alc ← AlcançáveisR(g,vx,Alc)(15) FSE(16) FPARA(17) RET Alc(18) FIM

Paulo Matos

Page 176: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

6.5. Algoritmos para grafos nao pesados 157

6.5.4 Os que alcancam um vertice

Como este é um problema muito semelhante ao anterior, só que em vez de se utilizar ossucessores, utilizam-se os antecessores, a sua resolução �ca como proposta para trabalho de casa.

6.5.5 Fecho Transitivo

O fecho transitivo permite obter um grafo G′ cujos ramos representam todos os caminhospossíveis, entre dois quaisquer vértices de G. O algoritmo tem por base a equação do cálculo dofecho transitivo (Eq. 6.16), envolvendo assim o cálculo do expoente (Eq. 6.15) e da reunião degrafos (Eq. 6.12).

O algoritmo proposto, vai realizando a reunião dos Gn, para n = 1, 2, ..., enquanto esta opera-ção produzir alterações na solução �nal. Quando tal já não acontecer, então está determinado ofecho transitivo. Para tal, o algoritmo recorre às operações de exponenciação e de reunião entregrafos.

(1) PROC FechoTransitivoE(g:Grafo):Grafo(2) VAR gres, gaux :Grafo(3) VAR i :INTEIRO(4) INÍCIO(5) gres.new()(6) gaux.new()(7) REP(8) gres ← gaux

(9) gaux ← gres ∪ Exponencião(g,i)(10) i ← i + 1(11) ATÉ gaux = gres

(12) RET gres

(13) FIM

De notar que o algoritmo proposto não é muito ine�ciente, uma vez que recorre à opera-ção de exponenciação, quando tal pode ser feito utilizando-se os resultados anteriores de Gn

conjuntamente com a operação de composição, da seguinte forma:

(1) PROC FechoTransitivoC(g:Grafo):Grafo(2) VAR gres, gaux, gcomp :Grafo(3) INÍCIO(4) gres.new()(5) gaux.new()(6) gcomp.new()(7) gcomp ← Composição(g,I)(8) REP(9) gres ← gaux

(10) gaux ← gres ∪ gcomp

(11) gcomp ← Composição(g,gcomp)(12) ATÉ gaux = gres

Paulo Matos

Page 177: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

158 Capıtulo 6. Grafos

(13) RET gres

(14) FIM

É possível implementar uma solução com base no algoritmo para determinar o caminho entredois vértices, uma vez que os ramos do grafo que resulta do fecho transitivo, assinalam a existên-cia, ou não, de caminho entre quaisquer dois vértices do grafo original. A implementação destasolução �ca como proposta de trabalho.

6.6 Travessia de grafos

À semelhança das árvores, os grafos também permitem travessias. No entanto o processoé mais complicado uma vez que podem existir vários sucessores para cada vértice, bem comosituações de caminhos cíclicos.

Para garantir que qualquer forma de travessia não �que em ciclo in�nito, devido aos caminhoscíclicos, é necessário controlar os vértices já processados, como aliás já se fez nos algoritmos paradeterminar o caminho entre vértices.

As formas de travessia mais utilizadas são: o Depth First e o Breadth First, que também sãoaplicadas na pesquisa de soluções em grafos, isto é, servem para determinar se a partir de umdado vértice, designado por vértice origem, é possível alcançar um vértice objectivo (destino).

6.6.1 Depth-First

Este tipo de estratégia, também designada por travessia/pesquisa em profundidade, está nabase da solução utilizada para determinar o caminho entre dois vértices. A ideia consiste emprocessar o vértice inicial e depois, de forma recursiva, cada um dos seus sucessores. A ordemsegundo a qual se processam os sucessores depende apenas da implementação, não existindoqualquer restrição por parte do algoritmo. No entanto a utilização de diferentes critérios podedar origem a resultados distintos.

A Figura 6.15 representa um grafo e ilustra o processo de travessia para duas possíveis situa-ções, na primeira os sucessores são processados da esquerda para a direita, enquanto na segundapela ordem inversa.

De notar que os grafos G1 e G2 da Figura 6.15 possuem, na realidade, uma estrutura emforma de árvore, por isso e devido ao processo pela qual são obtidos, são vulgarmente designadospor Spanning Trees de G.

O algoritmo para esta forma de travessia é o seguinte:

(1) PROC DepthFirstI(g:Grafo,vi:Vértice)(2) VAR visit :Set(Vértices)(3) INÍCIO(4) visit.new()(5) DepthFirstR(g,vi,visit)

Paulo Matos

Page 178: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

6.6. Travessia de grafos 159

Figura 6.15: Exemplo de duas travessias Depth First sobre o mesmo grafo.

(6) FIM(7)

(8) PROC DepthFirstR(g:Grafo,vi:Vértice,INOUT:visit:Set(Vértices))(9) VAR vx :Vértice(10) INÍCIO(11) visit ← visit ∪ {vi}(12) processar(vi)(13) PARA ∀ vx ∈ Sucessores(vi) FAZER(14) SE vx /∈ visit ENTÃO(15) DepthFirstR(g,vx,visit)(16) FSE(17) FPARA(18) FIM

Onde processar(...) representa os procedimentos a executar para cada vértice ao longo datravessia.

6.6.2 Breadth First

O Breadth First, também designado por travessia/pesquisa em largura, começa por processarem primeiro lugar o vértice inicial, depois todos os seus sucessores e em seguida os sucessoresdestes e daí por diante, garantindo assim que qualquer vértice é processado antes de qualquerum dos seus descendentes, quer estes sejam sucessores, ou sucessores dos sucessores. A ordempela qual são escolhidos os sucessores de cada um dos vértices é determinante para os resultadosobtidos. O algoritmo é o seguinte:

(1) PROC BreathFirst(g:Grafo,vi:Vértice)(2) VAR visit :Set(Vértices)(3) VAR q :Queue(Véritces)(4) VAR vx, vy :Vértice(5) INÍCIO

Paulo Matos

Page 179: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

160 Capıtulo 6. Grafos

(6) visit.new()(7) q.new()(8) visit ← {vi}(9) q.QInsert(vi)(10) ENQ q.QEmpty() = FALSO FAZER(11) vx ← q.QRemove()(12) processar(vx)(13) PARA ∀ vy ∈ Sucessores(vx) FAZER(14) SE vy /∈ visit ENTÃO(15) q.QInsert(vy)(16) visit ← visit ∪ {vy}(17) FSE(18) FPARA(19) FENQ(20) FIM

Como exercício propõe-se que indique a ordem segundo a qual os dois grafos da Figura 6.16são processados, para ambas formas de travessia, caso estas se iniciem pelo vértice 1 e pelo vértice4.

Figura 6.16: Grafos para os exercıcios.

6.6.3 Iterative Deepening

Esta é uma táctica de pesquisa de soluções (e não de travessia) que tem por base o que sedesigna por um Depth-First com limite de profundidade, ou seja, um Depth-First em que existeum parâmetro que controla a profundidade da travessia/pesquisa.

O Iterative Deepening o que faz é invocar o Depth-First com limite de profundidade, come-çando por uma profundidade de 1 e aumentando sucessivamente essa profundidade, até alcançara solução pretendida.

Paulo Matos

Page 180: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

6.6. Travessia de grafos 161

6.6.4 Ordem Topologica

Seja G = (V,R) um grafo orientado não-cíclico, diz-se então que a ordem topológica de Gé uma sequência composta pelos vértices de G, de tal forma que, para todo e qualquer par devértices v1 e v2, tal que (v1, v2) ∈ R, v1 precede v2 na ordem da sequência.

A ordem topológica é útil quando se utilizam grafos para representar problemas de precedên-cias, como no caso dos processos de construção ou de produção. A Figura 6.18 representa umapossível sequência da ordem topológica do grafo da Figura 6.17.

Figura 6.17: Grafo de precedencias.

Figura 6.18: Ordem topologica do grafo da Figura 6.17.

Para obter a ordem topológica há que obter um vértice do grafo, aqui designado por vi, quenão possua antecessores, o qual passa a ser o primeiro elemento da sequência, depois remove-sevi do grafo e todos os ramos que dele partem e volta-se a realizar o mesmo procedimento paraobter o segundo elemento. O algoritmo é o seguinte:

(1) PROC OrdemTopológica(g:Grafo):Seq(Vértices)(2) VAR s :Seq(Vértices)(3) VAR q :Grafo(4) VAR vx :Vértice(5) INÍCIO(6) s.new()(7) q.new()(8) q ← g(9) ENQ q.Vértices() 6= ∅ FAZER(10) vx ← VérticeSemAntecessores(q)(11) q ← RemoverRamos(q,vx)(12) q.Vértices ← q.Vértices \ {vx}(13) s ← <s,vx>(14) FENQ(15) RET s

Paulo Matos

Page 181: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

162 Capıtulo 6. Grafos

(16) FIM

6.6.5 Arvore geradora de um grafo (Spanning Tree)

Seja G = (V,R), um grafo ligado, não-orientado, de�ne-se como árvore geradora de G, umsub-grafo de G, em que dados dois quaisquer vértices vx e vy, existe um só caminho entre vx evy.

A árvore geradora é obtida recorrendo, por exemplo, a um processo de travessia do tipoDepth-First, ou Breadth-First, sendo a sua estrutura dependente da técnica utilizada e da ordempela qual se processam os sucessores de cada vértice.

Na Figura 6.19 está representado um grafo G e duas possíveis árvores geradores, obtidasutilizando como estratégia o Depth-First. A árvore T1 é obtida processando em primeiro lugaros sucessores de menor índice, enquanto que na árvore T2 processam-se em primeiro lugar ossucessores de maior índice.

Figura 6.19: Grafo G e respectivas arvores geradoras por Depth-First.

Na Figura 6.20 encontram-se representadas duas soluções que utilizam o Breadth-First. Naárvore T3 processaram-se em primeiro os sucessores de menor índice e na T4 os de maior.

Figura 6.20: Arvores geradoras por Breadth-First do grafo G da Figura 6.19.

Paulo Matos

Page 182: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

6.7. Grafos Pesados 163

6.7 Grafos Pesados

Um grafo pesado não é mais do que um grafo cujos ramos possuem um peso, ou custo. Estadiferença permite alargar o leque de aplicações deste tipo de estrutura, servindo por exemplo, pararepresentar mapas em que os vértices são cidades/vilas e os ramos possíveis ligações rodoviárias,onde o custo pode ser o número de quilómetros a percorrer, ou a qualidade da estrada (auto, ip,ic, nacional, etc).

Formalmente de�ne-se um grafo pesado como sendo um triplo, tal que G =< V, R, fc >, emque V representa o conjunto dos vértices, R o conjunto dos ramos e fc é uma função, em quedados dois vértices pertencentes a V , retorna o custo associado ao ramo que os liga.

fc : V xV ′ → Custo/Peso (6.18)

O peso pode ser um qualquer valor do tipo P , desde que respeite as seguintes propriedades:

• Deve conter um operador ⊕, tal que:

∀(vx, vy), (vy, vz) ∈ R, fc((vx, vy)) = p1, fc((vy, vz)) = p2 ⇒ Custo(< vx, vy, vz >) = p1⊕p2

(6.19)

O qual deverá ser:

– Associativo;

– Possuir um elemento absorvente, que corresponde ao peso de um caminho inexistente,aqui representado por ∞;

– E eventualmente, um elemento neutro que representa o custo de ir do vértice V paraele próprio, aqui representado por 0.

• Qualquer elemento de P deve respeitar a seguinte condição:∀p ∈ P, 0 ≤ ∞p.

6.7.1 Representacao de grafos pesados

Os grafos pesados utilizam o mesmo tipo de estrutura dos grafos não-pesados, isto é matrizesde adjacências ou listas de adjacências, só que agora há que associar a informação referente aopeso do grafo.

No caso das matrizes de adjacências, onde cada elemento representa um ramo, que assinalaa presença ou ausência, do mesmo (entre eventualmente outras coisas), passa agora a contertambém o peso do ramo. Se o ramo não existir então o peso será de ∞.

No caso das listas de adjacências, onde a presença de um ramo é detectada pela existênciado mesmo na lista associada ao vértice origem, continua assim a sê-lo, mas agora cada nodo dalista contém o peso do ramo.

Paulo Matos

Page 183: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

164 Capıtulo 6. Grafos

6.7.2 Algoritmos para grafos pesados

Spanning Tree Mınima

Seja G = (V,R) um grafo pesado não-orientado, de�ne-se como Spanning Tree Mínima de G,à árvore T = (V,R′), tal que R′ ⊆ R, em que para quaisquer dois vértices vx e vy ∈ V , existe emT no máximo um caminho que liga vx com vy e que corresponde ao caminho de menor custo.

Uma solução possível para determinar a Spanning Tree Mínima consiste em utilizar o algo-ritmo de Prim's, no qual a árvore é construída acrescentado vértice a vértice, começando poraqueles que aparentam permitir obter a solução mínima. Daí que esta estratégia seja do tipogreedy, isto é, é feita com alguma incerteza, mas que na realidade permite obter a solução dese-jada.

O algoritmo tem por base uma estratégia do tipo Breadth-First porque explora primeiro todasas soluções em largura antes de aprofundar a pesquisa.

Para o funcionamento deste algoritmo é necessário que cada vértice contenha mais dois cam-pos: um que indica se o vértice já faz parte da árvore e que será assinalado por uma cor (preto sejá �zer parte da árvore e branco para o caso contrário); e um segundo campo, valor, que assinalaa melhor solução obtida para esse vértice até ao estado presente da execução do algoritmo. Ini-cialmente todos os vértices possuem cor branca e o valor da solução é in�nito, excepto o vérticeinicial cujo valor é zero.

O algoritmo funciona da seguinte forma:

1. Colocar todos os vértices com cor branca;

2. Colocar todos os vértices com valor in�nito;

3. Colocar o vértice inicial com valor zero;

4. R' ← ∅

5. Seleccionar segundo um Breadth-First o vértice de cor branca de menor valor va (que naprimeira iteração corresponde ao vértice inicial);

6. Coloca a cor de va a preto;

7. Para todo e qualquer vértice vs que seja sucessor de va fazer:

7.1. Se vs tem cor branca então

7.1.1. Se o Custo(va,vs) < Valor(vs) então7.1.1.1. Valor(vs) ← Custo(va,vs)7.1.1.2. R' = R' \ (_,vs)7.1.1.3. R' = R' ∪ (va,vs)

8. Voltar ao ponto 5

O algoritmo termina quando já não existirem vértices de cor branca. O grafo da Figura 6.21serve para ilustrar o funcionamento deste algoritmo, onde o vértice inicial é v1.

Paulo Matos

Page 184: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

6.7. Grafos Pesados 165

Figura 6.21: Grafo exemplo para gerar a Spanning Tree Mınima.

A evolução do processo encontra-se ilustrada entre a Figura 6.22 e a Figura 6.27. Caso o valordos vértices seja igual a selecção faz-se pelo de menor índice.

O algoritmo �nal é o seguinte, onde se utiliza uma queue para garantir que os vértices sãoprocessados segundo a estratégia Breadth-First :

(1) PROC SpanningTreeMin(g:Grafo,vi:Vértice):Grafo(2) VAR t :GrafoE // Grafo onde cada vértice comporta uma cor e um valor(3) VAR tf :GRAFO(4) VAR va, vs, vx :Vértice(5) VAR q :QUEUE(Vertice)(6) VAR lsuc :SEQ(Vértice)(7) VAR ccam :CUSTO(8) INÍCIO(9) t.new()(10) t.setVertices(g.getVertices())(11) t.setRamos()(12) PARA ∀ vx ∈ t.Vertices() FAZER(13) vx.setCor(branco)(14) vx.setValor()(15) FPARA(16) t.setValor(vi,0)(17) q.enqueue(vi)(18) REP(19) va ← q.dequeue()(20) t.setCor(va,preto)

Paulo Matos

Page 185: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

166 Capıtulo 6. Grafos

Figura 6.22: Primeira iteracao do algoritmo para determinar a Spanning Tree Mınima.

(21) lsuc ← g.getSucc(va)(22) lsuc ← RemoverVPretos(t,lsuc) // Remover de lsuc os vértices(23) // que já estejam a preto(24) PARA ∀ vs ∈ lsuc FAZER(25) ccam ← t.getCRamo(va,vs) + t.getValor(va)(26) SE ccam < t.getValor(vs) ENTÃO(27) SE t.getValor(vs) <> ∞ ENTÃO(28) t.remRamo(_,vs)(29) // q.remElem(vs) // É necessário existir um mecanismo que(30) // permita remover vértices da queue pela sua designação(31) FSE(32) t.addRamo(va,vs)(33) t.setValor(vs,ccam)(34) FSE(35) FPARA(36) lsuc ← OrdenarValor(t,lsuc) // Ordenar os vértices de lsuc pelo valor(37) q ← concatenar(q,lsuc)(38) ENQ q.empty() = FALSE(39) tf ← Converter(t) // Eliminar os campos valor e cor(40) RET tf

(41) FIM

Paulo Matos

Page 186: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

6.7. Grafos Pesados 167

Figura 6.23: Segunda iteracao do algoritmo para determinar a Spanning Tree Mınima.

Caminho de menor custo

O caminho pode ser avaliado em função do número de transições, ou em função do pesoassociado a cada um dos ramos. No primeiro caso o caminho pode ser determinado com basenuma travessia Breadth-First que permite apurar o caminho mais curto até um determinadovértice.

Como já foi visto, o Breadth-First processa os vértices em largura, ou seja, a ordem pela qualos vértices são processados é igual à ordem pela qual são expandidos. Este processo é possívelatravés da utilização de uma queue.

Para apurar o menor caminho entre dois vértices é necessário alterar ligeiramente o processode travessia Breadth-First. Para tal há que associar a cada vértice inserido na queue, a sequênciade vértices percorridos entre o vértice inicial e o ascendente do vértice em causa, dai que a queueseja formada por elementos do tipo V erticexSeq(V ertice).

(1) PROC CaminhoMínimo(g:Grafo,vi:Vértice,vf:Vértice):Seq(Vértice)(2) VAR cam :Seq(Vértice)(3) VAR q :Queue(Vértice x Seq(Vértice))(4) VAR visit :Set(Vértice)(5) VAR vs, vx :Vértice(6) INÍCIO(7) visit.new()(8) q.new()(9) visit ← {vi}(10) q.enqueue((vi,<>))

Paulo Matos

Page 187: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

168 Capıtulo 6. Grafos

Figura 6.24: Terceira iteracao do algoritmo para determinar a Spanning Tree Mınima.

(11) REP(12) (vx,cam) ← q.dequeue()(13) SE vx = vf ENTÃO(14) RET <cam,vx>(15) SENÃO(16) PARA ∀ vs ∈ g.getSucc(vx) FAZER(17) SE vs /∈ visit ENTÃO(18) q.enqueue((vs,<vx,cam>))(19) visit ← visit ∪ {vs}(20) FSE(21) FPARA(22) FSE(23) ENQ q.empty() = FALSE(24) RET <>(25) FIM

Uma solução e�ciente para determinar o caminho do custo mínimo com base nos pesos dosramos foi proposta por Dijkstra. O processo é muito semelhante ao anterior, isto é, os vérticessão coloridos a branco (indicação de que ainda não foi apurado o menor custo possível) e comcusto in�nito, à excepção do vértice inicial cujo custo é zero. Depois escolhe-se o vértice demenor custo até ai apurado (vx), o qual passa a ser considerado como já processado (cor preta).De seguida, veri�ca-se para cada um dos sucessores, se o caminho via vx, é o de menor custo atéai apurado. Se tal acontecer, então há que actualizar, para o sucessor em causa, o custo apuradoe o ascendente (vx). O processo termina quando se alcançar o vértice �nal (com o caminho demenor custo).

Paulo Matos

Page 188: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

6.7. Grafos Pesados 169

Figura 6.25: Quarta iteracao do algoritmo para determinar a Spanning Tree Mınima.

Para implementar esta solução há que de�nir a seguinte estrutura:

VERTY = vert x cor x custo x camvert : Vérticecor : Cor ∈ {Preto, Branco}custo : Custocam : Seq(Vértice)

E respectivos operadores:

getCor: Grafo x Vértice → Cor // Devolve o valor de corsetCor: Grafo x Vértice x Cor → Grafo // Atribui Cor a corgetCusto: Grafo x Vértice → Custo // Devolve o valor de custosetCusto: Grafo x Vértice x Custo → Grafo // Atribui Custo a custogetCam: Grafo x Vértice → Seq(Vértice) // Devolve o valor de camsetCam: Grafo x Vértice x Seq(Vértice) → Grafo // Faz a atribuição de um

// vértice e caminho a este// associado, respectivamente,// a vert e cam

O algoritmo é o seguinte, em que V NULO signi�ca que não existe vértice:

Paulo Matos

Page 189: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

170 Capıtulo 6. Grafos

Figura 6.26: Quinta iteracao do algoritmo para determinar a Spanning Tree Mınima.

(1) PROC Dijkstra(g:Grafo,vi:Vértice,vf:Vértice):Seq(Vértice)(2) VAR sv :Seq(VERTY)(3) VAR cam :Seq(Vértice)(4) VAR vs, vx :Vértice(5) VAR cust :Custo(6) INÍCIO(7) sv.new() // ∀ vx ∈ g.Vértices(): sv.setCor(vx,Branco),(8) // sv.setCusto(vx,+∞), sv.setCam(vx,<>)(9) sv.setCusto(vi,0)(10) vx ← vi

(11) REP(12) SE vx = vf ENTÃO(13) cam ← <sv.getCam(vx),vf>(14) RET cam(15) SENÃO(16) sv.setCor(vx,Preto)(17) PARA ∀ vs ∈ g.getSuc(vx) FAZER(18) SE sv.getCor(vs) = Branco ENTÃO(19) cust ← sv.getCusto(vx) + g.getCRamo(vx,vs)(20) SE sv.getCusto(vs) > cust ENTÃO(21) sv.setCusto(vs,cust)(22) cam ← <sv.getCam(vx),vx>(23) sv.setCam(vs,cam)(24) FSE(25) FSE(26) FPARA

Paulo Matos

Page 190: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

6.7. Grafos Pesados 171

Figura 6.27: Sexta iteracao do algoritmo para determinar a Spanning Tree Mınima.

(27) FSE(28) vx ← sv.getVertMenorCusto() // Selecciona o vértice de menor(29) // custo até ai apurado(30) ENQ vx 6= VNULO(31) RET <>(32) FIM

A solução anterior só funciona se o grafo não contiver ramos com custos negativos. Mas se talacontecer, é sempre possível realizar uma pequena alteração ao grafo que viabiliza a utilizaçãodesta solução, que consiste em adicionar ao custo de cada ramo, um o�set que coloque todos osramos com custos positivos.

Exercıcios:

a) Implemente as estruturas para representar o grafo da Figura 6.28. Cada vértice contém onome e a população da cidade, e cada ramo contém a designação da estrada e a respectivadistância.

b) Implemente para o exercício anterior, as rotinas apresentadas ao longo deste capítulo.

Paulo Matos

Page 191: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

172 Capıtulo 6. Grafos

Figura 6.28: Mapa do Exemplo 6. ?? .

Paulo Matos

Page 192: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

Paulo Jorge Matos

Page 193: ALGORITMOS E ESTRUTURAS DE DADOS - ipb.ptreis.quarteu/aed2005/pdfs/sebenta.pdf · ii Sebenta elaborada pelo docente Paulo Matos, no ambito da disciplina de Algoritmos e Estru-turas

174

Paulo Matos