Changing On3 to On2 in JavaScript
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
Moving from O(n^3) to O(n^2) is usually not about writing "faster JavaScript." It is about removing one entire dimension of repeated work. In practice that means recognizing which inner-loop computation can be replaced with precomputed data, a hash lookup, or a sorted traversal.
Where O(n^3) Usually Comes From
A cubic algorithm often appears as three nested loops over the same dataset. For example, suppose you want to know whether any three numbers sum to a target:
This code is correct, but it checks every triple explicitly. For large inputs, that becomes expensive quickly.
The important observation is that the third loop is only answering one question: "Given values[i] and values[j], does there exist a matching third value?" Once you see the question clearly, you can often replace the loop with a faster lookup.
Reducing One Loop with a Set
One common technique is to keep two loops and replace the innermost scan with a Set membership check.
This is O(n^2) on average because:
- the outer loop runs
ntimes - the inner loop runs about
ntimes - each
Setlookup is constant time on average
The exact problem does not matter much. The pattern is what matters: if the third loop is just checking membership, counting occurrences, or finding a complement, a hash-based structure usually removes it.
Another Strategy: Sort and Use Two Pointers
Sometimes the cubic code is comparing ordered values, ranges, or sums. In those cases, sorting once and then scanning with two pointers can reduce complexity.
Sorting costs O(n log n), but the main scan is O(n^2), so the total remains quadratic.
This approach is useful when:
- comparisons depend on numeric ordering
- you need deterministic traversal
- duplicate handling is easier after sorting
A More General Optimization Mindset
When you see O(n^3), do not immediately reach for syntax tricks. Ask what each loop is contributing.
Typical outcomes:
- One loop is searching for a value that could be found with a
MaporSet. - One loop recomputes the same partial result many times and should be cached.
- One loop exists only because the data is unsorted.
- The problem can be reformulated into pair processing instead of triple processing.
For example, if an algorithm repeatedly counts frequencies inside an inner loop, precompute the counts once:
This example is not a triple-loop rewrite, but it shows the same principle: do shared work once, then reuse it.
When O(n^2) Is Still the Best You Can Do
Not every cubic algorithm can be reduced further without changing the problem. Some tasks inherently depend on examining all pairs, and O(n^2) is already the right target. That is still a major improvement. If n is 10,000, reducing from cubic to quadratic changes the problem from effectively unusable to potentially manageable.
The real goal is not to chase a specific Big O label. The goal is to match the algorithm to the information you actually need at each step.
Common Pitfalls
- Replacing loops without understanding the repeated work. If you do not know what the inner loop is doing, the rewrite will be accidental rather than correct.
- Claiming
SetorMapmakes everything constant time in all cases. Hash lookups are average-case constant time, not a guarantee of zero cost. - Forgetting duplicate rules when switching to sorting or hash-based logic. Test cases with repeated values often expose bugs.
- Mutating the original input by sorting it in place. Use a copy such as
[...values]if callers expect the original order. - Optimizing asymptotic complexity while ignoring correctness. Verify the new algorithm against small examples before benchmarking.
Summary
- Cutting
O(n^3)toO(n^2)usually means removing one repeated search. - '
SetandMaphelp when the innermost loop is doing membership or frequency work.' - Sorting plus two pointers is a strong option for ordered numeric problems.
- Focus on what each loop contributes, not on syntax alone.
- Preserve correctness first, then measure the performance improvement.

