Skip to main content

Basic Data Structures

TL;DR

Arrays provide O(1) random access but O(n) insertion/deletion. Linked lists enable O(1) insertion/deletion but require O(n) traversal. Stacks enforce LIFO access, queues enforce FIFO access. Hash tables provide O(1) average lookup through key-value mapping. Each structure trades off different performance characteristics for different problem types.

Core Concepts

Arrays and Dynamic Arrays

Arrays are fixed-size collections of elements stored in contiguous memory. Each element occupies the same space and is accessible via index.

Characteristics:

  • Random access: O(1) to get element at any index
  • Cache-friendly: Contiguous memory enables CPU cache optimization
  • Fixed size: Cannot add elements beyond initial capacity
  • Insertion/deletion: O(n) due to need to shift elements

Dynamic arrays (like Python lists or Java ArrayLists) resize automatically when needed, providing O(1) amortized insertion while maintaining array benefits.

Linked Lists

Linked lists are collections of nodes where each node contains data and a pointer to the next node. This enables efficient insertion/deletion without shifting.

Variants:

  • Singly linked: Each node points to next only; traversal is unidirectional
  • Doubly linked: Each node points to both next and previous; bidirectional traversal
  • Circular: Last node points back to first; no explicit end

Characteristics:

  • Access: O(n) - must traverse from head
  • Insertion/deletion: O(1) if position is known (after finding position)
  • Memory overhead: Extra space for pointers
  • No random access: Cannot jump to middle elements

Stacks

Stacks are LIFO (Last In, First Out) collections where elements are added and removed from the same end (top).

Operations:

  • Push: Add element to top - O(1)
  • Pop: Remove element from top - O(1)
  • Peek: View top element - O(1)

Applications:

  • Function call stacks
  • Undo/redo functionality
  • Expression evaluation
  • Parentheses matching

Queues

Queues are FIFO (First In, First Out) collections where elements are added at rear and removed from front.

Operations:

  • Enqueue: Add element to rear - O(1)
  • Dequeue: Remove element from front - O(1)
  • Front: View front element - O(1)

Applications:

  • Task scheduling
  • Breadth-first search
  • Print job queues
  • Message processing

Hash Tables

Hash tables are key-value stores that use a hash function to map keys to array indices, enabling fast lookup.

Key concepts:

  • Hash function: Converts key to array index
  • Collision: Multiple keys map to same index
  • Load factor: Ratio of entries to capacity
  • Collision resolution: Chaining (linked lists at each index) or open addressing (find alternative slot)

Characteristics:

  • Average case: O(1) for insert, delete, lookup
  • Worst case: O(n) when collisions dominate
  • Space: O(n) for n elements

Sets and Collections

Sets are unordered collections of unique elements. Multisets allow duplicate elements.

Properties:

  • Uniqueness: Sets enforce no duplicates
  • Membership testing: O(1) average case
  • Set operations: Union, intersection, difference

Key Algorithms and Techniques

Linear Search in Arrays

Scan array sequentially to find element. O(n) time, no preprocessing needed.

Array Rotation

Shift elements left or right, wrapping around. Useful for circular buffer implementations.

Linked List Reversal

Iterate through list, reversing direction of pointers. In-place with O(n) time, O(1) space.

Stack-based Expression Evaluation

Convert infix to postfix notation using stack, then evaluate. Handles operator precedence correctly.

BFS with Queue

Process graph level-by-level by enqueueing all neighbors of current node. Discovers shortest paths in unweighted graphs.

Practical Example

# Array operations
arr = [1, 2, 3, 4, 5]
arr.append(6) # O(1) amortized
arr.insert(0, 0) # O(n) - shifts elements
print(arr[2]) # O(1) random access

# Linked list node
class Node:
def __init__(self, value):
self.value = value
self.next = None

# Stack using list
stack = []
stack.append(1) # Push
stack.append(2)
top = stack.pop() # Pop - O(1)

# Queue using deque
from collections import deque
queue = deque()
queue.append(1) # Enqueue
queue.popleft() # Dequeue - O(1)

# Hash table / Dictionary
hash_map = {}
hash_map['key'] = 'value' # O(1) average
if 'key' in hash_map: # O(1) average
print(hash_map['key'])

# Set operations
set_a = {1, 2, 3}
set_b = {2, 3, 4}
print(set_a & set_b) # Intersection: {2, 3}
print(set_a | set_b) # Union: {1, 2, 3, 4}
print(set_a - set_b) # Difference: {1}

Common Pitfalls

warning

Confusing stack and queue order: Push/pop from same end (LIFO) for stacks; enqueue rear, dequeue front (FIFO) for queues. Mixing them up breaks algorithms like BFS.

Not accounting for linked list traversal cost: O(n) traversal overhead for linked lists differs from O(1) array access. Choose structures based on operation patterns.

Hash collision underestimation: Worst-case O(n) performance when collisions dominate. Monitor load factor and choose good hash functions.

Array bounds errors: Off-by-one errors accessing array elements cause crashes or corruption. Always verify indices are within [0, length-1].

Not freeing memory: Memory leaks from unreferenced linked list nodes. Explicitly nullify references when removing nodes.

Self-Check

  1. Why is insertion O(n) in arrays but O(1) in linked lists? What trade-offs exist?
  2. How does a hash table achieve O(1) average lookup? What causes worst-case O(n)?
  3. When would you use a stack versus a queue? Give a real-world example for each.

One Takeaway

info

Each basic data structure excels at different operations. Arrays provide fast random access, linked lists enable fast insertion/deletion, stacks/queues enforce specific access patterns, and hash tables provide fast lookups by key. Understanding operation costs guides choosing the right structure for your problem.

References

  • Cormen, T. H., Leiserson, C. E., Rivest, R. L., & Stein, C. (2009). Introduction to Algorithms (3rd ed.). MIT Press.
  • Sedgewick, R., & Wayne, K. (2011). Algorithms (4th ed.). Addison-Wesley.
  • Bhargava, A. Y. (2016). Grokking Algorithms. Manning Publications.