Essential Data Structures and Algorithmic Patterns for Technical Interviews

Hash Table Fundamentals

The std::unordered_map and std::unordered_set are critical for O(1) average time complexity lookups. When using unordered_map<int, int>, map.find(key) returns an iterator to the entry if present, or map.end() if not. Similarly, unordered_set provides find() and count() methods to verify existence.

Array Deduplication

// Method 1: Using unordered_set
// Method 2: Sorting followed by erase-unique idiom
std::sort(arr.begin(), arr.end());
arr.erase(std::unique(arr.begin(), arr.end()), arr.end());

Two-Pointer Techniques

Pair Summation

Given a array nums and a target, find the indices of two numbers that sum to the target. Using a hash map allows us to store visited values and their indices to achieve linear time complexity.

std::vector<int> findPair(const std::vector<int>& nums, int target) {
    std::unordered_map<int, int> seen;
    for (int i = 0; i < nums.size(); ++i) {
        int complement = target - nums[i];
        if (seen.count(complement)) return {seen[complement], i};
        seen[nums[i]] = i;
    }
    return {};
}

Container With Most Water

Use two pointers starting at both ends of the array. To potentially find a larger area, always move the pointer pointing to the shorter vertical line, as moving the longer one will only decrease the width without increasing height.

Sliding Window Patterns

Longest Substring Without Repeating Characters

Maintain a window using a set to track unique characters. If a duplicate is encountered, increment the left boundary and remove elements until the substring becomes valid again.

int maxLength(const std::string& s) {
    std::unordered_set<char> window;
    int maxLen = 0, left = 0;
    for (int right = 0; right < s.length(); ++right) {
        while (window.count(s[right])) {
            window.erase(s[left++]);
        }
        window.insert(s[right]);
        maxLen = std::max(maxLen, (int)window.size());
    }
    return maxLen;
}

Linked List Operations

Cycle Detection

Use Floyd's Cycle-Finding Algorithm (Tortoise and Hare). A fast pointer moves twice as quickly as a slow pointer. If they meet, a cycle exists. To find the entry point, reset one pointer to the head and move both at a constant speed; they will collide at the start of the cycle.

ListNode* detectCycle(ListNode* head) {
    ListNode *fast = head, *slow = head;
    while (fast && fast->next) {
        slow = slow->next;
        fast = fast->next->next;
        if (slow == fast) {
            ListNode* entry = head;
            while (entry != slow) {
                entry = entry->next;
                slow = slow->next;
            }
            return entry;
        }
    }
    return nullptr;
}

Tree Traversal and Manipulation

Maximum Depth of Binary Tree

Level-order traversal is often the most intuitive approach for depth-related problems.

int getDepth(TreeNode* root) {
    if (!root) return 0;
    return 1 + std::max(getDepth(root->left), getDepth(root->right));
}

Valid Binary Search Tree

Ensure that every node's value falls within a strict range determined by its ancestors. Use long integers to handle INT_MIN and INT_MAX edge cases.

bool isValid(TreeNode* node, long minVal, long maxVal) {
    if (!node) return true;
    if (node->val <= minVal || node->val >= maxVal) return false;
    return isValid(node->left, minVal, node->val) && 
           isValid(node->right, node->val, maxVal);
}

Tags: C++ algorithms Data Structures LeetCode

Posted on Sun, 13 Sep 2026 16:56:13 +0000 by fatfrank