Array Data Structure and Operations

1. Array Operations (Insert, Delete, Access)

Operation Time Complexity Description Use Case
Access by Index O(1) Direct memory address calculation: base + index × size Fast random access to elements
Search (Unsorted) O(n) Linear scan through all elements Finding element in unordered array
Search (Sorted) O(log n) Binary search on sorted array Efficient searching when sorted
Insert at End O(1) Add element after last index (if space available) Appending elements efficiently
Insert at Position O(n) Shift all elements after insertion point right Maintaining order while inserting
Delete at End O(1) Remove last element, decrease size Stack-like pop operation
Delete at Position O(n) Shift all elements after deletion point left Removing element while maintaining order
Traverse/Iterate O(n) Visit each element sequentially Processing all elements

Example: Basic Array Operations

// Array creation and initialization
const arr = [1, 2, 3, 4, 5];
const zeros = new Array(5).fill(0); // [0, 0, 0, 0, 0]

// Access - O(1)
const first = arr[0];        // 1
const last = arr[arr.length - 1]; // 5

// Search - O(n)
function linearSearch(arr, target) {
    for (let i = 0; i < arr.length; i++) {
        if (arr[i] === target) return i;
    }
    return -1;
}

// Insert at end - O(1) amortized
arr.push(6); // [1, 2, 3, 4, 5, 6]

// Insert at position - O(n)
function insertAt(arr, index, value) {
    arr.splice(index, 0, value);
    return arr;
}
insertAt(arr, 2, 10); // [1, 2, 10, 3, 4, 5, 6]

// Delete at end - O(1)
arr.pop(); // [1, 2, 10, 3, 4, 5]

// Delete at position - O(n)
arr.splice(2, 1); // [1, 2, 3, 4, 5]

// Traverse - O(n)
arr.forEach((val, idx) => console.log(`arr[${idx}] = ${val}`));
Common Techniques Approach Time Application
Two Pointers Use two indices moving toward/away from each other O(n) Reverse array, find pairs, palindrome check
Sliding Window Maintain window of fixed/variable size O(n) Subarray sum, max window, substring problems
Prefix Sum Precompute cumulative sums for range queries O(n) prep, O(1) query Range sum queries, subarray problems
Hash Map Store element-index mappings for O(1) lookup O(n) Two sum, frequency counting, duplicates

Example: Two Pointers Technique

// Reverse array in-place
function reverseArray(arr) {
    let left = 0, right = arr.length - 1;
    while (left < right) {
        [arr[left], arr[right]] = [arr[right], arr[left]];
        left++;
        right--;
    }
    return arr;
}

// Find pair with given sum (sorted array)
function twoSum(arr, target) {
    let left = 0, right = arr.length - 1;
    while (left < right) {
        const sum = arr[left] + arr[right];
        if (sum === target) return [left, right];
        if (sum < target) left++;
        else right--;
    }
    return null;
}

Example: Prefix Sum

// Build prefix sum array
function buildPrefixSum(arr) {
    const prefix = [arr[0]];
    for (let i = 1; i < arr.length; i++) {
        prefix[i] = prefix[i - 1] + arr[i];
    }
    return prefix;
}

// Range sum query O(1)
function rangeSum(prefix, left, right) {
    if (left === 0) return prefix[right];
    return prefix[right] - prefix[left - 1];
}

// Example usage
const arr = [1, 2, 3, 4, 5];
const prefix = buildPrefixSum(arr);
console.log(rangeSum(prefix, 1, 3)); // 9 (2+3+4)

2. 2D Arrays and Multi-dimensional Arrays

Traversal Pattern Direction Time Complexity Use Case
Row-major Order Left to right, top to bottom O(m × n) Standard matrix traversal, most common in memory layout
Column-major Order Top to bottom, left to right O(m × n) Column operations, vertical processing
Spiral Traversal Clockwise from outer to inner layers O(m × n) Matrix printing, spiral pattern generation
Diagonal Traversal Along diagonals (top-left to bottom-right) O(m × n) Diagonal matrix operations, DP problems
Zigzag Traversal Alternating left-right, right-left O(m × n) Binary tree level order variations
Layer-by-layer Process outer layer, then inner recursively O(m × n) Rotate matrix, boundary processing

Example: Matrix Traversal Patterns

// Row-major traversal
function rowMajor(matrix) {
    const result = [];
    for (let i = 0; i < matrix.length; i++) {
        for (let j = 0; j < matrix[0].length; j++) {
            result.push(matrix[i][j]);
        }
    }
    return result;
}

// Column-major traversal
function columnMajor(matrix) {
    const result = [];
    for (let j = 0; j < matrix[0].length; j++) {
        for (let i = 0; i < matrix.length; i++) {
            result.push(matrix[i][j]);
        }
    }
    return result;
}

// Spiral traversal
function spiralOrder(matrix) {
    const result = [];
    let top = 0, bottom = matrix.length - 1;
    let left = 0, right = matrix[0].length - 1;
    
    while (top <= bottom && left <= right) {
        // Right
        for (let j = left; j <= right; j++) result.push(matrix[top][j]);
        top++;
        // Down
        for (let i = top; i <= bottom; i++) result.push(matrix[i][right]);
        right--;
        // Left
        if (top <= bottom) {
            for (let j = right; j >= left; j--) result.push(matrix[bottom][j]);
            bottom--;
        }
        // Up
        if (left <= right) {
            for (let i = bottom; i >= top; i--) result.push(matrix[i][left]);
            left++;
        }
    }
    return result;
}

// Diagonal traversal
function diagonalTraversal(matrix) {
    const result = [];
    const m = matrix.length, n = matrix[0].length;
    
    // Upper diagonals (starting from first row)
    for (let d = 0; d < n; d++) {
        let i = 0, j = d;
        while (i < m && j >= 0) {
            result.push(matrix[i][j]);
            i++;
            j--;
        }
    }
    
    // Lower diagonals (starting from first column)
    for (let d = 1; d < m; d++) {
        let i = d, j = n - 1;
        while (i < m && j >= 0) {
            result.push(matrix[i][j]);
            i++;
            j--;
        }
    }
    return result;
}
Note: For cache efficiency, row-major traversal is preferred in most languages (C, Java, JavaScript) as it accesses consecutive memory locations. Column-major traversal can cause cache misses.

3. Dynamic Arrays (ArrayList, Vector Resizing)

Strategy Growth Factor Amortized Insert Space Overhead
Double When Full 2× capacity O(1) Up to 100% (worst case 50% wasted)
1.5× Growth 1.5× capacity O(1) ~33% overhead, better memory reuse
Golden Ratio φ ≈ 1.618× capacity O(1) Optimal for memory block reuse
Fixed Increment capacity + k O(n) Low space overhead but slower growth
Aspect Doubling (2×) 1.5× Growth Trade-offs
Resize Frequency Fewer resizes (log₂ n) More frequent resizes (log₁.₅ n) Doubling: less overhead, 1.5×: steadier growth
Memory Reuse Cannot reuse freed blocks Can reuse previously freed memory 1.5× better for memory fragmentation
Space Waste Up to 50% capacity unused Up to 33% capacity unused 1.5× more space efficient
Implementation JavaScript, Java, Python use variants of doubling Used in some C++ STL implementations Most systems use 1.5× to 2× range

Example: Dynamic Array Implementation

class DynamicArray {
    constructor(initialCapacity = 2) {
        this.data = new Array(initialCapacity);
        this.size = 0;
        this.capacity = initialCapacity;
    }
    
    // O(1) amortized
    push(value) {
        if (this.size === this.capacity) {
            this.resize(this.capacity * 2); // Double capacity
        }
        this.data[this.size++] = value;
    }
    
    // O(1)
    pop() {
        if (this.size === 0) return undefined;
        const value = this.data[--this.size];
        
        // Shrink if utilization < 25%
        if (this.size > 0 && this.size < this.capacity / 4) {
            this.resize(Math.floor(this.capacity / 2));
        }
        return value;
    }
    
    // O(n) for resize operation
    resize(newCapacity) {
        const newData = new Array(newCapacity);
        for (let i = 0; i < this.size; i++) {
            newData[i] = this.data[i];
        }
        this.data = newData;
        this.capacity = newCapacity;
    }
    
    // O(1)
    get(index) {
        if (index < 0 || index >= this.size) {
            throw new Error("Index out of bounds");
        }
        return this.data[index];
    }
    
    getSize() { return this.size; }
    getCapacity() { return this.capacity; }
}

// Usage
const arr = new DynamicArray();
for (let i = 0; i < 10; i++) {
    arr.push(i);
    console.log(`Size: ${arr.getSize()}, Capacity: ${arr.getCapacity()}`);
}
// Size: 1, Capacity: 2
// Size: 2, Capacity: 2
// Size: 3, Capacity: 4
// Size: 4, Capacity: 4
// Size: 5, Capacity: 8
// ...
Warning: When shrinking dynamic arrays, use hysteresis (e.g., shrink at 25% full, not 50%) to avoid thrashing - repeated resize operations when size oscillates around threshold.

4. Sparse Arrays (Memory Optimization)

Representation Space Complexity Access Time Best For
Dictionary/Hash Map O(k) where k = non-zero elements O(1) average Very sparse arrays, random access pattern
Coordinate List (COO) O(k) - stores (index, value) pairs O(k) - requires search Building sparse structures, easy construction
Compressed Sparse Row (CSR) O(k + rows) O(log k) per row Matrix operations, row-wise access, scientific computing
Run-Length Encoding O(runs) where runs = consecutive segments O(runs) Long sequences of same values (images, game boards)
Bit Vector O(n/8) - one bit per element O(1) Boolean arrays, presence/absence tracking

Example: Sparse Array Implementations

// HashMap-based sparse array
class SparseArray {
    constructor() {
        this.data = new Map();
        this.defaultValue = 0;
    }
    
    set(index, value) {
        if (value !== this.defaultValue) {
            this.data.set(index, value);
        } else {
            this.data.delete(index); // Don't store default values
        }
    }
    
    get(index) {
        return this.data.has(index) ? this.data.get(index) : this.defaultValue;
    }
    
    getSize() {
        return this.data.size; // Number of non-default values
    }
}

// Coordinate List (COO) format - for sparse matrix
class COOMatrix {
    constructor() {
        this.rows = [];
        this.cols = [];
        this.values = [];
    }
    
    add(row, col, value) {
        if (value !== 0) {
            this.rows.push(row);
            this.cols.push(col);
            this.values.push(value);
        }
    }
    
    get(row, col) {
        for (let i = 0; i < this.rows.length; i++) {
            if (this.rows[i] === row && this.cols[i] === col) {
                return this.values[i];
            }
        }
        return 0;
    }
}

// Run-Length Encoding
class RLEArray {
    constructor() {
        this.runs = []; // [{value, count}, ...]
    }
    
    encode(arr) {
        if (arr.length === 0) return;
        
        let currentValue = arr[0];
        let count = 1;
        
        for (let i = 1; i < arr.length; i++) {
            if (arr[i] === currentValue) {
                count++;
            } else {
                this.runs.push({value: currentValue, count});
                currentValue = arr[i];
                count = 1;
            }
        }
        this.runs.push({value: currentValue, count});
    }
    
    decode() {
        const result = [];
        for (const run of this.runs) {
            for (let i = 0; i < run.count; i++) {
                result.push(run.value);
            }
        }
        return result;
    }
}

// Example usage
const sparse = new SparseArray();
sparse.set(1000, 42);
sparse.set(5000, 99);
console.log(sparse.get(1000)); // 42
console.log(sparse.get(2000)); // 0 (default)
console.log(sparse.getSize()); // 2 (only stores non-zero values)

5. Array Rotation (Left Rotation, Right Rotation)

Technique Algorithm Time Space
Rotate Left by k Reversal algorithm: reverse(0,k-1), reverse(k,n-1), reverse(0,n-1) O(n) O(1)
Rotate Right by k Same as left rotate by (n-k) O(n) O(1)
Cyclic Rotation GCD-based approach for in-place rotation O(n) O(1)
Transpose Matrix Swap elements across diagonal: matrix[i][j] ↔ matrix[j][i] O(n²) O(1)
Rotate Matrix 90° Transpose then reverse each row (or column) O(n²) O(1)
Partition (Dutch Flag) Three-way partitioning for sorting 0s, 1s, 2s O(n) O(1)

Example: Array Rotation

// Rotate left by k positions - Reversal Algorithm
function rotateLeft(arr, k) {
    const n = arr.length;
    k = k % n; // Handle k > n
    
    reverse(arr, 0, k - 1);     // Reverse first k
    reverse(arr, k, n - 1);     // Reverse remaining
    reverse(arr, 0, n - 1);     // Reverse entire array
    return arr;
}

function reverse(arr, start, end) {
    while (start < end) {
        [arr[start], arr[end]] = [arr[end], arr[start]];
        start++;
        end--;
    }
}

// Rotate right by k positions
function rotateRight(arr, k) {
    return rotateLeft(arr, arr.length - k);
}

// Example
const arr = [1, 2, 3, 4, 5];
rotateLeft(arr, 2);  // [3, 4, 5, 1, 2]

Example: Matrix Rotation 90°

// Rotate matrix 90° clockwise in-place
function rotateMatrix90(matrix) {
    const n = matrix.length;
    
    // Step 1: Transpose
    for (let i = 0; i < n; i++) {
        for (let j = i + 1; j < n; j++) {
            [matrix[i][j], matrix[j][i]] = 
            [matrix[j][i], matrix[i][j]];
        }
    }
    
    // Step 2: Reverse each row
    for (let i = 0; i < n; i++) {
        matrix[i].reverse();
    }
    
    return matrix;
}

// Example
const mat = [
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9]
];
rotateMatrix90(mat);
// Result: [[7,4,1], [8,5,2], [9,6,3]]

Example: Dutch National Flag Problem

// Sort array of 0s, 1s, and 2s in-place
function dutchFlagPartition(arr) {
    let low = 0;           // Boundary for 0s
    let mid = 0;           // Current element
    let high = arr.length - 1; // Boundary for 2s
    
    while (mid <= high) {
        if (arr[mid] === 0) {
            [arr[low], arr[mid]] = [arr[mid], arr[low]];
            low++;
            mid++;
        } else if (arr[mid] === 1) {
            mid++;
        } else { // arr[mid] === 2
            [arr[mid], arr[high]] = [arr[high], arr[mid]];
            high--;
        }
    }
    return arr;
}

// Example
const colors = [2, 0, 1, 2, 1, 0, 1, 2, 0];
dutchFlagPartition(colors); // [0, 0, 0, 1, 1, 1, 2, 2, 2]

// Time: O(n), Space: O(1), Single pass!

6. Kadane's Algorithm (Maximum Subarray Sum)

Problem Algorithm Time Key Insight
Maximum Subarray Sum Kadane's Algorithm O(n) Track max ending here vs. max so far
Maximum Product Subarray Track both max and min products O(n) Negative × negative = positive, need min for negative
Longest Subarray Sum K Prefix sum + hash map O(n) If prefix[j] - prefix[i] = k, found subarray
Subarray Sum Equals K Prefix sum + hash map for count O(n) Count occurrences of (currentSum - k) in map
Maximum Circular Subarray Max(normal Kadane, total - min subarray) O(n) Circular max = total sum - minimum subarray sum

Example: Kadane's Algorithm - Maximum Subarray Sum

// Find maximum sum of contiguous subarray
function maxSubarraySum(arr) {
    let maxSoFar = arr[0];      // Global maximum
    let maxEndingHere = arr[0];  // Maximum ending at current position
    
    for (let i = 1; i < arr.length; i++) {
        // Either extend existing subarray or start new one
        maxEndingHere = Math.max(arr[i], maxEndingHere + arr[i]);
        maxSoFar = Math.max(maxSoFar, maxEndingHere);
    }
    
    return maxSoFar;
}

// With indices tracking
function maxSubarrayWithIndices(arr) {
    let maxSoFar = arr[0];
    let maxEndingHere = arr[0];
    let start = 0, end = 0, tempStart = 0;
    
    for (let i = 1; i < arr.length; i++) {
        if (arr[i] > maxEndingHere + arr[i]) {
            maxEndingHere = arr[i];
            tempStart = i; // New subarray starts here
        } else {
            maxEndingHere = maxEndingHere + arr[i];
        }
        
        if (maxEndingHere > maxSoFar) {
            maxSoFar = maxEndingHere;
            start = tempStart;
            end = i;
        }
    }
    
    return {sum: maxSoFar, start, end};
}

// Example
const arr = [-2, 1, -3, 4, -1, 2, 1, -5, 4];
console.log(maxSubarraySum(arr)); // 6 (subarray [4,-1,2,1])
console.log(maxSubarrayWithIndices(arr)); // {sum: 6, start: 3, end: 6}

Example: Maximum Product Subarray

function maxProductSubarray(arr) {
    let maxProd = arr[0];
    let currentMax = arr[0]; // Max product ending here
    let currentMin = arr[0]; // Min product ending here
    
    for (let i = 1; i < arr.length; i++) {
        // Store currentMax before updating
        const temp = currentMax;
        
        // Current element could be:
        // 1. Standalone (if prev products are small)
        // 2. currentMax * arr[i] (if positive)
        // 3. currentMin * arr[i] (if arr[i] negative)
        currentMax = Math.max(
            arr[i],
            currentMax * arr[i],
            currentMin * arr[i]
        );
        
        currentMin = Math.min(
            arr[i],
            temp * arr[i],
            currentMin * arr[i]
        );
        
        maxProd = Math.max(maxProd, currentMax);
    }
    
    return maxProd;
}

// Example
console.log(maxProductSubarray([2, 3, -2, 4])); // 6
console.log(maxProductSubarray([-2, 0, -1]));   // 0

Example: Subarray Sum Equals K

// Count subarrays with sum = k
function subarraySumK(arr, k) {
    const prefixSumCount = new Map();
    prefixSumCount.set(0, 1); // Empty subarray
    
    let count = 0;
    let currentSum = 0;
    
    for (const num of arr) {
        currentSum += num;
        
        // If (currentSum - k) exists in map,
        // we found subarray(s) with sum k
        const target = currentSum - k;
        if (prefixSumCount.has(target)) {
            count += prefixSumCount.get(target);
        }
        
        // Update prefix sum count
        prefixSumCount.set(
            currentSum,
            (prefixSumCount.get(currentSum) || 0) + 1
        );
    }
    
    return count;
}

// Example
console.log(subarraySumK([1, 1, 1], 2));    // 2
console.log(subarraySumK([1, 2, 3], 3));    // 2

Array Key Techniques Summary

  • Two Pointers: O(n) solution for pair finding, reversing, palindromes
  • Sliding Window: Efficient for subarray/substring problems with constraints
  • Prefix Sum: O(1) range queries after O(n) preprocessing
  • Kadane's Algorithm: Maximum subarray sum in single pass O(n)
  • Dutch Flag: Three-way partitioning for sorting limited distinct values
  • Reversal: In-place array rotation with O(1) space
  • Dynamic Arrays: Amortized O(1) insertion via geometric growth
  • Sparse Arrays: Hash maps save space when array is mostly empty