Skip to content
DSA complexity 7 min read

Time vs Space Complexity: The Trade-Off

Time complexity measures how the number of operations grows with input size; space complexity measures how the extra memory grows. Both are written in Big-O, and the central skill of DSA is choosing the right balance between them — because you can very often spend more memory to save time, or the reverse.

This page explains what each measures, then shows the time-space trade-off with a concrete example you can run in any language.

What time complexity measures

Time complexity counts the operations an algorithm performs as a function of input size n. We ignore constants and keep the dominant term: a loop over the array is O(n), a loop inside a loop is O(n²). It’s a model of growth, not a stopwatch reading — it predicts how the runtime scales, not its exact milliseconds.

What space complexity measures

Space complexity counts the extra memory an algorithm uses beyond its input — usually called auxiliary space. A loop that uses a couple of counters is O(1) space. Building a new array of size n is O(n) space. The recursion call stack counts too: a recursion n levels deep is O(n) space even if you allocate nothing yourself.

#include <vector>
using namespace std;

// O(n) time, O(1) auxiliary space: just a running total.
long long sum(const vector<int>& a) {
    long long total = 0;
    for (int x : a) total += x;
    return total;
}

The trade-off in action

Consider: does an array contain any duplicate value? The memory-free approach compares every pair — O(n²) time, O(1) space:

#include <vector>
using namespace std;

bool hasDuplicate(const vector<int>& a) {
    for (size_t i = 0; i < a.size(); i++)
        for (size_t j = i + 1; j < a.size(); j++)
            if (a[i] == a[j]) return true;
    return false;
}

Now spend memory to save time: a hash set remembers what we’ve seen, turning each lookup into O(1). The result is O(n) time but O(n) space:

#include <vector>
#include <unordered_set>
using namespace std;

bool hasDuplicate(const vector<int>& a) {
    unordered_set<int> seen;
    for (int x : a) {
        if (seen.count(x)) return true;
        seen.insert(x);
    }
    return false;
}

Same problem, two valid answers. We paid O(n) memory to cut the time from O(n²) to O(n) — usually a great deal when n is large.

How to choose

  • Optimize time when inputs are large and memory is plentiful — the common interview default. Most “make it faster” answers add a hash map or precomputed table.
  • Optimize space when memory is the bottleneck: huge datasets, embedded systems, or when an in-place algorithm is required. Reversing an array in place is O(1) space; copying to a new one is O(n).
  • Both matter. State both complexities for every solution. Interviewers expect “O(n) time, O(n) space” — not just the time half.

For beginners: “Can I trade memory for speed?” is the question to ask whenever you see nested loops. A set or map that records what you’ve already computed is the most common way to delete an inner loop.

Going deeper: Recursive solutions hide space in the call stack. A naive recursion may be O(2ⁿ) time and O(n) stack space; adding memoization trades O(n) memory to collapse the time. Always account for stack depth — see analyzing recursive code.

Next steps

Now learn to derive these numbers yourself: analyzing loops for iterative code and analyzing recursion for recursive code. Then keep the complexity cheat sheet handy and test yourself with the complexity exercises.

Last updated June 25, 2026
Was this helpful?