Recursion on Arrays and Strings in DSA Rust - Time & Space Complexity
When we use recursion on arrays or strings, we want to know how the time needed grows as the input gets bigger.
We ask: How many steps does the computer take as the array or string length increases?
Analyze the time complexity of the following code snippet.
fn sum_array(arr: &[i32], index: usize) -> i32 {
if index == arr.len() {
return 0;
}
arr[index] + sum_array(arr, index + 1)
}
This function adds all elements of an array by calling itself with the next index until it reaches the end.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: Recursive call to process one element at a time.
- How many times: Once for each element in the array, so n times for array length n.
Each recursive call handles one element, so the total steps grow directly with the array size.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | 10 recursive calls |
| 100 | 100 recursive calls |
| 1000 | 1000 recursive calls |
Pattern observation: The number of steps grows in a straight line as input size increases.
Time Complexity: O(n)
This means the time to complete grows directly with the size of the array or string.
[X] Wrong: "Recursion always means exponential time because it calls itself multiple times."
[OK] Correct: Here, each call only makes one recursive call, so the total steps grow linearly, not exponentially.
Understanding how recursion steps grow helps you explain your code clearly and choose the best approach during interviews.
"What if the function made two recursive calls each time instead of one? How would the time complexity change?"
Practice
Solution
Step 1: Understand recursion on arrays
Recursion on arrays means the function calls itself with a smaller portion of the array each time.Step 2: Identify the base case
The recursion stops when the array is empty or has one element (base case).Final Answer:
Calling the same function with a smaller part of the array until a base case is reached -> Option BQuick Check:
Recursion = smaller input calls [OK]
- Confusing recursion with loops
- Not defining a base case
- Trying to process the whole array at once
Solution
Step 1: Understand Rust slice parameter
A recursive function processing an array usually takes a slice reference like &[i32] to avoid ownership issues.Step 2: Check return type
Returning i32 is common if the function calculates a value like sum or count.Final Answer:
fn recurse(arr: &[i32]) -> i32 -> Option DQuick Check:
Slice input with reference = fn recurse(arr: &[i32]) -> i32 [OK]
- Using Vec<i32> causes ownership moves
- Returning a reference to local data
- Using mutable slice when not needed
arr = &[1, 2, 3]?
fn sum(arr: &[i32]) -> i32 {
if arr.is_empty() {
0
} else {
arr[0] + sum(&arr[1..])
}
}
println!("{}", sum(arr));Solution
Step 1: Trace recursive calls
sum(&[1,2,3]) = 1 + sum(&[2,3]) sum(&[2,3]) = 2 + sum(&[3]) sum(&[3]) = 3 + sum(&[]) sum(&[]) = 0 (base case)Step 2: Calculate total sum
sum = 1 + 2 + 3 + 0 = 6Final Answer:
6 -> Option CQuick Check:
1+2+3 = 6 [OK]
- Forgetting base case returns 0
- Adding wrong slice range
- Confusing output with length
fn count_occurrences(arr: &[i32], target: i32) -> i32 {
if arr.len() == 0 {
0
} else if arr[0] == target {
1 + count_occurrences(&arr[1..], target)
} else {
count_occurrences(arr, target)
}
}Solution
Step 1: Analyze recursive calls
In the else branch, the function calls itself with the same array slice, causing infinite recursion.Step 2: Identify fix
The else branch should call with &arr[1..] to move forward and avoid infinite loop.Final Answer:
Infinite recursion because the else branch calls with the same array slice -> Option AQuick Check:
Same slice call causes infinite recursion [OK]
- Not reducing slice in else branch
- Confusing base case condition
- Returning wrong type
s: &str, which recursive Rust function correctly reverses the string?Solution
Step 1: Understand string slicing and recursion
To reverse, recursively reverse substring from index 1 onward, then append first character.Step 2: Check base case and concatenation order
Base case is empty string returning empty String. Concatenate reversed tail + first char.Step 3: Compare options
fn reverse(s: &str) -> String { if s.len() == 0 { String::new() } else { format!("{}{}", reverse(&s[1..]), &s[0..1]) } } matches correct base case and concatenation order.Final Answer:
fn reverse(s: &str) -> String { if s.len() == 0 { String::new() } else { format!("{}{}", reverse(&s[1..]), &s[0..1]) } } -> Option AQuick Check:
Reverse tail + first char = correct recursion [OK]
- Appending first char before recursive call
- Wrong base case condition
- Using incorrect string slices
Ready to prove it?
Pass the test and this pattern moves unknown
