Time vs Space Complexity — Optimize
10+ exercises. Show before/after in all 3 languages with complexity comparison and benchmarks.
Exercise 1: Duplicate Detection — O(n²) to O(n)
Before (Slow)
Go
// O(n²) time, O(1) space
func hasDuplicate(arr []int) bool {
for i := 0; i < len(arr); i++ {
for j := i + 1; j < len(arr); j++ {
if arr[i] == arr[j] {
return true
}
}
}
return false
}
Java
// O(n²) time, O(1) space
public static boolean hasDuplicate(int[] arr) {
for (int i = 0; i < arr.length; i++)
for (int j = i + 1; j < arr.length; j++)
if (arr[i] == arr[j]) return true;
return false;
}
Python
# O(n²) time, O(1) space
def has_duplicate(arr):
for i in range(len(arr)):
for j in range(i + 1, len(arr)):
if arr[i] == arr[j]:
return True
return False
After (Optimized)
Go
// O(n) time, O(n) space — hash set
func hasDuplicate(arr []int) bool {
seen := make(map[int]bool, len(arr))
for _, v := range arr {
if seen[v] {
return true
}
seen[v] = true
}
return false
}
Java
// O(n) time, O(n) space
public static boolean hasDuplicate(int[] arr) {
HashSet<Integer> seen = new HashSet<>(arr.length);
for (int v : arr) {
if (!seen.add(v)) return true;
}
return false;
}
Python
# O(n) time, O(n) space
def has_duplicate(arr):
return len(arr) != len(set(arr))
Complexity
| Time | Space |
| Before | O(n²) | O(1) |
| After | O(n) | O(n) |
Exercise 2: Two Sum — O(n²) to O(n)
Before (Slow)
Go
// O(n²) time, O(1) space
func twoSum(nums []int, target int) (int, int) {
for i := 0; i < len(nums); i++ {
for j := i + 1; j < len(nums); j++ {
if nums[i]+nums[j] == target {
return i, j
}
}
}
return -1, -1
}
Java
// O(n²) time, O(1) space
public static int[] twoSum(int[] nums, int target) {
for (int i = 0; i < nums.length; i++)
for (int j = i + 1; j < nums.length; j++)
if (nums[i] + nums[j] == target)
return new int[]{i, j};
return new int[]{-1, -1};
}
Python
# O(n²) time, O(1) space
def two_sum(nums, target):
for i in range(len(nums)):
for j in range(i + 1, len(nums)):
if nums[i] + nums[j] == target:
return [i, j]
return [-1, -1]
After (Optimized)
Go
// O(n) time, O(n) space
func twoSum(nums []int, target int) (int, int) {
seen := make(map[int]int)
for i, v := range nums {
if j, ok := seen[target-v]; ok {
return j, i
}
seen[v] = i
}
return -1, -1
}
Java
// O(n) time, O(n) space
public static int[] twoSum(int[] nums, int target) {
HashMap<Integer, Integer> seen = new HashMap<>();
for (int i = 0; i < nums.length; i++) {
int complement = target - nums[i];
if (seen.containsKey(complement))
return new int[]{seen.get(complement), i};
seen.put(nums[i], i);
}
return new int[]{-1, -1};
}
Python
# O(n) time, O(n) space
def two_sum(nums, target):
seen = {}
for i, v in enumerate(nums):
if target - v in seen:
return [seen[target - v], i]
seen[v] = i
return [-1, -1]
Complexity
| Time | Space |
| Before | O(n²) | O(1) |
| After | O(n) | O(n) |
Exercise 3: Fibonacci — O(2ⁿ) to O(n) to O(1) Space
Before (Slow)
Go
// O(2^n) time, O(n) space (call stack)
func fib(n int) int {
if n <= 1 { return n }
return fib(n-1) + fib(n-2)
}
Java
// O(2^n) time, O(n) space
public static int fib(int n) {
if (n <= 1) return n;
return fib(n - 1) + fib(n - 2);
}
Python
# O(2^n) time, O(n) space
def fib(n):
if n <= 1: return n
return fib(n - 1) + fib(n - 2)
After (Optimized)
Go
// O(n) time, O(1) space
func fib(n int) int {
if n <= 1 { return n }
a, b := 0, 1
for i := 2; i <= n; i++ {
a, b = b, a+b
}
return b
}
Java
// O(n) time, O(1) space
public static int fib(int n) {
if (n <= 1) return n;
int a = 0, b = 1;
for (int i = 2; i <= n; i++) {
int t = a + b; a = b; b = t;
}
return b;
}
Python
# O(n) time, O(1) space
def fib(n):
if n <= 1: return n
a, b = 0, 1
for _ in range(2, n + 1):
a, b = b, a + b
return b
Complexity
| Time | Space |
| Before | O(2ⁿ) | O(n) |
| After | O(n) | O(1) |
Exercise 4: Range Sum Query — O(n) per Query to O(1)
Before (Slow)
Go
// O(n) per query, O(1) space
func rangeSum(arr []int, left, right int) int {
sum := 0
for i := left; i <= right; i++ {
sum += arr[i]
}
return sum
}
Java
// O(n) per query
public static int rangeSum(int[] arr, int left, int right) {
int sum = 0;
for (int i = left; i <= right; i++) sum += arr[i];
return sum;
}
Python
# O(n) per query
def range_sum(arr, left, right):
return sum(arr[left:right + 1])
After (Optimized)
Go
// O(n) build, O(1) per query, O(n) space
func buildPrefix(arr []int) []int {
prefix := make([]int, len(arr)+1)
for i, v := range arr {
prefix[i+1] = prefix[i] + v
}
return prefix
}
func rangeSum(prefix []int, left, right int) int {
return prefix[right+1] - prefix[left]
}
Java
// O(n) build, O(1) per query
public static int[] buildPrefix(int[] arr) {
int[] prefix = new int[arr.length + 1];
for (int i = 0; i < arr.length; i++)
prefix[i + 1] = prefix[i] + arr[i];
return prefix;
}
public static int rangeSum(int[] prefix, int left, int right) {
return prefix[right + 1] - prefix[left];
}
Python
# O(n) build, O(1) per query
def build_prefix(arr):
prefix = [0] * (len(arr) + 1)
for i, v in enumerate(arr):
prefix[i + 1] = prefix[i] + v
return prefix
def range_sum(prefix, left, right):
return prefix[right + 1] - prefix[left]
Complexity
| Time (per query) | Space |
| Before | O(n) | O(1) |
| After | O(1) | O(n) precomputed |
Exercise 5: Queue with List Pop(0) — O(n) to O(1)
Before (Slow)
Go
// O(n) per dequeue (slice copy)
type SlowQueue struct {
data []int
}
func (q *SlowQueue) Enqueue(val int) { q.data = append(q.data, val) }
func (q *SlowQueue) Dequeue() int {
val := q.data[0]
q.data = q.data[1:] // O(n) — underlying array not reclaimed
return val
}
Java
// O(n) per dequeue
ArrayList<Integer> queue = new ArrayList<>();
queue.add(val); // O(1) amortized
queue.remove(0); // O(n) — shifts all elements
Python
# O(n) per dequeue
queue = []
queue.append(val) # O(1) amortized
queue.pop(0) # O(n) — shifts all elements
After (Optimized)
Go
// O(1) per dequeue — circular buffer
type FastQueue struct {
data []int
head, tail int
size, cap int
}
func NewFastQueue(cap int) *FastQueue {
return &FastQueue{data: make([]int, cap), cap: cap}
}
func (q *FastQueue) Enqueue(val int) {
q.data[q.tail] = val
q.tail = (q.tail + 1) % q.cap
q.size++
}
func (q *FastQueue) Dequeue() int {
val := q.data[q.head]
q.head = (q.head + 1) % q.cap
q.size--
return val
}
Java
// O(1) per dequeue
import java.util.ArrayDeque;
ArrayDeque<Integer> queue = new ArrayDeque<>();
queue.add(val); // O(1)
queue.poll(); // O(1)
Python
# O(1) per dequeue
from collections import deque
queue = deque()
queue.append(val) # O(1)
queue.popleft() # O(1)
Complexity
| Time (dequeue) | Space |
| Before | O(n) | O(n) |
| After | O(1) | O(n) |
Exercise 6: String Building — O(n²) to O(n)
Before (Slow)
Go
// O(n²) time
func repeat(ch byte, n int) string {
s := ""
for i := 0; i < n; i++ {
s += string(ch) // new string each iteration
}
return s
}
Java
// O(n²) time
public static String repeat(char ch, int n) {
String s = "";
for (int i = 0; i < n; i++) s += ch;
return s;
}
Python
# O(n²) worst case
def repeat(ch, n):
s = ""
for _ in range(n):
s += ch
return s
After (Optimized)
Go
// O(n) time
import "strings"
func repeat(ch byte, n int) string {
var sb strings.Builder
sb.Grow(n)
for i := 0; i < n; i++ {
sb.WriteByte(ch)
}
return sb.String()
}
Java
// O(n) time
public static String repeat(char ch, int n) {
StringBuilder sb = new StringBuilder(n);
for (int i = 0; i < n; i++) sb.append(ch);
return sb.toString();
}
Python
# O(n) time
def repeat(ch, n):
return ch * n
Complexity
| Time | Space |
| Before | O(n²) | O(n²) total allocations |
| After | O(n) | O(n) |
Exercise 7: Finding Max/Min — O(n log n) to O(n)
Before (Slow)
Go
import "sort"
// O(n log n) time, O(n) space
func findMinMax(arr []int) (int, int) {
sorted := make([]int, len(arr))
copy(sorted, arr)
sort.Ints(sorted)
return sorted[0], sorted[len(sorted)-1]
}
Java
// O(n log n) time
public static int[] findMinMax(int[] arr) {
int[] copy = arr.clone();
Arrays.sort(copy);
return new int[]{copy[0], copy[copy.length - 1]};
}
Python
# O(n log n) time
def find_min_max(arr):
s = sorted(arr)
return s[0], s[-1]
After (Optimized)
Go
// O(n) time, O(1) space
func findMinMax(arr []int) (int, int) {
mn, mx := arr[0], arr[0]
for _, v := range arr[1:] {
if v < mn { mn = v }
if v > mx { mx = v }
}
return mn, mx
}
Java
// O(n) time, O(1) space
public static int[] findMinMax(int[] arr) {
int min = arr[0], max = arr[0];
for (int v : arr) {
if (v < min) min = v;
if (v > max) max = v;
}
return new int[]{min, max};
}
Python
# O(n) time, O(1) space
def find_min_max(arr):
return min(arr), max(arr)
Complexity
| Time | Space |
| Before | O(n log n) | O(n) |
| After | O(n) | O(1) |
Exercise 8: Frequency Count — O(n²) to O(n)
Before (Slow)
Go
// O(n²) time, O(n) space
func frequency(arr []int) map[int]int {
freq := map[int]int{}
for _, v := range arr {
count := 0
for _, w := range arr {
if w == v { count++ }
}
freq[v] = count
}
return freq
}
Java
// O(n²) time
public static Map<Integer, Integer> frequency(int[] arr) {
Map<Integer, Integer> freq = new HashMap<>();
for (int v : arr) {
int count = 0;
for (int w : arr) if (w == v) count++;
freq.put(v, count);
}
return freq;
}
Python
# O(n²) time
def frequency(arr):
freq = {}
for v in arr:
count = sum(1 for w in arr if w == v)
freq[v] = count
return freq
After (Optimized)
Go
// O(n) time, O(n) space
func frequency(arr []int) map[int]int {
freq := make(map[int]int, len(arr))
for _, v := range arr {
freq[v]++
}
return freq
}
Java
// O(n) time, O(n) space
public static Map<Integer, Integer> frequency(int[] arr) {
Map<Integer, Integer> freq = new HashMap<>();
for (int v : arr) freq.merge(v, 1, Integer::sum);
return freq;
}
Python
# O(n) time, O(n) space
from collections import Counter
def frequency(arr):
return Counter(arr)
Complexity
| Time | Space |
| Before | O(n²) | O(n) |
| After | O(n) | O(n) |
Exercise 9: Checking Sorted — O(n log n) to O(n)
Before (Slow)
Go
import "sort"
// O(n log n) time, O(n) space
func isSorted(arr []int) bool {
sorted := make([]int, len(arr))
copy(sorted, arr)
sort.Ints(sorted)
for i := range arr {
if arr[i] != sorted[i] { return false }
}
return true
}
Java
// O(n log n) time, O(n) space
public static boolean isSorted(int[] arr) {
int[] copy = arr.clone();
Arrays.sort(copy);
return Arrays.equals(arr, copy);
}
Python
# O(n log n) time, O(n) space
def is_sorted(arr):
return arr == sorted(arr)
After (Optimized)
Go
// O(n) time, O(1) space
func isSorted(arr []int) bool {
for i := 1; i < len(arr); i++ {
if arr[i] < arr[i-1] { return false }
}
return true
}
Java
// O(n) time, O(1) space
public static boolean isSorted(int[] arr) {
for (int i = 1; i < arr.length; i++)
if (arr[i] < arr[i - 1]) return false;
return true;
}
Python
# O(n) time, O(1) space
def is_sorted(arr):
return all(arr[i] <= arr[i + 1] for i in range(len(arr) - 1))
Complexity
| Time | Space |
| Before | O(n log n) | O(n) |
| After | O(n) | O(1) |
Exercise 10: Recursive to Iterative — O(n) Stack to O(1)
Before (Slow Space)
Go
// O(n) time, O(n) space (stack)
func factorial(n int) int {
if n <= 1 { return 1 }
return n * factorial(n-1)
}
Java
// O(n) time, O(n) space
public static long factorial(int n) {
if (n <= 1) return 1;
return n * factorial(n - 1);
}
Python
# O(n) time, O(n) space
def factorial(n):
if n <= 1: return 1
return n * factorial(n - 1)
After (Optimized)
Go
// O(n) time, O(1) space
func factorial(n int) int {
result := 1
for i := 2; i <= n; i++ {
result *= i
}
return result
}
Java
// O(n) time, O(1) space
public static long factorial(int n) {
long result = 1;
for (int i = 2; i <= n; i++) result *= i;
return result;
}
Python
# O(n) time, O(1) space
def factorial(n):
result = 1
for i in range(2, n + 1):
result *= i
return result
Complexity
| Time | Space |
| Before | O(n) | O(n) stack |
| After | O(n) | O(1) |
Optimization Summary
| Exercise | Before | After | Strategy |
| 1 | O(n²) time, O(1) space | O(n) time, O(n) space | Hash set |
| 2 | O(n²) time, O(1) space | O(n) time, O(n) space | Hash map |
| 3 | O(2ⁿ) time, O(n) space | O(n) time, O(1) space | Iterative with two variables |
| 4 | O(n) per query | O(1) per query, O(n) build | Prefix sum precomputation |
| 5 | O(n) per dequeue | O(1) per dequeue | Deque / circular buffer |
| 6 | O(n²) time | O(n) time | StringBuilder / join |
| 7 | O(n log n) time | O(n) time | Single pass |
| 8 | O(n²) time | O(n) time | Hash map counting |
| 9 | O(n log n) time, O(n) space | O(n) time, O(1) space | Adjacent comparison |
| 10 | O(n) space (stack) | O(1) space | Recursion → iteration |