Estruturas de Dados e Algoritmos em C - 3ª Ed. Rev. e Aum.

Page 1

9cm x 24cm

17cm x 24cm

30mm

17cm x 24cm

9cm x 24cm

António Adrego da Rocha

Dirigido aos estudantes de programação; aprofunda o estudo dos vários tipos de algoritmos expondo a análise experimental e formal da sua complexidade. Pseudocódigo compatível com C e Java.

Um valioso recurso para docentes e estudantes do Ensino Superior; profissionais; e na preparação de Certificações Red Hat. Inclui a configuração completa de um cenário prático real.

Conheça a organização e os mecanismos mais sofisticados de um sistema operativo e perceba como este gere e coordena o seu computador. Com exemplos em Unix (e variantes, Linux e Mac OS) e Windows.

Livro indispensável na criação e desenvolvimento de jogos digitais: programação de jogos para web, dispositivos móveis e Windows 8. Com Python, Pygame, HTML5 e outros. Para estudantes e profissionais.

Esta obra tem como objetivo fornecer uma competência sólida no desenvolvimento de programas de média e elevada complexidade e um conhecimento profundo sobre estruturas de dados avançadas e algoritmos complexos, usando a linguagem de programação C e aplicando o paradigma da programação modular. Assim, utiliza uma metodologia que dá particular ênfase à decomposição funcional das soluções, através da implementação de tipos de dados abstratos. Inclui exemplos, exercícios, programas e leituras recomendadas, com vista a facilitar a aprendizagem dos alunos. Para atingir este objetivo, o livro está organizado em cinco grandes temas: Estudo do paradigma da programação modular na linguagem C, apresentando os aspetos fundamentais para implementar tipos de dados abstratos aplicando a metodologia de programação defensiva;

Nesta nova edição foram acrescentadas secções sobre Árvore Rubinegra, Árvore Autoequilibrada, Caminhos e Circuitos Hamiltonianos, Circuitos e Caminhos Eulerianos. Principais temas abordados no livro: Recursividade; Programação Modular; Listas; Árvores; Pesquisa, Seleção e Ordenação; Memórias; Filas e Pilhas;

Estudo das principais estruturas de dados dinâmicas;

Memórias Associativas;

Estudo das principais classes de algoritmos;

Filas com Prioridade;

Estudo da implementação dos diferentes tipos de memórias;

Grafos;

Estudo do tipo de dados abstrato grafo/dígrafo e seus algoritmos mais importantes.

Outros Tópicos de Programação.

Este livro é dirigido aos estudantes de disciplinas de programação, que frequentam licenciaturas que exijam conceitos sólidos de programação, um conhecimento profundo sobre algoritmos e estruturas de dados avançadas e implementação de tipos de dados abstratos na linguagem C, assim como a programadores. Este livro disponibiliza ainda a correspondência dos principais termos técnicos para o português do Brasil.

ISBN 978-972-722-769-3

9 789727 227693

Implementação de tipos de dados abstratos na linguagem C; Algoritmos e estruturas de dados fundamentais para escrever programas de média e elevada complexidade.

Programas apresentados na obra disponíveis em www.fca.pt, até o livro se esgotar ou ser publicada nova edição atualizada ou com alterações, ou na página pessoal do autor, em http://sweet.ua.pt/~f706/ensino/livro3/html.

Professor Auxiliar no Departamento de Eletrónica, Telecomunicações e Informática da Universidade de Aveiro. A sua atividade de investigação tem sido dedicada à simulação e análise de algoritmos em linguagem C, na modelação e simulação em VHDL de arquiteturas de máquinas de estados finitas hierárquicas e na sua síntese em C++. No decurso da sua atividade pedagógica tem lecionado Programação em Pascal, Sistemas Operativos, Sistemas Digitais, Programação em VHDL, Programação em linguagem C, Programação em linguagem Java, Algoritmos e Estruturas de Dados Avançadas e Programação em Assembly. Autor dos livros Análise da Complexidade de Algoritmos, Estruturas de Dados e Algoritmos em Java, Introdução à Programação usando C e Programação Avançada usando C, e coautor do livro Introdução à Programação em Java, todos publicados pela FCA.


Distribuição

Lidel – edições técnicas, lda

SEDE: R. D. Estefânia, 183, R/C Dto., 1049-057 LISBOA Internet: 21 354 14 18 – livraria@lidel.pt / Revenda: 21 351 14 43 – revenda@lidel.pt Formação/Marketing: 21 351 14 48 – formacao@lidel.pt / marketing@lidel.pt Ensino Línguas/Exportação: 21 351 14 42 – depinternacional@lidel.pt Fax: 21 352 26 84

LIVRARIA: Av. Praia da Vitória, 14 – 1000-247 LISBOA Tel.: 21 354 14 18 – e-mail: livraria@lidel.pt

Copyright © fevereiro 2014 (3ª Edição Revista e Aumentada); agosto 2008 (1ª Edição) FCA – Editora de Informática, Lda. ISBN: 978-972-722-769-3 Capa: José Manuel Reis Ilustração da capa: Miguel Montenegro Impressão e acabamento: Cafilesa – Soluções Gráficas, Lda. – Venda do Pinheiro Depósito Legal Nº Livro segundo o Novo Acordo Ortográfico

Todos os nossos livros passam por um rigoroso controlo de qualidade, no entanto, aconselhamos a consulta periódica do nosso site (www.fca.pt) para fazer o download de eventuais correções.

Os nomes comerciais referenciados neste livro têm patente registada. Marcas Registadas de FCA – Editora de Informática, Lda. – ®

®

®

Reservados todos os direitos. Esta publicação não pode ser reproduzida, nem transmitida, no todo ou em parte, por qualquer processo eletrónico, mecânico, fotocópia, digitalização, gravação, sistema de armazenamento e disponibilização de informação, sítio Web, blogue ou outros, sem prévia autorização escrita da Editora, exceto o permitido pelo CDADC, em termos de cópia privada pela AGECOP – Associação para a Gestão da Cópia Privada, através do pagamento das respetivas taxas.


ÍNDICE Prefácio XIII 1- Recursividade 1 1.1 Introdução .................................................................................................................... 1 1.2 Cálculo do fatorial ....................................................................................................... 3 1.3 Expansão em série de Taylor ...................................................................................... 4 1.4 Números de Fibonacci ................................................................................................. 7 1.5 Cálculo dos coeficientes binomiais ............................................................................. 9 1.6 Cálculo das permutações........................................................................................... 12 1.7 Cálculo do determinante de uma matriz ................................................................. 14 1.8 Torres de Hanói ......................................................................................................... 16 1.9 Eliminação da recursividade .................................................................................... 21 1.10 Recursividade com retrocesso ................................................................................ 23 1.11 Questões sobre a eficiência da recursividade ........................................................ 30 Exercícios.......................................................................................................................... 31 Leituras Recomendadas .................................................................................................. 34 2 - Programação modular 35 2.1 Introdução .................................................................................................................. 35 2.2 Tipos de dados abstratos ........................................................................................... 38 2.3 Módulos na linguagem C .......................................................................................... 40 2.4 Abstração de dados na linguagem C ........................................................................ 42 2.5 Gestão da memória dinâmica ................................................................................... 43 2.6 Controlo de erros ....................................................................................................... 46 2.6.1 Programação defensiva ......................................................................................... 46 2.6.1.1 Controlo centralizado de erro ........................................................................ 47 2.6.1.2 Devolução sistemática de um código de erro pelas operações ...................... 47 2.6.2 Programação por contrato ..................................................................................... 48 2.6.2.1 Asserções ....................................................................................................... 49 © FCA - Editora de Informática

v


ESTRUTURAS DE DADOS E ALGORITMOS EM C

2.6.3 Conclusões ............................................................................................................ 50 2.7 Exemplo de um tipo de dados elemento matemático .............................................. 50 2.8 Exemplo de um tipo de dados memória ................................................................... 58 2.8.1 Implementação concreta ....................................................................................... 58 2.8.2 Implementação configurada.................................................................................. 65 2.8.2.1 Elemento constituinte da memória ................................................................ 66 2.8.2.2 Implementação da memória .......................................................................... 69 2.8.2.3 Exemplo de utilização ................................................................................... 73 2.8.3 Implementação genérica ....................................................................................... 75 Exercícios.......................................................................................................................... 80 Leituras Recomendadas .................................................................................................. 83 3 - Listas 85 3.1 Introdução .................................................................................................................. 85 3.2 Listas simples ............................................................................................................. 87 3.2.1 Algoritmos básicos ............................................................................................... 89 3.2.2 Simulação das operações básicas da lista ............................................................. 95 3.2.3 Considerações finais sobre listas simples ............................................................. 96 3.3 Listas biligadas ........................................................................................................... 96 3.4 Listas skip ................................................................................................................. 102 Exercícios........................................................................................................................ 111 Leituras Recomendadas ................................................................................................ 114 4 - Árvores 115 4.1 Introdução ................................................................................................................ 115 4.2 Árvore binária de pesquisa ..................................................................................... 118 4.2.1 Algoritmos básicos ............................................................................................. 120 4.2.2 Algoritmos de pesquisa e de seleção .................................................................. 121 4.2.3 Inserção e remoção de elementos ....................................................................... 124 4.2.4 Travessias ........................................................................................................... 130 4.2.5 Travessias não recursivas ................................................................................... 132 4.2.6 Balanceamento.................................................................................................... 138 4.2.7 Visualização........................................................................................................ 140 4.2.8 Simulação da árvore binária de pesquisa ............................................................ 142 4.2.9 Implementação alternativa .................................................................................. 143 4.3 Árvore de Adelson-Velskii Landis ......................................................................... 147 vi

© FCA - Editora de Informática


ÍNDICE

4.3.1 Inserção de elementos ......................................................................................... 149 4.3.2 Remoção de elementos ....................................................................................... 158 4.4 Árvore rubinegra ..................................................................................................... 160 4.4.1 Inserção de elementos ......................................................................................... 163 4.4.2 Remoção de elementos ....................................................................................... 169 4.5 Árvore autoequilibrada ........................................................................................... 175 4.5.1 Estrutura do nó.................................................................................................... 176 4.5.2 Abordagem recursiva (bottom-up) ...................................................................... 177 4.5.3 Abordagem repetitiva (top-down)....................................................................... 177 4.6 Amontoado binário .................................................................................................. 185 4.6.1 Inserção e remoção de elementos ....................................................................... 186 4.6.2 Promoção e despromoção de elementos ............................................................. 189 4.6.3 Considerações finais sobre amontoados ............................................................. 192 4.6.4 Simulação do amontoado.................................................................................... 193 Exercícios........................................................................................................................ 194 Leituras Recomendadas ................................................................................................ 198 5 - Pesquisa, Seleção e Ordenação 199 5.1 Introdução ................................................................................................................ 199 5.2 Complexidade algorítmica ...................................................................................... 200 5.3 Pesquisa .................................................................................................................... 203 5.3.1 Pesquisa sequencial ............................................................................................ 204 5.3.2 Pesquisa binária .................................................................................................. 206 5.3.3 Pesquisa ternária ................................................................................................. 212 5.3.4 Pesquisa por interpolação ................................................................................... 217 5.3.5 Pesquisa por dispersão ........................................................................................ 219 5.3.5.1 Deslocamento da posição de colocação....................................................... 221 5.3.5.2 Encadeamento de elementos........................................................................ 226 5.3.5.3 Considerações finais sobre a pesquisa por dispersão .................................. 229 5.3.6 Pesquisa por indexação ....................................................................................... 231 5.4 Seleção....................................................................................................................... 232 5.4.1 Seleção do maior valor ....................................................................................... 233 5.4.2 Seleção do menor valor ...................................................................................... 234 5.4.3 Seleção simultânea do maior e do menor valores ............................................... 234 5.4.4 Seleção do primeiro valor que serve ................................................................... 235 5.4.5 Seleção do melhor valor que serve ..................................................................... 236 © FCA - Editora de Informática

vii


ESTRUTURAS DE DADOS E ALGORITMOS EM C

5.4.6 Seleção do pior valor que serve .......................................................................... 236 5.4.7 Exemplificação dos algoritmos de seleção ......................................................... 237 5.4.8 Seleção do K-ésimo menor valor ........................................................................ 238 5.5 Ordenação ................................................................................................................ 239 5.5.1 Ordenação por seleção ........................................................................................ 240 5.5.2 Ordenação por troca............................................................................................ 243 5.5.3 Ordenação por inserção ...................................................................................... 250 5.5.4 Comparação dos algoritmos de ordenação simples ............................................ 255 5.6 Ordenação por fusão ............................................................................................... 256 5.7 Algoritmos de ordenação recursivos ...................................................................... 259 5.7.1 Ordenação por fusão ........................................................................................... 259 5.7.2 Ordenação por separação .................................................................................... 263 5.8 Algoritmo de ordenação Heap ................................................................................ 267 5.8.1 Construção do amontoado .................................................................................. 267 5.8.2 Implementação do algoritmo .............................................................................. 269 5.9 Generalização dos algoritmos de ordenação ......................................................... 274 5.10 Avaliação do desempenho dos algoritmos ........................................................... 277 Exercícios........................................................................................................................ 279 Leituras Recomendadas ................................................................................................ 282 6 - Memórias 283 6.1 Introdução ................................................................................................................ 283 6.2 Caracterização da memória de acesso aleatório ................................................... 284 6.3 Caracterização da memória fila ............................................................................. 285 6.4 Caracterização da memória pilha .......................................................................... 287 6.5 Caracterização da memória associativa ................................................................ 288 6.6 Caracterização da memória fila com prioridade .................................................. 290 6.7 Tipos de implementação .......................................................................................... 291 6.8 Implementação da memória de acesso aleatório ................................................... 293 6.9 Implementação da memória fila ............................................................................. 294 6.10 Implementação da memória pilha ........................................................................ 295 6.11 Implementação da memória associativa .............................................................. 297 6.12 Implementação da memória fila com prioridade................................................ 302 6.13 Considerações finais sobre memórias .................................................................. 305

viii

© FCA - Editora de Informática


ÍNDICE

7 - Filas e Pilhas 307 7.1 Introdução ................................................................................................................ 307 7.2 Memória fila ............................................................................................................. 308 7.2.1 Implementação estática....................................................................................... 309 7.2.2 Implementação semiestática ............................................................................... 316 7.2.3 Implementação dinâmica .................................................................................... 318 7.3 Memória pilha .......................................................................................................... 322 7.3.1 Implementação estática....................................................................................... 324 7.3.2 Implementação semiestática ............................................................................... 328 7.3.3 Implementação dinâmica .................................................................................... 330 7.4 Exemplos simples de aplicação de filas e pilhas .................................................... 334 7.5 Exemplos complexos de aplicação de pilhas .......................................................... 340 7.6 Implementações genéricas....................................................................................... 345 7.7 Torres de Hanói ....................................................................................................... 351 7.8 Eliminação da recursividade .................................................................................. 354 7.9 Memória fila dupla .................................................................................................. 356 Exercícios........................................................................................................................ 357 Leituras Recomendadas ................................................................................................ 358 8 - Memórias Associativas 359 8.1 Introdução ................................................................................................................ 359 8.2 Configuração da memória ...................................................................................... 360 8.3 Memória associativa estática .................................................................................. 362 8.4 Memória associativa semiestática .......................................................................... 370 8.5 Memória associativa dinâmica ............................................................................... 374 8.5.1 Versão dinâmica linear ....................................................................................... 374 8.5.2 Versão dinâmica logarítmica .............................................................................. 382 8.5.3 Versão dinâmica por dispersão ........................................................................... 392 8.5.4 Versão dinâmica hierárquica .............................................................................. 401 8.6 Exemplo de utilização .............................................................................................. 411 8.7 Memória associativa genérica ................................................................................. 414 Exercícios........................................................................................................................ 419 9 - Filas com Prioridade 421 9.1 Introdução ................................................................................................................ 421 © FCA - Editora de Informática

ix


ESTRUTURAS DE DADOS E ALGORITMOS EM C

9.2 Fila com prioridade estática ................................................................................... 423 9.3 Fila com prioridade dinâmica................................................................................. 432 9.4 Implementação com amontoado ............................................................................. 439 9.5 Exemplo de utilização .............................................................................................. 443 9.6 Fila com prioridade genérica .................................................................................. 444 Exercícios........................................................................................................................ 449 Leituras Recomendadas ................................................................................................ 450 10 - Grafos 451 10.1 Introdução .............................................................................................................. 451 10.2 Propriedades do grafo ........................................................................................... 452 10.3 Implementação do grafo ....................................................................................... 456 10.4 Caracterização do grafo ........................................................................................ 458 10.5 Dígrafo/Grafo dinâmico ........................................................................................ 459 10.6 Travessias ............................................................................................................... 471 10.7 Ordenação topológica ............................................................................................ 481 10.8 Componentes fortemente conexas ........................................................................ 489 10.9 Caminhos mais curtos ........................................................................................... 492 10.9.1 Vértices alcançáveis.......................................................................................... 492 10.9.2 Caminho mais curto .......................................................................................... 495 10.9.3 Todos os pares de caminhos mais curtos .......................................................... 507 10.9.4 Os K caminhos mais curtos .............................................................................. 510 10.10 Árvore abrangente de custo mínimo .................................................................. 511 10.10.1 Algoritmo de Prim .......................................................................................... 511 10.10.2 Algoritmo de Kruskal ..................................................................................... 516 10.11 Fecho transitivo.................................................................................................... 520 10.12 Caminhos e circuitos hamiltonianos .................................................................. 521 10.13 Circuitos e caminhos eulerianos ......................................................................... 525 Exercícios........................................................................................................................ 533 Leituras Recomendadas ................................................................................................ 536 11 - Outros Tópicos de Programação 537 11.1 Uniões...................................................................................................................... 537 11.2 Operadores bitwise ................................................................................................. 542 11.3 Sequências de ponteiros para funções.................................................................. 545 x

© FCA - Editora de Informática


ÍNDICE

11.4 Lista de parâmetros com comprimento variável ................................................ 546 11.5 Pré-processador da linguagem C ......................................................................... 548 11.5.1 Macros de substituição ..................................................................................... 549 11.5.2 Compilação condicional ................................................................................... 550 11.5.3 Asserções .......................................................................................................... 551 11.6 Compilação automática ......................................................................................... 552 11.7 Tratamento de sinais ............................................................................................. 554 11.8 Controlo de erros ................................................................................................... 555 11.9 Conjunto disjunto .................................................................................................. 557 11.10 Acesso indexado a ficheiros................................................................................. 560 11.11 Pesquisa exaustiva ............................................................................................... 565 11.11.1 Quadrado mágico ............................................................................................ 565 11.11.2 Subconjuntos de um conjunto ......................................................................... 570 11.11.3 Carregamento otimizado da mochila .............................................................. 574 11.12 Algoritmos numéricos ......................................................................................... 576 11.12.1 Cálculo da potência......................................................................................... 576 11.12.2 Avaliação do polinómio .................................................................................. 577 11.12.3 Multiplicação de polinómios ..........................................................................578 11.12.4 Multiplicação de matrizes ............................................................................... 580 Exercícios........................................................................................................................ 585 Leituras Recomendadas ................................................................................................ 586 Glossário de Termos - Português Europeu/Português do Brasil 587 Índice Remissivo 589

© FCA - Editora de Informática

xi


© FCA – Editora de Informática


Prefácio

Programação em linguagem C Hoje em dia, a Programação é considerada uma ciência de base de qualquer curso da área científica das Ciências e das Engenharias, fazendo por isso parte do seu currículo uma ou mais disciplinas de Ciência e Tecnologia da Programação. No caso de cursos que exijam conceitos sólidos de programação e de desenvolvimento de software, é normal que o seu estudo seja feito em várias disciplinas. Desde disciplinas de introdução à programação, dedicadas ao estudo de estruturas de dados básicas e de algoritmos simples, usando o paradigma procedimental, até disciplinas avançadas, dedicadas ao estudo de estruturas de dados avançadas e de algoritmos mais complexos, eventualmente, fazendo um estudo de técnicas algorítmicas sofisticadas e de análise da complexidade dos algoritmos, usando o paradigma modular, que permite combinar estruturas de dados e algoritmos na conceção de abstrações, indispensáveis para o desenvolvimento de aplicações informáticas sofisticadas, que habitualmente designamos por tipos de dados abstratos. A questão que se coloca é se essas disciplinas devem ser lecionadas usando uma linguagem não orientada a objetos, como a linguagem C, ou usando uma linguagem orientada a objetos, como a linguagem C++ ou a linguagem Java. A linguagem C tem a grande vantagem de ser uma linguagem de uso geral e adaptável a qualquer área científica, que permite implementar os paradigmas procedimental e modular, que são mais intuitivos do que o paradigma orientado a objetos, na iniciação à aprendizagem da programação. Como a linguagem C combina as características de uma linguagem de alto nível com as características do Assembly, ela pode ser usada como linguagem de suporte ao ensino de programação em Assembly. Acresce ainda que a linguagem C foi desenvolvida para programação de sistemas e está intimamente ligada ao desenvolvimento do sistema operativo Unix, pelo que também é a linguagem adequada para o desenvolvimento de aplicações de sistema. Tendo isto em consideração, em 2006 escrevi dois livros de programação usando a linguagem C: um primeiro livro, Introdução à Programação usando C, para introduzir a linguagem C, as estruturas de dados estáticas e os ficheiros e para explorar o paradigma da programação procedimental; e um segundo livro, Programação Avançada usando C, para estudar os aspetos mais avançados da linguagem C, as soluções recursivas e as estruturas de dados dinâmicas, e para explorar o paradigma da programação modular.

© FCA - Editora de Informática

xiii


ESTRUTURAS DE DADOS E ALGORITMOS EM C

Entretanto, algumas disciplinas de programação, resultado da última revisão curricular, têm vindo a dedicar-se ao estudo das estruturas de dados avançadas, das técnicas algorítmicas mais sofisticadas, da análise da complexidade dos algoritmos e da construção de abstrações essenciais para o desenvolvimento de aplicações informáticas. A primeira edição do livro Estruturas de Dados e Algoritmos em C era, portanto, uma evolução natural do livro Programação Avançada usando C, com um reforço do estudo de estruturas de dados dinâmicas avançadas, como são os casos das listas com atalhos e da árvore de Adelson-Velskii Landis, bem como uma reorganização de alguns capítulos. Esta edição que tem nas suas mãos é uma nova abordagem em termos de organização global dos assuntos e conta com um reforço do estudo de algoritmos de árvores, nomeadamente com a introdução das árvores rubinegra e autoequilibrada, de algoritmos de grafos, de pesquisa exaustiva e numéricos. Este livro tem como objetivo fornecer uma competência sólida no desenvolvimento de programas de média e elevada complexidade, usando a linguagem de programação C e aplicando o paradigma da programação modular. Assim, utiliza uma metodologia que dá particular ênfase à decomposição funcional das soluções, através da implementação de tipos de dados abstratos. Outro objetivo consiste na aprendizagem de técnicas mais avançadas de análise da solução de problemas, como as soluções recursivas, sendo estudados os aspetos relacionados com a eficiência das soluções. Também é objetivo introduzir as estruturas de dados e os algoritmos que, tipicamente, são encontrados em programas de média e elevada complexidade, nomeadamente as estruturas de dados semiestáticas e dinâmicas. Especificamente, este livro pretende atingir os seguintes objetivos:

xiv

O estudo de algoritmos recursivos, como alternativa à decomposição hierárquica das soluções, e a comparação com os algoritmos repetitivos equivalentes;

A gestão da memória dinâmica e o estudo de estruturas de dados dinâmicas, nomeadamente das listas ligadas e das árvores;

O estudo do paradigma da programação modular, que assenta na implementação de tipos de dados abstratos, começando pelos tipos de dados que modelam elementos matemáticos, como, por exemplo, números complexos, vetores, polinómios, matrizes e conjuntos;

O estudo da implementação de tipos de dados abstratos, que armazenam coleções de dados, ou seja, o estudo dos diferentes tipos de memórias, nomeadamente da memória de acesso aleatório, da memória fila, da memória pilha, da memória associativa e da memória fila com prioridade, e dos algoritmos associados para pesquisa, colocação e remoção de informação;

O estudo da implementação do tipo abstrato de dados grafo/dígrafo. © FCA - Editora de Informática


PREFÁCIO

Organização do livro Em termos gerais, este livro está organizado em seis partes. A primeira parte é constituída pelos dois primeiros capítulos e é dedicada a questões de engenharia de software, como a recursividade, como aspeto mais avançado da programação procedimental, e a introdução ao paradigma da programação modular, que assenta na construção de tipos de dados abstratos. A segunda parte é constituída pelos dois capítulos seguintes e é dedicada ao estudo das estruturas de dados dinâmicas, nomeadamente das listas ligadas (linked lists) e das árvores (trees). A terceira parte é constituída pelo quinto capítulo e é dedicada ao estudo dos algoritmos de pesquisa, de seleção e de ordenação. A quarta parte é constituída pelos quatro capítulos seguintes e é dedicada à caracterização dos diferentes tipos de memórias e ao estudo da memória fila (Queue), da memória pilha (Stack), da memória associativa (Content Access Memory) e da memória fila com prioridade (Priority Queue). A quinta parte é constituída pelo décimo capítulo e é dedicada ao estudo do tipo de dados abstrato dígrafo/grafo (Digraph/Graph). Finalmente, a sexta parte é constituída pelo último capítulo e é dedicada ao estudo de diversos tópicos de programação, mormente do tipo de dados abstrato conjunto disjunto (Disjoint Set), da técnica de pesquisa exaustiva e de alguns algoritmos numéricos. Vamos agora descrever com mais detalhe os capítulos constituintes do livro. O Capítulo 1 apresenta um aspeto avançado da programação procedimental, que é a implementação de subprogramas recursivos. Através da apresentação dos algoritmos recursivos clássicos, são estudados os vários aspetos associados à recursividade, nomeadamente a questão da sua eficiência, pelo que são abordados os algoritmos repetitivos equivalentes. Também é apresentada a técnica de programação dinâmica (dynamic programming) e referida a utilização de pilhas, como alternativas à recursividade. Finalmente, é explicada a técnica de exploração exaustiva de soluções, como é o caso da recursividade com retrocesso (backtracking). O Capítulo 2 apresenta o paradigma de programação modular, que assenta na implementação de módulos autónomos e coerentes, também designados por tipos de dados abstratos. A linguagem C não tem suporte para os tipos de dados abstratos, mas permite a implementação modular de aplicações. Por isso, são descritas as características dos ficheiros de interface e de implementação de um módulo, bem como a sua compilação separada. São enunciadas as características dos dois tipos de dados abstratos fundamentais, especificadamente os que modelam elementos matemáticos e os que armazenam coleções de dados, também designados por memórias. São apresentadas as funções da biblioteca de execução ANSI para a gestão da memória dinâmica. Também é explicado como se implementa a abstração de dados na linguagem C, recorrendo à criação de instâncias do tipo de dados abstrato na memória dinâmica e da sua manipulação através de ponteiros. São também discutidas as metodologias de controlo de erros – programação defensiva e por contrato. Depois, é usado o número complexo como exemplo da implementação de tipos de dados abstratos que modelam elementos matemáticos, servindo para apresentar a teoria sobre a implementação de módulos, nomeadamente a implementação de construtores (inicializador e cópia) e do destrutor. © FCA - Editora de Informática

xv


ESTRUTURAS DE DADOS E ALGORITMOS EM C

Finalmente, é usada uma memória indexada simplificada para mostrar a criação destes tipos de dados, nas suas implementações concreta, configurada e generalizada. O Capítulo 3 apresenta a teoria das listas ligadas, sendo descritos os algoritmos de pesquisa, de inserção e de remoção de elementos das listas: simplesmente ligadas (singly linked lists), duplamente ligadas (doubly linked lists) e com atalhos (skip lists). O Capítulo 4 apresenta a teoria das árvores. Começa por apresentar as propriedades matemáticas das árvores binárias. Depois, são estudadas as árvores binárias de pesquisa (binary search trees), as árvores de Adelson-Velskii Landis (AVL trees), as árvores rubinegras (red-black trees) e as árvores autoequilibradas (splay trees), sendo descritos os algoritmos de pesquisa, de inserção e de remoção de elementos. Para a árvore binária de pesquisa, apresentam-se também os algoritmos mais complexos, como as travessias em profundidade recursivas, o balanceamento de uma árvore e a sua visualização hierarquizada. São também apresentadas as travessias em profundidade não recursivas, recorrendo a uma pilha, e a travessia por níveis, recorrendo a uma fila. São ainda apresentadas a inserção e a pesquisa de elementos numa árvore binária de pesquisa, em que o elemento processado é colocado na raiz da árvore. Para as restantes árvores são explicados apenas os seus algoritmos de inserção e de remoção de elementos, que são mais complexos do que os da árvore binária de pesquisa, devido à exigência de manutenção do equilíbrio da árvore. Finalmente, é apresentado o caso particular das árvores completas, como são os amontoados binários (binary heaps) e os seus algoritmos. O Capítulo 5 é dedicado ao estudo dos algoritmos de pesquisa, de seleção e de ordenação. Começa por referir a questão da complexidade algorítmica, a notação O maiúsculo e as principais ordens de complexidade algorítmica. A primeira parte do capítulo é dedicada às principais técnicas de pesquisa. São apresentados os algoritmos de pesquisa sequencial, de pesquisa sequencial numa sequência ordenada, de pesquisa binária (nas suas versões repetitiva e recursiva), de pesquisa ternária e de pesquisa por interpolação. Depois, são abordados os aspetos fundamentais da pesquisa por dispersão, como as funções de dispersão, os métodos para resolver o problema das colisões, nomeadamente a sondagem linear, a sondagem quadrática e o encadeamento de elementos, e os respetivos algoritmos de inserção e de pesquisa de elementos numa tabela de dispersão. Também é explicado o princípio da pesquisa por indexação. A segunda parte do capítulo é dedicada aos algoritmos de seleção, como a pesquisa de máximo, de mínimo, do primeiro que serve, do melhor que serve, do pior que serve e do K-ésimo menor elemento. A terceira parte do capítulo é dedicada às principais técnicas de ordenação, como sejam a ordenação por seleção, a ordenação por troca, a ordenação por inserção e a ordenação por fusão, sendo apresentados os algoritmos mais representativos de cada técnica e as suas otimizações. São apresentados os algoritmos de ordenação Sequencial, Seleção, Bubble, Shell (numa versão de trocas), Shaker, Inserção, Shell (na sua versão original) e Fusão de Listas. De seguida, são apresentados os algoritmos de ordenação recursivos Merge (com e sem sequência auxiliar) e Quick. De seguida, é apresentado o algoritmo de ordenação Heap, que usa a sequência como se fosse um amontoado binário, tirando partido da eficiência dos seus algoritmos de xvi

© FCA - Editora de Informática


PREFÁCIO

manipulação. Finalmente, são desenvolvidos dois aspetos importantes dos algoritmos de ordenação, designadamente a sua generalização e a avaliação do seu desempenho. O Capítulo 6 é dedicado à caracterização das memórias, nomeadamente da memória de acesso aleatório, da memória fila, da memória pilha, da memória associativa e da memória fila com prioridade. São apresentadas as suas características e as suas operações básicas. Também são referidas as diferentes alternativas de utilização da memória do computador para implementar estruturas de dados, ou seja, as implementações estáticas, semiestáticas e dinâmicas. Finalmente, são expostas as estruturas de dados que podem ser usadas para concretizar os diferentes tipos de implementação de cada memória. O Capítulo 7 apresenta as memórias fila (Queue) e pilha (Stack) e começa por descrever a sua utilidade. Começa por apresentar as implementações estática, semiestática e dinâmica, configuradas para armazenar elementos definidos externamente através de um ficheiro de interface. As operações de colocação e de remoção de elementos nas implementações estática e dinâmica são explicadas graficamente, para melhor compreensão da utilização das estruturas de dados de suporte das memórias. São também apresentados vários exemplos simples e de média complexidade de utilização destas memórias. Depois, apresenta uma implementação genérica das memórias fila e pilha, baseada numa lista ligada. É mostrada uma nova simulação das Torres de Hanói baseada em pilhas. De seguida, é apresentado um exemplo de transformação de uma solução recursiva, numa solução repetitiva que utiliza uma pilha, como elemento armazenador de informação. Finalmente, são descritas as características e as operações básicas da memória fila duplamente terminada (Deque). O Capítulo 8 apresenta a memória associativa (Content Access Memory) e começa por descrever a sua utilidade. Aborda o elemento estruturado, constituinte da memória associativa, que é definido através de um tipo de dados da responsabilidade do utilizador. Depois, apresenta as implementações estática, semiestática e dinâmicas (linear, logarítmica, por dispersão e hierárquica), configuradas para armazenar um elemento estruturado. As operações de colocação e de remoção de elementos nas implementações estática e dinâmicas são explicadas graficamente, para melhor compreensão da utilização das estruturas de dados de suporte da memória associativa. É apresentado um exemplo de utilização desta memória para gerir uma base de dados. Finalmente, é mostrada uma implementação genérica baseada numa lista biligada. O Capítulo 9 é dedicado ao estudo da fila com prioridade (Priority Queue) e começa por descrever a sua utilidade. São apresentadas as implementações estática ordenada, estática não ordenada, dinâmica ordenada e baseada num amontoado binário, configuradas através de um tipo de dados da responsabilidade do utilizador. As operações de colocação e de remoção de elementos são explicadas graficamente, para melhor compreensão da sua funcionalidade. Depois, apresenta um exemplo de utilização desta memória para ordenar informação. Finalmente, é mostrada uma implementação genérica baseada num amontoado binário.

© FCA - Editora de Informática

xvii


ESTRUTURAS DE DADOS E ALGORITMOS EM C

O Capítulo 10 é dedicado ao estudo do dígrafo/grafo (Digraph/Graph). Começa pela descrição das suas propriedades e pela definição de alguns conceitos associados a este tipo de dados abstrato. São apresentadas as implementações: estática, baseada numa matriz de adjacências; e dinâmica, baseada numa lista de adjacências, sendo discutidas as suas vantagens e desvantagens. De seguida, é concretizada a implementação dinâmica das suas operações básicas, que vai servir de base à exposição de um conjunto de algoritmos típicos de dígrafos e grafos. Primeiro, são estudados os algoritmos de travessia, em profundidade Depth First Search e em largura Breadth First Search, sendo de seguida apresentadas algumas das suas aplicações, como a ordenação topológica e a deteção de componentes fortemente conexas, usando o algoritmo de Kosaraju-Sharir. Depois, são estudados os algoritmos relacionados com a conectividade de um dígrafo/grafo, começando pela deteção dos vértices alcançáveis, pelos algoritmos de Dijkstra, de Bellman-Ford e variantes de travessias em largura e em profundidade, para determinar o caminho mais curto de um vértice para todos os seus vértices alcançáveis, e pelo algoritmo de Floyd para determinar todos os pares de caminhos mais curtos. São também estudados os algoritmos de Prim e de Kruskal para determinar a árvore abrangente de custo mínimo de um grafo. Também é apresentado o algoritmo de Warshall para fazer o fecho transitivo de um dígrafo. Finalmente, são apresentados os algoritmos para determinar caminhos e circuitos hamiltonianos e eulerianos em grafos. O Capítulo 11 apresenta diversos tópicos de programação. Mostra alguns aspetos avançados da linguagem C, como as uniões e os operadores bitwise, que são normalmente usados em programação de sistemas operativos, as sequências de ponteiros para funções que permitem implementar aplicações baseadas em menus (menu driven applications) e a definição de subprogramas com lista variável de parâmetros. Também são apresentados vários aspetos associados à compilação, depuração e execução de programas, como as facilidades disponibilizadas pelo pré-processador, nomeadamente: as macros de substituição, a compilação condicional e as asserções, a compilação automática de aplicações usando makefiles e o tratamento de sinais. Apresenta ainda um tipo de dados abstrato, para fazer o controlo centralizado de erros em aplicações distribuídas por diversos módulos, e o tipo de dados abstrato conjunto disjunto (Disjoint Set) e os algoritmos Union/Find. Depois, apresenta o acesso indexado a ficheiros. De seguida, são apresentados três exemplos de pesquisa exaustiva para determinar o quadrado mágico (magic square problem), os subconjuntos de um conjunto (subset sum problem) e o carregamento otimizado da mochila (knapsack problem). Por último, são apresentados quatro algoritmos numéricos e a sua implementação eficiente, como o cálculo da potência, a avaliação do polinómio e a multiplicação de polinómios e de matrizes. Esta obra disponibiliza ainda um Glossário com a correspondência dos principais termos técnicos para o português do Brasil.

xviii

© FCA - Editora de Informática


ESTRUTURAS DE DADOS E ALGORITMOS EM C

Em alternativa, podemos implementar a operação de remoção repetitiva que se apresenta no Algoritmo 4.41. Se a árvore estiver vazia, a remoção termina. Caso contrário, a travessia não recursiva encontra o elemento se estiver na árvore. Se o elemento tem dois filhos, então é preciso fazer uma nova travessia não recursiva para encontrar o menor elemento da sua subárvore direita, copiar esse elemento e removê-lo da árvore. Nos restantes casos, são ajustadas as ligações do pai do elemento removido ou da raiz da árvore caso o elemento removido esteja na raiz. Mas, para fazer o percurso inverso do elemento removido até à raiz, para reequilibrar a árvore, é necessário usar uma pilha para armazenar os nós ao longo da travessia da árvore. Quando a remoção do elemento é efetuada com sucesso, a pilha é esvaziada e todos os nós do caminho são percorridos no sentido inverso e são equilibrados, tal como no algoritmo de inserção. Finalmente, a pilha é destruída e a operação de remoção termina. Este algoritmo é mais extenso e necessita de uma pilha, mas tem a vantagem de ser implementado num único subprograma. Como exercício de treino, adapte o Programa 4.1 para simular a inserção e a remoção de elementos na árvore AVL. Faça duas versões do programa: na primeira teste os algoritmos recursivos e na segunda teste os algoritmos repetitivos.

4.4 Árvore rubinegra A árvore rubinegra (árvore ARN) (em inglês RBT – Red-Black Tree) é uma árvore de pesquisa equilibrada em altura, proposta por Leonidas Guibas e Robert Sedgewick em 1978, em que cada nó é vermelho ou preto. Limitando a maneira de colorir os nós da árvore, podemos garantir que nenhum caminho tem mais do dobro dos nós do que qualquer outro caminho, pelo que a árvore fica aproximadamente equilibrada, sendo a altura de uma árvore com N elementos no máximo 2×log2 (N+1). As regras de coloração de um nó da árvore rubinegra são as seguintes: 

Cada nó ou é vermelho ou é preto;

A raiz da árvore e os nós externos são pretos;

Se um nó é vermelho, os seus dois filhos são pretos. O mesmo é dizer que um nó vermelho tem um pai preto. Ou seja, nunca existem dois nós vermelhos seguidos no mesmo caminho;

Todos os caminhos desde um nó qualquer (incluindo a própria raiz) até um nó externo têm o mesmo número de nós pretos.

A Figura 4.30 apresenta duas árvores rubinegras, sendo que os nós vermelhos estão a branco. Cada caminho a partir da raiz tem dois nós pretos. O número de nós pretos da raiz até uma folha define a designada altura preta (black tree height).

160

© FCA - Editora de Informática


ÁRVORES

Para implementar esta operação de forma repetitiva, precisamos de uma pilha para manter os nós percorridos pela travessia, de maneira a poder fazer o percurso inverso até à raiz (usando a mesma estratégia do Algoritmo 4.41), fazendo as rotações para corrigir a coloração dos nós e para inclinar a árvore para a esquerda. No entanto, a sua implementação não é trivial; antes pelo contrário, é complexa e bastante extensa. Como a travessia para determinar o nó a remover vai fazendo rotações de nós pelo caminho, então a ligação interna dos nós da árvore vai ficando alterada e, se não houver a respetiva atualização, mesmo armazenando os nós percorridos numa pilha não é depois possível fazer a travessia ascendente para ajustar corretamente a árvore. Como exercício de treino, adapte o Programa 4.1 para simular as versões recursivas da inserção e da remoção de elementos na árvore rubinegra.

4.5 Árvore autoequilibrada A árvore binária de pesquisa autoequilibrada (Splay Tree) foi proposta por Daniel Sleator e Robert Tarjan em 1985, com o objetivo de ter uma árvore binária de pesquisa que assegura que um qualquer número de operações sucessivas a partir de uma árvore vazia tem um custo linear logarítmico; o que não significa que o custo por operação seja logarítmico. Para uma longa sequência de operações, algumas das operações podem ter um custo maior, enquanto outras podem ter um custo menor, pelo que cada operação tem um custo amortizado logarítmico. É uma árvore binária de pesquisa, em que as operações que visitam os nós reorganizam a árvore. Sempre que um elemento é inserido, pesquisado ou removido da árvore, o elemento, ou o seu pai no caso da remoção, é colocado na raiz da árvore através da reorganização dos elementos da árvore – operação que em inglês se designa por splaying –, usando rotações à esquerda e à direita de elementos. Repare que para implementar esta operação não são necessários atributos adicionais nos nós da árvore. A lógica subjacente baseia-se no facto de ser previsível que o elemento venha a ser de novo acedido no futuro próximo, daí ficar na raiz da árvore para o futuro acesso ser mais eficiente. Por outro lado, a consequente alteração da posição de outros elementos próximos do elemento acedido vai tornar mais eficiente o seu acesso posterior. A operação de reorganização da árvore é uma sucessão de rotações, que podem ser rotações simples, que envolvem um elemento e o seu pai, e rotações duplas, que envolvem um elemento, o seu pai e o seu avô, para colocar o elemento acedido na raiz da árvore. A rotação simples – designada por rotação zig – é aplicada quando o pai do elemento é a raiz da árvore. Nesse caso, uma simples rotação à direita, se for um filho esquerdo, ou uma simples rotação à esquerda, se for um filho direito, coloca-o na raiz da árvore. A rotação dupla pode ser de dois tipos. Se o elemento é um elemento externo da árvore, ou seja, se é o filho esquerdo de um filho esquerdo ou um filho direito de um filho direito, então duas rotações simples seguidas do seu avô, respetivamente, à direita ou à

© FCA - Editora de Informática

175


ESTRUTURAS DE DADOS E ALGORITMOS EM C

esquerda, colocam-no na raiz da árvore. Esta rotação é designada por rotação zigzig e a Figura 4.38 apresenta-a, no caso da rotação dupla para a direita. Nó 1

Nó 3

Nó 2

Nó 2

Nó 3

Nó 1

Figura 4.38 - Rotação zigzig à direita

Se o elemento é um elemento interno da árvore, ou seja, se é o filho esquerdo de um filho direito ou um filho direito de um filho esquerdo, então uma rotação simples do seu pai, seguida de uma rotação simples do seu avô no sentido contrário, coloca-o na raiz da árvore. Esta rotação é designada por rotação zigzag e a Figura 4.39 apresenta-a, no caso da rotação dupla para a esquerda-direita. Nó 1

Nó 3 Nó 2

Nó 2

Nó 1

Nó 3

Figura 4.39 - Rotação zigzag à esquerda-direita

4.5.1 Estrutura do nó O nó da árvore autoequilibrada é idêntico ao nó da árvore binária de pesquisa – vamos designá-lo por SplayNode –, sendo o tipo nó decomposto apresentado na Figura 4.40. Como exercício de treino, implemente os algoritmos de criação e de destruição de um nó decomposto. Implemente também as operações de rotação à direita e à esquerda, que são idênticas às que foram apresentadas para a árvore binária de pesquisa com inserção na raiz da árvore (ver Algoritmo 4.28). Implemente ainda as operações de impressão da árvore (SplayDisplay) e de destruição da árvore (SplayDestroy). 176

© FCA - Editora de Informática


GRAFOS Matriz de Adjacências 0 1 0 0 1 0 0 1 0 0 1 0 0 1 0 0 0 0 0 1 0 0 0 0 0

2 1

3 5

0 1 1 0 0

Matriz de Warshall 1 1 1 0 1 1 1 0 1 0 0 0 0 0 0

4

2 1 1 1 1 0

1

3 5

4

Figura 10.45 - Demonstração da aplicação do algoritmo de Warshall

10.12 Caminhos e circuitos hamiltonianos Um caminho hamiltoniano é um caminho simples que percorre todos os vértices do grafo, o que implica que o grafo tem de ser conexo. Um grafo diz-se conexo se só possuir uma componente conexa, ou seja, se não tiver vértices desconexos. Logo, para determinar se um grafo é conexo, é preciso verificar o grau de todos os seus vértices e assegurar que nenhum vértice tem grau nulo, tal como se apresenta no Algoritmo 10.18. É preciso ter em consideração que, num grafo, cada aresta (vi, vj) é representada pelas arestas orientadas (vi, vj) e (vj, vi), pelo que os semigraus incidente e emergente de um vértice são iguais, sendo o grau do vértice representado por qualquer um deles. O algoritmo testa todas as situações de erro, devolvendo o respetivo código de erro. Se o grafo não for conexo, então devolve o código de erro NO_CONNECTED, senão devolve OK. int GraphIsConnected (PtDigraph pgraph) { PtBiNode V; if (pgraph == NULL || pgraph->Type) return NO_DIGRAPH; if (pgraph->NVertexes == 0) return DIGRAPH_EMPTY; if (pgraph->NEdges == 0) return NO_EDGE; for (V = pgraph->Head; V != NULL; V = V->PtNext) if (((PtVertex) V->PtElem)->InDeg == 0) return NO_CONNECTED; return OK; } Algoritmo 10.18 - Verificação de grafo conexo

Uma forma de detetar um caminho hamiltoniano entre dois vértices consiste em fazer uma travessia em profundidade a partir do primeiro vértice (vértice de partida) até atingir o segundo vértice (vértice de chegada), atravessando exatamente V−1 arestas. A deteção de um caminho hamiltoniano que se apresenta no Algoritmo 10.19 é, portanto, uma adaptação do algoritmo recursivo de travessia em profundidade, que tem dois vértices como parâmetros de entrada e que verifica se existe um caminho simples entre eles. © FCA - Editora de Informática

521


GRAFOS

Como exercício de treino, implemente um programa que cria um grafo e que apresenta no monitor o grafo, todos os caminhos hamiltonianos entre pares de vértices distintos e os circuitos hamiltonianos existentes para todos os seus vértices.

10.13 Circuitos e caminhos eulerianos Um grafo tem um circuito euleriano se, e só se, é conexo e se todos os seus vértices têm um grau par. Nesse caso, o grafo designa-se por grafo euleriano. O Algoritmo 10.21 utiliza esta propriedade para determinar se o grafo tem ou não um circuito euleriano. O algoritmo testa todas as situações de erro, devolvendo o respetivo código de erro. Para não ter de definir um novo código de erro, vamos usar o código NO_PATH para indicar que num grafo conexo não existe um circuito euleriano. int GraphHasEulerTour (PtDigraph pgraph) { PtBiNode V; unsigned int Error, Degree; if ((Error = GraphIsConnected (pgraph)) != OK) return Error; for (V = pgraph->Head; V != NULL; V = V->PtNext) { Degree = ((PtVertex) V->PtElem)->InDeg; if (Degree == 0 || Degree % 2 != 0 ) return NO_PATH; } return OK; } Algoritmo 10.21 - Verificação de existência de circuito euleriano num grafo

Num grafo euleriano, como todos os vértices têm um número par de arestas, um caminho que começa num vértice consegue sempre terminar nesse vértice. No entanto, o caminho pode não percorrer todas as arestas do grafo. Assim, para obter um circuito é preciso fazer vários ciclos (circuitos parcelares) compostos por arestas distintas, de modo a esgotar todas as arestas do grafo. Para percorrer as arestas do grafo apenas uma vez, podemos retirá-las do grafo durante os sucessivos percursos. Assim, este modo de atuação corrompe o grafo, pelo que deve ser previamente copiado. Portanto, podemos construir um circuito euleriano com um conjunto de circuitos parcelares disjuntos, que depois são interligados nos vértices de conexão. Ou seja, os circuitos parcelares são imbricados de maneira a criarem o circuito euleriano final. Este é o princípio do algoritmo de Carl Hierholzer (1873) para determinar um circuito euleriano. Por exemplo, no grafo da Figura 10.47 temos os circuitos parcelares disjuntos (1, 2, 3, 1), (3, 4, 2, 5, 7, 4, 6, 3) e (6, 7, 8, 6), com o primeiro e o segundo ligados no vértice 3 e o segundo e o terceiro ligados no vértice 6. Para obtermos o circuito euleriano temos de substituir o vértice 3 do primeiro circuito pelo segundo circuito e substituir o vértice 6 do segundo circuito pelo terceiro circuito, obtendo assim o circuito final (1, 2, (3, 4, 2, 5, 7, 4, (6, 7, 8, 6), 3), 1). © FCA - Editora de Informática

525


Esta obra tem como objetivo fornecer uma competência sólida no desenvolvimento de programas de média e elevada complexidade e um conhecimento profundo sobre estruturas de dados avançadas e algoritmos complexos, usando a linguagem de programação C e aplicando o paradigma da programação modular. Assim, utiliza uma metodologia que dá particular ênfase à decomposição funcional das soluções, através da implementação de tipos de dados abstratos. Inclui exemplos, exercícios, programas e leituras recomendadas, com vista a facilitar a aprendizagem dos alunos.

Nesta nova edição foram acrescentadas secções sobre Árvore Rubinegra, Árvore Autoequilibrada, Caminhos e Circuitos Hamiltonianos, Circuitos e Caminhos Eulerianos. Principais temas abordados no livro: Recursividade;

Para atingir este objetivo, o livro está organizado em cinco grandes temas: Estudo do paradigma da programação modular na linguagem C, apresentando os aspetos fundamentais para implementar tipos de dados abstratos aplicando a metodologia de programação defensiva;

Programação Modular; Listas; Árvores; Pesquisa, Seleção e Ordenação; Memórias; Filas e Pilhas;

Estudo das principais estruturas de dados dinâmicas;

Memórias Associativas;

Estudo das principais classes de algoritmos;

Filas com Prioridade;

Estudo da implementação dos diferentes tipos de memórias;

Grafos;

Estudo do tipo de dados abstrato grafo/dígrafo e seus algoritmos mais importantes.

Outros Tópicos de Programação.

Este livro é dirigido aos estudantes de disciplinas de programação, que frequentam licenciaturas que exijam conceitos sólidos de programação, um conhecimento profundo sobre algoritmos e estruturas de dados avançadas e implementação de tipos de dados abstratos na linguagem C, assim como a programadores. Este livro disponibiliza ainda a correspondência dos principais termos técnicos para o português do Brasil.

ISBN 978-972-722-769-3

9 789727 227693

Implementação de tipos de dados abstratos na linguagem C; Algoritmos e estruturas de dados fundamentais para escrever programas de média e elevada complexidade.

Programas apresentados na obra disponíveis em www.fca.pt, até o livro se esgotar ou ser publicada nova edição atualizada ou com alterações, ou na página pessoal do autor, em http://sweet.ua.pt/~f706/ensino/livro3/html.


Turn static files into dynamic content formats.

Create a flipbook
Issuu converts static files into: digital portfolios, online yearbooks, online catalogs, digital photo albums and more. Sign up and create your flipbook.