Determining the complexities given codes
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
Determining time and space complexity is mostly about identifying how often each part of the code runs as the input grows. The trick is not memorizing labels like O(n) or O(log n), but learning how loops, nesting, recursion, and data structures combine into a growth pattern.
Start With What Depends on Input Size
The first question is always: which variables grow with input size n?
Operations that do not depend on n are constant-time from an asymptotic point of view, even if they involve several ordinary statements.
For example:
This is O(1) time because the amount of work does not grow with the input size.
Analyze Loops by Counting Iterations
A single loop that runs n times is usually O(n) if the loop body is constant work.
If arr has length n, this is O(n) time.
Nested loops multiply when their iteration counts depend on the same input size.
Here the outer loop runs n times and the inner loop also runs n times for each outer iteration, so the total is O(n^2).
Sequential Blocks Usually Add, Then Simplify
If code has one O(n) block followed by one O(n^2) block, the total is:
- '
O(n + n^2)' - which simplifies to
O(n^2)
The dominant growth term wins.
That is why asymptotic analysis usually drops constants and lower-order terms. They matter for raw performance tuning, but not for big-O classification.
Watch for Shrinking Inputs
Not every loop is linear. If the input size shrinks by a constant factor each step, the complexity is often logarithmic.
This is O(log n) because each iteration cuts n roughly in half.
The same logic appears in binary search, heap operations, and balanced tree traversals.
Recursion Follows the Same Logic
For recursive code, count:
- how much work happens per call
- how many recursive calls are made
- how fast the problem size shrinks
Example:
This is O(log n) because there is one recursive call per level and the input halves each time.
By contrast, a recursion that branches into two recursive calls on half-size subproblems often lands near O(n) or O(n log n) depending on the per-level work.
Space Complexity Matters Too
Time complexity is only half the story. Ask whether the code allocates additional memory that grows with n.
Examples:
- a few scalar variables:
O(1)extra space - a copied array of size
n:O(n)extra space - recursion depth of
log n:O(log n)call-stack space
For instance:
This is O(n) extra space because result grows with the input.
A Good Step-by-Step Method
When you are given unfamiliar code, this sequence works well:
- identify the input size variable
- count loop or recursion depth
- determine whether nested parts multiply or sequential parts add
- identify the dominant term
- repeat the process for memory usage
That keeps the analysis mechanical instead of guess-based.
Common Pitfalls
The biggest mistake is counting source lines instead of counting how often they execute. Complexity is about growth with input, not about visual code length.
Another mistake is treating every nested loop as O(n^2). If the inner loop depends on a different variable or a shrinking bound, the result may be different.
People also forget to analyze space separately. An algorithm can be fast in time and still expensive in memory.
Finally, avoid mixing worst-case, average-case, and best-case analysis without saying which one you mean. Big-O is usually used for upper bounds, but the context still matters.
Summary
- Complexity comes from how execution count grows with input size.
- Constant work repeated
ntimes is usuallyO(n). - Nested dependent loops often multiply; sequential blocks usually add, then simplify.
- Shrinking-by-half patterns often lead to
O(log n). - Analyze both time and extra space, not just runtime.

