ResourcesComputer ScienceArrays & Linked Lists
Computer ScienceCollege

Arrays & Linked Lists

Arrays and linked lists are two of the most fundamental linear data structures in computer science. An array stores elements in contiguous memory locations with O(1) random access; a linked list connects scattered nodes via pointers with O(1) insertion and deletion.

This guide covers array fundamentals, linked list types (singly, doubly, circular), operations complexity comparison, memory layout, advanced topics like cycle detection and sentinel nodes, code walkthroughs, and a 10-question practice quiz.

Array vs Linked List: Insertion Comparison

>Initial State

Array (Contiguous Memory)

[0]1
[1]2
[2]3
[3]4
[4]5
contiguous addresses

Contiguous memory block

Linked List (Pointer-Linked)

1
2
2
3
3
4
4
5
5
null

Pointer-linked nodes

Both structures store the same sequence: [1, 2, 3, 4, 5]. Notice how array elements sit in contiguous memory while linked list nodes are connected by pointers.
Step 0 / 5
Arrays store elements contiguously and require shifting for insertion. Linked lists use pointers — insertion only requires updating references.

1Introduction

Data structures are fundamental building blocks in computer science, providing efficient ways to organize and store data. Among the most basic yet powerful linear data structures are arrays and linked lists. While both store collections of elements in a sequential manner, their underlying memory models, performance characteristics, and ideal use cases differ significantly.

Understanding these two structures is essential because they underpin nearly every higher-level data structure. Stacks, queues, hash tables, and even graphs are typically implemented on top of arrays or linked lists. Choosing the right structure for a given problem is a core skill tested in technical interviews and university exams alike.

In Practice

In real-world systems, the choice between arrays and linked lists can have dramatic performance implications. Database engines use arrays (B-trees) for cache-efficient index lookups. Operating system schedulers use linked lists for process queues where frequent insertion and removal are critical. Understanding the trade-offs lets you make informed architectural decisions.

Side-by-side comparison diagram of array contiguous memory layout versus linked list pointer-connected nodes

2Key Definitions

Essential terms for understanding arrays and linked lists at the university level.

Data Structure

A way of organizing and storing data for efficient access and modification

Abstract Data Type (ADT)

A mathematical model specifying operations without implementation details (e.g., List, Stack)

Array

Elements stored at contiguous memory locations, accessed by integer index

Linked List

Elements (nodes) linked by pointers, stored at non-contiguous memory locations

Node

Building block of a linked list containing data and pointer(s) to neighboring nodes

Head / Tail

First and last nodes of a linked list; head is the entry point for traversal

Contiguous Memory

Sequential, adjacent memory addresses; arrays require contiguous allocation

Random Access

Ability to access any element directly in O(1) time using an index

Sequential Access

Accessing elements by traversing from the beginning one by one; characteristic of linked lists

Pointer / Reference

Variable storing a memory address; the “glue” connecting linked list nodes

Dynamic Memory Allocation

Allocating memory at runtime (not compile time); linked lists use this extensively

Null Pointer

Special pointer value indicating no valid memory address; marks end of a linked list

Index / Subscript

Integer identifying an element's position in an array; typically zero-based

Traversal

Visiting each element in a data structure exactly once to perform some operation

Sentinel Node

A dummy node used to simplify edge cases in linked list operations

Cache Locality

Tendency for contiguous data to be loaded into CPU cache together; arrays benefit from this

3Array Fundamentals

An array is a collection of elements of the same data type stored in contiguous memory locations, accessed via an integer index. The address of any element can be calculated directly: address(arr[i]) = base_address + i * sizeof(element).

Key Characteristics

Advantages

  • O(1) random access via index calculation
  • Cache-friendly due to contiguous memory (spatial locality)
  • Memory efficient for data itself (no pointer overhead)
  • Simple, predictable memory layout

Disadvantages

  • Fixed size (resize requires O(n) copy)
  • O(n) insertion/deletion in the middle (shifting)
  • Potential wasted space if partially filled
  • Requires large contiguous memory block

Address Calculation

How O(1) Access Works

Base address:  0x1000

Element size:  4 bytes (int)

arr[3] address: 0x1000 + 3 * 4 = 0x100C

Direct arithmetic means any element is accessible in constant time, regardless of array size.

Array insertion process showing element shifting to make room for a new element

Types of Arrays

One-dimensional arrays store a simple sequence of elements. Multi-dimensional arrays (2D matrices, 3D tensors) are arrays of arrays, used for tables, images, and scientific computing. Dynamic arrays (Python list, Java ArrayList, C++ vector) automatically resize by allocating a larger block and copying elements, providing amortized O(1) append.

4Linked List Types

A linked list stores elements in nodes scattered throughout memory, connected by pointers. Unlike arrays, linked lists are dynamic in size and excel at insertions and deletions.

Linked list node structure showing data field and next pointer connecting nodes

Singly Linked List

Each node contains data and a next pointer. Traversal is forward-only. The last node's next pointer is null.

Head → [10|next] → [20|next] → [30|null]

Doubly Linked List

Each node contains prev pointer, data, and next pointer. Allows bidirectional traversal. More memory per node but O(1) deletion of a known node.

null ← [prev|10|next] ↔ [prev|20|next] ↔ [prev|30|next] → null

Circular Linked List

The last node's next pointer points back to the head, forming a circle. Can be singly or doubly linked. Useful for round-robin scheduling and circular buffers.

Head → [10|next] → [20|next] → [30|next] → (back to Head)

Care must be taken to avoid infinite loops during traversal.

Advantages and Disadvantages

Advantages

  • Dynamic size — grows and shrinks at runtime
  • O(1) insertion/deletion if position is known
  • No wasted pre-allocated space
  • Efficient for frequent middle modifications

Disadvantages

  • O(n) sequential access — no random access
  • Extra memory for pointers in each node
  • Poor cache locality — scattered memory
  • Reverse traversal hard (singly linked)

5Operations Comparison

The fundamental trade-off: arrays provide O(1) access but O(n) insertion/deletion; linked lists provide O(1) insertion/deletion (at known positions) but O(n) access.

OperationArraySingly LLDoubly LL
Access (by index)O(1)O(n)O(n)
Search (by value)O(n)O(n)O(n)
Insert at headO(n)O(1)O(1)
Insert at tailO(1)*O(n)†O(1)‡
Insert at middleO(n)O(n)O(n)
Delete at headO(n)O(1)O(1)
Delete at tailO(1)O(n)O(1)‡
Delete at middleO(n)O(n)O(n)

* Amortized O(1) for dynamic arrays    † O(1) if tail pointer maintained    ‡ With tail pointer

Time complexity comparison chart for array and linked list operations

6Advanced Topics

Floyd's Cycle Detection (Tortoise & Hare)

A cycle occurs when a node's next pointer references an earlier node. Floyd's algorithm uses two pointers: slow (moves 1 step) and fast (moves 2 steps). If they meet, a cycle exists. Time: O(n), Space: O(1).

Sentinel (Dummy) Nodes

A sentinel node is a dummy placeholder (often at the head) that eliminates special-case handling for empty lists and head operations. Code becomes simpler and less error-prone since you never need to check if head == null.

Cache Performance & Spatial Locality

Array: Excellent Locality

Contiguous elements are loaded into CPU cache together. Iterating through an array hits the L1/L2 cache frequently, making traversal very fast in practice.

Linked List: Poor Locality

Nodes scattered in memory cause frequent cache misses. Each pointer dereference may require loading a new cache line from main memory, significantly slowing traversal.

Linked list pointer manipulation during insertion and deletion operations

Dynamic Arrays: Bridging the Gap

Dynamic arrays (Python list, Java ArrayList, C++ vector) combine array benefits with resizing capability. When capacity is exceeded, a new array (typically 2x size) is allocated and elements are copied. Append is amortized O(1): most appends are O(1), with occasional O(n) resizes averaged across all operations.

7Code Walkthroughs

Introductory

Array Access by Index

arr = [10, 20, 30, 40]
print(arr[2])  # Output: 30

Line 1: Declares an array with 4 elements stored in contiguous memory.

Line 2: Accesses element at index 2 in O(1) time using address calculation: base + 2 * sizeof(int).

Time: O(1)Space: O(1)

Key insight: Arrays provide O(1) random access by index through direct address arithmetic.

Introductory

Linked List Traversal

def traverse(head):
    curr = head
    while curr:
        print(curr.val)
        curr = curr.next

Line 2: Start at the head node — the only entry point to the list.

Lines 3–4: Visit each node, printing its value. The while curr check stops at null (end of list).

Line 5: Follow the next pointer to advance to the subsequent node.

Time: O(n)Space: O(1)

Key insight: Linked lists require sequential traversal — no random access is possible.

Intermediate

Insert Element into Array

def insert(arr, idx, val):
    # Shift elements right from end to idx
    for i in range(len(arr)-1, idx-1, -1):
        arr[i+1] = arr[i]
    arr[idx] = val
    return arr

Line 2: Elements must be shifted right to make room — this is why array insertion is O(n).

Line 3: Iterate backwards from the last element to the insertion index.

Line 5: Place the new value at the target index after all shifts are complete.

Time: O(n)Space: O(1)

Key insight: Array insertion requires shifting all subsequent elements, making it O(n) in the worst case.

Intermediate

Reverse a Singly Linked List

def reverse(head):
    prev, curr = None, head
    while curr:
        next_node = curr.next  # Save next
        curr.next = prev       # Reverse link
        prev = curr            # Advance prev
        curr = next_node       # Advance curr
    return prev

Line 2: Initialize prev to None (new tail) and curr to head.

Line 4: Save curr.next before overwriting it — otherwise we lose the rest of the list.

Line 5: Point current node backward to prev — this reverses the link direction.

Line 8: After the loop, prev points to what was the last node — the new head.

Time: O(n)Space: O(1)

Key insight: Reversing a linked list requires a single pass with three pointers — prev, curr, and next.

Advanced

Merge Two Sorted Linked Lists

def merge(l1, l2):
    dummy = ListNode(0)
    curr = dummy
    while l1 and l2:
        if l1.val <= l2.val:
            curr.next = l1
            l1 = l1.next
        else:
            curr.next = l2
            l2 = l2.next
        curr = curr.next
    curr.next = l1 or l2
    return dummy.next

Line 2: A dummy/sentinel node simplifies the merge by providing a stable starting point.

Lines 5–10: Compare the heads of both lists, attach the smaller node, and advance that list's pointer.

Line 12: After one list is exhausted, attach the remainder of the other list directly.

Time: O(n+m)Space: O(1)

Key insight: Merging two sorted lists takes linear time and constant extra space using a dummy node pattern.

Advanced

Detect Cycle in Linked List (Floyd's Algorithm)

def hasCycle(head):
    slow = fast = head
    while fast and fast.next:
        slow = slow.next        # Move 1 step
        fast = fast.next.next   # Move 2 steps
        if slow == fast:
            return True
    return False

Line 2: Initialize two pointers at the head — slow (tortoise) and fast (hare).

Lines 4–5: Slow advances one node, fast advances two nodes per iteration.

Line 6: If they meet, a cycle exists. If fast reaches null, there is no cycle.

Time: O(n)Space: O(1)

Key insight: Floyd's tortoise and hare algorithm detects cycles in O(n) time with O(1) space.

8Memory Aids

Arrays = Apartment Building

“Fixed number of rooms, all next to each other. You know exactly which room by its number. Adding a room in the middle means demolishing and rebuilding the whole section.”

Linked Lists = Scavenger Hunt

“Each clue tells you where to find the next clue. Clues can be anywhere. Easy to add or remove a clue — just change what the previous one points to. But you must follow the chain to find a specific clue.”

Access vs Insert

“Array's weakness is linked list's strength (insertion/deletion). Linked list's weakness is array's strength (random access).”

Acronym Mnemonics

Array = Access Anywhere O(1). Linked List = Linked Locations O(n) access.

Pointers Are the Glue

“No pointers, no linked list. Pointers are the glue that holds nodes together. Break a pointer link and you lose the rest of the list.”

Cache Performance

“Array = books on one shelf (grab the next one instantly). Linked list = books scattered across a library (walk to each one).”

9Common Mistakes

Off-by-One Errors (Array)

Accessing array[size] instead of array[size-1]

Arrays are zero-indexed. The last valid index is size - 1. Accessing array[size] causes an index out of bounds error or undefined behavior.

Using Arrays for Frequent Middle Insertions

Repeatedly inserting/deleting in the middle of an array

Each middle insertion costs O(n) due to shifting. If your use case requires frequent middle modifications, a linked list is the better choice.

Null Pointer Dereferencing (Linked List)

Accessing current.next.data when current.next is None

Always check for null before dereferencing a pointer. This is the most common linked list bug: if current.next: ... before accessing current.next.data.

Losing the Head Pointer

Forgetting to update head when inserting at the beginning or deleting the first node

If the head reference is lost, the entire list becomes unreachable. Always update the head pointer when modifying the first node.

Incorrect Pointer Manipulation

Failing to update all necessary pointers during insertion or deletion

When inserting, set the new node's next pointer before updating the predecessor's next pointer. Reversing this order loses the rest of the list. For doubly linked lists, remember to update both next and prev.

Ignoring Edge Cases

Not handling empty lists, single-node lists, or head/tail operations

Linked list operations often require special handling for: empty lists, single-element lists, insertion/deletion at the head, and insertion/deletion at the tail. Test all these cases.

Memory Leaks (C/C++)

Not freeing deleted linked list nodes in manual-memory languages

In C/C++, every new must have a corresponding delete. When removing nodes, free the memory before losing the reference. In garbage-collected languages (Python, Java) this is handled automatically.

Confusing ADT with Data Structure

Believing “List” is an array or a linked list

“List” is an Abstract Data Type (ADT) specifying operations. Arrays and linked lists are concrete data structures that implement the List ADT. The distinction matters for exam questions and design discussions.

Frequently Asked Questions

When should I use an array versus a linked list?
Use an array (or dynamic array) when you know the approximate data size, need frequent random access (e.g., data[i]), prioritize cache performance, or insertions mostly happen at the end. Use a linked list when data size changes frequently, you need frequent insertions or deletions in the middle, sequential access is acceptable, and memory usage needs to be precisely minimal with no pre-allocation of unused space.
What is a dynamic array and how does it relate to arrays and linked lists?
A dynamic array (like std::vector in C++, ArrayList in Java, or Python's list) is an array that can grow or shrink. Internally, it's still stored in contiguous memory. When it runs out of space, a larger array is allocated (typically double the size), and all elements are copied. This makes insertion at the end amortized O(1), but individual resizes are O(n). It bridges the gap between fixed-size arrays and fully dynamic linked lists.
Why are arrays considered cache-friendly and linked lists not?
Arrays store elements contiguously in memory. When the CPU accesses an element, the cache loads a block of surrounding memory (spatial locality), so subsequent accesses to nearby elements are fast. Linked list nodes are scattered in memory, so each access may cause a cache miss, requiring the CPU to fetch from slower main memory.
What is the difference between an ADT and a data structure?
An Abstract Data Type (ADT) is a conceptual model that defines operations without specifying implementation (e.g., List, Stack, Queue). A data structure is a concrete implementation specifying how data is stored and operations carried out (e.g., Array, Singly Linked List, Hash Table). A "List" is an ADT; both "Array" and "Linked List" are data structures that can implement it.
Can a linked list be multi-dimensional like an array?
Not directly. A linked list is inherently a 1D linear structure. However, you can create a "list of lists" where each node's data field points to another linked list, effectively simulating multi-dimensional storage. This is more complex to manage than a traditional multi-dimensional array.
Are there other linear data structures besides arrays and linked lists?
Yes, but most are built on top of arrays or linked lists. Stacks (LIFO) are typically implemented with dynamic arrays or singly linked lists. Queues (FIFO) use circular arrays or singly linked lists. Deques (double-ended queues) use doubly linked lists or dynamic arrays with efficient operations at both ends.

Practice Quiz

Test your understanding of arrays and linked lists — select the correct answer for each question.

1.Which of the following operations has a time complexity of O(1) in an array?

2.What is a primary advantage of linked lists over arrays?

3.What distinguishes a doubly linked list from a singly linked list?

4.How do arrays and linked lists differ in memory allocation?

5.What is Floyd's cycle detection algorithm used for?

6.What is the time complexity of inserting an element at the beginning of an array?

7.What is a sentinel (dummy) node in linked list implementations?

8.What is the time complexity of traversing an entire linked list of n nodes?

9.When is a linked list generally preferred over an array?

10.What happens when a node's next pointer points to an earlier node in the list?

Study Tips

  • Draw memory diagrams: Sketch arrays as contiguous blocks and linked lists as scattered nodes with arrows. Visualizing pointer connections builds intuition for insertion, deletion, and traversal.
  • Implement from scratch: Code a singly linked list with insert, delete, search, and reverse operations. Then implement a dynamic array with resizing. Nothing cements understanding like writing the code yourself.
  • Compare operations side-by-side: For each operation (access, insert, delete), trace through both the array and linked list versions to internalize the complexity differences.
  • Practice edge cases: Empty structures, single-element structures, and head/tail operations are where most bugs hide. Test these systematically.

Related Topics