DSA

DSA Interview Questions and Answers

1. Find the First Non-Repeating Character in a String

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


2. Two Sum Problem

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


3. Move Zeroes to End

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


4. Longest Substring Without Repeating Characters

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


5. Group Anagrams

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


6. Valid Palindrome

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


7. Longest Common Prefix

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


8. Rotate Array

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


9. Maximum Subarray (Kadane’s Algorithm)

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


10. Contains Duplicate

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


Section 2: Linked Lists


1. Reverse a Linked List

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


2. Detect Cycle in a Linked List

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


3. Merge Two Sorted Lists

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


More Linked List questions and next sections coming up...

4. Remove N-th Node From End of List

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


5. Find the Middle of Linked List

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


6. Palindrome Linked List

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


7. Intersection of Two Linked Lists

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


8. Linked List Cycle II (Start of Cycle)

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


9. Remove Duplicates from Sorted List

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


10. Add Two Numbers (as Linked Lists)

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


Section 3: Stacks and Queues


1. Valid Parentheses

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


2. Min Stack

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.


3. Evaluate Reverse Polish Notation

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


4. Implement Queue using Stacks

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


5. Implement Stack using Queues

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.


6. Daily Temperatures

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


7. Next Greater Element

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


8. Largest Rectangle in Histogram

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


9. Sliding Window Maximum

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


10. Design a Circular Queue

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

Section 4: Trees and Graphs


1. Binary Tree Inorder Traversal (Recursive & Iterative)

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)


2. Maximum Depth of Binary 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)


3. Invert Binary Tree

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)


4. Diameter of Binary Tree

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)


5. Same Tree Check

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)


6. Lowest Common Ancestor (LCA)

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)


7. Level Order Traversal (BFS)

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)


8. Right Side View of Binary Tree

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)


9. Detect Cycle in Undirected Graph (Union Find)

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)


10. Topological Sort (Kahn's Algorithm)

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)


Scroll to Top