Learn to predict time and memory requirements, compare alternative solutions and justify complexity using operation counts.
LEARNING GOALS
🎯 Learning Objectives
After completing this level, you should be able to:
Identify input size and the basic operation of an algorithm.
Count operations and express time complexity as a function of input size.
Distinguish best, average and worst-case performance.
Use Big-O, Big-Omega and Big-Theta notation correctly.
Calculate auxiliary space and recursion-stack space.
Explain the complexity of common loop and recursive patterns.
FOUNDATION
🧭 1. What Is Performance Analysis?
Performance analysis studies how the resources required by an algorithm grow when the input becomes larger. The main resources are execution time and memory space.
Core idea: Instead of asking only “How many seconds did this program take?”, we ask “How does the work grow when the input changes from n to 2n or 10n?”
⏱️ Empirical Measurement
Run the program and record actual time or memory.
Machine and compiler dependent
Requires test data
Useful after implementation
📐 Theoretical Analysis
Count operations as a function of input size.
Machine independent
Works before implementation
Explains scalability
ANALYSIS MODEL
🔬 2. Input Size and Basic Operation
First decide what n represents. Then identify the operation that dominates the running time.
Problem
Input Size
Basic Operation
Search an array
Number of elements n
Key comparison
Sort records
Number of records n
Comparison/movement
Multiply matrices
Dimension n
Multiplication/addition
Traverse a graph
Vertices V, edges E
Visit vertex/edge
Match a string
Text n, pattern m
Character comparison
RAM model: Assignment, arithmetic, comparison and array access are treated as constant-time operations. This abstraction supports machine-independent comparison.
TIME ANALYSIS
⏱️ 3. Time Complexity
Time complexity is a function T(n) describing how the number of basic operations grows with input size n.
Constant Work
int first = a[0];
printf("%d", first);
Reason: Both statements execute once, independent of n.
Time Complexity: O(1)
Single Loop
for (int i = 0; i < n; i++) {
sum += a[i];
}
Reason: The loop body executes n times.
T(n) = an + b ⇒ O(n)
Nested Loops
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
count++;
}
}
Reason: n outer iterations × n inner iterations = n².
Time Complexity: O(n²)
Repeated Halving
for (int value = n; value > 1; value /= 2) {
count++;
}
Reason: After k iterations, n/2ᵏ ≤ 1, so k ≈ log₂n.
Time Complexity: O(log n)
INPUT BEHAVIOUR
🎭 4. Best, Average and Worst Cases
Best Case
Minimum work for an input of size n.
Linear search: first position → Θ(1)
Average Case
Expected work under an input distribution.
Linear search: about n/2 → Θ(n)
Worst Case
Maximum work for an input of size n.
Linear search: absent/last → Θ(n)
Important: Big-O does not automatically mean “worst case.” Big-O is an upper-bound notation; the case and notation describe different ideas.
GROWTH BOUNDS
📈 5. Asymptotic Notations
Asymptotic analysis focuses on the dominant term as n becomes large. Constants and lower-order terms do not change the growth class.
O(g(n))
Upper Bound
The algorithm grows no faster than a constant multiple of g(n), for sufficiently large n.
Ω(g(n))
Lower Bound
The algorithm grows at least as fast as a constant multiple of g(n).
Θ(g(n))
Tight Bound
The function is bounded above and below by constant multiples of g(n).
Example: T(n) = 3n² + 5n + 7. The dominant term is n², so the tight bound is Θ(n²).
Choose an input size and loop pattern. Results appear only after you click Analyze Growth.
Select values and click Analyze Growth.
Selected Complexity—
Estimated Operations—
When n Doubles—
C IMPLEMENTATION
💻 10. Compare Linear and Binary Search
This program counts comparisons for the same key. Binary search requires a sorted array.
#include <stdio.h>
int linearSearch(int a[], int n, int key, int *count) {
for (int i = 0; i < n; i++) {
(*count)++;
if (a[i] == key) return i;
}
return -1;
}
int binarySearch(int a[], int n, int key, int *count) {
int low = 0, high = n - 1;
while (low <= high) {
int mid = low + (high - low) / 2;
(*count)++;
if (a[mid] == key) return mid;
if (a[mid] < key) low = mid + 1;
else high = mid - 1;
}
return -1;
}
int main(void) {
int n, key;
scanf("%d", &n);
int a[n];
for (int i = 0; i < n; i++) scanf("%d", &a[i]);
scanf("%d", &key);
int linearCount = 0, binaryCount = 0;
int linearIndex = linearSearch(a, n, key, &linearCount);
int binaryIndex = binarySearch(a, n, key, &binaryCount);
printf("Linear Search: index = %d, comparisons = %d\n", linearIndex, linearCount);
printf("Binary Search: index = %d, comparisons = %d\n", binaryIndex, binaryCount);
return 0;
}
Sample Input
8
4 9 13 18 25 31 42 57
42
Sample Output
Linear Search: index = 6, comparisons = 7
Binary Search: index = 6, comparisons = 3
Linear SearchBest O(1), Worst O(n)
It may inspect every item.
Binary SearchBest O(1), Worst O(log n)
It halves the interval.
Auxiliary SpaceO(1) for both
Both versions are iterative.
C PROGRAM + TRACING
🔍 11. Program Tracing — Linear vs Binary Search
Trace the sample program one statement at a time and compare how both searches locate key 42.
PROGRAM TRACING — LINEAR VS BINARY SEARCH
💻 C Program
int a[] = {4, 9, 13, 18, 25, 31, 42, 57};for (int i = 0; i < n; i++) { linearCount++; if (a[i] == key) return i;int low = 0, high = n - 1;while (low <= high) { int mid = low + (high - low) / 2; binaryCount++; if (a[mid] == key) return mid; if (a[mid] < key) low = mid + 1; else high = mid - 1;
🧠 What is happening?
Press Next to begin with the sample input.
📊 Live Variables
Algorithm—
Index i—
Low—
High—
Mid—
Current Value—
Comparisons0
Key42
Output
—
Step 0 of 14
PLACEMENT PREPARATION
💼 12. Interview Points
Explain the reason: state how many times the dominant operation executes.
Mention assumptions: sorted input, balanced trees and uniform hashing can change complexity.
Separate time and space: an in-place algorithm may use little memory but still run slowly.
Discuss trade-offs: extra memory or preprocessing may reduce repeated-query time.
Use tight bounds: state Θ when upper and lower growth match.
01
Worst-case versus amortized complexity?
02
Why does recursive binary search use O(log n) space?
03
Can O(n²) also be O(n³)? Which is tighter?
04
When is extra space worth using?
PRACTICE WITH HINTS
✍️ 13. Practice Problems
Solve each problem first. Use Hint only when needed and Show Answer to verify your reasoning.
1. Complexity of accessing a[k]?
Array elements have direct indexed addresses.
Θ(1). Address calculation and access do not depend on array length.
2. Complexity of one loop from 0 to n−1?
Count the values taken by i.
Θ(n). The body executes n times.
3. Complexity of two full nested loops?
Multiply nested iteration counts.
Θ(n²). n × n executions.
4. Complexity when i doubles from 1 until n?
After k steps, i = 2ᵏ.
Θ(log n). Solve 2ᵏ ≥ n.
5. Complexity of a triangular loop with j from i to n−1?
Add n + (n−1) + ... + 1.
Θ(n²). n(n+1)/2 has dominant term n².
6. Combine sequential Θ(n) and Θ(n²) blocks.
Add, then retain the dominant term.
Θ(n²). Θ(n+n²) simplifies to Θ(n²).
7. Best and worst time of iterative binary search?
First midpoint versus repeated halving.
Best Θ(1), worst Θ(log n), assuming sorted input.
8. Best and worst time of insertion sort?
Consider sorted and reverse-sorted input.
Best Θ(n), worst Θ(n²).
9. Auxiliary space of recursive factorial?
Count active call frames.
Θ(n). Recursion depth is n.
10. Auxiliary space of iterative factorial?
Only fixed variables are used.
Θ(1).
11. Tight bound for 3n² + 5n + 7?
Keep the highest-order term.
Θ(n²).
12. Order log n, n, n log n, n² and 2ⁿ.
Logarithmic is smallest; exponential is largest.
O(log n) < O(n) < O(n log n) < O(n²) < O(2ⁿ).
13. Maximum binary-search comparisons for n = 1024?
Use ⌊log₂n⌋ + 1.
At most 11 comparisons.
14. Estimate n log₂n for n = 8.
log₂8 = 3.
24. 8 × 3 = 24.
15. At n = 10, compare n² and 2ⁿ.
Calculate 10² and 2¹⁰.
100 versus 1024. Exponential growth is over ten times larger.
16. Why is dynamic-array append amortized O(1)?
Resizing does not occur on every append.
Total copying across n geometric-growth appends is O(n), giving O(1) amortized per append.
17. Space for an additional n × n matrix?
Count stored elements.
Θ(n²).
18. Solve T(n) = T(n/2) + O(1).
Count halvings until 1.
Θ(log n).
19. Solve T(n) = 2T(n/2) + O(n).
There are log n levels with combined work n per level.
Θ(n log n). This is Merge Sort’s recurrence.
20. Why preserve both sizes in O(n + m)?
n and m may grow independently.
O(n + m) preserves both independent dimensions instead of incorrectly assuming m is proportional to n.
QUICK RECALL
📝 14. Quick Revision
Performance analysis studies resource growth as input size increases.
Time counts dominant operations; space counts growing memory.
Big-O is an upper bound, Ω is a lower bound and Θ is a tight bound.