Atividade de laboratório listas encadeadas simples 1. Estrutura básica Uma lista encadeada simples é uma estrutura de dados composta de uma seqüência de estruturas elementares chamadas nós. Cada nó contém a) uma informação útil (um dado) e b) a localização na memória do próximo nó na seqüência. Naturalmente, a informação da localização do próximo nó deve ser guardada em um ponteiro. Assim, cada nó deve ser uma struct que contenha estes dois campos a) e b). Vamos dar exemplos de tais structs, variando o tipo de informação contida nos nós: 1. A informação é um número inteiro. Neste caso, podemos escrever a struct como struct no { ; int info; struct no *prox; A informação é guardada no campo info, que é um int, e a localização do próximo nó é guardada no campo prox, que é um ponteiro. Este ponteiro deve ser do tipo struct no, já que vai apontar para o próximo nó. 2. A informação é uma string: struct no { ; char info[80]; struct no *prox; Neste caso, o campo info é uma string de 80 caracteres. 3. A informação consiste de vários dados, por exemplo nome e número de alunos: struct no { ; char nome[80]; int ra; struct no *prox; Neste caso, a informação guardada em cada nó consiste do conjunto da string nome e do inteiro ra. Além da definição da struct no, definiremos também um tipo para o ponteiro que aponta para um nó como pno typedef struct no * pno; Vamos representar por meio de um diagrama o primeiro caso. No diagrama abaixo está representada uma estrutura elementar e uma lista encadeada cujos dois primeiros elementos são 7 e 5 e o último é 9. O acesso ao primeiro elemento da lista é feito através do ponteiro inicial. O último elemento é marcado por seu ponteiro prox ter o valor inválido NULL, ao invés de apontar para um próximo elemento:
2. Adição de elementos à lista Uma lista tem duas operações fundamentais: adição de um elemento no começo da lista e remoção do primeiro elemento da lista. O diagrama abaixo mostra os passos para a inserção de um elemento da lista:
3. Exercícios adição de nós a uma lista. 1. Digite o programa a seguir que cria uma lista com um único elemento e a imprime: #include <stdio.h> #include <stdlib.h> struct no { int info; struct no *prox; ; typedef struct no * pno; int main() { /* Chamamos o ponteiro inicial de lista */ /* inicializamos com NULL, o que significa que a lista é vazia */ pno lista = NULL; pno n; /* usado para alocar um novo nó */ pno p; /* usado para imprimir a lista */ /* Um novo nó é criado e sua info é definida */ n = malloc(sizeof(struct no)); n->info = 10; /* O nó é adicionado à lista */ n->prox = lista; lista = n; /* A lista é imprimida */ printf("imprimindo lista:\n"); p = lista; /* começamos com o elemento inicial */ while(p!= NULL) { printf("%d ", p->info); /* imprimimos a info de cada elemento */ p = p->prox; /* avançamos para o próximo elemento da lista */ printf("\n"); system("pause"); return 0;
2. Repita na main() apenas a parte de criação de um novo nó e de adição à lista para que sejam inseridos os valores 20 e 30. 3. No programa resultante da questão 2, isole a criação de um novo nó dentro de uma função chamada get_node(). A função deve ser chamada assim: n = get_node(10). Esta chamada cria um novo nó cujo info é 10 e retorna um ponteiro para a localização deste nó. Isole também a adição de um novo nó dentro de uma função chamada push(). Esta função deve ser chamada assim: push(&lista, 10); para inserir o número 10 no início da lista (note que o argumento lista é passado por referência). Dica: os protótipos das funções devem ser: pno get_node(int); void push(pno *, int); A função push () deve chamar a função get_node() e a função main() deverá apenas chamar a função push(). 4. Isole a impressão da lista em uma função chamada print_list() que deve ser chamada assim: 5. Exercício resolvido: modifique a estrutura de dados para que a informação de cada nó seja um float. Escreva um programa que lê números digitados pelo usuário em qualquer quantidade, usando um laço (em cada laço, o programa deve perguntar se mais um número deve ser inserido). Dica: o laço deve ter uma forma assim: do { /* inserir aqui a entrada de dados na lista */ printf("mais um numero? (s/n): "); scanf(" %c", &c); /* atenção, o espaço antes do %c é importante */ while(c!= 'n'); Crie duas funções, uma que calcula a média e outra que calcula o desvio padrão dos valores da lista. Estas funções devem ser usadas assim: m = media(lista); s = desvio_padrao(lista, media); printf("media = %f, desvio padrao = %f\n", m, s); Dica: estas duas funções percorrem toda a lista, até o final, e portanto são parecidas com a print_list(). No final, o programa deve imprimir a lista de valores digitados, sua média e seu desvio padrão. Solução: veja no final.
4. Remoção de elementos de uma lista A operação fundamental de remoção de um elemento do início da lista é esquematizada no diagrama abaixo: Usando as funções definidas nos exercícios 3 e 4, podemos escrever um exemplo em que se retira o primeiro elemento de uma lista: main() { /* Ponteiro inicial da lista, inicialmente vazia: */ pno lista = NULL;
/* Será usado para guardar o endereço do primeiro elemento da lista */ pno p; /* Três elementos são inseridos: */ push(&lista, 10); push(&lista, 20); push(&lista, 30); /* A lista é imprimida: */ /* Remoção do primeiro elemento ------------------------------*/ /* guarda a localização do primeiro elemento: */ p = lista; /* imprime o primeiro elemento: */ printf("removendo o elemento %d.\n", p->info); /* faz com que o ponteiro inicial aponte para o segundo elemento: */ lista = p->prox; /* devolve a memória ocupada pelo primeiro elemento: */ free(p); /* A lista é novamente imprimida: */ 5. Exercícios remoção de elementos 6. Faça o exemplo de remoção acima funcionar. Dica: parta do código resultante do exercício 4, onde já estão definidas todas as funções necessárias. 7. Exercício resolvido: no código do exercício 6, isole a retirada do primeiro elemento em uma função chamada pop(). Esta função deve retornar 0 se a lista já era vazia antes de se tentar a remoção, ou 1 se a lista não era vazia (chamamos este valor inteiro de status). Ao invés de imprimir o valor retirado, ela deve passar este valor por referência a um parâmetro. Ela deve ser chamada assim: status = pop(&lista, &x); Solução: veja no final. 8. Exercício resolvido: crie uma função que lê uma quantidade qualquer de números entrados pelo usuário e os imprime na ordem inversa de entrada, usando as funções push() e pop() (observação: com estas funções, implementamos uma pilha (LIFO)). Solução: veja no final.
6. Exercícios resolvidos Solução do exercício 5: #include <stdio.h> #include <stdlib.h> #include <math.h> struct no { float info; struct no *prox; ; typedef struct no * pno; pno get_node(float x) { pno n = malloc(sizeof(struct no)); n->info = x; return n; void push(pno *lis, float x) { pno n = get_node(x); n->prox = *lis; *lis = n; void print_list(pno p) { printf("imprimindo lista:\n"); while(p!= NULL) { printf("%f ", p->info); p = p->prox; printf("\n"); float media(pno p) { float m = 0; int n = 0; while(p!= NULL) { m = m + p->info; n = n + 1; p = p->prox;
return m / n; float desvio_padrao(pno p, float m) { float s = 0; int n = 0; while(p!= NULL) { s = s + (p->info - m) * (p->info - m); n = n + 1; p = p->prox; return sqrt(s / n); int main() { pno lista = NULL; float x; char c; float m, s; do { printf("digite um numero: "); scanf("%f", &x); push(&lista, x); printf("mais um numero? (s/n): "); scanf(" %c", &c); while(c!= 'n'); m = media(lista); s = desvio_padrao(lista, m); printf("media = %f, desvio padrao = %f\n", m, s); system("pause"); return 0;
Solução do exercício 7 (apenas a função pop() e a função main(), as outras funções, a struct e o typedef devem ser iguais aos do exercício acima). /* A função de remoção. */ /* l (o ponteiro inicial da lista) e n são passados por referência. */ int pop(pno *l, int *n) { /* guarda o endereço do elemento a ser retirado: */ pno p = *l; /* verifica se a lista era vazia: */ if(p == NULL) return 0; /* passa o valor a ser retirado à referência n: */ *n = p->info; /* altera o ponteiro inicial para apontar ao segundo elemento: */ *l = p->prox; /* libera a memória ocupada pelo primeiro elemento */ free(p); /* retorna 1, pois a lista não era vazia: */ return 1; int main() { /* Ponteiro inicial da lista, inicialmente vazia: */ pno lista = NULL; /* usados para guardar os valores recebidos pelo pop: */ int status, x; /* Três elementos são inseridos: */ push(&lista, 10); push(&lista, 20); push(&lista, 30); /* A lista é imprimida: */ /* Remoção do primeiro elemento */ status = pop(&lista, &x); printf("status = %d, elemento removido: %d\n", status, x); /* A lista é novamente imprimida: */ system("pause"); return 0;
Solução do exercício 8 (apenas a função main(). O programa deve ser completado com o que falta) int main() { pno lista = NULL; int x; char c; /* Insere números na lista até que o usuário digite n */ do { printf("digite um numero: "); scanf("%d", &x); push(&lista, x); printf("mais um numero? (s/n): "); scanf(" %c", &c); /* atenção, o espaço antes de %c é importante! */ while(c!= 'n'); /* retira os elementos da lista e os imprime um por um */ /* até que pop() retorne 0 (isto é, a lista fique vazia) */ printf("numeros na ordem inversa: "); while(pop(&lista, &x)!= 0) { printf("%d ", x); printf("\n"); system("pause"); return 0;