Estrutura de Dados 18.12.2012

download Estrutura de Dados 18.12.2012

of 110

Transcript of Estrutura de Dados 18.12.2012

  • 2012

    Mariela Ins CortsEnyo Jos Tavares Gonsalves

    Estrutura de Dados

    2 Edio

  • Copyright 2010. Todos os direitos reservados desta edio SECRETARIA DE EDUCAO A DISTNCIA (SEAD/UECE). Nenhuma parte deste material poder ser reproduzida, transmitida e gravada, por qualquer meio eletrnico, por fotocpia e outros, sem a prvia autorizao, por escrito, dos autores.

    EXPEDIENTE Design instrucionalAntonio Germano Magalhes JuniorIgor Lima RodriguesPedro Luiz Furquim Jeangros

    Projeto grficoRafael Straus Timb VasconcelosMarcos Paulo Rodrigues Nobre

    Coordenador EditorialRafael Straus Timb Vasconcelos

    DiagramaoMarcus Lafaiete da Silva MeloFrancisco Jos da Silva Saraiva

    IlustraoMarcos Paulo Rodrigues Nobre

    CapaEmilson Pamplona Rodrigues de Castro

  • PRESIDENTE DA REPBLICALuiz Incio Lula da Silva

    MINISTRO DA EDUCAOFernando Haddad

    SECRETRIO DE EDUCAO A DISTNCIACarlos Eduardo Bielschowsky

    DIRETOR DO DEPARTAMENTO DE POLTICAS EM EDUCAO A DISTNCIA DPEADHlio Chaves Filho

    SISTEMA UNIVERSIDADE ABERTA DO BRASILCelso Costa

    GOVERNADOR DO ESTADO DO CEARCid Ferreira Gomes

    REITOR DA UNIVERSIDADE ESTADUAL DO CEARFrancisco de Assis Moura Araripe

    VICE-REITORAntnio de Oliveira Gomes Neto

    PR-REITORA DE GRADUAOJosefa Lineuda da Costa Murta

    COORDENADOR DA SECRETARIA DE EDUCAO A DISTNCIAAntonio Germano Magalhes Junior

    COORDENADOR GERAL UAB/UECEFrancisco Fbio Castelo Branco

    COORDENADORA ADJUNTA UAB/UECEJosete de Oliveira Castelo Branco Sales

    COORDENADOR DA LICENCIATURA EM INFORMTICAJoaquim Celestino Junior

    COORDENADOR DE TUTORIA E DOCNCIA DA LICENCIATURA EM INFORMTICAJorge Lus de Castro e Silva

  • SumrioApresentao ....................................................................................................................... 7

    Unidade 1Introduo Complexidade de Algoritmos ........................................................................... 9

    Captulo 1 - Introduo ...................................................................................................... 11O que anlise de algoritmos? ............................................................................................. 12

    Captulo 2 - Medidas de Complexidade ............................................................................. 14Comparao entre algoritmos ..............................................................................................16

    Unidade 2Representao dos Dados ..................................................................................................... 25

    Captulo 1 - Modelagem Conceitual ................................................................................... 27Tipo de dados .......................................................................................................................27Tipo abstratos de dados ........................................................................................................30Critrios para a escolha da estrutura de dados adequada ....................................................32

    Unidade 3Listas .................................................................................................................................... 37

    Captulo 1 - Listas ............................................................................................................... 39Introduo .............................................................................................................................39Definio do TAD Lista ...........................................................................................................39

    Implementao do TAD Lista usando alocao esttica ............................................................... 40Implementao do TAD Lista usando alocao dinmica ............................................................. 46Variaes sobre o TAD Lista ........................................................................................................... 53

    Captulo 2 - Pilhas .............................................................................................................. 55Implementaes do TAD Pilha usando vetores .....................................................................56Implementao do TAD Pilha usando ponteiros ...................................................................59

    Captulo 3 - Filas ................................................................................................................. 63Implementao do TAD Fila usando vetores .........................................................................64Implementao do TAD Fila usando ponteiros .....................................................................67

    Unidade 4rvores ................................................................................................................................ 73

    Captulo 1 - rvores ........................................................................................................... 75rvore binria ......................................................................................................................76

    Captulo 2 - rvore binria de busca .................................................................................. 79Definio do TAD rvore Binria de Busca ...........................................................................79Implementao do TAD rvore Binria de Busca .................................................................80

  • Ordens de percurso em rvores binrias ...................................................................................... 85Relao entre o nmero de ns de uma rvore binria e sua altura ........................................................................................................................ 87

    Captulo 3 - rvores AVL ..................................................................................................... 89

    Unidade 5Busca avanada ................................................................................................................... 97

    Captulo 1 - Tabela de disperso ........................................................................................ 99A funo de disperso ...........................................................................................................100Estratgias para resoluo de colises ..................................................................................102

    Encadeamento .............................................................................................................................. 102Tcnicas de sondagem ................................................................................................................... 103

    Captulo 2 - Busca digital .................................................................................................... 105rvore digital.........................................................................................................................106

    Captulo 3 - Estruturas autoajustveis................................................................................ 108Listas autoajustveis .............................................................................................................108

    Dados dos Autores ............................................................................................................... 110

  • O constante aumento da complexidade dos sistemas e suas demandas computa-cionais relacionadas a tempo e espao, impem o desafo de projetar solues algortmi-cas cada vez mais eficientes. Neste contexto, as estruturas de dados e seus algoritmos tm um papel fundamental uma vez que constituem os blocos construtores utilizados na resoluo dos mais diversos problemas em todas as areas da computao.

    O objetivo do presente livro apresentar de forma clara e amigvel diferentes estruturas de dados e seus algoritmos de manipulao, analisando a estratgia mais eficiente para a sua implementao, em termos de complexidade. Esta anlise envolve a utilizao de tcnicas de projeto associadas a tcnicas de programao, as quais so adequadas s caracteristicas da aplicao especfica.

    O livro est organizado em cinco unidades. As primeiras fornecem as bases ne-cessrias para a anlise e o projeto de uma boa soluo algortmica incluindo concei-tos bsicos sobre complexidade, tipos e estruturas de dados. A Unidade 3 apresenta o conceito central de Listas, a partir do qual duas importantes e amplamente utilizadas estruturas so derivadas: Pilhas e Filas.

    A Unidade 4 apresenta a estrutura de rvore, sua conceituao, implementao e algoritmos de manipulao bsicos. Mais especificamente, nesta unidade so explo-radas as rvores Binrias de Busca analizando suas caractersticas e as implicaes em relao eficincia. Neste contexto, a fundamentao e funcionamento das rvores balanceadas (AVL) so apresentados. Finalmente, na Unidade 5 so descritas as tcni-cas avanadas de pesquisa aplicadas sobre estruturas de dados especficas, tais como tabelas de disperso, busca digital e estruturas autoajustveis.

    O contedo apresentado destina-se principalmente para professores e alunos de graduao em Cincias da Computao ou reas afins, fornecendo um embassamento te-rico e uma clara noo das estratgias a serem seguidas na implementao dos algorit-mos para a manipulao eficiente das estruturas de dados mais amplamente utilizadas.

    Os autores

  • Unidade

    Objetivos:

    Nesta unidade introduzido o conceito de complexidade de algoritmos. Este conceito central na Cincia da Computao, uma vez que possibilita avaliar e comparar solues algortmicas, fornecendo os insumos necessrios para determinar qual o algoritmo mais adequado para resolver determinada classe de problemas.

    1Introduo Complexidade

    de Algoritmos

  • 11ESTRUTURA DE DADOS

    Captulo 1Introduo

    Um algoritmo determina um conjunto de regras no ambguas as quais especificam, para cada entrada, uma seqncia finita de operaes, gerando como resultado uma sada. O algoritmo representa uma soluo para um problema se, para cada entrada, gera uma resposta correta, sem-pre que dispor de tempo e memria suficientes.

    Um algoritmo pode ser implementado atravs de diferentes progra-mas. Ou seja, diferentes implementaes podem ser propostas a partir de um nico algoritmo. Esta situao nos coloca na dificuldade de escolher qual a melhor soluo para o problema especfico. Assim sendo, apenas resolver o problema parece no ser suficiente uma vez que diferentes solu-es podem ser idealizadas a partir de algoritmos, para a resoluo de um nico problema. Neste contexto, se torna necessrio um mecanismo que permita determinar se uma soluo melhor do que uma outra, de forma a fornecer uma ferramenta de apoio decio em relao qualidade das solues propostas.

    De forma geral, a qualidade de um programa depende do ponto de vis-ta. Por um lado, o usurio determina a qualidade de um programa atravs de critrios, tais como:

    Facilidade de uso e entendibilidade da interface do programa levan-do em conta diferentes nveis de experincia.

    Compatibilidade do programa com outros programas ou verses de programas, de forma a facilitar a troca de informao com outros sistemas existentes.

    Desempenho, em relao velocidade de execuo e tempo de res-posta.

    Quantidade de memria utilizada pelo programa durante a sua exe-cuo, aspecto que pode se tornar um fator limitante.

    Os ltimos dois itens esto diretamente ligados quantidade de dados a serem processados, ou seja, ao tamanho da entrada.

    Por outro lado, critrios que podem ser determinantes desde o ponto de vista da organizao desenvolvedora incluem:

    Portabilidade do cdigo entre diferentes plataformas.

    Documentao e padronizao do cdigo.

    Facilidade de evoluo e manuteno.

    Reusabilidade do cdigo, permitindo que pores de um programa sejam reaproveitadas para desenvolver outros produtos, aumentan-do a produtividade.

    Programa codifica um al-goritmo para ser executa-do no computador resol-vendo um problema. fundamental que o pro-grama produza a soluo com dispndio de tempo e memria: Importncia de Projeto e Analise de Algo-ritmos.

  • 12 ESTRUTURA DE DADOS

    A correta avaliao destes critrios influenciada por diversos fatores, tais como: caractersticas do hardware, sistema operacional, linguagens e compiladores, plataforma, etc. Estes fatores so considerados acidentais uma vez que no esto diretamente relacionados qualidade da soluo. Em contrapartida, os fatores essenciais so inerentes soluo e determi-nantes para a sua qualidade. O tempo gasto e o espao fsico na memria so considerados fatores essenciais. Consequentemente preciso de um mtodo formal para medir o tempo e a memria requeridos pelo algoritmo, independentemente das caractersticas de plataforma de hardware e sof-tware sendo utilizadas.

    O que anlise de algoritmos?A anlise de algoritmos o corao da Cincia da Computao e tem

    por objetivo estabelecer medidas de desempenho dos algoritmos, com vis-tas gerao de algoritmos cada vez mais eficientes. Adicionalmente for-nece fundamentos para a escolha do melhor algoritmo para a resoluo de um problema especfico, com base na sua complexidade computacional.

    O tempo de execuo e o espao de memria alocado so os dois fatores principais que determinam a complexidade computacional de uma algoritmo.

    Complexidade temporal consiste no nmero (aproximado) de instru-es executadas.

    Complexidade espacial consiste na quantidade de memria utilizada.

    De forma geral, tanto a complexidade temporal quanto a espacial po-dem ser descritas por funes que tm como parmetro principal o tama-nho da entrada sendo processada. Como exemplos temos que:

    Ordenar 100.000 elementos leva mais tempo que ordenar 10 ele-mentos.

    A abertura de um editor de textos leva mais tempo e consume mais memria quando aberto com um arquivo grande do que com um arquivo pequeno.

    Alm do tamanho da entrada, as caractersticas apresentadas na organizao dos dados, tambm podem influenciar na eficincia de um algoritmo em relao a uma outra soluo. Por exemplo, o desempenho de um algoritmo especfico para ordenar um conjunto onde os dados se encontram parcialmente ordenados pode ser muito diferente se utilizado para ordenar um conjunto de dados totalmente desordenados, conside-rando conjuntos de igual tamanho. Logo importante estabelecer de que forma o tamanho e caractersticas da entrada podem influenciar no com-portamento do algoritmo.

    Em alguns casos, se o algoritmo no recursivo, no contem itera-es e no usa algoritmos com essas caractersticas, o nmero de passos necessrios pode ser independente do tamanho da entrada, e consequen-temente a complexidade temporal constante.

    Um exemplo de algoritmo com estas caractersticas o algoritmo que imprime HELLO WORD, que possui complexidade constante.

    A principal caracterstica de um algoritmo recursivo a ocorrncia de chama-das a ele prprio. Para avaliar a complexi-dade de um algoritmo re-cursivo preciso analisar quantas vezes a funo vai ser chamada, e quan-tas operaes acarretam cada chamada.

  • 13ESTRUTURA DE DADOS

    #include main() { Printf (Hello Word!\n); }Considerando que impossvel fazer uma predio exata do tempo e

    memria a serem utilizados a estratgia consiste em estabelecer uma apro-ximao, com base em conceitos e modelos matemticos.

    1. Descreva com suas palavras a relao entre os conceitos de algoritmo e programa.

    2. Determine em quais casos e de que forma as especificaes a seguir podem depender do tamanho ou organizao dos dados da entrada. Jus-tifique a sua resposta.

    a. Procurar o maior elemento em uma sequncia.

    b. Modificar o contedo de uma varivel.

    c. Imprimir o contedo de uma sequncia de elementos.

    d. A partir dos dados de uma pessoa: nome, data de nascimento, sexo, determinar se a pessoa maior de 18 anos.

  • 14 ESTRUTURA DE DADOS

    Captulo 2Medidas de Complexidade

    De forma geral, a complexidade de um algoritmo determinada pela quantidade de trabalho requerido sobre um determinado tamanho da en-trada. O trabalho depende diretamente do nmero de operaes bsicas efetuadas.

    Considere o problema de estabelecer se um determinado elemento pertence a um conjunto de 100 elementos. A soluo mais simples para este problema envolve uma pesquisa sequencial onde o elemento procu-rado comparado a cada um dos elementos pertencentes ao conjunto. O nmero de comparaes realizadas ir depender dos diversos cenrios possveis. A principio podemos analizar duas situaes: o elemento en-contrado na primeira comparao realizada ou, o elemento no existe no conjunto. No primeiro caso seria preciso somente uma comparao, en-quanto que no ltimo caso todo o conjunto precisaria ser checado, envol-vendo, portanto 100 comparaes.

    Situao 1:Elemento procurado: 10

    Conjunto de elementos:

    10 7 50 13 99 5 22 ... 2 66 29 15 88 77 0

    1 2 3 4 5 6 7 94 95 96 97 98 99 100

    Neste caso, na primeira comparao o logaritmo encontra o elemento pro-curado. A comparao a seguinte: Elemento procurado igual ao primeiro elemento do vetor? R-Sim. 10=10.

    Situao 2:Elemento procurado: 10

    Conjunto de elementos:

    10 7 50 13 99 5 22 ... 2 66 29 15 88 77 0

    1 2 3 4 5 6 7 94 95 96 97 98 99 100

    Neste caso, o elemento comparado com todos os elementos do vetor e no encontrado. As comparaes so as seguintes:

    1) Elemento procurado igual ao primeiro elemento do vetor? R-no. 105.2) Elemento procurado igual ao segundo elemento do vetor? R-no. 107. ...

    100) Elemento procurado igual ao centsimo elemento do vetor? R-no. 100.

    A partir das situaes apresentadas podemos concluir que a comple-xidade de um algoritmo pode ser estabelecida utilizando uma estratgia pessimista ou mxima, ou otimista ou mnima. Na estratgia pessimista, o algoritmo analisado levando em conta o cenrio mais adverso, o que normalmente ir resultar no maior tempo de execuo. Este critrio nor-

  • 15ESTRUTURA DE DADOS

    malmente o mais utilizado uma vez que estabelece uma cota ou limite su-perior no tempo requerido. Em oposio a esta abordagem, a complexidade otimista ou mnima obtida quando o problema analisado levando em conta um cenrio ideal demandando, portanto, menos tempo de execuo. Pode ainda ser considerado um caso mdio ou esperado, correspondente mdia dos tempos de execuo de todas as entradas de tamanho n. Estas estratgias podem ser utilizadas tanto para estabelecer a complexidade espacial quanto temporal dos algoritmos.

    A anlise para o clculo destas medidas pode ser realizada empiri-camente, isto , com base na experincia e na observao prtica, exclusi-vamente, sem se basear em nenhuma teoria. No entanto, a medio obtida pode ser influenciada por fatores acidentais referentes plataforma espe-cfica, como por exemplo, a capacidade de processamento do computador sendo utilizado. Consequentemente, a execuo de um algoritmo em um determinado computador pode ter um desempenho diferente medio resultante a partir da execuo do mesmo algoritmo em um outro compu-tador com caractersticas diferentes. Estas possveis divergncias tornam a abordagem baseada na medio emprica pouco confivel.

    1. Determine o caso otimista e o caso pessimista a partir das seguintes especificaes:

    a. Encontrar o maior elemento de um vetor (desordenado) de inteiros.

    b. Encontrar o maior elemento de um vetor ordenado de forma decres-cente de inteiros.

    c. Remover um dado elemento de um vetor de inteiros.

    d. Idem anterior considerando um vetor ordenado em forma crescente.

    2. Considere o problema de encontrar o maior elemento de um vetor de inteiros. O algoritmo a seguir apresenta uma soluo simples para o problema.

    int Max (A Vetor){ int i, Temp;

    Temp = A[0]; for (i := 1; i < n; i++) if (Temp < A[i]) Temp := A[i]; return (Temp);}a. Determine o nmero de comparaes realizadas entre os elementos

    de A, considerando que A contem n elementos.

    b. Descreva quais so as situaes que representam o melhor caso, o pior caso e o caso mdio para o algoritmo.

  • 16 ESTRUTURA DE DADOS

    Comparao entre algoritmosComo dito anteriormente, para um dado problema podem existir di-

    versos algoritmos possveis. Cabe ao programador analisar as possibilida-des e escolher o algoritmo mais adequado utilizando como critrio bsico a sua complexidade.

    Uma abordagem amplamente adotada para analizar a complexidade de algoritmos baseada na anlise sobre as operaes fundamentais que compem o algoritmo, a partir das quais derivada uma funo custo mo-delando o comportamento que o algoritmo ir a adotar de acordo com os dados de entrada fornecidos. Em particular, quando consideradas entradas suficientemente pequenas, o custo reduzido, mesmo no caso de algorit-mos ineficientes. J para tamanhos de entrada suficientemente grandes, a escolha por um determinado algoritmo pode ser um problema crtico. Logo, a anlise de algoritmos interessante para valores grandes da entrada, ou seja, no caso de algoritmos que manipulam grandes quantidades de dados.

    O estudo da complexidade consiste em determinar a ordem de magni-tude do nmero de operaes fundamentais realizadas pelo algoritmo, des-critas a partir da definio da funo custo. A partir desse estudo possvel realizar a comparao do comportamento assinttico atravs da anlise dos grficos correspondentes.

    A comparao entre funes com base no critrio de comportamento assinttico consiste no estudo do crescimento de funes para valores gran-des de n, no nosso caso, referente ao tamanho da entrada. A partir dessa anlise, as funes so classificadas em ordens, onde cada ordem agru-pa funes de crescimento semelhante. O algoritmo assintoticamente mais eficiente o melhor para todas as entradas. Neste contexto, uma funo considerada uma cota assinttica superior ou domina assintoticamente ou-tra, quando cresce mais rapidamente do que outra: graficamente, o grfico da primeira funo fica por cima da segunda a partir de certo ponto m. No caso geral, nem sempre possvel determinar se f (n) < g (n).

    Sejam f e g as duas funes de custo que queremos comparar:1. Se f sempre inferior a g, ou seja, o grfico de f fica sempre por bai-

    xo do grfico de g, ento a escolha para o algoritmo correspondente a f bvia.

    Grfico1-f sempre inferior a g. Neste caso, o algoritimo f melhor.

    2. Se f s vezes inferior a g, e vice-versa, e os grficos de f e g se inter-ceptam em um nmero infinito de pontos. Neste caso, consideramos que h empate, e a funo custo no ajuda a escolher um algoritmo.

    Comportamento observa-do de f(n) quando n tende a infinito.

  • 17ESTRUTURA DE DADOS

    Grfico2-svezesfsuperioresvezesgsuperioreosgrficosseinterceptamemumnmeroinfinitodepontos.Empate.

    3. Se f s vezes g inferior a g, e vice-versa, e os grficos de f e g se cortam em um nmero finito de pontos. Portanto, a partir de certo valor de n, f sempre superior a g, ou sempre inferior. Neste caso, consideramos melhor aquele algoritmo que inferior ao outro para grandes valores de n.

    Grfico3-Seosgrficosseinterceptamumaquantidadefinitadevezes,omelhoraque-lequemenorqueooutroparavaloresgrandesden.

    As funes mais comumente encontradas em anlise de programas, considerando n como tamanho da entrada, so:

    f(n) = k Constantef(n) = k . n Linearf(n) = n . log n Logartmicaf(n) = k . n2 Quadrticaf(n) = k . n3 Cbicaf(n) = nk Polinomialf(n) = k . en ExponencialA figura a seguir representa a ordem de crescimento das funes mais

    comumente utilizadas na representao da complexidade dos algoritmos. Comparativamente podemos concluir que, na medida em que o tamanho da entrada n aumenta, a funo linear n cresce mais rapidamente do que a funo log(n), que por sua vez cresce mais lentamente do que a funo quadrtica n2, e assim por diante.

  • 18 ESTRUTURA DE DADOS

    Sempre possvel determinar a taxa de crescimento relativo de duas funes f(n) e g(n) calculando Lim f(n) / g(n)

    xA partir deste clculo existem os seguintes valores possveis:

    0, ento g(n) limite superior para = f(n) c, ento f(n) e g(n) tem complexidades equivalentes infinito, ento f(n) limite superior para g(n)No caso de funes oscilantes, como o caso de sen(n) ou cos(n), nenhu-

    ma afirmao pode ser feita.

    1. Considere os algoritmos A e B com complexidades: CA(n) = 1000 n

    2

    CB(n) = 0, 1 n3

    Determine a partir de qual valor de n, CB domina assintoticamente CA?

    2. Para um determinado problema P, temos algoritmos A, B, C, e D com as seguintes complexidades.

    CA(n) = 100 n log n

    CB(n) = 1000 n

    CC(n) = 4 n2

    CD(n) = 10 5 en

    Classificar os algoritmos do melhor at o pior, em termos de complexi-dade, sempre considerando valores grandes da varivel n (tamanho da entrada). Justifique.

  • 19ESTRUTURA DE DADOS

    A notao utilizada para denotar a dominao assinttica foi introdu-zida por Knuth em 1968. De acordo com esta notao, a expresso g(n) = O(f(n)) expressa que f(n) domina assintticamente g(n). A expresso pode ser lida da seguinte forma: g(n) da ordem no mximo de f(n). As definies a seguir formalizam a notao de Knuth.

    A notao mais comumente usada para medir algoritmos O, uma vez que determina um limite superior da complexidade:

    Definio. A funo custo C(n) O(F(n)) se existem constantes positivas c e m tais que:

    C(n) c . F(n), quando n m

    A definio acima afirma que existe algum ponto m a partir do qual c . F(n) sempre pelo menos to grande quanto C(n). Assim, se os fatores constantes so ignorados, F(n) pelo menos to grande quanto C(n).

    Como exemplo, seja g(n) = (n + 1)2. Logo, g(n) O(n2), quando m = 1 e c = 4, uma vez que (n + 1)2 4n2 para n 1.

    Em outras palavras, quanto dizemos que C(n) = O(F(n)) estamos ga-rantindo que a funo C(n) no cresce mais rpido do que F(n). Assim, F(n) um limite superior para C(n).

    De forma geral, os termos de menor peso e os fatores podem sempre ser eliminados uma vez que para valores grandes da variaveis, estes com-ponentes se tornam despresiveis. Assim O(4n3 + 10n2) e O(n3) so sinni-mos, mas o segundo termo mais preciso.

    Se um algoritmo O(1) significa que o nmero de operaes funda-mentais executadas limitado por uma constante.

    Intuitivamente, se g(n) = 2 n2, ento g(n) = O(n4), g(n) = O(n3) e g(n) = O(n2). Todas as respostas so tecnicamente corretas, mas a menor a me-lhor resposta.

    Outras definies formais

    Limite inferior: A notao usada para especificar o limite inferior da complexi-dade de um algoritmo.

    Complexidadeexata:A notao usada para especificar exatamente a complexidade de um algoritmo.

    Limite superior estrito: oEnfim a notao o usada para especificar que a complexidade de um algoritmo e inferior estritamente a certa funo.

    Donald Ervin Knuth, na-cido em Milwaukee, em 10 de Janeiro de 1938, um cientista computacio-nal de renome e professor emrito da Universidade de Stanford. o autor do livro The Art of Computer Programming, uma das principais referncias da Cincia da Computao. considerado o criador do campo de Anlise de Algoritmos e fez diversas e importantes contribuies a vrios ramos da teoria da computao

  • 20 ESTRUTURA DE DADOS

    1. Suponha g(n) = n e f(n) = n2, demostre qual das seguintes afirmaes verdadeira:

    a. f(n) = O(g(n))

    b. g(n) = O(f(n))

    2. Determine a ordem mxima para as seguintes funes e justifique a sua resposta detalhando os valores de c e n adequados.

    a. g(n) = 3n3 + 2n2 + n

    b. h(n) = n2 + n + 1

    Operaes com a notao O

    Algumas operaes que podem ser realizadas com a notao O so apresentadas a seguir:

    f(n) = O(f(n))c . O(f(n)) = O(f(n)), onde c uma constanteO(f(n)) + O(f(n)) = O(f(n))O(O(f(n))) = O(f(n))O(f(n)) + O(g(n)) = O(max (f(n), g(n)))O(f(n)) . O(g(n)) =O(f(n) . g(n))f(n ). O(g(n)) = O(f(n) . g(n))

    Estas operaes foram demostradas matematicamente na literatura e tem aplicao direta para o clculo do tempo de execuo de (trechos de) programas. Por exemplo, a regra da soma, O(f(n)) + O(g(n)) pode ser utili-zada para calcular o tempo de execuo de uma sequncia de trechos ou sentenas de programas. Aplicando essa regra, somente ser considerado o trecho que tiver atribudo o custo mximo dentre os trechos ou sentenas considerados no sumatrio.

    Aplicando a abordagem de dividir para conquistar, a complexidade de um algoritmo pode ser determinada a partir da complexidade das suas par-tes. De forma geral, a anlise da complexidade feita em forma particular para cada algoritmo, mas existem conceitos gerais que s dependem das estruturas algortmicas ou comandos de controle utilizados no algoritmo:

    1) Sequncia ou conjuno um tipo de comando que, no fluxo lgico do programa, executado e o controle passa para o prximo co-mando na sequncia.

    A anlise da complexidade neste caso envolve a aplicao da regra da soma para a notao O com base na complexidade dos comandos.

    2) Seleo ou disjuno um tipo de comando que, no fluxo de exe-cuo permite que desvios possam ser feitos a partir do resultado da avaliao de uma condio, executando um bloco de comandos e outros no.

  • 21ESTRUTURA DE DADOS

    A anlise sempre feita considerando o pior caso. Consequen-temente, na avaliao da complexidade considerado o desvio que envolve a execuo do bloco de comandos mais custoso, a partir da avaliao da condio. Adicionalmente, a avaliao da condio consome tempo constante

    3) Repetio um tipo de comando que, no fluxo de execuo do pro-grama, permite que um bloco de comandos seja repetido enquanto uma condio satisfeita.

    Alm da checagem da condio (O(1)), o custo de um comando de repetio envolve o custo do bloco envolvido, multiplicado pelo nmero de vezes que ser executado, no pior caso. Vale ressaltar que pode existir aninhamento de comandos de repe-tio. Neste caso a anlise feita de dentro para fora. O lao pode ser definido (for) ou indefinido (while).

    4) Um comando de atribuio possibilita que um valor possa ser atri-budo a uma varivel.

    Um comando de atribuio consome tempo constante.

    Por exemplo, dado um vetor v de nmeros inteiros de tamanho n, re-tornar o maior elemento deste vetor.

    1: int Maximo (int v[], int n){2: int i: n;3: int max;4:5: if (n = 0) printf ( Vetor vazio );6: else {7: max = v[0];8: for (i = 1; i < n; i++) 9: if (v[i] > max)max = v[i];10: }11: return max;12:}O nmero n de elementos do vetor representa o tamanho da entrada.

    O programa contm um comando de iterao (8) que contm um comando de desio que, por sua vez, contm apenas um comando de atribuio (9). O comando de atribuio requer tempo constante O(1) para ser executado, assim como a avaliao do comando de desio. Considerando o pior caso, o comando de atribuio sempre ser executado.

    O tempo para incrementar o ndice do lao e avaliar sua condio de terminao tambm O(1), e o tempo combinado para executar uma vez o lao composto pelas linhas (8) e (9) O(max(1, 1)) = O(1), conforme a regra de soma para a notao O, e considerando que o nmero de iteraes do lao n - 1, ento o tempo gasto no lao (8) O((n - 1) x 1) = O(n - 1), conforme a regra do produto para a notao O.

  • 22 ESTRUTURA DE DADOS

    O algoritmo estruturado com base em um comando de desio, cuja condio checada em tempo constante, da mesma forma que a edio da mensagem de erro (O(1)). Por outro lado, utilizando a regra da soma nova-mente, temos que a execuo das linhas (7), (8) e (9) consome O(max(1, n - 1)) = O(n - 1). No pior caso temos que a execuo do bloco (5) consome O(n - 1). Finalmente, o comando (11) executado em tempo O(1). Aplicando novamente a regra da soma entre (5) e (11) temos que O(max(n 1, 1)) = O(n - 1). Portanto a complexidade temporal do algoritmo Maximo linear.

    Simplificando a anlise, a quantidade de trabalho do algoritmo pode ser determinada pela quantidade de execues da operao fundamental. No entanto, pode existir mais de uma operao fundamental com pesos diferen-tes. Neste caso, a regra de soma para a notao O pode ser aplicada.

    Considere o algoritmo buscaBinaria descrito a seguir:

    int buscaBinaria (int array[], int chave, int n){ int inf = 0; //Limite inferior int sup = n - 1; //Limite superior int meio; while (inf

  • 23ESTRUTURA DE DADOS

    Nesta unidade foi apresentado um estudo introdutrio sobre os fun-damentos da teoria sobre complexidade de algoritmos. Esta teoria de fundamental importncia uma vez que possibilita determinar a melhor soluo para um determinado tipo de problema, assim como tambm ela-borar projetos de algoritmos cada vez mais eficientes.

    CORMEN T. H., LEISERSON C. E., RIVEST R. L., STEIN C. (2001). Intro-ductiontoAlgorithms. McGraw-Hill e The Mit Press.

    KNUTH D. E. (1968). TheArtofComputerProgramming, Vol. 1: Funda-mental Algorithms. Addison-Wesley.

    KNUTH D. E. (1971).MathematicalAnalysisofAlgorithms. Procedings IFIP Congress 71, vol. 1, North Holland, 135-143.

    WIRTH N. (1986). AlgorithmsandDataStrutures. Prentice-Hall.

    ZIVIANI N. (2005). ProjetodeAlgoritmoscomimplementaesemPas-cal e C, 2da. Edio. Thomson.

  • Unidade

    Objetivos:

    Nesta unidade descrito o processo de abstrao seguindo para a modelagem e desenvolvimento de uma soluo computacional a partir de um problema do mundo real. Neste contexto, os conceitos de tipos de dados, estruturas e tipos abstratos so introduzidos, ressaltando sua importncia para a adequada modelagem, manipulao e representao na memria do computador.

    Representao dos Dados

    2

  • 27ESTRUTURA DE DADOS

    Captulo 1Modelagem Conceitual

    Para que um problema do mundo real possa ser resolvido compu-tacionalmente necessrio utilizar mtodos e tcnicas que possibilitem a modelagem adequada do problema, de forma que possa ser interpretado e processado pelo computador para gerar uma soluo.

    Os modelos so utilizados para representar o mundo real de forma simplificada, com o objetivo de facilitar o gerenciamento da complexidade. Um modelo reflete os aspectos considerados importantes para o desenvolvi-mento da aplicao, deixando em um segundo plano, os aspectos que no so relevantes.

    A modelagem de situaes do mundo real alcanada atravs de um processo de abstrao, a partir do qual, somente as propriedades relevantes para a aplicao so consideradas no modelo.

    O processo de abstrao envolve a observao das entidades presentes no domnio do problema para a correspondente representao no domnio computacional.

    No nvel mais baixo de abstrao, a representao dos dados a nvel de mquina acontece de acordo a padres de bits e bytes de memria. No entanto, as linguagens de programao modernas possibilitam que o programador possa trabalhar com objetos relativamente complexos em um nvel mais alto de abstrao, tornando transparente ao desenvolvedor a ma-nipulao dos dados ao nvel da sua representao interna no computador. Esta facilidade alcanada atravs da utilizao de uma variedade de tipos de dados.

    Tipo de dados O tipo de dados associado a uma varivel, numa linguagem de progra-

    mao, define o conjunto de valores que a varivel pode assumir. Isto , a declarao da varivel como sendo de um tipo especfico determina:

    1. A quantidade de bits que devem ser reservados na memria.2. Como o dado representado por esse padro de bits deve ser inter-

    pretado (p.e., uma cadeia de bits pode ser interpretada como sendo um inteiro ou real).

    Bit significa dgito binrio, do ingles BInary digiT. Um bit a menor unidade de informao que pode ser armazenada ou transmiti-da. Um bit pode assumir somente 2 valores: 0 ou 1, verdadeiro ou falso.O conjunto de 8 bits de-nominado de Byte.

    O sistema operacional (SO) um programa ou conjunto de programas responsvel por gerenciar todos os recursos do sis-tema, tais como: coman-dos do usurio, arquivos, memria, etc.

  • 28 ESTRUTURA DE DADOS

    Os tipos tm geralmente associaes com valores na memria ou va-riveis. Consequentemente, tipos de dados podem ser vistos como mtodos para interpretar o contedo da memria do computador, o que pode variar conforme o sistema operacional e a linguagem de implementao.

    Na sua maioria, as linguagens de programao exigem que um co-mando declarativo que define uma varivel especifique tambm o tipo de dados associado varivel. Estas linguagens so chamadas de fortemen-te tipadas. Em contrapartida, as linguagens fracamente tipadas permitem que a definio do tipo correspondente a uma varivel possa ser determina-da dinamicamente, em tempo de execuo.

    Os tipos de dados podem ser classificados em dois grupos: primitivos ou bsicos e compostos ou estruturados.

    Os tipos de dados primitivos so fornecidos pela linguagem de progra-mao como um bloco de construo bsico. Dependendo da implementa-o da linguagem, os tipos primitivos podem ou no possuir correpondn-cia direta com objetos na memria. Exemplos de tipos primitivos comuns so: inteiro, real, caractere, booleano, dentre outros.

    Alm de estabelecer um esquema predeterminado de armazenamento, a definio de um tipo de dados primitivo estabelece tambm um conjunto de operaes predefinidas sobre aquele tipo. Consequentemente, a definio de uma varivel como instncia de um desses tipos determina o conjun-to de operaes que podem ser realizadas utilizando essas variveis. Por exemplo, ao considerar variveis do tipo primitivo inteiro (int), as operaes permitidas se traduzem nos operadores aritmticos vlidos para os valores desse tipo, no caso: soma (+), subtrao (-), multiplicao (*), diviso inteira (DIV) e resto da diviso inteira (MOD). Estas operaes so implementadas de forma nativa por qualquer linguagem de programao, e seu desenvolvi-mento fica transparente ao usurio.

    Dada a complexidade das entidades do domnio do problema a serem modeladas e representadas no universo computacional, o fosso semntico (gap semntico) envolvendo a descrio de alto nvel de uma entidade (do-mnio do problema), e a descrio de baixo nvel (domnio da soluo) pode ser inconcilivel. Neste contexto, a possibilidade de definir tipos de dados que possibilitem a representao destas entidades de forma mais prxima da realidade facilita o processo de abstrao, assim como contribui para a reduo do fosso semntico entre ambos os domnios.

    O programador pode definir tipos de dados prprios que mais corres-pondam s necessidades de suas aplicaes. Linguagens de programao atuais permitem aos programadores definir tipos de dados adicionais, uti-lizando os tipos primitivos e as estruturas como blocos construtivos. Por exemplo, para definir a varivel Empregado com esta estrutura heterognea em C seria:

    struct Empregado{ char Nome[9]; int Idade; float Qualificao;};No entanto, se a estrutura for muito frequente, o programa pode ficar

    volumoso e difcil de ser lido. O mtodo mais adequado consiste em descre-

    A memria o dispositivo que permite a um compu-tador armazenar dados de forma temporria ou per-manente.

    O fosso semntico repre-senta a diferena entre uma descrio de alto n-vel e outra de baixo nvel relativa a uma mesma en-tidade.

  • 29ESTRUTURA DE DADOS

    ver a estrutura de dados correspondente uma nica vez, associando-a a um nome descritivo, e utilizar esse nome todas as vezes que for necessrio.

    typedef struct { char Nome[9]; int Idade; float Qualificao;} TipoEmpregado;Esta declarao define um novo tipo denominado TipoEmpregado

    que pode ser usado para declarar variveis da mesma forma como um tipo primitivo.

    TipoEmpregado Empregado;A partir desta definio possvel gerar as correspondentes variveis

    instncia, as quais iro assumir valores nos atributos, dependendo do tipo. Por exemplo, valores vlidos para uma varivel do tipo Empregado podem ser:

    Nome: Joo SoaresIdade: 25 anosQualificao: 8.58

    Um tipo definido pelo usurio essencialmente um modelo usado para construir instncias de um dado tipo, que descreve todas as caractersticas que todas as instncias deste tipo devem assumir mas no constitui, ele prprio, uma ocorrncia real deste tipo.

    A forma conceitual dos dados materializada pela estrutura de dados utilizada na implementao do tipo. Uma estrutura de dados uma forma particular de se implementar um tipo, e construda dos tipos primitivos e/ou compostos de uma linguagem de programao, e por este motivo so chamados de tipos compostos ou estruturados.

    Um tipo composto envolve um conjunto de campos e membros orga-nizados de forma coerente, onde o tamanho total da estrutura corresponde soma dos tamanhos dos campos constituintes. Exemplos de tipos estru-turados so: registros, vetores, matrizes, arquivos, rvores, dentre outros.

    A escolha por uma estrutura de dados determinante na qualidade e esforo requerido para o desenvolvimento da soluo. As estruturas de da-dos e algoritmos so escolhidos com base em critrios diversos, tais como desempenho, restries de plataforma de hardware e software, capacidade do equipamento, volume de dados, etc. As estruturas de dados dividem-se em homogneas e heterogneos. As estruturas homogneas so conjuntos de dados formados por componentes do mesmo tipo de dado (p.e., vetores, ma-trizes, pilhas, listas, etc.). Em contrapartida, as estruturas heterogneas so conjuntos de dados formados por componentes pertencentes a tipos de dados diferentes (p.e., registros). A escolha de uma estrutura de dados apropriada pode tornar um problema complicado em uma soluo bastante trivial.

    Diferentemente do que acontece na definio de tipos de dados bsicos, onde um conjunto de operaes de manipulao fornecido pela linguagem de progamao (soma, subtrao,...), no caso dos tipos estruturados defini-dos pelo usurio apenas novos esquemas de armazenamento so definidos. Isto significa que, a principio, no so fornecidos meios para definir as ope-

  • 30 ESTRUTURA DE DADOS

    raes a serem executadas sobre instncias de tais estruturas. Consequen-temente, algoritmos de manipulao precisam ser desenvolvidos de forma a possibilitar a correta utilizao e acesso s novas estruturas definidas.

    1. Defina os conceitos de tipo de dado bsico e tipo de dado definido pelo usurio.

    2. Cite como exemplos tipos de dados bsicos que voc conhece e detalhe suas caractersticas e as operaes permitidas sobre esses tipos.

    3. Defina um tipo de dado estruturado que descreva de forma adequada e completa as seguintes informaes:

    a. Livro

    b. Crculo

    c. Filme

    d. Pessoa

    e. Aluno

    f. Item de estoque

    g. Conta bancaria

    Tipo abstratos de dados Um tipo de dado definido pelo usurio, incrementado com a definio e

    implementao das operaes necessrias para a sua manipulao, consti-tui um Tipo Abstrato de Dados (TAD), conceito central no contexto do Para-digmaOrientadoaObjetos. A utilizao de TADs permite que linguagens de programao de propsito geral sejam personalizadas para um domnio de aplicao mais especfico. Uma vez definidos, podem ser empregados como primitivas da linguagem, e possibilitam o desenvolvimento de compo-nentes de software reusveis e extensveis.

    TADs estendem a noo de tipo de dado (estrutura de dados + ope-raes) com base na utilizao de tcnicas de ocultamento da informao referente estrutura de dados utilizada e implementao das operaes definidas. Este objetivo alcanado atravs de uma clara separao entre interface e implementao.

    A seguir apresentado como exemplo a definio do tipo abstrato Retngulo. Um retngulo pode ser definido pela sua largura e altura. A es-pecificao do tipo retngulo pode ser definida como:

    typedef struct { float altura, largura;

    } TipoRetangulo;

    O paradigma orientado a objetos envolve um con-junto de tcnicas, mto-dos e ferramentas para anlise, projeto e imple-mentao de sistemas de software baseado na composio e interao de componentes de software denominados de objetos.

  • 31ESTRUTURA DE DADOS

    A partir destas informaes possvel calcular sua rea e perme-tro. Portanto, alm das operaes de consulta sobre as informaes do retngulo, as operaes de clculo de rea e permetro so requeridas.

    Desta forma, a interface do tipo abstrato retngulo inclui as seguintes operaes de manipulao.

    //inicia valores do retangulovoid inicia _ retangulo (float a, float l);

    // atribui um valor altura void InitAltura (float a);

    // retorna o valor da altura float Altura (void); // atribui um valor largura void InitLargura (float l); // retorna o valor da largura float Largura; // calcula o valor do permetro float Perimetro;

    // calcula o valor da rea float Area (prottipos das operaes);

    Finalmente, todos os mtodos implementados na linguagem de pro-gramao. Exemplos da implementao de alguns dos mtodos so apre-sentados a seguir.

    void inicia _ retangulo (float a, float l)altura = a;

    largura = l;}

    float Altura () { return altura; }

    float Perimetro () { return 2 * (altura + largura);}

    O projeto de um tipo abstrato uma tarefa difcil, pois este deve ser idealizado de forma a possibilitar a sua utilizao por parte de terceiros, fa-vorecendo o reuso e agilizando o processo de desenvolvimento de software. A atividade de projeto envolve a escolha de operaes adequadas, delineando seu comportamento consistente, de forma que estas possam ser combinadas para realizar funes mais complexas, a partir de operaes simples.

    O construtor um mto-do distinguido que tem como funo principal a de instanciar o objeto cor-retamente, para seu uso posterior.

    Prottipos na linguagem C define o cabealho das funes.

    A fase de projeto produz uma descrio computa-cional do que o software deve fazer, e deve ser co-erente com as especifica-es geradas na anlise, de acordo com os recursos tecnolgicos existentes.

  • 32 ESTRUTURA DE DADOS

    No intuito de aumentar o reuso das operaes, estas devem ser defini-das de forma coesa, e ter um comportamento coerente, com um propsito es-pecfico e evitando considerar diversos casos especiais em um mesmo cdigo.

    O conjunto de operaes que integram a interface do TAD deve ofere-cer todas as operaes necessrias para que os usurios possam manipu-lar a estrutura adequadamente. Um bom teste consiste em checar se todas as propriedades do objeto de um determinado tipo podem ser acessadas.

    A escolha pela representao mais adequada ao problema envolve uma anlise aprofundada, uma vez que cada representao possvel possui diferentes vantagens e desvantagens.

    1. Defina os conceitos de Tipo Abstrato de Dados. Estabelea o relaciona-mento que vincula ambos conceitos.

    2. Defina TADs para os tipos de dados estruturados para as entidades listadas a seguir especificando detalhadamente a interface completa. A interface deve incluir todas as operaes necessrias para a correta ma-nipulao do tipo, dentre elas os mtodos de inicializao, modificao, e consulta.

    a. Livro

    b. Crculo

    c. Filme

    d. Pessoa

    e. Aluno

    f. Item de estoque

    g. Conta bancria

    3. Implemente dois TADs do exerccio anterior utilizando a linguagem de programao da sua escolha ou pseudo-cdigo prximo da linguagem.

    Critrios para a escolha da estrutura de dados adequada

    Como dito anteriormente, a interface do TAD independe da implemen-tao e, portanto, diferentes estruturas de dados podem ser utilizadas como esquema de armazenamento na memria para seus atributos. A partir do esquema utilizado, os dados podem ser armazenados e recuperados.

    A forma em que a alocao de memria acontece, pode ser um fator determinante para a escolha de uma determinada estrutura de dados.

    Uso da memria

    Informalmente, podemos dizer que existem trs maneiras de reservar-mos espao de memria para o armazenamento de informaes:

    Uso de variveis globais (e estticas). O espao reservado para uma varivel global existe enquanto o programa estiver sendo executado.

    A memria o dispositivo que permite a um compu-tador guardar dados de forma temporria ou per-manente.

  • 33ESTRUTURA DE DADOS

    Uso de variveis locais. Neste caso o espao fica reservado apenas enquanto a funo que declarou a varivel est sendo executada, sendo liberado para outros usos quando a execuo da funo ter-mina. Por este motivo, a funo que chama no pode fazer refe-rncia ao espao local da funo chamada. As variveis globais ou locais podem ser simples ou vetores, sendo que no caso dos vetores, preciso informar o nmero mximo de elementos ou tamanho do vetor. A partir do tamanho informado, o compilador reserva o espa-o correspondente.

    Requisies ao sistema em tempo de execuo. Este espao alocado dinamicamente permanece reservado at que explicitamente seja liberado pelo programa. Uma vez que o espao liberado fica dispo-nvel para outros usos. Se o espao alocado no for liberado explici-tamente pelo programa, este ser automaticamente liberado quando a sua execuo terminar. A seguir ilustrada esquematicamente a alocao da memria pelo sistema operacional.

    Quando requisitamos ao sistema operacional para executar um de-terminado programa, o cdigo em linguagem de mquina do progra-ma deve ser carregado na memria. O sistema operacional reserva tam-bm o espao necessrio para ar-mazenarmos as variveis globais (e estticas) utilizadas ao longo do programa. O restante da memria utilizado pelas variveis locais e pe-las variveis alocadas dinamica-mente enquanto o programa est executando.

    Cada vez que uma determinada fun-o chamada, o sistema reserva o espao necessrio para as variveis locais da funo. Este espao per-tence pilha de execuo e, quando a funo termina, desempilhado e

    liberado. A parte da memria no ocupada pela pilha de execuo pode ser requisitada dinamicamente. Se ao longo de diversas chamadas a funo, a pilha cresce atingindo o espao disponvel existente, dizemos que ela estou-rou e o programa abortado com erro. Similarmente, se o espao de mem-ria livre for menor que o espao requisitado dinamicamente, a alocao no feita e o programa pode prever um tratamento de erro adequado (por exem-plo, podemos imprimir a mensagem Memria insuficiente e interromper a execuo do programa).

    Como mencionado anteriormente, a alocao de memria pode acon-tecer em tempo de compilao (esttica) ou em tempo de execuo (dinmi-ca). No caso da alocao de memria em forma esttica, o espao destinado para o armazenamento dos dados possui um tamanho fixo, que no pode ser modificado ao longo da execuo do programa. Adicionalmente, a aloca-o de memria acontece em espaos contguos, isto , um do lado do outro. Exemplos de alocao esttica de memria so variveis globais e vetores. No caso do vetor, a partir de certo endereo que armazena o primeiro ele-mento a1 do vetor, os elementos subsequentes podem ser acessados direta-

  • 34 ESTRUTURA DE DADOS

    mente incrementando em k o endereo inicial do vetor, onde k o tamanho de memria ocupado por cada elemento do vetor.

    Em contrapartida, no caso de alocao dinmica, a memria ge-renciada sob demanda, ou seja, os espaos de memria so alocados e de-salocados dependendo da necessidade, durante a execuo do programa. Consequentemente, a alocao no acontece necessariamente de forma contgua (ou sequencial), podendo, os dados, ficarem esparsos na memria do computador. A partir desta configurao onde os dados so armazena-dos de forma no sequencial, o acesso alcanado atravs de variveis do tipo ponteiro, indicando o endereo de memria correspondente. A seguir ilustrada esquematicamente a distribuio dos elementos integrantes de uma cadeia ao longo da memria. Na primeira coluna indicado o endereo correspondente ao contedo. A coluna ponteiro indica o endereo do prxi-mo elemento na cadeia. Note que o segundo elemento no ocupa o endereo consecutivo ao a1.

    Endereo Contedo Ponteiro Observao

    L=3FFB a1 1D34Primeiro elemento acessado

    a partir de L1D34 a2 BD2FBD2F a3 AC131500 an-2 16F716F7 an-1 5D4A

    5D4A an nullltimo elemento da cadeia. O endereo null indica que o elemento no tem sucessor.

    A partir das caractersticas de cada uma das abordagens para a alo-cao dos dados, vantagens e desvantagens podem ser estabelecidas.

    Alocaoesttica:

    Vantagem: Possibilita acesso direto ao local da memria, uma vez que os dados se encontram armazenados de forma contgua ou se-quencial. Consequentemente, em alguns casos, as operaes de busca podem ter custo constante O(1).

    Desvantagem: preciso determinar em tempo de codificao, quan-to espao necessrio. Esta estimativa pode ser difcil de ser esta-belecida, e est sujeita a flutuaes ao longo da execuo. Conse-quentemente o espao pode resultar insuficiente ou pode ter sido sobreestimado. Por outro lado, a alocao sequencial na memria prejudica as operaes de insero e remoo, uma vez que a se-quencialidade dos dados precisa ser preservada. Com isso, no pior caso, o custo destas operaes pode ser de O(n) por conta dos deslo-camentos necessrios para abrir ou fechar espaos para insero e remoo, respectivamente.

    O ponteiro ou apontador um tipo de dado que ar-mazena o endereo de um outro dado.A partir do ponteiro, o dado que se encontra no respectivo endereo pode ser acessado e manipu-lado.

  • 35ESTRUTURA DE DADOS

    Alocaodinmica:

    Vantagem: no necessrio fixar o tamanho da estrutura a priori, uma vez que a alocao de memria feita sob demanda em tempo de execuo. Com isso evitado o desperdcio de espao, e o risco de ficar sem espao reduzido. Consequentemente, no existe res-trio encima do nmero de inseres e remoes. Adicionalmente, estas operaes no requerem de nenhum esforo adicional uma vez que envolvem somente o ajuste dos ponteiros j que os elemen-tos se encontram esparsos na memria.

    Desvantagem: o gerenciamento dos dados diretamente da memria pode ser trabalhoso e propenso a erros. Como conseqncia da no linearidade na alocao dos dados, o acesso a um determina-do elemento i torna necessrio o percurso pelos i 1 elementos an-teriores na sequncia. Esta propriedade, que caracteriza o acesso sequencial aos dados, torna a operao de busca por um elemento de custo O(n).

    Nesta unidade foi apresentado o conceito de abstrao e seus nveis, e a sua importncia no processo de modelagem. Tipos de dados bsicos e estruturados foram definidos neste contexto, como meios de representar e interpretar as informaes manipuladas pela aplicao. Adicionalmente, o conceito de tipo abstrato estabelecido como um mecanismo de extenso e customizao de linguagens de programao, no intuito de facilitar o de-senvolvimento de sistemas. TADs so caracterizados por suas operaes, as quais so encapsuladas junto sua estrutura, sendo acessveis exclu-sivamente atravs da interface especificada, garantindo independncia da implementao utilizada. A independncia de representao torna possvel alterar a representao de um tipo sem que seus clientes sejam afetados. Finalmente, noes de gerncia de memria foram introduzidas, focando nas principais caractersticas que influenciam na escolha pela alocao esttica ou dinmica da memria. Uma anlise dos fatores que contribuem na tomada de deciso foi apresentada.

    CORMEN T. H., LEISERSON C. E., RIVEST R. L., STEIN C. (2001). Intro-ductiontoAlgorithms. McGraw-Hill e The Mit Press.

    TENENBAUM A. M., LANGSAM Y., AUGENSTEIN M. J. (1995). EstruturasdeDadosUsandoC, Makron Books/Pearson Education.

    WIRTH N. (1986). AlgorithmsandDataStructures. Prentice-Hall.

    ZIVIANI N. (2005). ProjetodeAlgoritmoscomimplementaesemPas-cal e C, 2da. Edio. Thomson.

  • Unidade

    Objetivos:

    Existem certas estruturas clssicas que se comportam como padres uma vez que so utilizadas na prtica em diversos domnios de aplicao. Nesta unidade apresentada a estrutura de dados Lista e o correspondente Tipo Abstrato, detalhando a sua interface e apresentando duas implementaes: vetores e ponteiros. Variantes do tipo abstrato listas, na forma de Pilhas e Filas, de ampla utilizao prtica, so descritas.

    Listas

    3

  • 39ESTRUTURA DE DADOS

    Captulo 1Listas

    IntroduoUm conjunto de elementos pode ser intuitivamente representado atra-

    vs de uma lista linear. Listas so estruturas extremamente flexveis que possibilitam uma ampla manipulao das informaes uma vez que inser-es e remoes podem acontecer em qualquer posio.

    Uma lista pode ser definida como uma estrutura linear, finita cuja or-dem dada a partir da insero dos seus elementos componentes. As listas so estruturas compostas, constitudas por dados de forma a preservar a relao de ordem linear entre eles. Cada elemento na lista pode ser um dado primitivo ou arbitrariamente complexo.

    Em geral, uma lista segue a forma a1, a2, a3, ..., an, onde n determina o tamanho da lista. Quando n = 0 a lista chamada nula ou vazia. Para toda lista, exceto a nula, ai + l segue (ou sucede) ai (i < n), e ai - 1 precede ai (i > 1). O primeiro elemento da lista a1, e o ltimo an. A posio correspondente ao elemento ai na lista i. A lista pode ser representada visualizando-se um vetor, por exemplo.

    As caractersticas bsicas da estrutura de dados lista so as seguintes:

    Homognea. Todos os elementos da lista so do mesmo tipo.

    A ordem nos elementos decorrente da sua estrutura linear, no entanto os elementos no esto ordenados pelo seu contedo, mas pela posio ocupada a partir da sua insero.

    Para cada elemento existe anterior e seguinte, exceto o primeiro, que no possui anterior, e o ltimo, que no possui seguinte.

    possvel acessar e consultar qualquer elemento na lista.

    possvel inserir e remover elementos em qualquer posio.

    Definio do TAD ListaComo apresentado na unidade anterior, a definio de um Tipo Abs-

    trato de Dados (TAD) envolve a especificao da interface de acesso para a manipulao adequada da estrutura, a partir da qual so definidas em detalhe, as operaes permitidas e os parmetros requeridos. O conjunto de operaes depende fortemente das caractersticas de cada aplicao, no entanto, possvel definir um conjunto de operaes mnimo, necessrio e comum a todas as aplicaes.

    Uma estrutura dita de linear uma vez que seus elementos componentes se encontram organizados de forma que todos, a ex-ceo do primeiro e lti-mo, possuem um elemen-to anterior e um posterior, somente.

  • 40 ESTRUTURA DE DADOS

    Interface do TAD Lista

    // Cria uma lista vaziaLista Criar ()

    //insere numa dada posio na lista.

    int Inserir (Lista l, tipo _ base dado, corrente pos)

    // Retorna o elemento de uma dada posio

    tipo _ base consultaElemento (Lista l, corrente pos);

    // Remove o elemento de uma determinada posio

    int Remover (Lista l, corrente pos);

    // Retorna 1 a lista est vazia, ou 0 em caso contrario.int Vazia (Lista l);

    // Retorna 1 se a lista est cheia, ou 0 em caso contrario.int Cheia (Lista l);

    // Retorna a quantidade de elementos na lista.int Tamanho (Lista l);

    // Retorna o prximo elemento na lista a partir da posio corrente.

    corrente proximoElemento (Lista l, corrente pos);

    /*Busca por um determinado elemento e retorna sua posio corrente, ou -1 caso no seja encontrado.*/

    corrente Busca (Lista l, tipo _ base dado);

    Uma vez definida a interface, esta pode ser implementada utilizando uma representao dos dados adequada. Existindo mais de uma estrutura adequada, a escolha depende principalmente das necessidades e caracters-ticas dos dados a serem manipulados pela aplicao.

    A seguir, o Tipo Abstrato Lista implementado utilizando duas estru-turas de dados comumente utilizadas e adequadas s necessidades, cada uma com vantagens e desvantagens particulares.

    Implementao do TAD Lista usando alocao esttica

    Na implementao de lista adotando alocao de memria esttica os elementos componentes so organizados em posies contguas de memria utilizando arranjos ou vetores.

    Vantagens e desvantagens desta estrutura foram discutidas na unida-de 3. Em particular, a utilizao de vetores se torna adequada no caso em que existe uma clara noo do tamanho da entrada a ser processada, e uma

  • 41ESTRUTURA DE DADOS

    perspectiva que indica que as aplicaes que iro utilizar o TAD no estaro executando muitas operaes de insero e remoo que possam vir a alte-rar significativamente o tamanho preestabelecido. A estruturao da lista utilizando alocao esttica apresentada graficamente a seguir. Note que, a partir do endereo correspondente ao primeiro elemento no vetor (pos), e conhecendo o tamanho (c) de cada componente na lista, possvel calcular o endereo na memria de qualquer elemento armazenado no vetor. Isso garante o acesso direto aos elemento em O(1).

    A seguir apresentada a definio da estrutura de dados e imple-mentao das operaes definidas na interface utilizando alocao esttica de memria atravs da definio de vetores. Considerando a utilizao de alocao esttica de memria, o tamanho da estrutura de dados precisa ser determinado em tempo de compilao.

    #define tamanho

    Como o prottipo da lista definido de modo a ser implementado usando vetor ou outro recurso, torna-se necessrio definir um tipo que pos-sa ser utilizado como elemento que acessado nas operaes (ou corren-te). No caso desta implementao inicial, utilizando vetor, os elementos so acessados atravs de suas posies no vetor, sendo que estas posies so representadas como interiores que iniciam em 0 (zero) na primeira posio e seguem at n-1 (tamanho do vetor - 1). Portanto, torna-se necessrio definir o tipo corrente como inteiro.

    typedef int corrente;

    Uma lista um tipo de dado que estrutura elementos cujo tipo pode ser arbitrariamente complexo, envolvendo inclusive, a utilizao de outros TADs. A definio a seguir especifica o tipo base dos elementos da lista como inteiros.

    typedef int tipo _ base;

    Os elementos da lista so organizados em um vetor, de tamanho prede-finido. Adicionalmente, um atributo contendo a quantidade de elementos da lista (quant_Elem) includo na estrutura no intuito de tornar mais fcil e gil o acesso aos elementos e possibilitar o controle do crescimento da estrutura.

    typedef struct { tipo _ base v[tamanho]; int quant _ Elem; } no _ Lista;

    A notao utilizada na im-plementao prxima linguagem C.

  • 42 ESTRUTURA DE DADOS

    A informao relativa quantidade de elementos existente na lista pode ser til em diversas situaes uma vez que, conhecendo a posio na memria (endereo) do primeiro elemento da lista, possvel calcular o en-dereo do ltimo elemento e, consequentemente, da primeira posio dispo-nvel. Isto possvel por conta da propriedade de armazenamento contguo propiciada pela estrutura de dados.

    Finalmente a lista definida como um ponteiro estrutura de dados onde os elementos so agregados.

    typedef no _ Lista *Lista;

    A criao de uma lista inicialmente vazia envolve a definio do pon-teiro correspondente, apontando a um endereo de memria reservado com tamanho adequado para o armazenamento da estrutura, em particular, o primeiro n representando a cabea da lista. O tamanho da lista iniciali-zado em zero uma vez que inicialmente no contm elementos.

    Lista Criar () { Lista l = (Lista) malloc (sizeof (no _ Lista)); if (l != NULL){ l -> quant _ Elem = 0; return (l); } else printf (No existe memria suficiente); return;}

    Normalmente, a lista precisa ser percorrida de forma a realizar algum tipo de processamento sobre os dados que a compem. Consequentemente se torna necessrio um mecanismo que possibilite checar no momento em que no seja possvel processar mais nenhum elemento. O mtodo ultimoE-lemento responsvel por fazer esta checagem.

    int ultimoElemento (Lista l, corrente pos) { if (pos + 1 == l -> quant _ elem) return (1) else return (0);}

    A execuo de uma operao de remoo requer a existencia de no mnimo um elemento na lista. A funo Vazia utilizada para informar se h elementos no vetor, retorna o valor 1 no caso da lista se encontrar vazia, e 0 em caso contrrio.

    int Vazia (Lista l) { if (l -> quant _ Elem == 0) return (1) else return (0);}

    Na linguagem C, a posi-o que corresponde ao primeiro elemento do ve-tor corresponde ao ndice i = 0. Em outras lingua-gens, como por exemplo Pascal, o primeiro elemen-to no vetor se encontra na posio i=1.

  • 43ESTRUTURA DE DADOS

    Outra operao que pode ser de utilidade a checagem pelo caso em que a estrutura que armazena a lista possa estar cheia, uma vez que esta situao pode inviabilizar a insero de um novo elemento. Este mtodo de fundamen-tal importncia, principalmente no caso de utilizao de alocao de memria esttica onde a tentativa de insero de um novo elemento pode acarretar o estouro da memria, fazendo com que o programa termine com erro.

    int Cheia (Lista l) { if (l -> quant _ Elem == tamanho) return (1) else return (0);}

    Uma funo que pode ser definida para auxiliar a verificao de pr-ximo elemento e a insero uma funo vailadPos. Esta recebe a lista e a posio atual e verifica se a posio maior ou igual a zero e se a posio maior que a quantidade de elementos -1.

    int validaPos(Lista l, corrente pos){ if(pos >=0&&pos< ((l-> quant _ Elem) -1)){ return (1); }else{ return (0); }}

    Adicionalmente, o percurso ao longo dos elementos de uma lista re-quer de uma operao que possibilite a movimentao ao longo da estrutu-ra, elemento a elemento. A funo proximoElemento retorna o ndice no vetor correspondente posio do prximo elemento, se for o ltimo e no tiver prximo, retorna -1.

    corrente proximoElemento (Lista l, corrente pos) { pos = pos++; if (validaPos(l, pos) return (pos); return (-1);}

    A operao de insero possivelmente uma das mais importantes, uma vez que atravs dela que a lista ser construda. Em particular, o tipo lista no possui nenhuma restrio em relao insero, podendo acontecer em qualquer posio. Desta forma, na hora de inserir um ele-mento na lista, necessrio informar qual a posio correspondente ao novo elemento, seja no inicio, meio ou fim da lista. Considerando que a insero nem sempre possvel por conta da limitao de espao da estru-tura de dados utilizada, a funo retorna 1 se a insero foi realizada com sucesso e 0 em caso contrrio

  • 44 ESTRUTURA DE DADOS

    Algoritmo0

    int Inserir (Lista l, tipo _ base dado, corrente pos) { int i;if (!validaPos(l pos) || (cheia (l))) return (0);

    for (i = l->) quant _ elem ; i > = pos; i--){l -> v[i] = l -> v[i-1];}l -> v[pos] = dado;l -> quant _ elem = (l -> quant _ elem)+1;

    return (1);}

    Dependendo da posio onde o elemento ser inserido, o trabalho re-querido pode ser estimado de forma a estabelecer o custo da funo. Pelo fato de se utilizar alocao esttica e contgua de memria para o armaze-namento dos elementos da lista, a insero de um elemento em uma execu-o requer que o espao fsico na memria seja providenciado em tempo de compilao. Para isso necessrio deslocar (shift) todos os elementos neces-srios, desde a posio requerida at o final da lista. Por exemplo, se a lista possui 5 elementos e o novo elemento precisa ser inserido na posio 3, os ltimos 3 elementos precisaro ser deslocados direita, para que a posio de insero requerida fique disponvel para o novo elemento a ser inserido. A situao ilustrada na figura a seguir.

    O pior cenrio neste caso acontece quando requerida a insero na primeira posio da lista, obrigando o deslocamento de todos os elementos da lista em uma posio direita. Nete caso, o custo requerido O(n).

    Analogamente operao de insero, a operao de remoo exclui um elemento em qualquer posio, portanto esta posio precisa ser infor-mada explicitamente. O algoritmo responsvel por checar se a posio informada representa uma posio vlida dentro da estrutura.

  • 45ESTRUTURA DE DADOS

    Algoritmo0.1

    int Remover (Lista l, corrente pos) {int i;if (vazia(l) || (!validaPos(l)) return (0);

    int dado = l -> v[pos];for (i = pos + 1 ; i < l -> quant _ elem; i++) l -> v[i-1] = l -> v[i]; l -> quant _ elem = (l -> quant _ elem)-1;return (1);

    }

    Realizando uma anlise anloga ao caso da insero, o custo para a remoo O(n).

    No caso em que seja necessrio remover um elemento de acordo com algum contedo especfico, o elemento em questo precisa ser previamente localizado atravs da operao de Busca. A partir da posio retornada pela busca, caso o elemento seja efetivamente encontrado na estrutura, a remo-o poder ser efetivamente realizada.

    A busca por um determinado elemento pode ser originada de duas for-mas. Na primeira variante, a busca pode ser orientada por um determinado contedo, retornando como resultado a sua posio na lista, no caso em que o elemento for efetivamente encontrado, caso contrrio retorna -1.

    corrente Busca (Lista l, tipo _ base dado) { int i; for (i = 0; i v[i] == dado) return (i); return (-1);}

    O pior caso possvel para esta busca, consiste na situao onde o elemento procurado no se encontra na lista. Nesta situao a lista ser percorrida na totalidade, sem sucesso, passando pelos n elementos que de-terminam seu tamanho. Consequentemente, o custo desta operao no seu pior caso O(n), ou seja linear.

    Na segunda variante, a busca pode acontecer a partir de uma deter-minada posio na lista, retornando o elemento contido nessa posio.

  • 46 ESTRUTURA DE DADOS

    tipo _ base Consulta (Lista l, corrente pos) {return (l -> v[pos-1]);}

    A complexidade da busca neste caso O(1) uma vez que consiste no aces-so direto posio correspondente, demandando para isso tempo constante.

    1. Considerando a implementao do TAD utilizando alocao esttica de memria resolva as questes a seguir:

    a) Explique por que o custo da remoo em uma lista implementada uti-lizando alocao esttica e contgua de memria O(n).

    b) Implemente a operao que retorna a quantidade de elementos na lis-ta, cujo cabealho : int tamanho (lista l). Determine a complexidade da operao implementada.

    c) Implemente o mtodo auxiliar que verifique se uma determinada po-sio vlida, isto , se encontra dentro dos limites do vetor. O cabe-alho da operao int validaPos (corrente pos).

    Implementao do TAD Lista usando alocao dinmica Na implementao do Tipo Abstrato lista adotando alocao de mem-

    ria dinmica, a alocao de memria gerenciada sob demanda em tempo de execuo. Esta caracterstica determina que os elementos componentes so organizados em posies no-contguas, ficando espalhados ao longo da memria. Consequentemente, no preciso estimar a priori o tamanho da estrutura uma vez que o espao alocado na medida da necessidade, dependendo das operaes de insero e remoo realizadas.

    Vantagens e desvantagens desta estrutura foram discutidas na Unida-de 3. Em particular, esta estrutura se torna adequada quando o tamanho da estrutura desconhecido e pode variar de forma imprevisvel. No entan-to, a gerncia da memria torna a implementao mais trabalhosa e pro-pensa a erros, podendo acarretar em perda de informao. Adicionalmente, o acesso aos dados seqencial, no sentido que para acessar o elemento na posio m, se torna necessrio percorrer os m - 1 elementos anteriores. Com isso, no pior caso, a busca por um elemento na lista demanda custo O(n).

    A seguir apresentada a definio da estrutura de dados e imple-mentao das operaes definidas na interface para o TAD Lista utilizando alocao dinmica de memria atravs de ponteiros. A notao utilizada na implementao prxima linguagem C. Esta estrutura representa uma seqncia de elementos encadeados por ponteiros, ou seja, cada elemento deve conter, alm do dado propriamente dito, uma referncia para o prximo elemento da lista. Graficamente, a estrutura de dados para a implementa-o de listas utilizando ponteiros a seguinte:

  • 47ESTRUTURA DE DADOS

    Por exemplo, uma lista com valores de ponteiros a endereos reais tem a seguinte forma:

    O espao total de memria gasto pela estrutura proporcional ao n-mero de elementos nela armazenados: para cada novo elemento inserido na estrutura alocado um espao de memria para armazen-lo. Consequen-temente, no possvel garantir que os elementos armazenados na lista ocuparo um espao de memria contguo, e, portanto no possvel ter acesso direto aos elementos da lista. Isto implica que, para percorrer todos os elementos da lista, devemos explicitamente seguir o encadeamento dos elementos. Para isto, preciso definir a estrutura do n, como uma estrutu-ra auto-referenciada contendo, alm do contedo de informao, um pontei-ro ao prximo elemento na sequncia. As definies a seguir implementam a estrutura correspondente.

    typedef struct node *no _ ptr;

    struct node { tipo _ base elemento; no _ ptr prox; };

    Finalmente a lista definida como um ponteiro ao primeiro n da lis-ta, a partir do qual a sequncia de ns encadeada.

    typedef no _ PTR Lista;

    Uma lista um tipo de dado que estrutura elementos cujo tipo pode ser arbitrariamente complexo, envolvendo inclusive, a utilizao de outros TADs. A definio a seguir especifica o tipo base dos elementos da lista como inteiros.

    typedef int tipo _ base;

    A implementao de listas com ponteiros requer um cuidado especial uma vez que qualquer erro na manipulao dos ponteiros pode acarretar em perda parcial ou total da lista de elementos. Assim sendo, a utilizao de um ponteiro auxiliar para o percurso ao longo da lista pode ser de grande utilida-de. Com esse objetivo definimos um tipo Corrente, a ser utilizado como cpia da lista de forma a possibilitar a sua manipulao com segurana.

    typedef no _ ptr Corrente;

  • 48 ESTRUTURA DE DADOS

    A funo que cria uma lista vazia utilizando alocao dinmica de memria apresentada a seguir. A funo tem como valor de retorno a lista vazia inicializada, isto , o valor de retorno NULL, pois no existem ele-mentos na lista.

    Lista Criar (){ return (NULL);}

    O mtodo Inicializar posiciona o ndice da posio corrente no inicio da lista. Desta forma o ponteiro pos aponta para o mesmo local onde se encontra o primeiro elemento da lista. Este mtodo til quando a lista precisa ser percorrida desde o inicio.

    corrente Inicializar (Lista L){ corrente pos = L; return (pos);}

    O deslocamento de um elemento para o seguinte na lista dado pelo percurso ao longo dos ponteiros, onde, a partir do n atual, a funo a se-guir retorna o ponteiro onde se localiza o prximo elemento na lista.

    corrente proximoElemento (corrente p) { return (p -> prox);}

    A procura por um contedo na lista realizada atravs da funo Busca. Esta funo percorre a lista desde o inicio, enquanto o elemento no for encontrado. A funo retorna a posio do elemento na lista em caso de sucesso na procura, ou NULL se o elemento no for encontrado.

    corrente Busca (Lista L, tipo _ base x) { corrente p = L; while ((p != NULL) && (p -> elemento != x)) p = p -> prox; return p;}

    O acesso s informaes contidas nos ns da lista realizado atravs da funo Consulta, que retorna o contedo de informao armazenado no n referenciado pelo ponteiro corrente.

    tipo _ base Consulta (Lista L, corrente p){ if (p != NULL) return (p -> elemento); }

  • 49ESTRUTURA DE DADOS

    A remoo de um elemento da lista envolve a anlise de duas situa-es: a remoo do primeiro n da lista ou de um n em outra posio qual-quer, sem ser no inicio da lista. A seguir, o processo de remoo em cada caso ilustrado.

    No caso da remoo do primeiro elemento da lista necessrio que o ponteiro lista seja atualizado, indicando o novo primeiro elemento.

    No caso de remoo de um elemento, sem ser o primeiro, o processo consiste em fazer um by pass sobre esse elemento atravs do ajuste correto dos ponteiros, para posteriormente liberar a memria correspondente. Para efetivar a remoo preciso o n anterior ao n a ser removido, que pode ser obtido a partir da funo auxiliar Anterior.

    A funo Remover apresentada a seguir, remove o elemento corres-pondente a uma determinada posio pos, passada por parmetro. Esta posio pode ser resultado de um processo de busca, a partir de um deter-minado contedo. Em ambos os casos preciso liberar a memria corres-pondente ao n removido.

    A seguir apresentado o algoritmo que implementa o processo de re-moo.

    Algoritmo0.2

    A funo inerior retorna o n anterior ao n passado (pos). Ela percor-re a toda a lista verificando se o prximo elemento o elemento pos.

    corrente Anterior(Lista l, corrente pos){ corrente ant = null;

    if(pos!= l){ corrente atual = l; while(atual != null & atual -> prox != pos){ atual = atual -> prox; } ant = atual }

    return (1); }

  • 50 ESTRUTURA DE DADOS

    Na remoo encontramos duas situaes: 1) quando o elemento a ser remo-vido a primeira posio (N anterior null); e 2) quando o elemento a ser removido no o elemento da primeira posio (N anterior no null).

    int Remover(Lista l, corrente pos){ corrente noAnterior = Anterior (L,pos);

    corrente tmp _ cell = pos;

    if(noAnterior == NULL){ l = l -> prox; }else{ noAnterior -> prox = pos -> prox; } free (tmp _ cell);

    return (1); }

    O algoritmo apresentado para a funo remover utiliza uma funo an-terior. Esta funo anterior tem o papel de retornar o elemento que antecede a posio atual. Tendo em vista que para o elemento atual ser removido, basta ligar o elemento anterior ao prximo. A seguir a funo anterior apresentada:

    corrente Anterior(Lista l, corrente pos){; corrente ant = null;

    if(pos != NULL){ corrente atual = l; while(atual != null & atual ->prox !=pos){ atual = atual -> prox; } ant = atual;

    return (ant); }

  • 51ESTRUTURA DE DADOS

    A insero em uma lista pode acontecer em qualquer posio, que pode ser no inicio, no final ou qualquer outra posio no meio da lista. O contedo do parmetro pos representa a posio de insero requerida para o elemento novo a ser inserido, no caso de insero na cabea da lista pos null. Neste caso, o ponteiro L que apontava ao primeiro elemento da lista aponta agora para o novo elemento inserido.

    Para qualquer outro valor de pos, o processo de insero acontece como ilustrado a seguir. A lista precisa ser percorrida at a posio de in-sero requerida. Nesse ponto, o novo elemento ser inserido atualizando os ponteiros correspondentes.

    int Inserir (Lista l, tipo _ base dado, corrente pos){ int i; Corrente atual = Inicializar (l); Lista novo = (Lista) malloc(sizeof(struct node)); novo -> elemento = dado; if (pos == NULL){ novo -> prox = l; l = novo; } else { while (atual -> next != NULL) and (atual -> next != pos) atual = atual -> prox; novo -> prox = atual -> prox; atual -> prox = novo; } else return (1);}

    A funo Vazia utilizada para verificar se a lista possui ou no ele-mentos armazenados. A verificao consiste em checar se o ponteiro ao primeiro elemento null.

  • 52 ESTRUTURA DE DADOS

    int Vazia (Lista L) { if (L == NULL) return (1) else return (0);}A funo ultimoElemento utilizada para verificar o final da lista.

    Esta checagem consiste em determinar se o prximo elemento que segue ao atual NULL.

    int ultimoElemento (corrente p) { if (p -> prox == NULL) return (1) return (0);}

    Com exceo de Busca e Anterior todas as operaes consomem tempo O(1). Isto por que somente um nmero fixo de instrues executado sem levar em conta o tamanho da lista. Para Busca e Anterior o custo O(n), pois a lista inteira pode precisar ser percorrida se o elemento no se encontra ou for o ltimo da lista.

    Utilizando o TAD Lista definido nesta unidade, implemente como apli-cao um programa que, dadas duas listas A e B, crie uma terceira lista L intercalando os elementos das duas listas A e B.

    Lista Intercala (Lista A , Lista B) { Lista L = cria (); corrente pos _ L = Inicializar (L); corrente pos _ A = Inicializar (A); corrente pos _ B = Inicializar (B);

    /*Assumimos que A e A tem o mesmo tamanho while (not ultimoElemento(pos _ A)) && (not ultimoElemento (pos _ B)){ Inserir (L, Consulta (pos _ A), null); pos _ A = proximoElemento (pos _ A); Inserir (L, Consulta (pos _ B), null); pos _ B = proximoElemento (pos _ B); } return(L);}

  • 53ESTRUTURA DE DADOS

    Considerando a implementao do TAD utilizando alocao dinmica de memria resolva as questes a seguir:

    1. Implemente a operao que retorna a quantidade de elementos na lista, cujo cabealho : int Tamanho (Lista l). Determine a complexidade da operao implementada.

    2. Implemente uma rotina para a remoo de uma lista desalocando a me-mria utilizada. O cabealho da rotina void Remove_list (Lista L).

    3. Implemente a rotina auxiliar chamada anterior usada na remoo, de acordo com o seguinte cabealho corrente anterior (Lista L, corrente pos). Esta rotina retorna a posio do elemento anterior a uma outra posio pos. Se o elemento no for encontrado retorna NULL.

    4. Utilizando as operaes definidas na interface do TAD Lista implemente um mtodo que dadas duas listas L1 e L2, calcule L1 L2 (unio) e L1 L2 (interseo). O resultado das operaes deve ser retornado em uma terceira lista L3.

    5. Utilizando as operaes definidas na interface do TAD Lista implemente um mtodo que dada uma lista retorne uma segunda lista onde os ele-mentos pertencentes primeira estejam ordenados em forma crescente. Determine a complexidade do seu algoritmo.

    6. Escreva um programa que, utilizando o TAD Lista, faa o seguinte:a) Crie quatro listas (L1, L2, L3 e L4);

    b) Insira sequencialmente, na lista L1, 10 nmeros inteiros obtidos de forma randmica (entre 0 e 99);

    c) Idem para a lista L2;

    d) Concatene as listas L1 e L2, armazenando o resultado na lista L3;

    e) Armazene na lista L4 os elementos da lista L3 (na ordem inversa);

    f) Exiba o contedo das listas L1, L2, L3 e L4.

    Variaes sobre o TAD Lista

    Lista ordenada

    Uma lista ordenada uma lista onde seus elementos componentes so organi-zados de acordo a um critrio de ordenao com base em um campo chave. A ordem estabelecida determina que a insero de um determinado elemento na lista ir a acontecer no lugar correto. A lista pode ser ordenada de forma crescente ou decres-cente.

  • 54 ESTRUTURA DE DADOS

    A partir da existncia de um critrio de ordenao na lista, a funo responsvel pela busca por um determinado contedo na lista pode ser adaptado de forma a tornar a busca mais eficiente.

    Lista circular

    A conveno consiste em manter a ltima clula apontando para a primeira. Desta forma, o teste por fim de lista nunca satisfeito. Com isso, precisa ser estabe-lecido um critrio de parada de forma a evitar que o percurso na lista no encontre nunca o fim. Uma forma padro estabelecido com base no nmero de elementos existentes na lista.

    Lista duplamente encadeada

    Em alguns casos pode ser conveniente o percurso da lista de trs para frente atravs da adio de um atributo extra na estrutura de dados, contendo um ponteiro para a clula anterior. Esta mudana na estrutura fsica acarreta um custo extra no espao requerido e tambm aumenta o trabalho requerido nas inseres e remoes, uma vez que existem mais ponteiros a serem ajustados. Por outro lado simplifica a remoo, pois no mais precisamos procurar a clula anterior (O(n)), uma vez que esta pode ser acessada diretamente atravs do ponteiro correspondente.

  • 55ESTRUTURA DE DADOS

    Captulo 2Pilhas

    Em geral, as operaes de insero e remoo realizadas sobre lis-tas so custosas. No caso da implementao utilizando alocao de mem-ria esttica, estas operaes acarretam a movimentao dos elementos. No caso da alocao dinmica o deslocamento at a posio correta de insero ou remoo envolve o percurso ao longo do encadeamento pelos elemen-tos. Em ambos os casos, o custo destas operaes O(n). Estas situaes desfavorveis podem ser contornadas se os elementos a serem inseridos e removidos se encontram em posies determinadas especialmente, como a primeira ou ltima posio.

    Uma Pilha uma lista com a restrio de que inseres e remoes so executadas exclusivamente em uma posio, referenciada como fim ou topo.

    Pilhas so conhecidas como estruturas LIFO do ingls Last In First Out ou ltimo que entra primeiro que sai. Em uma Pilha o nico elemento acessvel o elemento que se encontra no topo. Consequentemente, a ope-rao de busca ao longo da estrutura, por exemplo, no uma operao aplicvel para esta estrutura de dados. Graficamente, uma pilha pode ser representada da seguinte forma:

    O funcionamento de uma pilha pode ser facilmente interpretado a partir de uma analogia simples com uma pilha de livros pesados. Livros so empilhados, um encima de outro, sendo que o ltimo livro empilhado o que fica no topo da pilha, e, portanto o nico visvel e que pode ser con-sultado sem precisar movimentar outros exemplares. Por se tratar de livros pesados, o acesso a um livro determinado na pilha, requer que os livros

  • 56 ESTRUTURA DE DADOS

    encima deste sejam retirados, um a um, a partir do topo. Desta forma, o ltimo livro empilhado ser o primeiro a ser retirado da pilha. A partir des-ta descrio, o funcionamento da pilha pode ser modelado de acordo com a seguinte interface.

    Interface do TAD Pilha

    // Cria uma pilha vaziaPilha Criar ();

    //insere um novo elemento no topo da pilha. int Push (Pilha p, tipo _ base dado);

    // consulta pelo elemento que se encontra no topotipo _ base Top (Pilha p);

    //Remove e retorna o elemento do topotipo _ base Pop (Pilha p);

    /* Retorna 1 se no tem mais elementos na pilha, ou 0 em caso contrario.*/

    int Vazia (Pilha p)

    // Retorna 1 se a pilha estiver cheia, e 0 em caso contrario.int Cheia (Pilha p);

    // Retorna a quantidade de elementos na pilha.int Tamanho (Pilha p); Pilhas so listas, portanto as abordagens de implementao utiliza-

    das para listas so vlidas para o caso da implementao de pilhas. Consi-derando que a implementao de pilhas representa uma variao de listas, boa parte da especificao e implementao de listas pode ser aproveitada.

    Implementaes do TAD Pilha usando vetoresLevando em conta as restries inerentes prpria estrutura de da-

    dos e as restries na manipulao da pilha, a estrutura projetada para a implementao de listas modificada, adicionando mais um campo de informao referente localizao do elemento que se encontra no topo da pilha. Esta informao indispensvel na implementao das operaes de insero e remoo, de forma a possibilitar o acesso direto ao local. Com essa pequena alterao na estrutura de dados as operaes passam a de-mandar tempo constante.

    O fato de mquinas mo-dernas possurem opera-es sobre pilhas como parte do conjunto de ins-trues, faz desta estru-tura uma abstrao fun-damental na Cincia da Computao, depois do vetor.

  • 57ESTRUTURA DE DADOS

    typedef int tipo _ base#define tamanho;

    struct estrutura _ Pilha { int topo; tipo _ base elementos [tamanho]; };

    typedef struct estrutura _ Pilha *Pilha;

    Em C uma pilha definida como um ponteiro estrutura, que pas-sado por valor para as funes que iro modificar o contedo da pilha.

    Dada a semelhana com a estrutura de dados utilizada para o caso de listas, o mtodo Criar se mantem essencialmente o mesmo, adicionando somente a sentena de inicializao do campo topo para a primeira posio.

    Algoritmo1

    Pilha Criar(){ Pilha p = (Pilha) Malloc (sizeof(estrutura _ Pilha)); if(p != NULL){ p -> topo = -1; return (p); } else printf ("No existe memria suficiente"); return;

    }

    Na escolha pela extremidade do vetor a ser definida como o topo onde a insero e remoo de elementos estar acontecendo, importante ana-lisar os custos decorrentes desta escolha. Como discutido anteriormente, a propriedade de alocao contgua de memria traz como desvantagem a ne-cessidade de movimentaes dos elementos ao longo da estrutura de dados. Nesse caso, se o topo for escolhido como a primeira posio do vetor, o custo da insero e remoo seria de O(n). Em contrapartida, a definio de topo como sendo o ltimo elemento inserido mais conveniente uma vez que este pode ser acessado em tempo constante a partir do tamanho da estrutura.

    Algoritmo2