Data Structures and Algorithms for Frontend Interviews

Generally speaking, most frontend interviews tend to focus more on frontend-oriented coding questions, such as building UI components, managing state, or working with APIs. That said, many top companies will still test your understanding of core data structures and algorithms. It’s always a good idea to brush up on the fundamentals, just in case.
Honest Advice on Data Structures and Algorithms for Frontend
Now, let me be honest with you for a second. From my own experience, both as someone who has been interviewed for over 13 years and as someone who has conducted numerous interviews myself, if a company is strictly asking LeetCode-style algorithm questions for a frontend role, that’s a red flag. It suggests that the company likely lacks the necessary competency and resources to evaluate frontend engineers properly. Solving a linked list reversal or inverting a binary tree is not a strong indicator of whether someone can build a scalable UI, understand browser behavior, or write maintainable frontend code.
With that being said, I still think it’s essential to have a high-level understanding of the significant data structures, not because you’ll be asked to implement them from scratch, but because knowing them will help you recognize patterns and think through problems efficiently.
I would focus on a few core concepts: stacks, sets, queues, and priority queues; some basic tree traversal logic; ES6 maps; and hash tables. Occasionally, you may encounter a TRIE-style question that relates to a frontend concept (such as memoization or autocomplete). However, I’ve rarely been asked to reverse a linked list or build a graph using adjacency lists. And if I am? I walk away. Not worth it.
Now, if you do want to brush up on these concepts, the best free tutorials out there are from a nonprofit called FreeCodeCamp. We’re not affiliated with them at FrontendLead.com, but their videos are solid and beginner-friendly. Below, you’ll find a high-level summary of each concept, a simple example, and a direct link to the in-depth video for each topic.
Table of Contents
- Stacks
- Sets
- Queues & Priority Queues
- Binary Search Trees
- BST Traversals & Height
- Maps & ES6 Map
- Hash Tables
- Linked List
- Trie
- Heap (Max & Min)
- Graph & Adjacency Lists
- Graph BFS
1. Stacks (Built from Scratch)
A stack is a linear data structure that follows the Last In, First Out (LIFO) principle. This means the last item added is the first one removed — like a stack of plates. Stacks are used in scenarios like undo/redo functionality, browser history, and tracking function calls.
class Stack {
constructor() {
this.items = []; // Internal array to hold elements
}
push(value) {
// Add an element to the top of the stack
this.items[this.items.length] = value;
}
pop() {
// Return undefined if the stack is empty
if (this.items.length === 0) return undefined;
// Get the last item (top of stack)
const last = this.items[this.items.length - 1];
// Reduce the length of the array to remove it
this.items.length = this.items.length - 1;
// Return the removed value
return last;
}
peek() {
// Look at the top value without removing it
return this.items[this.items.length - 1];
}
isEmpty() {
// Return true if no elements are in the stack
return this.items.length === 0;
}
}
// Example usage
const stack = new Stack();
stack.push(10); // [10]
stack.push(20); // [10, 20]
stack.pop(); // 20
2. Sets (Built from Scratch)
A set is a collection of unique values — no duplicates allowed. You can use sets when you need to filter out duplicates, quickly check membership, or store flags. JavaScript includes a built-in Set object, but here’s how to build one from scratch.
class MySet {
constructor() {
this.items = {}; // Use an object to store unique values as keys
}
add(value) {
// Add the value as a key (won't allow duplicates)
this.items[value] = true;
}
has(value) {
// Return true if the key exists
return this.items.hasOwnProperty(value);
}
delete(value) {
// Remove the key from the object
delete this.items[value];
}
values() {
// Return an array of all stored keys
return Object.keys(this.items);
}
}
// Example usage
const set = new MySet();
set.add("apple");
set.add("apple"); // Duplicate won't be added
set.has("apple"); // true
set.delete("apple");
3. Queues (Built from Scratch)
A queue is a data structure that follows the First In, First Out (FIFO) principle. This means the first item added is the first one removed — like people waiting in line. Queues are useful for scheduling tasks, animations, or buffering streams.
class Queue {
constructor() {
this.items = {}; // Object to hold the queue
this.head = 0; // Index of the front element
this.tail = 0; // Index of the next empty slot
}
enqueue(value) {
// Add element at the tail index
this.items[this.tail] = value;
this.tail++;
}
dequeue() {
// If queue is empty, return undefined
if (this.head === this.tail) return undefined;
// Get the front item
const value = this.items[this.head];
// Remove it from the object
delete this.items[this.head];
this.head++;
// Return the dequeued value
return value;
}
peek() {
// Look at the front of the queue
return this.items[this.head];
}
isEmpty() {
// True if no elements left
return this.head === this.tail;
}
}
// Example usage
const queue = new Queue();
queue.enqueue("A");
queue.enqueue("B");
queue.dequeue(); // "A"
4. Binary Search Tree (Built from Scratch)
A Binary Search Tree (BST) is a hierarchical data structure where each node has up to two children. The left child contains values less than the parent, and the right child contains values greater than the parent. BSTs allow fast lookup, insertion, and deletion — typically in O(log n) time. They’re useful in search-heavy applications and form the foundation for other advanced data structures.
class TreeNode {
constructor(value) {
this.value = value; // The value stored in the node
this.left = null; // Left child
this.right = null; // Right child
}
}
class BinarySearchTree {
constructor() {
this.root = null; // Root of the tree
}
insert(value) {
const newNode = new TreeNode(value);
if (this.root === null) {
// If tree is empty, set the new node as root
this.root = newNode;
return;
}
let current = this.root;
while (true) {
if (value < current.value) {
// Go left
if (current.left === null) {
current.left = newNode;
return;
}
current = current.left;
} else {
// Go right
if (current.right === null) {
current.right = newNode;
return;
}
current = current.right;
}
}
}
find(value) {
let current = this.root;
while (current) {
if (value === current.value) return true;
current = value < current.value ? current.left : current.right;
}
return false;
}
}
// Example usage
const bst = new BinarySearchTree();
bst.insert(10);
bst.insert(5);
bst.insert(15);
bst.find(15); // true
bst.find(7); // false
5. BST Traversals & Height
Once you’ve built a tree, you’ll often want to traverse it — meaning visit each node in a specific order. The three main types of depth-first traversal are:
- In-order (left → root → right)
- Pre-order (root → left → right)
- Post-order (left → right → root)
Additionally, calculating the height of a tree is useful for understanding its depth or balance.
class TreeTraversal {
static inOrder(node, result = []) {
if (!node) return result;
TreeTraversal.inOrder(node.left, result); // Traverse left subtree
result.push(node.value); // Visit root
TreeTraversal.inOrder(node.right, result); // Traverse right subtree
return result;
}
static preOrder(node, result = []) {
if (!node) return result;
result.push(node.value); // Visit root
TreeTraversal.preOrder(node.left, result); // Traverse left
TreeTraversal.preOrder(node.right, result); // Traverse right
return result;
}
static postOrder(node, result = []) {
if (!node) return result;
TreeTraversal.postOrder(node.left, result); // Traverse left
TreeTraversal.postOrder(node.right, result); // Traverse right
result.push(node.value); // Visit root
return result;
}
static height(node) {
if (!node) return -1; // Height of empty tree is -1
const leftHeight = TreeTraversal.height(node.left);
const rightHeight = TreeTraversal.height(node.right);
return 1 + Math.max(leftHeight, rightHeight); // 1 + taller side
}
}
// Example usage
const exampleTree = new BinarySearchTree();
exampleTree.insert(10);
exampleTree.insert(5);
exampleTree.insert(15);
exampleTree.insert(2);
exampleTree.insert(7);
// Traversals
TreeTraversal.inOrder(exampleTree.root); // [2, 5, 7, 10, 15]
TreeTraversal.preOrder(exampleTree.root); ... premium content.