Skip to content

Data Structures & Algorithms in C++

This guide covers essential data structures and algorithms commonly asked in technical interviews for embedded systems and validation engineering roles.


Arrays

Basic Operations

#include <iostream>
#include <vector>
#include <algorithm>

// ============================================================
// STATIC ARRAY
// ============================================================
int arr[5] = {1, 2, 3, 4, 5};
int size = sizeof(arr) / sizeof(arr[0]);  // Get array size

// ============================================================
// DYNAMIC ARRAY (std::vector)
// ============================================================
std::vector<int> vec = {1, 2, 3, 4, 5};

// Common operations
vec.push_back(6);           // Add to end: O(1) amortized
vec.pop_back();             // Remove from end: O(1)
vec.insert(vec.begin(), 0); // Insert at beginning: O(n)
vec.erase(vec.begin());     // Remove from beginning: O(n)
int first = vec.front();    // Access first: O(1)
int last = vec.back();      // Access last: O(1)
int elem = vec[2];          // Random access: O(1)
size_t len = vec.size();    // Get size: O(1)
bool empty = vec.empty();   // Check if empty: O(1)
vec.clear();                // Remove all: O(n)
vec.reserve(100);           // Pre-allocate capacity

// Iteration
for (int i = 0; i < vec.size(); i++) {
    std::cout << vec[i] << " ";
}

for (const auto& val : vec) {
    std::cout << val << " ";
}

for (auto it = vec.begin(); it != vec.end(); ++it) {
    std::cout << *it << " ";
}

Two Pointers Technique

// Find pair with given sum (sorted array)
bool hasPairWithSum(const std::vector<int>& arr, int target) {
    int left = 0;
    int right = arr.size() - 1;

    while (left < right) {
        int sum = arr[left] + arr[right];
        if (sum == target) {
            return true;
        } else if (sum < target) {
            left++;
        } else {
            right--;
        }
    }
    return false;
}

// Remove duplicates from sorted array (in-place)
int removeDuplicates(std::vector<int>& nums) {
    if (nums.empty()) return 0;

    int writeIdx = 1;
    for (int readIdx = 1; readIdx < nums.size(); readIdx++) {
        if (nums[readIdx] != nums[readIdx - 1]) {
            nums[writeIdx++] = nums[readIdx];
        }
    }
    return writeIdx;
}

// Reverse array in-place
void reverseArray(std::vector<int>& arr) {
    int left = 0, right = arr.size() - 1;
    while (left < right) {
        std::swap(arr[left++], arr[right--]);
    }
}

Sliding Window

// Maximum sum subarray of size k
int maxSumSubarray(const std::vector<int>& arr, int k) {
    if (arr.size() < k) return -1;

    int windowSum = 0;
    for (int i = 0; i < k; i++) {
        windowSum += arr[i];
    }

    int maxSum = windowSum;
    for (int i = k; i < arr.size(); i++) {
        windowSum += arr[i] - arr[i - k];  // Slide window
        maxSum = std::max(maxSum, windowSum);
    }
    return maxSum;
}

// Longest substring without repeating characters
int lengthOfLongestSubstring(const std::string& s) {
    std::unordered_map<char, int> charIndex;
    int maxLen = 0, start = 0;

    for (int end = 0; end < s.length(); end++) {
        if (charIndex.count(s[end]) && charIndex[s[end]] >= start) {
            start = charIndex[s[end]] + 1;
        }
        charIndex[s[end]] = end;
        maxLen = std::max(maxLen, end - start + 1);
    }
    return maxLen;
}
// Standard binary search
int binarySearch(const std::vector<int>& arr, int target) {
    int left = 0, right = arr.size() - 1;

    while (left <= right) {
        int mid = left + (right - left) / 2;  // Avoid overflow

        if (arr[mid] == target) {
            return mid;
        } else if (arr[mid] < target) {
            left = mid + 1;
        } else {
            right = mid - 1;
        }
    }
    return -1;  // Not found
}

// Find first occurrence
int findFirst(const std::vector<int>& arr, int target) {
    int left = 0, right = arr.size() - 1;
    int result = -1;

    while (left <= right) {
        int mid = left + (right - left) / 2;

        if (arr[mid] == target) {
            result = mid;
            right = mid - 1;  // Continue searching left
        } else if (arr[mid] < target) {
            left = mid + 1;
        } else {
            right = mid - 1;
        }
    }
    return result;
}

// Find last occurrence
int findLast(const std::vector<int>& arr, int target) {
    int left = 0, right = arr.size() - 1;
    int result = -1;

    while (left <= right) {
        int mid = left + (right - left) / 2;

        if (arr[mid] == target) {
            result = mid;
            left = mid + 1;  // Continue searching right
        } else if (arr[mid] < target) {
            left = mid + 1;
        } else {
            right = mid - 1;
        }
    }
    return result;
}

// Search in rotated sorted array
int searchRotated(const std::vector<int>& nums, int target) {
    int left = 0, right = nums.size() - 1;

    while (left <= right) {
        int mid = left + (right - left) / 2;

        if (nums[mid] == target) return mid;

        // Left half is sorted
        if (nums[left] <= nums[mid]) {
            if (target >= nums[left] && target < nums[mid]) {
                right = mid - 1;
            } else {
                left = mid + 1;
            }
        }
        // Right half is sorted
        else {
            if (target > nums[mid] && target <= nums[right]) {
                left = mid + 1;
            } else {
                right = mid - 1;
            }
        }
    }
    return -1;
}

Linked List

Singly Linked List

// Node definition
struct ListNode {
    int val;
    ListNode* next;

    ListNode(int x) : val(x), next(nullptr) {}
    ListNode(int x, ListNode* n) : val(x), next(n) {}
};

// ============================================================
// BASIC OPERATIONS
// ============================================================

// Insert at beginning: O(1)
ListNode* insertAtHead(ListNode* head, int val) {
    ListNode* newNode = new ListNode(val);
    newNode->next = head;
    return newNode;
}

// Insert at end: O(n)
ListNode* insertAtTail(ListNode* head, int val) {
    ListNode* newNode = new ListNode(val);
    if (!head) return newNode;

    ListNode* curr = head;
    while (curr->next) {
        curr = curr->next;
    }
    curr->next = newNode;
    return head;
}

// Delete node with value: O(n)
ListNode* deleteNode(ListNode* head, int val) {
    if (!head) return nullptr;

    if (head->val == val) {
        ListNode* temp = head->next;
        delete head;
        return temp;
    }

    ListNode* curr = head;
    while (curr->next && curr->next->val != val) {
        curr = curr->next;
    }

    if (curr->next) {
        ListNode* temp = curr->next;
        curr->next = curr->next->next;
        delete temp;
    }
    return head;
}

// Get length: O(n)
int getLength(ListNode* head) {
    int count = 0;
    while (head) {
        count++;
        head = head->next;
    }
    return count;
}

// Search: O(n)
bool search(ListNode* head, int val) {
    while (head) {
        if (head->val == val) return true;
        head = head->next;
    }
    return false;
}

Common Interview Problems

// ============================================================
// REVERSE LINKED LIST (Iterative)
// ============================================================
ListNode* reverseList(ListNode* head) {
    ListNode* prev = nullptr;
    ListNode* curr = head;

    while (curr) {
        ListNode* next = curr->next;
        curr->next = prev;
        prev = curr;
        curr = next;
    }
    return prev;
}

// ============================================================
// REVERSE LINKED LIST (Recursive)
// ============================================================
ListNode* reverseListRecursive(ListNode* head) {
    if (!head || !head->next) return head;

    ListNode* newHead = reverseListRecursive(head->next);
    head->next->next = head;
    head->next = nullptr;
    return newHead;
}

// ============================================================
// DETECT CYCLE (Floyd's Cycle Detection)
// ============================================================
bool hasCycle(ListNode* head) {
    ListNode* slow = head;
    ListNode* fast = head;

    while (fast && fast->next) {
        slow = slow->next;
        fast = fast->next->next;

        if (slow == fast) return true;
    }
    return false;
}

// Find cycle start
ListNode* detectCycleStart(ListNode* head) {
    ListNode* slow = head;
    ListNode* fast = head;

    while (fast && fast->next) {
        slow = slow->next;
        fast = fast->next->next;

        if (slow == fast) {
            // Found cycle, find start
            slow = head;
            while (slow != fast) {
                slow = slow->next;
                fast = fast->next;
            }
            return slow;
        }
    }
    return nullptr;
}

// ============================================================
// FIND MIDDLE NODE
// ============================================================
ListNode* findMiddle(ListNode* head) {
    ListNode* slow = head;
    ListNode* fast = head;

    while (fast && fast->next) {
        slow = slow->next;
        fast = fast->next->next;
    }
    return slow;
}

// ============================================================
// MERGE TWO SORTED LISTS
// ============================================================
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
    ListNode dummy(0);
    ListNode* tail = &dummy;

    while (l1 && l2) {
        if (l1->val <= l2->val) {
            tail->next = l1;
            l1 = l1->next;
        } else {
            tail->next = l2;
            l2 = l2->next;
        }
        tail = tail->next;
    }

    tail->next = l1 ? l1 : l2;
    return dummy.next;
}

// ============================================================
// REMOVE NTH NODE FROM END
// ============================================================
ListNode* removeNthFromEnd(ListNode* head, int n) {
    ListNode dummy(0);
    dummy.next = head;

    ListNode* first = &dummy;
    ListNode* second = &dummy;

    // Move first n+1 steps ahead
    for (int i = 0; i <= n; i++) {
        first = first->next;
    }

    // Move both until first reaches end
    while (first) {
        first = first->next;
        second = second->next;
    }

    // Delete nth node from end
    ListNode* temp = second->next;
    second->next = second->next->next;
    delete temp;

    return dummy.next;
}

// ============================================================
// CHECK PALINDROME
// ============================================================
bool isPalindrome(ListNode* head) {
    if (!head || !head->next) return true;

    // Find middle
    ListNode* slow = head;
    ListNode* fast = head;
    while (fast->next && fast->next->next) {
        slow = slow->next;
        fast = fast->next->next;
    }

    // Reverse second half
    ListNode* secondHalf = reverseList(slow->next);

    // Compare
    ListNode* first = head;
    ListNode* second = secondHalf;
    bool result = true;

    while (second) {
        if (first->val != second->val) {
            result = false;
            break;
        }
        first = first->next;
        second = second->next;
    }

    // Restore list (optional)
    slow->next = reverseList(secondHalf);

    return result;
}

Hash Map / Unordered Map

Basic Operations

#include <unordered_map>
#include <map>

// ============================================================
// UNORDERED MAP (Hash Table) - Average O(1) operations
// ============================================================
std::unordered_map<std::string, int> hashMap;

// Insert
hashMap["apple"] = 5;
hashMap.insert({"banana", 3});
hashMap.emplace("cherry", 7);

// Access
int count = hashMap["apple"];           // Creates entry if not exists
int count2 = hashMap.at("banana");      // Throws if not exists

// Check existence
if (hashMap.count("apple")) { /* exists */ }
if (hashMap.find("apple") != hashMap.end()) { /* exists */ }

// Delete
hashMap.erase("apple");

// Iteration
for (const auto& [key, value] : hashMap) {
    std::cout << key << ": " << value << "\n";
}

// ============================================================
// ORDERED MAP (Red-Black Tree) - O(log n) operations
// ============================================================
std::map<std::string, int> orderedMap;
// Keys are automatically sorted
orderedMap["zebra"] = 1;
orderedMap["apple"] = 2;
orderedMap["mango"] = 3;
// Iteration gives: apple, mango, zebra

Common Interview Problems

// ============================================================
// TWO SUM
// ============================================================
std::vector<int> twoSum(const std::vector<int>& nums, int target) {
    std::unordered_map<int, int> numToIndex;

    for (int i = 0; i < nums.size(); i++) {
        int complement = target - nums[i];
        if (numToIndex.count(complement)) {
            return {numToIndex[complement], i};
        }
        numToIndex[nums[i]] = i;
    }
    return {};
}

// ============================================================
// FIRST NON-REPEATING CHARACTER
// ============================================================
int firstUniqChar(const std::string& s) {
    std::unordered_map<char, int> freq;

    for (char c : s) {
        freq[c]++;
    }

    for (int i = 0; i < s.length(); i++) {
        if (freq[s[i]] == 1) return i;
    }
    return -1;
}

// ============================================================
// GROUP ANAGRAMS
// ============================================================
std::vector<std::vector<std::string>> groupAnagrams(
    std::vector<std::string>& strs) {

    std::unordered_map<std::string, std::vector<std::string>> groups;

    for (const auto& s : strs) {
        std::string key = s;
        std::sort(key.begin(), key.end());
        groups[key].push_back(s);
    }

    std::vector<std::vector<std::string>> result;
    for (auto& [key, group] : groups) {
        result.push_back(std::move(group));
    }
    return result;
}

// ============================================================
// SUBARRAY SUM EQUALS K
// ============================================================
int subarraySum(const std::vector<int>& nums, int k) {
    std::unordered_map<int, int> prefixCount;
    prefixCount[0] = 1;  // Empty prefix

    int count = 0, prefixSum = 0;

    for (int num : nums) {
        prefixSum += num;

        // Check if (prefixSum - k) exists
        if (prefixCount.count(prefixSum - k)) {
            count += prefixCount[prefixSum - k];
        }

        prefixCount[prefixSum]++;
    }
    return count;
}

// ============================================================
// LRU CACHE
// ============================================================
class LRUCache {
private:
    int capacity_;
    std::list<std::pair<int, int>> cache_;  // {key, value}
    std::unordered_map<int, std::list<std::pair<int, int>>::iterator> map_;

public:
    LRUCache(int capacity) : capacity_(capacity) {}

    int get(int key) {
        if (map_.find(key) == map_.end()) return -1;

        // Move to front (most recently used)
        cache_.splice(cache_.begin(), cache_, map_[key]);
        return map_[key]->second;
    }

    void put(int key, int value) {
        if (map_.find(key) != map_.end()) {
            // Update existing
            map_[key]->second = value;
            cache_.splice(cache_.begin(), cache_, map_[key]);
            return;
        }

        // Evict if full
        if (cache_.size() == capacity_) {
            int lruKey = cache_.back().first;
            cache_.pop_back();
            map_.erase(lruKey);
        }

        // Insert new
        cache_.push_front({key, value});
        map_[key] = cache_.begin();
    }
};

Trees

Binary Tree

// Node definition
struct TreeNode {
    int val;
    TreeNode* left;
    TreeNode* right;

    TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
};

// ============================================================
// TREE TRAVERSALS
// ============================================================

// Inorder (Left, Root, Right) - gives sorted order for BST
void inorder(TreeNode* root, std::vector<int>& result) {
    if (!root) return;
    inorder(root->left, result);
    result.push_back(root->val);
    inorder(root->right, result);
}

// Preorder (Root, Left, Right)
void preorder(TreeNode* root, std::vector<int>& result) {
    if (!root) return;
    result.push_back(root->val);
    preorder(root->left, result);
    preorder(root->right, result);
}

// Postorder (Left, Right, Root)
void postorder(TreeNode* root, std::vector<int>& result) {
    if (!root) return;
    postorder(root->left, result);
    postorder(root->right, result);
    result.push_back(root->val);
}

// Level Order (BFS)
std::vector<std::vector<int>> levelOrder(TreeNode* root) {
    std::vector<std::vector<int>> result;
    if (!root) return result;

    std::queue<TreeNode*> q;
    q.push(root);

    while (!q.empty()) {
        int size = q.size();
        std::vector<int> level;

        for (int i = 0; i < size; i++) {
            TreeNode* node = q.front();
            q.pop();
            level.push_back(node->val);

            if (node->left) q.push(node->left);
            if (node->right) q.push(node->right);
        }
        result.push_back(level);
    }
    return result;
}

// Iterative Inorder using Stack
std::vector<int> inorderIterative(TreeNode* root) {
    std::vector<int> result;
    std::stack<TreeNode*> stack;
    TreeNode* curr = root;

    while (curr || !stack.empty()) {
        while (curr) {
            stack.push(curr);
            curr = curr->left;
        }
        curr = stack.top();
        stack.pop();
        result.push_back(curr->val);
        curr = curr->right;
    }
    return result;
}

Binary Search Tree (BST)

// ============================================================
// BST OPERATIONS
// ============================================================

// Search: O(h) where h = height
TreeNode* searchBST(TreeNode* root, int val) {
    if (!root || root->val == val) return root;

    if (val < root->val) {
        return searchBST(root->left, val);
    }
    return searchBST(root->right, val);
}

// Insert: O(h)
TreeNode* insertBST(TreeNode* root, int val) {
    if (!root) return new TreeNode(val);

    if (val < root->val) {
        root->left = insertBST(root->left, val);
    } else {
        root->right = insertBST(root->right, val);
    }
    return root;
}

// Delete: O(h)
TreeNode* deleteNode(TreeNode* root, int key) {
    if (!root) return nullptr;

    if (key < root->val) {
        root->left = deleteNode(root->left, key);
    } else if (key > root->val) {
        root->right = deleteNode(root->right, key);
    } else {
        // Node found
        if (!root->left) {
            TreeNode* temp = root->right;
            delete root;
            return temp;
        }
        if (!root->right) {
            TreeNode* temp = root->left;
            delete root;
            return temp;
        }

        // Two children: get inorder successor
        TreeNode* successor = root->right;
        while (successor->left) {
            successor = successor->left;
        }
        root->val = successor->val;
        root->right = deleteNode(root->right, successor->val);
    }
    return root;
}

// Validate BST
bool isValidBST(TreeNode* root, long minVal = LONG_MIN, long maxVal = LONG_MAX) {
    if (!root) return true;

    if (root->val <= minVal || root->val >= maxVal) return false;

    return isValidBST(root->left, minVal, root->val) &&
           isValidBST(root->right, root->val, maxVal);
}

Common Tree Problems

// ============================================================
// MAXIMUM DEPTH
// ============================================================
int maxDepth(TreeNode* root) {
    if (!root) return 0;
    return 1 + std::max(maxDepth(root->left), maxDepth(root->right));
}

// ============================================================
// CHECK IF BALANCED
// ============================================================
int checkHeight(TreeNode* root) {
    if (!root) return 0;

    int leftHeight = checkHeight(root->left);
    if (leftHeight == -1) return -1;

    int rightHeight = checkHeight(root->right);
    if (rightHeight == -1) return -1;

    if (std::abs(leftHeight - rightHeight) > 1) return -1;

    return 1 + std::max(leftHeight, rightHeight);
}

bool isBalanced(TreeNode* root) {
    return checkHeight(root) != -1;
}

// ============================================================
// LOWEST COMMON ANCESTOR
// ============================================================
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
    if (!root || root == p || root == q) return root;

    TreeNode* left = lowestCommonAncestor(root->left, p, q);
    TreeNode* right = lowestCommonAncestor(root->right, p, q);

    if (left && right) return root;
    return left ? left : right;
}

// ============================================================
// PATH SUM
// ============================================================
bool hasPathSum(TreeNode* root, int targetSum) {
    if (!root) return false;

    if (!root->left && !root->right) {
        return root->val == targetSum;
    }

    return hasPathSum(root->left, targetSum - root->val) ||
           hasPathSum(root->right, targetSum - root->val);
}

// ============================================================
// INVERT BINARY TREE
// ============================================================
TreeNode* invertTree(TreeNode* root) {
    if (!root) return nullptr;

    std::swap(root->left, root->right);
    invertTree(root->left);
    invertTree(root->right);

    return root;
}

// ============================================================
// SERIALIZE AND DESERIALIZE
// ============================================================
class Codec {
public:
    std::string serialize(TreeNode* root) {
        if (!root) return "null";

        return std::to_string(root->val) + "," +
               serialize(root->left) + "," +
               serialize(root->right);
    }

    TreeNode* deserialize(const std::string& data) {
        std::queue<std::string> nodes;
        std::stringstream ss(data);
        std::string item;

        while (std::getline(ss, item, ',')) {
            nodes.push(item);
        }

        return buildTree(nodes);
    }

private:
    TreeNode* buildTree(std::queue<std::string>& nodes) {
        std::string val = nodes.front();
        nodes.pop();

        if (val == "null") return nullptr;

        TreeNode* node = new TreeNode(std::stoi(val));
        node->left = buildTree(nodes);
        node->right = buildTree(nodes);

        return node;
    }
};

Sorting Algorithms

// ============================================================
// QUICK SORT - Average O(n log n), Worst O(n²)
// ============================================================
int partition(std::vector<int>& arr, int low, int high) {
    int pivot = arr[high];
    int i = low - 1;

    for (int j = low; j < high; j++) {
        if (arr[j] < pivot) {
            i++;
            std::swap(arr[i], arr[j]);
        }
    }
    std::swap(arr[i + 1], arr[high]);
    return i + 1;
}

void quickSort(std::vector<int>& arr, int low, int high) {
    if (low < high) {
        int pi = partition(arr, low, high);
        quickSort(arr, low, pi - 1);
        quickSort(arr, pi + 1, high);
    }
}

// ============================================================
// MERGE SORT - O(n log n) always
// ============================================================
void merge(std::vector<int>& arr, int left, int mid, int right) {
    std::vector<int> leftArr(arr.begin() + left, arr.begin() + mid + 1);
    std::vector<int> rightArr(arr.begin() + mid + 1, arr.begin() + right + 1);

    int i = 0, j = 0, k = left;

    while (i < leftArr.size() && j < rightArr.size()) {
        if (leftArr[i] <= rightArr[j]) {
            arr[k++] = leftArr[i++];
        } else {
            arr[k++] = rightArr[j++];
        }
    }

    while (i < leftArr.size()) arr[k++] = leftArr[i++];
    while (j < rightArr.size()) arr[k++] = rightArr[j++];
}

void mergeSort(std::vector<int>& arr, int left, int right) {
    if (left < right) {
        int mid = left + (right - left) / 2;
        mergeSort(arr, left, mid);
        mergeSort(arr, mid + 1, right);
        merge(arr, left, mid, right);
    }
}

// ============================================================
// HEAP SORT - O(n log n)
// ============================================================
void heapify(std::vector<int>& arr, int n, int i) {
    int largest = i;
    int left = 2 * i + 1;
    int right = 2 * i + 2;

    if (left < n && arr[left] > arr[largest]) largest = left;
    if (right < n && arr[right] > arr[largest]) largest = right;

    if (largest != i) {
        std::swap(arr[i], arr[largest]);
        heapify(arr, n, largest);
    }
}

void heapSort(std::vector<int>& arr) {
    int n = arr.size();

    // Build max heap
    for (int i = n / 2 - 1; i >= 0; i--) {
        heapify(arr, n, i);
    }

    // Extract elements
    for (int i = n - 1; i > 0; i--) {
        std::swap(arr[0], arr[i]);
        heapify(arr, i, 0);
    }
}

Time & Space Complexity Reference

Data Structure Access Search Insert Delete Space
Array O(1) O(n) O(n) O(n) O(n)
Linked List O(n) O(n) O(1)* O(1)* O(n)
Hash Map N/A O(1)† O(1)† O(1)† O(n)
BST O(log n)‡ O(log n)‡ O(log n)‡ O(log n)‡ O(n)
Heap O(1)§ O(n) O(log n) O(log n) O(n)

*With reference to node, †Average case, ‡Balanced tree, §For min/max only



References

  • Introduction to Algorithms (CLRS)
  • LeetCode, HackerRank problem sets
  • cppreference.com