Data Structures
Core Memory Layouts & Abstract Data Types
Data structures are the foundational building blocks of software engineering. Understanding how data is stored, referenced, and accessed in memory enables you to write code that runs efficiently at scale.
Track Curriculum
Subtopics & Modules
Arrays & Dynamic Arrays
Contiguous memory layout, dynamic sizing, element access, and memory alignment.
Strings & String Manipulation
Immutability vs mutability, pattern matching, StringBuilder, and encoding.
Linked Lists (Singly, Doubly, & Circular)
Node pointers, dynamic memory allocation, cycle detection, and LRU cache implementation.
Stacks & Queues
LIFO vs FIFO semantics, monotonic stacks, double-ended queues (deque), and call stack tracking.
Trees & Binary Search Trees
Hierarchical structures, BFS/DFS traversals, BST balancing, and tree recursion.
Heaps & Priority Queues
Min-heaps, max-heaps, heapify operations, median maintenance, and Top-K element tracking.
Graphs & Representations
Adjacency matrix vs adjacency list, directed/undirected graphs, Union-Find, and topological sort.
Hash Tables & Hashing
Hash functions, collision resolution (chaining vs open addressing), load factor, and dynamic rehashing.
Tries & Prefix Trees
Prefix matching, autocomplete systems, dictionary search, and Trie node structures.
Interview Questions by Module Topic
Select a module to view its related questions, or explore all modules organized by seniority level
Arrays & Dynamic Arrays
Arrays store elements in contiguous physical memory slots. Because each slot has a fixed byte width (e.g., 4 bytes for 32-bit integers), the memory controller calculates the target address instantly using arithmetic: Address = Base_Address + (Index * Element_Size). This pointer offset computation is a single CPU instruction, achieving O(1) time regardless of index size. When a dynamic array (like C++ `std::vector`, Java `ArrayList`, or Python `list`) exceeds its capacity, it allocates a new buffer double the current size, copies all existing N elements over, and frees the old buffer (an O(N) operation). However, doubling the capacity means that after copying N items, the next N inserts require no reallocation whatsoever. Summing all doubling operations across N appends yields (1 + 2 + 4 + ... + N) ≈ 2N total copy operations. Dividing 2N total operations by N total inserts yields 2 operations per append on average: O(1) Amortized Time.
#include <iostream>
#include <vector>
int main() {
std::vector<int> numbers;
// Tracking capacity doublings
for (int i = 1; i <= 16; i++) {
numbers.push_back(i);
std::cout << "Element " << i
<< " | Size: " << numbers.size()
<< " | Capacity: " << numbers.capacity() << "\n";
}
return 0;
}🔍 Code Mechanics (C++): Notice how capacity doubles from 1 -> 2 -> 4 -> 8 -> 16. Each expensive doubling step pays for the subsequent series of instant O(1) pushes, maintaining amortized O(1) complexity.