Skip to content

Arrays and Strings

The simplest and fastest data structure: Arrays are like a row of mailboxes — each slot is numbered, and you can access any slot instantly by its number. This contiguous memory layout gives O(1) access and excellent cache performance, making arrays the foundation of almost everything.

Why it matters: Arrays are the most cache-friendly data structure — sequential access patterns exploit CPU cache lines, making array operations 10-100x faster than pointer-based alternatives for the same asymptotic complexity.

The key insight: The real-world performance gap between arrays and linked lists is much larger than Big-O suggests — cache misses cost 100+ cycles, so array traversal is dramatically faster even though both are O(n).

An array is a contiguous block of memory where each element occupies a fixed number of bytes and is Indexed by an integer offset from the base address. This is the simplest and most cache-efficient Data structure available. On modern hardware, accessing element ii of an array arr compiles to a Single instruction: load from base + i * element_size.

Arrays have excellent spatial locality: accessing arr[i] loads the entire cache line ( 64 Bytes) into L1 cache, so accessing arr[i+1]``arr[i+2]Etc. Hits cache. This is why a linear Scan through an array is 10-100x faster than following pointers through a linked list, Even though both are O(n)O(n) in theory.

PropertyArrayLinked List
Access by indexO(1)O(1)O(n)O(n)
Insert at frontO(n)O(n)O(1)O(1)
Insert at backO(1)O(1) amortisedO(1)O(1) with tail pointer
Insert in middleO(n)O(n)O(1)O(1) with pointer
Cache behaviourExcellent (contiguous)Poor (pointer chasing)
Memory overheadNone (or small for dynamic)One pointer per element

Dynamic arrays (Python listC++ std::vectorJava ArrayList) automatically resize when full. The standard strategy is geometric growth: when capacity is exhausted, allocate a new array of Capacity cmc \cdot m (where cc is the growth factor, 2) and copy all elements.

  • Growth factor of 2: amortised O(1)O(1) per append, but memory usage can be up to 2n2n
  • Growth factor of 1.5: amortised O(1)O(1) per append, and the old array can sometimes be reused (for memory allocators that support in-place resizing)
class DynamicArray:
def __init__(self, capacity=1):
self.data = [None] * capacity
self.size = 0
self.capacity = capacity
def append(self, value):
if self.size == self.capacity:
self._resize(self.capacity * 2)
self.data[self.size] = value
self.size += 1
def _resize(self, new_capacity):
new_data = [None] * new_capacity
for i in range(self.size):
new_data[i] = self.data[i]
self.data = new_data
self.capacity = new_capacity
def pop(self):
if self.size == 0:
raise IndexError("pop from empty array")
value = self.data[self.size - 1]
self.data[self.size - 1] = None
self.size -= 1
# Optional: shrink if size < capacity / 4
if self.size > 0 and self.size <= self.capacity // 4:
self._resize(self.capacity // 2)
return value