O modelo básico consiste em uma estrutura de cabeçalho que contém um ponteiro para os dados, a capacidade máxima e o comprimento atual. O ponteiro aponta para uma área de memória alocada dinamicamente para armaznear os elementos. Quando o comprimento atinge a capacidade, a lista é expandida para acomodar mais itens.
Implementação em C
A seguir, apresenta-se uma implementação dividida em três arquivos: um cabeçalho e dois arquivos de código-fonte.
Arquivo de cabeçalho: lista_seq.h
typedef int tipo_elemento;
typedef struct {
tipo_elemento *dados;
int capacidade;
int comprimento;
} ListaSequencial;
void inicializar_lista(ListaSequencial *ls, int tam);
void liberar_lista(ListaSequencial *ls);
void inserir_no_fim(ListaSequencial *ls, tipo_elemento valor);
void inserir_em_posicao(ListaSequencial *ls, int pos, tipo_elemento valor);
void exibir_lista(const ListaSequencial *ls);
int buscar_elemento(const ListaSequencial *ls, tipo_elemento valor);
void remover_por_valor(ListaSequencial *ls, tipo_elemento valor);
ListaSequencial *criar_lista(int tam);
void destruir_lista(ListaSequencial *ls);
Arquivo de implementação: lista_seq.c
#include "lista_seq.h"
#include <stdio.h>
#include <stdlib.h>
void inicializar_lista(ListaSequencial *ls, int tam) {
ls->dados = (tipo_elemento *)malloc(sizeof(tipo_elemento) * tam);
if (ls->dados == NULL) {
ls->capacidade = 0;
return;
}
ls->capacidade = tam;
ls->comprimento = 0;
}
void liberar_lista(ListaSequencial *ls) {
if (ls->dados != NULL) {
free(ls->dados);
ls->dados = NULL;
}
ls->capacidade = 0;
ls->comprimento = 0;
}
static int expandir_lista(ListaSequencial *ls) {
int nova_capacidade = ls->capacidade * 2;
tipo_elemento *temp = (tipo_elemento *)malloc(sizeof(tipo_elemento) * nova_capacidade);
if (temp == NULL) {
return -1;
}
for (int i = 0; i < ls->comprimento; ++i) {
temp[i] = ls->dados[i];
}
free(ls->dados);
ls->dados = temp;
ls->capacidade = nova_capacidade;
printf("Lista expandida com sucesso!\n");
return 0;
}
void inserir_no_fim(ListaSequencial *ls, tipo_elemento valor) {
if (ls->comprimento >= ls->capacidade && expandir_lista(ls) != 0) {
printf("Falha ao expandir a lista!\n");
return;
}
ls->dados[ls->comprimento] = valor;
++ls->comprimento;
}
void inserir_em_posicao(ListaSequencial *ls, int pos, tipo_elemento valor) {
if (pos < 0 || pos > ls->comprimento) {
printf("Posição inválida!\n");
return;
}
if (ls->comprimento >= ls->capacidade && expandir_lista(ls) != 0) {
return;
}
for (int i = ls->comprimento - 1; i >= pos; --i) {
ls->dados[i + 1] = ls->dados[i];
}
ls->dados[pos] = valor;
++ls->comprimento;
}
void exibir_lista(const ListaSequencial *ls) {
for (int i = 0; i < ls->comprimento; ++i) {
printf("%d\t", ls->dados[i]);
}
printf("\n");
}
int buscar_elemento(const ListaSequencial *ls, tipo_elemento valor) {
for (int i = 0; i < ls->comprimento; ++i) {
if (ls->dados[i] == valor) {
return i;
}
}
return -1;
}
void remover_por_valor(ListaSequencial *ls, tipo_elemento valor) {
int pos = buscar_elemento(ls, valor);
if (pos == -1) {
printf("Elemento não encontrado!\n");
return;
}
for (int i = pos + 1; i < ls->comprimento; ++i) {
ls->dados[i - 1] = ls->dados[i];
}
--ls->comprimento;
}
ListaSequencial *criar_lista(int tam) {
ListaSequencial *ls = (ListaSequencial *)malloc(sizeof(ListaSequencial));
if (ls == NULL) {
return NULL;
}
inicializar_lista(ls, tam);
return ls;
}
void destruir_lista(ListaSequencial *ls) {
liberar_lista(ls);
free(ls);
}
Arquivo de teste: teste.c
#include "lista_seq.h"
#include <stdio.h>
void testar_operacoes(ListaSequencial *ls) {
for (int i = 0; i < 5; i++) {
inserir_no_fim(ls, 200 + i);
}
exibir_lista(ls);
inserir_no_fim(ls, 400);
exibir_lista(ls);
inserir_em_posicao(ls, 1, 500);
exibir_lista(ls);
remover_por_valor(ls, 400);
exibir_lista(ls);
}
void teste_com_variavel_local() {
ListaSequencial ls;
inicializar_lista(&ls, 10);
testar_operacoes(&ls);
liberar_lista(&ls);
}
void teste_com_ponteiro() {
ListaSequencial *ls = criar_lista(10);
if (ls != NULL) {
testar_operacoes(ls);
destruir_lista(ls);
}
}
int main() {
teste_com_variavel_local();
printf("===\n");
teste_com_ponteiro();
return 0;
}
As operações de inserção e expansão requerem cuidado com as condições de contorno, especialmente para evitar acessos fora dos limites e garantir o gerenciamento correto da memória.