Capítulo
2
Recapitulação
2.2
Listas usando Vetores
2.2
Estrutura de Dados Lista - Definição
Uma Estrutura de Dados
Lista é um conjunto de dados dispostos e/ou acessáveis em
uma seqüência determinada.
-
Este conjunto de dados pode possuir uma ordem
intrínseca (Lista Ordenada) ou não.
-
Este conjunto de dados pode ocupar espaços
de memória fisicamente consecutivos, espelhando a sua ordem, ou
não.
-
Se os dados estiverem dispersos fisicamente,
para que este conjunto seja uma lista, ele deve possuir operações
e informações adicionais que permitam que seja tratado como
tal (Lista Encadeada).
Vimos 2 Aspectos
Em um projeo de software, 2 aspectos
devem ser considerados:
-
De que forma estão organizados os dados,
qual a sua estrutura.
-
Quais procedimentos atuam sobre estes dados,
que operações podem ser realizadas sobre eles.
-
Vamos ver agora estes aspectos para as listas.
2.2.1
Listas usando Vetores: Modelagem de Listas utilizando Programação
Estruturada
-
Modelaremos a Estrutura de Dados Lista utilizando
a técnica da Programação Estruturada e como forma
de armazenamento um Vetor (Array).
-
Veremos somente algoritmos.
-
Veremos algoritmos tanto para
uma lista cronológica como para uma lista ordenada.
-
Procedimento Didático:
-
Modelagem da Lista e de seus
algoritmos usando a técnica estruturada.
-
Exercícios
Modelagem
da Lista
-
Aspecto Estrutural:
-
Necessitamos de um vetor para
armazenar as informações.
-
Necessitamos de um indicador
da posição atual do ultimo elemento da lista.
-
Necessitamos de uma constante
que nos diga quando a lista está cheia e duas outras para codificar
erros.
constantes Maxlista = 100;
tipo Lista {
inteiro dados[Maxlista];
inteiro ultimo;
};
-
Colocar e retirar dados da lista.
-
Testar se a lista está vazia ou cheia
e outros testes.
-
Inicializa-la e garantir a ordem dos elementos.
-
Operações: Colocar e retirar
dados da lista:
Adiciona(dado)
AdicionaNoInício(dado)
AdicionaNaPosição(dado,posição)
AdicionaEmOrdem(dado)
Retira()
RetiraDoInício()
RetiraDaPosição(posição)
RetiraEspecífico(dado)
-
Operações: Testar a lista e
outros testes:
ListaCheia
ListaVazia
Posicao(dado)
Contem(dado)
Igual(dado1,dado2)
Maior(dado1,dado2)
Menor(dado1,dado2)
-
Operações: Inicializar ou limpar:
InicializaLista
DestroiLista
Algoritmo
InicializaLista
FUNÇÃO inicializaLista()
início
aLista.ultimo <-
-1;
fim;
Observação:
Este e os próximos algoritmos pressupõem que foi definida
uma variável global tipo Lista denominada aLista.
Algoritmo
DestroiLista
FUNÇÃO destroiLista()
início
aLista.ultimo <-
-1;
fim;
Observação:
Este algoritmo parece redundante. Colocar a sua semantica em separado da
inicialização porém, é importante. Mais tarde
veremos que, utilizando alocação deinâmica de memória,
possuir um destrutor para alista é muito importante.
Algoritmo
ListaCheia
Booleano FUNÇÃO listaCheia()
início
SE (aLista.ultimo
= Maxlista - 1) ENTÃO
RETORNE(Verdade)
SENÃO
RETORNE(Falso);
FIM SE
fim;
Algoritmo
ListaVazia
Booleano FUNÇÃO listaVazia()
início
SE (aLista.ultimo
= -1) ENTÃO
RETORNE(Verdade);
SENÃO
RETORNE(Falso);
FIM SE
fim;
Algoritmo
Adiciona
-
Procedimento:
-
Testamos se há espaço.
-
Incrementamos o último.
-
Adicionamos o novo dado.
-
Parâmetros:
-
O dado a ser inserido.
-
Lista (global).
Constantes
ErroListaCheia = -1;
ErroListaVazia = -2;
ErroPosição = -3;
Inteiro FUNÇÃO adiciona(inteiro
dado)
início
SE (listaCheia) ENTÃO
RETORNE(ErroListaCheia);
SENÃO
aLista.ultimo <- aLista.ultimo
+ 1.
aLista.dados[aLista.ultimo] <- dado;
RETORNE(aLista.ultimo);
FIM SE
fim;
Algoritmo
Retira
-
Procedimento:
-
Testamos se há elementos.
-
Decrementamos o último.
-
Devolvemos o último elemento.
-
Parâmetros:
Inteiro FUNÇÃO retira()
início
SE (listaVazia) ENTÃO
RETORNE(ErroListaVazia);
SENÃO
aLista.ultimo <- aLista.ultimo
- 1.
RETORNE(aLista.dados[aLista.ultimo
+ 1]);
FIM SE
fim;
Observação:
note que aqui se aplicam as mesmas restrições das diversas
variantes do algoritmo desempilha.
Algoritmo
AdicionaNoInício
-
Procedimento:
-
Testamos se há espaço.
-
Incrementamos o último.
-
Empuramos tudo para trás.
-
Adicionamos o novo dado na primeira posição.
-
Parâmetros:
-
O dado a ser inserido.
-
Lista (global).
Inteiro FUNÇÃO adicionaNoInício(inteiro
dado)
variáveis
inteiro posição; "Variável
auxiliar para caminhar"
início
SE (listaCheia) ENTÃO
RETORNE(ErroListaCheia);
SENÃO
aLista.ultimo <- aLista.ultimo
+ 1;
posição <- aLista.ultimo;
ENQUANTO (posição > 0)
FAÇA
"Empurrar tudo para tras"
aLista.dados[posicao] <- aLista.dados[posicao
- 1];
posição <- posição
- 1;
FIM ENQUANTO
aLista.dados[0] <- dado;
RETORNE(0);
FIM SE
fim;
Algoritmo
RetiraDoInício
-
Procedimento:
-
Testamos se há elementos.
-
Decrementamos o último.
-
Salvamos e primeiro elemento.
-
Empuramos tudo para a frente.
-
Parâmetros:
Inteiro FUNÇÃO retiraDoInício()
variáveis
inteiro posição, valor;
início
SE (listaVazia) ENTÃO
RETORNE(ErroListaVazia);
SENÃO
aLista.ultimo <- aLista.ultimo
- 1;
valor <- aLista.dados[0];
posição <- 0;
ENQUANTO (posição <=
aLista.ultimo) FAÇA
"Empurrar tudo para frente"
aLista.dados[posicao] <- aLista.dados[posicao
+ 1];
posição <- posição
+ 1;
FIM ENQUANTO
RETORNE(valor);
FIM SE
fim;
Algoritmo
AdicionaNaPosição
-
Procedimento:
-
Testamos se há espaço e se a
posição existe.
-
Incrementamos o último.
-
Empuramos tudo para trás a partir da
posição.
-
Adicionamos o novo dado na posição
dada.
-
Parâmetros:
-
O dado a ser inserido.
-
A posição onde inserir.
-
Lista (global).
Inteiro FUNÇÃO adicionaNaPosição(inteiro
dado, destino)
variáveis
inteiro posição;
início
SE (listaCheia) ENTÃO
RETORNE(ErroListaCheia);
SENÃO
SE (destino > aLista.ultimo + 1) ENTÃO
RETORNE(ErroPosição);
FIM SE
aLista.ultimo <- aLista.ultimo +
1;
posição <- aLista.ultimo;
ENQUANTO (posição > destino)
FAÇA
"Empurrar tudo para tras"
aLista.dados[posicao] <- aLista.dados[posicao
- 1];
posição <- posição
- 1;
FIM ENQUANTO
aLista.dados[destino] <- dado;
RETORNE(destino);
FIM SE
fim;
Algoritmo
RetiraDaPosição
-
Procedimento:
-
Testamos se há elementos e se a posição
existe.
-
Decrementamos o último.
-
Salvamos elemento na posição.
-
Empuramos tudo para frente até posição.
-
Parâmetros:
-
O dado a ser inserido.
-
A posição onde inserir.
-
Lista (global).
Inteiro FUNÇÃO retiraDaPosição(inteiro
fonte)
variáveis
inteiro posição;
inteiro valor;
início
SE (fonte > aLista.ultimo) ENTÃO
RETORNE(ErroPosição);
SENÃO
SE (listaVazia) ENTÃO
RETORNE(ErroListaVazia);
SENÃO
aLista.ultimo <- aLista.ultimo
- 1;
valor <- aLista.dados[fonte];
posição <- fonte;
ENQUANTO (posição <=
aLista.ultimo) FAÇA
"Empurrar tudo para frente"
aLista.dados[posicao] <- aLista.dados[posicao
+ 1];
posição <- posição
+ 1;
FIM ENQUANTO
RETORNE(valor);
FIM SE
FIM SE
fim;
Algoritmo
AdicionaEmOrdem
-
Procedimento:
-
Necessitamos de uma função para
comparar os dados (maior)
-
Testamos se há espaço.
-
Procuramos pela posição onde
inserir comparando dados.
-
Chamamos adicionaNaPosição.
-
Observação: Quando
o dado a ser armazenado em uma lista for algo mai scomplexo do que um inteiro,
a comparação de precedênci não será mais
tão simples (ex.: Empregado1 > Empregado2) e será resultado
de um conjunto mais complexo de operações.
Para deixar os algoritmos
de operações sobre lista independentes do tipo de dado específico
armazenado na lista, usamos uma função deste tipo.
Algoritmo AdicionaEmOrdem
Inteiro FUNÇÃO adicionaEmOrdem(inteiro
dado)
variáveis
inteiro posição; "Variável
auxiliar para caminhar"
início
SE (listaCheia) ENTÃO
RETORNE(ErroListaCheia);
SENÃO
posição <- 0;
ENQUANTO (posição <=
aLista.ultimo E
maior(dado, aLista.dados[posição]))
FAÇA
"Encontrar posição para
inserir"
posição <- posição
+ 1;
FIM ENQUANTO
RETORNE(adicionaNaPosição(dado,posição));
FIM SE
fim;
Algoritmo
RetiraEspecífico
-
Retira um dado específico
da lista.
-
Procedimento:
-
Testamos se há elementos.
-
Testamos se o dado existe e qual sua posição.
-
Necessitamos de uma função Posição(dado)
-
Chamamos RetiraDaPosição.
-
Parâmetros:
-
O dado a ser retirado.
-
Lista (global).
Preâmbulo:
Algoritmo Posição
Inteiro FUNÇÃO posição(inteiro
dado)
variáveis
inteiro posição;
início
posição <- 0;
ENQUANTO (posição <=
aLista.ultimo E
NÃO(IGUAL(dado,aLista.dados[posição]))) FAÇA
posição <- posição
+ 1;
FIM ENQUANTO
SE (posição > aLista.ultimo)
ENTÃO
RETORNE(ErroPosição);
SENÃO
RETORNE(posição);
FIM SE
fim;
Algoritmo RetiraEspecífico
Inteiro FUNÇÃO retiraEspecífico(inteiro
dado)
variáveis
inteiro posição;
início
SE (listaVazia) ENTÃO
RETORNE(ErroListaVazia);
SENÃO
posição <- posição(dado);
SE (posição < 0) ENTÃO
RETORNE(ErroPosição);
SENÃO
RETORNE(retiraDaPosição(posição));
FIM SE
FIM SE
fim;
Algoritmos
Restantes
-
As seguintes funções:
booleano Contem(dado)
booleano Igual(dado1, dado2)
booleano Menor(dado1, dado2)
Ficam por conta do aluno.
2.2.2.2
Exercícios de implementação:
-
Obrigatório: Utilize os algoritmos
implementados nesta aula para desenvovler um pequeno programa em "C" que
leia números inteiros e os insira/delete de uma lista. Este programinha
deve ter um pequeno menu (em ASCII) que oferece todas as opções
(funções) acima (menos a de criar uma lista). Numere as opções
e permita ao usuário entrar com o número da função
a ser executada, passando à execução em seguida. Pesquise
a utilização do comando switch em "C" para a implemntação
deste menu em um laço.
-
Observe que será necessário
que você implemente ainda uma função que mostra o conteúdo
(todos os elementos em ordem) da lista, pois esta função
não foi dada em aula mas é necessária ao trabalho.
-
Entrega: terca feira.
-
Opcional: Para melhorar o seu domínio
de "C" tente, depois que você implementou a lista para números
interios, implementá-la para strings de tamanho máximo 30
caracteres.
-
Observe que para definir um vetor de strings
de no máximo 30 caracteres, voce deve em "C" definir uma matriz
de caracteres
tipo Lista {
caracter
dados[Maxlista][31];
inteiro
ultimo;
}
-
Observe que você pode referenciar um
string na posição 3 da lista simplesmente utilizando aLista.dados[3]
e omitindo o segundo índice. O resultado será um ponteiro
para string (char *).
-
As suas funções de manipulação
de listas devem refletir o fato de que voce agora está trabalhando
com uma matriz.
-
Observe também que o tamanho é
31 para cada linha da matriz, pois "C" utiliza o caracter ASCII 0 (zero)
para indicar o fim de um string e um string de 30 caracteres ocupará
31 bytes.