My Oracle Interview Experience (July 2026) : Round 1 (Senior Platform Engineer)
#oracle
#interview-experience
#backend-interview-experience
I'll be honest, I went into this round expecting mostly systems and infra questions, given the "Platform Engineer" title. Instead I got one array problem that looked deceptively simple and one SQL question that looked deceptively simple in a completely different way. Both of them had a trap sitting just under the surface and I walked into both traps before I found my way out.
This is the write-up of Round 1 exactly as it happened, mistakes included. I'll add the later rounds as separate posts once I've been through them.
| Detail | Info |
|---|---|
| Company | Oracle |
| Role | Senior Platform Engineer |
| Round | Round 1 (Technical / Problem Solving) |
| Topics | Array + Sliding Window, SQL |
| Number of questions | 2 |
| Overall Difficulty | ⭐⭐⭐☆☆ (3/5) |
Question 1: Maximum of Minimums of Every Window
The problem: given an array A of size n and an integer x, look at every contiguous window of length x, find the minimum inside each window and return the largest of those minimums.
Example:
A = [1, 3, -1, 5, 3, 6]
x = 3
Windows:
[1, 3, -1] -> min = -1
[3, -1, 5] -> min = -1
[-1, 5, 3] -> min = -1
[5, 3, 6] -> min = 3
Answer = 3
My First Instinct (and Why It Was Slow)
The obvious first approach: for every window, just scan its x elements and find the minimum directly.
static int bruteForce(int[] a, int x) {
int best = Integer.MIN_VALUE;
for (int i = 0; i + x <= a.length; i++) {
int windowMin = Integer.MAX_VALUE;
for (int j = i; j < i + x; j++) {
windowMin = Math.min(windowMin, a[j]);
}
best = Math.max(best, windowMin);
}
return best;
}
I said out loud that this is O(n · x), since there are roughly n windows and each one costs x work to scan. The interviewer just asked, "Can we avoid recomputing the minimum from scratch every time the window slides by one?" That was the nudge most of the work in the window barely changes between one window and the next, so redoing it all every time is wasteful.
My Actual Mistake: Trying to Track "the Minimum So Far"
My first attempt at speeding it up was to just keep a single running minimum as I scan left to right, updating it as I go.
// WRONG: a running minimum never "forgets" old values that have
// already slid out of the current window.
static int wrongGlobalMin(int[] a, int x) {
int runningMin = Integer.MAX_VALUE;
int best = Integer.MIN_VALUE;
for (int i = 0; i + x <= a.length; i++) {
for (int j = i; j < i + x; j++) {
runningMin = Math.min(runningMin, a[j]);
}
best = Math.max(best, runningMin);
}
return best;
}
This felt right for about ten seconds, until I tested it on a small case in my head: A = [-100, 5, 6, 7, 8, 9], x = 2. The real answer is 8 (the window [8, 9] has the largest minimum). But my running minimum locks onto -100 in the very first window and never lets go, since I never remove old values once they slide out of the window. So it returns -100, which is completely wrong. I caught this by testing it, not because I spotted the flaw by staring at the code worth remembering that trick for yourself.
The hint that got me unstuck: the interviewer asked, "What if a value that used to be your minimum has already slid out of the window do you have any way to know that?" That's the entire problem in one sentence. A plain running minimum has no concept of "expiring."
The Fix: A Monotonic Deque
The right structure here is a deque (double-ended queue) that stores indices, kept in increasing order of their values, front to back. Two rules keep it correct:
Drop indices from the front once they've slid out of the current window (they're too old to matter anymore). Drop indices from the back whenever the new value is smaller than or equal to them (they can never be the minimum again while this new, smaller value is still inside the window, so there's no reason to keep them around).
Because of these two rules, the front of the deque is always the index of the current window's minimum no rescanning needed.
Here's how it plays out on the actual interview example, step by step:
| i | A[i] | Expired from front | Popped from back (≥ A[i]) | Deque after (indices) | Window min |
|---|---|---|---|---|---|
| 0 | 1 | none | none | [0] | – |
| 1 | 3 | none | none | [0, 1] | – |
| 2 | -1 | none | 1(3), 0(1) | [2] | -1 |
| 3 | 5 | none | none | [2, 3] | -1 |
| 4 | 3 | none | 3(5) | [2, 4] | -1 |
| 5 | 6 | 2 | none | [4, 5] | 3 |
Max of window minimums = 3 matching the expected answer.
import java.util.*;
public class MaxOfMins {
static int optimal(int[] a, int x) {
Deque<Integer> deque = new ArrayDeque<>(); // indices, values increasing front-to-back
int best = Integer.MIN_VALUE;
for (int i = 0; i < a.length; i++) {
// Drop indices that fell out of the window on the left.
while (!deque.isEmpty() && deque.peekFirst() <= i - x) {
deque.pollFirst();
}
// Maintain increasing order.
while (!deque.isEmpty() && a[deque.peekLast()] >= a[i]) {
deque.pollLast();
}
deque.offerLast(i);
if (i >= x - 1) {
best = Math.max(best, a[deque.peekFirst()]);
}
}
return best;
}
}
I cross-checked this against the brute force version on 2,000 randomly generated arrays before I trusted it fully every single one matched.
Follow-Up Questions I Got and What I Said
"What's the time and space complexity?"
O(n) time each index gets pushed and popped from the deque at most once across the whole run, so the total work is linear, not quadratic. O(x) space for the deque in the worst case, since it can never hold more than the current window size.
"Why does popping from the back never lose the true answer?"
Because if a later value is smaller than or equal to something already in the deque, that older value can never win it will expire from the window before or at the same time as the new value and the new value is smaller anyway. It's safe to throw away, not just convenient.
"What if x is larger than the array?"
I said there are zero valid windows in that case, so I'd return something like Integer.MIN_VALUE or throw an explicit exception, depending on what the caller expects and that I'd rather make that contract explicit than silently return a meaningless number.
"Could you solve this with a different data structure, like a max-heap or a TreeMap?"
Yes , a multiset-style structure (like a TreeMap<Integer, Integer> counting occurrences) also works, giving O(log x) per operation instead of O(1) amortized with the deque. I said I'd prefer the deque here since it hits true O(n) instead of O(n log x) and the code ends up simpler too.
Question 2: SQL Classify Every Node in a Tree
The table:
id pid
1 NULL
2 1
3 1
4 2
The task: label every node as Root (no parent), Inner (has at least one child) or Leaf (no children).
Expected output: 1 → Root, 2 → Inner, 3 → Leaf, 4 → Leaf.
My Mistake: The Classic NOT IN + NULL Trap
My first pass at the query looked completely reasonable:
SELECT id,
CASE
WHEN pid IS NULL THEN 'Root'
WHEN id NOT IN (SELECT pid FROM Tree) THEN 'Leaf'
ELSE 'Inner'
END AS label
FROM Tree;
I ran it against the sample data in my head and it looked fine at first glance. But the interviewer asked me to actually trace what SELECT pid FROM Tree returns on this exact table. That list is NULL, 1, 1, 2 and it includes a NULL, because the root's pid is NULL.
Here's the trap: in SQL, NOT IN against a list that contains even one NULL doesn't behave the way most people expect. For any value v, v NOT IN (list containing NULL) evaluates to UNKNOWN, not TRUE or FALSE because SQL can't prove v doesn't equal the unknown NULL. An UNKNOWN result is treated as false in a WHERE/CASE check, so my NOT IN branch silently never fires for anyone. When I actually ran it, every non-root node came back as Inner, including nodes 3 and 4, which are actually leaves.
The hint that helped me catch it: he simply asked, "What does the subquery actually return here walk me through the values." That single question is what exposed the NULL sitting in the list.
The Fix: Filter Out NULLs Before Using IN / NOT IN
The safe pattern is to flip the logic to IN instead of NOT IN and explicitly exclude NULL from the subquery so it can never poison the comparison:
SELECT id,
CASE
WHEN pid IS NULL THEN 'Root'
WHEN id IN (SELECT pid FROM Tree WHERE pid IS NOT NULL) THEN 'Inner'
ELSE 'Leaf'
END AS label
FROM Tree
ORDER BY id;
I ran both versions against the sample table to see the difference side by side:
| id | Buggy NOT IN version |
Correct version | Expected |
|---|---|---|---|
| 1 | Root | Root | Root |
| 2 | Inner | Inner | Inner |
| 3 | Inner (wrong) | Leaf | Leaf |
| 4 | Inner (wrong) | Leaf | Leaf |
The corrected query matched the expected output exactly.
Follow-Up Questions I Got and What I Said
1. Is IN always safer than NOT IN when NULLs might be involved?
Not automatically safer, but the failure mode is much gentler: a stray NULL inside an IN list just means that particular NULL comparison contributes nothing extra, since you're checking "does a match exist," not "does no match exist." NOT IN is the one where a single NULL can quietly break every row, so I said I try to default to IN plus an explicit IS NOT NULL filter whenever the subquery might contain nulls at all.
2. How would you write this using a LEFT JOIN instead of a subquery?
SELECT t.id,
CASE
WHEN t.pid IS NULL THEN 'Root'
WHEN c.id IS NOT NULL THEN 'Inner'
ELSE 'Leaf'
END AS label
FROM Tree t
LEFT JOIN Tree c ON c.pid = t.id
GROUP BY t.id, t.pid;
I explained that joining each node to any row where it appears as a parent, then checking whether that join produced a match, sidesteps the NULL-in-a-list issue entirely, since a LEFT JOIN naturally handles "no match" as NULL on the joined side without any special-casing.
3. What if a node could have more than one parent does your query still work?
I said the classification logic itself wouldn't change, since I'm only checking existence of at least one child or one parent either way, but I'd want to add a DISTINCT or GROUP BY to make sure a node with multiple children doesn't get counted more than once and produce duplicate rows in the LEFT JOIN version.
Biggest Learnings From This Round
Both of my mistakes had the same root cause, even though one was an array problem and the other was SQL: I trusted a mental shortcut that works most of the time and stopped checking it against a concrete example. A running minimum "usually" looks right until a value expires. A NOT IN subquery "usually" looks right until a NULL sneaks into it. In both cases, actually tracing through a small, specific example not just reasoning abstractly is what exposed the bug and in both cases that's exactly what the interviewer's question pushed me to do.
Preparation Tips
Practice sliding window problems specifically with the monotonic deque pattern . it comes up constantly in "min/max of every window" style questions and it's a genuine pattern worth having memorized, not reinvented under pressure.
Before trusting any optimization to a brute force solution, test it against the brute force version on a handful of small, deliberately tricky cases especially cases where the "obviously right" answer changes partway through the array. That's exactly how I caught my running-minimum bug.
For SQL rounds, always ask yourself whether any subquery you're using inside an IN or NOT IN could ever return a NULL. If there's any chance of it, either filter the NULLs out explicitly or switch to a LEFT JOIN pattern instead.
Say your complexity analysis out loud even when it's not asked for directly in my round, walking through why something is O(n) instead of O(n log x) was where a couple of the best follow-up questions came from.
Don't be afraid to trace through a tiny concrete example on the spot, even mid-explanation. Both of my mistakes were caught by tracing, not by cleverness and interviewers generally see that as a strength, not a stall.
FAQs
1. What kind of questions does Oracle ask in a Senior Platform Engineer interview?
In my Round 1, it was a mix of a classic array/sliding-window DSA problem and a SQL question not the infrastructure-heavy questions I expected going in based on the "Platform Engineer" title. Don't assume the title tells you the whole syllabus.
2. What is the monotonic deque pattern and when should I use it?
It's a technique for maintaining the min or max of a sliding window in O(n) total time, using a deque that stores indices in increasing (or decreasing) order of their values. Reach for it any time you see "minimum/maximum of every window of size k" recomputing from scratch every window is the giveaway that a smarter structure is expected.
3. Why is NOT IN risky in SQL?
Because if the subquery inside NOT IN can return even a single NULL, every row's NOT IN check evaluates to UNKNOWN instead of TRUE/FALSE, which is treated as false silently breaking the query for every row, not just the one tied to the NULL. Filtering NULLs out of the subquery or rewriting the check as IN (or a LEFT JOIN), avoids the trap.
4. Is Oracle's technical round hard for a senior-level role?
I'd call it a solid 3 out of 5 for Round 1 specifically the concepts themselves (sliding window, basic tree classification) aren't obscure, but both questions had a real trap that a less careful answer would walk straight into. The bar felt like it was on catching the trap and reasoning about complexity, not memorizing a rare algorithm.
Conclusion
Two questions, two traps, two mistakes I caught myself into mostly by testing small examples instead of trusting my first instinct. If Round 2 happens, I'll write that one up too. For now, my one takeaway for anyone prepping for something similar: whatever pattern you reach for first, run it against one small, deliberately awkward example before you commit to it out loud.
