List: Array Implementation

Explore how dynamic arrays work with automatic resizing, insertion, deletion, and search operations. Understanding the foundation of ArrayList, Vector, and Python lists.

Interactive Operations
Dynamic Resizing
O(1) Access

Dynamic Array Visualization

[0]
[1]
[2]
[3]
Size: 0 / Capacity: 4
Load Factor: 0.0%
Memory: 16 bytes (assuming 4 bytes per integer)

Current Step:

Select an operation to begin visualization

Complexity Analysis

O(1)
Access
O(n)
Insert/Delete
O(n)
Search
O(n)*
Append*
O(n) Space
Dynamic allocation with capacity buffer
*Append is O(1) amortized due to doubling strategy

Operations

Insert

Add element at specific index, shift others right

Delete

Remove element at index, shift others left

Access

Direct access by index calculation

Search

Linear scan through elements

Dynamic Resizing

Growth Factor: Double capacity when full
Amortized Cost: O(1) for append operations
Memory Trade-off: Some wasted space for performance
Copy Cost: O(n) when resizing occurs
Frequency: Resizing becomes less frequent as array grows

Pros & Cons

Advantages

• O(1) random access by index
• Cache-friendly memory layout
• Dynamic size without manual management

Disadvantages

• Expensive insertion/deletion in middle
• Memory reallocation overhead
• Wasted space when not at capacity

Applications

ArrayList (Java): Resizable arrays
Python Lists: Dynamic arrays with extra features
JavaScript Arrays: Dynamic and sparse
C++ vector: High-performance dynamic arrays
Databases: Index structures and record storage

Implementation

class DynamicArray:
    def __init__(self, initial_capacity=4):
        self.data = [None] * initial_capacity  # Fixed-size array
        self.size = 0                          # Number of elements
        self.capacity = initial_capacity       # Current capacity
    
    def _resize(self):
        # Double the capacity when array is full
        old_capacity = self.capacity
        self.capacity *= 2
        new_data = [None] * self.capacity
        
        # Copy existing elements
        for i in range(self.size):
            new_data[i] = self.data[i]
        
        self.data = new_data
        print(f"Resized from {old_capacity} to {self.capacity}")
    
    def insert(self, index, value):
        # Validate index
        if index < 0 or index > self.size:
            raise IndexError("Index out of bounds")
        
        # Resize if necessary
        if self.size >= self.capacity:
            self._resize()
        
        # Shift elements to the right
        for i in range(self.size, index, -1):
            self.data[i] = self.data[i - 1]
        
        # Insert new element
        self.data[index] = value
        self.size += 1
    
    def delete(self, index):
        # Validate index
        if index < 0 or index >= self.size:
            raise IndexError("Index out of bounds")
        
        # Get value to return
        value = self.data[index]
        
        # Shift elements to the left
        for i in range(index, self.size - 1):
            self.data[i] = self.data[i + 1]
        
        self.size -= 1
        return value
    
    def search(self, value):
        # Linear search through the array
        for i in range(self.size):
            if self.data[i] == value:
                return i
        return -1  # Not found
    
    def access(self, index):
        # Direct access by index
        if index < 0 or index >= self.size:
            raise IndexError("Index out of bounds")
        return self.data[index]
    
    def append(self, value):
        # Insert at the end
        self.insert(self.size, value)
    
    def get_size(self):
        return self.size
    
    def get_capacity(self):
        return self.capacity