Linked Lists

A linked list is a data structure composed of “nodes”. Each node contains, at the minimum, a piece of data, and a pointer to another node. It is a FIFO structure (First In, First Out), but unlike a stack, some implementations allow the insertion and removal of elements in the middle of the structure rather than requiring all additions and removals to occur at the extremities of the list.

A sample implementation is provided below.

 

#ifndef LINKED_LIST_H // Prevent multiple inclusions
#define LINKED_LIST_H

/*******************************************************************************
 * Preprocessor Directives
 ******************************************************************************/


/*******************************************************************************
 * Macros and Constants
 ******************************************************************************/


/*******************************************************************************
 * Abstract Data Types
 ******************************************************************************/
/* Generic node in a linked list */
typedef struct lnode_t {
   struct lnode_t* next;
   void*    data;
   size_t   width;
} lnode_t;

/* Generic list */
typedef struct llist_t {
   lnode_t* head;
   size_t   length;
} llist_t;

/*******************************************************************************
 * Public Function Prototypes
 *******************************************************************************/
/* Handle compiling C code as part of a C++ project */
#ifdef __cplusplus
extern "C" {
#endif

/* @functionName: llistInit
 * @brief:        Initializes an empty linked list.
 * @param:        list: A pointer to the list.
 */
int llistInit(llist_t* list);

/* @functionName: llistDestroy
 * @brief:        Destroys and cleans up an entire list.
 * @param:        list: A pointer to the list.
 */
int llistDestroy(llist_t* list);

/* @functionName: llistInsert
 * @brief:        Inserts a new node at the beginning of a linked list.
 * @param:        list: A pointer to the list.
 * @param:        ins:  A pointer to the node to insert.
 */
int llistInsert(llist_t* list, lnode_t* ins);

/* @functionName: llistDelete
 * @brief:        Delete an item at a specific position in a list.
 * @param:        list: A pointer to the list.
 * @param:        idx:  The index of the item to delete.
 */
int llistDelete(llist_t* list, int idx);

/* @functionName: llistAppend
 * @brief:        Appends an item to the end of a list.
 * @param:        list: A pointer to the list.
 * @param:        append:  A pointer to the node to add to the list.
 */
int llistAppend(llist_t* list, lnode_t* append);

/* @functionName: llistInsertAfter
 * @brief:        Adds an item to a linked list at a specific index.
 * @param:        list: A pointer to the list.
 * @param:        add:       A pointer to the node to add to the list.
 * @param:        position:  The index at which to add the new node.
 */
int llistInsertAfter(llist_t* list, lnode_t* add, int position);

#ifdef __cplusplus
}
#endif

#endif // LINKED_LIST_H

 

/*******************************************************************************
 * @file:      linked_list.c
 * @author:    Matthew Giassa
 * @email:     matthew@giassa.net
 * @copyright: Matthew Giassa, 2008
 * @brief:     Simple linked list implementation as part of Mmath library for
 *             current research projects in biomedical engineering.
 */

/*******************************************************************************
 * Preprocessor Directives
 ******************************************************************************/
/* System Includes */
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

/* Project Includes */
#include "inc/linked_list.h"


/*******************************************************************************
 * Macros and Constants
 ******************************************************************************/
#define EOK (0)


/*******************************************************************************
 * Abstract Data Types
 ******************************************************************************/


/*******************************************************************************
 * Private Function Prototypes
 ******************************************************************************/
lnode_t* llistCloneNode(lnode_t* node);
lnode_t* llistFreeNode(lnode_t* node);
int      llistDump(llist_t* list);


/*******************************************************************************
 * Function Definitions
 ******************************************************************************/
#if 0
/*----------------------------------------------------------------------------*/
int main(void) {
   llist_t list = { 0 };   /* Need to zero out contents prior to calling init */
   lnode_t* cur;
   int i, ret;

   /* Initialize random seed */
   srand(time(NULL));

   /* Initialize list and populate with a single zeroed out entry */
   if ((ret = llistInit(&list)) != EOK) {
      return ret;
   }

   /* Create template entry for populating simple list */
   const int nEntries = 10;
   cur = calloc(1, sizeof(lnode_t));
   cur->width = sizeof(int);
   cur->data = calloc(1, cur->width);

   /* Populate the list with random entries */
   for (i=0; i<nEntries; i++) {
      *(int*)(cur->data) = rand() % 100;
      llistInsert(&list, cur);
   }

   /* Dump list contents to console */
   llistDump(&list);

   /* Attempt to insert entries throughout the list at corner cases */
   *(int*)(cur->data) = rand() % 100;
   llistInsertAfter(&list, cur, nEntries-1);
   *(int*)(cur->data) = rand() % 100;
   llistInsertAfter(&list, cur, 0);
   *(int*)(cur->data) = rand() % 100;
   llistInsertAfter(&list, cur, nEntries*2);

   /* Dump list contents to console */
   llistDump(&list);

   /* Insert more random entries in random positions */
   for (i=0; i<nEntries; i++) {
      *(int*)(cur->data) = rand() % 100;
      llistInsertAfter(&list, cur, rand() % 100);
   }

   /* Append some dummy entries to the end */
   for (i=0; i<nEntries; i++) {
      *(int*)(cur->data) = 0;
      llistAppend(&list, cur);
   }

   /* Dump list contents to console */
   llistDump(&list);

   /* Try to make some good and bad deletions */
   llistDelete(&list, -1);
   llistDelete(&list, 0);
   llistDelete(&list, 1);
   llistDelete(&list, 100);
   llistDelete(&list, 1);
   llistDelete(&list, 1);
   llistDelete(&list, 1);
   llistDelete(&list, list.length);    /* Expected to fail */
   llistDelete(&list, list.length-1);
   llistDelete(&list, list.length-2);

   /* Dump list contents to console */
   llistDump(&list);

   llistDestroy(&list);

   return EOK;
}
#endif

/*----------------------------------------------------------------------------*/
lnode_t* llistCloneNode(lnode_t* node) {
   if (!node) {
      return NULL;
   }

   lnode_t* tmp;
   if ((tmp = malloc(1*sizeof(lnode_t))) == NULL) {
      return NULL;
   }
   if ((tmp->data = malloc(1*tmp->width)) == NULL) {
      free(tmp);
      return NULL;
   }

   /* Copy contents of node, accounting for dynamically allocated contents */
   tmp->width = node->width;
   tmp->next = node->next;
   memcpy(tmp->data, node->data, node->width);
   return tmp;
}


/*----------------------------------------------------------------------------*/
lnode_t* llistFreeNode(lnode_t* node) {
   if (!node) {
      return NULL;
   }

   free(node->data);
   memset(node, 0, sizeof(lnode_t));
   free(node);
   return NULL;
}


/*----------------------------------------------------------------------------*/
int llistInit(llist_t* list) {
   if (!list) {
      return (-EINVAL);
   }
   int ret = EOK;

   /* Just make sure the provided "head" node pointer points to NULL, so that
    * we aren't erroneously calling init on an already-populated list.
    */
   if (list->head != NULL) {
      ret = (-EIO);
   } else {
      list->length = 0;
   }

   return ret;
}


/*----------------------------------------------------------------------------*/
int llistDestroy(llist_t* list) {
   if (!list) {
      return (-EINVAL);
   }
   lnode_t* cur = list->head;
   lnode_t* next;

   /* Delete first entry in list until empty */
   while (cur) {
      next = cur->next;
      llistDelete(list, 0);
      cur = next;
   }
   list->length = 0;

   return EOK;
}


/*----------------------------------------------------------------------------*/
int llistInsert(llist_t* list, lnode_t* ins) {
   if (!list || !ins) {
      return (-EINVAL);
   }
   lnode_t* tmp;
   if ((tmp = llistCloneNode(ins)) == NULL) {
      return (-ENOMEM);
   }

   if (list->head == NULL) {
      /* Account for empty list */
      list->head = tmp;
      list->head->next = NULL;
   } else {
      /* Insert at beginning of list */
      tmp->next = list->head;
      list->head = tmp;
   }
   list->length++;

   return EOK;
}


/*----------------------------------------------------------------------------*/
int llistAppend(llist_t* list, lnode_t* append) {
   if (!list || !append) {
      return (-EINVAL);
   }
   lnode_t* tmp;
   if ((tmp = llistCloneNode(append)) == NULL) {
      return (-ENOMEM);
   }
   lnode_t* cur = list->head;

   if (list->head == NULL) {
      /* Account for empty list */
      list->head = tmp;
      list->head->next = NULL;
   } else {
      /* Seek end of list */
      while(cur->next != NULL) {
         cur = cur->next;
      }
      /* Append */
      cur->next = tmp;
      cur = tmp;
      cur->next = NULL;
   }
   list->length++;

   return EOK;
}


/*----------------------------------------------------------------------------*/
int llistInsertAfter(llist_t* list, lnode_t* add, int position) {
   if (!list || !list->head || !add || position < 0) {
      return (-EINVAL);
   }
   int i, ret = EOK;
   lnode_t *left, *right, *tmp;
   if ((tmp = llistCloneNode(add)) == NULL) {
      return (-ENOMEM);
   }

   right = list->head;
   for (i=0; i <= position; i++) {
      if (right == NULL) {
         /* Out of bounds; Abort */
         ret = (-EIO);
         break;
      }
      left = right;
      right = right->next;
   }
   if (ret == EOK) {
      left->next = tmp;
      tmp->next = right;
      list->length++;
   }
   return ret;
}


/*----------------------------------------------------------------------------*/
int llistDelete(llist_t* list, int idx) {
   if (!list || idx < 0) {
      return (-EINVAL);
   }
   int i = 0;
   lnode_t* cur = list->head;
   lnode_t* prev;

   /* Find the entry to be removed */
   cur = list->head;
   for (i=0; cur != NULL; i++) {
      if (i == idx) {
         if (cur == list->head) {
            list->head = cur->next;
            llistFreeNode(cur);
            list->length--;
            return EOK;
         } else {
            prev->next = cur->next;
            llistFreeNode(cur);
            list->length--;
            return EOK;
         }
      } else {
         prev = cur;
         cur = cur->next;
      }
   }

   /* Disqualify out-of-bounds requests */
   printf("ERROR, out of bounds request\n");
   return (-EINVAL);;
}


/*----------------------------------------------------------------------------*/
int llistDump(llist_t* list) {
   if (!list) {
      return (-EINVAL);
   }
   int i = 0;
   lnode_t* cur = list->head;

   while (cur) {
      printf("Iteration %d - Size:%zd - Data:%d - Length:%zd\n",
            i, cur->width, *(int*)cur->data, list->length);
      i++;
      cur = cur->next;
   }

   return EOK;
}