Understand
30-second explanation
Build the interview-ready mental model before comparing implementations.
Each customer i (1-indexed) places an order at time `order[i]` and the dish takes `prep[i]` to cook, so it is served at `order[i] + prep[i]`. Customers are served in increasing serve-time, breaking ties by the smaller customer number. Pair each customer's serve time with its id, sort by (serveTime, id), and output the ids in that order. O(n log n) from the sort.
Interview contextWhat is testedWhere candidates slipHow to stand outExplore interview contextHide interview context
What's being tested
Whether you derive the correct sort key (order + prep) and implement a stable, deterministic tie-break by customer id, while remembering the output is 1-indexed ids.
Where candidates slip
Sorting by prep time or order time alone instead of their sum, mishandling ties (non-deterministic order), or outputting 0-indexed positions or the times themselves.
How to stand out
Note the tie-break makes the result deterministic and that the sum is the only meaningful key for a single-server first-come-by-completion model; mention stability vs explicit id comparison.
Choose
Compare the available approaches
Use constraints and trade-offs to select the route you can defend in an interview.
Interactive decision treeInteractiveChoose the right approachExplore the full treeCollapse the tree
How to read it:The serve time is order + prep; sort customers by (serveTime, id) and output the ids.
Learn & trace
Build each solution step by step
Move from the core idea to a visual trace, implementation, proof, and edge cases.
Approach 1 of 2
Brute Force — Repeatedly Scan for the Next-Served Customer
1Hint 1Explore hintHide hint
At each step, the next customer served is the unserved one with the smallest serve time (ties → smallest id). You could just scan for that customer each round.
2Hint 2Explore hintHide hint
Repeat n times: each round scan all remaining customers to find the minimum (serveTime, id), output its id, and mark it served.
3Hint 3Explore hintHide hint
That's a selection-sort-style O(n²). Correct, but one comparator-based sort does the same in O(n log n).
A · Core idea
Mimic the server literally. Repeatedly look through every customer not yet served, pick the one whose serve time order + prep is smallest (breaking ties by smaller id), output its id, and mark it served. Do this n times.
Why it's correct. Each round emits exactly the customer the server would serve next, so the full sequence is the true serving order.
Why it's slow. Finding each minimum costs a full O(n) scan and we do n rounds → O(n²). For the constraints (n ≤ 1000) this even passes, but it's the unsorted way of saying 'sort'.
The structure it reveals. Repeatedly extracting the minimum by a fixed key is sorting. Replace the n scans with one comparator sort on (serveTime, id) and read the ids off in order — O(n log n), same result.
B · Visual dry run
Interactive dry run
Select a frame to connect the decision with its visual state.
n = 3, orders = [[1,1],[2,2],[1,1]]round 1
min among (2,1),(4,2),(2,3) → (2,1)
output 11 3 2C · Code with teaching comments
Reference implementation previewpublic int[] jimOrdersBrute(int[][] orders) { int n = orders.length; boolean[] served = new boolean[n]; int[] result = new int[n]; for (int r = 0; r < n; r++) {Explore the full codeHide the full code
D · Why it works and complexityO(n²) timeO(n) spaceOpen the invariant and proofExplore the proofHide the proof
Repeatedly selecting the minimum by a fixed key is selection sort in disguise; a single comparator sort on (serveTime, id) collapses the n scans into O(n log n).
ComplexityO(n²) time, O(n) space — n rounds, each scanning up to n remaining customers to pick the minimum → O(n²). Output buffer is O(n).
E · Edge cases and pitfalls3O(n²) selection loop where a single sort suffices.Explore edge casesHide edge cases
Pitfalls
- O(n²) selection loop where a single sort suffices.
- Comparing only prep or only order time instead of their sum.
- Outputting 0-indexed positions instead of 1-indexed ids.
Approach 2 of 2 · Optimal
Greedy — Sort by (Serve Time, Id) (Optimal)
1Hint 1Explore hintHide hint
Each customer's serve time is independent: order + prep. The serving order is just these times sorted ascending.
2Hint 2Explore hintHide hint
Attach the 1-indexed id to each serve time so you can output ids, and so ties can be broken deterministically.
3Hint 3Explore hintHide hint
Sort the (serveTime, id) pairs by serveTime then id, and emit the ids. One comparator sort, done.
A · Core idea
Compute each customer's serve time order[i] + prep[i], pair it with the customer's 1-indexed id, sort the pairs by serve time (ties broken by smaller id), and output the ids in that order.
Why sorting by finish time is optimal — the exchange argument. The server always serves the dish that is ready earliest. Suppose a serving sequence puts a customer with a later serve time before one with an earlier serve time. Those two are out of finish-time order, so the sequence doesn't match 'earliest-ready served first'. Swap that adjacent pair: now the earlier-finishing dish is served first, which is exactly the server's rule, and no other customer's relative position changes. Repeating these swaps sorts the whole sequence by serve time — and each swap only fixes an out-of-order pair, never creating a new violation. The unique fully-ordered sequence is the answer.
Why ties break by id. When two serve times are equal the server's rule says lower id first. Encoding id as the secondary sort key makes the output deterministic and matches the specification. (A stable sort on a pre-id-ordered list also works, but an explicit secondary key is clearest.)
Why the sum is the only key. In this single-server-by-completion model each dish's readiness depends solely on its own order and prep times; nothing couples customers. So neither order nor prep alone determines serving order — only their sum does.
B · Visual dry run
Interactive dry run
Select a frame to connect the decision with its visual state.
n = 5, orders = [[8,1],[4,2],[5,6],[1,1],[3,1]]Array state
before sort
pairs (serve, id)
4 5 2 1 3C · Code with teaching comments
D · Why it works and complexityO(n log n) timeO(n) spaceOpen the invariant and proofExplore the proofHide the proof
The entire problem is choosing the comparator: (order + prep, id). Once the right finish-time key is identified, an O(n log n) sort produces the serving order directly. Reusable kernel: 'process jobs by independent completion time, deterministic tie-break, emit indices'.
ComplexityO(n log n) time, O(n) space — Building the key array is O(n); the comparator sort is O(n log n) and dominates; emitting ids is O(n). O(n) auxiliary space for the pairs.
E · Edge cases and pitfalls6all equal serve times — Every pair ties → output is ids in ascending order 1,2,…,n.Explore edge casesHide edge cases
Edge cases handled
all equal serve timesEvery pair ties → output is ids in ascending order 1,2,…,n.n = 1Single customer → output [1] regardless of times.tie between c1 and c3 at same serve timeSecondary key id puts c1 before c3 (e.g. example 3 → 1 3 2).
Pitfalls
- Sorting by prep or order time alone instead of their sum.
- Non-deterministic tie handling — must break ties by smaller id.
- Returning serve times or 0-indexed positions instead of 1-indexed ids; also use long to avoid 10⁹ + 10⁹ overflow.
Practice
Try the solution yourself
Edit the optimal implementation, test another input, and observe the result.
Editable code previewpublic int[] jimOrders(int[][] orders) { int n = orders.length; Integer[] ids = new Integer[n]; for (int i = 0; i < n; i++) ids[i] = i; Arrays.sort(ids, (a, b) -> {Explore the playgroundClose the playground
Avoid mistakes
Recognize the interview traps
Understand why common attempts fail before reviewing the corrected implementation.
1Sorting by the wrong key
The serve time is order + prep. Sorting on prep alone (or order alone) produces the wrong serving sequence.
Explore the code comparisonHide the code comparison
Sorting by the wrong key
The serve time is order + prep. Sorting on prep alone (or order alone) produces the wrong serving sequence.
Don't write this
ids = sorted(range(n), key=lambda i: orders[i][1]) # prep onlyWrite this instead
ids = sorted(range(n), key=lambda i: (orders[i][0] + orders[i][1], i))2Missing the id tie-break
When two serve times match, the lower id must come first. Omitting the secondary key makes ordering non-deterministic.
Explore the code comparisonHide the code comparison
Missing the id tie-break
When two serve times match, the lower id must come first. Omitting the secondary key makes ordering non-deterministic.
Don't write this
Arrays.sort(ids, (a, b) -> Long.compare(
orders[a][0] + (long) orders[a][1],
orders[b][0] + (long) orders[b][1]));Write this instead
Arrays.sort(ids, (a, b) -> {
long sa = orders[a][0] + (long) orders[a][1];
long sb = orders[b][0] + (long) orders[b][1];
if (sa != sb) return Long.compare(sa, sb);
return Integer.compare(a, b);
});3Integer overflow on the sum
order and prep can each be 10⁹; their int sum overflows. Cast to long before adding.
Explore the code comparisonHide the code comparison
Integer overflow on the sum
order and prep can each be 10⁹; their int sum overflows. Cast to long before adding.
Don't write this
int serve = orders[i][0] + orders[i][1]; // can overflow intWrite this instead
long serve = orders[i][0] + (long) orders[i][1];Revise
Carry the pattern into the interview
Pattern to remember
Greedy by completion time: sort on a derived key (serveTime, id) and read off the ids
Code pattern to memorize
serve[i] = order[i] + prep[i]
build list of (serve[i], id=i+1)
sort by serve ascending, then id ascending
return the ids in sorted orderRules to remember
- 1Serve time = order (placement) time + preparation (cook) time. That single sum is the sort key.
- 2Customers are 1-indexed in the output — id = array position + 1.
- 3Ties on serve time break by the SMALLER customer id (earlier customer number served first).
- 4Sort the (serveTime, id) pairs; the output is the sequence of ids, not the serve times.
- 5The order in which customers appear in the input doesn't matter beyond fixing their ids.
Use this pattern when
- Order events by a computed completion/finish time
- A deterministic tie-break (by id / index) is required
- The answer is the permutation of indices induced by sorting a derived key
- Single-server scheduling where each job's finish time is independent
Choose another pattern when
- Jobs have dependencies or release constraints affecting each other → scheduling DP, not a sort
- You must minimise total/weighted completion time by choosing order → shortest-job-first, a different key
- Serve time isn't a simple sum (e.g. server can batch) → model changes
- Ties are broken arbitrarily / not by id → comparator differs
Interview takeaway
When each job's finish time is independent and you just report processing order, compute the finish key, sort with a deterministic tie-break, and emit the indices. The whole problem is choosing the right comparator: (orderTime + prepTime, id).
Continue
Transfer the pattern to another problem
Use a direct variation or pattern sibling to prove the idea is reusable.
Transferable patternGreedy by completion time: when each job's finish moment is independent, sort on the derived key (order + prep, id) and emit indices. The adjacent-swap exchange argument shows earliest-finish-first is the unique correct order; the id key makes ties deterministic.
Practice queueDirect variations and pattern siblings
Direct follow-ups
- Priyanka and ToysAnother sort-first greedy — sort then sweep with a simple rule.
- Greedy FloristSort on a derived key and assign, justified by an exchange argument.
- Minimum Time to Finish All Jobs (SJF)Coming soonWhen minimising total completion time you sort by prep alone — a different key.
- Task SchedulerComing soonOrdering jobs by a computed priority to optimise a schedule.