Curated questions covering arrays, linked lists, trees, graphs, sorting, searching, dynamic programming, and Big O notation.
Big O notation describes the worst-case time or space complexity of an algorithm in terms of input size n. It represents the growth rate. Common complexities: O(1) constant, O(log n) logarithmic, O(n) linear, O(n log n) linearithmic, O(n^2) quadratic, O(2^n) exponential, O(n!) factorial.
// Array: O(1) access\nint arr[5] = {1,2,3,4,5};\narr[2]; // O(1)\n\n// Linked list: O(n) access\nstruct Node { int data; Node* next; };A stack is a LIFO (Last In First Out) data structure. Operations: push (add to top), pop (remove from top), peek/top (view top), isEmpty. Used for: function call stack, undo operations, expression evaluation, DFS.
class Stack {\n constructor() { this.items = []; }\n push(x) { this.items.push(x); }\n pop() { return this.items.pop(); }\n peek() { return this.items[this.items.length - 1]; }\n isEmpty(){ return this.items.length === 0; }\n}A queue is a FIFO (First In First Out) data structure. Operations: enqueue (add to rear), dequeue (remove from front), peek/front, isEmpty. Used for: BFS, task scheduling, print queues, producer-consumer.
class Queue {\n constructor() { this.items = []; }\n enqueue(x) { this.items.push(x); }\n dequeue() { return this.items.shift(); }\n front() { return this.items[0]; }\n isEmpty() { return this.items.length === 0; }\n}A BST is a binary tree where each node's left subtree contains only nodes with smaller values, and the right subtree contains only nodes with larger values. Operations: search, insert, delete - O(log n) average, O(n) worst case (unbalanced).
function search(node, target) {\n if (!node) return null;\n if (target === node.val) return node;\n if (target < node.val) return search(node.left, target);\n return search(node.right, target);\n}// BFS\nfunction bfs(graph, start) {\n const queue = [start], visited = new Set([start]);\n while (queue.length) {\n const node = queue.shift();\n for (const neighbor of graph[node]) {\n if (!visited.has(neighbor)) { visited.add(neighbor); queue.push(neighbor); }\n }\n }\n}Binary search finds a target in a sorted array by repeatedly halving the search space. Time complexity: O(log n). Requires the array to be sorted.
function binarySearch(arr, target) {\n let lo = 0, hi = arr.length - 1;\n while (lo <= hi) {\n const mid = Math.floor((lo + hi) / 2);\n if (arr[mid] === target) return mid;\n else if (arr[mid] < target) lo = mid + 1;\n else hi = mid - 1;\n }\n return -1;\n}// Merge Sort\nfunction mergeSort(arr) {\n if (arr.length <= 1) return arr;\n const mid = Math.floor(arr.length / 2);\n const left = mergeSort(arr.slice(0, mid));\n const right = mergeSort(arr.slice(mid));\n return merge(left, right);\n}Dynamic programming solves problems by breaking them into overlapping subproblems and storing results (memoization or tabulation) to avoid redundant computation. Used for: Fibonacci, knapsack, longest common subsequence, shortest path.
// Fibonacci with memoization\nfunction fib(n, memo = {}) {\n if (n <= 1) return n;\n if (memo[n]) return memo[n];\n return memo[n] = fib(n-1, memo) + fib(n-2, memo);\n}\n\n// Tabulation\nfunction fibTab(n) {\n const dp = [0, 1];\n for (let i = 2; i <= n; i++) dp[i] = dp[i-1] + dp[i-2];\n return dp[n];\n}A hash tl-table maps keys to values using a hash function. Average O(1) for insert, delete, and lookup. Collisions are handled by chaining (linked lists) or open addressing (probing).
// JavaScript Map (hash table)\nconst map = new Map();\nmap.set("key", "value");\nmap.get("key"); // O(1)\nmap.has("key"); // O(1)\nmap.delete("key"); // O(1)// Min-heap using array\nclass MinHeap {\n constructor() { this.heap = []; }\n push(val) { this.heap.push(val); this._bubbleUp(); }\n pop() { /* swap root with last, remove, sift down */ }\n}A trie (prefix tree) is a tree where each node represents a character. Used for efficient string search, autocomplete, and spell checking. Search/insert: O(m) where m is string length.
class TrieNode {\n constructor() { this.children = {}; this.isEnd = false; }\n}\nclass Trie {\n constructor() { this.root = new TrieNode(); }\n insert(word) {\n let node = this.root;\n for (const ch of word) {\n if (!node.children[ch]) node.children[ch] = new TrieNode();\n node = node.children[ch];\n }\n node.isEnd = true;\n }\n}Topological sort orders vertices of a directed acyclic graph (DAG) such that for every edge u->v, u comes before v. Used for task scheduling, build systems, and dependency resolution. Implemented with DFS or Kahn's algorithm (BFS).
// Kahn's algorithm (BFS)\nfunction topoSort(graph, n) {\n const inDegree = new Array(n).fill(0);\n for (const [u, v] of graph) inDegree[v]++;\n const queue = [];\n for (let i = 0; i < n; i++) if (inDegree[i] === 0) queue.push(i);\n const result = [];\n while (queue.length) {\n const u = queue.shift();\n result.push(u);\n for (const v of adj[u]) if (--inDegree[v] === 0) queue.push(v);\n }\n return result;\n}Dijkstra's algorithm finds the shortest path from a source to all vertices in a weighted graph with non-negative edges. Uses a min-heap (priority queue). Time complexity: O((V+E) log V).
function dijkstra(graph, start) {\n const dist = {};\n const pq = [[0, start]]; // [distance, node]\n dist[start] = 0;\n while (pq.length) {\n const [d, u] = pq.shift(); // use min-heap in practice\n if (d > (dist[u] ?? Infinity)) continue;\n for (const [v, w] of graph[u]) {\n if ((dist[u] + w) < (dist[v] ?? Infinity)) {\n dist[v] = dist[u] + w;\n pq.push([dist[v], v]);\n }\n }\n }\n return dist;\n}Two pointers use two indices to traverse an array or linked list, often from both ends or at different speeds. Used for: finding pairs with a sum, removing duplicates, detecting cycles (Floyd's algorithm).
// Find pair with target sum in sorted array\nfunction twoSum(arr, target) {\n let lo = 0, hi = arr.length - 1;\n while (lo < hi) {\n const sum = arr[lo] + arr[hi];\n if (sum === target) return [lo, hi];\n else if (sum < target) lo++;\n else hi--;\n }\n return null;\n}The sliding window technique maintains a window of elements and slides it across the array to solve subarray/substring problems efficiently. Reduces O(n^2) brute force to O(n).
// Maximum sum subarray of size k\nfunction maxSumSubarray(arr, k) {\n let sum = arr.slice(0, k).reduce((a, b) => a + b, 0);\n let max = sum;\n for (let i = k; i < arr.length; i++) {\n sum += arr[i] - arr[i - k];\n max = Math.max(max, sum);\n }\n return max;\n}Floyd's algorithm (tortoise and hare) detects cycles in a linked list using two pointers - slow (moves 1 step) and fast (moves 2 steps). If they meet, a cycle exists. Time: O(n), Space: O(1).
function hasCycle(head) {\n let slow = head, fast = head;\n while (fast && fast.next) {\n slow = slow.next;\n fast = fast.next.next;\n if (slow === fast) return true;\n }\n return false;\n}// Adjacency list\nconst graph = {\n A: ["B", "C"],\n B: ["A", "D"],\n C: ["A"],\n D: ["B"]\n};\n\n// Adjacency matrix\nconst matrix = [\n [0, 1, 1, 0],\n [1, 0, 0, 1],\n [1, 0, 0, 0],\n [0, 1, 0, 0]\n];The 0/1 knapsack problem: given items with weights and values, find the maximum value that fits in a knapsack of capacity W. Each item can be taken (1) or not (0). Solved with DP in O(nW) time.
function knapsack(weights, values, W) {\n const n = weights.length;\n const dp = Array(n+1).fill(null).map(() => Array(W+1).fill(0));\n for (let i = 1; i <= n; i++)\n for (let w = 0; w <= W; w++) {\n dp[i][w] = dp[i-1][w];\n if (weights[i-1] <= w)\n dp[i][w] = Math.max(dp[i][w], dp[i-1][w-weights[i-1]] + values[i-1]);\n }\n return dp[n][W];\n}// Priority queue (min-heap) in JavaScript\nconst pq = new MinPriorityQueue();\npq.enqueue("task1", 3); // priority 3\npq.enqueue("task2", 1); // priority 1\npq.dequeue(); // returns task2 (lowest priority number)function inOrder(node) {\n if (!node) return;\n inOrder(node.left);\n console.log(node.val); // sorted for BST\n inOrder(node.right);\n}// Fenwick tree prefix sum\nclass BIT {\n constructor(n) { this.tree = new Array(n+1).fill(0); }\n update(i, delta) { for (; i < this.tree.length; i += i & -i) this.tree[i] += delta; }\n query(i) { let s = 0; for (; i > 0; i -= i & -i) s += this.tree[i]; return s; }\n}// Iterative DFS\nfunction dfs(graph, start) {\n const stack = [start], visited = new Set();\n while (stack.length) {\n const node = stack.pop();\n if (visited.has(node)) continue;\n visited.add(node);\n for (const neighbor of graph[node]) stack.push(neighbor);\n }\n}class UnionFind {\n constructor(n) { this.parent = Array.from({length:n},(_,i)=>i); }\n find(x) { return this.parent[x]===x ? x : this.parent[x]=this.find(this.parent[x]); }\n union(x,y) { this.parent[this.find(x)] = this.find(y); }\n connected(x,y) { return this.find(x)===this.find(y); }\n}// LIS - O(n^2) DP\nfunction lis(arr) {\n const dp = new Array(arr.length).fill(1);\n for (let i = 1; i < arr.length; i++)\n for (let j = 0; j < i; j++)\n if (arr[j] < arr[i]) dp[i] = Math.max(dp[i], dp[j]+1);\n return Math.max(...dp);\n}// Standard LCS: O(m*n) space\nconst dp = Array(m+1).fill(null).map(()=>Array(n+1).fill(0));\n\n// Space-optimized: O(n) space\nlet prev = new Array(n+1).fill(0);\nlet curr = new Array(n+1).fill(0);\n// Use only two rowsExplore 500+ free tutorials across 20+ languages and frameworks.