Prof. Hilton Cardoso Marins Junior [email protected] LISTAS LINEARES COM DISCIPLINA DE ACESSO.
Transcript of Prof. Hilton Cardoso Marins Junior [email protected] LISTAS LINEARES COM DISCIPLINA DE ACESSO.
Prof. Hilton Cardoso Marins [email protected]
LISTAS LINEARES COM DISCIPLINA DE ACESSO
Listas Lineares com Disciplina de Acesso
Em algumas aplicações pode aparecer um critério que determina como será a inserção e remoção dos elementos que compõem um conjunto de dados. Veremos a seguir dois critérios:
LIFO (Last In First Out): Último elemento inserido será o primeiro a ser removido. Estruturas Lineares com essa disciplina de acesso são denominadas PILHAS pois pode-se fazer um analogia com uma pilha de livros, onde um novo livro sempre será colocado no topo da pilha e só podemos retirar o livro do topo da pilha, caso contrário ela desabaria.
FIFO (First In First Out): Primeiro elemento inserido será o primeiro a ser removido. Estruturas Linears com essa disciplina de acesso são denominadas FILAS pois pode-se fazer uma analogia com o funcionamento de uma fila de pessoas em um guichê, onde as pessoas são atendidas na ordem em que chegaram.
Pilha
A utilização do critério LIFO torna a Pilha uma ferramenta ideal para processamento de estruturas aninhadas de profundidade imprevisível, situação em que é necessário garantir que subestruturas mais internas sejam processadas antes das estruturas que as contenham.
Vejamos a seguir alguns exemplos de estruturas aninhadas:
Quando é necessário caminhar em um conjunto de dados e guardar uma lista de coisas a fazer posteriormente (Editor de Textos – desfazer digitação).
Controle de sequências de chamadas de subprogramas e algoritmos recursivos. Veremos detalhadamente quando analisarmos a pilha de
execução de um programa.
Sintaxe de expressões aritméticas.
Exemplo: Calculadora
Um bom exemplo de aplicação de pilha é o funcionamento das calculadoras da HP (Hewlett-Packard). Elas trabalham com expressões pós-fixadas, então para avaliarmos uma expressão como (1-2)*(4+5) podemos digitar 1 2 – 4 5 + *.
O funcionamento dessas calculadoras é muito simples. Cada operando é empilhado numa pilha de valores. Quando se encontra um operador, desempilha-se o número apropriado de operandos (dois para operadores binários e um para operadores unários), realiza-se a operação devida e empilha-se o resultado.
Deste modo, na expressão acima, são empilhados os valores 1 e 2. Quando aparece o operador -, 1 e 2 são desempilhados e o resultado da operação, no caso -1 (1 - 2), é colocado no topo da pilha. A seguir, 4 e 5 são empilhados. O operador seguinte, +, desempilha o 4 e o 5 e empilha o resultado da soma, 9. Nesta hora, estão na pilha os dois resultados parciais, -1 na base e 9 no topo. O operador *, então, desempilha os dois e coloca -9 (-1 * 9) no topo da pilha.
Exemplo: A Pilha de Execução de um Programa
Todo programa C é composto por uma ou mais funções (sendo main a primeira função a ser executada).
Ao encontrar a invocação de uma função, o computador cria um novo "espaço de trabalho", que contém todos os parâmetros e todas as variáveis locais da função.
Esse espaço de trabalho é colocado na pilha de execução (sobre o espaço de trabalho que invocou a função) e a execução da função começa (confinada ao seu espaço de trabalho).
Quando a execução da função termina, o seu espaço de trabalho é retirado da pilha e descartado. O espaço de trabalho que estiver agora no topo da pilha é reativado e a execução é retomada do ponto em que havia sido interrompida.
Exemplo: A Pilha de Execução de um Programa
main(){funcao_1();
}
funcão_1(){funcao_2();
}
funcao_2(){funcao_3();
}
funcao_3(){...
}
Pilha de Execução
Em algum momento este programa será
executado.
Neste momento o fluxo de execução
será desviado para...
Exemplo: A Pilha de Execução de um Programa
main(){funcao_1();
}
funcão_1(){funcao_2();
}
funcao_2(){funcao_3();
}
funcao_3(){...
}
Pilha de Execução
main
espaço de trabalho", que contém todos os parâmetros e todas as variáveis locais da função
“espaço de trabalho", que contém todos os parâmetros e todas as variáveis locais desta função.
Toda chamada de função provoca a criação de uma área de trabalho correspondente.
Toda finalização de função provoca a eliminação da área de trabalho correspondente.
Exemplo: A Pilha de Execução de um Programa
main(){funcao_1();
}
funcão_1(){funcao_2();
}
funcao_2(){funcao_3();
}
funcao_3(){...
}
Pilha de Execução
funcao_1
main
Exemplo: A Pilha de Execução de um Programa
main(){funcao_1();
}
funcão_1(){funcao_2();
}
funcao_2(){funcao_3();
}
funcao_3(){...
}
Pilha de Execução
funcao_2
funcao_1
main
Exemplo: A Pilha de Execução de um Programa
main(){funcao_1();
}
funcão_1(){funcao_2();
}
funcao_2(){funcao_3();
}
funcao_3(){...
}
Pilha de Execução
funcao_3
funcao_2
funcao_1
main
Exemplo: A Pilha de Execução de um Programa
main(){funcao_1();
}
funcão_1(){funcao_2();
}
funcao_2(){funcao_3();
}
funcao_3(){...
}
Pilha de Execução
funcao_2
funcao_1
main
Exemplo: A Pilha de Execução de um Programa
main(){funcao_1();
}
funcão_1(){funcao_2();
}
funcao_2(){funcao_3();
}
funcao_3(){...
}
Pilha de Execução
funcao_1
main
Exemplo: A Pilha de Execução de um Programa
main(){funcao_1();
}
funcão_1(){funcao_2();
}
funcao_2(){funcao_3();
}
funcao_3(){...
}
Pilha de Execução
main
Exemplo: A Pilha de Execução de um Programa
main(){funcao_1();
}
funcão_1(){funcao_2();
}
funcao_2(){funcao_3();
}
funcao_3(){...
}
Pilha de Execução
Operaçõs com Pilha
Em uma Pilha a inserções e remoções sempre ocorrem na mesma extremidade: no topo da pilha.
Operações com Pilha
A figura abaixo mostra como funciona a recuperação e o armazenamento de informações em uma PILHA:
Ação Conteúdo da Pilha
insere(a) a
insere(b) ba
insere(c) cba
recupera ba
insere(d) dba
recupera ba
recupera a
recupera Pilha vazia
Operações com Pilha
Empilhar - Inserir um elemento no topo da pilha.Desempilhar - Recuperar um elemento do topo da
pilha.Calcular o tamanho da pilha (quantidade de
elementos).
Representações de uma Pilha
A Pilha, sendo também uma Lista Linear, pode ser representada por contiguidade ou por encadeamento. A escolha de uma destas formas dependerá da frequência com que determinadas operações serão executadas.
Pilha por Contiguidade
Neste tipo de representação os elementos da Pilha são armazenados em posições contíguas de memória conforme mostra figura abaixo:
Devido ao fato de que o comprimento da pilha (n) pode se modificar durante a execução do programa, consideraremos uma Pilha de n elementos como sendo parte de um vetor de m elementos, sendo m >= n.
0
1
.
.
.
n
.
.
.
m
topo da pilha
Pilha de n elementos.Vetor de m elementos.
m é o tamanho máximo permitido para a pilha.
Em C definiríamos assim: tipo pilha[m]
Pilha por Encadeamento
Cada elemento da pilha contém informações próprias e uma ligação, através de um ponteiro, com outro elemento da pilha.
topo proxinfo
proxinfo
proxinfo
NULL
Pilha por Encadeamento
Assim como na Lista Encadeada podemos utilizar um descritor.
Obs: Podemos também utilizar o encadeamento duplo e circular.
topo proxinfo
proxinfo
proxinfo
NULL
nfundo
100
Fila
A utilização do critério FIFO torna a Fila uma ferramenta ideal para processamento de itens de acordo com a “ordem de chegada”.
Sistemas Operacionais, por exemplo, utilizam filas para regular a ordem na qual tarefas devem receber processamento e recursos devem ser alocados a processos.
Operações com Fila
Inserir um elemento no final da fila.Recuperar um elemento do início da fila.Verificar o número de elementos da fila.
Fila é uma lista onde todas as inclusões são feitas em uma extremidade e todas as exclusões na outra extremidade
Representação
A Fila também pode ser representada por contiguidade ou por encadeamento.
Fila por Contiguidade
Neste tipo de representação os elementos da Fila são armazenados em posições contíguas de memória conforme mostra figura abaixo:
Onde: n: é o numero de elementos da fila.
m: tamanho máximo permitido para a fila.
livre e proximo: variáveis que indicam a posição a inserir e a recuperar.
Em C definiríamos assim: tipo fila[m]
Devido ao fato de que o comprimento da fila (n) pode se modificar durante a execução do programa, iremos representá-la como sendo parte de um vetor de m elementos, sendo m >= n.
X1
X2 ... X
n ... Xm
Fila por Contiguidade
A
A B
A B
A B
A B C
livre
prox. livre
livre
livre
livre
livre
prox.
prox.
prox.
prox.
prox.
Fila vazia
insere(A)
insere(B)
recupera
recupera
insere(C)
A variável livre indica uma posição para inserção (indica também o n = número de itens na fila). A variável prox indica a posição a ser recuperada.
A figura ao lado mostra como funciona a recuperação e o armazenamento de informações em uma Fila.
Quando a posição a inserir (livre) for a mesma que a recuperar (próximo) significa que não há mais nenhum elemento restante na fila. Quando livre for igual a m (tamanho do vetor) indica fila cheia.
Em uma Fila a inserções sempre ocorrem no final e as remoções sempre ocorrem no início.
Fila por Contiguidade
A
A B
A B
A B
A B C
livre
prox. livre
livre
livre
livre
livre
prox.
prox.
prox.
prox.
prox.
Fila vazia
insere(A)
insere(B)
recupera
recupera
insere(C)
Podemos perceber que as operações de inserção ocupam um espaço na memória que não é reaproveitado.
Com algumas inserções e recuperações de itens, a fila vai de encontro ao limite (m) do espaço alocado para ela.
Se em um programa definíssemos a fila tipo fila[m], só conseguiriámos inserir e recuperar m itens da fila, pois as posições recuperadas são descartadas e não tornam livres para posterior inserção. Como solucionar problema ?
A solução ideal seria reutilizar as posições que já foram recuperadas. Para isso usa-se a representação circular de fila.
Fila Circular por Contiguidade
Para possibilitar que posições recuperadas possam ser reutizadas, devemos fazer com que as variáveis livre e proximo, ao alcançarem seus valores de limite (m), voltem a ter valores iniciais (0). Isto pode ser implementado através da aritmética modular conforme veremos a seguir.
Precisaríamos também de uma variável (tamanho) para controlar o tamanho da fila. A cada inserção é incrementada e a cada recuperação decrementada.
X0
Xn
Xm
X1
. . .
. . .
Fila Circular por Contiguidade
Usaremos a aritmética modular para fazer com que as variáveis livre e proximo, ao alcançarem seus valores de limite (m), voltem a ter valores iniciais (0).
Considerando inicialmente m = 5, livre = 0 e proximo = 0:
Nas Inserções livre = livre % m
1a. inserção: livre = (0 % 5) a posição livre no vetor é 02a. inserção: livre++ livre = (1 % 5) a posição livre no vetor é 13a. inserção: livre++ livre = (2 % 5) a posição livre no vetor é 24a. inserção: livre++ livre = (3 % 5) a posição livre no vetor é 35a. inserção: livre++ livre = (4 % 5) a posição livre no vetor é 46a. inserção: livre++ livre = (5 % 5) a posição livre no vetor é 07a. inserção: livre++ livre = (1 % 5) a posição livre no vetor é 1
...
Fila Circular por Contiguidade
Nas Recuperações proximo = proximo % m
1a. recuperação: proximo = (0 % 5) a posição proximo no vetor é 02a. recuperação: proximo++ proximo = (1 % 5) a posição proximo no vetor é 13a. recuperação: proximo++ proximo = (2 % 5) a posição proximo no vetor é 24a. recuperação: proximo++ proximo = (3 % 5) a posição proximo no vetor é 35a. recuperação: proximo++ proximo = (4 % 5) a posição proximo no vetor é 46a. recuperação: proximo++ proximo = (5 % 5) a posição proximo no vetor é 07a. recuperação: proximo++ proximo = (1 % 5) a posição proximo no vetor é 1
…
Fila Circular por Contiguidade
O uso mais comum para uma fila circular pode ser na operação de sistemas que “retêm” a informação lida e escrita em um arquivo de disco ou console (buffers).
Um outro uso comum ocorre na aplicação de programas em tempo-real, nos quais, por exemplo, o usuário pode continuar a introduzir dados pelo teclado enquanto o programa executa uma outra tarefa.
Durante um curto intervalo, o que é digitado não é mostrado na tela; este intervalo dura até que o programa complete o outro processo no qual ele trabalhava. Para realizar isto, o programa de aplicação deve continuar verificando entradas do teclado durante a execução do outro processo. Se uma tecla tiver sido digitada, ela será rapidamente colocada na fila e processo continuará. Depois do processo ter sido completado, os caracteres serão recuperados da fila e tratados de maneira normal.
Fila por Encadeamento
Cada elemento da fila contém um ligação, através de um ponteiro, com outro elemento da fila.
fim proxinfo
proxinfo
proxinfo
NULL
inicio
Inserções sempre no FINAL
Recuperações sempre no INÍCIO