Top Banner
MAKRON Books Estruturas de Dados Usando C EDITORA AFILIADA
904

Estruturas.de.dados.usando.c. .tenenbaum

Jul 23, 2015

Download

Education

Diego Silva
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
  • MAKRONBooks

    Estruturas de DadosUsando C

    EDITORA AFILIADA

  • MAKRONBooks Estruturas de Dados

    Usando C

    Aaron Ai Tenenbaum, Yedidyah Langsam,Moshe J. Augenstein

    TraduoTeresa Cristina Flix de Souza

    Reviso Tcnica e Adaptao dos ProgramasRoberto Carlos MayerProfessor do Departamento de Cincia da Computaoda Universidade de So PauloDiretor da MBI Mayer & Bunge Informtica S/C Ltda

    MAKRON Books do Brasil Editora Ltda.So PauloRua Tabapu, 1348 Itaim BibiCEP 04533-004(011) 829-8604 e (011) 820-6622

    Rio de Janeiro Lisboa Bogot Buenos Aires Guatemala Madrid Mxico New York Panam San Juan Santiago

    Auckland Hamburg Kuala Lumpur London Milan Montreal New Delhi Paris Singapore Sydney Tokyo Toronto

  • Do originalData Structures Using CCopyright 1989 by Prentice Hall, Inc.Original em ingls publicado pela McGraw-Hill, Inc.Copyright. 1995 da MAKRON Books do Brasil Editora ]Todos os direitos para a lngua portuguesa reservados pela MAKRON Books do BrasilEditora Ltda.Nenhuma parte desta publicao poder ser reproduzida, guardada pelo sistema "retrieval"ou transmitida de qualquer modo ou por qualquer outro meio, seja este eletrnico, mecnico,de fotocpia, de gravao, ou outros, sem prvia autorizao, por escrito, da Editora.

    EDITOR: MILTON MIRA DE ASSUMPO FILHOGerente Editorial: Daisy Pereira DanielProdutora Editorial: Mnica Franco JacinthoProdutor Grfico: Jos RodriguesEditorao Eletrnica e Fotolitos: E.R.J. Informtica Ltda.

    Dados Internacionais de Catalogao na Publicao (CIP)(Cmara Brasileira do Livro, SP, Brasil)

    Tenenbaum, Aaron M.Estruturas de dados usando C / Aaron M. Tenenbaum,

    Yedidyah Langsam, Moshe J. Augenstein ; traduoTeresa Cristina Flix de Souza ; reviso tcnica eadaptao dos programas Roberto Carlos Mayer. So Paulo : MAKRON Books, 1995.

    ISBN 85-346-0348-0

    1. C (Linguagem de programao para computadores)2. Dados - Estruturas (Cincia da computao)I. Langsam, Yedidyah, 1952- II. Augenstein, MosheJ., 1947- III. Ttulo.

    95-0783 CDD-005.73

    ndices para catlogo sistemtico:1. Dados : Estruturas : Processamento de dados

    005.732. Estruturas de dados : Processamento de dados

    005.73

  • minha esposa, Miriam (AT)A minha esposa, Vivienne Esther (YL)

    A minha filha, Chaya (MA)

  • MAKRONBooks

    Sumrio

    Prefcio XVII

    1. Introduo s Estruturas de Dados

    1.1 Informaes e SignificadoInteiros Binrios e DecimaisNmeros ReaisStrings de CaracteresHardware & Software0 Conceito de ImplementaoUm ExemploTipos de Dados AbstratosSeqncias Como Definies de ValoresUm TDA para Strings de Caracteres de Tamanho VarivelTipos de Dados em CPonteiros em C Estruturas de Dados e C

    Exerccios1.2. Vetores em C

    0 Vetor Como um TDAUsando Vetores Unidimensionais

    ,1

    14679

    111218232527273032343637

    VII

  • VIII Estruturas de Dados Usando C

    Implementando Vetores UnidimensionaisVetores Como ParmetrosStrings de Caracteres em COperaes com Strings de CaracteresVetores BidimensionaisVetores Multidimensionais

    Exerccios1.3. Estruturas em C

    Implementando EstruturasUniesImplementao de UniesParmetros de EstruturaRepresentando Outras Estruturas de DadosNmeros RacionaisAlocao de Armazenamento e Escopo de Variveis

    Exerccios

    2. A Pilha

    2.1. Definio e ExemplosOperaes PrimitivasUm ExemploA Pilha Como um Tipo de Dado Abstrato

    Exerccios2.2. Representando Pilhas em C

    Implementando a Operao POPVerificando Condies ExcepcionaisImplementando a Operao Push

    Exerccios2.3. Um Exemplo: Infixo, Posfixo e PrefixoDefinies Bsicas e ExemplosAvaliando uma Expresso PosfixaPrograma para Avaliar uma Expresso PosfixaLimitaes do ProgramaConvertendo uma Expresso da Forma Infixa para a PosfxalPrograma para Converter uma Expresso da Forma Infixa na Forma Posfixa .

    Exerccios

    39434445465053576265686973737783

    .86

    868891959697

    102105105109111111114116119120126129

  • Sumrio IX

    3. Recursividade

    3.1. Definies Recursivas e ProcessosA Funo FatorialMultiplicao de Nmeros NaturaisA Seqncia de FibonacciA Busca BinriaPropriedades das Definies ou Algoritmos Recursivos

    Exerccios3.2. Recursividade em C

    Fatorial em COs Nmeros de Fibonacci em CBusca Binria em CCadeias RecursivasDefinio Recursiva de Expresses Algbricas

    Exerccios3.3. Escrevendo Programas Recursivos

    0 Problema das Torres de HanoiConverso da Forma Prefixa para a Posfixa Usando Recursividade

    Exerccios3.4. Simulando a Recursividade

    Retorno de uma FunoImplementando Funes RecursivasSimulao de FatorialAprimorando a Rotina SimuladaEliminando GotosSimulando as Torres de Hanoi

    Exerccios3.5. Eficincia da RecursividadeExerccios

    4. Filas e Listas

    4.1. A Fila e sua Representao SeqencialA Fila Como um Tipo de Dado AbstratoImplementao de Filas em C

    132

    132133136137138142143145145150152154155159162164171176180182184185190192195202204206

    207

    207209

  • X Estruturas de Dados Usando C

    A Operao InsertA Fila de PrioridadeImplementao em Vetor de uma Fila de Prioridade

    Exerccios4.2. Listas LigadasInserindo e Removendo Ns de uma ListaImplementao Ligada de PilhasAs Operaes Getnode e FreenodeImplementao Ligada de FilasListas Ligadas Como Estrutura de DadosExemplos de Operaes de ListaImplementao em Lista de Filas de PrioridadeNs de Cabealho

    Exerccios4.3. Listas em C

    Implementao de Listas em Vetor ,Limitaes da Implementao em VetorAlocando e Liberando Variveis DinmicasListas Ligadas Usando Variveis DinmicasFilas Como Listas em CExemplos de Operaes de Listas em CListas No-Inteiras e No-HomogneasComparando a Implementao em Vetor e a Dinmica de ListasImplementando Ns de Cabealho

    Exerccios ,4.4. Um Exemplo: Simulao Usando Listas Ligadas

    0 Processo de SimulaoEstruturas de Dados0 Programa de Simulao

    Exerccios4.5. Outras Estruturas de Lista

    Listas CircularesA Pilha Como uma Lista CircularA Fila Como uma Lista CircularOperaes Primitivas Sobre Listas Circulares0 Problema de Josephus

    215216218220223225230231233235238241241244245245249250256258260262264265265268269271272276279279281282283285

  • Sumrio XI

    Ns de CabealhoSoma de Inteiros Positivos Longos Usando Listas CircularesListas Duplamente LigadasSoma de Inteiros Longos Usando Listas Duplamente Ligadas

    Exerccios

    5. rvores5.1. rvores Binrias

    Operaes Sobre rvores BinriasAplicaes de rvores Binrias

    Exerccios5.2. Representaes de rvores Binrias

    Representao de Ns de rvores BinriasNs Internos e ExternosRepresentao Implcita em Vetores de rvores BinriasEscolhendo uma Representao de rvore BinriaPercursos de Arvores Binrias em Crvores Binrias EncadeadasPercurso Usando um Campo Fatherrvores Binrias Heterogneas

    Exerccios5.3. Um Exemplo: o Algoritmo de Huffman

    0 Algoritmo de HuffmanUm Programa em C

    Exerccios5.4. Representando Listas Como rvores Binrias

    Localizando o Ksimo Elemento Eliminando um ElementoImplementando Listas Representadas Por Arvores em CConstruindo uma Lista Representada Por rvoreRevisitando o Problema de Josephus

    Exerccios5.5. rvores e Suas Aplicaes

    Representaes de rvores em C Percurso de rvores

    287288291294300

    303

    303311312318320320324325330331335340343346350353355360361364366371374376377378381385

  • XII Estruturas de Dados Usando C

    Expresses Gerais Como rvoresAvaliando uma rvore de ExpressesConstruindo uma rvore

    Exerccios5.6. Um Exemplo: rvores de JogosExerccios

    6. Classificao

    6.1. Viso GlobalConsideraes Sobre a EficinciaNotao 0Eficincia da Classificao

    Exerccios6.2. Classificaes Por Troca

    Classificao Por BolhaQuicksortEficincia do Quicksort

    Exerccios6.3. Classificao Por Seleo e Por rvore

    Classificao de Seleo DiretaClassificaes por Arvore Binria ..HeapsortO Heap Como uma Fila de PrioridadeClassificao Usando um HeapO Procedimento Heapsort

    Exerccios6.4. Classificaes Por Insero

    Insero SimplesClassificao de ShellClassificao por Clculo de Endereo

    Exerccios ,6.5. Classificaes por Intercalao e de Raiz

    O Algoritmo de Cook-KimClassificao de Razes

    Exerccios

    387390393395398406

    .408

    408411415418421424424427436439441442444448449452455457459459462466469472476477482

  • Sumrio XIII

    7. Operao de Busca

    7.1 Tcnicas Bsicas de Pesquisa0 Dicionrio Como um Tipo de Dado AbstratoNotao AlgortmicaOperao de Busca SeqencialEficincia da Operao de Busca SeqencialReoordenando uma Lista para Obter a Eficincia Mxima de BuscaOperao de Busca Numa Tabela OrdenadaA Busca Seqencial IndexadaA Busca BinariaBusca por Interpolao

    Exerccios7.2. Busca em rvores

    Insero Numa rvore de Busca BinariaEliminao Numa Arvore de Busca BinariaEficincia das Operaes de Arvore de Busca BinariaEficincia das Arvores de Busca Binaria No-Uniformesrvores de Busca timas

    rvores BalanceadasExerccios7.3. rvores de Busca Geral

    rvores de Busca MultidirecionaisPesquisando uma rvore MultidirecionalImplementando uma rvore MultidirecionalPercorrendo uma rvore MultidirecionalInsero Numa rvore de Busca Multidirecionalrvores-BAlgoritmos para a Insero na Arvore-BComputando Father e IndexEliminao em rvores de Busca MultidirecionaisEficincia das rvores de Busca MultidirecionaisAprimorando a Arvore-Brvores-Brvores de Busca DigitaisTries

    486

    486488490491493495497498501504506510513514517521523526535537537541542545548554563566571576580585587593

  • XIV Estruturas de Dados Usando C

    Exerccios7.4 Espalhamento

    Solucionando Colises de Espalhamento Com o Endereamento aBerto .Eliminando Itens de uma Tabela de espalhamento

    Eficincia dos Mtodos de RecomprovaoReordenamento da Tabela de EspalhamentoMtodo de BrentEspalhamento em rvore BinriaAperfeioamentos Com Memria AdicionalEspalhamento CombinadoEncadeamento SeparadoEspalhamento em Armazenamento ExternoO Mtodo SeparadorEspalhamento Dinmico e Espalhamento ExtensvelEspalhamento LinearSelecionando uma Funo de EspalhamentoFunes de Espalhamento PerfeitasClasses Universais de Funes de Espalhamento

    Exerccios

    8. Grafos e Suas Aplicaes .

    8.1. GrficosUma Aplicao de Grafos

    Representaes de Grafos em CFechamento TransitivoAlgoritmo de WarshallUm Algoritmo de Menor Caminho

    Exerccios8.2. Um Problema de Fluxo

    Melhorando uma Funo de FluxoUm Exemplo

    O Algoritmo e o ProgramaExerccios8.3. Representao Ligada de Grafos

    Revisitando o Algoritmo de Dijkstra

    593595

    .598603604607609612616620624628632633641649653659661

    .664

    664668670672676678681683686690692697699706

  • Sumrio XV

    Organizando o Conjunto de Ns de GrafoUma Aplicao no Escalonamento0 Programa em C

    Exerccios8.4. Percurso de Grafos e Florestas Geradoras

    Mtodos de Percurso de Grafos .Florestas Geradoras

    Grafos No-Orientados e Seus PercursosPercurso em ProfundidadeAplicaes do Percurso em ProfundidadeEficincia do Percurso em ProfundidadePercurso em LarguraArvores Geradoras MnimasAlgoritmo de KruskalO Algoritmo da Fila de rvores

    Exerccios

    9. Gerenciamento de Armazenamento

    9.1. Listas GeraisOperaes que Modificam uma Lista

    ExemplosA Representao em Lista Ligada de uma ListaRepresentao de ListasA Operao Crlist0 Uso de Cabealhos de ListasLiberando Ns de ListaListas Gerais em CLinguagens de Programao e Listas

    Exerccios9.2. Gerenciamento Automtico de Listas

    0 Mtodo de Contagem de RefernciasColeta de Lixo Algoritmos para Coleta de LixoColeta e CompactaoVariaes da Coleta de Lixo

    708710715719722723728729733737740740742745746747

    750

    750753755757760763764766770773775777777784786794802

  • XVI Estruturas de Dados Usando C

    Exerccios9.3. Gerenciamento da Memria Dinmica

    Compactao de Blocos de ArmazenamentoPrimeira Escolha, Melhor Escolha, Pior EscolhaAprimoramentos no Mtodo da Primeira EscolhaLiberando Blocos de Armazenamento0 Mtodo da Marca Limtrofe0 Sistema em TurmasOutros Sistemas em Turma

    Exerccios

    Bibliografia e Referncias

    ndice Analtico

    804806808810818819822825834837

    841

    865

  • Prefcio

    MAKRONBooks

    Este texto foi elaborado para um curso de dois semestres sobre estruturasde dados e programao. Durante vrios anos, ministramos um curso sobreestruturas de dados para estudantes que passaram por um curso de umsemestre de programao com linguagens de alto nvel e por um curso de umsemestre de programao usando linguagem de montagem.

    Descobrimos que investimos muito tempo ensinando tcnicas deprogramao porque os estudantes ainda no possuam experincia suficien-te em programao nem conseguiam implementar estruturas abstratas porconta prpria. Os alunos mais brilhantes ocasionalmente percebiam o queestvamos fazendo. Os mais fracos nunca conseguiram. Baseados nessaexperincia, chegamos concluso de que um primeiro curso de estruturasde dados deveria ser ministrado paralelamente a um segundo curso sobreprogramao. Este livro representa o resultado dessa concluso.

    O texto apresenta conceitos abstratos, demonstra como esses concei-tos so teis para a soluo de problemas e, em seguida, mostra como asabstraes podem concretizar-se por meio do uso de uma linguagem deprogramao. nfase igual atribuda s verses abstrata e concreta de umconceito para que os estudantes aprendam o conceito propriamente dito, suaimplementao e sua aplicao. A linguagem usada neste texto C. Essalinguagem bem adequada a esse tipo de curso porque dispe das estruturasde controle necessrias para tornar os programas legveis, e permite queestruturas de dados bsicas, como as pilhas, as listas ligadas e as rvores,sejam implementadas de vrias maneiras. Esse aspecto possibilita aos

    XVII

  • XVIII Estruturas de Dados Usando C

    estudantes acompanhar as opes e os compromissos que um programadorenfrenta numa situao real. C est tambm amplamente disponvel emvrios computadores diferentes e continua a crescer em popularidade. Se-gundo Kernighan e Ritchie, C "uma linguagem agradvel, expressiva everstil".

    O nico pr-requisito para os estudantes que usarem este texto umcurso de um semestre em programao. Os estudantes que passaram por umcurso de programao usando linguagens como FORTRAN, Pascal ou PL/Ipodem utilizar este texto juntamente com um dos textos elementares sobreC listados na bibliografia. O Captulo 1 fornece tambm informaes neces-srias para que tais estudantes se acostumem com a linguagem C.

    O Captulo 1 uma introduo s estruturas de dados. A Seo 1.1apresenta o conceito de uma estrutura de dados abstrata e o conceito de umaimplementao. As Sees 1.2 e 1.3 introduzem os vetores e as estruturasem C. As implementaes dessas duas estruturas de dados, alm de suasaplicaes, so descritas tambm. O Captulo 2 discute as pilhas e suaimplementao em C. Como esta a primeira estrutura de dados novaapresentada, inclumos uma ampla anlise dos detalhes de sua implemen-tao. A Seo 2.3 traz as notaes posfixas, prefixas e infixas. O Captulo 3aborda a recursividade, suas aplicaes e sua implementao. O Captulo 4apresenta filas, filas de prioridade e listas ligadas e suas implementaes usandoum vetor de ns disponveis e o armazenamento dinmico. O Captulo 5 discuteas rvores e o Captulo 6 apresenta a notao O, alm de cobrir a classificao.O Captulo 7 discute a operao de busca interna e externa. O Captulo 8apresenta os grafos; e o Captulo 9 analisa o gerenciamento do armazenamento.

    No final do livro, inclumos uma ampla bibliografia com cada refe-rncia classificada pelo captulo ou seo a que se aplica.

    Um curso de um semestre de estruturas de dados consiste na Seo1.1, Captulos 2-7, sees 8.1 e 8.2, e parte da 8.4. Partes dos Captulos 3, 6,7 e 8 podem ser omitidas se o tempo for escasso.

    O texto adequado para o curso C82 e partes dos cursos C87 e C813do Curriculum 78 (Communications of the ACM, maro de 1979); cursos UC1e UC8 dos Programas de Graduao em Sistemas de Informao (Communi-cations of the ACM, dezembro de 1973) e curso I1 do Curriculum 68(Communications of the ACM, maro de 1968). Em particular, o texto cobreem parte ou no todo os tpicos P1, P2, P3, P4, P5, S2, Dl, D2, D3 e D6 doCurriculum 78.

  • Prefcio XIX

    Os algoritmos so apresentados como intermedirios entre as des-cries em portugus e os programas em C. Estes foram escritos em estilo Cmisturado com comentrios em portugus. Os algoritmos permitem que oleitor se concentre no mtodo usado para solucionar um problema, sem sepreocupar com declaraes de variveis e peculiaridades da linguagem real.Ao transformar um algoritmo num programa, apresentamos essas questese destacamos os problemas que as acompanham.

    O padro de endentao usado para os programas em C e algoritmos uma verso livre do formato sugerido por Kernighan e Ritchie (The CProgramming Language, Prentice-Hall, 1978), que achamos bastante til.Adotamos tambm a conveno de indicar por meio de comentrios aconstruo sendo encerrada por cada ocorrncia de uma chave de fechamento(}). Juntamente com o padro de endentao, essa uma valiosa ferramentapara aprimorar a compreenso do programa. Distinguimos entre algoritmose programas usando o primeiro em itlico e o ltimo em romano.

    A maioria dos conceitos deste livro ilustrada por vrios exemplos.Alguns deles so tpicos importantes (isto , notao posfixa, aritmtica demltiplas palavras etc.) e podem ser tratados como tal. Outros ilustramdiferentes tcnicas de implementao (como o armazenamento seqencial dervores). O instrutor poder discutir quantos exemplos quiser. Os exemplospodem ser tambm passados para os estudantes como leitura adicional.Prevemos que um instrutor no conseguir discutir todos os exemplos comdetalhes suficientes durante um curso de um ou dois semestres. Achamos que,no estgio de desenvolvimento de um estudante para o qual este texto foielaborado, mais importante discutir vrios exemplos em detalhes do que umaampla variedade de tpicos superficialmente.

    Todos os algoritmos e programas deste livro foram testados e depu-rados. Gostaramos de agradecer a Miriam Binder e Irene LaClaustra porseu inestimvel apoio nessa tarefa. Sua dedicao ultrapassou a responsa-bilidade de ambas e suas sugestes foram sempre importantssimas. Evidente-mente, quaisquer erros remanescentes so de total responsabilidade dos autores.

    Os exerccios variam muito em termos de tipo e dificuldade. Algunsso exerccios de treinamento para assegurar a compreenso de tpicosapresentados no livro. Outros envolvem modificaes de tpicos e algoritmos.Outros ainda introduzem novos conceitos e so bastante desafiantes. Fre-qentemente, um grupo de exerccios sucessivos inclui o desenvolvimentocompleto de um novo tpico que pode ser usado como base para um projetoou como leitura adicional. O instrutor deve ter cuidado ao solicitar a resoluo

  • XX Estruturas de Dados Usando C

    dos exerccios para que o grau de dificuldade seja adequado ao nvel dosestudantes. imperativo que os estudantes desenvolvam vrios projetos deprogramao (de cinco a doze, dependendo do nvel de dificuldade) por semestre.

    Procuramos usar a linguagem C, conforme especificado na primeiraedio do livro de K & R. No usamos nenhum recurso encontrado atualmen-te em vrios compiladores de computadores pessoais (por exemplo, o Micro-soft (R) C e o Turbo C (R), da Borland), embora alguns desses recursostenham sido includos no padro ANSI. Em particular, passamos ponteirospara estruturas como parmetros, mesmo que o novo padro permita passara estrutura em si. Kernighan e Ritchie comentam em sua segunda edioque mais eficiente passar um ponteiro quando a estrutura grande. Nousamos tambm nenhuma funo que retorne uma estrutura como resultado.Evidentemente, necessrio informar aos estudantes quaisquer idiossincra-sias do compilador em questo que eles estejam usando. Inclumos tambmalgumas referncias a vrios compiladores C para computadores pessoais.

    Miriam Binder e Irene LaClaustra passaram horas digitando e corri-gindo o manuscrito original, e gerenciando uma grande equipe de estudantesque mencionamos a seguir. Sua cooperao e pacincia com a nossa mudanacontnua de idias sobre incluses e eliminaes so apreciadas sinceramente.

    Gostaramos de agradecer a Shaindel Zundel-Margulis, CynthiaRichman, Gittie Rosenfeld-Wertenteil, Mindy Rosman-Schreiber, NinaSilverman, Helene Turry e Devorah Sadowsky-Weinschneider por sua ines-timvel colaborao.

    A equipe do City University Computer Center merece meno espe-cial. Eles foram muito teis auxiliando-nos no uso das excelentes instalaesdo Centro. A mesma coisa pode-se dizer da equipe do Brooklyn CollegeComputer Center.

    Gostaramos de agradecer aos editores e equipe da Prentice-Halle principalmente aos revisores por seus teis comentrios e sugestes.

    Finalmente, agradecemos a nossas esposas, Miriam Tenenbaum,Vivienne Langsam e Gail Augenstein, por seus conselhos e estmulos durantea longa e rdua tarefa de produzir um livro como este.

    Aaron TenenbaumYedidyah LangsamMoshe Augenstein

  • MAKRONBooks

    Captulo 1

    Introduo s Estruturas de Dados

    Um computador uma mquina que manipula informaes. O estudo dacincia da computao inclui o exame da organizao, manipulao e utili-zao destas informaes num computador. Conseqentemente, muitoimportante para um estudante da cincia da computao entender os con-ceitos de organizao e manipulao de informaes para continuar o estudodo campo.

    1.1 INFORMAES E SIGNIFICADO

    Se a cincia da computao fundamentalmente o estudo da informao, aprimeira pergunta que surge : o que significa a informao? Infelizmente,embora o conceito de informao seja a base do campo inteiro, essa perguntano pode ser respondida com exatido. Por um lado, o conceito de informaona cincia da computao semelhante aos conceitos de ponto, linha e plano,na geometria: todos eles so termos indefinidos sobre os quais podem serfeitas afirmaes, mas eles podem ser explicados em termos de conceitoselementares.

    1

  • 2 Estruturas de Dados Usando C Cap. 1

    Na geometria, possvel discutir sobre o tamanho de uma linhaindependentemente do fato de o conceito de uma linha ser ele mesmoindefinido. O tamanho de uma linha uma medida de quantidade. De modosemelhante, na cincia da computao, podemos avaliar quantidades deinformaes. A unidade bsica da informao o bit, cujo valor compreendeuma entre duas possibilidades mutuamente exclusivas. Por exemplo, se uminterruptor de luz pode estar em uma das duas posies, mas no em ambassimultaneamente, o fato de ele estar na posio de "ligado" ou na posio de"desligado" um bit de informao. Se um dispositivo pode estar em mais de doisestados possveis, o fato de ele estar em determinado estado representa mais deum bit de informao. Por exemplo, se um dial tem oito posies possveis, o fatode ele estar na posio 4 exclui sete outras possibilidades, enquanto o fato de uminterruptor estar ligado exclui somente outra possibilidade.

    Voc pode visualizar esse fenmeno sob outro prisma. Vamos suporque tivssemos chaves de duas alternativas, mas pudssemos usar quantasdelas precisssemos. Quantas chaves desse tipo seriam necessrias pararepresentar um dial com oito posies? Evidentemente, uma chave s poderepresentar duas posies (Figura 1.1.1a). Duas chaves podem representarquatro posies diferentes (Figura 1.1.1b) e so necessrias trs chaves pararepresentar oito posies diferentes (Figura 1.1.1c). Em geral, n chavespodem representar 2n possibilidades diferentes.

    Os dgitos binrios 0 e 1 so usados para representar os dois possveisestados de determinado bit (na realidade, a palavra "bit" uma contraodas palavras "binary digit"). Dados n bits, uma string de n ls e 0s usadapara representar seus valores. Por exemplo, a string 101011 representa seischaves, estando a primeira delas "ativada" (1), a segunda "desativada" (0),a terceira ativada, a quarta desativada, a quinta e a sexta ativadas.

    Verificamos que so suficientes trs bits para representar oitopossibilidades. As oito possveis configuraes desses trs bits (000, 001, 010,011, 100, 101, 110 e 111) podem ser usadas para representar os inteiros de0 a 7. Entretanto, no h nada nas definies desses bits que impliqueintrinsecamente que determinada definio representa determinado inteiro.Qualquer atribuio de valores inteiros s definies de bits vlida desdeque no sejam atribudos dois inteiros mesma definio de bits. Assim queocorrer uma atribuio desse tipo, determinada definio de bit poder serinterpretada com ambigidade como um inteiro especfico. Examinemosvrios mtodos amplamente usados para interpretar definies de bits comointeiros.

  • Cap. 1 Introduo s estruturas de dados 3

    (c) Trs chaves (oito possibilidades).Figura 1.1.1

    Chave 1Desligado

    Ligado

    (a) Uma chave (duas possibilidades).

    Chave 1 Chave 2Desligado Desligado

    Desligado Ligado

    Ligado Desligado

    Desligado Ligado

    (b) Duas chaves (quatro possibilidades).

    Chave 1 Chave 2 Chave 3Desligado Desligado Desligado

    Desligado Desligado Ligado

    Desligado

    Desligado

    Ligado

    Ugado

    Ligado

    Ligado Ligado

    Ligado

    Desligado

    Desligado Desligado

    Ligado

    Desligado

    Ligado

    Ligado

    Ligado

    Desligado

    Ugado

  • 4 Estruturas de Dados Usando C Cap. 1

    INTEIROS BINRIOS E DECIMAIS

    O mtodo mais amplamente usado para interpretar definies de bits comointeiros no-negativos o sistema de numerao binrio. Nesse sistema,cada posio de bit representa uma potncia de 2. A posio da extremadireita representa 2o que equivale a 1, a prxima posio esquerdarepresenta 21 que 2, a prxima posio de bit representa 22, que equivalea 4, e assim por diante. Um inteiro representado por uma soma de potnciasde 2. Uma string toda de Os representa o nmero 0. Se aparecer um 1 emdeterminada posio de bit, a potncia de 2 representada por essa posiode bit ser includa na soma; mas, se aparecer um 0, essa potncia de 2 noser includa na soma. Por exemplo, o grupo de bits 00100110 apresenta lsnas posies 1, 2 e 5 (contando da direita para a esquerda com a posio daextrema direita considerada posio 0). Sendo assim, 00100110 representao inteiro 21 + 22 + 25 = 2 + 4 + 32 = 38. Sob esse prisma, toda string de bitsde tamanho n representa um inteiro no-negativo nico, entre 0 e 2n-1, e todointeiro no-negativo entre 0 e 2a"1 pode ser representado por uma nicastring de bits de tamanho n.

    Existem dois mtodos amplamente usados para representar nme-ros binrios negativos. No primeiro mtodo, chamado notao de comple-mento de um, um nmero negativo representado mudando cada bit emseu valor absoluto para a definio do bit oposto. Por exemplo, como 00100110representa 38, 11011001 usado para representar -38. Isso significa que obit da extrema esquerda de um nmero no mais usado para representaruma potncia de 2, mas reservado para o sinal do nmero. Uma string debits comeando com um 0 representa um nmero positivo, enquanto umastring de bits comeando com um 1 representa um nmero negativo. Emfuno de n bits, a faixa de nmeros que pode ser representada -2 ( n - l ) + 1(um 1 seguido por n - 1 zeros) a 2(n-1) - 1 (um 0 seguido por n - 1 uns).Observe que, com essa representao, existem duas representaes para onmero 0: um 0 "positivo" consistindo em todos os Os, e um zero "negativo"consistindo em todos os ls.

    O segundo mtodo que representa nmeros binrios negativos chamado notao de complemento de dois. Nessa notao, 1 somado representao de complemento de um de um nmero negativo. Por exemplo,como 11011001 representa -38 na notao de complemento um, 11011010 usado para representar -38 na notao de complemento de dois. Dados nbits, a faixa de nmeros que pode ser representada 2(n-1) (um 1 seguido por

  • Cap. 1 Introduo s estruturas de dados 5

    n - 1 zeros) a 2(n-1) -1 (um 0 seguido por n - 1 uns). Observe que -2(n-1) podeser representado em notao de complemento de dois, mas no em notaode complemento de um. Entretanto, seu valor absoluto, 2(n-1), no pode serrepresentado em ambas as notaes, usando n bits. Alm disso, observe queexiste apenas uma representao para o nmero 0 usando n bits na notaode complemento de dois. Para constatar isso, considere o 0 usando oito bits:00000000. O complemento de um 11111111, que o 0 negativo nessanotao. Somar 1 para produzir a forma de complemento de 2 resulta em100000000, que tem nove bits. Como somente oito bits so permitidos, o bitda extrema esquerda (ou a "sobra") descartado, deixando 00000000 comomenos 0.

    O sistema de numerao binrio definitivamente no o nicomtodo pelo qual os bits podem ser usados para representar inteiros. Porexemplo, uma string de bits pode ser usada para representar inteiros nosistema numrico decimal, da seguinte forma: quatro bits podem ser usadospara representar um dgito decimal entre 0 e 9 na notao binria descritaanteriormente. Uma string de bits de qualquer tamanho pode ser divididaem conjuntos consecutivos de quatro bits, com cada conjunto representandoum dgito decimal. Dessa forma, a string representa o nmero formado poresses dgitos decimais na notao decimal convencional. Por exemplo, nessesistema, a string de bits 00100110 separada em duas strings de quatro bitscada: 0010 e 0110. A primeira string representa o dgito decimal 2 e asegunda, o dgito decimal 6, de modo que a string inteira representa o inteiro26. Essa representao chamada decimal codificado em binrio.

    Uma caracterstica importante da representao de decimal codifi-cado em binrio de inteiros no-negativos que nem todas as strings de bitsso representaes vlidas de um inteiro decimal. Quatro bits podem serusados para representar um dentre 16 possibilidades diferentes, uma vezque existem 16 estados possveis para um conjunto de quatro bits. Entretan-to, na representao de inteiro decimal codificado em binrio, somente dezdessas 16 possibilidades so usadas. Ou seja, cdigos como 1010 e 1100, cujosvalores binrios so 10 ou acima, no so vlidos em nmeros decimaiscodificados em binrio.

  • 6 Estruturas de Dados Usando C Cap. 1

    NMEROS REAISO mtodo comum usado pelos computadores para representar nmeros reais a notao de ponto flutuante. Existem vrios tipos de notao de pontoflutuante e cada um tem caractersticas prprias. O conceito-chave que umnmero real representado por um nmero, chamado mantissa, vezes umabase elevada a uma potncia de inteiro, chamada expoente. Em geral, abase fixa, e a mantissa e o expoente variam de modo a representar nmerosreais diferentes. Por exemplo, se a base for fixada com 10, o nmero 387,53poderia ser representado como 38753 x 10-2. (Lembre-se de que 10-2 0,01.)A mantissa 38753 e o expoente -2. Outras possveis representaes so0,38753 x 103 e 387,53 x 10. Optamos pela representao na qual a mantissa um inteiro sem Os finais.

    Na notao de ponto flutuante que descrevemos (que no necessa-riamente implementada em nenhuma mquina particular exatamente comodescrito), um nmero real representado por uma string de 32 bits consis-tindo em uma mantissa de 24 bits seguida por um expoente de 8 bits. A base fixada em 10. Tanto a mantissa como o expoente so inteiros binrios decomplemento de dois. Por exemplo, a representao binria de 24 bits de38753 000000001001011101100001, e a representao binria de comple-mento de dois de oito bits de -2 11111110; a representao de 387,53 00000000100101110110000111111110. Voc encontrar a seguir outros n-meros reais e suas representaes de ponto flutuante:

    0 00000000000000000000000000000000

    100 00000000000000000000000100000010

    0,5 00000000000000000000010111111111

    0,000005 00000000000000000000010111111010

    12.000 00000000000000000000110000000011

    -387,53 11111111011010001001111111111110

    -12.000 11111111111111111111010000000011

  • Cap. 1 Introduo s estruturas de dados 7

    A vantagem da notao de ponto flutuante que ela pode ser usadapara representar nmeros com valores absolutos muito grandes ou muitopequenos. Por exemplo, na notao apresentada anteriormente, o maiornmero que pode ser representado (223-1) x 10127, que, na realidade, umnmero muito grande. O menor nmero positivo que pode ser representado 10-128, que muito pequeno. O fator limitante na exatido com a qual osnmeros podem ser representados em determinada mquina o nmero dedgitos binrios significativos na mantissa. Nem todo nmero entre o maiore o menor pode ser representado. Nossa representao s permite 23 bitssignificativos. Dessa forma, um nmero como 10 milhes e 1, que exige 24dgitos binrios na mantissa, precisaria ser aproximado para 10 milhes (1x 107), que s exige um dgito significativo.

    STRINGS DE CARACTERES

    Como sabemos, nem sempre a informao interpretada em termos num-ricos. Itens como nomes, ttulos de cargos e endereos precisam tambm serrepresentados de alguma maneira dentro de um computador. Para permitira representao desses objetos no-numricos, necessrio outro mtodo deinterpretao de strings de bits. Geralmente, tais informaes so represen-tadas na forma de strings de caracteres. Por exemplo, em alguns computa-dores, os oito bits 00100110 so usados para representar o caractere '&'. Umpadro de oito bits diferente usado para representar o caractere 'A', outropara representar o 'B', outro ainda para representar o 'C', e mais um paracada caractere que tenha uma representao em determinada mquina. Umamquina russa usa padres de bits para representar caracteres russos,enquanto uma mquina israelense usa padres de bits para representarcaracteres do hebraico. (Na realidade, os caracteres usados ficam transpa-rentes para a mquina; o conjunto de caracteres pode ser alterado usando-seuma cadeia de impresso diferente na impressora.)

    Se so usados oito bits para representar um caractere, podem serrepresentados at 256 caracteres diferentes, uma vez que existem 256padres de bits diferentes. Se a string 11000000 usada para representar ocaractere 'A' e 11000001 usada para representar o caractere 'B', a stringde caracteres 'AB' seria representada pela string de bits 1100000011000001.

  • 8 Estruturas de Dados Usando C Cap. 1

    Em geral, uma string de caracteres (STR) representada pela concatenaodas strings de bits que representam os caracteres individuais da string.

    Como acontece no caso dos inteiros, no h nada em determinadastring de bits que a torne intrinsecamente adequada para representar umcaractere especfico. A atribuio de strings de bits a caracteres pode sertotalmente aleatria, mas precisa ser adotada com coerncia. possvel quealguma regra conveniente seja usada ao atribuir strings de bits a caracteres.Por exemplo, duas strings de bits podem ser atribudas a duas letras, demodo que uma delas com o valor binrio menor seja atribuda letra quevem antes no alfabeto. Entretanto, essa regra apenas uma convenincia;ela no ditada por nenhuma relao intrnseca entre caracteres e stringsde bits. Na verdade, os prprios computadores variam o nmero de bitsusados para representar um caractere. Alguns computadores usam sete bits(e, portanto, s permitem at 128 caracteres possveis), alguns usam oito (at256 caracteres) e outros usam dez (at 1.024 caracteres possveis). O nmerode bits necessrio para representar um caractere em determinado computa-dor chamado tamanho do byte e um grupo de bits com esse nmero chamado byte.

    Observe que usar oito bits para representar um caractere significaque podem ser representados 256 caracteres. No se encontra freqentemen-te um computador que use tantos caracteres diferentes (embora se concebaque um computador inclua letras maisculas e minsculas, caracteresespeciais, itlicos, negritos e outros tipos de caracteres), de modo que muitoscdigos de oito bits no so usados para representar caracteres.

    Sendo assim, verificamos que a prpria informao no tem signifi-cado. Qualquer significado por ser atribudo a determinado padro de bits,desde que seja feito com coerncia. a interpretao de um padro de bitsque d significado. Por exemplo, a string de bits 00100110 pode ser inter-pretada como o nmero 38 (binrio), o nmero 26 (decimal codificado embinrio) ou o caractere '&'. Um mtodo de interpretar um padro de bits freqentemente chamado tipo de dado. Apresentamos vrios tipos de dados:inteiros binrios, inteiros no-negativos decimais codificados em binrios,nmeros reais e strings de caracteres. Da surgem estas perguntas: comodeterminar que tipos de dados estejam disponveis para interpretar padresde bits e que tipos de dados possam ser usados para interpretar determinadopadro de bits?

  • Cap. 1 Introduo s estruturas de dados 9

    HARDWARE & SOFTWARE

    A memria (conhecida tambm como armazenamento ou ncleo) de umcomputador apenas um grupo de bits (chaves). Em qualquer momento daoperao de um computador, determinado bit na memria 0 ou 1 (desati-vado ou ativado). A definio de um bit chamada seu valor ou seucontedo.

    Os bits na memria de um computador so agrupados em unidadesmaiores, como bytes. Em alguns computadores, vrios bytes so agrupadosem unidades chamadas palavras. Cada unidade desse tipo (byte ou palavra,dependendo da mquina) recebe a atribuio de um endereo, isto , umnome que identifica determinada unidade entre todas as unidades namemria. Em geral, esse endereo numrico, de modo que podemos falardo byte 746 ou da palavra 937. Um endereo freqentemente chamadoposio, e o contedo de uma posio so os valores dos bits que formam aunidade nessa posio.

    Todo computador tem um conjunto de tipos de dados "nativos". Issosignifica que ele construdo com um mecanismo para manipular padresde bits coerentes com os objetos que eles representam. Por exemplo, vamossupor que um computador contenha uma instruo para somar dois inteirosbinrios e introduzir sua soma em determinada posio na memria parauso posterior. Sendo assim, deve existir um mecanismo incorporado nocomputador para:

    1. extrair os padres de bits dos operandos de duas posies deter-minadas;

    2. produzir um terceiro padro de bits representando o inteirobinrio que seja a soma dos dois inteiros binrios representadospelos dois operandos; e

    3. armazenar o padro de bits resultante em determinada posio.O computador "sabe" interpretar os padres de bits nas posies

    especficas como inteiros binrios porque o hardware que executa a instruofoi projetado para fazer isso. Essa operao parecida com uma lmpadaque "sabe" que est acesa quando o interruptor est em determinada posio.

  • 10 Estruturas de Dados Usando C Cap. 1

    Se a mesma mquina tiver tambm uma instruo para somar doisnmeros reais, dever existir um mecanismo embutido separado para inter-pretar operandos como nmeros reais. So necessrias duas instruesdistintas para as duas operaes, e cada instruo carrega em si mesma umaidentificao implcita dos tipos de seus operandos, alm de suas posiesexplcitas. Portanto, cabe ao programador saber que tipo de dado est contidoem cada posio usada, e escolher entre usar uma instruo de soma deinteiros ou reais para obter a soma de dois nmeros.

    Uma linguagem de programao de alto nvel ajuda consideravel-mente nessa tarefa. Por exemplo, se um programador C declarar:int x, y;float a, b;

    ser reservado espao em quatro posies para quatro nmeros diferentes.Essas quatro posies podem ser referenciadas pelos identificadores x, y,a e b. Um identificador usado no lugar de um endereo numrico para citardeterminada posio de memria porque conveniente para o programador.O contedo das posies reservadas para x e y ser interpretado comointeiros, enquanto o contedo de a e 6 ser interpretado como nmeros deponto flutuante. O compilador responsvel pela converso de programas Cem linguagem de mquina traduzir o "+" contido na instruox = x + y;

    em soma de inteiros, e traduzir o "+" contido na instruoa = a + b;

    em soma de pontos flutuantes. Na verdade, um operador como o "+" um operadorgenrico porque tem vrios significados diferentes dependo de seu contexto. Ocompilador evita a necessidade de o programador especificar o tipo de soma quedeve ser efetuada, examinando o contexto e usando a verso adequada.

    importante reconhecer o papel-chave desempenhado pelas decla-raes numa linguagem de alto nvel. por meio das declaraes que oprogramador especifica como o contedo da memria do computador deve serinterpretado pelo programa. Ao fazer isso, uma declarao determina aquantidade de memria necessria para certa entidade, como o contedodessa memria deve ser interpretado, e outros detalhes fundamentais. Asdeclaraes especificam tambm para o computador o significado dos smbo-los das operaes que sero posteriormente usados.

  • Cap. 1 Introduo s estruturas de dados 11

    O CONCEITO DE IMPLEMENTAOAt agora, discutimos os tipos de dados como um mtodo de interpretaodo contedo da memria de um computador. O conjunto de tipos de dadosnativos que um computador pode suportar determinado pelas funesincorporadas em seu hardware. Entretanto, podemos visualizar o conceitode "tipo de dado" sob uma perspectiva totalmente diferente; no em termosdo que um computador pode fazer, mas em funo do que o usurio querfazer. Por exemplo, se algum quiser obter a soma de dois inteiros, esseusurio no se importar muito com o mecanismo detalhado pelo qual essasoma ser obtida. O usurio est interessado em manipular o conceitomatemtico de "inteiro", no em manipular bits do hardware. O hardwaredo computador pode ser usado para representar um inteiro e s ser tilpara esse propsito se a representao tiver sucesso.

    Quando o conceito de "tipo de dado" dissociado dos recursos dohardware do computador, um nmero ilimitado de tipos de dados pode serconsiderado. Um tipo de dado um conceito abstrato, definido por umconjunto de propriedades lgicas. Assim que um tipo de dado abstrato definido e as operaes vlidas envolvendo esse tipo so especificadas,podemos implementar esse tipo de dado (ou uma aproximao). Umaimplementao pode ser uma implementao de hardware, na qual ocircuito para efetuar as operaes necessrias elaborado e construdo comoparte de um computador; ou pode ser uma implementao de software,na qual um programa consistindo em instrues de hardware j existentes criado para interpretar strings de bits na forma desejada e efetuar asoperaes necessrias. Dessa maneira, a implementao de software incluiuma especificao de como um objeto do novo tipo de dado representadopor objetos dos tipos de dados j existentes, alm de uma especificao decomo esse objeto ser manipulado em conformidade com as operaes defi-nidas para ele. No restante deste livro, o termo "implementao" ser usadono sentido de "implementao de software".

  • 12 Estruturas de Dados Usando C Cap. 1

    UM EXEMPLO

    Ilustraremos esses conceitos com um exemplo. Vamos supor que o hardwarede um computador contenha uma instruo:MOVE (origem, dest, compr)que copia uma string de caracteres de bytes com o tamanho representadopor compr a partir de um endereo especificado por origem para um endereoespecificado por dest. (Apresentamos instrues do hardware com letrasmaisculas. O tamanho deve ser especificado por um inteiro e, por essa razo,ns o indicamos com letras minsculas, origem e dest podem ser especificadospor identificadores que representam posies de armazenamento.) Um exem-plo dessa instruo MOVE(a,b,3), que copia os trs bytes comeando naposio a para os trs bytes comeando na posio 6.

    Observe os papis distintos desempenhados pelos identificadores ae b nessa operao. O primeiro operando da instruo MOVE o contedoda posio especificada pelo identificador a. O segundo operando, entretanto,no o contedo da posio b, uma vez que esse contedo irrelevante paraa execuo da instruo. Em substituio, a prpria posio o operandoporque ela especifica o destino da string de caracteres. Embora um identifi-cador sempre represente uma posio, comum o uso de um identificadorcomo referncia ao contedo dessa posio. Sempre ficar evidente pelocontexto se um identificador est referenciando uma posio ou seu contedo.O identificador que aparece como primeiro operando de uma instruoMOVE refere-se ao contedo de memria, enquanto o identificador queaparece como segundo operando indica uma posio.

    Alm disso, partimos da premissa de que o hardware do computadorcontm as usuais instrues aritmticas e de desvio, que indicamos usandoa notao ao estilo C. Por exemplo, a instruo:z = x + y;

    interpreta o contedo dos bytes nas posies x e y como inteiros binrios,soma-os e insere a representao binria de sua soma no byte na posio z.(No operamos sobre inteiros maiores que um byte e ignoramos a possibilidadede sobrecarga.) Nesse caso, mais uma vez x ey so usados como referncias acontedos de memria, enquanto z usado como referncia a uma posio dememria, mas a interpretao correta evidenciada pelo contexto.

  • Cap. 1 Introduo s estruturas de dados 13

    Ocasionalmente, necessrio acrescentar uma quantidade numendereo para obter outro endereo. Por exemplo, se a uma posio namemria, possvel referenciar a posio quatro bytes frente de a. Nopodemos referir-nos a essa posio como a + 4, uma vez que essa notao reservada ao contedo inteiro da posio a + 4. Sendo assim, introduzimosa notao a[4] como uma referncia a essa posio. Apresentamos tambma notao a[x] para referenciar o endereo dado pela soma do contedo dosinteiros binrios do byte em x com o endereo a.

    A instruo MOVE exige que o programador especifique o tamanhoda string a ser copiada. Dessa forma, seu operador uma string de caracteresde tamanho fixo (isto , o tamanho da string deve ser conhecido). Uma stringde tamanho fixo e um inteiro binrio com tamanho em bytes podem serconsiderados tipos de dados nativos dessa mquina particular.

    Vamos supor que precisemos implementar strings de caracteres detamanhos variveis nessa mquina. Ou seja, permitiremos que os programa-dores usem uma instruo:MOVEVAR(origem, dest)para deslocar uma string de caracteres da posio especificada por origempara a posio representada por dest, sem precisar determinar qualquertamanho.

    Para implementar esse novo tipo de dado, devemos primeiro deter-minar como ele ser representado na memria da mquina e, em seguida,indicar como essa representao ser manipulada. Evidentemente, neces-srio conhecer a quantidade de bytes que deve ser deslocada para executaressa instruo. Como a operao MOVEVAR no especifica esse nmero, eleprecisa estar contido dentro da representao da prpria string de caracteres.Uma string de caracteres de tamanho varivel, com o tamanho l, pode serrepresentada por um conjunto contguo de / + 1 bytes (l < 256). O primeirobyte contm a representao binria do tamanho / e os bytes restantescontm a representao dos caracteres na string. As representaes de trsstrings como essas aparecem ilustradas na Figura 1.1.2. (Observe que osdgitos 5 e 9 nessa figura no substituem os padres de bits que representamos caracteres '5' e '9', mas os padres 00000101 e 00001001 [presumindo-seoito bits para um byte], que representam os inteiros cinco e nove. De modosemelhante, o 14 na Figura 1.1.2c substitui o padro de bits 00001110. Notetambm que esta representao muito diferente do modo como as stringsde caracteres so realmente implementadas em C.)

  • 14 Estruturas de Dados Usando C Cap. 1

    O programa para implementar a operao de MOVEVAR pode serescrito como segue (i uma posio de memria auxiliar):MOVE(origem, d e s t , 1 ) ;f o r ( i = l ; i < d e s t ; i + + )

    M O V E ( o r i g e m [ i ] , d e s t [ i ] , 1 ) ;De maneira semelhante, podemos implementar uma operao

    CONCATVAR(cl, c2, c3) para concatenar duas strings de caracteres detamanho varivel nas posies cl e c2, e colocar o resultado em c3. A Figura1.1.2c ilustra a concatenao das duas strings das Figuras 1.1.2a e b:

    (a)

    (b)

    (c)Figura 1.1.2 Strings de caracteres de tamanho varivel.

    /* move o comprimento */z = c l + c 2 ;MOVE(z, c 3 , 1 ) ;/* move a primeira string */for (i = 1; i

  • Cap. 1 Introduo s estruturas de dados 15

    Entretanto, uma vez que a operao de MOVEVAR esteja definida,CONCATVAR pode ser implementada, usando MOVEVAR, como segue:M O V E V A R ( c 2 , c 3 [ c l ] ) ; / * move a segunda string */M O V E V A R ( c l , c 3 ) ; / * move a primeira string */z = cl + c2; /* atualiza o tamanho do resultado */M O V E ( z , c 3 , 1 ) ;

    A Figura 1.1.3 ilustra as fases dessa operao sobre as strings daFigura 1.1.2. Embora esta ltima verso seja mais curta, na verdade ela no mais eficiente, porque todas as instrues usadas na implementao deMOVEVAR so executadas cada vez que MOVEVAR usada.

    A instruo z = cl + c2 em ambos os algoritmos anteriores departicular interesse. A instruo de adio opera independentemente do usode seus operandos (nesse caso, partes das strings de caracteres de tamanhovarivel). A instruo elaborada para tratar seus operandos como inteirosde byte nico, a despeito de qualquer outro uso que o programador determinepara eles. De modo semelhante, a referncia a c3[cl] para a posio cujoendereo dado pela soma do contedo do byte na posio cl com o endereoc3. Sendo assim, o byte em cl considerado armazenando um inteiro binrio,embora ele seja tambm o incio de uma string de caracteres de tamanhovarivel. Isso ilustra o fato de que um tipo de dado um mtodo de tratar ocontedo de memria e que esse contedo no tem um valor intrnseco.

    Observe que essa representao de strings de caracteres de tamanhovarivel permite somente strings cujo tamanho seja menor ou igual ao maiorinteiro binrio que caiba num nico byte. Se um byte tem oito bits, issosignifica que a maior string ter 255 (ou seja, 28"1) caracteres. Para permitirstrings mais extensas, deve-se escolher uma representao diferente e criarum novo conjunto de programas. Se usarmos essa representao de stringsde caracteres de tamanho varivel, a operao de concatenao ser invlidase a string resultante tiver mais de 255 caracteres. Como o resultado de umaoperao como essa indefinido, uma ampla variedade de aes pode serimplementada caso essa operao seja experimentada. Uma possibilidade usar somente os 255 primeiros caracteres do resultado. Outra possibilidade ignorar totalmente a operao e no deslocar nada para o campo doresultado. Existe tambm a opo de imprimir uma mensagem de advertn-cia ou de pressupor que o usurio queira chegar a qualquer resultado que oimplementador determinar.

  • 16 Estruturas de Dados Usando C Cap. 1

    Figura 1.1.3 As operaes de CONCATVAR.

    C1

    C 2

    C 3 C3[C1]

    (a) MOVEVAR (C2, C3[C1]);

    (b) MOVEVAR (C2, C3);

    C 3

    Z

    C 3

    5 H E L L O

    9 E V E R Y B O D Y

    9 E V E R Y B O D Y

    5 H E L L O E V E R Y B O D Y

    14

    14 H E L L O E V E R Y B O D Y

    (c) Z = Cl + C2; MOVE (Z, C3, 1);

  • Cap. 1 Introduo s estruturas de dados 17

    Na verdade, a linguagem C usa uma implementao totalmentediferente de strings de caracteres, que evita esta limitao sobre o tamanhoda string. Em C, todas as strings terminam com um caractere especial, ' \0 ' .Este caractere, que nunca aparece dentro de uma string, automaticamenteintroduzido pelo compilador no final de cada string. Como o tamanho dastring no conhecido antecipadamente, todas as operaes de strings devemproceder de um caractere por vez at que ' \0 ' seja encontrado.

    O programa para implementar a operao de MOVEVAR, sob estaimplementao, pode ser escrito assim:i = 0;while (source[i] != '\0') {

    MOVE(source[i], d e s t [ i ] , 1);i++;

    }d e s t [ i ] = '\O';/* encerra a string de destino com '\0' */

    Para implementar a operao de concatenao, CONCATVAR(cl ,c2,c3),podemos escrever:i = 0;/* move a primeira string */while ( c l [ i ] != '\O') {

    MOVE(cl[], c 3 [ i ] , 1);i++;

    }/* move a segunda string */j = 0;while ( c 2 [ j ] != '\0')

    M O V E ( C 2 [ j + + ] , c 3 [ i + + ] , 1 ) ;/* encerra a string de destino com um \0 */c 3 [ i ] = ' \ 0 ' ;

    Uma desvantagem da implementao de strings de caracteres de C que otamanho de uma string de caracteres no est prontamente disponvel semavanar na string um caractere por vez at encontrar um ' \0 ' . Isto maisdo que compensado pela vantagem de no haver um limite arbitrrio impostosobre o tamanho da string.

    Assim que for definida uma representao para objetos de determi-nado tipo de dado e forem escritas as rotinas para operar sobre estarepresentao, o programador poder usar este tipo de dado para solucionar

  • 18 Estruturas de Dados Usando C Cap. 1

    problemas. O hardware original da mquina mais os programas para imple-mentar tipos de dados mais complexos do que os fornecidos pelo hardwarepodem ser considerados uma mquina "melhor" do que a que consiste nohardware isoladamente. O programador da mquina original no precisapreocupar-se com o modo como o computador projetado e com o circuitousado para executar cada instruo. O programador s precisa conhecer asinstrues disponveis e como essas instrues podem ser usadas. De modosemelhante, o programador que usar a mquina "estendida" (consistindo emhardware e software), ou o "computador virtual", como citado ocasionalmen-te, no precisar preocupar-se com os detalhes da implementao dosdiversos tipos de dados. Tudo o que o programador precisa saber como elespodem ser manipulados.

    TIPOS DE DADOS ABSTRATOS

    Uma ferramenta til para especificar as propriedades lgicas de um tipo dedado o tipo de dado abstrato, ou TDA. Fundamentalmente, um tipo dedado significa um conjunto de valores e uma seqncia de operaes sobreestes valores. Este conjunto e estas operaes formam uma construomatemtica que pode ser implementada usando determinada estrutura dedados do hardware ou do software. A expresso "tipo de dado abstrato"refere-se ao conceito matemtico bsico que define o tipo de dado.

    Ao definir um tipo de dado abstrato como um conceito matemtico,no nos preocupamos com a eficincia de tempo e espao. Estas so questesde implementao. Na realidade, a definio de um TDA no se relacionacom nenhum detalhe da implementao. E possvel at que no se consigaimplementar determinado TDA num determinado hardware ou usandodeterminado sistema de software. Por exemplo, j constatamos que o TDAinteiro no universalmente implementado. Apesar disso, especificando-seas propriedades matemticas e lgicas de um tipo de dado ou estrutura, oTDA ser uma diretriz til para implementadores e uma ferramenta de apoiopara os programadores que queiram usar o tipo de dado corretamente.

    Existem vrios mtodos para especificar um TDA. O mtodo queusamos semiformal e faz uso intensivo da notao de C, mas estende estanotao onde necessrio. Para ilustrar o conceito de um TDA e nosso mtodode especificao, examine o TDA RACIONAL, que corresponde ao conceito

  • Cap. 1 Introduo s estruturas de dados 19

    matemtico de um nmero racional. Um nmero racional o que pode serexpresso como o quociente de dois inteiros. As operaes sobre nmerosracionais que definimos so a criao de um nmero racional a partir de doisinteiros, a adio, a multiplicao e o teste de igualdade. Veja a seguir umaespecificao inicial deste TDA:

    /*definio de valor*/abstract typedef RATIONAL;condition RATIONAL[1] 0;

    /*definicao de operador*/abstract RATIONAL makerational(a,b)int a,b;precondition b0;postcondition makerational[0] == a;

    makerational[l] == b;

    abstract RATIONAL add(a,b) /* written a + b */RATIONAL a,b;postcondition a d d [ l ] == a [ l ] * b [ l ] ;

    add[0] == a [ 0 ] * b[l] + b[0] * a [ 1 ];

    abstract RATIONAL mult(a,b) /* written a*b */RATIONAL a,b;postcondition mult[0] == a[0] * b [ 0 ] ;

    m u l t [ l ] = = a [ l ] * b [ l ] ;

    abstract equal(a,b) /* written a==b */RATIONAL a,b;postcondition equal == ( a [ 0 ] * b [ l ] == b [ 0 ] * a [ l ] ) ;

    Um TDA consiste em duas partes: a definio de valores e a definiode operadores. A definio dos valores determina o conjunto de valores parao TDA e consiste em duas partes: uma clusula de definio e uma clusulade condio. Por exemplo, a definio de valor para o TDA RACIONALdeclara que o valor de RACIONAL consiste em dois inteiros, sendo o segundodeles diferente de 0. Evidentemente, os dois inteiros que formam um nmeroracional so o numerador e o denominador. Usamos notao de vetor(colchetes) para indicar as partes de um tipo abstrato.

    As palavras-chave abstract typedef introduzem uma definio devalor, e a palavra-chave condition usada para especificar quaisquercondies (ou critrios) impostas sobre o tipo recm-definido. Nesta definio,

  • 20 Estruturas de Dados Usando C Cap. 1

    a condio especifica que o denominador no pode ser 0. A clusula dadefinio necessria, mas a clusula da condio pode no ser necessria paratodo TDA

    Imediatamente depois da definio do valor, vem a definio dosoperadores. Cada operador definido como uma funo abstrata com trspartes: um cabealho, as pr-condies opcionais e as ps-condies. Porexemplo, a definio do operador do TDA RACIONAL inclui as operaes decriao (makerational), adio (add) e multiplicao (mult), alm de um testede igualdade (equal). Examinemos primeiro a especificao para multiplica-o, por ser a mais simples. Ela contm um cabealho e ps-condies, masnenhuma pr-condio:abstract RATIONAL mult(a,b) I* written a*b */RATIONAL a,b;postcondition mult[0] == a[0]*b[0];

    mult[l] == a[l]*b[l];

    O cabealho desta definio so as duas primeiras linhas, parecidocom um cabealho de funo de C. A palavra-chave abstract indica que estano uma funo de C, mas uma definio de operador de TDA. O comentrioiniciando com a nova palavra-chave written indica uma forma alternativade escrever a funo.

    A ps-condio especifica o que a operao faz. Numa ps-condio,o nome da funo (neste caso, mult) usado para indicar o resultado daoperao. Sendo assim, mult[0] representa o numerador do resultado, emult[l], o denominador do resultado. Ou seja, ele especifica quais condiessero verdadeiras depois da execuo da operao. Neste exemplo, a ps-con-dio especifica que o numerador do resultado de uma multiplicao racionalequivale ao produto inteiro dos numeradores dos dois elementos e que odenominador igual aos produtos inteiros dos dois denominadores.

    A especificao para a adio (add) simples e especifica que:

    A operao de criao (makerational) cria um nmero racional apartir de dois inteiros e contm o primeiro exemplo de uma pr-condio. Emgeral, as pr-condies especificam as restries que devem ser atendidasantes da aplicao da operao. Neste exemplo, a pr-condio declara quemakerational no poder ser aplicada se seu segundo parmetro for 0.

  • Cap. 1 Introduo s estruturas de dados 21

    A especificao para a igualdade (equal) mais significativa e maiscomplexa em termos de conceito. Em geral, quaisquer dois valores num TDAso "iguais" se e somente se os valores de seus componentes forem iguais.Na realidade, geralmente se presume que uma operao de igualdade (e dedesigualdade) existe e definida dessa maneira, portanto no se faz neces-srio nenhum operador explcito de igualdade. A operao de atribuio(definindo o valor de um objeto com o valor de outro) mais um exemplo deuma operao freqentemente pressuposta para um TDA que no especi-ficada explicitamente.

    Entretanto, para alguns tipos de dados, dois valores com componen-tes desiguais podem ser considerados iguais. Na verdade, este o caso dosnmeros racionais; por exemplo, os nmeros racionais 1/2, 2/4, 3/6 e 18/36so todos iguais, a despeito da desigualdade de seus componentes. Doisnmeros racionais so considerados iguais se seus componentes forem iguais,quando os nmeros forem reduzidos aos mnimos termos (ou seja, quandoseus numeradores e denominadores forem ambos divididos por seu maiordivisor comum). Uma maneira de testar a igualdade dos racionais reduziros dois nmeros a seus mnimos termos e depois testar a igualdade dosnumeradores e denominadores. Outra forma de testar a igualdade de racio-nais verificar se os produtos cruzados (isto , o numerador de um multipli-cado pelo denominador do outro) so iguais. Este o mtodo que usamos aoespecificar a operao de igualdade abstrata.

    A especificao abstrata ilustra o papel de um TDA como umadefinio puramente lgica de um novo tipo de dado. Como conjuntos de doisinteiros, dois pares ordenados sero desiguais se seus componentes no foremiguais; mas, como nmeros racionais, eles podem ser iguais. improvvelque qualquer implementao de nmeros racionais implementasse um testede igualdade, formando realmente os produtos cruzados; eles poderiam ficargrandes demais para representar como inteiros de mquina. Muito prova-velmente, uma implementao primeiro reduziria as entradas a seus mni-mos termos e depois testaria a igualdade dos componentes. Na verdade, umaimplementao razovel insistiria em que makerational, add e mult sproduzissem nmeros racionais em seus mnimos termos. Entretanto, defi-nies matemticas como especificaes de tipo de dado abstrato no preci-sam ater-se a detalhes da implementao.

    Na verdade, a percepo de que dois racionais podem ser iguais,mesmo se desiguais em nvel de componentes, obriga-nos a reescrever asps-condies de makerational, add e mult. Ou seja, se:

  • 22 Estruturas de Dados Usando C Cap. 1

    no ser necessrio que m0 seja igual a a0O * 60 e ml seja igual a a1 * b1,somente que mO * a1 * b1 sejam iguais a ml * a0 * 60. Veja a seguir umaespecificao de TDA mais exata para RATIONAL:/*definicao do valor*/abstract typedef RATIONAL;condition RATIONAL[1] 0;

    /*definicao do operador*/abstract equal(a,b) /* written a == b*IRATIONAL a,b;postcondition equal = = ( a [ 0 ] * b [ l ] = = b [ 0 ] * a [ l ] ) ;

    abstract RATIONAL makerational(a,b) I* w r i t t e n [ a , b ] * /int a,b;precondition b 0;postcondition makerational[0]*b == a*makerational[1]abstract RATIONAL add(a,b) I* written a + b*IRATIONAL a,b;postcondition add = = ( a [ 0 ] * b[l] + b[0] * a [ l ] ) , a [ l ] * b [ l ] ]

    abstract RATIONAL mult{a,b) /* written a * b */RATIONAL a,b;p o s t c o n d i t i o n m u l t = = [ a [ 0 ] * b [ 0 ] , a [ l ] * b [ l ] ]

    Neste caso, o operador equal definido primeiro, e o operador == estendido para a igualdade de racionais, usando a clusula written. Esteoperador usado em seguida para especificar os resultados de operaesracionais subseqentes (add e mult).

    O resultado da operao de makerational sobre os inteiros a e bproduz um racional que equivale a a/b, mas a definio no especifica osverdadeiros valores do numerador e denominador resultantes. A especifica-o para makerational introduz tambm a notao [a,6] para o racionalformado a partir dos inteiros a e 6, e esta notao usada, em seguida, aodefinir add e mult.

    As definies de add e mult especificam que seus resultados equiva-lem aos resultados no-reduzidos da operao correspondente, mas os com-ponentes individuais no so necessariamente iguais.

  • Cap. 1 Introduo s estruturas de dados 23

    Observe mais uma vez que, ao definir esses operadores, no estamosespecificando como eles devem ser computados, somente qual deve ser seuresultado. O modo como eles so computados determinado por sua imple-mentao, no por sua especificao.

    SEQNCIAS COMO DEFINIES DE VALORES

    Ao desenvolver as especificaes para vrios tipos de dados, usamos freqen-temente a notao da teoria dos conjuntos para especificar os valores de umTDA. Em particular, til usar a notao de seqncias matemticas queapresentamos agora.

    Uma seqncia apenas um conjunto ordenado de elementos.Ocasionalmente, uma seqncia S escrita como a enumerao de seuselementos, como em:

    S =

    Se S contm n elementos, diz-se que S de tamanho n. Pressupomosa existncia de uma funo de tamanho, len, de modo que len(S) seja otamanho da seqncia S. Presumimos tambm a existncia das funesfirst(S), que retorna o valor do primeiro elemento de S (so, no exemploanterior), e last(S), que retorna o valor do ltimo elemento de S (sn-1, noexemplo anterior). Existe uma seqncia especial de tamanho 0, chamadanilseq, que no contm elementos, first(nilseq) e last(nilseq) so indefinidas.

    Queremos definir um TDA, stp1, cujos valores so seqncias deelementos. Se as seqncias podem ser de tamanho arbitrrio e consistemem elementos de mesmo tipo, tp, ento stpl pode ser definido por:abstract typedef tp stpl;

    Como alternativa, queremos tambm definir um TDA stp2, cujosvalores so seqncias de tamanho fixo e cujos elementos so de tiposespecificados. Nesse caso, especificaramos a definio:abstract typedef < t p 0 , tpl, tp2, . . . , tpn s t p 2 ;

  • 24 Estruturas de Dados Usando C Cap. 1

    Evidentemente, podemos tambm especificar uma seqncia detamanho fixo, cujos elementos sejam do mesmo tipo. Poderamos escreverento:abstract typedef tp,n stp3;

    Nesse caso, stp3 representa uma seqncia de tamanho n, cujoselementos so do tipo tp.

    Por exemplo, usando a notao anterior, poderamos definir osseguintes tipos:abstract typedef int intseq;

    I* sequencia de inteiros de *//* qualquer tamanho */

    abstract typedef seq3;/* sequencia de tam 3 *//* consistindo em um inteiro,*//* um caractere e um numero *//* de ponto flutuante */

    abstract typedef int, 10 intseq;/*sequencia de 10 inteiros */

    abstract typedef ,2 pair;/* sequencia arbitraria de *//* tamanho 2 */

    Duas seqncias so iguais quando cada elemento da primeira igual ao elemento correspondente da segunda. Uma subseqncia umaparte contgua de uma seqncia. Se S uma seqncia, a funo sub(S,i,j)refere-se subseqncia de S comeando na posio i em S e consistindo emj elementos consecutivos. Sendo assim, se T igual a sub(S,i,k), e T aseqncia , t0 = si, t1 = si + 1,..., tk-1 = Si + k - i. Se i no est entre0 e len(S) - k, ento sub(S,i,k) definida como nilseq.

    A concatenao de duas seqncias, escritas S + T, a seqnciaconsistindo em todos os elementos de S seguidos por todos os elementos deT. Ocasionalmente, precisamos especificar a insero de um elemento nomeio de uma seqncia. place(S,i,x) definida como a seqncia S com oelemento x inserido imediatamente depois da posio i (ou no primeiroelemento da seqncia, se i for -1). Todos os elementos subseqentes serodeslocados em uma posio. Ou seja, place(S,i,x) equivale a sub(S,0,i) + + sub(S,i + 1, len(S) - i - 1).

  • Como ilustrao do uso de notao de seqncias ao definir um TDA,desenvolvemos uma especificao de TDA para a string de caracteres detamanho varivel. Existem quatro operaes bsicas (alm de igualdade eatribuio) geralmente includas em sistemas que suportam tais strings:

    lengthconcat

    substr

    pos

    uma funo que retorna o atual tamanho da stringuma funo que retorna a concatenao de suas duasstrings de entradauma funo que retorna uma substring de umdeterminada stringuma funo que retorna a primeira posio de umastring como uma substring de outra

    abstract typedef char STRING;

    abstract length(s)STRING s;postcondition length == len(s);

    abstract STRING concat(s1,s2)STRING s1, S2;postcondition concat == sl + s2;

    abstract STRING substr(s1,i,j)STRING s1;int i,j;precondition 0

  • 26 Estruturas de Dados Usando C Cap. 1

    STRING s l , s 2 ;postcondition /*lastpos = len(sl) - Ien(s2) */

    ((pos == -1) && ( f o r ( i = 0;i = 0) && (pos

  • Cap. 1 Introduo s estruturas de dados 27

    TIPOS DE DADOS EM C

    A linguagem C contm quatro tipos bsicos de dados: int, float, char e double.Na maioria dos computadores, esses quatro tipos so nativos no hardwareda mquina. J vimos como os inteiros, os reais e os caracteres podem serimplementados no hardware. Uma varivel double um nmero de pontoflutuante de dupla preciso. Existem trs qualificadores que podem seraplicados aos ints: short, long e unsigned. Uma varivel de inteiro short oulong refere-se ao tamanho mximo do valor da varivel. Os verdadeirostamanhos mximos implicados por short int, long int ou int variam de umamquina para outra. Um inteiro unsigned um sempre inteiro positivo, quesegue as leis aritmticas do mdulo 1-, onde n o nmero de bits num inteiro.

    Uma declarao de varivel em C especifica dois itens. Primeiro, aquantidade de armazenamento que deve ser reservada para os objetosdeclarados com esse tipo. Por exemplo, uma varivel do tipo int precisa terespao suficiente para armazenar o maior valor inteiro possvel. Segundo,ela especifica como os dados representados por strings de bits devem serinterpretados. Os mesmos bits numa posio especfica de armazenamentopodem ser interpretados como um inteiro ou um nmero de ponto flutuante,resultando em dois valores numricos totalmente diferentes.

    Uma declarao de varivel especifica que deve ser reservado arma-zenamento para um objeto do tipo especificado e que o objeto nessa posiode armazenamento pode ser referenciado com o identificador de varivelespecificado.

    PONTEIROS EM C

    Na realidade, C permite que o programador referencie a posio de objetosbem como os prprios objetos (isto , o contedo de suas posies). Porexemplo, se x for declarado como um inteiro, &x se referir posioreservada para conter x. &x chamado ponteiro.

    possvel declarar uma varivel cujo tipo de dado seja um ponteiroe cujos possveis valores sejam posies de memria. Por exemplo, asdeclaraes:

  • 28 Estruturas de Dados Usando C Cap. 1

    int *pi;float *pf;char *pc;

    declara trs variveis ponteiro: pi um ponteiro para um inteiro, pf umponteiro para um nmero de ponto flutuante e pc um ponteiro para umcaractere. O asterisco indica que os valores das variveis sendo declaradasso ponteiros para valores do tipo especificado na declarao, em vez deobjetos desse tipo.

    Sob vrios aspectos, um ponteiro como qualquer outro tipo de dadoem C. O valor de um ponteiro uma posio de memria da mesma formaque o valor de um inteiro um nmero. Os valores dos ponteiros podem seratribudos como quaisquer outros valores. Por exemplo, a declarao pi = &xatribui um ponteiro para o inteiro x varivel ponteiro pi.

    A notao *pi em C refere-se ao inteiro na posio referenciada peloponteiro pi. A declarao x = *pi atribui o valor deste inteiro varivel inteira x.

    Observe que C insiste em que uma declarao de um ponteiro especi-fique o tipo de dado para o qual o ponteiro aponta. Nas declaraes anteriores,cada uma das variveis pi, pf e pc so ponteiros para tipos de dados especficos:int, float e char, respectivamente. O tipo de pi no simplesmente um"ponteiro", mas um "ponteiro para um inteiro". Na verdade, os tipos de pi e pfso diferentes: pi um ponteiro para um inteiro e pf um ponteiro para umnmero de ponto flutuante. Cada tipo de dado dt em C gera outro tipo de dado,pdt, chamado "ponteiro para dt". Chamamos de dt o tipo base de pdt.

    A converso de pf do tipo "ponteiro para um nmero de pontoflutuante" para o tipo "ponteiro para um inteiro" pode ser feita escrevendo-se:pi = (int *) pf;onde o operador {int *) converte o valor de pf para o tipo "ponteiro para umint" ou uint *".

    A importncia da associao de cada ponteiro com determinado tipobase evidencia-se ao rever os recursos aritmticos que C oferece para osponteiros. Se pi um ponteiro para um inteiro, ento pi + 1 o ponteiro parao inteiro imediatamente seguinte ao inteiro *pi em memria, pi - 1 oponteiro para o inteiro imediatamente anterior a *pi, pi + 2 o ponteiro parao segundo inteiro depois de *pi, e assim por diante. Por exemplo, suponhaque determinada mquina use endereamento de bytes, que um inteiro exija

  • Cap. 1 Introduo s estruturas de dados 29

    quatro bytes e que o valor de pi seja 100 (isto , pi aponta para o inteiro *pina posio 100). Sendo assim, o valor de pi - 1 96, o valor de pi +1 104 e0 valor de pi + 2 108. O valor de *(pi - 1) o contedo dos quatro bytes,96, 97, 98 e 99, interpretado como um inteiro; o valor de *(pi + 1) o contedodos bytes 104, 105, 106 e 107, interpretado como um inteiro; e o valor de *(pi+ 2) o inteiro nos bytes 108, 109, 110 e 111.

    De modo semelhante, se o valor da varivel pc 100 (lembre-se deque pc um ponteiro para um caractere) e um caractere tem um byte, pc - 1refere-se posio 99, pc + 1 posio 101 e pc + 2 posio 102. Assim, oresultado da aritmtica de ponteiros em C depende do tipo base do ponteiro.

    Observe tambm a diferena entre *pi + 1, que se refere a 1 somadoao inteiro *pi, e *(pi + 1), que se refere ao inteiro posterior ao inteiro naposio pi.

    Uma rea na qual os ponteiros de C desempenham um notvel papel na passagem de parmetros para funes. Normalmente, os parmetrosso passados para uma funo em C por valor, isto , os valores sendopassados so copiados nos parmetros da funo chamada no momento emque a funo for chamada. Se o valor de um parmetro for alterado dentroda funo, o valor no programa de chamada no ser modificado. Porexemplo, examine o seguinte segmento de programa e funo (os nmerosde linhas so usados somente a ttulo de referncia):1 x = 5;2 printf("%d\n", x);3 funct(x);4 printf("%d\n", x);

    5 funct(y)6 i n t y;7 {8 ++y;9 p r i n t f ( " % d \ n " , y ) ;10 } /* end funct */

    A linha 2 imprime 5 e, em seguida, a linha 3 chama funct. O valorde x, que 5, copiado em y e funct comea a execuo. A linha 9 imprime6 e funct retorna. Entretanto, quando a linha 8 incrementa o valor de y, ovalor de x permanece inalterado. Dessa forma, a linha 4 imprime 5. x e yreferem-se a duas variveis diferentes que tm o mesmo valor no incio defunct. y pode mudar independentemente de x.

  • 30 Estruturas de Dados Usando C Cap. 1

    Se quisermos usar funct para modificar o valor de x, precisaremospassar o endereo de x como segue:1 x = 52 printf("%d\n", x);3 funct(&x);4 printf("%d\n", x);

    5 funct(py)6 int *py;7 {8 ++(*py);9 printf("%d\n", *py);10 } /* end funct */

    A linha 2 imprime novamente 5, e a linha 3 chama funct. Entretanto,o valor passado agora no o valor inteiro de x, mas o valor do ponteiro &x.Esse o endereo de x. O parmetro de funct no mais y de tipo int, maspy de tipo int *. ( conveniente nomear as variveis de ponteiro comeandocom a letra p para lembrar ao programador e ao leitor do programa que ela um ponteiro. Entretanto, isso no uma exigncia da linguagem C epoderamos ter chamado de y o parmetro do tipo ponteiro.) Agora, a linha8 incrementa o inteiro na posio py. Contudo, em si mesmo, py no alteradoe mantm seu valor inicial, &x. Dessa forma, py aponta para o inteiro x, demodo que, quando *py for incrementado, x ser incrementado. A linha 9imprime 6 e, quando funct retorna, a linha 4 imprime 6 tambm. Os ponteirosrepresentam o mecanismo usado em C para permitir que uma funochamada modifique variveis numa funo de chamada (ou chamadora).

    ESTRUTURAS DE DADOS EM C

    Um programador C pode imaginar a linguagem C como definindo uma novamquina, com capacidades, tipos de dados e operaes exclusivas. O usuriopode declarar a soluo de um problema em termos de construes mais teisde C do que em termos de construes de linguagem de mquina de baixonvel. Assim, os problemas podem ser solucionados mais facilmente porqueexiste um conjunto mais abrangente de ferramentas.

  • Cap. 1 Introduo s estruturas de dados 31

    Por sua vez, o estudo das estruturas de dados envolve duas metascomplementares. A primeira meta identificar e desenvolver entidades eoperaes matemticas teis e determinar que classes de problemas podemser solucionadas, usando essas entidades e operaes. A segunda meta determinar representaes para essas entidades abstratas e implementar asoperaes abstratas sobre essas representaes concretas. A primeira metavislumbra um tipo de dado de alto nvel como uma ferramenta que pode serusada para solucionar outros problemas, e a segunda meta percebe aimplementao de tal tipo de dado como um problema a ser resolvido usandoos tipos de dados j existentes. Ao determinar representaes para entidadesabstratas, precisamos ter o cuidado de especificar os recursos disponveispara construir tais representaes. Por exemplo, deve ser declarado se alinguagem C completa est disponvel ou se estamos restritos aos recursosdo hardware de determinada mquina.

    Nas prximas duas sees deste captulo, examinaremos vriasestruturas de dados que j existem em C: o vetor e a estrutura. Descrevere-mos os recursos j disponveis em C para a utilizao desses recursos. Almdisso, enfatizaremos as definies abstratas dessas estruturas de dados ecomo elas podem ajudar na soluo de problemas. Por ltimo, examinaremoscomo essas estruturas poderiam ser implementadas se C no estivessedisponvel (embora um programador C possa simplesmente usar as estrutu-ras de dados conforme definidas na linguagem, sem se preocupar com amaioria desses detalhes de implementao).

    No restante deste livro, desenvolveremos estruturas de dados maiscomplexas e mostraremos sua utilidade na soluo de problemas. Alm disso,ensinaremos a implementar essas estruturas de dados usando as estruturasde dados j disponveis em C. Como os problemas que surgem no decorrerda tentativa de implementar estruturas de dados de alto nvel so muitocomplexos, isto nos permitir tambm examinar a linguagem C mais profun-damente para adquirir uma experincia valiosa no uso dessa linguagem.

    Com freqncia, nenhuma implementao, nenhum hardware ousoftware pode modelar por completo um conceito matemtico. Por exemplo, impossvel representar arbitrariamente grandes inteiros num computadorporque o tamanho da memria de uma mquina finito. Sendo assim, no o tipo de dado "inteiro" que representado pelo hardware, mas o tipo dedado "inteiro entre x e y", onde x e y so os menores e maiores inteirosrepresentveis por essa mquina.

  • 32 Estruturas de Dados Usando C Cap. 1

    importante reconhecer as limitaes de determinada implementa-o. Freqentemente, ser possvel apresentar vrias implementaes domesmo tipo de dado, cada uma com vantagens e desvantagens prprias.Talvez determinada implementao seja melhor que outra para uma aplica-o especfica, e o programador precisa conhecer os possveis compromissosenvolvidos.

    Uma considerao importante em qualquer implementao a suaeficincia. Na verdade, a razo pela qual as estruturas de dados de alto nvel,que discutimos, no so construdas em C a significativa sobrecarga queacarretariam. Existem linguagens de nvel muito mais alto que C, quepossuem vrios desses tipos de dados j incorporados, mas muitas delas soineficientes e, portanto, no amplamente usadas.

    Em geral, a eficincia determinada por dois fatores: tempo e espao.Se determinada aplicao depender intensivamente da manipulao deestruturas de dados de alto nvel, a velocidade na qual essas manipulaespodem ser executadas ser o principal determinante da velocidade daaplicao inteira. De modo semelhante, se um programa usar uma grandequantidades destas estruturas, uma implementao que utiliza uma quan-tidade excessiva de espao para representar a estrutura de dados no serprtica. Infelizmente, em geral existe um compromisso entre esses doisprismas de eficincia, de modo que uma implementao veloz usa maisarmazenamento do que uma implementao lenta. A escolha da implemen-tao nesse caso requer uma avaliao cuidadosa dos compromissos entre asvrias possibilidades.

    EXERCCIOS

    1.1.1. No texto feita uma analogia entre o tamanho de uma linha e o nmerode bits de informao numa string de bits. De que maneira essaanalogia inadequada?

    1.1.2. Determine que tipos de dados de hardware esto disponveis nocomputador de sua instalao e que operaes podem ser executadassobre eles.

  • Cap. 1 Introduo s estruturas de dados 33

    1.1.3. Prove que existem 2a definies diferentes para n chaves bivalentes.Suponha que queremos ter m definies. Quantas chaves seriamnecessrias?

    1.1.4. Interprete as seguintes definies de bits como inteiros positivosbinrios, como inteiros binrios em complemento de dois, e comointeiros decimais codificados em binrio. Se uma definio no puderser interpretada como um inteiro decimal codificado em binrio,explique por qu.(a) 10011001(b) 1001(c) 000100010001(d) 01110111(e) 01010101(f) 100000010101

    1.1.5. Escreva funes em C, add, subtract e multiply, que leiam duas stringsde Os e ls representando inteiros no-negativos binrios, e imprima astring representando a soma, a diferena e o produto, respectivamente.

    1.1.6. Imagine um computador ternrio no qual a unidade bsica de memriaseja um "trit" (ternary digit) em vez de um bit. Esse trit pode ter trspossveis definies (0, 1 e 2) em vez de apenas duas (0 e 1). Mostrecomo os inteiros no-negativos podem ser representados em notaoternria usando esses trits com um mtodo anlogo notao binriapara bits. Existe algum inteiro no-negativo que pode ser representadousando notao ternria e trits, mas que no pode ser representadousando notao binria e bits? Existe algum que pode ser representadousando bits, mas que no pode ser representado usando trits? Por queos computadores binrios so mais comuns do que os computadoresternrios?

    1.1.7. Escreva um programa C para ler uma string de Os e ls representandoum inteiro positivo em notao binria e imprima uma string de Os,ls e 2s representando o mesmo nmero em notao ternria (veja oexerccio anterior). Escreva outro programa C para ler um nmeroternrio e imprimir o equivalente em notao binaria.

  • 34 Estruturas de Dados Usando C Cap. 1

    1.1.8. Escreva uma especificao de TDA para os nmeros complexos, a +bi, onde abs(a + bi) sqrt(a2 + b2), (a + bi) + (c + di) (a + c) + (6 + d)i,(a + b)*(c + di)(a*c-b*d) + (a*d + b*c)ie -(a + bi) (-a) + (-b)i.

    1.2. VETORES EM C

    Nesta e na prxima seo, examinaremos vrias estruturas de dados querepresentam uma parte inestimvel da linguagem C. Veremos como usaressas estruturas e como elas podem ser implementadas. Essas estruturasso tipos de dados compostos ou estruturados, ou seja, elas so formadasde estruturas de dados mais simples que existem na linguagem. O estudodessas estruturas de dados envolve uma anlise de como as estruturassimples se combinam de modo a formar a composio e como extrair umcomponente especfico da composio. Esperamos que voc j tenha vistoessas estruturas de dados num curso introdutrio de programao em C esaiba como elas so definidas e usadas em C. Portanto, nessas sees, nodescreveremos os detalhes associados a tais estruturas; destacaremos apenasos recursos interessantes sob o ponto de vista da estrutura de dados.

    O primeiro desses tipos de dados o vetor. A forma mais simples devetor um vetor unidimensional, que pode ser definido abstratamentecomo um conjunto finito e ordenado de elementos homogneos. Por "finito"entendemos que existe um nmero especfico de elementos no vetor. Essenmero pode ser grande ou pequeno, mas ele precisa existir. Por "ordenado"entendemos que os elementos do vetor so organizados de tal forma queexista um elemento zero, um primeiro elemento, um segundo, um terceiro eassim por diante. Por "homogneo" entendemos que todos os elementos novetor precisam ser do mesmo tipo. Por exemplo, um vetor pode conter inteirosou caracteres, mas no ambos.

    Entretanto, especificar a forma de uma estrutura de dados nodescreve totalmente sua estrutura. Precisamos tambm especificar como aestrutura acessada. Por exemplo, a declarao C:i n t a [ 1 0 0 ] ;

    especifica um vetor de 100 inteiros. As duas operaes bsicas que acessamum vetor so a extrao e o armazenamento. A operao de extrao uma funo que aceita um vetor, a, e um ndice, i, e retorna um elemento do

  • Cap. 1 Introduo s estruturas de dados 35

    vetor. Em C, o resultado dessa operao indicado pela expresso a[i]. Aoperao de armazenamento aceita um vetor, a, um ndice, i, e um elemento, x.Em C, esta operao indicada pelo comando de atribuio a[i] = x. Asoperaes so definidas pela norma que dita que, depois de a instruo deatribuio anterior ser executada, o valor de a[i] x. Antes da atribuio deum valor a um elemento do vetor, seu valor indefinido e uma referncia aele numa expresso ser invlida.

    O menor elemento do ndice de um vetor chamado limite mnimoe, em C, sempre 0, e o maior elemento chamado limite mximo. Se lower o limite mnimo de um vetor e upper, o limite mximo, o nmero deelementos no vetor, chamado faixa, dado por upper - lower + 1. Por exemplo,no vetor a, declarado anteriormente, o limite mnimo 0, o limite mximo 99 e a faixa 100.

    Uma caracterstica importante de um vetor em C que nem o limitemximo nem o mnimo (e conseqentemente a faixa tambm) podem seralterados durante a execuo de um programa. O limite mnimo semprefixado em 0, e o limite mximo fixado quando o programa escrito.

    Uma tcnica muito til declarar um limite como um identificadorde constante a fim de que o trabalho necessrio para modificar o tamanhode um vetor seja minimizado. Por exemplo, examine o seguinte segmento deprograma para declarar e inicializar um vetor:i n t a [ 1 0 0 ] ;f o r ( i = 0 ; i < 100; a [ i + + ] = 0 ) ;

    Para mudar o vetor para um tamanho maior (ou menor), a constante100 deve ser alterada em dois locais: uma vez nas declaraes e uma vez nocomando for. Examine a seguinte alternativa equivalente:# d e f i n e NUMELTS 100i n t a[NUMELTS];f o r ( i = 0; i < NUMELTS; a [ i + + ] = 0 ) ;

    Agora, s necessria uma mudana na definio da constante paraalterar o limite mximo.

  • 36 Estruturas de Dados Usando C Cap. 1

    O VETOR COMO UM TDA

    Podemos representar um vetor como um tipo de dado abstrato com umapequena extenso das convenes e notao discutidas anteriormente. Pres-supomos a funo type(arg), que retorna o tipo de seu argumento, arg.Evidentemente, uma funo como essa no pode existir em C porque C nopode determinar dinamicamente o tipo de uma varivel. Entretanto, comono estamos preocupados com a implementao aqui, mas com a especifica-o, o uso de uma funo como essa permitido.

    Deixemos que ARRTYPE(ub, eltype) indique o TDA correspondenteao tipo de vetor em C eltype vetor[ub]. Este nosso primeiro exemplo de umTDA parametrizado, no qual o exato TDA determinado pelos valores de umou mais parmetros. Nesse caso, ub e eltype so os parmetros; observe queeltype um indicador de tipo, no um valor. Agora, podemos visualizarqualquer vetor unidimensional como uma entidade do tipo ARRTYPE. Porexemplo, ARRTYPE(10, int) representaria o tipo do vetor x na declaraoint x [10]. Podemos ento visualizar qualquer vetor unidimensional comouma entidade do tipo ARRTYPE. Veja a seguir a especificao:abstract typedef eltype, ub ARRTYPE(ub, eltype);condition type(ub) == int}abstract eltype extract(a,i) /* written a[i] */ARRTYPE(ub, eltype) a;int i;precondition 0

  • i n t num[NUMELTS];int i;int total;float avg;float diff;

    /* vetor de numeros */

    /* soma dos numeros *//* media dos numeros *//* diferena entre cada *//* numero e a media */

    total = 0;for (i = 0; i < NUMELTS; i ++) {

    /* le os nmeros no vetor e os soma */scanf("%d", &num[i]);total += num[i];

    } / * fim for * /avg = t o t a l / NUMELTS; /* calcula a media */printf("Xndiferena dos nmeros"); /* imprime titulo */

    /* imprime cada numero e sua diferena */for (i = 0; i < NUMELTS? i ++) {

    diff = num[i] - avg;

    Um vetor unidimensional usado quando necessrio para manter umagrande quantidade de itens na memria e para referenciar todos os itens deuma maneira uniforme. Examinemos como essas duas exigncias aplicam-seem situaes prticas.

    Suponha que precisemos ler 100 inteiros, encontrar sua mdia edeterminar o quanto cada inteiro se desvia dessa mdia. O seguinte progra-ma far isto:#define NUMELTS 100aver(){

    USANDO VETORES UNIDIMENSIONAIS

    sim, nesse exemplo, todos os elementos do vetor, com exceo daquele ao qualelt atribudo, mantm os mesmos valores.

    Observe que, assim que a operao extract definida, juntamentecom sua notao de colchetes, a[i], a notao pode ser usada na ps-condiopara a especificao da subseqente operao de store. Entretanto, dentroda ps-condio de extract, deve ser usada uma notao de seqnciasubscrita porque a prpria notao de colchetes do vetor est sendo definida.

    Cap. 1 Introduo s estruturas de dados 37

  • 38 Estruturas de Dados Usando C Cap. 1

    printf("\n %d %d", num[i], d i f f ) ;} /* fim for */printf("\nA media, : %d", avg);

    } /* fim aver */

    Esse programa usa dois grupos de 100 nmeros. O primeiro grupo o conjunto de inteiros de entrada e representado pelo vetor num; o segundogrupo o conjunto de diferenas que so valores sucessivos atribudos varivel diff na segunda repetio. Surge ento a pergunta: por que usar umvetor para armazenar todos os valores do primeiro grupo simultaneamente,enquanto uma nica varivel utilizada para guardar um valor do segundogrupo por vez?

    A resposta bem simples. Cada diferena calculada, impressa enunca necessria novamente. Sendo assim, a varivel diff pode serutilizada para a diferena do prximo inteiro e a mdia. Entretanto, osinteiros originais, que so os valores do vetor num, precisam todos sermantidos na memria. Embora cada um possa ser somado ao total quandoentra, precisa ser mantido at que a mdia seja calculada para que oprograma compute a diferena entre ele e a mdia. Para tanto, usa-se umvetor.

    Evidentemente, poderiam ser usadas 100 variveis separadas paraarmazenar os inteiros. Entretanto, a vantagem de um vetor que ele permiteque o programador declare somente um identificador e obtenha mesmo assimuma grande quantidade de espao. Alm disso, e