CÓDIGO-FONTE: https://github.com/r-bauer/prioQueue/archive/master.zip
Como explicado na postagem anterior(Fila Circular), um problema singular ocorre quando se cria um tipo de dado abstrato como a fila usando vetores, aonde inserir e remover elementos continuamente desloca os dados por toda a memória, como solução utiliza-se um buffer circular, em um segundo caso quando criamos uma fila com lista encadeada, a mesma sofre de uma deficiência ligeiramente diferente em seu formato, continuar criando e apagando nós da lista, acabara por fragmentar a memória com as sucessivas operações de alocar e liberar.
Como explicado na postagem anterior(Fila Circular), um problema singular ocorre quando se cria um tipo de dado abstrato como a fila usando vetores, aonde inserir e remover elementos continuamente desloca os dados por toda a memória, como solução utiliza-se um buffer circular, em um segundo caso quando criamos uma fila com lista encadeada, a mesma sofre de uma deficiência ligeiramente diferente em seu formato, continuar criando e apagando nós da lista, acabara por fragmentar a memória com as sucessivas operações de alocar e liberar.
No caso da fila com lista
encadeada temos duas soluções para o problema:
1.      
Usar funções próprias para alocar e devolver a
memória, esta função iria alocar uma sequencia de nós que seriam usados e
devolvidos.
2.      
Criar uma lista encadeada de nós livres,
conforme os itens foram inseridos, os nós são movidos da lista livre para a
fileira, ao remover, os nós retornam para a lista livre.
Será utilizada a abordagem numero
2, as rotinas de lista encadeada a serem utilizadas, foram introduzidas
anteriormente na postagem lista encadeada genérica parte 1 e parte 2. Para os
arquivos de aplicação qapp.c e qapp.h, teremos a adaptação da lista
ligada para uma fila, semelhante à forma com que se trabalhou com os arquivos llapp.c e llapp.h.
Uma lista ordenada será usada
para representar a fila de prioridade, utilizando uma lista ordenada
ascendente, implica numa alteração do processo de inserção, aonde abandonamos o
FIFO (First In, First Out) para colocar o elemento na posição indicada
para sua prioridade, a rotina insere fica responsável em manter a ordem
enquanto a rotina remove mantém sua simplicidade ao remover o primeiro da fila,
em termos de desempenho o processo de inserção requer uma media de
aproximadamente n/2 nós para inserção
e apenas um nó para eliminação.
| 
typedef struct tagSNODEDATA 
{ 
      char
  *pText;                 // ponteiro para um texto 
      unsigned
  int uiPriority;     // o nivel de prioridade 
}SNODEDATA; | 
A vantagem de uma lista sobre
vetor para programar a fila de prioridade reside no fato de que não é
necessário nenhum deslocamento de dados, um item pode ser inserido numa lista
sem deslocar os demais, enquanto que a mesma operação num vetor fica impossível
se não houver espaços vazios.
A estratégia para o programa se
inicia com a criação de duas estruturas de lista encadeada slQueue e slFreeList no
arquivo qdriver.c.
| 
    SLIST *slQueue, *slFreeList; 
    // configura a
  estrutura de lista ligada para filas 
    slQueue
  = CreateLList(  CreateData1, 
                           
  DeleteData1, 
                            DuplicatedNode,  
                           
  NodeDataCmp1 ); 
    slFreeList =
  CreateLList(   CreateData2, 
                                DeleteData2, 
                                DuplicatedNode,
   
                                NodeDataCmp2 ); | 
Antes de detalhar as rotinas
especificas de cada lista, é importante ressaltar que toda a memória usada será
criada pela lista livre e compartilhada pelas duas listas, o que resulta na
criação de todos os nós pela lista livre no seguinte trecho.
| 
    for (iCount
  = 0; iCount < QMAX; ++iCount) 
    { 
       
  AddNodeAtHead(slFreeList, cRecord); 
    } | 
A consequência desta lógica acaba
com as rotinas CreateData1 e DeleteData1 jamais sendo executadas no código,
com suas declarações sendo vazias no arquivo qapp.c.
| 
void
  *CreateData1(void *data) 
{ 
    return (NULL); 
} 
BOOL  DeleteData1(void *data) 
{ 
    return TRUE; 
} | 
O mesmo acontece para tratamento de
elementos duplicados , apesar de incluir a rotina DuplicateNode, a mesma não é chamada por nenhuma das duas listas,
mantendo sua declaração apenas com fim de compatibilidade, ambas as listas
compartilham a mesma rotina.
| 
int   DuplicatedNode(SLINK slkNewNode, SLINK
  slkListNode) 
{ 
    return 2; 
} | 
A única operação especifica para
a lista slQueue será a comparação dos
nós através do valor de prioridade para posicionar na fila.
| 
int   NodeDataCmp1(void
  *first, void *second) 
{ 
    return (((pND)first)->uiPriority -
  ((pND)second)->uiPriority); 
} | 
Como a lista slFreeList fica responsável por gerar e apagar o nós, CreateData2 e DeleteData2 serão as únicas rotinas com a tarefa de alocar e
liberar.
| 
void
  *CreateData2(void *data) 
{ 
    SNODEDATA
  *pNewData; 
    // aloca sua estrutura de dados 
    pNewData = (SNODEDATA *) malloc(sizeof(SNODEDATA)); 
    // move os
  valores para a estrutura de dados 
    pNewData->uiPriority
  = 0; 
   
  pNewData->pText = (char *)
  malloc(TEXT_SIZE + 1); 
    *pNewData->pText
  = '\0'; 
    return
  (pNewData);     // retorna o endereco da estrutura 
} 
BOOL  DeleteData2(void
  *data) 
{ 
    // SNODEDATA
  consiste em: um ponteiro e um inteiro. 
    // Sendo nescessario liberar a string manualmente 
    free( ((pND) data)->pText); 
    // e somente agora libera SNODEDATA 
    free(  data ); 
} | 
Com a adaptação das rotinas especificas
de lista encadeada para uso com a fila de prioridade, monta-se o controlador de
fila, com as rotinas para inserir e remover elementos da fila.
| 
Função | 
InsertQueue | 
| 
Propósito | 
Armazenar um dado baseado em sua prioridade | 
| 
Parâmetros | 
SLIST *slQueue - lista ligada usada para fila de prioridade 
SLIST *slFree - lista com as vagas livres a serem inseridas 
void *pNewEntry - a nova entrada a ser guardada na fila | 
| 
Retorno | 
TRUE - dado armazenado 
FALSE - não foi possível armazenar o dado | 
| 
Descrição | 
Enfileira os dados passados no inicio da lista livre, e depois
  adiciona o nó na lista baseada na prioridade. | 
| 
BOOL InsertQueue(SLIST *slQueue, SLIST *slFree, void *pNewEntry) 
{ 
    SLINK slkCurr, slkNewNode; 
    // existe algum
  noh livre na lista 
    if
  (slFree->uiCount == 0) 
        return (FALSE); 
    // usa como novo noh, o inicio da lista livre 
    slkNewNode
  = slFree->slkHead; 
    DataCopy(slkNewNode->pdata,
  pNewEntry); 
    // adiciona os dados numa lista vazia 
    if
  (slQueue->uiCount == 0) 
    { 
        //
  encontrou uma vaga, corrige a lista livre 
        // atualiza
  a o inicio da lista para o seguinte 
        slFree->slkHead = slFree->slkHead->next; 
        // ponteiros do noh = NULO por ser o primeira insercaoh 
        slkNewNode->prior = NULL; 
       
  slkNewNode->next = NULL; 
        // indica que o novo noh faz parte da lista de prioridade 
        slQueue->slkTail = slkNewNode; 
        slQueue->slkHead = slkNewNode; 
        slQueue->uiCount = 1;// inicia o contador da lista  
        --slFree->uiCount;   // retira uma
  vaga da lista livre 
        return
  (TRUE); 
    } 
    // jah existem
  dados na lista  
    else 
    { 
        //´percorre
  a lista para encontrar a posicaoh de insercaoh 
        for (slkCurr
  = slQueue->slkHead; ; slkCurr = slkCurr->next) 
        { 
            if
  ((slkCurr == NULL) || // se for fim da fila 
           // ou dentro dele 
          
  (slQueue->fNodeDataCmp(pNewEntry, slkCurr->pdata) < 0))  
            { 
                //
  encontrou uma vaga, corrige a lista livre 
                //
  atualiza a o inicio da lista para o seguinte 
                slFree->slkHead = slFree->slkHead->next; 
                // eh o fim da lista ? 
                if
  (slkCurr == NULL) { 
                    // novo noh
  aponta para o fim da fila 
                    slkNewNode->prior =
  slQueue->slkTail;    
                    // naoh tem
  para quem apontar  
                    slkNewNode->next =
  NULL; 
                    // o seguinte
  do fim aponta para o novo noh 
                   
  slQueue->slkTail->next = slkNewNode;     
                    //
  indica o novo fim da lista 
                    slQueue->slkTail =
  slkNewNode; 
                } 
                else
  { 
                    // estah
  adicionando no inicio da lista ? 
                    if
  (slkCurr->prior == NULL) 
                        //
  atualiza o inicio da lista 
                        slQueue->slkHead =
  slkNewNode;       
                    // atualiza os
  ponteiro do novo noh 
                    slkNewNode->prior =
  slkCurr->prior;        
                    // colocando
  ele entre o atual e anterior 
                    slkNewNode->next =
  slkCurr;              
                    // naoh estah
  adicionando no inicio 
                    if
  (slkCurr->prior != NULL) 
                        //
  anterior p/ referenciar o novo noh 
                       
  slkCurr->prior->next = slkNewNode;    
                    // ajusta o
  atual p/ referenciar o novo noh 
                    slkCurr->prior =
  slkNewNode; 
                } 
                //
  atualiza a contagem das lista 
                //
  adiciona um noh na lista de prioridade 
                ++slQueue->uiCount; 
                // subtrai
  um noh da lista de livres 
                --slFree->uiCount; 
                return
  (TRUE); 
            } 
        } 
    } 
} | 
| 
Função | 
RemoveQueue | 
| 
Propósito | 
Retira o dado de uma fila | 
| 
Parâmetros | 
SLIST *slQueue - lista ligada usada para fila de prioridade 
SLIST *slFree - lista com as vagas livres a serem inseridas 
 void *pOurData - o dado
  retirado da fila | 
| 
Retorno | 
TRUE - recuperou a informação da fila 
FALSE - Fila vazia ou erro ao copiar dado para buffer | 
| 
Descrição | 
Para remover, usa um ponteiro para o nó no inicio da fila. Move esse nó
  inicial retirado da fila e o insere na lista de nó livres. Obs.: Se não os
  dados não forem usados antes que se execute uma nova operação p/ enfileirar
  (insertQueue), os dados serão perdidos, portanto copie se for necessário. | 
| 
BOOL RemoveQueue(SLIST *slQueue, SLIST *slFree, void *pOurData) 
{ 
    SLINK slkRemove; 
    // existe algum
  dado p/ desinfilerar ? 
    if
  (slQueue->uiCount == 0) 
        return (FALSE); 
    // faz a copia dos dados desenfilerados 
    DataCopy(pOurData,
  slQueue->slkHead->pdata); 
    // salva a
  posicaoh do dado a ser retirado da fila 
    slkRemove = slQueue->slkHead; 
    // remove o noh
  da fila de prioridade 
    // atualizando o inicio p/ o
  seguinte da lista 
    slQueue->slkHead =
  slQueue->slkHead->next;   
    // se diferente de nulo, tem elemento armazenados na fila 
    if
  (slQueue->slkHead != NULL) 
        // eliminando a referencia
  de um previo 
        slQueue->slkHead->prior =
  NULL;           
    // se o inicio
  for nulo, fila foi esvaziada, atualiza o fim  
    else 
        // naoh existe dados
  armazenados 
        slQueue->slkTail = NULL;                 
    --slQueue->uiCount; // ajusta a contagem de itens na fila 
    // e adiciona
  no inicio da lista de livres 
    slkRemove->prior = NULL;          // aponta
  o anterior p/ NULO 
    // e o seguinte para o inicio
  da lista de livres 
    slkRemove->next = slFree->slkHead; 
    // e atualiza o inicio da
  lista de nohs livres 
    slFree->slkHead = slkRemove;       
    ++slFree->uiCount;  // avisa que tem
  + 1 noh livre 
    return
  (TRUE); 
} | 
Para testarmos o resultado, a
função QDriver servirá de exemplo de
tratamento para fila de prioridade, irá abrir para leitura um arquivo de dados
consistindo em linhas de texto no formato X9MESSAGE, aonde:
| 
X | 
I – para inserir na fila 
R – remover 
L – listar a fila | 
| 
9 | 
Prioridade do item da fila | 
| 
MESSAGE | 
Texto a ser inserido | 
Obs.: A ações D e P não tem
prioridade ou texto.
O arquivo texto com dados deverá ter
o seguinte padrão:
| 
I8Lacuna Coil 
I4Anthrax 
R 
L 
R 
I1Various 
I9White
  Zombie 
I2John
  Williams 
I4Killswitch
  Engage 
L 
I4Ayreon | 
O código simplificado de teste
para abrir o arquivo e executar as ações que encontrar em cada linha segue
abaixo.
| 
fin = fopen(argv[1], "rt"); 
pTemp = CreateData2(NULL); 
while
  (fgets(cRecord, sizeof(cRecord) - 1, fin) !=
  NULL) 
{ 
      if (*cRecord == 'I') 
      { 
            ((pND)
  pTemp)->uiPriority = *(cRecord + 1) - '0'; 
            ((pND)
  pTemp)->pText = cRecord + 2; 
            InsertQueue(slQueue,
  slFreeList, pTemp); 
      } 
      else if (*cRecord
  == 'R') 
      { 
            RemoveQueue(slQueue,
  slFreeList, pTemp); 
      } 
      else if (*cRecord
  == 'L') 
      { 
            SLINK
  slkCurr; 
            printf("\n- Fila -\n"); 
            for ( slkCurr
  = slQueue->slkHead;  
                  slkCurr
  != NULL;  
                  slkCurr
  = slkCurr->next) 
            { 
                  printf("%d %s\n",  
                        ((pND)
  slkCurr->pdata)->uiPriority,  
                        ((pND)
  slkCurr->pdata)->pText); 
            } 
            printf("\n"); 
      } 
} 
fclose(fin); | 
Os arquivos deste projeto se
encontram no github: https://github.com/r-bauer/prioQueue, o
arquivo prio.txt deve ser criado para ser usado no teste como indicado acima, ao rodar o programa terá como
resultado:  
| 
C:\Algorit\Projects\pqueue\Debug>pqueue
  PRIO.TXT 
        Enfilerou (1 Bryan Adams) 
        Enfilerou (4 KC and the Sunshine
  Band) 
        Desenfilerou (1 Bryan Adams) 
----------- Fila ateh o momento ----------- 
4 KC and the Sunshine
  Band 
        Enfilerou (2 Meat Loaf) 
        Enfilerou (1 R.E.M) 
        Enfilerou (9 Sugar
  Ray) 
        Desenfilerou (1
  R.E.M) 
        Desenfilerou (2
  Meat Loaf) 
----------- Fila ateh o
  momento ----------- 
4 KC and the Sunshine
  Band 
9 Sugar Ray 
        Enfilerou (7 The Mission) 
        Enfilerou (4 Temple of the Dog) 
        Enfilerou (3 Pantera) 
        Enfilerou (1
  Ministry) 
        Enfilerou (1 Kelly
  Clarkson) 
        Desenfilerou (1
  Ministry) 
        Desenfilerou (1
  Kelly Clarkson) 
----------- Fila ateh o momento ----------- 
3 Pantera 
4 KC and the Sunshine
  Band 
4 Temple of the Dog 
7 The Mission 
9 Sugar Ray | 
