Hello! Welcome to your fifth lesson.
In our last session, we delved into time complexity and Big O notation, establishing a framework for analyzing how an algorithm's runtime scales with input size. You learned to analyze both iterative and recursive code, a foundational skill for your interview preparation.
However, time is only one dimension of efficiency. An algorithm also consumes memory. A solution that is blazingly fast but requires an enormous amount of RAM might be impractical. This lesson focuses on the second pillar of algorithm analysis: Space Complexity.
By the end of this lesson, you will be able to analyze an algorithm's memory usage, distinguishing between the space taken by the input and the extra space it requires. Crucially, we will also explore how recursion impacts memory through the call stack. Mastering both time and space analysis will give you the complete vocabulary to discuss an algorithm's performance in any technical interview.
1. What is Space Complexity?
Just as time complexity measures how runtime grows, space complexity measures how the amount of memory an algorithm needs grows as the input size (n) increases. Like time complexity, we use Big O notation to describe this growth.
An algorithm's total memory usage can be broken down into two parts:
- Input Space: The memory required to store the input data itself (e.g., an array of size
n). - Auxiliary Space: The extra or temporary memory that an algorithm uses during its execution. This includes variables, data structures created to aid the computation, and the space used by function calls.
Space Complexity = Input Space + Auxiliary Space
In many interview contexts and competitive programming, when someone asks for "space complexity," they are often most interested in the auxiliary space complexity. Why? Because the input space is a given requirement of the problem, while the auxiliary space is a direct consequence of your algorithmic choices. A solution with O(1) auxiliary space is generally better than one with O(n) auxiliary space, even if both have the same total space complexity of O(n).
Time and Space Complexity - Strivers A2Z DSA Course
Let's start with a clear definition of space complexity and the distinction between auxiliary and input space from the 'Time and Space Complexity' video by take U forward.
Watch the segment from 25:54 to 27:26. Focus on the core definition: Space Complexity = Auxiliary Space + Input Space.
2. Analyzing Space in Iterative Algorithms
Let's analyze some familiar iterative code to see how this works.
Example 1: Constant Auxiliary Space, O(1)
Consider a function that sums the elements of an array.
// arr has n elements
long sumArray(int[] arr) {
long sum = 0; // One variable (e.g., 8 bytes)
// The loop counter 'i' is also one variable (e.g., 4 bytes)
for (int i = 0; i < arr.length; i++) {
sum += arr[i];
}
return sum;
}
- Input Space: The array
arrtakesO(n)space. - Auxiliary Space: We use two variables,
sumandi. The memory they occupy does not change, regardless of whether the array has 10 elements or 10 million. This is constant space, which we denote asO(1). - Total Space Complexity:
O(n)(input) +O(1)(auxiliary) =O(n).
Example 2: Linear Auxiliary Space, O(n)
Now, let's look at a function that creates a new data structure. Imagine we want to count the frequency of each number in an array. A HashMap is a good tool for this.
The article 'Time and Space Complexity' on GeeksforGeeks provides a perfect Java example for this scenario. It also clearly defines auxiliary space.
First, look at the Java code in the section titled 'Example : Frequency of array elements'. Notice the creation of a HashMap. Then, read the paragraph that starts with 'There is also auxiliary space...'. This will crystallize the difference between the total space and the extra space used by the freq map.
Let's analyze the code from the article:
static void countFreq(int arr[], int n) {
// Auxiliary space starts here
HashMap<Integer, Integer> freq = new HashMap<>();
for (int i = 0; i < n; i++) {
// The map grows as we add elements
freq.put(arr[i], freq.getOrDefault(arr[i], 0) + 1);
}
// ...
}
- Input Space: The array
arrtakesO(n)space. - Auxiliary Space: We create a
HashMapcalledfreq. In the worst-case scenario (where allnelements inarrare unique), the map will storenkey-value pairs. Therefore, the space required by the map grows linearly with the input size. The auxiliary space isO(n). - Total Space Complexity:
O(n)(input) +O(n)(auxiliary) =O(n).
Notice that while both examples have O(n) total space complexity, their auxiliary space requirements are vastly different. This is the kind of trade-off you'll constantly evaluate.
Test your understanding!
What is the auxiliary space complexity of the following Java function, which creates a new array containing the squares of the input array's elements?
int[] getSquares(int[] nums) {
int n = nums.length;
int[] squares = new int[n]; // A new array is created
for (int i = 0; i < n; i++) {
squares[i] = nums[i] * nums[i];
}
return squares;
}
Show answer
The function creates a new array, squares, which has the same size as the input array, n. The space required for this new array scales linearly with the input size. Therefore, the auxiliary space complexity is O(n). The variables n and i contribute O(1) space, which is non-dominant and can be ignored.
An Important Rule for Interviews
It can be tempting to save space by modifying the input data directly. For example, in the getSquares problem above, you could overwrite nums[i] with its square. While this reduces auxiliary space to O(1), it's generally considered bad practice unless you're explicitly told it's acceptable.
Time and Space Complexity - Strivers A2Z DSA Course
The 'take U forward' video gives a crucial piece of advice about modifying input data that is highly relevant for your interview preparation goals.
Watch the segment from 29:36 to 31:56. This explains the software engineering principle behind not tampering with input data and how to communicate this trade-off to an interviewer.
3. Space Complexity of Recursive Algorithms: The Call Stack
Analyzing space in recursive algorithms requires understanding the call stack. When a function is called, a block of memory called a stack frame is allocated to store its local variables, parameters, and return address. When functionA calls functionB, a new stack frame for functionB is pushed on top of functionA's frame.
The space complexity of a recursive algorithm is determined by the maximum depth of the call stack at any point during execution.
Example 1: Linear Space Recursion, O(n)
Let's revisit the factorial function from the last lesson.
int factorial(int n) {
if (n <= 1) {
return 1;
}
return n * factorial(n - 1);
}
When you call factorial(4), the call stack looks like this before the base case is hit:
factorial(4)is called.factorial(4)callsfactorial(3). Stack:[factorial(4), factorial(3)]factorial(3)callsfactorial(2). Stack:[factorial(4), factorial(3), factorial(2)]factorial(2)callsfactorial(1). Stack:[factorial(4), factorial(3), factorial(2), factorial(1)]
The stack reaches its maximum depth of n (or n+1 depending on how you count). Since each stack frame takes O(1) space, the total space complexity due to recursion is O(n).
Big O Notation Series #8: Space Complexity and Big O Gotchas
The video 'Space Complexity and Big O Gotchas' from Kantan Coding provides an excellent visual explanation of how recursive calls build up on the call stack, leading to linear space complexity.
Watch the entire segment from 00:01 to 03:54. The countdown function is analogous to our factorial example. Pay close attention to the statement that 'each one of these calls exists on the call stack simultaneously.'
Example 2: Time vs. Space in Branching Recursion
In the last lesson, we saw that the fibonacci function has a time complexity of O(2^n) because of its branching recursion tree. What about its space complexity?
int fibonacci(int n) {
if (n <= 1) {
return n;
}
return fibonacci(n - 1) + fibonacci(n - 2);
}

Although the total number of calls is exponential, they don't all exist on the call stack at the same time. The program explores one branch of the recursion tree down to its base case, and only then does it backtrack to explore another. The maximum number of stack frames on the call stack at any single moment is determined by the height of the recursion tree, which is n.
Therefore, the space complexity of this fibonacci function is O(n), not O(2^n). This is a critical distinction and a common interview question.
Example 3: Logarithmic Space Recursion, O(log n)
What happens if a recursive call halves the problem size, like in a recursive binary search?
// A simplified recursive function that divides its input
void function(int n) {
if (n <= 1) {
return; // Base case
}
function(n / 2); // Recurse on half the problem
}
The sequence of calls would be n -> n/2 -> n/4 -> ... -> 1. The number of times you can divide n by 2 until you reach 1 is logâ‚‚(n). The maximum depth of the call stack is log n. Therefore, the space complexity is O(log n).
Visualizing Space Complexity
Just like with time complexity, we can visualize how different space complexities scale. Efficient algorithms aim for lower space requirements, especially as the input size n grows large.

Test your understanding!
Consider this recursive function that sums the elements of an array. What is its auxiliary space complexity?
int recursiveSum(int[] arr, int index) {
// Base case
if (index == arr.length) {
return 0;
}
// Recursive step
return arr[index] + recursiveSum(arr, index + 1);
}
Hint: Think about the maximum depth of the call stack.
Show answer
The function starts at index = 0 and calls itself with index + 1 until index reaches arr.length. If the array has n elements, the recursion will go n levels deep before hitting the base case (recursiveSum(arr, 0) calls recursiveSum(arr, 1), and so on, up to recursiveSum(arr, n)).
The maximum depth of the call stack is proportional to n. Therefore, the auxiliary space complexity is O(n).
Conclusion
You have now completed your tour of the fundamentals of algorithm analysis. You can analyze an algorithm's efficiency in terms of both time and space, for both iterative and recursive solutions. This two-dimensional perspective is essential for making informed decisions as a software engineer and for succeeding in technical interviews.
Key Takeaways:
- Space Complexity measures an algorithm's memory usage as a function of input size
n. - Auxiliary Space is the extra memory used by an algorithm, distinct from the input space. This is often the primary focus of analysis.
- Iterative Algorithms: Space is determined by the variables and data structures you declare. This can be
O(1),O(n), or other complexities. - Recursive Algorithms: Space is primarily determined by the maximum depth of the call stack. Linear recursion is typically
O(n), while "divide and conquer" recursion is oftenO(log n). - Time vs. Space: A branching recursion like Fibonacci can have exponential time (
O(2^n)) but linear space (O(n)).
Preview of the next lesson:
Now that you have the tools to analyze algorithms, we will begin learning specific techniques to design efficient ones. In the next lesson, we will cover the Two-Pointer Technique, a simple yet powerful pattern for solving a wide range of problems on arrays and strings in optimal time and space.