Practice problem
Top Three Scores
MediumSolve in Python or JavaScript · graded against 2 sample tests and a hidden test set in your browser · Published
The Bug Report
The program reads a line of scores and prints the three highest, highest first. It runs, it always prints exactly three values separated by spaces, and every value it prints really did come from the input. The three it picks are sometimes the wrong three, and in the wrong order.
Input that shows the problem
5
9 100 25 7 80What the program prints
9 80 7What it should print
100 80 25The largest score in the input is 100 and the program does not mention it, while printing 7 — the smallest score — as though it were in the top three. Notice what it did keep: 9, 80 and 7. A lot of inputs come out perfectly right, and the ones that go wrong all seem to mix scores of different lengths.
Input Format
The input has exactly two lines:
- Line 1: a single integer n, the number of scores (at least 3).
- Line 2: n integers separated by single spaces, the scores themselves.
Output Format
Print one line containing three integers separated by single spaces: the three highest scores, largest
first. When scores repeat, print the repeats — three scores of 9 print as 9 9 9.
How to Debug It
The report mentions that the wrong answers seem to involve scores of different lengths, and that is worth
confirming before anything else, because if it holds it rules out half the program. Build two inputs that
differ in that one respect: a count line of 3 followed by 1 2 3, then a count line of 3 followed by
9 10 11. The first has all single-digit scores, the second mixes widths. Run both. If the all-single-digit one is right and the mixed one is wrong, the slicing and
the reversing are working fine on both — the same three lines of code ran either way — and the difference
has to be in the order the values are in by the time they get sliced.
So inspect that order directly rather than inferring it. Print the list on the line right after it is
sorted, before -3 or slice(-3) touches it. Use the failing sample. What comes back will be in an
order, consistently, and it will not be smallest-to-largest. Find where 9 sits relative to 100 in that
output; if 9 is the greatest thing in the list, ask what comparison could possibly reach that conclusion.
The answer gives you a one-sentence hypothesis about what the sort thinks it is comparing. Test it cheaply
before changing the program: sort ["9", "100", "25"] as text by hand, character by character, and see
whether you reproduce the order the program produced. When it matches, change one thing — how the sort
compares two values — leaving the slice and the reverse exactly as they are. Then re-run the second
sample, all single digits, which was correct before and must stay correct, plus a case with duplicates
such as a count line of 4 followed by 11 2 11 2.
What Was Wrong
Both versions sorted the scores as text instead of as numbers, for slightly different reasons — and the reasons are worth knowing separately, because they are two of the most common ways this happens.
In JavaScript, the scores really were numbers: .map(Number) converted them on the way in. The problem is
sort() called with no arguments. Its default behaviour is not to compare numerically; it converts each
element to a string and sorts by the resulting text in code-unit order. So [9, 100, 25, 7, 80] becomes
[100, 25, 7, 80, 9], because "100" starts with '1' and "9" starts with '9'. Taking the last three
of that and reversing gives 9 80 7.
In Python, sorted compares whatever it is given, and it was given strings: input().split() returns a
list of text pieces and nothing ever converted them. sorted(["9", "100", "25", "7", "80"]) compares text
the same way, producing the same order and the same wrong answer.
The fix in each language is to tell the sort to compare by numeric value:
ordered = sorted(scores, key=int)const ordered = [...scores].sort((a, b) => a - b);The Python version keeps the values as strings — handy, since " ".join(...) needs strings — but orders
them by int(...), which is what key is for. The JavaScript comparator returns a negative number, zero,
or a positive number, which is the contract sort expects, and subtraction satisfies it for ordinary
numbers. Both leave the slice and the reverse untouched, because those two steps were never wrong.
Common Mistakes
- Writing the JavaScript comparator as
(a, b) => a > b.sortneeds a number back, not a boolean.trueandfalsecollapse to 1 and 0, so the comparator can never say "a comes first", and the result is an order that depends on the engine's sorting algorithm rather than on the data. Returna - b. - Fixing Python by converting to
intand forgetting the output. Once the list holds integers," ".join(...)raises aTypeErrorbecause it only joins strings. Either sort bykey=intand keep the strings, or convert to integers and convert back when printing — but do decide which, rather than finding out from a traceback. - Assuming the sort is fine because the answer was right the last five times. Every score being the same width makes text order and numeric order agree, so a whole test suite of single-digit or equal-length values will pass over this bug without a murmur. The inputs that catch it are the ones that mix widths, which is why they belong in the tests on purpose.
Sample tests
Sample 1
Input
5 9 100 25 7 80
Expected output
100 80 25
Sorted by value the scores run 7, 9, 25, 80, 100, so the top three highest-first are 100, 80 and 25.
Sample 2
Input
4 4 9 7 2
Expected output
9 7 4
Every score here is a single digit, which — for reasons the bug report gets into — is exactly when the broken program looks correct.
Your solution
Run tries the first sample. Submit grades against every sample and the hidden tests. Your code is saved in this browser as you go. This problem accepts more than one language: pick yours above the editor, and each keeps its own work while this page is open.
Press Esc then Tab to move keyboard focus out of the code editor.
Output will appear here after you run your code.