Tutorials Logic, IN info@tutorialslogic.com
DSA

Top 50 DSA Interview Questions

Curated questions covering arrays, linked lists, trees, graphs, sorting, searching, dynamic programming, and Big O notation.

01

What is 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.

02

What is the difference between time complexity and space complexity?

  • Time complexity - measures how the runtime grows with input size. Expressed in Big O.
  • Space complexity - measures how much extra memory an algorithm uses. Includes auxiliary space (not counting input).
  • Auxiliary space - extra space used by the algorithm excluding input. O(1) auxiliary space means constant extra memory.
03

What is the difference between an array and a linked list?

  • Array - contiguous memory; O(1) random access; O(n) insertion/deletion in middle; fixed size (static arrays).
  • Linked List - non-contiguous nodes with pointers; O(n) access; O(1) insertion/deletion at head; dynamic size.
  • Use arrays for frequent reads; linked lists for frequent insertions/deletions at known positions.
Example
// Array: O(1) access
int arr[5] = {1,2,3,4,5};
arr[2]; // O(1)

// Linked list: O(n) access
struct Node { int data; Node* next; };
04

What is the difference between singly and doubly linked lists?

  • Singly linked list - each node has a next pointer. Can only traverse forward. Less memory.
  • Doubly linked list - each node has next and prev pointers. Can traverse both directions. Easier deletion. More memory.
  • Circular linked list - last node points back to the first.
05

What is a stack and what are its operations?

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.

Example
class Stack {
  constructor() { this.items = []; }
  push(x) { this.items.push(x); }
  pop()    { return this.items.pop(); }
  peek()   { return this.items[this.items.length - 1]; }
  isEmpty(){ return this.items.length === 0; }
}
06

What is a queue and what are its operations?

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.

Example
class Queue {
  constructor() { this.items = []; }
  enqueue(x) { this.items.push(x); }
  dequeue()  { return this.items.shift(); }
  front()    { return this.items[0]; }
  isEmpty()  { return this.items.length === 0; }
}
07

What is the difference between a stack and a queue?

  • Stack - LIFO. Last element added is the first removed. push/pop from the same end.
  • Queue - FIFO. First element added is the first removed. enqueue at rear, dequeue from front.
  • Deque (double-ended queue) - supports insertion and deletion at both ends.
08

What is a binary search tree (BST)?

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).

Example
function search(node, target) {
  if (!node) return null;
  if (target === node.val) return node;
  if (target < node.val) return search(node.left, target);
  return search(node.right, target);
}
09

What is the difference between a tree and a graph?

  • Tree - connected, acyclic graph with a root; n nodes, n-1 edges; hierarchical structure; one path between any two nodes.
  • Graph - can have cycles, multiple components, and directed/undirected edges; more general structure.
  • A tree is a special case of a graph.
10

What is the difference between BFS and DFS?

  • BFS (Breadth-First Search) - explores level by level using a queue. Finds shortest path in unweighted graphs. O(V+E).
  • DFS (Depth-First Search) - explores as deep as possible using a stack/recursion. Used for cycle detection, topological sort, connected components. O(V+E).
Example
// BFS
function bfs(graph, start) {
  const queue = [start], visited = new Set([start]);
  while (queue.length) {
    const node = queue.shift();
    for (const neighbor of graph[node]) {
      if (!visited.has(neighbor)) { visited.add(neighbor); queue.push(neighbor); }
    }
  }
}
11

What is binary search?

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.

Example
function binarySearch(arr, target) {
  let lo = 0, hi = arr.length - 1;
  while (lo <= hi) {
    const mid = Math.floor((lo + hi) / 2);
    if (arr[mid] === target) return mid;
    else if (arr[mid] < target) lo = mid + 1;
    else hi = mid - 1;
  }
  return -1;
}
12

What is the difference between merge sort and quick sort?

  • Merge Sort - divide and conquer; always O(n log n); stable; requires O(n) extra space.
  • Quick Sort - divide and conquer; O(n log n) average, O(n^2) worst case; in-place; not stable; faster in practice.
  • Use merge sort for stability; quick sort for in-place sorting.
Example
// Merge Sort
function mergeSort(arr) {
  if (arr.length <= 1) return arr;
  const mid = Math.floor(arr.length / 2);
  const left = mergeSort(arr.slice(0, mid));
  const right = mergeSort(arr.slice(mid));
  return merge(left, right);
}
13

What is dynamic programming?

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.

Example
// Fibonacci with memoization
function fib(n, memo = {}) {
  if (n <= 1) return n;
  if (memo[n]) return memo[n];
  return memo[n] = fib(n-1, memo) + fib(n-2, memo);
}

// Tabulation
function fibTab(n) {
  const dp = [0, 1];
  for (let i = 2; i <= n; i++) dp[i] = dp[i-1] + dp[i-2];
  return dp[n];
}
14

What is the difference between memoization and tabulation?

  • Memoization (top-down) - recursive approach; stores results of subproblems in a cache; only computes needed subproblems.
  • Tabulation (bottom-up) - iterative approach; fills a tl-table from base cases up; computes all subproblems.
  • Tabulation is generally faster (no recursion overhead); memoization is easier to implement.
15

What is a hash table?

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).

Example
// JavaScript Map (hash table)
const map = new Map();
map.set("key", "value");
map.get("key");    // O(1)
map.has("key");    // O(1)
map.delete("key"); // O(1)
16

What is the difference between a heap and a BST?

  • Heap - complete binary tree satisfying heap property (parent >= children for max-heap). O(log n) insert/delete. O(1) find-max. Used for priority queues.
  • BST - binary tree with ordering property. O(log n) search/insert/delete. O(n) in-order traversal gives sorted output.
Example
// Min-heap using array
class MinHeap {
  constructor() { this.heap = []; }
  push(val) { this.heap.push(val); this._bubbleUp(); }
  pop() { /* swap root with last, remove, sift down */ }
}
17

What is a trie?

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.

Example
class TrieNode {
  constructor() { this.children = {}; this.isEnd = false; }
}
class Trie {
  constructor() { this.root = new TrieNode(); }
  insert(word) {
    let node = this.root;
    for (const ch of word) {
      if (!node.children[ch]) node.children[ch] = new TrieNode();
      node = node.children[ch];
    }
    node.isEnd = true;
  }
}
18

What is topological sorting?

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).

Example
// 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}
19

What is Dijkstra's algorithm?

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).

Example
function dijkstra(graph, start) {
  const dist = {};
  const pq = [[0, start]]; // [distance, node]
  dist[start] = 0;
  while (pq.length) {
    const [d, u] = pq.shift(); // use min-heap in practice
    if (d > (dist[u] ?? Infinity)) continue;
    for (const [v, w] of graph[u]) {
      if ((dist[u] + w) < (dist[v] ?? Infinity)) {
        dist[v] = dist[u] + w;
        pq.push([dist[v], v]);
      }
    }
  }
  return dist;
}
20

What is the two-pointer technique?

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).

Example
// Find pair with target sum in sorted array
function twoSum(arr, target) {
  let lo = 0, hi = arr.length - 1;
  while (lo < hi) {
    const sum = arr[lo] + arr[hi];
    if (sum === target) return [lo, hi];
    else if (sum < target) lo++;
    else hi--;
  }
  return null;
}
21

What is the sliding window technique?

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).

Example
// Maximum sum subarray of size k
function maxSumSubarray(arr, k) {
  let sum = arr.slice(0, k).reduce((a, b) => a + b, 0);
  let max = sum;
  for (let i = k; i < arr.length; i++) {
    sum += arr[i] - arr[i - k];
    max = Math.max(max, sum);
  }
  return max;
}
22

What is Floyd's cycle detection algorithm?

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).

Example
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;
}
23

What is the difference between linear and binary search?

  • Linear search - checks each element sequentially; O(n); works on unsorted arrays.
  • Binary search - halves the search space each step; O(log n); requires sorted array.
  • For small arrays or unsorted data, linear search is fine. For large sorted data, always use binary search.
24

What are the common sorting algorithms and their complexities?

  • Bubble Sort - O(n^2) average; simple but inefficient.
  • Selection Sort - O(n^2); in-place.
  • Insertion Sort - O(n^2) average, O(n) best; good for small/nearly sorted arrays.
  • Merge Sort - O(n log n); stable; O(n) space.
  • Quick Sort - O(n log n) average; in-place.
  • Heap Sort - O(n log n); in-place; not stable.
  • Counting/Radix Sort - O(n+k); for integers.
25

What is a graph and what are its representations?

  • Adjacency Matrix - 2D array; O(1) edge lookup; O(V^2) space; good for dense graphs.
  • Adjacency List - array of lists; O(V+E) space; good for sparse graphs.
  • Edge List - list of edges; simple; used in some algorithms like Kruskal's.
Example
// Adjacency list
const graph = {
  A: ["B", "C"],
  B: ["A", "D"],
  C: ["A"],
  D: ["B"]
};

// Adjacency matrix
const matrix = [
  [0, 1, 1, 0],
  [1, 0, 0, 1],
  [1, 0, 0, 0],
  [0, 1, 0, 0]
];
26

What is the knapsack problem?

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.

Example
function knapsack(weights, values, W) {
  const n = weights.length;
  const dp = Array(n+1).fill(null).map(() => Array(W+1).fill(0));
  for (let i = 1; i <= n; i++)
    for (let w = 0; w <= W; w++) {
      dp[i][w] = dp[i-1][w];
      if (weights[i-1] <= w)
        dp[i][w] = Math.max(dp[i][w], dp[i-1][w-weights[i-1]] + values[i-1]);
    }
  return dp[n][W];
}
27

What is the difference between Prim's and Kruskal's algorithm?

  • Prim's - grows a minimum spanning tree from a starting vertex. Uses a priority queue. O(E log V). Better for dense graphs.
  • Kruskal's - sorts all edges by weight and adds them if they do not create a cycle (Union-Find). O(E log E). Better for sparse graphs.
  • Both find the Minimum Spanning Tree (MST) of a weighted undirected graph.
28

What is the difference between Bellman-Ford and Dijkstra?

  • Dijkstra - O((V+E) log V). Does not work with negative edge weights.
  • Bellman-Ford - O(VE). Works with negative edge weights. Can detect negative cycles.
  • Use Dijkstra for non-negative weights; Bellman-Ford when negative weights are possible.
29

What is the difference between a balanced and unbalanced BST?

  • Balanced BST - height is O(log n). Operations are O(log n). Examples: AVL tree, Red-Black tree.
  • Unbalanced BST - can degrade to O(n) height (like a linked list) if elements are inserted in sorted order.
  • Java TreeMap and C++ std::map use Red-Black trees. Python sortedcontainers uses a B-tree variant.
30

What is the difference between AVL tree and Red-Black tree?

  • AVL tree - strictly balanced (height difference <= 1 for every node). Faster lookups. More rotations on insert/delete.
  • Red-Black tree - loosely balanced. Fewer rotations on insert/delete. Faster insertions. Used in most standard library implementations.
31

What is the difference between a min-heap and max-heap?

  • Min-heap - parent is always <= children. Root is the minimum element. Used for: Dijkstra, Prim's, priority queues where smallest is highest priority.
  • Max-heap - parent is always >= children. Root is the maximum element. Used for: heap sort, priority queues where largest is highest priority.
Example
// Priority queue (min-heap) in JavaScript
const pq = new MinPriorityQueue();
pq.enqueue("task1", 3); // priority 3
pq.enqueue("task2", 1); // priority 1
pq.dequeue(); // returns task2 (lowest priority number)
32

What is the difference between DFS pre-order, in-order, and post-order traversal?

  • Pre-order (Root, Left, Right) - visits root first. Used for copying a tree or prefix expression.
  • In-order (Left, Root, Right) - visits nodes in sorted order for BST. Used to get sorted output.
  • Post-order (Left, Right, Root) - visits root last. Used for deleting a tree or postfix expression.
Example
function inOrder(node) {
  if (!node) return;
  inOrder(node.left);
  console.log(node.val); // sorted for BST
  inOrder(node.right);
}
33

What is the difference between a complete binary tree and a full binary tree?

  • Full binary tree - every node has either 0 or 2 children. No node has exactly 1 child.
  • Complete binary tree - all levels are fully filled except possibly the last, which is filled from left to right.
  • Perfect binary tree - all internal nodes have 2 children and all leaves are at the same level.
34

What is the difference between depth and height of a tree?

  • Depth of a node - number of edges from the root to that node. Root has depth 0.
  • Height of a node - number of edges on the longest path from that node to a leaf.
  • Height of a tree - height of the root node.
35

What is the difference between a directed and undirected graph?

  • Undirected graph - edges have no direction. If A connects to B, B connects to A.
  • Directed graph (digraph) - edges have direction. A->B does not imply B->A.
  • Weighted graph - edges have weights/costs. Used in shortest path algorithms.
36

What is the difference between strongly connected and weakly connected graphs?

  • Strongly connected - in a directed graph, every vertex is reachable from every other vertex.
  • Weakly connected - the underlying undirected graph is connected, but not all directed paths exist.
  • Connected (undirected) - there is a path between every pair of vertices.
37

What is the difference between a greedy algorithm and dynamic programming?

  • Greedy - makes the locally optimal choice at each step. Fast but does not always give the global optimum. Works for: activity selection, Huffman coding, Dijkstra.
  • Dynamic programming - considers all subproblems and stores results. Always finds the global optimum for problems with optimal substructure and overlapping subproblems.
38

What is the difference between divide and conquer and dynamic programming?

  • Divide and conquer - divides problem into independent subproblems, solves recursively, combines results. Subproblems do NOT overlap. Examples: merge sort, binary search.
  • Dynamic programming - subproblems DO overlap. Stores results to avoid recomputation. Examples: Fibonacci, knapsack, LCS.
39

What is the difference between a stack-based and queue-based approach for graph traversal?

  • Stack (DFS) - explores as deep as possible before backtracking. Finds any path. Used for cycle detection, topological sort.
  • Queue (BFS) - explores all neighbors at current depth before going deeper. Finds shortest path in unweighted graphs.
40

What is the difference between Huffman coding and run-length encoding?

  • Huffman coding - variable-length prefix codes. Frequent characters get shorter codes. Optimal for symbol-by-symbol encoding.
  • Run-length encoding (RLE) - replaces consecutive repeated characters with count + character. Efficient for data with many consecutive repeats (images, simple text).
41

What is the difference between a segment tree and a Fenwick tree (BIT)?

  • Segment tree - supports range queries (sum, min, max) and point/range updates. O(log n) for both. More flexible.
  • Fenwick tree (Binary Indexed Tree) - simpler implementation. Supports prefix sum queries and point updates. O(log n). Less memory.
Example
// Fenwick tree prefix sum
class BIT {
  constructor(n) { this.tree = new Array(n+1).fill(0); }
  update(i, delta) { for (; i < this.tree.length; i += i & -i) this.tree[i] += delta; }
  query(i) { let s = 0; for (; i > 0; i -= i & -i) s += this.tree[i]; return s; }
}
42

What is the difference between a stable and unstable sorting algorithm?

  • Stable sort - preserves the relative order of equal elements. Examples: merge sort, insertion sort, Timsort.
  • Unstable sort - may change the relative order of equal elements. Examples: quick sort, heap sort, selection sort.
  • Stability matters when sorting objects by multiple keys.
43

What is the difference between amortized and average-case complexity?

  • Average-case complexity - expected performance over all possible inputs of size n (requires probability distribution).
  • Amortized complexity - average performance per operation over a sequence of operations. No probability involved.
  • Example: dynamic array push_back is O(1) amortized (occasional O(n) resize is spread across all operations).
44

What is the difference between topological sort using DFS and Kahn algorithm?

  • DFS approach - marks nodes and pushes them after visiting descendants. Reverse postorder gives topological order.
  • Kahn algorithm - repeatedly removes nodes with indegree 0 using a queue.
  • Kahn algorithm is very useful for detecting cycles because processed node count will be less than total nodes if a cycle exists.
45

What is the difference between Dijkstra and Bellman-Ford algorithm?

  • Dijkstra - finds shortest paths with non-negative edge weights. Usually O((V + E) log V) with a priority queue.
  • Bellman-Ford - supports negative edges and can detect negative cycles. O(VE), so slower.
  • Use Bellman-Ford when negative weights are possible; use Dijkstra for faster shortest paths with non-negative weights.
46

What is the difference between Prim and Kruskal algorithm?

  • Prim - grows a minimum spanning tree from a starting node using the cheapest outgoing edge.
  • Kruskal - sorts all edges and adds the smallest edge that does not create a cycle using disjoint set union.
  • Prim is often good for dense graphs; Kruskal is simple and strong for sparse graphs.
47

What is disjoint set union and where is it used?

Disjoint set union (DSU), also called union-find, tracks groups of connected elements. It supports find and union operations efficiently using path compression and union by rank/size. It is used in Kruskal MST, cycle detection, connected components, and dynamic connectivity problems.

Example
function find(parent, x) {
  if (parent[x] !== x) parent[x] = find(parent, parent[x]);
  return parent[x];
}

function union(parent, rank, a, b) {
  let pa = find(parent, a), pb = find(parent, b);
  if (pa === pb) return false;
  if (rank[pa] < rank[pb]) [pa, pb] = [pb, pa];
  parent[pb] = pa;
  if (rank[pa] === rank[pb]) rank[pa]++;
  return true;
}
48

What is the difference between recursion and backtracking?

  • Recursion - a function calls itself to solve smaller subproblems.
  • Backtracking - a recursive search technique that builds a candidate solution, abandons invalid paths, and tries alternatives.
  • Backtracking is used for permutations, combinations, N-Queens, Sudoku, and constraint satisfaction problems.
49

What is the sliding window technique?

Sliding window is used for array or string problems where a contiguous range is expanded and shrunk while maintaining useful state. It avoids recomputing from scratch and often reduces O(n^2) brute force solutions to O(n).

Example
function maxSum(nums, k) {
  let sum = 0, best = -Infinity;
  for (let i = 0; i < nums.length; i++) {
    sum += nums[i];
    if (i >= k) sum -= nums[i - k];
    if (i >= k - 1) best = Math.max(best, sum);
  }
  return best;
}
50

What is the two-pointer technique?

The two-pointer technique uses two indexes that move through an array or string to find pairs, ranges, or partitions efficiently. It is common in sorted arrays, palindrome checks, merging, and removing duplicates.

Example
function twoSumSorted(nums, target) {
  let left = 0, right = nums.length - 1;
  while (left < right) {
    const sum = nums[left] + nums[right];
    if (sum === target) return [left, right];
    if (sum < target) left++; else right--;
  }
  return [-1, -1];
}
Next Step

Use Dsa interview prep to move into practice and application.

After reviewing interview answers, strengthen the same topic with tutorials, coding practice, or a stronger resume story.

Ready to Level Up Your Skills?

Explore 500+ free tutorials across 20+ languages and frameworks.