A bubble sort over a nearly-ordered status list ships without a swapped flag — what does that cost?
answer
- what does a pass with no swaps prove
- the outer loop never checks progress
- count comparisons on already-ordered input
- one clean pass certifies the whole array
- best case collapses to a single pass
basics
~20 sWithout the flag the outer loop always runs n-1 passes, so even an already-ordered list costs n(n-1)/2 comparisons. Tracking whether a pass swapped anything lets the algorithm stop after one clean pass, cutting the best case to n-1 comparisons.
solid answer
~40 sThe missing flag is not a constant-factor nit — it changes the best case from O(n^2) to O(n). A pass that performs zero swaps proves no adjacent pair is out of order, which for a total order means the whole array is sorted, so the algorithm can stop right there. On an already-ordered list that is one pass and n-1 comparisons instead of n-1 passes and n(n-1)/2 comparisons. Two caveats I would state in the review: the worst case is unchanged at O(n^2), and "nearly ordered" only helps when elements need to move a short distance *leftward* — each pass moves a given element at most one slot left, so a single small value stranded at the end still forces about n passes despite the flag.
code
pseudocode · 5 linesn = length(a)
for i in 0..n-2
for j in 0..n-2-i
if a[j] > a[j+1]
swap(a[j], a[j+1])go deeper
Know that a pass which swaps nothing means the data is already in order, and that checking for it lets the loop stop early instead of running a fixed number of passes.
Explain the cost change precisely: best case from quadratic to linear, worst case unchanged, and give the correctness argument that adjacent ordering everywhere implies global ordering.
Show judgment in the review: quantify the common path on the actual data, resist the overclaim that nearly-ordered means linear, and say when the real fix is a different algorithm rather than a tuned loop.
Frame it as a standards question — whether the team accepts quadratic code on small inputs at all, and what size threshold and review rule keeps that decision from silently drifting upward.
## The code under review ``` n = length(a) for i in 0..n-2 for j in 0..n-2-i if a[j] > a[j+1] swap(a[j], a[j+1]) ``` This is correct — it sorts. The defect is that the outer loop is a fixed count: it runs n-1 passes whether or not the data still needs them. Feed it a list that is already in order and it performs n(n-1)/2 comparisons and zero swaps, doing quadratic work to discover that there was nothing to do. ## The fix and why it is sound ``` n = length(a) for i in 0..n-2 swapped = false for j in 0..n-2-i if a[j] > a[j+1] swap(a[j], a[j+1]) swapped = true if swapped == false break ``` The correctness argument is short and worth being able to state out loud: if a full pass performs no swap, then for every adjacent pair `a[j] <= a[j+1]`. Under a total order, pairwise adjacent ordering across the whole array implies global ordering by transitivity. So a clean pass is a *proof of sortedness*, and stopping is safe. Cost after the fix: - **Best case** (already ordered): one pass, n-1 comparisons, 0 swaps — O(n). - **Worst case** (reversed): unchanged, n(n-1)/2 comparisons and swaps — O(n^2). The flag never fires early on adversarial input. - **Swaps**, with or without the flag: exactly the number of inversions in the input. ## The part candidates get wrong: what "nearly ordered" buys It is tempting to say the flag makes the algorithm linear on nearly-ordered data. That is too strong, and an interviewer will push on it. The number of passes is governed by how far elements have to move **toward the front**, because each pass moves a given element at most one position left. Concretely: - A list that is ordered except that a few values sit slightly *ahead* of where they belong — those large values are carried right quickly, so the flag fires after a couple of passes. Genuine win. - A list that is ordered except that the single smallest value sits at the very end — it crawls one slot left per pass, so about n passes still run. The flag saves almost nothing. The honest statement is: with early exit, the pass count is roughly one more than the maximum leftward distance any element must travel. That asymmetry is a property of adjacent-exchange scanning, and it is exactly the sort of directional detail that separates a memorised answer from an understood one. ## The other bound in the loop Note the inner bound `n-2-i` rather than `n-2`. It skips the tail that is already final, roughly halving the total comparisons. This is a constant-factor improvement only — it does not change the complexity class, and it is independent of the flag. A review comment should distinguish the two: the shrinking bound is an optimisation, the flag is a change of best-case complexity. ## How to write the review comment On a status list that is nearly ordered most of the time — the realistic case, since the list is re-sorted after small updates — the missing flag means the common path pays the worst-case comparison count on every call. The suggested change is three lines, does not alter the result, and turns the common path linear. If someone objects that "bubble sort is O(n^2) anyway", the answer is that O(n^2) is the *worst-case upper bound*; best case with the flag is O(n), and the code as written never reaches it. Worth pairing with a second, larger comment: at any size where the quadratic term matters, the right fix is not tuning this loop but choosing a different algorithm entirely. The flag is what you ask for when the list is small and the algorithm is staying. ## Common wrong answers - "Bubble sort is O(n^2), period." It is O(n^2) worst case; with the early exit the best case is O(n). - "The flag improves the worst case." It does not; reversed input still costs quadratic time. - "With the flag, any nearly-ordered input is linear." Only if no element has to travel far leftward. - "It is just a constant factor." On ordered input it is the difference between n and n^2 comparisons.
- Does the flag make bubble sort linear on any nearly-ordered input?No. Each pass moves a given element at most one position leftward, so the pass count is roughly the maximum leftward distance any element must travel. A single small value stranded at the end still forces about n passes. The flag guarantees a linear best case on already-ordered input, not on every input that is merely close to ordered.
- Does the flag change the worst-case complexity?No. On reversed input every pass swaps, so the flag never fires and the run costs n(n-1)/2 comparisons and swaps. The change is to the best case only, from quadratic to linear, plus real savings on partly ordered data.
- Why is a pass with zero swaps enough to declare the array sorted?Zero swaps means every adjacent pair satisfies a[j] <= a[j+1]. Under a total order, adjacent ordering everywhere implies global ordering by transitivity, so no further pass could change anything. That is the correctness argument for breaking out early.
- What does the shrinking inner bound n-2-i buy you?It stops the scan before the tail that is already in final position, cutting the total comparison count roughly in half. That is a constant-factor win only — it does not change the complexity class, and it is a separate improvement from the early-exit flag.
saying these in an interview costs you the question
- Says bubble sort is O(n^2) in every case
- Claims the flag improves the worst case
- Thinks early exit makes any nearly-ordered input linear
- Calls the missing flag a mere constant-factor loss
- Cannot justify why a swap-free pass proves sortedness