Data Structures Using Java
Interview Questions with Answers

Explore Our Courses
1. What is the difference between Array and ArrayList in Java?
Example:
int[] arr = {1, 2, 3}; // Array
ArrayList<Integer> list = new ArrayList<>(); // ArrayList
list.add(1);
Master Data Structures in Java and unlock high-paying job opportunities. Start today!
2. Explain time and space complexity with examples.
- Time Complexity measures how the runtime of an algorithm grows with input size n.
- Space Complexity measures how much extra memory is required.
Example:
// O(n) time and space
void printAndStore(int[] arr) {
List<Integer> list = new ArrayList<>();
for (int num : arr) {
System.out.println(num); // O(n)
list.add(num); // O(n) space
}
}
Common Complexities:
- Constant: O(1)
- Linear: O(n)
- Logarithmic: O(log n)
- Quadratic: O(n^2)
3. What are the advantages of using Collections over Arrays?
Advantages:
- Resizable – ArrayList, LinkedList grow/shrink as needed.
- Predefined methods – like sort(), contains(), addAll().
- Type safety – using Generics.
- Easier insertion/deletion – especially with LinkedList.
Example:
List<String> names = new ArrayList<>();
names.add("Soniya");
names.remove("Soniya");
Collections.sort(names);
4. How does Java manage memory for data structures like LinkedList and HashMap?
- LinkedList:
- Each node contains data and references to next/previous.
- Memory is allocated on the heap for each node dynamically.
- Garbage Collector reclaims memory when nodes are unreachable.
- HashMap:
- Uses an array of buckets, each storing a linked list or tree of entries.
- Keys and values are stored as objects in heap.
- Internal resizing occurs when load factor exceeds a threshold (default 0.75).
5. What are Generics in Java? Why are they useful in data structures?
Generics allow classes and methods to operate on objects of various types while providing compile-time type safety.
Use in DS:
- Prevents runtime ClassCastException
- Supports reusability and cleaner code.
Example:
List<String> names = new ArrayList<>();
// Without generics: List names = new ArrayList(); leads to casting issues.
6. Compare Comparator and Comparable. Where are they used in DS problems?
Use in DS: Sorting objects in TreeSet, PriorityQueue, Arrays.sort(), etc.
Example:
class Student implements Comparable<Student> {
public int marks;
public int compareTo(Student s) {
return this.marks - s.marks;
}
}
Comparator<Student> byName = (s1, s2) -> s1.name.compareTo(s2.name);
7. Explain Autoboxing and Unboxing in Java with respect to collections.
- Autoboxing: Converting a primitive to its wrapper class.
- Unboxing: Converting a wrapper object to its primitive.
Required in Collections because they only store objects.
Example:
List<Integer> list = new ArrayList<>();
list.add(5); // Autoboxing (int → Integer)
int x = list.get(0); // Unboxing (Integer → int)
8. When would you use a primitive array over a Collection in Java?
Use primitive arrays when:
- Performance is critical (no boxing overhead).
- You know the fixed size beforehand.
- Working with simple data like integers, floats, etc.
Example:
int[] scores = new int[100]; // Faster and uses less memory
Use Collections when:
- Size changes frequently.
- Need flexibility or utility methods (sorting, searching).
9. What is the difference between shallow copy and deep copy in DS context?
- Shallow Copy: Copies object references (not actual objects).
- Deep Copy: Copies entire object structure recursively.
Example:
ArrayList<ArrayList<Integer>> original = new ArrayList<>();
ArrayList<ArrayList<Integer>> shallow = original; // Shallow
ArrayList<ArrayList<Integer>> deep = new ArrayList<>();
for (ArrayList<Integer> list : original) {
deep.add(new ArrayList<>(list)); // Deep copy
}
In shallow copy, changes affect both. In deep copy, they don’t.
10. What are wrapper classes and why are they important in DS?
Wrapper Classes convert primitives into objects.
Examples: int → Integer, char → Character
Importance in DS:
- Collections (like ArrayList, HashMap) work with objects.
- Enable use of utility methods and constants.
Example:
List<Integer> nums = new ArrayList<>();
nums.add(10); // Uses Integer wrapper
They also support null values (unlike primitives) and are essential for autoboxing.
11. Write a program to find the maximum subarray sum (Kadane’s Algorithm).
Explanation: Kadane’s Algorithm finds the contiguous subarray with the maximum sum in O(n) time.
public class MaxSubArraySum {
public static int maxSubArraySum(int[] nums) {
int maxSum = nums[0], currentSum = nums[0];
for (int i = 1; i < nums.length; i++) {
currentSum = Math.max(nums[i], currentSum + nums[i]); // extend or restart
maxSum = Math.max(maxSum, currentSum);
}
return maxSum;
}
public static void main(String[] args) {
int[] arr = {-2, 1, -3, 4, -1, 2, 1, -5, 4};
System.out.println("Max Subarray Sum: " + maxSubArraySum(arr)); // Output: 6
}
}
12. How to rotate an array by K steps in Java?
Explanation: Right rotate array by k steps using reversal algorithm.
import java.util.Arrays;
public class RotateArray {
public static void rotate(int[] nums, int k) {
k = k % nums.length; // handle large k
reverse(nums, 0, nums.length - 1);
reverse(nums, 0, k - 1);
reverse(nums, k, nums.length - 1);
}
private static void reverse(int[] nums, int start, int end) {
while (start < end) {
int tmp = nums[start];
nums[start++] = nums[end];
nums[end--] = tmp;
}
}
public static void main(String[] args) {
int[] nums = {1, 2, 3, 4, 5};
rotate(nums, 2); // Output: [4, 5, 1, 2, 3]
System.out.println(Arrays.toString(nums));
}
}
13. Implement the two-pointer technique to remove duplicates from a sorted array.
public class RemoveDuplicates {
public static int removeDuplicates(int[] nums) {
if (nums.length == 0) return 0;
int i = 0; // slow pointer
for (int j = 1; j < nums.length; j++) { // fast pointer
if (nums[j] != nums[i]) {
nums[++i] = nums[j];
}
}
return i + 1;
}
public static void main(String[] args) {
int[] nums = {1, 1, 2, 2, 3};
int length = removeDuplicates(nums);
for (int i = 0; i < length; i++) {
System.out.print(nums[i] + " "); // Output: 1 2 3
}
}
}
14. How do you reverse a string without using built-in methods?
public class ReverseString {
public static String reverse(String s) {
char[] chars = s.toCharArray();
int start = 0, end = chars.length - 1;
while (start < end) {
char temp = chars[start];
chars[start++] = chars[end];
chars[end--] = temp;
}
return new String(chars);
}
public static void main(String[] args) {
System.out.println(reverse("Java")); // Output: avaJ
}
}
15. Explain the difference between String, StringBuilder, and StringBuffer.
Example:
StringBuilder sb = new StringBuilder("Java");
sb.append(" Developer"); // Mutable
System.out.println(sb); // Output: Java Developer
16. Check if a string is a palindrome (ignore special characters and case).
public class PalindromeCheck {
public static boolean isPalindrome(String s) {
s = s.replaceAll("[^a-zA-Z0-9]", "").toLowerCase();
int i = 0, j = s.length() - 1;
while (i < j) {
if (s.charAt(i++) != s.charAt(j--)) return false;
}
return true;
}
public static void main(String[] args) {
System.out.println(isPalindrome("A man, a plan, a canal: Panama")); // true
}
}
17. Find the longest common prefix among an array of strings.
public class LongestCommonPrefix {
public static String longestCommonPrefix(String[] strs) {
if (strs.length == 0) return "";
String prefix = strs[0];
for (int i = 1; i < strs.length; i++) {
while (!strs[i].startsWith(prefix)) {
prefix = prefix.substring(0, prefix.length() - 1);
if (prefix.isEmpty()) return "";
}
}
return prefix;
}
public static void main(String[] args) {
String[] input = {"flower", "flow", "flight"};
System.out.println(longestCommonPrefix(input)); // Output: "fl"
}
}
18. Given two strings, check if one is a permutation of the other.
import java.util.Arrays;
public class CheckPermutation {
public static boolean isPermutation(String s1, String s2) {
if (s1.length() != s2.length()) return false;
char[] a = s1.toCharArray();
char[] b = s2.toCharArray();
Arrays.sort(a);
Arrays.sort(b);
return Arrays.equals(a, b);
}
public static void main(String[] args) {
System.out.println(isPermutation("abc", "bca")); // true
}
}
19. Implement anagram check using frequency count in Java.
public class AnagramCheck {
public static boolean isAnagram(String s1, String s2) {
if (s1.length() != s2.length()) return false;
int[] count = new int[26]; // assuming lowercase letters
for (char c : s1.toCharArray()) count[c - 'a']++;
for (char c : s2.toCharArray()) {
if (--count[c - 'a'] < 0) return false;
}
return true;
}
public static void main(String[] args) {
System.out.println(isAnagram("listen", "silent")); // true
}
}
20. How to search an element in a rotated sorted array?
Explanation: Use modified binary search – one half is always sorted.
public class SearchRotatedArray {
public static int search(int[] nums, int target) {
int low = 0, high = nums.length - 1;
while (low <= high) {
int mid = (low + high) / 2;
if (nums[mid] == target) return mid;
// Left half is sorted
if (nums[low] <= nums[mid]) {
if (nums[low] <= target && target < nums[mid]) high = mid - 1;
else low = mid + 1;
} else {
// Right half is sorted
if (nums[mid] < target && target <= nums[high]) low = mid + 1;
else high = mid - 1;
}
}
return -1;
}
public static void main(String[] args) {
int[] arr = {4, 5, 6, 7, 0, 1, 2};
System.out.println(search(arr, 0)); // Output: 4
}
}
Crack your next tech interview with confidence—master Java Data Structures the smart way!
21. What is the difference between Singly and Doubly Linked Lists?
22. How to detect a cycle in a linked list?
Answer: Floyd’s Cycle Detection (Tortoise and Hare algorithm)
public class CycleDetection {
static class Node {
int data;
Node next;
Node(int data) { this.data = data; }
}
public static boolean hasCycle(Node head) {
Node slow = head, fast = head;
while (fast != null && fast.next != null) {
slow = slow.next;
fast = fast.next.next;
if (slow == fast) return true;
}
return false;
}
}
23. Write a Java function to reverse a linked list.
public class ReverseList {
static class Node {
int val;
Node next;
Node(int val) { this.val = val; }
}
public static Node reverse(Node head) {
Node prev = null, curr = head;
while (curr != null) {
Node next = curr.next;
curr.next = prev;
prev = curr;
curr = next;
}
return prev; // New head
}
}
24. Merge two sorted linked lists.
public class MergeSortedLists {
static class Node {
int val;
Node next;
Node(int val) { this.val = val; }
}
public static Node merge(Node l1, Node l2) {
Node dummy = new Node(-1);
Node tail = dummy;
while (l1 != null && l2 != null) {
if (l1.val <= l2.val) {
tail.next = l1; l1 = l1.next;
} else {
tail.next = l2; l2 = l2.next;
}
tail = tail.next;
}
tail.next = (l1 != null) ? l1 : l2;
return dummy.next;
}
}
25. Find the middle element of a linked list.
public class MiddleNode {
static class Node {
int val;
Node next;
Node(int val) { this.val = val; }
}
public static Node findMiddle(Node head) {
Node slow = head, fast = head;
while (fast != null && fast.next != null) {
slow = slow.next;
fast = fast.next.next;
}
return slow; // Middle node
}
}
26. How to remove nth node from the end of a linked list?
public class RemoveNthFromEnd {
static class Node {
int val;
Node next;
Node(int val) { this.val = val; }
}
public static Node removeNthFromEnd(Node head, int n) {
Node dummy = new Node(0);
dummy.next = head;
Node first = dummy, second = dummy;
for (int i = 0; i <= n; i++) first = first.next;
while (first != null) {
first = first.next;
second = second.next;
}
second.next = second.next.next;
return dummy.next;
}
}
27. How is memory management handled in Java for linked structures?
- Heap Allocation: Each Node object is dynamically allocated on the heap.
- Garbage Collector (GC): Java automatically frees memory when nodes become unreachable.
- No Manual free(): Unlike C/C++, Java doesn’t require manual memory deallocation.
- No Memory Leaks (mostly): As long as there are no unintended references, memory is managed safely.
28. What are dummy nodes and why are they useful in list manipulation?
Dummy Node: A placeholder node at the start (or end) of a list.
Why useful?
- Avoids null checks for head insertion/deletion.
- Simplifies logic for corner cases.
Example:
Node dummy = new Node(-1);
dummy.next = head; // Helps in uniform traversal and deletion
Used in problems like:
- Merging two lists
- Removing nth node
- Reversing a sublist
29. Implement a program to check if a linked list is a palindrome.
Steps:
- Find middle.
- Reverse second half.
- Compare both halves.
public class PalindromeList {
static class Node {
int val;
Node next;
Node(int val) { this.val = val; }
}
public static boolean isPalindrome(Node head) {
if (head == null || head.next == null) return true;
Node slow = head, fast = head;
while (fast != null && fast.next != null) {
slow = slow.next;
fast = fast.next.next;
}
Node secondHalf = reverse(slow);
Node firstHalf = head;
while (secondHalf != null) {
if (firstHalf.val != secondHalf.val) return false;
firstHalf = firstHalf.next;
secondHalf = secondHalf.next;
}
return true;
}
private static Node reverse(Node head) {
Node prev = null, curr = head;
while (curr != null) {
Node next = curr.next;
curr.next = prev;
prev = curr;
curr = next;
}
return prev;
}
}
30. Flatten a multilevel doubly linked list.
Each node may have:
- next
- prev
- child → a separate doubly linked list
Flatten using DFS approach:
class Node {
int val;
Node prev;
Node next;
Node child;
Node(int val) { this.val = val; }
}
public class FlattenList {
public static Node flatten(Node head) {
if (head == null) return head;
Node curr = head;
while (curr != null) {
if (curr.child != null) {
Node next = curr.next;
Node child = flatten(curr.child);
curr.next = child;
child.prev = curr;
curr.child = null;
while (curr.next != null) curr = curr.next;
curr.next = next;
if (next != null) next.prev = curr;
}
curr = curr.next;
}
return head;
}
}
31. Implement a Stack using Two Queues
Use two queues: one for pushing, the other for reversing order during pop.
import java.util.*;
public class StackUsingQueues {
Queue<Integer> q1 = new LinkedList<>();
Queue<Integer> q2 = new LinkedList<>();
public void push(int x) {
q2.add(x);
while (!q1.isEmpty()) q2.add(q1.poll());
Queue<Integer> temp = q1; q1 = q2; q2 = temp;
}
public int pop() {
return q1.poll();
}
public int top() {
return q1.peek();
}
public boolean isEmpty() {
return q1.isEmpty();
}
}
32. Design a Min-Stack (getMin in O(1))
Maintain an auxiliary stack to track the minimum.
import java.util.Stack;
public class MinStack {
Stack<Integer> stack = new Stack<>();
Stack<Integer> minStack = new Stack<>();
public void push(int x) {
stack.push(x);
if (minStack.isEmpty() || x <= minStack.peek()) minStack.push(x);
}
public void pop() {
if (stack.pop().equals(minStack.peek())) minStack.pop();
}
public int top() {
return stack.peek();
}
public int getMin() {
return minStack.peek();
}
}
33. Evaluate a Postfix Expression Using Stack
Example input: “231*+9-” → Result: -4
import java.util.*;
public class PostfixEval {
public static int evaluate(String expr) {
Stack<Integer> stack = new Stack<>();
for (char c : expr.toCharArray()) {
if (Character.isDigit(c)) stack.push(c - '0');
else {
int b = stack.pop(), a = stack.pop();
switch (c) {
case '+': stack.push(a + b); break;
case '-': stack.push(a - b); break;
case '*': stack.push(a * b); break;
case '/': stack.push(a / b); break;
}
}
}
return stack.pop();
}
public static void main(String[] args) {
System.out.println(evaluate("231*+9-")); // Output: -4
}
}
34. Check for Balanced Parentheses in an Expression
import java.util.*;
public class BalancedParentheses {
public static boolean isValid(String expr) {
Stack<Character> stack = new Stack<>();
for (char ch : expr.toCharArray()) {
if (ch == '(' || ch == '{' || ch == '[') stack.push(ch);
else if (ch == ')' && (stack.isEmpty() || stack.pop() != '(')) return false;
else if (ch == '}' && (stack.isEmpty() || stack.pop() != '{')) return false;
else if (ch == ']' && (stack.isEmpty() || stack.pop() != '[')) return false;
}
return stack.isEmpty();
}
public static void main(String[] args) {
System.out.println(isValid("{[()]}")); // true
System.out.println(isValid("{[(])}")); // false
}
}
35. Implement a Queue Using Two Stacks
import java.util.Stack;
public class QueueUsingStacks {
Stack<Integer> in = new Stack<>();
Stack<Integer> out = new Stack<>();
public void enqueue(int x) {
in.push(x);
}
public int dequeue() {
if (out.isEmpty()) {
while (!in.isEmpty()) out.push(in.pop());
}
return out.isEmpty() ? -1 : out.pop();
}
}
36. What is the difference between Queue and Deque in Java?
37. Solve the Sliding Window Maximum Using Deque
import java.util.*;
public class SlidingWindowMax {
public static List<Integer> maxSlidingWindow(int[] nums, int k) {
Deque<Integer> dq = new LinkedList<>();
List<Integer> res = new ArrayList<>();
for (int i = 0; i < nums.length; i++) {
while (!dq.isEmpty() && dq.peek() < i - k + 1) dq.poll();
while (!dq.isEmpty() && nums[dq.peekLast()] < nums[i]) dq.pollLast();
dq.offer(i);
if (i >= k - 1) res.add(nums[dq.peek()]);
}
return res;
}
public static void main(String[] args) {
int[] nums = {1,3,-1,-3,5,3,6,7};
System.out.println(maxSlidingWindow(nums, 3)); // Output: [3,3,5,5,6,7]
}
}
38. What is the use of PriorityQueue in Java? How does it work internally?
Use: Efficient for problems involving min/max retrieval, like:
- Dijkstra’s algorithm
- Median maintenance
- Top-K elements
How it works:
- Min-heap by default (smallest element at head)
- Internally uses a binary heap (array-based)
- Time complexities:
- offer() – O(log n)
- poll() – O(log n)
- peek() – O(1)
PriorityQueue<Integer> pq = new PriorityQueue<>();
pq.offer(3);
pq.offer(1);
pq.offer(2);
System.out.println(pq.poll()); // Output: 1 (min element)
39. Design a Data Structure for LRU Cache Using LinkedHashMap
import java.util.*;
public class LRUCache extends LinkedHashMap<Integer, Integer> {
private final int capacity;
public LRUCache(int capacity) {
super(capacity, 0.75f, true); // accessOrder = true
this.capacity = capacity;
}
@Override
protected boolean removeEldestEntry(Map.Entry<Integer, Integer> eldest) {
return size() > capacity;
}
public int get(int key) {
return getOrDefault(key, -1);
}
public void put(int key, int value) {
super.put(key, value);
}
}
40. Implement a Circular Queue and Explain Its Use Cases
Use cases:
- CPU scheduling (Round Robin)
- Fixed-size buffers (e.g., circular logs, streaming data)
- Real-time systems
public class CircularQueue {
private int[] data;
private int front, rear, size, capacity;
public CircularQueue(int k) {
capacity = k;
data = new int[k];
front = 0;
rear = -1;
size = 0;
}
public boolean enqueue(int val) {
if (isFull()) return false;
rear = (rear + 1) % capacity;
data[rear] = val;
size++;
return true;
}
public boolean dequeue() {
if (isEmpty()) return false;
front = (front + 1) % capacity;
size--;
return true;
}
public int front() {
return isEmpty() ? -1 : data[front];
}
public boolean isFull() { return size == capacity; }
public boolean isEmpty() { return size == 0; }
}
Your dream job is one step away—build strong Java DS skills with us now!
41. What is the difference between Binary Tree and Binary Search Tree (BST)?
TreeNode Class (For some of the below examples)
class TreeNode {
int val;
TreeNode left, right;
TreeNode(int val) {
this.val = val;
}
}
42. Write a Java method for Inorder Traversal (Recursive and Iterative)
Recursive Inorder Traversal
void inorderRecursive(TreeNode root) {
if (root == null) return;
inorderRecursive(root.left);
System.out.print(root.val + " ");
inorderRecursive(root.right);
}
Iterative Inorder Traversal
void inorderIterative(TreeNode root) {
Stack<TreeNode> stack = new Stack<>();
TreeNode current = root;
while (current != null || !stack.isEmpty()) {
while (current != null) {
stack.push(current);
current = current.left;
}
current = stack.pop();
System.out.print(current.val + " ");
current = current.right;
}
}
43. Check if a Binary Tree is Balanced
A tree is height-balanced if for every node, height(left) - height(right) ≤ 1
boolean isBalanced(TreeNode root) {
return checkHeight(root) != -1;
}
int checkHeight(TreeNode node) {
if (node == null) return 0;
int left = checkHeight(node.left);
int right = checkHeight(node.right);
if (left == -1 || right == -1 || Math.abs(left - right) > 1) return -1;
return Math.max(left, right) + 1;
}
44. Find the Lowest Common Ancestor (LCA) of Two Nodes in a Binary Tree
TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if (root == null || root == p || root == q) return root;
TreeNode left = lowestCommonAncestor(root.left, p, q);
TreeNode right = lowestCommonAncestor(root.right, p, q);
if (left != null && right != null) return root;
return left != null ? left : right;
}
45. Convert a Binary Tree to Its Mirror Tree
TreeNode mirror(TreeNode node) {
if (node == null) return null;
TreeNode left = mirror(node.left);
TreeNode right = mirror(node.right);
node.left = right;
node.right = left;
return node;
}
46. Check if a Tree is a Subtree of Another
boolean isSubtree(TreeNode s, TreeNode t) {
if (s == null) return false;
if (isSameTree(s, t)) return true;
return isSubtree(s.left, t) || isSubtree(s.right, t);
}
boolean isSameTree(TreeNode s, TreeNode t) {
if (s == null && t == null) return true;
if (s == null || t == null || s.val != t.val) return false;
return isSameTree(s.left, t.left) && isSameTree(s.right, t.right);
}
47. Implement Level Order Traversal Using a Queue
void levelOrder(TreeNode root) {
if (root == null) return;
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
while (!queue.isEmpty()) {
TreeNode node = queue.poll();
System.out.print(node.val + " ");
if (node.left != null) queue.offer(node.left);
if (node.right != null) queue.offer(node.right);
}
}
48. Serialize and Deserialize a Binary Tree
Serialization: Convert tree to string (Preorder)
Deserialization: Rebuild tree from string
class Codec {
public String serialize(TreeNode root) {
if (root == null) return "X";
return root.val + "," + serialize(root.left) + "," + serialize(root.right);
}
public TreeNode deserialize(String data) {
Queue<String> nodes = new LinkedList<>(Arrays.asList(data.split(",")));
return buildTree(nodes);
}
private TreeNode buildTree(Queue<String> nodes) {
String val = nodes.poll();
if (val.equals("X")) return null;
TreeNode node = new TreeNode(Integer.parseInt(val));
node.left = buildTree(nodes);
node.right = buildTree(nodes);
return node;
}
}
49. Convert a BST to a Doubly Linked List (Inorder-Based)
TreeNode prev = null, head = null;
public TreeNode bstToDLL(TreeNode root) {
if (root == null) return null;
convert(root);
return head;
}
private void convert(TreeNode node) {
if (node == null) return;
convert(node.left);
if (prev == null) head = node;
else {
prev.right = node;
node.left = prev;
}
prev = node;
convert(node.right);
}
50. Count the Number of Leaf Nodes in a Tree
int countLeafNodes(TreeNode root) {
if (root == null) return 0;
if (root.left == null && root.right == null) return 1;
return countLeafNodes(root.left) + countLeafNodes(root.right);
}
51. Implement BFS and DFS for a Graph
// Graph: Adjacency List Representation
Map<Integer, List<Integer>> graph = new HashMap<>();
// BFS
void bfs(int start) {
Set<Integer> visited = new HashSet<>();
Queue<Integer> queue = new LinkedList<>();
queue.add(start);
while (!queue.isEmpty()) {
int node = queue.poll();
if (!visited.contains(node)) {
visited.add(node);
System.out.print(node + " ");
for (int neighbor : graph.getOrDefault(node, new ArrayList<>())) {
queue.add(neighbor);
}
}
}
}
// DFS
void dfs(int node, Set<Integer> visited) {
visited.add(node);
System.out.print(node + " ");
for (int neighbor : graph.getOrDefault(node, new ArrayList<>())) {
if (!visited.contains(neighbor)) dfs(neighbor, visited);
}
}
52. Detect a Cycle in a Directed Graph Using DFS
boolean hasCycle(Map<Integer, List<Integer>> graph) {
Set<Integer> visited = new HashSet<>();
Set<Integer> recursionStack = new HashSet<>();
for (int node : graph.keySet()) {
if (dfsCycle(node, visited, recursionStack, graph)) return true;
}
return false;
}
boolean dfsCycle(int node, Set<Integer> visited, Set<Integer> stack, Map<Integer, List<Integer>> graph) {
if (stack.contains(node)) return true;
if (visited.contains(node)) return false;
visited.add(node);
stack.add(node);
for (int neighbor : graph.getOrDefault(node, new ArrayList<>())) {
if (dfsCycle(neighbor, visited, stack, graph)) return true;
}
stack.remove(node);
return false;
}
53. What is a Priority Queue and How is it Implemented Using Heaps in Java?
PriorityQueue is a special queue where elements are dequeued based on priority (not insertion order).
Internally:
- Uses binary heap (min-heap by default)
- Time complexities:
- add() – O(log n)
- poll() – O(log n)
- peek() – O(1)
PriorityQueue<Integer> pq = new PriorityQueue<>();
pq.offer(10); pq.offer(5); pq.offer(8);
System.out.println(pq.poll()); // Output: 5 (smallest)
Use custom comparator for max-heap:
PriorityQueue<Integer> maxHeap = new PriorityQueue<>(Collections.reverseOrder());
54. Find the Shortest Path in an Unweighted Graph Using BFS
int shortestPath(Map<Integer, List<Integer>> graph, int src, int dest) {
Queue<Integer> queue = new LinkedList<>();
Map<Integer, Integer> distance = new HashMap<>();
queue.offer(src);
distance.put(src, 0);
while (!queue.isEmpty()) {
int node = queue.poll();
if (node == dest) return distance.get(node);
for (int neighbor : graph.getOrDefault(node, new ArrayList<>())) {
if (!distance.containsKey(neighbor)) {
distance.put(neighbor, distance.get(node) + 1);
queue.offer(neighbor);
}
}
}
return -1; // unreachable
}
55. Explain Topological Sorting and Implement It
Topological Sort: Linear ordering of DAG nodes such that for every directed edge u → v, u comes before v.
Use Case: Task scheduling, build systems.
List<Integer> topologicalSort(Map<Integer, List<Integer>> graph) {
Set<Integer> visited = new HashSet<>();
Stack<Integer> stack = new Stack<>();
for (int node : graph.keySet()) {
if (!visited.contains(node)) topoDfs(node, visited, stack, graph);
}
List<Integer> result = new ArrayList<>();
while (!stack.isEmpty()) result.add(stack.pop());
return result;
}
void topoDfs(int node, Set<Integer> visited, Stack<Integer> stack, Map<Integer, List<Integer>> graph) {
visited.add(node);
for (int neighbor : graph.getOrDefault(node, new ArrayList<>())) {
if (!visited.contains(neighbor)) topoDfs(neighbor, visited, stack, graph);
}
stack.push(node);
}
56. What are Union-Find Data Structures and Their Applications?
Union-Find (Disjoint Set): Efficient structure to manage partitioned sets.
Operations:
- find(x): Find root of set containing x
- union(x, y): Merge sets containing x and y
Optimizations:
- Path Compression (flatten tree)
- Union by Rank (attach smaller to larger)
Applications:
- Kruskal’s Algorithm (MST)
- Cycle detection
- Connected components
- Network connectivity
57. Implement Dijkstra’s Algorithm Using Java and PriorityQueue
class Node implements Comparable<Node> {
int vertex, weight;
Node(int v, int w) { vertex = v; weight = w; }
public int compareTo(Node other) { return this.weight - other.weight; }
}
int[] dijkstra(Map<Integer, List<Node>> graph, int start, int n) {
int[] dist = new int[n];
Arrays.fill(dist, Integer.MAX_VALUE);
dist[start] = 0;
PriorityQueue<Node> pq = new PriorityQueue<>();
pq.offer(new Node(start, 0));
while (!pq.isEmpty()) {
Node current = pq.poll();
for (Node neighbor : graph.getOrDefault(current.vertex, new ArrayList<>())) {
int newDist = dist[current.vertex] + neighbor.weight;
if (newDist < dist[neighbor.vertex]) {
dist[neighbor.vertex] = newDist;
pq.offer(new Node(neighbor.vertex, newDist));
}
}
}
return dist;
}
58. Explain Trie Data Structure and Use-Case for Auto-Complete
Trie (Prefix Tree):
- Tree-like structure for efficient prefix matching.
- Each node represents a character, paths form words.
Use Cases:
- Auto-complete
- Spell checker
- Prefix queries
- IP routing (binary trie)
Basic Trie Implementation:
class TrieNode {
Map<Character, TrieNode> children = new HashMap<>();
boolean isEndOfWord = false;
}
class Trie {
TrieNode root = new TrieNode();
void insert(String word) {
TrieNode node = root;
for (char c : word.toCharArray())
node = node.children.computeIfAbsent(c, k -> new TrieNode());
node.isEndOfWord = true;
}
boolean search(String word) {
TrieNode node = root;
for (char c : word.toCharArray()) {
if (!node.children.containsKey(c)) return false;
node = node.children.get(c);
}
return node.isEndOfWord;
}
}
59. How Would You Implement a Disjoint Set in Java?
class DisjointSet {
int[] parent, rank;
DisjointSet(int n) {
parent = new int[n];
rank = new int[n];
for (int i = 0; i < n; i++) parent[i] = i;
}
int find(int x) {
if (parent[x] != x) parent[x] = find(parent[x]); // Path Compression
return parent[x];
}
void union(int x, int y) {
int rootX = find(x), rootY = find(y);
if (rootX == rootY) return;
if (rank[rootX] < rank[rootY]) parent[rootX] = rootY;
else if (rank[rootX] > rank[rootY]) parent[rootY] = rootX;
else {
parent[rootY] = rootX;
rank[rootX]++;
}
}
}
60. What is a Segment Tree and When Should It Be Used?
Segment Tree: A binary tree to solve range queries (sum, min, max) in O(log n) time.
Use Cases:
- Range sum / min / max
- Range updates
- Competitive programming
- Sliding window problems
Operations:
- Build: O(n)
- Query: O(log n)
- Update: O(log n)
Basic Segment Tree for Range Sum:
class SegmentTree {
int[] tree, arr;
int n;
SegmentTree(int[] input) {
arr = input;
n = input.length;
tree = new int[4 * n];
build(0, 0, n - 1);
}
void build(int node, int start, int end) {
if (start == end) tree[node] = arr[start];
else {
int mid = (start + end) / 2;
build(2 * node + 1, start, mid);
build(2 * node + 2, mid + 1, end);
tree[node] = tree[2 * node + 1] + tree[2 * node + 2];
}
}
int query(int l, int r) { return query(0, 0, n - 1, l, r); }
int query(int node, int start, int end, int l, int r) {
if (r < start || end < l) return 0; // No overlap
if (l <= start && end <= r) return tree[node]; // Total overlap
int mid = (start + end) / 2;
return query(2 * node + 1, start, mid, l, r) +
query(2 * node + 2, mid + 1, end, l, r);
}
}