Finance firm interview prep (Parts 1-4) + full LeetCode Handbook (Phases 1-7), with syntax highlighting, diagrams, and English read-aloud
Three questions reconstructed from supplied screenshots: SP1 Python execution order, SP2 Scala Sudoku, SP3 Scala maximum-profit interval. The original online test format, option wording and full paper cannot be verified. Below are five clearly labeled MOCK questions, designed for two Python MCQ, two Python coding, two Scala MCQ and two existing Scala coding in total.
Click an explanation or table cell to read it aloud; double-click any English word (including code) for its pronunciation. Scala and Python answers appear side by side. Python translations of the Scala tasks are supplementary practice.
SP1–SP3 · Previously recorded questions
| Question | Original language | Core pattern |
|---|---|---|
| 1. Execution order | Python | Definition vs. call; __main__ |
| 2. Sudoku validation | Scala | Rows, columns, regions; sets |
| 3. Maximum-profit interval | Scala | Kadane; track start and end |
Key point:SP1 is Python; SP2 Sudoku and SP3 Maximum Profit are Scala. If you already know Java and Python, focus on the Scala syntax below.
| Scala syntax | Python / Java equivalent | Question |
|---|---|---|
| val / var | Non-reassignable reference / reassignable variable (Java final / regular variable) | SP2 / SP3 |
| def f(x: List[Int]): List[Int] | Method declaration with parameter and return types; similar to Java methods / Python def | SP2 / SP3 |
| List[Int] / List[List[Int]] | Integer list / nested integer lists; like Python list[int] / list[list[int]] | SP2 / SP3 |
| .toArray / nums(i) | Convert to array / access by index; Python nums[i], Java nums[i] | SP2 / SP3 |
| board(row)(col) | Two-dimensional indexing; Python board[row][col] | SP2 |
| 1 to 9 / 0 until 9 | Includes 9 / excludes 9; like Python range(1,10) / range(9) | SP2 |
| for (i <- 0 until 9) | Iteration; Python for i in range(9) | SP2 / SP3 |
| .toSet / != expected | Create a unique-value set and compare; Python set(values) != expected | SP2 |
| ListBuffer[String]() / errors += msg | Mutable string buffer / append; Python errors = []; errors.append(msg) | SP2 |
| .map(_.toArray) | Transform each item; the underscore _ is shorthand for a lambda parameter | SP2 |
| s"LINE ${row + 1} INVALID" | String interpolation; Python f"LINE {row + 1} INVALID" | SP2 |
| .isEmpty / List.empty[Int] | Check for an empty list / return an empty integer list | SP3 |
| .toLong / nums.length | Convert to a 64-bit integer / array length | SP3 |
| if / else / currentSum += value | Conditional branch / accumulate; similar to Java and Python | SP3 |
| .foreach(println) / .toList | Print each item / convert to an immutable List | SP2 |
val expected = (1 to 9).toSet val errors = ListBuffer[String]() for (row <- 0 until 9) { if (board(row).toSet != expected) { errors += s"LINE ${row + 1} INVALID" } } var currentSum = 0L currentSum += 3
expected = set(range(1, 10))
errors = []
for row in range(9):
if set(board[row]) != expected:
errors.append(f"LINE {row + 1} INVALID")
current_sum = 0
current_sum += 3Quick memory rule: val is a fixed reference; var allows reassignment; until excludes the end; to includes the end; Scala indexes with (); collect errors with ListBuffer。
A file contains top-level block A, block B inside main(), a main guard that calls main(), and top-level block C after the guard. What is the execution order when running python3 file.py? Assume normal completion.
| Choice | Execution order |
|---|---|
| A | A then C |
| B | A then C then B |
| C | A then B then C |
| D | Only B is executed |
| E | A then B |
python3 file.py
A B C
Answer: C. Defining main() does not execute its body. The main guard is true for direct execution, so main() runs before the interpreter continues to block C.
Given a filled 9 × 9 grid of integers as List[List[Int]], check whether every row, column, and 3 × 3 region contains exactly the digits 1 through 9. Return all invalid units as LINE x INVALID, COLUMN y INVALID, or REGION z INVALID. Return List("VALID") when all checks pass.
Practice conventions: Input shape is guaranteed to be 9 × 9. Unit numbers are 1-based; regions run left to right, top to bottom. Return row errors, then column errors, then region errors. Confirm these details if the actual test specifies different conventions.
6 2 3 4 5 6 7 8 9 4 5 6 7 8 9 1 2 3 7 8 9 1 2 3 4 5 6 2 3 4 5 6 7 8 9 1 5 6 7 8 9 1 2 3 4 8 9 1 2 3 4 5 6 7 3 4 5 6 7 8 9 1 2 6 7 8 9 1 2 3 4 5 9 1 2 3 4 5 6 7 8
LINE 1 INVALID COLUMN 1 INVALID REGION 1 INVALID
import scala.collection.mutable.ListBuffer
def validateSudoku(grid: List[List[Int]]): List[String] = {
// Input is guaranteed to be a filled 9 x 9 board.
val board = grid.map(_.toArray).toArray
val expected = (1 to 9).toSet
val errors = ListBuffer[String]()
for (row <- 0 until 9) {
if (board(row).toSet != expected) {
errors += s"LINE ${row + 1} INVALID"
}
}
for (col <- 0 until 9) {
val values = (0 until 9).map(row => board(row)(col))
if (values.toSet != expected) {
errors += s"COLUMN ${col + 1} INVALID"
}
}
for (region <- 0 until 9) {
val startRow = (region / 3) * 3
val startCol = (region % 3) * 3
val values = scala.collection.mutable.Set[Int]()
for (row <- startRow until startRow + 3) {
for (col <- startCol until startCol + 3) {
values += board(row)(col)
}
}
if (values.toSet != expected) {
errors += s"REGION ${region + 1} INVALID"
}
}
if (errors.isEmpty) List("VALID") else errors.toList
}
val grid = List(
List(6,2,3,4,5,6,7,8,9),
List(4,5,6,7,8,9,1,2,3),
List(7,8,9,1,2,3,4,5,6),
List(2,3,4,5,6,7,8,9,1),
List(5,6,7,8,9,1,2,3,4),
List(8,9,1,2,3,4,5,6,7),
List(3,4,5,6,7,8,9,1,2),
List(6,7,8,9,1,2,3,4,5),
List(9,1,2,3,4,5,6,7,8)
)
validateSudoku(grid).foreach(println)
// LINE 1 INVALID
// COLUMN 1 INVALID
// REGION 1 INVALIDdef validate_sudoku(board):
expected = set(range(1, 10))
errors = []
for row in range(9):
if set(board[row]) != expected:
errors.append(f"LINE {row + 1} INVALID")
for col in range(9):
values = [board[row][col] for row in range(9)]
if set(values) != expected:
errors.append(f"COLUMN {col + 1} INVALID")
for region in range(9):
start_row = (region // 3) * 3
start_col = (region % 3) * 3
values = [
board[row][col]
for row in range(start_row, start_row + 3)
for col in range(start_col, start_col + 3)
]
if set(values) != expected:
errors.append(f"REGION {region + 1} INVALID")
return errors if errors else ["VALID"]
board = [
[6,2,3,4,5,6,7,8,9],
[4,5,6,7,8,9,1,2,3],
[7,8,9,1,2,3,4,5,6],
[2,3,4,5,6,7,8,9,1],
[5,6,7,8,9,1,2,3,4],
[8,9,1,2,3,4,5,6,7],
[3,4,5,6,7,8,9,1,2],
[6,7,8,9,1,2,3,4,5],
[9,1,2,3,4,5,6,7,8]
]
print("\n".join(validate_sudoku(board)))
# LINE 1 INVALID
# COLUMN 1 INVALID
# REGION 1 INVALIDGiven monthly net profits as data: List[Int], find a nonempty contiguous interval with the greatest total profit. Return List(start, end), using zero-based inclusive indices. Negative values represent monthly losses.
Practice conventions: Empty input returns an empty list. For equal maximum sums, choose the earliest start; for the same start, choose the earliest end. These rules are additions because the screenshot does not specify them.
data = [-2, 1, -3, 4, -1, 2, 1, -5, 4]
[3, 6]
The selected values are [4, -1, 2, 1], whose sum is 6. Return the indices [3,6], not the sum 6. These are already monthly profits, not stock prices.
def maxProfitInterval(data: List[Int]): List[Int] = {
if (data.isEmpty) return List.empty[Int]
val nums = data.toArray
var currentSum = nums(0).toLong
var bestSum = currentSum
var currentStart = 0
var bestStart = 0
var bestEnd = 0
for (i <- 1 until nums.length) {
// Restart only when the previous sum is negative.
if (currentSum < 0) {
currentSum = nums(i).toLong
currentStart = i
} else {
currentSum += nums(i).toLong
}
// Strict comparison preserves the earliest tied interval.
if (currentSum > bestSum) {
bestSum = currentSum
bestStart = currentStart
bestEnd = i
}
}
List(bestStart, bestEnd)
}
println(maxProfitInterval(List(-2,1,-3,4,-1,2,1,-5,4)))
// List(3, 6)
println(maxProfitInterval(List(-5,-2,-8)))
// List(1, 1)def max_profit_interval(data):
if not data:
return []
current_sum = best_sum = data[0]
current_start = best_start = best_end = 0
for i in range(1, len(data)):
# Restart or extend the current interval.
if current_sum < 0:
current_sum = data[i]
current_start = i
else:
current_sum += data[i]
if current_sum > best_sum:
best_sum = current_sum
best_start = current_start
best_end = i
return [best_start, best_end]
print(max_profit_interval([-2,1,-3,4,-1,2,1,-5,4]))
# [3, 6]
print(max_profit_interval([-5,-2,-8]))
# [1, 1]These are practice predictions, not confirmed SPH exam questions. Original SP1–SP3 stay first. Each code comparison is Scala left and Python right.
What does the Python program print? Focus on when a default list is created. The Scala example uses a fresh List each call and is not behaviorally identical.
def add(x: Int, xs: List[Int] = List.empty[Int]): List[Int] = xs :+ x
println(add(1))
println(add(2))def add(x, xs=[]):
xs.append(x)
return xs
print(add(1))
print(add(2))| A | [1], then [2] |
| B | [1], then [1, 2] |
| C | [1, 2], then [1, 2] |
| D | TypeError |
Answer: B — Python evaluates the default list once when the function is defined. The second call reuses and mutates that same list. The Scala example demonstrates an immutable default List, not identical default behavior.
Given a list of integers and a target, return the indices of two distinct elements whose sum equals the target. Assume exactly one solution. Return the indices in ascending order.
nums = [2, 7, 11, 15], target = 9[0, 1]import scala.collection.mutable
def twoSum(nums: List[Int], target: Int): List[Int] = {
val seen = mutable.Map[Int, Int]()
val a = nums.toArray
for (i <- a.indices) {
val need = target - a(i)
if (seen.contains(need)) return List(seen(need), i)
seen(a(i)) = i
}
List.empty[Int]
}
println(twoSum(List(2, 7, 11, 15), 9))def two_sum(nums, target):
seen = {}
for i, x in enumerate(nums):
need = target - x
if need in seen:
return [seen[need], i]
seen[x] = i
return []
print(two_sum([2, 7, 11, 15], 9))Given a string s, return the length of its longest contiguous substring with no repeated characters. Treat each character as a character/code point in the given input.
s = "abcabcbb"3import scala.collection.mutable
def longestUnique(s: String): Int = {
val seen = mutable.Set[Char]()
var left = 0
var best = 0
for (right <- s.indices) {
while (seen.contains(s(right))) {
seen -= s(left)
left += 1
}
seen += s(right)
best = math.max(best, right - left + 1)
}
best
}
println(longestUnique("abcabcbb"))def longest_unique(s):
seen = set()
left = 0
best = 0
for right, ch in enumerate(s):
while ch in seen:
seen.remove(s[left])
left += 1
seen.add(ch)
best = max(best, right - left + 1)
return best
print(longest_unique("abcabcbb"))Which statement is true about this Scala code? Distinguish reassignment of a reference from changing an immutable List.
val xs = List(1, 2)
val ys = xs :+ 3
println(xs)
println(ys)xs = [1, 2]
ys = xs + [3]
print(xs)
print(ys)| A | xs becomes List(1, 2, 3) |
| B | xs remains List(1, 2); ys is List(1, 2, 3) |
| C | val prevents calling methods on xs |
| D | The code cannot compile |
Answer: B — List is immutable and :+ creates a new List. The val binding of xs stays unchanged. Python concatenation with + similarly creates a new list here.
What are the two printed values? Note the inclusive vs exclusive ranges and Set deduplication.
val a = (1 until 4).toList
val b = List(1, 1, 2, 3).toSet.size
println(a)
println(b)a = list(range(1, 4))
b = len(set([1, 1, 2, 3]))
print(a)
print(b)| A | List(1, 2, 3), then 3 |
| B | List(1, 2, 3, 4), then 4 |
| C | List(1, 2), then 3 |
| D | List(1, 2, 3), then 4 |
Answer: A — until excludes 4, so a contains 1, 2 and 3. toSet removes the duplicate 1, leaving 3 distinct values.
Four questions supplied by the user for extra preparation. These are not confirmed SPH Media exam questions. Choose the output of the Python code on the right. Scala equivalents or comparisons appear on the left; behavior can differ between languages and is noted in the explanations. Answers are folded by default.
What are the final values printed for my_list1, my_list2 and my_list3? The second call supplies an explicit empty list.
import scala.collection.mutable.ArrayBuffer
object DefaultDemo {
private val sharedDefault = ArrayBuffer.empty[Int]
def appendTo(element: Int,
target: ArrayBuffer[Int] = sharedDefault): ArrayBuffer[Int] = {
target += element
target
}
}
val myList1 = DefaultDemo.appendTo(1)
val myList2 = DefaultDemo.appendTo(2, ArrayBuffer.empty[Int])
val myList3 = DefaultDemo.appendTo(3)
println("my_list1: " + myList1)
println("my_list2: " + myList2)
println("my_list3: " + myList3)def append_to(element, target=[]):
target.append(element)
return target
my_list1 = append_to(1)
my_list2 = append_to(2, [])
my_list3 = append_to(3)
print("my_list1:", my_list1)
print("my_list2:", my_list2)
print("my_list3:", my_list3)| Option | Python output |
|---|---|
| A | [1], [2], [3] |
| B | [1, 3], [2], [1, 3] |
| C | [1, 2, 3], [2], [3] |
| D | [1, 3], [2], [3] |
Answer: B — Python evaluates the default [] once when the function is defined. Calls 1 and 3 share that same list, so both my_list1 and my_list3 reference [1, 3]. The explicit [] in call 2 is separate. Scala comparison explicitly defines sharedDefault to reproduce this behavior; a fresh Scala default expression may behave differently.
What is printed by results? Pay attention to when each lambda reads i.
// Scala for-comprehension captures each iteration value.
val multipliers = (0 until 3).map { i =>
(x: Int) => i * x
}
val results = multipliers.map(m => m(2))
println("results: " + results)
// Scala normally prints Vector(0, 2, 4).def create_multipliers():
return [lambda x: i * x for i in range(3)]
multipliers = create_multipliers()
results = [m(2) for m in multipliers]
print("results:", results)| Option | Python output |
|---|---|
| A | [0, 2, 4] |
| B | [0, 0, 0] |
| C | [4, 4, 4] |
| D | [2, 2, 2] |
Answer: C — Python lambdas close over the same i variable, whose final value after range(3) is 2; every lambda then returns 2 * 2 = 4. The Scala code intentionally shows a language difference: the for-comprehension creates a distinct iteration binding and normally yields Vector(0, 2, 4). This is a comparison, not identical output.
What is the complete order of print statements, from definition to the call of my_func()?
// Higher-order functions mimic decorator composition.
def dec1(func: () => Unit): () => Unit = {
println("dec1 applied")
() => {
println("dec1 executed")
func()
}
}
def dec2(func: () => Unit): () => Unit = {
println("dec2 applied")
() => {
println("dec2 executed")
func()
}
}
val myFunc = dec1(dec2(() => println("my_func executed")))
println("--- Start Calling ---")
myFunc()def dec1(func):
print("dec1 applied")
def wrapper(*args, **kwargs):
print("dec1 executed")
return func(*args, **kwargs)
return wrapper
def dec2(func):
print("dec2 applied")
def wrapper(*args, **kwargs):
print("dec2 executed")
return func(*args, **kwargs)
return wrapper
@dec1
@dec2
def my_func():
print("my_func executed")
print("--- Start Calling ---")
my_func()| Option | Python output |
|---|---|
| A | dec1 applied → dec2 applied → Start → dec1 executed → dec2 executed → my_func executed |
| B | dec2 applied → dec1 applied → Start → dec1 executed → dec2 executed → my_func executed |
| C | Start → dec1 applied → dec2 applied → my_func executed |
| D | dec2 executed → dec1 executed → Start → my_func executed |
Answer: B — @dec1 above @dec2 means my_func = dec1(dec2(my_func)). Application happens bottom-up (dec2 applied, then dec1 applied). Calling the decorated function enters the outer wrapper first (dec1 executed, then dec2 executed), then the original function.
What gets printed, and which value is returned by check_order()? Note that the finally block also contains return.
// Idiomatic Scala: finally is for cleanup, not return.
def checkOrder(): String = {
try {
println("In try")
"Return from try"
} catch {
case _: Exception => "Return from except"
} finally {
println("In finally")
}
}
val result = checkOrder()
println("Final Result: " + result)
// Scala comparison returns "Return from try".def check_order():
try:
print("In try")
return "Return from try"
except Exception:
return "Return from except"
finally:
print("In finally")
return "Return from finally"
result = check_order()
print("Final Result:", result)| Option | Python output |
|---|---|
| A | In try → Final Result: Return from try |
| B | In try → In finally → Final Result: Return from try |
| C | In try → In finally → Final Result: Return from finally |
| D | In try → Return from except → In finally |
Answer: C — Python always executes finally before completing the function. A return in finally overrides a pending return from try (or except), so the final result is "Return from finally". The Scala example deliberately follows recommended cleanup style without a return in finally; it demonstrates a different result, not the Python behavior.
SP1–SP3 are reconstructions from supplied screenshots, not a verified original paper. SP4–SP8 are assistant-created mock practice questions. SP9–SP12 were supplied by the user for extra Python practice and have not been verified as SPH Media exam questions. Scala/Python comparisons can intentionally differ in language-specific behavior; Python is authoritative for SP9–SP12.
SPH Media Data Engineer interview page · Python main-guard documentation
Public sources: NodeFlair · SPH Data Engineer interviews · Glassdoor · SPH interviews · SPH Audience Data Engineer JD
Click any cell to read it aloud. Click it again to stop. Use the voice and speed controls above.
| Category | What to say |
|---|---|
| 1. Confirm the goal | Let me confirm my understanding. |
| 1. Confirm the goal | We need to return … |
| 1. Confirm the goal | Could you give me an example? |
| 2. Check constraints | Can the input be empty? |
| 2. Check constraints | Can there be duplicate values? |
| 2. Check constraints | Is the input already sorted? |
| 2. Check constraints | What should I return if there is no solution? |
| 2. Check constraints | How large can the input be? |
| 3. Choose a data structure | I’ll use a dictionary to store … |
| 3. Choose a data structure | This lets me look up values quickly. |
| 3. Choose a data structure | I’ll use a set to check whether a value exists. |
| 3. Choose a data structure | I’ll use a min-heap to keep track of the smallest value. |
| 4. Explain the algorithm | I’ll use a sliding window to track the current segment. |
| 4. Explain the algorithm | I’ll expand the right side and shrink from the left when needed. |
| 4. Explain the algorithm | I’ll use BFS to process the nodes level by level. |
| 4. Explain the algorithm | I’ll use a stack to handle the nested structure. |
| 4. Explain the algorithm | I’ll sort the intervals first, then merge overlapping ones. |
| 5. Discuss improvement | A simple solution is … It takes O(n squared) time. |
| 5. Discuss improvement | We can improve it by using … |
| 6. Start coding | I’ll first explain my approach, then write the code. |
Recognize the trigger. Name the state. Explain the update. Reconstruct the code.
30-second recall: read the memory line, look away, say the four steps, then write the code from a blank page.
| Pattern | Trigger | Memory line |
|---|---|---|
| Hash map / Two Sum | Pair + target sum | Check complement → store current value. |
| Sliding window | Contiguous range + constraint | Right expands → left repairs → update best. |
| Intervals | Overlapping ranges | Sort when needed → compare endpoints → merge. |
| Min-heap / Meeting Rooms | Concurrent tasks need resources | Sort starts → reuse earliest room → push end. |
| Dijkstra | Non-negative weighted shortest paths | Pop earliest → skip stale → relax neighbors. |
| Clone Graph | Deep-copy nodes and links | Check map → create and record → connect copies. |
| Top-K | Keep K best candidates | Count if needed → maintain a size-K heap. |
| Dynamic programming | Repeated subproblems | Define state → base case → transition → answer. |
BFS uses a queue to explore by distance. Topological sort uses a queue of tasks whose dependency counts are zero. The queue looks similar; the eligibility rule is different.
def twoSum(nums, target):
seen = {}
for i, x in enumerate(nums):
need = target - x
if need in seen:
return [seen[need], i]
seen[x] = i
return []def lengthOfLongestSubstring(s):
seen = set()
left = 0
ans = 0
for right, ch in enumerate(s):
while ch in seen:
seen.remove(s[left])
left += 1
seen.add(ch)
ans = max(ans, right - left + 1)
return ansimport heapq
def findKthLargest(nums, k):
heap = []
for x in nums:
heapq.heappush(heap, x)
if len(heap) > k:
heapq.heappop(heap)
return heap[0]def merge(intervals):
intervals.sort(key=lambda x: x[0])
merged = []
for start, end in intervals:
if not merged or start > merged[-1][1]:
merged.append([start, end])
else:
merged[-1][1] = max(merged[-1][1], end)
return mergedfrom collections import deque
def levelOrder(root):
if not root:
return []
q = deque([root])
ans = []
while q:
level = []
level_size = len(q)
for _ in range(level_size):
node = q.popleft()
level.append(node.val)
if node.left:
q.append(node.left)
if node.right:
q.append(node.right)
ans.append(level)
return ansfrom collections import defaultdict, deque
def canFinish(numCourses, prerequisites):
graph = defaultdict(list)
indegree = [0] * numCourses
for course, pre in prerequisites:
graph[pre].append(course)
indegree[course] += 1
q = deque(i for i in range(numCourses) if indegree[i] == 0)
done = 0
while q:
node = q.popleft()
done += 1
for nxt in graph[node]:
indegree[nxt] -= 1
if indegree[nxt] == 0:
q.append(nxt)
return done == numCoursesdef numIslands(grid):
if not grid or not grid[0]:
return 0
rows, cols = len(grid), len(grid[0])
def dfs(r, c):
if r < 0 or c < 0 or r >= rows or c >= cols or grid[r][c] != "1":
return
grid[r][c] = "0"
dfs(r + 1, c)
dfs(r - 1, c)
dfs(r, c + 1)
dfs(r, c - 1)
ans = 0
for r in range(rows):
for c in range(cols):
if grid[r][c] == "1":
ans += 1
dfs(r, c)
return ansdef search(nums, target):
l, r = 0, len(nums) - 1
while l <= r:
m = (l + r) // 2
if nums[m] == target:
return m
if nums[l] <= nums[m]:
if nums[l] <= target < nums[m]:
r = m - 1
else:
l = m + 1
else:
if nums[m] < target <= nums[r]:
l = m + 1
else:
r = m - 1
return -1