The Ultimate Knowledge Hub
Problem: Given a string s, find the first non-repeating character and return its index. If none exists, return -1.
JavaScript:
function firstUniqChar(s) {
const freq = {};
for (let char of s) {
freq[char] = (freq[char] || 0) + 1;
}
for (let i = 0; i < s.length; i++) {
if (freq[s[i]] === 1) return i;
}
return -1;
}
Explanation: Count frequency of each character, then iterate to find the first character with a count of 1. Time: O(n), Space: O(1).
Problem: Given an array of integers nums and an integer target, return indices of the two numbers that add up to the target.
JavaScript:
function twoSum(nums, target) {
const map = new Map();
for (let i = 0; i < nums.length; i++) {
const diff = target - nums[i];
if (map.has(diff)) return [map.get(diff), i];
map.set(nums[i], i);
}
}
Explanation: Hash map for quick lookup. Time: O(n), Space: O(n).
Problem: Move all zeroes to the end of the array while maintaining the relative order of non-zero elements.
JavaScript:
function moveZeroes(nums) {
let lastNonZero = 0;
for (let i = 0; i < nums.length; i++) {
if (nums[i] !== 0) {
[nums[lastNonZero], nums[i]] = [nums[i], nums[lastNonZero]];
lastNonZero++;
}
}
}
Explanation: In-place swapping technique. Time: O(n), Space: O(1).
JavaScript:
function lengthOfLongestSubstring(s) {
let map = new Map();
let maxLen = 0;
let start = 0;
for (let end = 0; end < s.length; end++) {
if (map.has(s[end])) {
start = Math.max(map.get(s[end]) + 1, start);
}
map.set(s[end], end);
maxLen = Math.max(maxLen, end - start + 1);
}
return maxLen;
}
Explanation: Sliding window with hash map. Time: O(n), Space: O(n).
JavaScript:
function groupAnagrams(strs) {
const map = {};
for (let str of strs) {
const sorted = str.split('').sort().join('');
if (!map[sorted]) map[sorted] = [];
map[sorted].push(str);
}
return Object.values(map);
}
Explanation: Use sorted string as key. Time: O(n * k log k), Space: O(n).
JavaScript:
function isPalindrome(s) {
s = s.replace(/[^a-z0-9]/gi, '').toLowerCase();
let left = 0, right = s.length - 1;
while (left < right) {
if (s[left] !== s[right]) return false;
left++;
right--;
}
return true;
}
Explanation: Clean and compare from both ends. Time: O(n), Space: O(1).
JavaScript:
function longestCommonPrefix(strs) {
if (!strs.length) return '';
let prefix = strs[0];
for (let i = 1; i < strs.length; i++) {
while (strs[i].indexOf(prefix) !== 0) {
prefix = prefix.slice(0, -1);
}
}
return prefix;
}
Explanation: Reduce prefix one-by-one. Time: O(n * m), Space: O(1).
JavaScript:
function rotate(nums, k) {
k %= nums.length;
reverse(nums, 0, nums.length - 1);
reverse(nums, 0, k - 1);
reverse(nums, k, nums.length - 1);
}
function reverse(arr, start, end) {
while (start < end) {
[arr[start], arr[end]] = [arr[end], arr[start]];
start++;
end--;
}
}
Explanation: Reverse parts to rotate. Time: O(n), Space: O(1).
JavaScript:
function maxSubArray(nums) {
let max = nums[0], sum = nums[0];
for (let i = 1; i < nums.length; i++) {
sum = Math.max(nums[i], sum + nums[i]);
max = Math.max(max, sum);
}
return max;
}
Explanation: Kadane’s algorithm. Time: O(n), Space: O(1).
JavaScript:
function containsDuplicate(nums) {
const set = new Set();
for (let num of nums) {
if (set.has(num)) return true;
set.add(num);
}
return false;
}
Explanation: Use Set for fast lookup. Time: O(n), Space: O(n).
Problem: Given the head of a singly linked list, reverse it.
JavaScript:
function reverseList(head) {
let prev = null, curr = head;
while (curr) {
const next = curr.next;
curr.next = prev;
prev = curr;
curr = next;
}
return prev;
}
Explanation: Iteratively reverse the links. Time: O(n), Space: O(1).
Problem: Return true if the linked list has a cycle.
JavaScript:
function hasCycle(head) {
let slow = head, fast = head;
while (fast && fast.next) {
slow = slow.next;
fast = fast.next.next;
if (slow === fast) return true;
}
return false;
}
Explanation: Floyd’s Tortoise and Hare. Time: O(n), Space: O(1).
Problem: Merge two sorted linked lists and return the merged list.
JavaScript:
function ListNode(val, next = null) {
this.val = val;
this.next = next;
}
function mergeTwoLists(l1, l2) {
let dummy = new ListNode(-1);
let curr = dummy;
while (l1 && l2) {
if (l1.val < l2.val) {
curr.next = l1;
l1 = l1.next;
} else {
curr.next = l2;
l2 = l2.next;
}
curr = curr.next;
}
curr.next = l1 || l2;
return dummy.next;
}
Explanation: Iterative merge. Time: O(n + m), Space: O(1).
JavaScript:
function removeNthFromEnd(head, n) {
let dummy = new ListNode(0);
dummy.next = head;
let fast = dummy, slow = dummy;
for (let i = 0; i <= n; i++) fast = fast.next;
while (fast) {
fast = fast.next;
slow = slow.next;
}
slow.next = slow.next.next;
return dummy.next;
}
Explanation: Use two pointers to find the node before the one to be removed. Time: O(n), Space: O(1).
JavaScript:
function middleNode(head) {
let slow = head, fast = head;
while (fast && fast.next) {
slow = slow.next;
fast = fast.next.next;
}
return slow;
}
Explanation: Fast pointer moves twice as fast as slow. Time: O(n), Space: O(1).
JavaScript:
function isPalindrome(head) {
let slow = head, fast = head;
while (fast && fast.next) {
slow = slow.next;
fast = fast.next.next;
}
let prev = null;
while (slow) {
const next = slow.next;
slow.next = prev;
prev = slow;
slow = next;
}
while (prev) {
if (head.val !== prev.val) return false;
head = head.next;
prev = prev.next;
}
return true;
}
Explanation: Reverse second half and compare with first. Time: O(n), Space: O(1).
JavaScript:
function getIntersectionNode(headA, headB) {
let a = headA, b = headB;
while (a !== b) {
a = a ? a.next : headB;
b = b ? b.next : headA;
}
return a;
}
Explanation: Equalize path lengths by switching heads. Time: O(n), Space: O(1).
JavaScript:
function detectCycle(head) {
let slow = head, fast = head;
while (fast && fast.next) {
slow = slow.next;
fast = fast.next.next;
if (slow === fast) break;
}
if (!fast || !fast.next) return null;
slow = head;
while (slow !== fast) {
slow = slow.next;
fast = fast.next;
}
return slow;
}
Explanation: After detecting a cycle, reset slow to head to find cycle start. Time: O(n), Space: O(1).
JavaScript:
function deleteDuplicates(head) {
let current = head;
while (current && current.next) {
if (current.val === current.next.val) {
current.next = current.next.next;
} else {
current = current.next;
}
}
return head;
}
Explanation: Skip nodes with duplicate values. Time: O(n), Space: O(1).
JavaScript:
function addTwoNumbers(l1, l2) {
let dummy = new ListNode(0);
let curr = dummy;
let carry = 0;
while (l1 || l2 || carry) {
const sum = (l1?.val || 0) + (l2?.val || 0) + carry;
carry = Math.floor(sum / 10);
curr.next = new ListNode(sum % 10);
curr = curr.next;
l1 = l1?.next;
l2 = l2?.next;
}
return dummy.next;
}
Explanation: Simulate addition with carry handling. Time: O(n), Space: O(n).
JavaScript:
function isValid(s) {
const stack = [];
const map = {
')': '(',
'}': '{',
']': '['
};
for (let char of s) {
if (!map[char]) {
stack.push(char);
} else {
if (stack.pop() !== map[char]) return false;
}
}
return stack.length === 0;
}
Explanation: Use stack to match brackets. Time: O(n), Space: O(n).
JavaScript:
class MinStack {
constructor() {
this.stack = [];
this.minStack = [];
}
push(x) {
this.stack.push(x);
const min = this.minStack.length === 0 ? x : Math.min(x, this.getMin());
this.minStack.push(min);
}
pop() {
this.stack.pop();
this.minStack.pop();
}
top() {
return this.stack[this.stack.length - 1];
}
getMin() {
return this.minStack[this.minStack.length - 1];
}
}
Explanation: Track min value in a parallel stack. Time: O(1) operations.
JavaScript:
function evalRPN(tokens) {
const stack = [];
for (let token of tokens) {
if (!isNaN(token)) {
stack.push(Number(token));
} else {
const b = stack.pop();
const a = stack.pop();
if (token === '+') stack.push(a + b);
else if (token === '-') stack.push(a - b);
else if (token === '*') stack.push(a * b);
else if (token === '/') stack.push(Math.trunc(a / b));
}
}
return stack.pop();
}
Explanation: Use a stack to evaluate expressions. Time: O(n), Space: O(n).
JavaScript:
class MyQueue {
constructor() {
this.stack1 = [];
this.stack2 = [];
}
push(x) {
this.stack1.push(x);
}
pop() {
if (!this.stack2.length) {
while (this.stack1.length) this.stack2.push(this.stack1.pop());
}
return this.stack2.pop();
}
peek() {
if (!this.stack2.length) {
while (this.stack1.length) this.stack2.push(this.stack1.pop());
}
return this.stack2[this.stack2.length - 1];
}
empty() {
return !this.stack1.length && !this.stack2.length;
}
}
Explanation: Use two stacks to simulate a queue. Time: Amortized O(1).
JavaScript:
class MyStack {
constructor() {
this.queue = [];
}
push(x) {
this.queue.push(x);
for (let i = 0; i < this.queue.length - 1; i++) {
this.queue.push(this.queue.shift());
}
}
pop() {
return this.queue.shift();
}
top() {
return this.queue[0];
}
empty() {
return this.queue.length === 0;
}
}
Explanation: Rotate the queue to mimic stack LIFO behavior. Time: O(n) for push.
JavaScript:
function dailyTemperatures(temperatures) {
const res = Array(temperatures.length).fill(0);
const stack = [];
for (let i = 0; i < temperatures.length; i++) {
while (stack.length && temperatures[i] > temperatures[stack[stack.length - 1]]) {
const prevIndex = stack.pop();
res[prevIndex] = i - prevIndex;
}
stack.push(i);
}
return res;
}
Explanation: Monotonic stack to track next warmer day. Time: O(n), Space: O(n).
JavaScript:
function nextGreaterElements(nums) {
const stack = [];
const res = new Array(nums.length).fill(-1);
for (let i = 0; i < nums.length * 2; i++) {
const num = nums[i % nums.length];
while (stack.length && nums[stack[stack.length - 1]] < num) {
res[stack.pop()] = num;
}
if (i < nums.length) stack.push(i);
}
return res;
}
Explanation: Circular array handling with a monotonic stack. Time: O(n), Space: O(n).
JavaScript:
function largestRectangleArea(heights) {
const stack = [], n = heights.length;
let maxArea = 0;
for (let i = 0; i <= n; i++) {
const h = i === n ? 0 : heights[i];
while (stack.length && h < heights[stack[stack.length - 1]]) {
const height = heights[stack.pop()];
const width = stack.length === 0 ? i : i - stack[stack.length - 1] - 1;
maxArea = Math.max(maxArea, height * width);
}
stack.push(i);
}
return maxArea;
}
Explanation: Use stack to manage height indices. Time: O(n), Space: O(n).
JavaScript:
function maxSlidingWindow(nums, k) {
const deque = [], res = [];
for (let i = 0; i < nums.length; i++) {
if (deque.length && deque[0] <= i - k) deque.shift();
while (deque.length && nums[i] > nums[deque[deque.length - 1]]) deque.pop();
deque.push(i);
if (i >= k - 1) res.push(nums[deque[0]]);
}
return res;
}
Explanation: Maintain deque of max values’ indices. Time: O(n), Space: O(k).
JavaScript:
class MyCircularQueue {
constructor(k) {
this.queue = Array(k);
this.size = k;
this.front = 0;
this.rear = 0;
this.count = 0;
}
enQueue(value) {
if (this.isFull()) return false;
this.queue[this.rear] = value;
this.rear = (this.rear + 1) % this.size;
this.count++;
return true;
}
deQueue() {
if (this.isEmpty()) return false;
this.front = (this.front + 1) % this.size;
this.count--;
return true;
}
Front() {
return this.isEmpty() ? -1 : this.queue[this.front];
}
Rear() {
return this.isEmpty() ? -1 : this.queue[(this.rear - 1 + this.size) % this.size];
}
isEmpty() {
return this.count === 0;
}
isFull() {
return this.count === this.size;
}
}
Explanation: Use array with modulo indexing. Time: O(1) for all ops.
Next up: Section 4 - Trees and Graphs...
JavaScript:
function inorderTraversal(root) {
const res = [];
function dfs(node) {
if (!node) return;
dfs(node.left);
res.push(node.val);
dfs(node.right);
}
dfs(root);
return res;
}
function inorderTraversalIterative(root) {
const res = [], stack = [];
let curr = root;
while (curr || stack.length) {
while (curr) {
stack.push(curr);
curr = curr.left;
}
curr = stack.pop();
res.push(curr.val);
curr = curr.right;
}
return res;
}
Explanation:
Inorder traversal (Left → Node → Right) is used to get sorted order in BSTs.
Recursive approach uses depth-first traversal and implicit call stack.
Iterative approach manually simulates recursion using an explicit stack.
Time Complexity: O(n)
Space Complexity: O(h) (call stack or explicit stack, where h = height of the tree)
JavaScript:
function maxDepth(root) {
if (!root) return 0;
return 1 + Math.max(maxDepth(root.left), maxDepth(root.right));
}
Explanation:
This is a basic DFS problem.
At each node, we recursively compute the maximum depth of the left and right subtrees, and add 1.
Useful to understand recursive tree traversal and base case design.
Time Complexity: O(n)
Space Complexity: O(h)
JavaScript:
function invertTree(root) {
if (!root) return null;
[root.left, root.right] = [invertTree(root.right), invertTree(root.left)];
return root;
}
Explanation:
Recursively swap left and right child of every node.
Helps develop intuition about structural transformations on trees.
Time Complexity: O(n)
Space Complexity: O(h)
JavaScript:
function diameterOfBinaryTree(root) {
let diameter = 0;
function dfs(node) {
if (!node) return 0;
let left = dfs(node.left);
let right = dfs(node.right);
diameter = Math.max(diameter, left + right);
return 1 + Math.max(left, right);
}
dfs(root);
return diameter;
}
Explanation:
Diameter is the length of the longest path between any two nodes in the tree.
At each node, compute max depth of left and right subtrees, sum them, and update global maximum.
Time Complexity: O(n)
Space Complexity: O(h)
JavaScript:
function isSameTree(p, q) {
if (!p && !q) return true;
if (!p || !q || p.val !== q.val) return false;
return isSameTree(p.left, q.left) && isSameTree(p.right, q.right);
}
Explanation:
Recursively compare structure and node values.
A common question to test recursive thinking and base case design.
Time Complexity: O(n)
Space Complexity: O(h)
JavaScript:
function lowestCommonAncestor(root, p, q) {
if (!root || root === p || root === q) return root;
const left = lowestCommonAncestor(root.left, p, q);
const right = lowestCommonAncestor(root.right, p, q);
return !left ? right : !right ? left : root;
}
Explanation:
LCA is the deepest node that has both p and q as descendants.
Use post-order DFS. If p and q are found in different subtrees, current node is the LCA.
Time Complexity: O(n)
Space Complexity: O(h)
JavaScript:
function levelOrder(root) {
const res = [], queue = [];
if (root) queue.push(root);
while (queue.length) {
const level = [], n = queue.length;
for (let i = 0; i < n; i++) {
const node = queue.shift();
level.push(node.val);
if (node.left) queue.push(node.left);
if (node.right) queue.push(node.right);
}
res.push(level);
}
return res;
}
Explanation:
Breadth-first search to traverse level by level.
Use a queue to store nodes of current level.
Time Complexity: O(n)
Space Complexity: O(n)
JavaScript:
function rightSideView(root) {
const res = [], queue = [];
if (root) queue.push(root);
while (queue.length) {
let size = queue.length;
for (let i = 0; i < size; i++) {
const node = queue.shift();
if (i === size - 1) res.push(node.val);
if (node.left) queue.push(node.left);
if (node.right) queue.push(node.right);
}
}
return res;
}
Explanation:
Track the last node of each level in level-order traversal.
That last node is what you see from the right.
Time Complexity: O(n)
Space Complexity: O(n)
JavaScript:
function hasCycle(edges, n) {
const parent = Array(n).fill(0).map((_, i) => i);
function find(x) {
if (x !== parent[x]) parent[x] = find(parent[x]);
return parent[x];
}
function union(x, y) {
const rootX = find(x), rootY = find(y);
if (rootX === rootY) return false;
parent[rootX] = rootY;
return true;
}
for (const [u, v] of edges) {
if (!union(u, v)) return true;
}
return false;
}
Explanation:
Use Union-Find (Disjoint Set Union) to detect if adding an edge connects two nodes already in the same set.
If yes, it forms a cycle.
Time Complexity: O(E * α(N)) where α is inverse Ackermann.
Space Complexity: O(N)
JavaScript:
function topologicalSort(numCourses, prerequisites) {
const graph = Array.from({ length: numCourses }, () => []);
const indegree = Array(numCourses).fill(0);
for (let [u, v] of prerequisites) {
graph[v].push(u);
indegree[u]++;
}
const queue = [], res = [];
for (let i = 0; i < numCourses; i++) {
if (indegree[i] === 0) queue.push(i);
}
while (queue.length) {
const node = queue.shift();
res.push(node);
for (let nei of graph[node]) {
indegree[nei]--;
if (indegree[nei] === 0) queue.push(nei);
}
}
return res.length === numCourses ? res : [];
}
Explanation:
Topological sort orders nodes in a Directed Acyclic Graph (DAG) such that for any edge (u → v), u appears before v.
Kahn’s algorithm repeatedly removes nodes with zero indegree and updates their neighbors.
Time Complexity: O(V + E)
Space Complexity: O(V + E)