Dynamic Arrays

Dynamic arrays are resizable arrays that can grow and shrink during runtime. Understanding how to use vectors in C++ and their operations is fundamental for competitive programming.

What are Dynamic Arrays?

Dynamic arrays automatically manage memory and can resize themselves as needed. In C++, the primary dynamic array is the vector:

Advantages of Dynamic Arrays:

  • Automatic memory management
  • Can grow and shrink as needed
  • Random access to elements (O(1))
  • Rich set of operations and algorithms

Basic Vector Operations

Here are the fundamental operations you'll use with vectors:


#include <vector>
#include <iostream>

int main() {
    // Declaration and initialization
    vector<int> v;                    // Empty vector
    vector<int> v2(5);               // 5 elements, all 0
    vector<int> v3(5, 10);           // 5 elements, all 10
    vector<int> v4 = {1, 2, 3, 4, 5}; // Initialize with values
    
    // Adding elements
    v.push_back(10);  // Add to end
    v.push_back(20);
    v.push_back(30);
    
    // Accessing elements
    cout << "First element: " << v[0] << endl;        // Direct access
    cout << "First element: " << v.front() << endl;   // First element
    cout << "Last element: " << v.back() << endl;     // Last element
    cout << "Safe access: " << v.at(1) << endl;       // Bounds checking
    
    // Size and capacity
    cout << "Size: " << v.size() << endl;
    cout << "Capacity: " << v.capacity() << endl;
    cout << "Empty? " << v.empty() << endl;
    
    // Removing elements
    v.pop_back();     // Remove last element
    
    return 0;
}
                    

Iterating Through Vectors

There are several ways to iterate through vector elements:


#include <vector>

int main() {
    vector<int> numbers = {10, 20, 30, 40, 50};
    
    // Method 1: Range-based for loop (C++11+)
    cout << "Range-based loop: ";
    for (int num : numbers) {
        cout << num << " ";
    }
    cout << endl;
    
    // Method 2: Traditional for loop with index
    cout << "Index-based loop: ";
    for (size_t i = 0; i < numbers.size(); i++) {
        cout << numbers[i] << " ";
    }
    cout << endl;
    
    // Method 3: Iterator-based loop
    cout << "Iterator-based loop: ";
    for (auto it = numbers.begin(); it != numbers.end(); ++it) {
        cout << *it << " ";
    }
    cout << endl;
    
    // Method 4: Reverse iteration
    cout << "Reverse loop: ";
    for (auto it = numbers.rbegin(); it != numbers.rend(); ++it) {
        cout << *it << " ";
    }
    cout << endl;
    
    return 0;
}
                    

2D Vectors (Matrices)

Vectors can be nested to create 2D arrays or matrices:


#include <vector>

int main() {
    // Create a 3x4 matrix
    int rows = 3, cols = 4;
    vector<vector<int>> matrix(rows, vector<int>(cols));
    
    // Initialize with values
    vector<vector<int>> matrix2 = {
        {1, 2, 3},
        {4, 5, 6},
        {7, 8, 9}
    };
    
    // Fill with values
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < cols; j++) {
            matrix[i][j] = i * cols + j + 1;
        }
    }
    
    // Print matrix
    for (const auto& row : matrix) {
        for (int val : row) {
            cout << val << " ";
        }
        cout << endl;
    }
    
    // Jagged arrays (different row sizes)
    vector<vector<int>> jagged = {
        {1, 2},
        {3, 4, 5, 6},
        {7}
    };
    
    return 0;
}
                    

Advanced Vector Operations

Vectors support many advanced operations for manipulation:


#include <vector>
#include <algorithm>

int main() {
    vector<int> v = {5, 2, 8, 1, 9, 3};
    
    // Insert elements
    v.insert(v.begin() + 2, 100);      // Insert 100 at position 2
    v.insert(v.end(), {6, 7});         // Insert multiple at end
    
    // Erase elements
    v.erase(v.begin() + 1);            // Erase element at position 1
    v.erase(v.begin() + 2, v.begin() + 4); // Erase range
    
    // Resize operations
    v.resize(10);          // Resize to 10 elements (fills with 0)
    v.resize(5);           // Shrink to 5 elements
    v.reserve(20);         // Reserve capacity for 20 elements
    
    // Clear and swap
    vector<int> v2 = {10, 20, 30};
    v.swap(v2);            // Swap contents of v and v2
    v.clear();             // Remove all elements
    
    // Using algorithms
    vector<int> nums = {5, 2, 8, 1, 9, 3};
    
    sort(nums.begin(), nums.end());                    // Sort ascending
    reverse(nums.begin(), nums.end());                 // Reverse order
    
    auto it = find(nums.begin(), nums.end(), 8);       // Find element
    if (it != nums.end()) {
        cout << "Found 8 at position: " << it - nums.begin() << endl;
    }
    
    return 0;
}
                    

Memory Management

Understanding how vectors manage memory helps with performance:


#include <vector>

int main() {
    vector<int> v;
    
    cout << "Initial - Size: " << v.size() 
         << ", Capacity: " << v.capacity() << endl;
    
    // Adding elements shows capacity growth
    for (int i = 1; i <= 10; i++) {
        v.push_back(i);
        cout << "After adding " << i 
             << " - Size: " << v.size() 
             << ", Capacity: " << v.capacity() << endl;
    }
    
    // Reserve space to avoid reallocations
    vector<int> v2;
    v2.reserve(1000);  // Reserve space for 1000 elements
    
    // Shrink to fit - reduce capacity to size
    v.shrink_to_fit();
    
    return 0;
}
                    

Common Patterns

Some frequently used patterns in competitive programming:

Reading Input into Vector


// Read n integers into vector
int n;
cin >> n;
vector<int> arr(n);
for (int i = 0; i < n; i++) {
    cin >> arr[i];
}

// Alternative: using istream_iterator
vector<int> arr2(istream_iterator<int>(cin), 
                 istream_iterator<int>());
                    

Prefix Sums


vector<int> computePrefixSums(const vector<int>& arr) {
    vector<int> prefix(arr.size() + 1, 0);
    for (int i = 0; i < arr.size(); i++) {
        prefix[i + 1] = prefix[i] + arr[i];
    }
    return prefix;
}
                    

Sliding Window Maximum


vector<int> slidingWindowMax(vector<int>& nums, int k) {
    vector<int> result;
    deque<int> dq; // Store indices
    
    for (int i = 0; i < nums.size(); i++) {
        // Remove elements outside window
        while (!dq.empty() && dq.front() <= i - k) {
            dq.pop_front();
        }
        
        // Maintain decreasing order
        while (!dq.empty() && nums[dq.back()] <= nums[i]) {
            dq.pop_back();
        }
        
        dq.push_back(i);
        
        if (i >= k - 1) {
            result.push_back(nums[dq.front()]);
        }
    }
    
    return result;
}
                    

Performance Tips

Optimization Tips

  • Reserve space: Use reserve() when you know the size
  • Use emplace_back: More efficient than push_back for complex objects
  • Pass by reference: Avoid copying large vectors
  • Use [] for known bounds: Faster than at() but no bounds checking
  • Prefer range-based loops: Cleaner and often faster

CodeForces Challenge Problems

Practice dynamic array operations:

Easy

Array Operations

Practice basic vector operations and manipulations.

Vectors STL
Solve Problem