Skip to content

Dynamic Array

A dynamic array (also known as a resizable array or growable array) is a random access, variable-size list data structure that allows elements to be added or removed. It automatically resizes itself when needed.

Features

  • Automatic resizing when capacity is exceeded
  • Amortized O(1) append operation
  • O(1) random access by index
  • Generic type support for type safety
  • Memory efficient with automatic shrinking

Time Complexities

Operation Time Complexity
Access O(1)
Append O(1) amortized, O(n) worst case
Insert O(n)
Remove O(n)
Pop O(1) for end, O(n) for arbitrary index
Search O(n)
Count O(n)

Basic Usage

Creating and Initializing

from algo.data_structs.arrays.dynamic_array import DynamicArray

# Create with default capacity
arr = DynamicArray()

# Create with custom initial capacity
arr = DynamicArray(initial_capacity=10)

# Check if empty
print(arr.is_empty())  # True
print(len(arr))        # 0

Adding Elements

# Append elements to the end
arr.append(1)
arr.append(2)
arr.append(3)
print(list(arr))  # [1, 2, 3]

# Extend with multiple elements
arr.extend([4, 5, 6])
print(list(arr))  # [1, 2, 3, 4, 5, 6]

# Insert at specific position
arr.insert(0, 0)     # Insert at beginning
arr.insert(3, 2.5)   # Insert in middle
print(list(arr))     # [0, 1, 2, 2.5, 3, 4, 5, 6]

Accessing Elements

# Access by index
print(arr[0])   # 0 (first element)
print(arr[-1])  # 6 (last element)

# Modify elements
arr[1] = 10
print(arr[1])   # 10

# Get array length
print(len(arr))  # 8

Removing Elements

# Remove by value (first occurrence)
arr.remove(2.5)
print(list(arr))  # [0, 10, 2, 3, 4, 5, 6]

# Pop from end
last = arr.pop()
print(last)       # 6
print(list(arr))  # [0, 10, 2, 3, 4, 5]

# Pop from specific index
first = arr.pop(0)
print(first)      # 0
print(list(arr))  # [10, 2, 3, 4, 5]

# Clear all elements
arr.clear()
print(len(arr))   # 0

Search Operations

arr = DynamicArray()
arr.extend([1, 2, 3, 2, 4, 2])

# Find index of element
print(arr.index(2))      # 1 (first occurrence)
print(arr.index(2, 2))   # 3 (starting from index 2)
print(arr.index(2, 1, 4)) # 1 (within range [1, 4))

# Count occurrences
print(arr.count(2))      # 3
print(arr.count(5))      # 0 (non-existent)

# Handle not found
try:
    arr.index(10)
except ValueError as e:
    print(f"Not found: {e}")

Array Manipulation

arr = DynamicArray()
arr.extend([1, 2, 3, 4, 5])

# Reverse the array
arr.reverse()
print(list(arr))  # [5, 4, 3, 2, 1]

# Iteration
for item in arr:
    print(item)

# Convert to list
python_list = list(arr)

Memory Management

arr = DynamicArray()

# Fill array to observe capacity growth
for i in range(10):
    arr.append(i)
    print(f"Size: {len(arr)}, Capacity: {arr.capacity()}")

# Capacity grows: 1 → 2 → 4 → 8 → 16

# Remove elements to observe shrinking
while len(arr) > 2:
    arr.pop()

print(f"After removal - Size: {len(arr)}, Capacity: {arr.capacity()}")

# Manual capacity reduction
arr.shrink_to_fit()
print(f"After shrink - Size: {len(arr)}, Capacity: {arr.capacity()}")

Working with Different Types

Strings

str_arr = DynamicArray()
str_arr.extend(['apple', 'banana', 'cherry'])

print(str_arr.index('banana'))  # 1
print(str_arr.count('apple'))   # 1

Mixed Types

mixed_arr = DynamicArray()
mixed_arr.extend([1, 'hello', 3.14, True])

print(len(mixed_arr))  # 4
print(mixed_arr[1])    # 'hello'
print(mixed_arr[3])    # True

Custom Objects

class Person:
    def __init__(self, name: str, age: int):
        self.name = name
        self.age = age

    def __eq__(self, other):
        return isinstance(other, Person) and self.name == other.name

people = DynamicArray()
alice = Person("Alice", 30)
bob = Person("Bob", 25)

people.extend([alice, bob])
print(people.count(alice))  # 1

Performance Characteristics

Resizing Behavior

The dynamic array uses a doubling strategy for growth and halving strategy for shrinkage:

  • Growth: When capacity is exceeded, double the capacity
  • Shrinkage: When utilization drops below 25%, halve the capacity
  • This provides amortized O(1) append operations

Memory Efficiency

# Example showing automatic resizing
arr = DynamicArray(initial_capacity=2)

# Growth pattern
arr.append(1)  # capacity: 2
arr.append(2)  # capacity: 2
arr.append(3)  # capacity: 4 (doubled)
arr.append(4)  # capacity: 4
arr.append(5)  # capacity: 8 (doubled)

# Shrinkage pattern (when size ≤ capacity/4)
for _ in range(6):  # Remove 6 elements
    arr.pop()
# Capacity will shrink when appropriate

Error Handling

arr = DynamicArray()

# Index errors
try:
    arr[0]  # IndexError: empty array
except IndexError:
    print("Cannot access empty array")

try:
    arr.insert(-1, 5)  # IndexError: negative index
except IndexError:
    print("Invalid insert index")

# Value errors
arr.extend([1, 2, 3])
try:
    arr.remove(5)  # ValueError: not found
except ValueError:
    print("Value not in array")

try:
    arr.index(5)  # ValueError: not found
except ValueError:
    print("Value not found")

String Representation

arr = DynamicArray()
arr.extend([1, 2, 3])

print(str(arr))   # DynamicArray([1, 2, 3])
print(repr(arr))  # DynamicArray(size=3, capacity=4, data=[1, 2, 3])

Common Patterns

Building from Iterable

# From list
arr = DynamicArray()
arr.extend([1, 2, 3, 4, 5])

# From range
arr2 = DynamicArray()
arr2.extend(range(10))

# From tuple
arr3 = DynamicArray()
arr3.extend((1, 2, 3))

Filtering Elements

arr = DynamicArray()
arr.extend(range(10))

# Remove all even numbers
i = 0
while i < len(arr):
    if arr[i] % 2 == 0:
        arr.pop(i)
    else:
        i += 1

Stack-like Usage

stack = DynamicArray()

# Push
stack.append(1)
stack.append(2)
stack.append(3)

# Pop
while not stack.is_empty():
    print(stack.pop())  # 3, 2, 1

API Reference

Dynamic Array Implementation

A dynamic array (also known as a resizable array, growable array, or array list) is a random access, variable-size list data structure that allows elements to be added or removed. It automatically resizes itself when needed.

Key Features:

  • Automatic resizing when capacity is exceeded
  • Amortized O(1) append operation
  • O(1) random access by index
  • O(n) insertion/deletion at arbitrary positions

Time Complexities:

  • Access: O(1)
  • Append: O(1) amortized, O(n) worst case (when resizing)
  • Insert: O(n)
  • Delete: O(n)
  • Search: O(n)

Space Complexity: O(n)

DynamicArray(initial_capacity=1)

Bases: Generic[T]

A dynamic array implementation that automatically resizes as needed.

This implementation uses a backing array that doubles in size when capacity is exceeded and halves when utilization drops below 25% (to avoid thrashing).

Initialize a new dynamic array.

Parameters:

Name Type Description Default
initial_capacity int

Initial capacity of the backing array (default: 1)

1

Raises:

Type Description
ValueError

If initial_capacity is less than 1

__getitem__(index)

Get element at specified index.

Parameters:

Name Type Description Default
index int

Index of element to retrieve (supports negative indexing)

required

Returns:

Type Description
T

Element at the specified index

Raises:

Type Description
IndexError

If index is out of bounds

__iter__()

Return an iterator over the array elements.

__len__()

Return the number of elements in the array.

__repr__()

Return detailed string representation for debugging.

__setitem__(index, value)

Set element at specified index.

Parameters:

Name Type Description Default
index int

Index where to set the value (supports negative indexing)

required
value T

Value to set

required

Raises:

Type Description
IndexError

If index is out of bounds

__str__()

Return string representation of the array.

append(value)

Add an element to the end of the array.

Time Complexity: O(1) amortized, O(n) worst case (when resizing)

Parameters:

Name Type Description Default
value T

Element to append

required

capacity()

Return the current capacity of the backing array.

clear()

Remove all elements from the array.

Time Complexity: O(1)

count(value)

Return the number of occurrences of value.

Time Complexity: O(n)

Parameters:

Name Type Description Default
value T

Value to count

required

Returns:

Type Description
int

Number of occurrences

extend(iterable)

Extend the array by appending all elements from the iterable.

Time Complexity: O(k) where k is the number of elements to add

Parameters:

Name Type Description Default
iterable

Iterable containing elements to add

required

index(value, start=0, stop=None)

Return the index of the first occurrence of value.

Time Complexity: O(n)

Parameters:

Name Type Description Default
value T

Value to search for

required
start int

Start index for search (default: 0)

0
stop Optional[int]

Stop index for search (default: end of array)

None

Returns:

Type Description
int

Index of the first occurrence

Raises:

Type Description
ValueError

If value is not found

insert(index, value)

Insert an element at the specified index.

Time Complexity: O(n) - need to shift elements

Parameters:

Name Type Description Default
index int

Index where to insert (0 <= index <= size)

required
value T

Element to insert

required

Raises:

Type Description
IndexError

If index is out of valid insertion range

is_empty()

Check if the array is empty.

pop(index=None)

Remove and return element at specified index (default: last element).

Time Complexity: O(1) for last element, O(n) for arbitrary index

Parameters:

Name Type Description Default
index Optional[int]

Index of element to remove (default: -1, last element)

None

Returns:

Type Description
T

The removed element

Raises:

Type Description
IndexError

If array is empty or index is out of bounds

remove(value)

Remove the first occurrence of the specified value.

Time Complexity: O(n)

Parameters:

Name Type Description Default
value T

Value to remove

required

Raises:

Type Description
ValueError

If value is not found

reverse()

Reverse the array in place.

Time Complexity: O(n)

shrink_to_fit()

Reduce capacity to match the current size.

This can save memory but may cause performance issues if more elements are added later.