Beruflich Dokumente
Kultur Dokumente
Introdução
Neste capítulo iremos discutir alguns aspectos de listas e como podemos implementar esta
estrutura de dados em C.
Listas
Uma lista linear de informações é uma estrutura de dados simples que armazena
informações sobre dados que apresentam uma relação entre seus elementos.
A maneira mais simples de se manter uma lista na memória do computador é colocar seus
nós em posições contíguas. Neste caso o elemento j+c estará c bytes (ou palavras, depende do
sistema) à frente do elemento j da lista. A constante c corresponde ao número de bytes (ou palavras)
necessário para armazenar cada elemento da lista. Cada elemento da lista é comumente chamado de
nó. Um nó da lista pode conter vários tipos de informações que são armazenados em campos. Cada
nó contém um (ou mais) elemento(s) que identificam unicamente o nó, a chave. Uma lista pode
estar ordenada ou não de acordo com a chave.
#include <stdlib.h>
#include <stdio.h>
int *PtrIni = 0;
int *PtrFim = 0;
int tam = 0;
int main(){
int vetor[10], valor, pos, retiraini, retirafim, i;
valor=10;
insereInilst(vetor, valor);
valor=11;
insereInilst(vetor, valor);
valor=12;
insereFimlst(vetor,valor);
valor=13;
pos = 2;
inserePoslst(vetor,valor,pos);
retiraini=retiraIniLst(vetor);
printf("\n\nretirada do inicio da lista\n\n");
for (i=0; i<10; i++){
printf("\nvalor armz no item vetor[ %d ] = %d\n",i, vetor[i]);
}
system("pause");
valor=20;
insereInilst(vetor, valor);
system("pause");
retirafim=retiraFimLst(vetor);
printf("\n\nretirada do fim da lista\n\n");
for (i=0; i<10; i++){
printf("\nvalor armz no item vetor[ %d ] = %d\n",i, vetor[i]);
}
system("pause");
}
void insereInilst(int *vetor, int valor){
*PtrIni = valor;
tam++;
}
else {
printf("Limite de memoria alcancado - overflow\n");
}
}
if (PtrIni==PtrFim) {
PtrIni=0;
PtrFim=0;
}
else {
while (PtrTrb!=PtrFim) {
*PtrTrb = *(PtrTrb+1);
PtrTrb++;
}
PtrFim=PtrFim - 1;
}
tam--;
return retirado;
}
PtrTrb=PtrFim;
if (pos>tam){
exit(1);
}
else {
PtrAux = &vetor[pos-1];
while (PtrTrb >= PtrAux) {
*(PtrTrb+1) = *PtrTrb;
PtrTrb = PtrTrb -1;
}
PtrFim = PtrFim + 1;
}
*PtrAux = valor;
tam++;
}
if (PtrIni==0) {
PtrIni = &vetor[0];
PtrFim = &vetor[0];
}
else {
PtrFim = PtrFim + 1;
}
*PtrFim = valor;
tam++;
}
int retirado;
retirado = *PtrFim;
if (PtrIni==PtrFim){
PtrIni = 0;
PtrFim = 0;}
else {
PtrFim--;
}
tam--;
return retirado;
}
/* list01.c */
#include <stdio.h>
#define MAX 4
struct Taluno {
char nome[40];
unsigned long int dre;
};
void main ()
{
int aluno;
unsigned long dre;
char linha[80];
struct Taluno turma[MAX];
exit(0);
}
puts("Dados da turma");
for (i=0; i<MAX; i++) {
printf("\nAluno %d\n", i);
printf("Nome = %s\n", t[i].nome);
printf("Nome = %lu\n", t[i].dre);
}
}
No exemplo anterior são feitas duas comparações a cada interação ( i < MAX e turma[i].dre
== dre). Para diminuir o número de comparações vamos usar um artifício simples: colocar uma
posição extra no final da lista e sempre que for necessário buscar um elemento na lista, o seu valor
será inserido nesta posição extra. Deste modo sempre acharemos o elemento na lista. No entanto, se
o elemento estiver no final da lista significa que o elemento não foi encontrado. Observar que a lista
passa ter agora uma posição a mais, que é reservada para o elemento a ser procurado.
O programa list02.c mostra um exemplo de busca em uma lista seqüencial com uma posição a mais.
/* programa list02.c */
#include <stdio.h>
struct Taluno {
char nome[40];
unsigned long int dre;
};
void main ()
{
int aluno;
unsigned long dre;
char linha[80];
struct Taluno turma[MAX];
le (turma);
imprime (turma);
puts("Qual o DRE a procurar? "); gets(linha);
sscanf(linha, "%lu", &dre);
aluno = busca1 (turma, dre);
if (aluno == -1)
puts("Nao ha aluno com este dre.");
else
printf("O aluno com dre %lu chama-se %s\n",
turma[aluno].dre, turma[aluno].nome);
exit(0);
turma[MAX-1].dre=dre;
while (turma[i].dre != dre) i++;
if (i != MAX-1 ) return i;
else return -1;
}
Os exemplos anteriores assumiam dois fatos a cerca da lista: a lista está cheia e desordenada.
Quando a lista está ordenada a procura pode ser interrompida antes de chegar ao fim da lista. Neste
algoritmo ao invés de procurar o elemento, o teste é se o elemento da lista é menor que o procurado.
O programa list03.c mostra um exemplo de busca em uma lista ordenada. Para facilitar
vamos mostrar apenas a rotina busca_ord que executa este algoritmo, já que todo o restante do
programa é similar ao anterior. No arquivo indicado na ligação mostramos o arquivo todo.
/* programa list03.c */
turma[MAX-1].dre=dre;
while (turma[i].dre < dre) i++;
if (i == (MAX-1) || turma[i].dre != dre ) return -1; /* nao achei. */
else return i;
}
Um algoritmo mais eficiente para busca em uma lista ordenada é o algoritmo de busca
binária. Primeiro o algoritmo procura o elemento no elemento do meio da tabela. Caso não esteja
ele descarta a metade onde o elemento não está e passa a procurar no meio da metade que sobrou. O
algoritmo vai dividindo a lista em duas metades, sempre descartando a metade onde o elemento não
está.
O programa list04.c mostra um exemplo de busca binária em uma lista ordenada. Para
facilitar vamos mostrar somente a rotina busca_bin que implementa este algoritmo.
/* programa list04.c */
/* Busca um elemento na lista L */
int busca_bin ( Taluno t[], unsigned long int dre) {
int inf=0, sup=MAX-1, achou=-1, meio;
Agora passaremos a apresentar algoritmos para inserir e remover elementos de uma lista.
Ambos os algoritmos utilizam a rotina de busca para inserir e remover.
Primeiro vamos apresentar um algoritmo de inserção que não precisa que a lista esteja
ordenada. Neste algoritmo, o elemento, caso ele não já esteja na lista, é inserido após o último
elemento. O algoritmo de remoção move, a partir do último elemento até o elemento seguinte ao
que será removido, todos os elementos uma posição para à esquerda.
O exemplo list06.c abaixo mostra um programa que inclui estes dois algoritmos. Como a
lista não estará sempre cheia, já que estaremos removendo e inserindo elementos a todo instante,
iremos modificar as rotinas de busca. A modificação adiciona uma variável n que contém o número
de elementos na lista no instante em questão. O programa pede ao usuário que indique através de
uma letra qual operação deseja executar. As operações possíveis são as seguintes: [I]nserir,
[R]emover, [L]istar e [S]air. Para facilitar o entendimento a lista utilizada contém somente números
inteiros.
/* programa list06.c */
/* Busca um elemento na lista L */
#define MAX 5
#include <stdio.h>
#include <ctype.h>
char meu_menu();
void insere(int item[], int *n);
void meu_remove(int item[], int *n);
void listar(int item[], int n);
void ordena(int item[], int n);
int busca_bin (int item[], int n, int x);
int main()
{
int item[MAX];
int n=0, sair=0;
int a, b, t;
char opcao;
do {
opcao = meu_menu();
switch (opcao) {
case 'i':
insere(item, &n);
break;
case 'r':
meu_remove(item, &n);
break;
case 'l':
listar(item, n);
break;
case 's':
sair = 1;
break;
default:
puts("Opcao invalida.");
break;
}
} while (!sair);
}
int a, b, t;
sup = n-1;
while (inf <= sup) {
meio = (1/2)*(inf+sup);
if (item[meio]==x) {
achou=meio;
inf = sup + 1;
}
else if (item[meio]<x)
inf = meio+1;
else
sup = meio-1;
}
return achou;
}
int i;
char meu_menu () {
int x, indice, i;
char linha[80];
if (*n != 0) {
printf("Valor a remover? ");
gets(linha); sscanf(linha, "%d", &x);
indice = busca_bin(item, *n, x);
printf("Indice = %d\n", indice);
if (indice != -1) {
for (i=indice; i<*n; i++) item[i] = item[i+1];
*n = *n - 1;
}
else puts("Nao existe este elemento.");
}
else puts("Lista vazia");
}
A Figura lista encadeada abaixo mostra como seria uma lista usando ponteiros para encadear
os elementos da lista. O ponteiro pt aponta para o nó inicial da lista. Cada nó está representado por
um retângulo dividido em duas partes. Uma das partes contém a informação e a outra o ponteiro
para o próximo nó. Observar que no último nó a seta aponta para a terra (ponteiro nulo), que indica
fim da lista.
Lista Linear Simplesmente Encadeada
O algoritmo LEnc mostra as operações de inserção e retirada que podem ser feitas em
qualquer posição de uma lista encadeada e a operação de percurso (listar) da lista.
#include <stdio.h>
#include <stdlib.h>
struct node {
int info;
struct node *prox;
};
int main( ){
int escolha;
int valor,pos,retira;
printf("Lista Linear Encadeada\n");
do {
menu();
printf("escolha uma das opcoes \n");
scanf("%d", &escolha);
switch(escolha)
{
case 1:
printf("\nINSERE NO DO FIM DA LISTA\n");
printf("\n\n DIGITE O NUMERO PARA INSERCAO NO FINAL DA LISTA ");
scanf("%d",&valor);
insereFim(valor);
printf("\n\n o valor inserido no final da lista foi %d \n\n\n", *tras);
system("pause");
break;
case 2:
printf("\n\n INSERE NO INICIO DA LISTA \n");
printf("\n\n DIGITE O NUMERO PARA INSERCAO NO INICIO DA LISTA ");
scanf("%d", &valor);
insereIni(valor);
printf("\n\n o valor inserido no inicio da lista foi %d ", *frente);
system("pause");
break;
case 3:
printf("\n\n INSERE EM UMA POSICAO DA LISTA \n");
printf("\n\n DIGITE O NUMERO PARA INSERCAO EM UMA POSICAO DA
LISTA ");
scanf("%d",&valor);
printf("\n\n DIGITE A POSICAO NA LISTA ");
scanf("%d", &pos);
inserepos(valor,pos);
system("pause");
break;
case 4:
printf("\n\n RETIRA DO FINAL DA LISTA \n");
retira=retiraFim();
printf("\nO valor retirado do final da lista foi %d ", retira);
system("pause");
break;
case 5:
printf("\n\n RETIRA DO INICIO DA LISTA \n");
retira=retiraFrente();
printf("\n\n O VALOR DO PRIMEIRO ELEMENTO RETIRADO DA LISTA %d\n", retira);
system("pause");
break;
case 6:
printf("\n\n RETIRA DE UMA POSICAO DA LISTA \n");
system("pause");
break;
case 7:
printf("\n\n imprimir a lista \n");
percurso();
system("pause");
break;
case 8:
printf("\n\n terminar o processamento da lista \n");
destroi();
system("pause");
break;
default:
destroi();
printf("\n\n FIM DO PROGRAMA \n");
system("pause");
break;
}
}while(escolha < 8);
printf("\n\n______________FIM DO
PROGRAMA_____________________________\n\n");
}
void menu()
{
system("cls");
printf("\n\n ESCOLHA UMA DAS OPCOES \n\n");
printf(" 1 : para inserir no final da lista \n");
printf(" 2 : para inserir no inicio da lista \n");
printf(" 3 : para inserir em uma posicao da lista \n");
printf(" 4 : retirar do final da lista \n");
printf(" 5 : retirar do inicio da lista \n");
printf(" 6 : retirar de uma posicao da lista \n");
printf(" 7 : imprimir a lista \n\n");
}
if (frente==0) {
frente=tras=p;
}
else
{
tras->prox=p;
tras=p;
}
}
int i = 1;
struct node *p = 0;
struct node *ptrtrb, *ptraux;
p=(node *) malloc(sizeof(struct node));
printf("o valor de p e %X ",p, "\n\n");
p->info=valor;
p->prox=0;
if (pos==1) {
p->prox=frente;
frente=p;
if(tras==0) tras=frente;
}
else {
if ((ptrtrb==0) && (i==pos)) {
tras->prox=p;
tras=p;
}
else {
ptraux->prox=p;
p->prox=ptrtrb;
}
}
}
void percurso()
{
struct node *aux=0;
if (frente==0)
printf("\n\n fila vazia \n");
else {
aux=frente;
printf("\n o endereco do primeiro no da fila e %X \n", frente);
while(aux->prox!=0) {
printf("\n aux -> info %d \n", aux->info);
printf("\n aux -> prox %X \n", aux->prox);
aux=aux->prox;
}
printf("\n o endereco do ultimo no e %X \n", tras);
printf("\n aux -> info %d \n", aux -> info);
printf("\n aux -> prox %X \n", aux -> prox);
aux=0;
}
}
void destroi()
{
struct node *aux=0;
aux=frente;
while(aux!=0) {
trb=aux;
aux=aux->prox;
free(trb);
}
aux=0;
frente=0;
tras=0;
trb=0;
}
int retiraFrente()
{
int retirado;
if (frente==0)
printf("\nfila vazia\n");
else
{
if (frente==tras) {
retirado=frente->info;
aux=frente;
frente=tras=0;
}
else
{
aux=frente;
retirado=frente->info;
frente=frente->prox;
}
free(aux);
return(retirado);
}
}
int retiraFim()
{
int retirado;
struct node *ptrtrb, *ptraux;
if (tras==0) {
printf("\nfila vazia\n");
system("pause");
return 0;
}
if (frente==tras) {
retirado=frente->info;
ptrtrb=frente;
frente=tras=0;
}
else {
retirado=tras->info;
ptrtrb=ptraux=frente;
while(ptrtrb->prox!=0) {
ptraux=ptrtrb;
ptrtrb=ptrtrb->prox;
};
tras=ptraux;
tras->prox=0;
}
free(ptrtrb);
return(retirado);
}
O algoritmo implista mostra uma implementação alternativa de como percorrer uma lista
encadeada simples.
int i=0;
struct tElemento *pont;
pont = ptlista;
while (pont) {
printf("Elemento %d = %d\n", i++, pont->info);
pont = pont->prox;
}
}
Outros algoritmos relevantes em listas são: busca, inserção e remoção. Para facilitar a busca
e localização dos nós a serem removidos e das posições de inserção modificaremos a lista para
incluir o que costuma ser chamado de nó cabeça. Neste tipo de lista o primeiro nó não irá conter
informação, ele apenas faz com que o algoritmo de busca não necessite diferenciar o primeiro nó
dos demais. A figura lista encadeada com nó cabeça ilustra esta lista.
lista encadeada com nó cabeça
A rotina de busca que iremos mostrar é um algoritmo simples que tem como protótipo
ponteiros para ponteiros. O primeiro **ant aponta para o nó anterior ao nó procurado e **ponte
aponta para o nó procurado.
A figura remoção de lista encadeada com nó cabeça ilustra o algoritmo de remoção, que tem
o código mostrado abaixo.
remocao de lista encadeada com nó cabeça
int x;
char linha[80];
tElemento **ant, **pont;
Listas Circulares
O algoritmo de busca em uma lista encadeada pode ser melhorado se modificarmos a lista de
modo que ela passe a ser circular como está mostrado na figura listcirc abaixo.
Neste caso o algoritmo não tem como testar o final da lista. A solução é armazenar o dado
que se está procurando no nó cabeça. Ao término do algoritmo a chave é sempre encontrada, e
pode-se descobrir se ela pertencia ou não a lista pelo ponteiro que foi dado como resposta. Caso o
ponteiro termine apontando para o nó cabeça, podemos afirmar que o elemento não se encontra na
lista.
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
struct tElemento {
int info;
struct tElemento *prox;
};
char tela();
struct tElemento *cria_no();
void insere (struct tElemento *, int );
void meu_remove (struct tElemento *, int);
void listar(struct tElemento *);
void main()
{
struct tElemento *ptlista;
char linha[80];
char opcao;
int sair = 0, valor;
ptlista = cria_no();
ptlista->prox=ptlista;
do {
opcao = tela();
switch (opcao) {
case 'i':
puts("Qual dado a inserir?"); gets(linha);
sscanf(linha, "%d", &valor);
insere(ptlista, valor);
break;
case 'r':
puts("Qual dado a remover?"); gets(linha);
sscanf(linha, "%d", &valor);
meu_remove(ptlista, valor);
break;
case 'l':
listar(ptlista);
break;
case 's':
sair = 1;
break;
default:
puts("Opcao invalida.");
break;
}
} while (!sair);
}
char tela () {
int i=0;
struct tElemento *pont;
pont = ptlista->prox;
while (pont != ptlista) {
printf("Elemento %d = %d\n", i++, pont->info);
pont = pont->prox;
}
}
------------------------------------------------------------------------
Exercícios
1. Escreva um programa similar ao list06.c. Neste programa a lista conterá elementos do seguinte
tipo.
typedef struct {
char nome[40];
unsigned long int dre;
} Taluno;
typdef struct {
int n; /* numero de elementos na lista em determinado instante */
Taluno alunos[50]; /* lista propriamente dita */
} Tlista;
2. Escreva um programa que converta uma expressão em notação com parênteses e converta
para notação polonesa reversa. Assuma que todas as operações da expressão são escritas
com parênteses. Por exemplo: (A * (B + C)).
3. Um palíndromo é um conjunto de caracteres que lido nos dois sentidos fornece o mesmo
resultado. Por exemplo: osso, 63736, orava o avaro. Escreva um programa que leia um
conjunto de caracteres e utilizando uma pilha verifique se um conjunto de caracteres é um
palíndromo. A pilha servirá para armazenar o conjunto de caracteres e permitir que ele seja
lido da direita para à esquerda.
4. Escreva um programa que mantenha duas pilhas em um único vetor (int vetor[MAXSIZE];),
de tal maneira que nenhuma das duas pilhas forneça mensagem de pilha cheia até que todo o
vetor esteja cheio e uma pilha não pode ser movida para uma posição diferente dentro do
vetor. Escreva as rotinas insere1, retira1, insere2 e retira2 para manter as duas pilhas.
(Sugestão: as duas pilhas devem crescer, uma em direção à outra.).
5. Escreva um programa que leia um texto de tamanho MAXLINES em que cada linha tem
tamanho MAXCARACS. Imprima em ordem crescente cada palavra contida no texto e
quantas vezes ela aparece. Assuma que as palavras são separadas por um ou mais caracteres
brancos. Use uma lista ordenada para ir armazenando cada uma das diferentes palavras que
foram sendo encontradas. O texto pode conter caracteres maiúsculos e minúsculos mas
palavras escritas em diferentes caixas (maiúsculas e minúsculas) serão contadas e impressas
uma vez apenas.