Unit 2: Linked Lists and Queues

Table of Contents

1. Linked Lists

A Linked List is a linear data structure, like an array, but its elements are not stored in contiguous memory locations. Instead, elements are linked using pointers.

Each element in a linked list is called a node. A node consists of two parts:

  1. Data: The actual value stored in the node.
  2. Next (Pointer/Link): A pointer that stores the memory address of the next node in the sequence.

The entry point to the linked list is a special pointer called the head, which points to the first node. The last node in the list has its next pointer set to NULL.

Arrays vs. Linked Lists

Feature Array Linked List
Memory Contiguous memory allocation. Non-contiguous (dynamic) allocation.
Size Static (fixed size). Must be known at compile time. Dynamic (size can grow and shrink at runtime).
Access Fast random access (O(1)). Can access arr[i] directly. Slow sequential access (O(n)). Must traverse from the head.
Insertion/Deletion Slow (O(n)). Requires shifting elements. Fast (O(1)), if the position (node) is known.
Memory Overhead None. Extra memory is required for the next pointer in each node.

Singly Linked Lists

This is the standard linked list described above, where each node has one pointer (next) pointing to the next node in the list. Traversal is one-way (forward) only.

Basic Operations:

Common Mistake: Losing the list. When inserting at the beginning, always make the new node point to the current head before you update head to point to the new node.

Doubly Linked Lists

A doubly linked list (DLL) node contains two pointers in addition to the data:

  1. Data: The value.
  2. Next: Points to the next node (or NULL).
  3. Prev: Points to the previous node (or NULL for the head).

Advantages over Singly Linked List:

Disadvantage:

Circular Lists

A circular linked list is a list where the last node does not point to NULL. Instead, its next pointer points back to the head node, forming a circle.

Advantages:

A Circular Doubly Linked List also exists, where the head.prev points to the last node, and the last.next points to the head.

Array and Linked Representation

This refers to the comparison of implementing linear lists using arrays versus linked lists, as detailed in the table above.

Normal and Circular Representation of Stack in Lists

As discussed in Unit 1, the array implementation of a stack has a fixed-size limitation. A linked list is a perfect solution for this.

Normal Linked List Stack

A stack is implemented using a standard singly linked list. The LIFO behavior is achieved by performing push and pop operations at the beginning (head) of the list.

This implementation is very efficient and overcomes the overflow problem (it's only limited by the total system memory).

Circular List Stack

While possible, using a circular list to implement a stack is less common. One could maintain a single pointer last (pointing to the last node). The "top" of the stack would be last.next (the head). Pushing and popping would still happen at the "head" (last.next), which remains an O(1) operation.

Self Organizing Lists

A self-organizing list is a linked list that reorders its nodes based on access patterns to improve average search time. The goal is to keep frequently accessed elements near the front of the list.

Common heuristics:

  1. Move-to-Front (MTF): When a node is accessed, it is moved to the very front (head) of the list.
  2. Transpose (or Swap): When a node is accessed, it is swapped with the node immediately *before* it.

Skip Lists

A skip list is a probabilistic data structure that provides efficient search (average O(log n)) in a sorted list. It's essentially a linked list with "express lanes."

It's built in layers. The bottom layer (Level 0) is a regular sorted linked list. Each higher layer (Level 1, Level 2, ...) contains a subset of the nodes from the layer below. A node in Level i has a random probability of also appearing in Level i+1.

To search, you start at the highest layer. You traverse forward until you find a node greater than your target. You then drop down one layer and continue. This "skipping" allows you to bypass large portions of the list, achieving logarithmic time complexity, similar to a balanced binary search tree.

Exam Tip: Skip lists are an important alternative to balanced trees (like AVL or Red-Black trees) because they are often simpler to implement and can perform just as well.

2. Queues

A Queue is a linear data structure that follows the FIFO (First-In, First-Out) principle. This means the element added first is the first one to be removed. Think of it as a line of people waiting for a ticket.

The primary operations are:

A queue is managed with two pointers: front (points to the first element) and rear (points to the last element).

Array Representation of Queue

A simple 1D array can be used, but it's inefficient. If you dequeue, the front pointer moves, and that space is "lost." You would have to shift all elements (O(n)), which is slow.

A much better solution is a Circular Array (or Circular Queue).

Linked Representation of Queue

This is a more flexible implementation that avoids the fixed-size limit. A singly linked list is used with two external pointers: front and rear.

Common Mistake: When the queue becomes empty after a dequeue, you must remember to also set the rear pointer to NULL (not just front).

De-queue (Deque)

A Deque (pronounced "deck"), or Double-Ended Queue, is a generalization of a queue that allows insertion and deletion from both ends (front and rear).

Operations:

It can be implemented using either a circular array or a doubly linked list.

Priority Queues

A Priority Queue is an abstract data type where each element has an associated priority. The operations are:

This is different from a normal queue; the "first-in" element is not necessarily the "first-out."

Implementations: