Tipo de Dado Abstrato para Lista
Encadeada e Primitiva.
Os exemplos até agora vistos para
lista encadeada, foram criados com o propósito de mostrar o conceito dos nós, para
manter a simplicidade as rotinas eram dependentes do tipo de dado e da forma
como o mesmo deveria ser armazenado, no caso optamos por salvar nomes numa
lista sequencial, como feito anteriormente com a estrutura de dados pilha, esta
postagem mostra as rotinas implementadas para uma lista encadeada genérica que como
o nome indica, pode ser usada para qualquer tipo de informação que precise ser
armazenado e tratado dentro de uma lista.
O arquivo de cabeçalho llgen.h mostra as declarações das primitivas
para a nossa lista duplamente encadeada que consiste numa corrente de elementos
chamados de nó, cada nó possui um tipo de dado e dois ponteiros usados para
apontar para o elemento anterior e próximo. Tipo de dado usado é o ponteiro
void que consiste no ponteiro genérico de C, que permite ser moldado através do
operador cast para qualquer tipo de dado sem erros.
typedef struct tagSNODE
{
struct tagSNODE *prior;
struct tagSNODE *next;
void
*pdata;
} SNODE;
|
Neste mesmo arquivo, define-se
SLINK como um tipo ponteiro para o nó, SLIST como uma estrutura de dados de uma
lista ligada contendo o nó do inicio, o nó do fim, o total de nós na lista e
apontamentos para funções especificas da implementação dos dados.
typedef SNODE
* SLINK;
typedef struct tagSLIST
{
SLINK slkHead;
SLINK slkTail;
unsigned int uiCount;
void * ( *
fCreateData ) ( void * );
BOOL ( * fDeleteData ) ( void
* );
int ( *
fDuplicatedNode ) ( SLINK, SLINK );
int ( *
fNodeDataCmp ) ( void *, void * );
} SLIST;
|
Por mais abrangente e genérica
que se possa criar uma função, a lista encadeada realmente genérica consegue
manipular apenas as conexões do nó, esse tipo de operação não precisa saber
como os dados se parecem, restando quatro tipos de operações que manipulam os
dados diretamente e dependem do tipo definido. Criar, apagar, comparar e tratar
duplicado são funções que fazem parte do arquivo llapp.c de rotinas especificas da aplicação e passam a integrar a
estrutura de dados lista através dos ponteiros da estrutura mostrada.
FUNÇÕES
ESPECIFICAS PARA LISTA ENCADEADA
|
||||||||||
Nome
|
Descrição
|
|||||||||
fCreateData
|
Recebe um ponteiro do objeto definido
para a aplicação, espera-se o retorno de um ponteiro para o que venha a ser o
dado armazenado na lista.
|
|||||||||
fDeleteData
|
Recebe um ponteiro do objeto definido
para a aplicação que deve estar armazenado na lista ligada. fDeleteData deve
destruir o objeto
|
|||||||||
fDuplicateNode
|
São passados dois ponteiros. O
primeiro ponteiro é o nó que deve ser adicionado e o segundo é o nó que já se
encontra na lista, mas que é igual(duplicada) a informação do primeiro
ponteiro, retornando:
|
|||||||||
fNodeDataCmp
|
São passados dois ponteiros do objeto
definido para o dado e deve compara-lo, retornando um numero que é < 0,
zero, ou > 0, dependendo da relação entre o primeiro e segundo objeto.
|
Para permitir maior legibilidade
foram feitas as seguintes redefinições:
#define
LLHead (L->slkHead)
#define
LLTail (L->slkTail)
#define
NodeCount (L->uiCount)
#define
CreateData (*(L->fCreateData))
#define DeleteData
(*(L->fDeleteData))
#define DuplicatedNode
(*(L->fDuplicatedNode))
#define NodeDataCmp
(*(L->fNodeDataCmp))
|
As rotinas abaixo fazem parte das
primitivas genéricas para lista encadeadas, tais funções manipulam somente as
ligações do nó, sendo completamente independentes da aplicação e não devem ser
alteradas.
Função
|
AddNodeAtHead
|
Propósito
|
Adiciona um nó no inicio (cabeça) da lista NÃO ordenada
|
Parâmetros
|
SLIST *L -> ponteiro para estrutura de dados que trata a lista NÃO
ordenada
void *nd -> nó a ser inserido na lista NÃO ordenada
|
Retorno
|
TRUE - adicionou no inicio da lista o nó
FALSE - não conseguiu criar um nó para inserir na lista
|
Descrição
|
Armazena um novo nó sempre no inicio da lista, primeiro aloca espaço
para o dado, a seguir aloca um nó com ponteiro para o dado, então adiciona o nó
na lista.
|
BOOL AddNodeAtHead( SLIST *L, void
*nd )
{
SLINK slkPn; //
ponteiro do noh a ser inserido na lista
slkPn = CreateNode(L, nd); // cria o noh e
passa o endereco
if (slkPn
== NULL) // naoh conseguiu alocar o noh
return
FALSE; // retorna indicando fracasso
// adiciona o
noh
if
(LLHead == NULL) // primeiro noh a ser inserido na lista ?
{
LLHead =
LLTail = slkPn;// sim
}
else // naoh
{
LLHead->prior = slkPn; // noh antes do
inicio(cabeca)
slkPn->next = LLHead; // inicio como
seguinte do novo noh
LLHead = slkPn; // aponta
o inicio para o novo noh
}
++NodeCount; //
soma o novo noh no total
return
TRUE;
}
|
Função
|
AddNodeAscend
|
Propósito
|
Adiciona um nó ascendente
|
Parâmetros
|
SLIST *L -> ponteiro para estrutura de dados que trata a lista
void *nd -> nó a ser inserido na lista
|
Retorno
|
TRUE - adicionou na lista o nó, caso seja duplicata, remove, adiciona
ou ignora, depende da configuração da lista
FALSE - não conseguiu criar um nó para inserir na lista
|
Descrição
|
Adiciona um nó numa lista ordenada
|
BOOL AddNodeAscend( SLIST *L, void
*nd )
{
SLINK slkPn; // ponteiro
de para o noh a ser inserido na lista
SLINK slkPrev; // ponteiro do
link anterior da lista durante a busca
SLINK slkCurr; // ponteiro do
link atual da lista durante a busca
SNODE snDummy; // noh
temporario
int
iCompare; //
resultado da comparacaoh entre dois nohs
int
iAction; // acaoh a ser
tomada em caso de nohs duplicados
slkPn = CreateNode(L, nd); // cria o noh e
passa o endereco
if (slkPn
== NULL) // naoh conseguiu alocar o noh
return
FALSE; // retorna indicando fracasso
// ignora o
tratamento especial que se faz para o inicio da lista
// adiciona o
noh temporario ao inicio da lista
// e passa a
tratar o noh inicial(cabeca) como um noh igual aos demais
//
simplificando a logica de tratamento
snDummy.next = LLHead;
snDummy.prior
= NULL;
if (snDummy.next != NULL) // se a
lista naoh estiver vazia
snDummy.next->prior = &snDummy; // igual, LLHEAD->prior = &snDummy;
// se prepara para percorrer a lista
slkPrev = &snDummy; // noh
anterior aponta p/ o noh temporario
slkCurr = snDummy.next; // noh atual aponta p/ o noh inicial da lista
// busca
// enquanto o
link do noh for valido
// copia o noh
atual para o anterior
// copia o noh
seguinte para o atual
for ( ;
slkCurr != NULL; slkPrev = slkCurr, slkCurr = slkCurr->next)
{
// usa
funcaoh que compara dados do atual com o noh a ser inserido
iCompare =
NodeDataCmp(slkPn->pdata, slkCurr->pdata);
if
(iCompare <= 0) {
break; // o novo
noh eh igual ou precede o atual
}
}
// se naoh
percorreu a lista ateh o final
// e o
encontrou uma copia(duplicada) dos dados na lista
if
((slkCurr != NULL) && (iCompare == 0))
{
// chama a
funcaoh que retorna a acaoh desejada
// no caso
de noh duplicado
iAction = DuplicatedNode(slkPn,
slkCurr);
// fDuplicateNode retorna:
// 2 -> adicionar duplicada
if
(iAction == 2) {
// nada
faz, deve ser inserido
// o
tratamento continua fora
// da
condicional 'if ((slkCurr != NULL) && (iCompare == 0))'
}
// fDuplicateNode retorna:
// 0 -> nenhuma acaoh na lista
// 1 -> destruir duplicada
else // if (iAction
== 0 || iAction == 1)
{
//
primeiro, arruma a lista ligada tiramos o noh
//
temporario do inicio e restauramos o inicio original
LLHead = snDummy.next;
LLHead->prior = NULL;
// se apropriado, apaga o noh duplicado
if
(iAction == 1) // ||(iAction == 0)
{
//
apaga os dados que dependem da implementacaoh do objeto
DeleteData(slkPn->pdata);
free(slkPn); //
desalocamos o noh
}
// se a
iAction == 0, um erro aconteceu,
// o
tratamento vai ser feito fora desta funcaoh
return
TRUE;
// encontrou uma
duplicata,tomou a acaoh de ignorar ou excluir
}
}
// vai
adicionar o noh entre o anterior e o atual
slkPrev->next = slkPn; // o noh
anterior aponta para o novo
slkPn->prior = slkPrev; // o novo aponta
'prior' para o noh anterior
slkPn->next = slkCurr; // o novo
aponta 'next' para o noh atual
// se o noh
corrente aponta para uma posicao valida na lista
if
(slkCurr != NULL)
{
slkCurr->prior = slkPn; // noh atual aponta 'prior' para o novo noh
}
// do
contrario, significa que percorreu a lista ateh o final
else
{
LLTail = slkPn; // este noh eh a novo fim(cauda)
}
++NodeCount; //
soma o novo noh no total
// retiramos o
noh temporario
LLHead = snDummy.next;
LLHead->prior
= NULL;
return TRUE;
}
|
Função
|
CreateLList
|
||||||||
Propósito
|
Cria e inicia uma estrutura de gerenciamento de lista ligada
|
||||||||
Parâmetros
|
ponteiro para as funções especifica da lista
|
||||||||
Retorno
|
ponteiro SLIST
pL - ponteiro para uma estrutura do tipo
lista inicializada
NULL - não conseguiu criar
a lista ligada
|
||||||||
Descrição
|
Cria uma estrutura de lista ligada e retorna um ponteiro da mesma. Em
erro, retorna NULL. Esta função aceita ponteiros para as quatro funções
especificas de uma lista e inicializa a estrutura com elas.
|
SLIST *CreateLList( void
* ( * fCreateData ) ( void * ),
BOOL ( * fDeleteData ) ( void * ),
int ( *
fDuplicatedNode ) ( SLINK, SLINK ),
int ( *
fNodeDataCmp ) ( void *, void *))
{
SLIST *pL;
// aloca uma estrutura que gerencia uma lista ligada
pL
= (SLIST *) malloc(sizeof(SLIST));
if (pL == NULL)
//
caso fracasse em alocar memoria
return
NULL; //
retorna a indicacaoh
// tendo
alocado, inicializa as variaveis
pL->slkHead
= NULL;
pL->slkTail =
NULL;
pL->uiCount
= 0;
// e armazena
as funcoesh especificas para tratamento da lista
pL->fCreateData = fCreateData;
pL->fDeleteData = fDeleteData;
pL->fDuplicatedNode = fDuplicatedNode;
pL->fNodeDataCmp = fNodeDataCmp;
return
(pL); // devolve o enderecoh da lista
}
|
Função
|
CreateNode
|
Propósito
|
Criar um nó para a lista ligada
|
Parâmetros
|
SLIST *L -> estrutura
lista c/ ponteiro para a função CreateData
void *data -> ponteiro genérico c/ o dado do nó
|
Retorno
|
slkNewNode - ponteiro para uma estrutura do tipo nó
NULL - não conseguiu criar um nó
|
Descrição
|
Cria um nó e então chama a função especifica da aplicação
CreateData() para criar o dado da estrutura do nó. Em caso de falha retorna
NULO
|
SLINK CreateNode( SLIST *L, void
*data )
{
SLINK slkNewNode;
// aloca os
ponteiros para seguinte, anterior e dado para o noh
slkNewNode
= (SLINK) malloc(sizeof(SNODE));
if (slkNewNode == NULL)
// caso fracasse em alocar memoria
return
(NULL); // retorna indicacaoh de NULO
// tendo
sucesso na alocacaoh, inicializa as variaveis
slkNewNode->prior = NULL;
slkNewNode->next = NULL;
// agora chama a aplicacaoh especifica para alocacah de dados
slkNewNode->pdata = CreateData(data);
// caso naoh
consiga alocar memoria para o dado do noh
if
(slkNewNode->pdata == NULL)
{
free(slkNewNode); // libera a
memoria previamente alocada
return
(NULL); // retorna NULO para indicar falha
}
// alocou toda
a memoria nescessaria
else {
return
(slkNewNode); // retorna o ponteiro para a funcaoh
}
}
|
Função
|
DeleteNode
|
Propósito
|
Apaga um nó da lista ligada
|
Parâmetros
|
SLIST *L ->
estrutura lista c/ ponteiro para a função DeleteData
SLINK slkToDelete -> o nó a ser excluído
|
Retorno
|
TRUE - exclui o nó e atualizou
a lista
FALSE - não era um nó valido p/ exclusão
|
Descrição
|
Apaga um nó apontado por slkToDelete. Verifica se o nó se encontra no
inicio, meio ou fim p/ tratamento especifico. Função chama a aplicação
especifica da lista para apagar o dado
|
BOOL DeleteNode( SLIST *L, SLINK slkToDelete )
{
SLINK slkPn;
if
(slkToDelete == NULL) // certifica-se de ser um noh valido
return
FALSE; // antes de tentar apagar qualquer coisa
if
(slkToDelete->prior == NULL)// se encontra no
inicio da lista (cabeca)
{
LLHead = slkToDelete->next;// atualiza o inicio
LLHead->prior = NULL; // o anterior
do inicio deve ser NULO
}
else if (slkToDelete->next == NULL) // se encontra no fim da lista (cauda)
{
LLTail = slkToDelete->prior;// atualiza o fim
LLTail->next = NULL; // o
seguinte do fim deve ser NULO
}
else // se encontra no meio da lista
{
slkPn = slkToDelete->prior; // vai para o noh anterior ao da exclusaoh
slkPn->next =
slkToDelete->next;// atualiza para o proximo
slkPn = slkToDelete->next; // vai para o noh
seguinte ao da exclusaoh
slkPn->prior =
slkToDelete->prior;// e atualiza para o anterior
}
DeleteData(slkToDelete->pdata); // apaga o
dado do noh
free(slkToDelete); //
libera a memoria do noh
--NodeCount; // subtrai o noh do total
return TRUE;
}
|
Função
|
FindNode
|
Propósito
|
Pesquisa a lista inteira obs.:por ser NÃO ordenada
|
Parâmetros
|
SLIST *L -> estrutura com as variáveis de controle e função de comparação
void *nd -> nó a ser encontrado
|
Retorno
|
slkPCurr - aponta para o nó desejado na lista
NULL - não encontrou o nó
|
Descrição
|
Encontra um nó, iniciando a busca no inicio da lista, avançando em
cada nó e comparando os itens do dado com a chave de pesquisa. Precisa
percorrer toda a lista, por se tratar de uma NÃO ordenada
|
SLINK FindNode( SLIST *L, void
*nd )
{
SLINK
slkPCurr; // o noh que estamos examinando
if
(LLHead == NULL) { // lista vazia ?
return
(NULL); //
retornando indicando NULO como falha
}
// posiciona
slkPCurr no inicio da lista
// enquanto for
valido, avancah p/ o seguinte
for
(slkPCurr = LLHead; slkPCurr != NULL; slkPCurr = slkPCurr->next)
{
// compara,
se igual
if
(NodeDataCmp(nd, slkPCurr->pdata) == 0)
{
return
(slkPCurr);// retorna o ponteiro p/ a posicaoh da
lista
}
}
// naoh
consigou encontrar o noh
return
(NULL);
}
|
Função
|
FindNodeAscend
|
Propósito
|
Pesquisa a lista ordenada.
|
Parâmetros
|
SLIST *L -> estrutura com as variáveis de controle e função de comparação
void *nd -> nó a ser encontrado
|
Retorno
|
slkPCurr - aponta para o nó desejado na lista
NULL - não encontrou o nó
|
Descrição
|
Encontra um nó, iniciando a busca no inicio da lista, avançando em
cada nó e comparando os itens do dado com a chave de pesquisa ENQUANTO não
for maior que o desejado, por estar previamente ordenada, não pode se
encontrar no restante da fila
|
SLINK FindNodeAscend( SLIST *L, void
*nd )
{
SLINK
slkPCurr; // o noh que estamos examinando
int iCmpResult; // resultado
da comparacao
if
(LLHead == NULL) { // lista vazia ?
return
(NULL); //
retornando indicando NULO como falha
}
// posiciona
slkPCurr no inicio da lista
// enquanto for
valido, avancah p/ o seguinte
for
(slkPCurr = LLHead; slkPCurr != NULL; slkPCurr = slkPCurr->next)
{
// salva o
resultado, para poder fazer duas analises posteriores
iCmpResult = NodeDataCmp(nd,
slkPCurr->pdata);
// compara,
se o noh pesquisado eh maior que a posicaoh atual da lista
if
(iCmpResult < 0)
{
// indica falha, pois
naoh pode estar no restante da fila
return
(NULL);
}
// compara,
se igual
if
(iCmpResult == 0)
{
return
(slkPCurr);// retorna o ponteiro p/ a posicaoh da
lista
}
}
// naoh
consigou encontrar o noh em toda lista
return
(NULL);
}
|
Função
|
DestroyLList
|
Propósito
|
Apaga a estrutura de gerenciamento de lista ligada e seus nós
|
Parâmetros
|
ponteiro SLIST
pL - ponteiro da estrutura do
tipo lista a ser liberada
|
Retorno
|
TRUE - liberou toda a memória
alocada previamente
FALSE - não conseguiu apagar a lista ligada
|
Descrição
|
Após verificar que se trata de
uma lista valida, percorre a mesma, apagando os nós individualmente, por fim,
libera a memória usada pela própria estrutura de gerenciamento.
|
BOOL
DestroyLList(SLIST *pL)
{
SLINK slk1, slk2; // cursores p/
percorrer as lista
if (pL ==
NULL) // caso naoh exista uma estrutura valida
return
FALSE; //
retorna a indicacaoh de falha
// percorre a
lista apagando todos os nohs
for (slk1 =
pL->slkHead; slk1 != NULL;) {
slk2 = slk1; // faz
um backup da posicaoh
slk1 = slk1->next; // avanca para
o proximo
DeleteNode(pL, slk2); // usa o backup
para apagar o noh
}
free(pL); // libera a
estrutura que gerencia a lista ligada
return
TRUE; //
informa que a lista foi destruida
}
|
Devido a grande extensão do
assunto, o texto foi dividido em duas partes, a próxima postagem trará um
exemplo de aplicação realizando várias operações com as primitivas apresentadas.
Nenhum comentário:
Postar um comentário