03.01.2015 Views

ESTRUTURAS DE DADOS (LEI, LM, LEE) PROGRAMAÇÃO III (LTSI)

ESTRUTURAS DE DADOS (LEI, LM, LEE) PROGRAMAÇÃO III (LTSI)

ESTRUTURAS DE DADOS (LEI, LM, LEE) PROGRAMAÇÃO III (LTSI)

SHOW MORE
SHOW LESS

You also want an ePaper? Increase the reach of your titles

YUMPU automatically turns print PDFs into web optimized ePapers that Google loves.

<strong>ESTRUTURAS</strong> <strong>DE</strong> <strong>DADOS</strong> (<strong>LEI</strong>, <strong>LM</strong>, <strong>LEE</strong>)<br />

<strong>PROGRAMAÇÃO</strong> <strong>III</strong> (<strong>LTSI</strong>)<br />

Universidade da Beira Interior, Departamento de Informática<br />

Hugo Pedro Proença, 2012/2013


Pilhas e Filas<br />

<br />

Dois dos tipos de objectos mais comummente usados em algoritmos<br />

computacionais são as “Pilhas” e “Filas”.<br />

<br />

Ambos são especificações de um tipo mais genérico: as listas<br />

ligadas.<br />

Normalmente, uma lista ligada tem associado um critério de ordenação<br />

que determina a posição dos elementos na lista.<br />

A posição de cada elemento nas “Pilhas” e “Filas” é determinada pela<br />

ordem pela qual os elementos são introduzidos no conjunto.<br />

Ao remover um elemento de uma lista, é comum eliminar todos os<br />

recursos a ele associados.<br />

Ao contrário, nas “Pilhas” e “Filas” é usual a remoção do elemento do<br />

conjunto para posterior colocação noutro, não se efectuado por esta<br />

razão a libertação de recursos.


Fila (Queue)<br />

Uma fila é uma lista ordenada em que…<br />

Todas as inserções se fazem na última posição da lista.<br />

Todas as remoções se fazem da primeira posição da lista.<br />

O primeiro elemento a ser inserido na fila é também o<br />

primeiro a ser processado.<br />

São também designadas de “FIFO” (First In, First Out).<br />

Na ciência computacional são tipicamente utilizadas<br />

para manter informação temporariamente para futuro<br />

processamento.<br />

Neste contexto, as filas desempenham funções de buffers.


Pilhas (Stack)<br />

Uma pilha é uma lista ordenada em que…<br />

Todas as inserções se fazem na primeira posição da lista.<br />

Todas as remoções se fazem da primeira posição da lista.<br />

O último elemento a ser inserido na pilha é o primeiro<br />

a ser processado.<br />

São também designadas por LIFO (Last In, First Out).<br />

São bastante utilizadas em sistemas computacionais.<br />

Por exemplo, os sisteams operativos implementam-nas para<br />

o tratamento de “interrupts” e chamadas ao sistema.


Fila: inserção (Função push())<br />

Exemplo:<br />

F 1 3 5 8<br />

nv 4


Pilha: inserção (Função push())<br />

Exemplo:<br />

P<br />

F 1 3 5 8<br />

el


Fila e Pilha: remoção (Função pop())<br />

Exemplo:<br />

F 1 3 5 8<br />

nv 4


Pilhas e Filas: remoção<br />

Tal como atrás referido, uma das (poucas)<br />

diferenças fundamentais das Pilas e Filas<br />

relativamente ao tipo mais geral (Listas) é a não<br />

eliminação dos nós removidos.<br />

Ao invés, é devolvido um apontador para o elemento<br />

que acabou de ser retirado do conjunto.<br />

Desta forma, é possível a transição de um elemento<br />

entre vários conjuntos (Pilhas ou Filas).


Pilhas e Filas: remoção<br />

Esta característica conduz a algumas alterações ao<br />

nível da implementação da função pop().<br />

Por um lado, a cabeça da Pilha || Fila é alterada,<br />

pelo que tem que ser sempre devolvido um apontador<br />

para a posição actualizada.<br />

Por outro, desejamos receber um apontador para o<br />

elemento removido da Pilha||Fila.<br />

As funções não permitem o retorno de mais que uma<br />

variável…


Pilhas e Filas: remoção<br />

A solução está na passagem de parãmetros por<br />

referência.<br />

Alguns dos parãmetros que utilizamos na chamada à<br />

função pop() são “bi-direccionais”.<br />

Parãmetros de entrada e de saída.<br />

Protótipo:<br />

Nodo* pop(Nodo **p);<br />

Apontador para o elemento removido<br />

Cabeça da pilha||fila actualizada


Duplo **<br />

<br />

<br />

<br />

Tome-se o seguinte exemplo:<br />

void funcao(int a){<br />

a=a+1; //”variável” alterada na função<br />

}<br />

Ao chamar a função, por exemplo a partir do<br />

main(){<br />

int b=5;<br />

funcao(b);<br />

printf(“B=%d\n”,b);<br />

}<br />

Como é óbvio, uma vez que um parãmetro é uma cópia de uma<br />

variável, o valor de “b” não é alterado na função “main”.


Duplo **<br />

<br />

<br />

Repare-se agora na semelhança deste exemplo com a situação anterior<br />

Nodo* funcao(Nodo *a){ //cabeça da pilha||fila<br />

}<br />

Nodo *el=a;<br />

if (a==NULL)<br />

return(NULL);<br />

a=a->nseg; //o apontador “a” é alterado<br />

el->nseg=NULL;<br />

return(el);<br />

Ao chamar a função, por exemplo a partir do<br />

main(){<br />

}<br />

Nodo *b=NULL, *el;<br />

…<br />

el=funcao(b);<br />

…<br />

“el” fica com o valor de retorno da função. Tudo OK!<br />

<br />

Mas “b” (apontador para a cabeça da lista), não é alterado na função main(), pelo que manteria o<br />

mesmo valor, isto é, apontaría para o mesmo local.


Duplo **<br />

Exemplo 2: Atente-se na seguinte situação, onde o problema<br />

anterior não se verifica.<br />

void funcao(Nodo *a){<br />

a->chave=10;<br />

}<br />

Ao chamar a função a partir do main(), o valor do atributo<br />

chave sería alterado.<br />

main(){<br />

}<br />

Nodo *b=NULL;<br />

…<br />

funcao(b);<br />

…<br />

A única cópia existente é a do apontador “b”


Duplo **<br />

Solução: O parãmetro passado na função “pop” é o de<br />

“um apontador para um apontador para um nodo”.<br />

Assim sendo, as alterações efectuadas no “apontador para<br />

nodo” têm repercurssões fora da função.<br />

Nodo* pop(Nodo **a){<br />

Nodo *el=*a;<br />

if (*a==NULL)<br />

return(NULL);<br />

(*a)=(*a)->nseg;<br />

el->nseg=NULL;<br />

}<br />

return(el);<br />

Parãmetro<br />

Passível de alteração<br />

a<br />

Nodo


Duplo **<br />

Para chamar a função pop(), deveremos ter em<br />

atenção esta mudança no tipo de parãmetro.<br />

main(){<br />

}<br />

Nodo *p=NULL, *el;<br />

… //Inserções, …<br />

el=pop(&p);<br />

…<br />

O endereço de “p” aponta para um local que<br />

aponta para um nodo.<br />

Necessita de ser libertado, ou colocado noutra pilha||fila


Pilhas: push()<br />

O prótótipo para a função de inserção em pilhas é<br />

semelhante à função “insereInicio()” das listas<br />

ligadas:<br />

Nodo* push(Nodo *p, Nodo *nv){<br />

if (p==NULL)<br />

return(nv);<br />

nv->nseg=p;<br />

return(nv);<br />

}


Filas: push()<br />

O prótótipo para a função de inserção em filas é<br />

semelhante à função “insereFim()” das listas ligadas:<br />

Nodo* push(Nodo *f, Nodo *nv){<br />

Nodo *aux=f;<br />

if (aux==NULL)<br />

return(nv);<br />

while(aux->nseg!=NULL)<br />

aux=aux->nseg;<br />

aux->nseg=nv;<br />

return(f);<br />

}


Pilhas e Filas: Exercício<br />

Implemente o comportamento previsto para a<br />

inserção numa fila apenas usando pilhas.<br />

Protótipo: Nodo* pushfila(Nodo*f, Nodo *nv);<br />

Inversamente, implemente o comportamento<br />

previsto para uma pilha apenas usando filas.<br />

Protótipo: Nodo* pushPilha(Nodo*p, Nodo *nv);

Hooray! Your file is uploaded and ready to be published.

Saved successfully!

Ooh no, something went wrong!