Skip to main content
Advanced Data Structures
Advanced Data Structures
IOT
Rx 2h Lecture #1: Dynamic Memory Allocation

Linked Lists

Objectives

  • Understand the concept of a linked list
  • Implement the basic operations
  • Analyze the complexity of operations
  • Distinguish singly and doubly linked lists

Principle

A linked list is a data structure where each element (node) contains:

  • A value (data)
  • A pointer to the next element
[1] -> [2] -> [3] -> [4] -> NULL

Comparison with Arrays

CriterionArrayLinked list
Access by indexO(1)O(n)
Insertion at the frontO(n)O(1)
Insertion at the backO(1)*O(n)**
Deletion at the frontO(n)O(1)
MemoryContiguousFragmented

* Amortized dynamic array ** O(1) with a tail pointer

When to use which?
  • Array: frequent access by index, known fixed size
  • List: frequent insertions/deletions, variable size

Singly Linked List

Structure

typedef struct Node {
    int data;           // Data
    struct Node* next;  // Pointer to the next node
} Node;

typedef Node* List;  // Convenient alias

Creating a Node

Node* create_node(int value) {
    Node* node = (Node*)malloc(sizeof(Node));
    if (node == NULL) {
        exit(EXIT_FAILURE);
    }
    node->data = value;
    node->next = NULL;
    return node;
}

Fundamental Operations

Prepend (O(1)):

List prepend(List list, int value) {
    Node* node = create_node(value);
    node->next = list;  // New node points to the old head
    return node;        // New node becomes the head
}

Append (O(n)):

List append(List list, int value) {
    Node* node = create_node(value);

    if (list == NULL) {
        return node;
    }

    Node* current = list;
    while (current->next != NULL) {
        current = current->next;
    }
    current->next = node;
    return list;
}

Traversal:

void print_list(List list) {
    Node* current = list;
    while (current != NULL) {
        printf("%d -> ", current->data);
        current = current->next;
    }
    printf("NULL\n");
}

Search (O(n)):

Node* find(List list, int value) {
    Node* current = list;
    while (current != NULL) {
        if (current->data == value) {
            return current;
        }
        current = current->next;
    }
    return NULL;  // Not found
}

Memory deallocation:

void free_list(List list) {
    Node* current = list;
    while (current != NULL) {
        Node* next = current->next;
        free(current);
        current = next;
    }
}

Doubly Linked List

Structure

Each node has two pointers: previous and next.

typedef struct DNode {
    int data;
    struct DNode* prev;  // Pointer to the previous node
    struct DNode* next;  // Pointer to the next node
} DNode;

typedef struct {
    DNode* head;  // First element
    DNode* tail;  // Last element
    int size;     // Number of elements
} DoublyLinkedList;
NULL <- [1] <-> [2] <-> [3] <-> [4] -> NULL
         ^                       ^
        head                    tail

Advantages

  • Traversal in both directions
  • Deletion in O(1) if we have a pointer to the node
  • Append in O(1) with the tail pointer

Operations

Push front:

void push_front(DoublyLinkedList* list, int value) {
    DNode* node = create_dnode(value);

    if (list->head == NULL) {
        list->head = list->tail = node;
    } else {
        node->next = list->head;
        list->head->prev = node;
        list->head = node;
    }
    list->size++;
}

Push back:

void push_back(DoublyLinkedList* list, int value) {
    DNode* node = create_dnode(value);

    if (list->tail == NULL) {
        list->head = list->tail = node;
    } else {
        node->prev = list->tail;
        list->tail->next = node;
        list->tail = node;
    }
    list->size++;
}

Operation Complexity

OperationSinglyDoubly
Access to i-th elementO(n)O(n)
Insert at frontO(1)O(1)
Insert at backO(n)O(1)*
Delete at frontO(1)O(1)
Delete at backO(n)O(1)*
SearchO(n)O(n)

* With tail pointer

Common Errors

Pitfalls to avoid
  1. Uninitialized list: always initialize to NULL
  2. Access on NULL: check before node->next
  3. Lost reference: save the pointer before modifying it
  4. Memory leak: free all nodes

Applications

  • Stack and queue implementations
  • History list (web navigation)
  • Memory management (free block list)
  • Sparse representation (polynomials, matrices)