Linked list em C
Pretendo implementar alguns algoritmos de grafos no futuro e pensei que seria interessante implementar as estruturas de dados necessárias. Não tenho nenhum compromisso com a performance ou elegância do código, a implementação é a mais simples possível.
Caso queira ver uma boa implementação de linked list, essa é bem interessante.
Começando
Antes de implementar de fato, vamos pensar em como uma lista ligada funciona.
Definição: É um conjunto de itens onde cada item é parte de um nó. Cada nó também contém um link para o pŕóximo elemento.
Quer que eu desenhe?
Na imagem x é um nó e a seta indica um link para o elemento t.
Estruturas
Já temos informação o suficiente para começar a implementação.
Essas linhas de código expressam exatamente a definição dada anteriormente. Um link é um ponteiro para um nó, e um nó é formado por itens e links. Nesse caso eu resolvi usar 2 bytes sem sinal para representar o item.
Em seguida precisamos da representação da lista ligada em sí:
Head (cabeça) é um ponteiro para o primeiro elemento da lista ligada e tail (calda) é um ponteiro para o ultimo elemento. Size representa o tamanho da lista ligada.
Cabeçalho
A interface é a seguinte:
- Create: Cria uma lista ligada vazia;
- Destroy: Libera todos os nós e a lista ligada;
- Prepend: Adiciona um elemento no inicio da lista;
- Shift: Remove o primeiro elemento da lista e retorna seu valor;
- DeleteItem: Procura o item na lista e remove (considerando que nenhum elemento tem o mesmo valor, obviamente);
- Show: Mostra no terminal a lista;
- IsEmpty: Se a lista está vazia retorna falso, caso contrário verdadeiro;
- GetSize: Consulta o tamanho da lista.
A implementação tenta usar o conceito de encapsulamento. O usuário só tem acesso ao tipo da lista (llist) e seus métodos públicos. A biblioteca não tem nenhum método privado mas tem um tipo privado, o nó.
Criando e destruindo
Criar é bastante simples. Aloca a memória necessária, seta tudo pra zero/nulo e retorna a lista para o usuário utilizar. É, eu sei que malloc pode retornar nulo se não tiver memória o suficiente, mas não importa muito agora.
Agora vem o apocalipse. A destruição também é bem direta: enquanto a lista existe, libere a memória dos nós e vá para o próximo. No final, libere a memória da lista. Eu utilizei ali um link p para percorrer a lista, e um link auxiliar para guardar a referência para o próximo nó enquanto eu libero a memória do nó atual.
Inserindo coisas
Vamos considerar no meio da lista. O nó aponta primeiro pro elemento que vai ficar a sua frente:
O elemento que vai ficar atrás aponta para o novo nó:
Tcharam
Eu não fiz a implementação de uma inserção em qualquer posição da lista porque não vou precisar para implementar um grafo. Mas eu preciso inserir no começo.
No começo
Retirando coisas
Queremos retirar o elemento do meio:
Easy, só o nó anterior a ele apontar para o próximo.
Quase como um passo de mágica, não?
No começo
Retirar do começo é bem simples, o mais importante é lembrar dos edge cases.
- Se a lista já tiver vazia? retorna 0; (considerando que 0 não é um valor válido para a lista, só valores > 0)
- Resgata o item antes de apagar o nó;
- E se só tiver 1 elemento na lista? Esvazia a lista colocando para null;
- Se for qualquer outra situação, aponta a cabeça para o segundo elemento da lista.
- Retorna o item para o usuário caso ele precise.
Um item específico
Como eu disse acima, um item tem de ser único dentro da lista. Nada impede isso na verdade, vai ser um acordo de cavalheiro.
Essa função ficou complicada, mas está funcionando, é isso que importa.
Considerações finais
Os outros três métodos (Show, GetSize e IsEmpty) são bem triviais. Acho que com essa estrutura eu consigo implementar uma lista de adjacências para representar um grafo no futuro :D. Tentarei escrever um post sobre isso também.
Nesse projeto eu utilizei Ceedling, uma ferramenta para buildar e testar projetos em C. Foi a primeira vez que usei e funcionou perfeitamente. Fica a dica para quem escreve C para sistemas embarcados: começar a escrever “código em C que não é uma merda” haha.
Esse código pode ser encontrado aqui.