• Nenhum resultado encontrado

Quarta Lista de Exercícios Implementação e uso de Listas Encadeadas

N/A
N/A
Protected

Academic year: 2021

Share "Quarta Lista de Exercícios Implementação e uso de Listas Encadeadas"

Copied!
7
0
0

Texto

(1)

Pr´atica de Algoritmos e Estrutura de Dados I – DIM0426 Umberto Souza da Costa - 25/09/2007

– Quarta Lista de Exerc´ıcios – Implementa¸c˜ao e uso de Listas Encadeadas

1

Objetivos

O objetivo desta aula ´e estudar as estruturas de dados lista encadeada simples e listas circulares enfatizando sua implementa¸c˜ao atrav´es de classes em C++. Nesta aula tam-b´em exploraremos uma aplica¸c˜ao que deve fazer uso de uma lista circular em sua solu¸c˜ao. Espera-se que assim seja poss´ıvel perceber a importˆancia das listas encadeadas como es-trutura de dados na solu¸c˜ao de problemas pr´aticos. A lista encadeada pode ser utilizada, por exemplo, na implementa¸c˜ao de pilhas, filas e deques, bem como na solu¸c˜ao de alguns problemas computacionais que requerem tal estrutura. Os programas e documenta¸c˜oes devem entregues at´e a pr´oxima aula pr´atica (veja a Se¸c˜ao 4 para mais detalhes).

2

Parte I: Conceitos de Lista Encadeada Simples

Uma Lista Encadeada ´e uma estrutura de dados do tipo container, ou seja, serve para armazenar elementos em uma certa ordem. A lista encadeada oferece opera¸c˜oes de acesso geral, tais como inser¸c˜ao, remo¸c˜ao e busca arbitr´aria. Uma das caracter´ısticas mais impor-tantes de uma lista encadeada ´e seu car´ater dinˆamico, que permite armazenar um n´umero de elementos limitado apenas pela mem´oria dispon´ıvel.

Uma lista encadeada consiste de uma seq¨uˆencia linear de n´os dinamicamente alocados, que s˜ao encadeados (ou conectados) atrav´es de ponteiros (ou apontadores). Vers˜oes mais elaboradas de listas encadeadas utilizam n´os com ponteiros para os n´os sucessor e ante-cessor (listas duplamente encadeadas) e outras fazem com que o ´ultimo n´o aponte para o primeiro (listas circulares).

No nosso caso come¸caremos trabalhando com lista encadeada simples, cujo ´ultimo n´o aponta para NULL (aterramento). Uma estrutura b´asica representando essa id´eia ´e demonstrada abaixo: template< typename T > class ListNode { public: T elemento; ListNode *prox; };

Para facilitar a implementa¸c˜oes dos algoritmos de manipula¸c˜ao de uma lista encadeada simples, ´e poss´ıvel fazer uso de um n´o especial chamado de n´o-cabe¸ca. Este n´o especial tem as seguintes caracter´ısticas:

(2)

Nunca ´e removido;

N˜ao cont´em valor v´alido. Por´em, em algumas ocasi˜oes pode conter informa¸c˜oes relacionadas a lista, como, por exemplo, a quantidade de n´os ou um ponteiro para o ´

ultimo elemento da lista.

Desta forma o primeiro n´o de uma lista (n´o-cabe¸ca) ´e acess´ıvel via ponteiro especial (ptLista) e a partir dele ´e poss´ıvel realizar o acesso seq¨uˆencial `a todos os elementos da lista. A Figura 1 apresenta alguns exemplos esquem´aticos de listas.

ptLista Nó cabeça ptLista Nó cabeça B A D (a) (b)

Figura 1: Exemplo de (a) uma lista encadeada com n´o-cabe¸ca vazia; e (b) uma lista encadeada simples com n´o-cabe¸ca e 3 elementos, aterrada em D.

2.1 Busca

Uma das opera¸c˜oes mais importantes sobre lista ´e a busca, que consiste em localizar um determinado elemento na lista. A importˆancia da busca deve-se ao fato de que ela ´e invocada pelas opera¸c˜oes de inser¸c˜ao (descrita na Se¸c˜ao 2.2) e remo¸c˜ao (descrita na Se¸c˜ao 2.3).

A opera¸c˜ao de busca consiste em percorrer a lista seq¨uencialmente a partir do primeiro elemento v´alido at´e o final da mesma, procurando pela primeira1 ocorrˆencia do elemento solicitado.

Uma das formas mais comum de implementar a busca consiste fazer com que ela retorne dois apontadores: um para a primeira ocorrˆencia na lista do elemento procurado (pont) e outro para o n´o imediatamente anterior ao elemento procurado (ante). O apontador para o n´o imediatamente anterior ser´a utilizado nas opera¸c˜oes de inser¸c˜ao e remo¸c˜ao. Se o elemento solicitado n˜ao estiver na lista ent˜ao os apontadores pont deve apontar para um valor nulo (i.e. NULL). Um poss´ıvel pseudo-c´odigo para o algoritmo de busca encontra-se no C´odigo 1.

2.2 Inser¸c˜ao

O processo de inser¸c˜ao em uma lista deve ser bem planejado para evitar que a lista se “quebre” ou a inser¸c˜ao seja feita em local inapropriado. A inser¸c˜ao pode ser implementado

de v´arias maneiras, adicionando-se o elemento: ⋆ Ao final da lista;

No in´ıcio da lista;

De forma a preservar uma ordem pr´e-existente da lista;

Logo ap´os um ponteiro que aponta para um n´o v´alido da lista. 1

(3)

C´odigo 1 Pseudo-c´odigo do algoritmo de busca em lista encadeada simples.

01: Fun¸c~ao buscaLES( x: Tipo Chave; Ref ante↑, pont↑: Tipo ListNode ) : Neutro

02: | Var pt↑: ListNode; % pt: ponteiro de percurso

03: | ante := ptLista; % aponta p/ n´o-cabe¸ca

04: | pont := λ; % assumimos que chave n~ao est´a na lista

05: | pt := ptLista↑.prox; % pt aponta para o 1o

n´o v´alido

06: | Enquanto ( pt 6= λ ) Fa¸ca

07: | | Se ( pt ↑.chave < x ) Ent~ao

08: | | | ante := pt; % avan¸ca ponteiro anterior

09: | | | pt := pt↑.prox; % atualiza ponteiro de percurso

10: | | Sen~ao

11: | | | Se ( pt ↑.chave = x ) Ent~ao

12: | | | | pont := pt; % apontar p/ o n´o encontrado

13: | | | Fim-Se

14: | | | pt := λ; % for¸car sa´ıda

15: | | Fim-Se

16: | Fim-Enquanto

17: Fim-Fun¸c~ao.

A ´ultima op¸c˜ao ´e a mais vers´atil, pois basta fazer o ponteiro indicador de inser¸c˜ao apontar para posi¸c˜ao desejada para que a inser¸c˜ao ocorra de forma correta. Neste caso cabe ao programador certificar-se que o ponto de inser¸c˜ao manter´a as caracter´ısticas desejadas da lista (por exemplo, manter os elementos ordenados).

O C´odigo 2 apresenta um pseudo-c´odigo para a inser¸c˜ao feita ap´os um n´o indicando ao algoritmo. Se o elemento a ser inserido j´a estiver na lista o algoritmo n˜ao faz nada. C´odigo 2 Pseudo-c´odigo do algoritmo de inser¸c˜ao em lista encadeada simples.

01: Fun¸c~ao insereLES( x: Tipo Chave; novoValor: Tipo Objeto ) : Neutro

02: | Var pt↑, pont↑, ante↑: ListNode; % ponteiros p/ auxiliar inser¸c~ao

03: | buscaLES( x, ante, pont ); % achar posi¸c~ao para inser¸c~ao

04: | Se ( pont = λ ) Ent~ao % elemento n~ao est´a na lista! inserir...

05: | | Alocar( pt ); % solicitar n´o

06: | | pt↑.info := novoValor; % inicializar n´o

07: | | pt↑.chave := x; % inicializar chave

08: | | pt↑.prox := ante↑.prox; % ligar novo n´o `a lista

09: | | ante↑.prox := pt; % acertar lista

10: | Fim-Se

11: Fim-Fun¸c~ao.

A Figura 2 apresenta o processo de inser¸c˜ao de um novo n´o segundo o algoritmo apresentado no C´odigo 2.

2.3 Remo¸c˜ao

Outra opera¸c˜ao importante em uma lista encadeada ´e a remo¸c˜ao. Para remover um ele-mento ´e preciso, primeiramente, encontrar o eleele-mento que se deseja remover, mantendo um apontador para o mesmo. O procedimento de remo¸c˜ao ´e facilitado se tivermos um apontador para a posi¸c˜ao imediatamente anterior ao n´o que se deseja remover. Uma vez

(4)

ptLista Nó cabeça λ A B D Nó cabeça pt ante λ C λ λ pont = ptLista A B D (a) (b) Nó cabeça pt ante C λ λ pont = ptLista A B D Nó cabeça pt ante C λ λ pont = ptLista A B D (c) (d)

Figura 2: Exemplo de inser¸c˜ao de um novo n´o em uma lista. Em (a) temos a lista encadeada original; (b) o novo n´o (conte´udo C) ´e alocado e apontado por pt; (c) o novo n´o ´e ligado a lista atrav´es de seu campo prox, e; (d) o encadeamento da lista ´e ajustado de forma a incluir o novo n´o.

que os dois apontadores – para o n´o a ser removido e para o n´o antecessor imediato – est˜ao configurados a remo¸c˜ao pode ser efetuada de forma simples. Portanto, mais uma vez o procedimento de busca deve ser utilizado.

O processo supracitado ´e ilustrado pela Figura 3 e o algoritmo correspondente pode ser encontrado no C´odigo 3.

C´odigo 3 Pseudo-c´odigo do algoritmo de remo¸c˜ao em lista encadeada simples.

01: Fun¸c~ao removeLES( x: Tipo Chave ) : Tipo Objeto

02: | Var pont↑, ante↑: ListNode; % ponteiros p/ auxiliar remo¸c~ao

03: | valRecuperado: Tipo Objeto;

04: | buscaLES( x, ante, pont ); % achar posi¸c~ao para remo¸c~ao

05: | Se ( pont 6= λ ) Ent~ao % elemento foi encontrado! remover...

06: | | ante↑.prox := pont↑.prox; % bypass : acertar lista...

07: | | valRecuperado := pont↑.info; % guardar valor do n´o removido

08: | | Liberar( pont ); % liberar mem´oria do n´o

09: | Fim-Se

10: | Retornar valRecuperado;

11: Fim-Fun¸c~ao.

2.4 Tarefas

Implementar um lista encadeada simples com n´o cabe¸ca para armazenar um caractere, cuja chave deve ser o pr´oprio caractere. Esta lista deve ser mantida em ordem n˜ao decrescente de acordo com a chave.

(5)

B A C D λ Nó cabeça ptLista A C D λ Nó cabeça ante pont ptLista B (a) (b) A C D λ ante pont Nó cabeça ptLista B A C D λ ante pont Nó cabeça ptLista (c) (d)

Figura 3: Exemplo de remo¸c˜ao de um n´o de uma lista. Em (a) temos a lista encadeada original; (b) o novo a ser removido, apontado por pont, ´e localizado (conte´udo B) e seu antecessor ´e apontado por ante; (c) o novo a ser removido ´e contornado (bypass), e; (d) o n´o marcado ´e desalocado.

Neste exerc´ıcio vocˆe n˜ao deve se preocupar em criar uma classe completa (com constru-tor, destruconstru-tor, etc.), mais uma classe simples (`a semelhan¸ca de um struct), com membros p´ublicos, conforme exemplo fornecido na Se¸c˜ao 2. Construa um programa de teste que, atrav´es de um menu de op¸c˜oes, permita a realiza¸c˜ao de inser¸c˜ao, busca, remo¸c˜aoe listagem de elementos da lista.

Por´em, antes de come¸car vocˆe deve tomar uma decis˜ao de implementa¸c˜ao: tratar a lista (ptLista) como uma vari´avel global acess´ıvel dentro das v´arias fun¸c˜oes de manipula¸c˜ao; ou tratar a lista como uma vari´avel local (ao main) e passar o ponteiro para lista como um argumento para cada uma das fun¸c˜oes listadas na Se¸c˜ao 2.

3

Parte II: Lista Circular e o Problema de Josephus

3.1 Listas Circulares

Uma lista encadeada simples com n´o cabe¸ca ´e uma estrutura de dados importante e pode ter seu desempenho melhorado ainda mais. Isso pode ser feito tornando-a circular, ou seja, ligando-se o ´ultimo elemento da lista ao primeiro, conforme ilustrado na Figura 4. Com esse configura¸c˜ao ´e poss´ıvel percorrer uma lista continuamente, passando por todos os elementos e retornando ao primeiro naturalmente.

Assim sua tarefa inicial consiste em criar a classe circular de forma que ela realize um percorrimento circular sobre uma lista. Tente usar o m´aximo do princ´ıpio de heran¸ca, ou seja, n˜ao ´e necess´ario alterar a classe pai e a classe filha deve sobrecarregar apenas os m´etodos que tiverem comportamento diferentes da classe original. Sinta-se livre para criar novos m´etodos se necess´ario.

Na pr´oxima se¸c˜ao ser´a descrito um problema cuja solu¸c˜ao naturalmente requer o uso de uma lista circular.

3.2 O Problema de Josephus (Roleta Romana)

Um grupo de S soldados ´e circundado por uma for¸ca inimiga esmagadora. N˜ao h´a espe-ran¸ca de vit´oria e s´o existe um cavalo dispon´ıvel para escapar! Os soldados entram num

(6)

A

B

C

D

Head

<null>

Figura 4: Exemplo de uma lista encadeada simples circular com 4 elementos.

acordo para determinar qual deles dever´a escapar para trazer ajuda. Para tanto eles ir˜ao utilizar o m´etodo da roleta romana para selecionar o soldado sortudo. O procedimento funciona da seguinte forma:

1. Os soldados se disp˜oe em c´ırculo formando uma certa seq¨uˆencia fixa.

2. Um n´umero n > 0 ´e sorteado. Um dos nomes dos soldados tamb´em ´e sorteado. 3. Come¸cando-se pelo soldado cujo nome foi sorteado, eles iniciam uma contagem

seq¨uencial ao longo do c´ırculo em sentido hor´ario. Quando a contagem alcan¸ca n-´esimo soldado, ele ´e retirado do c´ırculo (e portanto eliminado da escolha) e a contagem reinicia com o soldado seguinte.

4. O processo continua de maneira que, toda vez que n ´e alcan¸cado, outro soldado ´e eliminado do c´ırculo (lembre-se, todo soldado retirado do c´ırculo n˜ao entra mais na contagem).

5. O ´ultimo soldado que restar dever´a montar o cavalo e escapar.

Considerando como entrada um n´umero n > 0, a ordem dos soldados no c´ırculo e o soldado a partir do qual a contagem se inicia, o objetivo ´e determinar a seq¨uˆencia na qual os soldados s˜ao eliminados do c´ırculo e o soldado que escapar´a.

A entrada para o programa ´e uma seq¨uˆencia de nomes dos soldados at´e que <CTRL>+d seja pressionado. A ordem de entrada dos nomes corresponde a ordem da lista, ou seja, o primeiro nome fornecido ´e o primeiro da lista, o segundo nome informado ser´a o segundo da lista e assim por diante. O programa deve imprimir os nomes na seq¨uˆencia em que os mesmos s˜ao eliminados e o nome do soldado que escapar´a.

Por exemplo, suponha que n = 3 e que existam 5 soldados romanos chamados A, B, C, D e E. Digamos que A tenha sido o nome sorteado para iniciar o procedimento de elimina¸c˜ao. Ent˜ao contamos trˆes soldados a partir de A de forma que C ´e o soldado eliminado. Em seguida, come¸camos em D e contamos D, E e novamente A, para que A seja eliminado a seguir. Depois B, D e E ´e o eliminado da vez; e finalmente, B, D e B, de modo que D seja o soldado a escapar. Veja logo abaixo como poderia ser e interface do programa executado para o exemplo acima:

$ ./josephus

Entre com n (> 0): 3

(7)

A B C D E

---Ordem de sa´ıda:

C A E B Soldado sorteado: D 3.3 Tarefas

1. Crie a classe que implemente o percorrimento circular sobre uma lista. Sinta-se livre para criar novos m´etodos se necess´ario.

2. Utilize a lista encadeada circular do exerc´ıcio anterior para resolver o problema de Josephus descrito na Se¸c˜ao 3.2.

4

Implementa¸c˜

ao, Entrega e Avalia¸c˜

ao

O trabalho deve ser desenvolvido em duplas, tentando, dentro do poss´ıvel, dividir as tarefas igualmente entre os componentes. Por´em os componentes devem ser capazes de explicar qualquer trecho de c´odigo do programa, mesmo que o c´odigo tenha sido desenvolvido pelo outro membro da equipe.

Os programas completos e sua documenta¸c˜ao html (gerada pelo Doxygen) devem ser entregues at´e a pr´oxima aula pr´atica, sem erros de compila¸c˜ao e testado. A en-trega da c´odigos-fonte e seus arquivos relacionados dever´a ser feita por email para um-berto@dimap.ufrn.br, at´e o prazo indicado. O n˜ao cumprimento das instru¸c˜oes de envio poder´a acarretar em penaliza¸c˜ao por pontos.

Referências

Documentos relacionados

Crisóstomo (2001) apresenta elementos que devem ser considerados em relação a esta decisão. Ao adquirir soluções externas, usualmente, a equipe da empresa ainda tem um árduo

A placa EXPRECIUM-II possui duas entradas de linhas telefônicas, uma entrada para uma bateria externa de 12 Volt DC e uma saída paralela para uma impressora escrava da placa, para

No entanto, maiores lucros com publicidade e um crescimento no uso da plataforma em smartphones e tablets não serão suficientes para o mercado se a maior rede social do mundo

utilizada, pois no trabalho de Diacenco (2010) foi utilizada a Teoria da Deformação Cisalhante de Alta Order (HSDT) e, neste trabalho utilizou-se a Teoria da

Os principais objectivos definidos foram a observação e realização dos procedimentos nas diferentes vertentes de atividade do cirurgião, aplicação correta da terminologia cirúrgica,

O relatório encontra-se dividido em 4 secções: a introdução, onde são explicitados os objetivos gerais; o corpo de trabalho, que consiste numa descrição sumária das

psicológicos, sociais e ambientais. Assim podemos observar que é de extrema importância a QV e a PS andarem juntas, pois não adianta ter uma meta de promoção de saúde se

libras ou pedagogia com especialização e proficiência em libras 40h 3 Imediato 0821FLET03 FLET Curso de Letras - Língua e Literatura Portuguesa. Estudos literários