Time vs Space Complexity — Specification
Official Documentation Reference
Source: Language standard libraries and algorithm analysis references
Table of Contents
- Docs Reference
- API / Configuration Reference
- Core Concepts & Rules
- Schema / Parameters Reference
- Behavioral Specification
- Edge Cases from Official Docs
- Version & Compatibility Matrix
- Official Examples
- Compliance & Best Practices Checklist
- Related Documentation
1. Docs Reference
2. API / Configuration Reference
Go: testing.B (Benchmark)
| Method | Description |
b.N | Number of iterations the benchmark should run |
b.ResetTimer() | Reset the timer after setup |
b.ReportAllocs() | Report memory allocations |
b.StartTimer() / b.StopTimer() | Manual timer control |
func BenchmarkMyAlgo(b *testing.B) {
data := setup()
b.ResetTimer()
for i := 0; i < b.N; i++ {
myAlgo(data)
}
}
// Run: go test -bench=. -benchmem
Java: JMH Annotations
| Annotation | Description |
@Benchmark | Marks a method as a benchmark |
@BenchmarkMode(Mode.AverageTime) | Measures average execution time |
@OutputTimeUnit(TimeUnit.NANOSECONDS) | Output time unit |
@State(Scope.Thread) | State object lifecycle |
@Warmup(iterations = 5) | Warmup iterations before measuring |
@Benchmark
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
public void benchmarkMyAlgo(MyState state) {
myAlgo(state.data);
}
Python: timeit Module
| Function | Description |
timeit.timeit(stmt, number=1000000) | Time execution of statement |
timeit.repeat(stmt, repeat=5, number=1000000) | Repeat timing for reliability |
timeit.default_timer() | High-resolution timer |
import timeit
t = timeit.timeit(lambda: my_algo(data), number=1000)
print(f"{t/1000*1000:.3f} ms per call")
Python: sys.getsizeof()
| Function | Description |
sys.getsizeof(obj) | Returns size of object in bytes (shallow) |
tracemalloc.start() | Start tracing memory allocations |
tracemalloc.get_traced_memory() | Get current and peak memory usage |
import sys
import tracemalloc
tracemalloc.start()
data = list(range(100000))
current, peak = tracemalloc.get_traced_memory()
print(f"Current: {current/1024:.1f} KB, Peak: {peak/1024:.1f} KB")
tracemalloc.stop()
3. Core Concepts & Rules
Rule 1: Big-O Describes Growth Rate, Not Actual Time
CLRS Chapter 3: "Asymptotic notation characterizes functions according to how fast they grow."
Big-O notation drops constants and lower-order terms. O(2n) = O(n). O(n² + n) = O(n²). This means Big-O is useful for comparing algorithms at large n, but for small n, actual measured time may differ from Big-O predictions.
# O(n) with large constant can be slower than O(n²) for small n
def algo_a(arr): # O(n) with constant factor 1000
for _ in range(1000):
for x in arr: pass
def algo_b(arr): # O(n²) but small constant
for i in range(len(arr)):
for j in range(i+1, len(arr)): pass
# For n < 1000, algo_b may be faster despite worse Big-O
Rule 2: Space Complexity Includes Stack Space
CLRS: "We shall generally regard a recursive algorithm that uses space s(n) in addition to the space for the recursion stack as using space s(n)."
Note: Some references count stack space separately. The convention used here counts all memory used, including the call stack. Always clarify which convention you're using.
// O(n) space due to recursion, even though no explicit allocation
func dfs(node *Node) {
if node == nil { return }
dfs(node.Left)
dfs(node.Right)
}
Rule 3: Amortized ≠ Average Case
CLRS Chapter 17: "Amortized analysis guarantees the average performance of each operation in the worst case."
Amortized analysis guarantees the total cost over a sequence of operations in the worst case. Average-case analysis assumes a probability distribution over inputs. They are different concepts:
| Analysis | Over what? | Guarantees |
| Worst-case | Single worst input | Every input |
| Average-case | Random inputs | Expected behavior |
| Amortized | Sequence of operations | Worst-case total |
4. Schema / Parameters Reference
Complexity Classes Reference
| Class | Growth | Example Operations | Typical Algorithms |
| O(1) | Constant | Array index, hash lookup | Direct access |
| O(log n) | Logarithmic | Halving search space | Binary search |
| O(n) | Linear | Single scan | Linear search, counting |
| O(n log n) | Linearithmic | Divide + merge | Merge sort, heap sort |
| O(n²) | Quadratic | Nested iteration | Bubble sort, selection sort |
| O(n³) | Cubic | Triple nesting | Matrix multiply, Floyd-Warshall |
| O(2ⁿ) | Exponential | All subsets | Subset enumeration |
| O(n!) | Factorial | All permutations | Brute-force TSP |
| Complexity | Max n (approximate) |
| O(n!) | 11-12 |
| O(2ⁿ) | 20-25 |
| O(n³) | 500 |
| O(n²) | 5,000-10,000 |
| O(n log n) | 1,000,000 |
| O(n) | 10,000,000-100,000,000 |
| O(log n) | Virtually unlimited |
| O(1) | Unlimited |
5. Behavioral Specification
Normal Operation
Time and space complexity describe the asymptotic behavior of algorithms. For a correct complexity analysis:
- Identify the input size parameter(s): n, V+E, n×m, etc.
- Count basic operations (comparisons, assignments, arithmetic)
- Express count as a function of input size
- Drop constants and lower-order terms
- Express in Big-O (upper bound), Big-Θ (tight bound), or Big-Ω (lower bound)
Documented Limitations
| Limitation | Details | Workaround |
| Big-O hides constants | O(n) with constant 10⁶ is slow | Profile with actual benchmarks |
| Assumes uniform cost model | Cache misses, branch prediction not modeled | Use cache-oblivious analysis for I/O |
| Ignores hardware effects | SIMD, parallelism, pipelining not captured | Benchmark on target hardware |
| Worst-case may be rare | Quicksort O(n²) worst case almost never occurs | Use randomized analysis |
Error / Failure Conditions
| Error | Condition | Official Resolution |
| Stack overflow | Recursion depth > stack limit | Convert to iterative or increase stack |
| Out of memory | Space complexity exceeds available RAM | Use streaming/external memory algorithms |
| Timeout (TLE) | Time complexity too high for input size | Choose algorithm with lower complexity class |
| Integer overflow | Intermediate calculations exceed type range | Use larger types or safe arithmetic |
6. Edge Cases from Official Docs
| Edge Case | Official Behavior | Reference |
| Empty input (n=0) | Most algorithms should handle gracefully — O(1) work | CLRS convention |
| n=1 | Single element — many algorithms are trivially O(1) | All sorting algorithms |
| Already sorted input | Best case for insertion sort O(n), worst for naive quicksort O(n²) | CLRS Ch. 7 |
| All identical elements | Affects duplicate detection, sorting stability | Language sort guarantees |
| Very large n (>10⁹) | O(n) may be too slow; need O(√n) or O(log n) | Competitive programming guides |
| Negative numbers | May affect hash functions, modular arithmetic | Language-specific hash behavior |
7. Version & Compatibility Matrix
Go
| Version | Change | Notes |
| Go 1.18+ | Generics | Generic data structures possible; no performance difference |
| Go 1.21+ | slices package | slices.Sort() — pattern-defeating quicksort, O(n log n) guaranteed |
Java
| Version | Change | Notes |
| Java 8 | HashMap tree bins | Worst-case per-bucket: O(n) → O(log n) when > 8 entries |
| Java 11 | Arrays.sort() improved | Dual-pivot quicksort for primitives; TimSort for objects |
| Java 17+ | Records, sealed classes | No performance impact on algorithms |
Python
| Version | Change | Notes |
| Python 3.7+ | dict preserves insertion order | Guaranteed by spec (was implementation detail in 3.6) |
| Python 3.10+ | match statement | Pattern matching — no complexity impact |
| Python 3.11+ | CPython 10-60% faster | Adaptive interpreter; same Big-O, better constants |
8. Official Examples
Example 1: Go Benchmark
Source: Go testing package
package main
import "testing"
func BenchmarkLinearSearch(b *testing.B) {
arr := make([]int, 10000)
for i := range arr { arr[i] = i }
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
linearSearch(arr, 9999)
}
}
func linearSearch(arr []int, target int) int {
for i, v := range arr {
if v == target { return i }
}
return -1
}
Run: go test -bench=BenchmarkLinearSearch -benchmem
Example 2: Python timeit
Source: Python timeit docs
import timeit
def linear_search(arr, target):
for i, v in enumerate(arr):
if v == target:
return i
return -1
arr = list(range(10000))
# Measure time for 1000 runs
t = timeit.timeit(lambda: linear_search(arr, 9999), number=1000)
print(f"Average: {t/1000*1000:.3f} ms per call")
Example 3: Python tracemalloc
Source: Python tracemalloc docs
import tracemalloc
tracemalloc.start()
# O(n) space allocation
data = [i ** 2 for i in range(100000)]
current, peak = tracemalloc.get_traced_memory()
print(f"Current: {current / 1024:.1f} KB")
print(f"Peak: {peak / 1024:.1f} KB")
tracemalloc.stop()
9. Compliance & Best Practices Checklist
Content Rules for specification.md: - Always link directly to the relevant doc section (not just the homepage) - Use official examples from the documentation when available - Note breaking changes and deprecated features between versions - Include official security / safety recommendations - Minimum 2 Core Rules, 3 Parameters, 3 Edge Cases, 2 Official Examples