```python
def partition(arr, low, high):
pivot = arr[high]
i = low - 1
for j in range(low, high):
if arr[j] <= pivot:
i += 1
arr[i], arr[j] = arr[j], arr[i]
arr[i+1], arr[high] = arr[high], arr[i+1]
return i + 1
arr = [8, 3, 1, 5, 2]
idx = partition(arr, 0, 4)
print(arr, idx)
```
What does this code print?
[1, 2, 8, 5, 3] 2 — the pivot 2 ends up at index 2 after partitioning
[1, 2, 8, 5, 3] 1 — pivot is arr[4]=2; elements <=2 are 1,2; after partition: [1, 2, | 8, 5, 3] with pivot at index 1
[1, 2, 3, 5, 8] 1 — partition fully sorts the array and returns the pivot index
[2, 3, 1, 5, 8] 0 — the pivot goes to the beginning of the array
Answer: B. [1, 2, 8, 5, 3] 1 — pivot is arr[4]=2; elements <=2 are 1,2; after partition: [1, 2, | 8, 5, 3] with pivot at index 1
ExplanationPivot = arr[4] = 2. i starts at -1. j=0: arr[0]=8 > 2, skip. j=1: arr[1]=3 > 2, skip. j=2: arr[2]=1 <= 2, i=0, swap arr[0] and arr[2] → [1, 3, 8, 5, 2]. j=3: arr[3]=5 > 2, skip. After loop: swap arr[i+1]=arr[1] with arr[4] → [1, 2, 8, 5, 3]. Return i+1 = 1. Pivot 2 is now at index 1, with all elements <=2 to its left and all elements >2 to its right, exactly as the Lomuto partition scheme guarantees.
Question 82 · Binary Search · hard
def binary_search(arr, target):
lo, hi = 0, len(arr) - 1
while lo <= hi:
mid = (lo + hi) // 2
if arr[mid] == target:
return mid
elif arr[mid] < target:
lo = mid + 1
else:
hi = mid - 1
return -1
arr = [2, 5, 8, 12, 16, 23, 38, 56, 72, 91]
print(binary_search(arr, 23))
What is the output?
5 — 23 is at index 5; trace: lo=0,hi=9,mid=4(16<23)→lo=5; lo=5,hi=9,mid=7(56>23)→hi=6; lo=5,hi=6,mid=5(23==23)→return 5
4 — mid starts at 4 and 16 is close to 23 so it returns 4
6 — binary search checks mid=5 first which is 23 but adjusts +1 for 0-indexing correction
-1 — 23 is not found because the algorithm skips over it during the narrowing
Answer: A. 5 — 23 is at index 5; trace: lo=0,hi=9,mid=4(16<23)→lo=5; lo=5,hi=9,mid=7(56>23)→hi=6; lo=5,hi=6,mid=5(23==23)→return 5
Explanationarr has 10 elements (indices 0-9). Iteration 1: lo=0, hi=9, mid=(0+9)//2=4, arr[4]=16 < 23, so lo=5. Iteration 2: lo=5, hi=9, mid=(5+9)//2=7, arr[7]=56 > 23, so hi=6. Iteration 3: lo=5, hi=6, mid=(5+6)//2=5, arr[5]=23 == 23, return 5. The target is found at index 5 after just 3 iterations — binary search's halving strategy (10 → 5 → 2 remaining elements) locates it in at most ceil(log2(10)) = 4 comparisons, far fewer than the 6 a linear scan of this array would need.
Question 83 · Inversion Counting · hard
```python
def count_inversions(arr):
count = 0
for i in range(len(arr)):
for j in range(i+1, len(arr)):
if arr[i] > arr[j]:
count += 1
return count
print(count_inversions([5, 3, 1, 4, 2]))
```
What is the output?
7 — inversions are: (5,3),(5,1),(5,4),(5,2),(3,1),(3,2),(4,2) = 7 pairs where left > right
10 — every pair is an inversion in a nearly reversed list of 5 elements
5 — one inversion per element in the array
4 — only adjacent pairs that are out of order count as inversions
Answer: A. 7 — inversions are: (5,3),(5,1),(5,4),(5,2),(3,1),(3,2),(4,2) = 7 pairs where left > right
ExplanationCheck all pairs (i,j) where i<j and arr[i]>arr[j]: (5,3)✓ (5,1)✓ (5,4)✓ (5,2)✓ (3,1)✓ (3,4)✗ (3,2)✓ (1,4)✗ (1,2)✗ (4,2)✓. Total = 7 inversions. Note: max inversions for 5 elements would be C(5,2)=10 (fully reversed). This array has 7 out of that possible 10.
Question 84 · Stack-Based Bracket Validation · hard
What is the output of this code?
def is_balanced(s):
stack = []
pairs = {')': '(', ']': '[', '}': '{'}
for ch in s:
if ch in '([{':
stack.append(ch)
elif ch in pairs:
if not stack or stack[-1] != pairs[ch]:
return False
stack.pop()
return len(stack) == 0
print(is_balanced('({[]})'))
print(is_balanced('({[}])'))
print(is_balanced('((()'))
What is printed?
True then False then False — '({[]})' is properly nested; '({[}])' has a mismatched } at position 4 — the stack top at that point is '[', which needs ']' to close it, not '}'; '((()' has 2 unmatched opening parens left on stack
True then True then False — both first and second strings have matching counts of each bracket type
True then False then True — '((()' is balanced because it contains a complete '()' pair inside
False then False then False — none of these strings are balanced because they mix different bracket types
Answer: A. True then False then False — '({[]})' is properly nested; '({[}])' has a mismatched } at position 4 — the stack top at that point is '[', which needs ']' to close it, not '}'; '((()' has 2 unmatched opening parens left on stack
ExplanationString 1 '({[]})': the algorithm pushes '(', '{', '[' onto the stack, then each closing bracket matches the top of the stack in reverse order — ']' pops '[', '}' pops '{', ')' pops '(' — leaving the stack empty, so the function returns True. String 2 '({[}])': '(', '{', '[' are pushed the same way, but the 4th character '}' is checked against the stack top '[', and since pairs['}'] is '{' while stack[-1] is '[', the condition stack[-1] != pairs[ch] is true, so the function returns False immediately without processing the remaining ']' and ')'. String 3 '((()': three '(' are pushed, then ')' pops one of them, leaving two unmatched '(' on the stack when the loop ends; since len(stack) is 2 rather than 0, the function returns False. The three print statements therefore output True, False, and False in that order.
Question 85 · Insertion Sort Analysis · hard
What does this code print?
```python
def insertion_sort(arr):
comparisons = 0
for i in range(1, len(arr)):
key = arr[i]
j = i - 1
while j >= 0 and arr[j] > key:
comparisons += 1
arr[j + 1] = arr[j]
j -= 1
if j >= 0:
comparisons += 1
arr[j + 1] = key
return comparisons
data = [4, 1, 3, 2]
print(insertion_sort(data))
print(data)
```
Trace through the loop carefully and predict both output lines — what gets printed?
6 then [1, 2, 3, 4] — inserting 1 costs one shift comparison, inserting 3 costs one shift plus one stopping comparison against 1, and inserting 2 costs two shift comparisons plus one stopping comparison against 1, for a running total of 1, 3, 6, while the list is sorted in place
4 then [1, 2, 3, 4] — the while loop's body only increments comparisons on iterations where a shift actually happens, so counting just those shift events across all three insertions gives 1 + 1 + 2 = 4, with the array still ending up sorted
7 then [1, 2, 3, 4] — every insertion, including the very first one where the key shifts past all preceding elements, ends by hitting a stopping comparison against some arr[j], so three insertions each add one extra count on top of the four shifts
6 then [4, 1, 3, 2] — the comparison total is right, but since arr is a parameter name local to the function, modifying it inside insertion_sort cannot change the list object that data refers to in the caller
Answer: A. 6 then [1, 2, 3, 4] — inserting 1 costs one shift comparison, inserting 3 costs one shift plus one stopping comparison against 1, and inserting 2 costs two shift comparisons plus one stopping comparison against 1, for a running total of 1, 3, 6, while the list is sorted in place
ExplanationTrace comparisons is initialized to 0 and the array starts as [4, 1, 3, 2].
For i=1, key=1 and j=0. The while condition checks arr[0]=4 > 1, which is true, so comparisons becomes 1, 4 shifts right, and j becomes -1. The loop now stops because j < 0, so the post-loop check (if j >= 0) does not add anything. Key 1 is placed at index 0: array is [1, 4, 3, 2]. Running total: 1.
For i=2, key=3 and j=1. The while condition checks arr[1]=4 > 3, true, so comparisons becomes 2, 4 shifts right, j becomes 0. The while condition checks arr[0]=1 > 3, false, so the loop exits with j=0 still >= 0. The post-loop check fires, adding one more: comparisons becomes 3. Key 3 is placed at index 1: array is [1, 3, 4, 2]. Running total: 3.
For i=3, key=2 and j=2. The while condition checks arr[2]=4 > 2, true, so comparisons becomes 4, 4 shifts right, j becomes 1. The while condition checks arr[1]=3 > 2, true, so comparisons becomes 5, 3 shifts right, j becomes 0. The while condition checks arr[0]=1 > 2, false, so the loop exits with j=0 still >= 0. The post-loop check fires, adding one more: comparisons becomes 6. Key 2 is placed at index 1: array is [1, 2, 3, 4]. Running total: 6.
The function returns 6, and since arr is the same list object as data (Python passes the reference, and every update uses in-place indexing like arr[j+1] = arr[j]), the caller's data list is also sorted. The two printed lines are 6 and [1, 2, 3, 4].
The count of 4 undercounts by ignoring that a loop which exits because arr[j] is no longer greater than key still performed one real comparison — the failing one — which the post-loop check exists specifically to record. The count of 7 overcounts by assuming that check always fires, but when the key shifts all the way past every earlier element (as happens for the very first insertion here), j ends at -1 and there is no arr[j] left to compare against, so no stopping comparison is recorded for that insertion.
Question 86 · Polymorphism and Inheritance · hard
Consider this Python code:
```python
class Animal:
def __init__(self, name):
self.name = name
def speak(self):
return f'{self.name} makes a sound'
class Dog(Animal):
def speak(self):
return f'{self.name} barks'
class Cat(Animal):
pass
animals = [Dog('Rex'), Cat('Whiskers'), Animal('Bird')]
for a in animals:
print(a.speak())
```
What is the output?
The output is: Rex barks, then Whiskers makes a sound, then Bird makes a sound.
Rex makes a sound then Whiskers makes a sound then Bird makes a sound — all use the base class method
Rex barks then Error — Cat has no speak() method defined and cannot inherit from Animal
Rex barks then Whiskers barks then Bird makes a sound — Cat inherits from Dog not Animal
Answer: A. The output is: Rex barks, then Whiskers makes a sound, then Bird makes a sound.
ExplanationDog.speak() overrides Animal.speak(), returning 'Rex barks'. Cat has no speak() method, so Python looks up the MRO to Animal.speak(), returning 'Whiskers makes a sound'. Animal('Bird').speak() returns 'Bird makes a sound'. This is polymorphism: the same method call .speak() produces different results depending on the actual class of the object. Cat inherits everything from Animal since it uses pass.
Question 87 · Iterative DFS · hard
Trace through this code and determine what it prints.
def dfs_iterative(graph, start):
visited = []
stack = [start]
while stack:
node = stack.pop()
if node not in visited:
visited.append(node)
for neighbor in reversed(graph[node]):
if neighbor not in visited:
stack.append(neighbor)
return visited
g = {'A': ['B', 'C'], 'B': ['D', 'E'], 'C': ['F'], 'D': [], 'E': [], 'F': []}
print(dfs_iterative(g, 'A'))
What is printed?
['A', 'B', 'D', 'E', 'C', 'F'] — DFS visits all 6 nodes (indices 0-5) using stack: pop A, push C then B (reversed); pop B, push E then D; pop D (leaf); pop E (leaf); pop C, push F; pop F. Visited order: A,B,D,E,C,F
['A', 'C', 'F', 'B', 'E', 'D'] — this is what results if the reversed() call is omitted: neighbors get pushed in listed order (B then C), so C ends up on top of the stack and is popped immediately after A, before B is ever visited.
['A', 'B', 'C', 'D', 'E', 'F'] — this is the order breadth-first search (BFS) would produce instead of DFS, visiting all of A's direct neighbors before going deeper, which for this graph happens to match alphabetical order.
The reversed() call is sometimes misread as reversing the final visited list rather than each neighbor list before pushing, which would incorrectly suggest an output of ['F', 'C', 'E', 'D', 'B', 'A'].
Answer: A. ['A', 'B', 'D', 'E', 'C', 'F'] — DFS visits all 6 nodes (indices 0-5) using stack: pop A, push C then B (reversed); pop B, push E then D; pop D (leaf); pop E (leaf); pop C, push F; pop F. Visited order: A,B,D,E,C,F
ExplanationTrace: stack=[A]. Pop A, visited=[A], push reversed(['B','C'])=['C','B'], stack=[C,B]. Pop B, visited=[A,B], push reversed(['D','E'])=['E','D'], stack=[C,E,D]. Pop D, visited=[A,B,D], no neighbors, stack=[C,E]. Pop E, visited=[A,B,D,E], no neighbors, stack=[C]. Pop C, visited=[A,B,D,E,C], push reversed(['F'])=['F'], stack=[F]. Pop F, visited=[A,B,D,E,C,F]. The reversed() call ensures each node's neighbors are pushed in reverse order, so the first-listed neighbor lands on top of the stack and is popped first — giving the same left-to-right traversal order as recursive DFS.
Question 88 · Run-Length Encoding · hard
Consider the following function:
```python
def compress(s):
if not s:
return ''
result = []
count = 1
for i in range(1, len(s)):
if s[i] == s[i-1]:
count += 1
else:
result.append(s[i-1] + str(count))
count = 1
result.append(s[-1] + str(count))
return ''.join(result)
```
What is the output of `print(compress('aaabbbccaa'))`?
a3b3c2a2 — run-length encoding over the 10-char input 'aaabbbccaa': 'aaa'→a3, 'bbb'→b3, 'cc'→c2, 'aa'→a2; note the two separate 'a' runs produce 2 entries
a5b3c2 — the algorithm is assumed to count all occurrences of each character regardless of position, combining both 'a' runs into a single count of 5
a3b3c2 — this assumes the function returns before appending the count for the last group, so the trailing 'aa' never gets added to the result
3a3b2c2a — the count (like 3) comes before the character in standard RLE of the 10-char string
Answer: A. a3b3c2a2 — run-length encoding over the 10-char input 'aaabbbccaa': 'aaa'→a3, 'bbb'→b3, 'cc'→c2, 'aa'→a2; note the two separate 'a' runs produce 2 entries
ExplanationTrace through 'aaabbbccaa': i=1: a==a, count=2. i=2: a==a, count=3. i=3: b≠a, append 'a3', count=1. i=4: b==b, count=2. i=5: b==b, count=3. i=6: c≠b, append 'b3', count=1. i=7: c==c, count=2. i=8: a≠c, append 'c2', count=1. i=9: a==a, count=2. After loop: append 'a2'. Result: 'a3b3c2a2'. The algorithm treats consecutive runs independently — the two 'a' groups are separate because they are not adjacent.
Question 89 · Kadane Maximum Subarray · hard
Consider this Python code that finds the maximum subarray sum using Kadane's algorithm:
```python
def max_subarray(arr):
max_sum = current = arr[0]
for x in arr[1:]:
current = max(x, current + x)
max_sum = max(max_sum, current)
return max_sum
print(max_subarray([-2, -3, 4, -1, -2, 1, 5, -3]))
```
What value does this code print?
7, since the contiguous subarray [4, -1, -2, 1, 5] produces the largest running sum found during the scan.
5, because the single largest element in the array exceeds every subarray sum computed around it.
10, from adding up every positive number in the array (4 + 1 + 5), even though those numbers are not contiguous.
4, since the algorithm's running sum resets to zero the instant it encounters any negative number.
Answer: A. 7, since the contiguous subarray [4, -1, -2, 1, 5] produces the largest running sum found during the scan.
ExplanationTrace the algorithm with max_sum = current = -2 (the first element). For x=-3: current = max(-3, -2 + -3) = max(-3, -5) = -3, so max_sum stays -2. For x=4: current = max(4, -3 + 4) = max(4, 1) = 4, so max_sum becomes 4. For x=-1: current = max(-1, 4 + -1) = 3, max_sum stays 4. For x=-2: current = max(-2, 3 + -2) = 1, max_sum stays 4. For x=1: current = max(1, 1 + 1) = 2, max_sum stays 4. For x=5: current = max(5, 2 + 5) = 7, so max_sum updates to 7. For x=-3: current = max(-3, 7 + -3) = 4, max_sum stays 7. The final answer is 7, matching the contiguous run [4, -1, -2, 1, 5] (4 - 1 - 2 + 1 + 5 = 7). The key insight is the line `current = max(x, current + x)`: the running sum only restarts at the current element when extending the previous run would make things worse, not simply whenever a negative number appears — that is why the dip through -1 and -2 stays inside the winning subarray instead of breaking it. Picking just the largest single number ignores that combining it with a nearby run can score higher, and summing all positive numbers ignores the requirement that the elements be contiguous.
Question 90 · Topological Sort · hard
Consider the following DFS-based topological sort implementation, run on the graph g:
```python
def topological_sort(graph):
visited = set()
result = []
def dfs(node):
if node in visited:
return
visited.add(node)
for neighbor in graph.get(node, []):
dfs(neighbor)
result.append(node)
for node in graph:
dfs(node)
return result[::-1]
g = {'A': ['B'], 'B': ['C'], 'C': [], 'D': ['C']}
print(topological_sort(g))
```
What is the exact output printed?
['D', 'A', 'B', 'C'] — dfs(A) recurses into B then C, appending C, B, A in that post-order; D is then visited separately and appended after its own dfs(C) call returns immediately since C is already visited; reversing the post-order list [C, B, A, D] gives this final result
['C', 'B', 'A', 'D'] — this is the raw post-order list built by the recursive dfs() calls before the final result[::-1] reversal is applied, so printing it directly would skip a required step
['A', 'D', 'B', 'C'] — although this respects the edges A→B, B→C, and D→C, it is not the sequence this specific code actually produces, since dfs(A) is invoked and fully completes before dfs(D) ever runs
RecursionError is raised — dfs(D) calls dfs(C) a second time and Python re-enters the same call frame indefinitely since C was already appended to result
Answer: A. ['D', 'A', 'B', 'C'] — dfs(A) recurses into B then C, appending C, B, A in that post-order; D is then visited separately and appended after its own dfs(C) call returns immediately since C is already visited; reversing the post-order list [C, B, A, D] gives this final result
ExplanationPython dict iteration follows insertion order, so the outer loop calls dfs('A') first, then dfs('B'), dfs('C'), dfs('D') — though only dfs('A') and dfs('D') do real work, since 'B' and 'C' get visited during A's recursion. dfs('A') recurses into 'B' (via graph.get('A', [])), which recurses into 'C' (via graph.get('B', [])); 'C' has no neighbors, so it is appended first, giving result=['C']. Returning up, 'B' is appended (result=['C','B']), then 'A' is appended (result=['C','B','A']). The outer loop's next calls, dfs('B') and dfs('C'), return immediately because both nodes are already in visited — a revisit simply triggers the early-return guard, not an error or infinite recursion. The call dfs('D') then runs: graph.get('D', []) = ['C'], so it calls dfs('C'), which returns immediately since 'C' is visited, and then 'D' is appended, giving result=['C','B','A','D']. Reversing this list with result[::-1] produces the printed output ['D','A','B','C']. This ordering respects every edge in the graph (A before B, B before C, D before C), but it is only one of several orderings that would be mathematically valid — a different valid ordering is not automatically what this exact code prints, since the output is fixed by the specific traversal order the code follows.
Question 91 · Min-Heap Insertion · hard
class MinHeap:\n def __init__(self):\n self.data = []\n def push(self, val):\n self.data.append(val)\n self._sift_up(len(self.data) - 1)\n def _sift_up(self, i):\n while i > 0:\n parent = (i - 1) // 2\n if self.data[i] < self.data[parent]:\n self.data[i], self.data[parent] = self.data[parent], self.data[i]\n i = parent\n else:\n break\n\nh = MinHeap()\nfor x in [5, 3, 8, 1, 4]:\n h.push(x)\nprint(h.data[0])\nprint(h.data)\n\nWhat does this code print?
1 then [1, 3, 8, 5, 4] — the min-heap invariant guarantees h.data[0]=1 (the minimum); after all insertions and sift-ups, the exact array is [1, 3, 8, 5, 4]
1 then [1, 2, 3, 4, 5] — a min-heap stores elements in fully sorted order
5 then [5, 3, 8, 1, 4] — push appends without reordering because _sift_up has a bug
3 then [3, 1, 8, 5, 4] — the first non-5 value pushed becomes the root
Answer: A. 1 then [1, 3, 8, 5, 4] — the min-heap invariant guarantees h.data[0]=1 (the minimum); after all insertions and sift-ups, the exact array is [1, 3, 8, 5, 4]
ExplanationTrace insertions: push(5): data=[5]. push(3): data=[5,3], sift_up: 3<5 → swap → [3,5]. push(8): data=[3,5,8], 8>3 → no swap. push(1): data=[3,5,8,1], sift_up: i=3, parent=1, 1<5 → swap → [3,1,8,5], i=1, parent=0, 1<3 → swap → [1,3,8,5]. push(4): data=[1,3,8,5,4], i=4, parent=1, 4>3 → stop. Final: [1,3,8,5,4]. Root (index 0) is always the minimum. The heap is NOT fully sorted — it only guarantees parent ≤ children. The heap-order property ensures correctness because the parent is always smaller than or equal to its children.
Question 92 · Grid Path Counting · hard
You're analyzing this recursive function:
```python
def count_paths(grid, r, c):
if r == 0 or c == 0:
return 1
return count_paths(grid, r-1, c) + count_paths(grid, r, c-1)
print(count_paths(None, 3, 3))
```
What is the output?
20 — this counts unique paths in a 4×4 grid (indices 0-3) from top-left to (3,3) moving only right or down; equals C(6,3) = 20
6 — there are only 6 paths in a 3×3 grid
64 — each cell has 2 choices for 6 steps: 2^6 = 64
10 — the function counts paths in a 3×3 grid, which equals C(5,2) = 10
Answer: A. 20 — this counts unique paths in a 4×4 grid (indices 0-3) from top-left to (3,3) moving only right or down; equals C(6,3) = 20
Explanationcount_paths(None, 3, 3) counts paths from (0,0) to (3,3) in a grid, moving only down (r-1) or right (c-1). Base case: any cell in row 0 or column 0 has exactly 1 path. The total equals C(r+c, r) = C(6, 3) = 20. Verification: count_paths(3,3) = count_paths(2,3) + count_paths(3,2). The recursion builds Pascal's triangle: row/col 0 all 1s, each interior cell = sum of cell above + cell to the left. The grid of values: [1,1,1,1], [1,2,3,4], [1,3,6,10], [1,4,10,20]. Answer: 20.
Question 93 · Longest Common Prefix · hard
What does this code print?
```python
def common_prefix(a, b):
i = 0
while i < len(a) and i < len(b) and a[i] == b[i]:
i += 1
return a[:i]
def lcp(strs):
if not strs:
return ''
prefix = strs[0]
for s in strs[1:]:
prefix = common_prefix(prefix, s)
if prefix == '':
return ''
return prefix
print(lcp(['classroom', 'classify', 'class']))
print(lcp(['apple', 'banana', 'cherry']))
```
What are the two printed lines?
The prints are 'class' then an empty string — comparing 'classroom' against 'classify' matches at the first five positions (c-l-a-s-s) and diverges at the sixth character ('r' vs 'i'), so common_prefix returns 'class'; comparing that against 'class' matches fully, keeping the prefix at 'class'. In the second call, 'apple' and 'banana' differ at the very first character ('a' vs 'b'), so common_prefix returns immediately, making prefix empty and triggering the early return before 'cherry' is even compared
'classi' (6 characters) then an empty string — the while loop keeps extending i as long as any of the three strings in each list share a character at that position, so 'classroom', 'classify', and 'class' are all compared together and agree through 'classi' before the seventh character breaks the match
'classroom' unchanged (all nine characters) then 'apple' unchanged — since prefix is initialised to the first string in the list and the while loop only compares a string against itself on the first pass, no trimming happens and the function returns the original first string in both calls
'class' paired with 'banana' as the results — the second call finds no shared prefix among 'apple', 'banana', and 'cherry', so the function falls back to returning the middle string in the list as a default value
Answer: A. The prints are 'class' then an empty string — comparing 'classroom' against 'classify' matches at the first five positions (c-l-a-s-s) and diverges at the sixth character ('r' vs 'i'), so common_prefix returns 'class'; comparing that against 'class' matches fully, keeping the prefix at 'class'. In the second call, 'apple' and 'banana' differ at the very first character ('a' vs 'b'), so common_prefix returns immediately, making prefix empty and triggering the early return before 'cherry' is even compared
ExplanationIn the first call, prefix starts as 'classroom'. Comparing it against 'classify' inside common_prefix, the two strings match at the first five positions — c, l, a, s, s — and disagree at the sixth character, where 'classroom' has 'r' and 'classify' has 'i'. So the inner while loop stops with i = 5, and common_prefix returns 'classroom'[:5], which is 'class'. That value becomes the new prefix. Comparing 'class' against 'class' next, every character matches all the way through (i reaches 5, the length of both strings), so the prefix stays 'class', and lcp returns 'class' for the first print. In the second call, prefix starts as 'apple'. Comparing it against 'banana', the very first characters already disagree ('a' vs 'b'), so the while loop body never executes and common_prefix returns 'apple'[:0], an empty string. Since prefix is now empty, the function returns '' immediately — 'cherry' is never even examined. So the second print shows an empty string. The key idea is that common_prefix only ever shrinks the prefix by comparing it pairwise against each next string, and a mismatch at position i means exactly i characters matched before that point.
Question 94 · Floyd Cycle Detection · hard
Trace Floyd's cycle detection algorithm on a linked list whose tail loops back into the middle of the list rather than to the head:
```python
def detect_cycle(head):
slow = fast = head
while fast and fast.next:
slow = slow.next
fast = fast.next.next
if slow == fast:
return True
return False
class Node:
def __init__(self, val):
self.val = val
self.next = None
a, b, c, d, e = Node(1), Node(2), Node(3), Node(4), Node(5)
a.next = b; b.next = c; c.next = d; d.next = e; e.next = c
print(detect_cycle(a))
```
What does this code print?
True — the pointers converge at the node whose value is 4 after three loop iterations, since fast closes the gap on slow by one extra step each pass and must catch it somewhere along the 3→4→5→3 cycle
False — because node c already has an incoming link from b, Python's slow == fast comparison always evaluates to False once a node has more than one predecessor
It prints True, though the pointers actually meet at the node valued 3, since fast overtakes the cycle's entry point on its very first pass through the loop
Running this raises an error, because e.next = c produces a circular reference that print() cannot safely traverse when displaying the function's result
Answer: A. True — the pointers converge at the node whose value is 4 after three loop iterations, since fast closes the gap on slow by one extra step each pass and must catch it somewhere along the 3→4→5→3 cycle
ExplanationThe list is 1→2→3→4→5 with the tail node e (value 5) looping back to c (value 3), forming the cycle 3→4→5→3. Tracing the pointers: after the first iteration slow sits at value 2 and fast at value 3 (no match); after the second iteration slow is at value 3 and fast is at value 5 (no match); after the third iteration slow advances to value 4 and fast also lands on value 4, so slow == fast fires and the function returns True. This works because once both pointers are inside the cycle, fast reduces its distance to slow by exactly one node per iteration, guaranteeing a collision without ever needing to know the cycle's length in advance. The comparison succeeds because slow and fast end up referencing the identical Node object, and Python's default object equality is identity-based, not dependent on how many other nodes point to it. Nothing here raises an error either — print() just displays the boolean that detect_cycle returns, since the algorithm is a simple iterative while loop that never attempts to print or recurse over the linked list itself.
Question 95 · 0/1 Knapsack Dynamic Programming · hard
Consider the following 0/1 knapsack dynamic programming code:
def knapsack_01(weights, values, capacity):
n = len(weights)
dp = [[0] * (capacity + 1) for _ in range(n + 1)]
for i in range(1, n + 1):
for w in range(capacity + 1):
dp[i][w] = dp[i-1][w]
if weights[i-1] <= w:
dp[i][w] = max(dp[i][w], dp[i-1][w - weights[i-1]] + values[i-1])
return dp[n][capacity]
print(knapsack_01([2, 3, 4, 5], [3, 4, 5, 6], 5))
What is the output of this code?
Value = 7 — optimal selection: items with weight 2 (value 3) and weight 3 (value 4) total weight 5, value 7; taking the item with weight 5 alone gives only 6
6 — take the single item with weight 5 and value 6
18 — sum of all four values (3+4+5+6=18), obtained by taking every item regardless of the weight capacity
5 — take only the item with weight 4 and value 5, since it is the single largest item that fits within capacity 5
Answer: A. Value = 7 — optimal selection: items with weight 2 (value 3) and weight 3 (value 4) total weight 5, value 7; taking the item with weight 5 alone gives only 6
ExplanationItems: (w=2,v=3), (w=3,v=4), (w=4,v=5), (w=5,v=6). Capacity=5. DP builds a table where dp[i][w] = max value using the first i items with capacity w. Key cells: dp[1][2]=3, dp[2][3]=4, dp[2][5]=7 (take both items 1 and 2: 2+3=5, 3+4=7), dp[3][4]=5, dp[3][5]=max(dp[2][5]=7, item3+dp[2][1]=5+0=5)=7, dp[4][5]=max(dp[3][5]=7, item4+dp[3][0]=6+0=6)=7. The optimal solution picks items 1 and 2 (weights 2+3=5, values 3+4=7), which beats taking the single item of weight 5 (value 6). The recurrence considers both including and excluding the current item because each item can either contribute to the optimal solution or not.
Question 96 · Sliding Window Maximum with Deque · hard
Consider this code:
```python
def sliding_window_max(nums, k):
from collections import deque
dq = deque()
result = []
for i, num in enumerate(nums):
while dq and nums[dq[-1]] <= num:
dq.pop()
dq.append(i)
if dq[0] <= i - k:
dq.popleft()
if i >= k - 1:
result.append(nums[dq[0]])
return result
print(sliding_window_max([1, 3, -1, -3, 5, 3, 6, 7], 3))
```
What does this code print?
[3, 3, 5, 5, 6, 7] — monotonic deque tracks max in each window of size 3: [1,3,-1]→3, [3,-1,-3]→3, [-1,-3,5]→5, [-3,5,3]→5, [5,3,6]→6, [3,6,7]→7
[3, 5, 6, 7] — only values that are the overall maximum in some window appear
[1, 3, 5, 5, 6, 7] — the first window starts at index 0 giving max 1
[3, 3, 5, 5, 6, 7] but computed in O(nk) time — the deque provides no optimization
Answer: A. [3, 3, 5, 5, 6, 7] — monotonic deque tracks max in each window of size 3: [1,3,-1]→3, [3,-1,-3]→3, [-1,-3,5]→5, [-3,5,3]→5, [5,3,6]→6, [3,6,7]→7
ExplanationThe monotonic deque maintains indices in decreasing value order. Trace with k=3: i=0(1): dq=[0]. i=1(3): pop 0 (1≤3), dq=[1]. i=2(-1): dq=[1,2], i≥2 → result=[3]. i=3(-3): dq=[1,2,3], dq[0]=1 ≤ 3-3=0? No. result=[3,3]. i=4(5): pop 3,2,1 (all ≤5), dq=[4], result=[3,3,5]. i=5(3): dq=[4,5], result=[3,3,5,5]. i=6(6): pop 5,4, dq=[6], result=[3,3,5,5,6]. i=7(7): pop 6, dq=[7], result=[3,3,5,5,6,7]. The deque ensures O(n) total time because each index is pushed and popped at most once.
Question 97 · REST API Query Parameters · hard
You build a REST API for a school library system. A GET request to '/api/books?genre=science&grade=9' should return filtered results. Given that the server receives this request, what is the output of parsing the URL, and how would you access these filter parameters in Express.js?
Query string 'genre=science&grade=9' (everything after '?') contains the filters. The server parses this into key-value pairs: {genre: 'science', grade: '9'}. In Express.js, these are accessed via req.query.genre and req.query.grade. Query parameters are the standard way to pass filters in GET requests without modifying the URL path
Request body contains the filters as JSON. GET requests always send data in the body, and the '?' in the URL is just a separator with no special meaning for parameter passing
URL path segments '/genre/science/grade/9' contain the filters. The server splits the URL by '/' to extract them as positional arguments from the route
HTTP headers contain the filter data. The '?' and '&' characters in the URL are encoding artifacts that the browser converts to custom headers before sending the request
Answer: A. Query string 'genre=science&grade=9' (everything after '?') contains the filters. The server parses this into key-value pairs: {genre: 'science', grade: '9'}. In Express.js, these are accessed via req.query.genre and req.query.grade. Query parameters are the standard way to pass filters in GET requests without modifying the URL path
ExplanationIn a URL, everything after '?' is the query string. The '&' character separates multiple key-value pairs. So '?genre=science&grade=9' gives two parameters: genre=science and grade=9. In Express.js: req.query gives {genre: 'science', grade: '9'}. In Flask: request.args.get('genre'). Query strings are visible in the URL (unlike POST body data), making them ideal for filters, searches, and pagination in GET requests. They are also bookmarkable and cacheable.
Question 98 · BFS Traversal and Shortest Paths · hard
In a graph representing Indian railway connections, you run BFS starting from Delhi. The adjacency list is: Delhi→[Jaipur, Agra, Lucknow], Jaipur→[Delhi, Ahmedabad], Agra→[Delhi, Kanpur], Lucknow→[Delhi, Kanpur], Kanpur→[Agra, Lucknow], Ahmedabad→[Jaipur]. What is the BFS traversal order, and at what 'level' (minimum hops) is each city from Delhi?
BFS order: Delhi, Jaipur, Agra, Lucknow, Ahmedabad, Kanpur. Level 0: Delhi. Level 1: Jaipur, Agra, Lucknow. Level 2: Ahmedabad, Kanpur. BFS explores all neighbors at distance d before moving to distance d+1, giving shortest hop counts in unweighted graphs
BFS order: Delhi, Jaipur, Ahmedabad, Agra, Kanpur, Lucknow. All cities are at level 1 because BFS visits everything in one pass
BFS order: Delhi, Lucknow, Kanpur, Agra, Jaipur, Ahmedabad. BFS always goes to the last neighbor first (like a stack), visiting the deepest path before backtracking
BFS order: Delhi, Agra, Kanpur, Lucknow, Jaipur, Ahmedabad. BFS sorts neighbors alphabetically before visiting, ensuring deterministic traversal order
Answer: A. BFS order: Delhi, Jaipur, Agra, Lucknow, Ahmedabad, Kanpur. Level 0: Delhi. Level 1: Jaipur, Agra, Lucknow. Level 2: Ahmedabad, Kanpur. BFS explores all neighbors at distance d before moving to distance d+1, giving shortest hop counts in unweighted graphs
ExplanationBFS uses a queue (FIFO). Start: enqueue Delhi (level 0). Dequeue Delhi → enqueue its unvisited neighbors: Jaipur, Agra, Lucknow (all level 1, because they are directly connected). Dequeue Jaipur → enqueue Ahmedabad (level 2, since Delhi already visited). Dequeue Agra → enqueue Kanpur (level 2, because Delhi visited). Dequeue Lucknow → Kanpur already queued. Dequeue Ahmedabad → Jaipur visited. Dequeue Kanpur → all visited. This produces the result [Delhi, Jaipur, Agra, Lucknow, Ahmedabad, Kanpur] because BFS guarantees shortest path in unweighted graphs — Kanpur is exactly 2 hops from Delhi via either Agra or Lucknow.
Question 99 · XSS and Web Security · hard
You are building a web page for your school and write: 'document.getElementById("score").innerHTML = studentScore;'. If studentScore = '<script>alert("hacked")</script>', what happens when this code executes, and how would you evaluate and fix this security vulnerability?
This creates a Cross-Site Scripting (XSS) vulnerability, though this exact payload is inert: browsers parse a <script> tag inserted via innerHTML but never execute it, by design. However, innerHTML still renders any other attacker-controlled markup as live HTML, so payloads like '<img src=x onerror=alert(1)>' or '<svg onload=alert(1)>' do execute. Prevention: use textContent instead of innerHTML (treats input as plain text, not HTML), or sanitize input with a library like DOMPurify
No vulnerability exists because modern browsers automatically block all script tags inserted via innerHTML as part of the Content Security Policy
The vulnerability is SQL injection — the script tag would be sent to the database and corrupt the school's student records
This creates a buffer overflow because innerHTML cannot handle strings containing angle brackets, causing the browser to crash and potentially execute arbitrary code
Answer: A. This creates a Cross-Site Scripting (XSS) vulnerability, though this exact payload is inert: browsers parse a <script> tag inserted via innerHTML but never execute it, by design. However, innerHTML still renders any other attacker-controlled markup as live HTML, so payloads like '<img src=x onerror=alert(1)>' or '<svg onload=alert(1)>' do execute. Prevention: use textContent instead of innerHTML (treats input as plain text, not HTML), or sanitize input with a library like DOMPurify
ExplanationinnerHTML interprets the assigned string as HTML markup. When the HTML parser encounters a <script> element inserted this way, browsers intentionally refuse to execute it — per the HTML5 specification, script elements created via innerHTML are marked non-executing, so the exact alert("hacked") payload shown here would not actually fire. This is a common misconception, and the code is still a genuine XSS vulnerability: innerHTML will happily render and execute any other event-handler-based payload, such as '<img src=x onerror=alert(1)>' or '<svg onload=alert(1)>', neither of which uses a script tag at all. The fix is the same either way: use textContent (node.textContent = studentScore), which treats the value as plain text and never parses it as HTML, or use a sanitization library like DOMPurify when HTML rendering is genuinely required. XSS is ranked among the OWASP Top 10 web application vulnerabilities.
Question 100 · Dijkstra vs Bellman-Ford · hard
You implement Dijkstra's algorithm to find the shortest train route from Mumbai to Chennai. The graph has weighted edges (distances in km). After processing, the algorithm reports Mumbai→Pune→Hyderabad→Chennai = 1,280 km. Why can Dijkstra's NOT be used if some edges had negative weights (e.g., a discount route)?
Dijkstra's fails with negative weights because it uses a greedy strategy: once a node is marked as "finalized" with its shortest distance, it is never reconsidered. A negative edge discovered later could provide a shorter path to an already-finalized node, but Dijkstra's would miss it. The Bellman-Ford algorithm handles negative weights by relaxing all edges V-1 times
Dijkstra's actually works fine with negative weights — this is a common misconception. The priority queue handles negative values correctly
Dijkstra's fails because negative weights cause the priority queue to enter an infinite loop, consuming all available memory until the program crashes
Dijkstra's fails because distances cannot be negative in real life, so the algorithm was never designed to process such inputs and throws an exception
Answer: A. Dijkstra's fails with negative weights because it uses a greedy strategy: once a node is marked as "finalized" with its shortest distance, it is never reconsidered. A negative edge discovered later could provide a shorter path to an already-finalized node, but Dijkstra's would miss it. The Bellman-Ford algorithm handles negative weights by relaxing all edges V-1 times
ExplanationDijkstra's greedy approach finalizes nodes in order of increasing distance. Once a node is finalized, its shortest distance is set permanently. With negative edges, a later-discovered path through a negative edge could reduce an already-finalized distance — but Dijkstra's never rechecks finalized nodes. Example: A→B=5, A→C=2, C→B=-4. Dijkstra finalizes B at distance 5, but the actual shortest is A→C→B = 2+(-4) = -2. Bellman-Ford relaxes all edges V-1 times, catching these cases. It also detects negative cycles (infinite shortest path).