Time complexity measures how the runtime of an algorithm grows as the input size increases. Space complexity measures how much extra memory an algorithm needs while running.
1. What is Time Complexity?
- Describes how the number of operations changes with input size
n. - Written using Big O notation:
O(1),O(n),O(n2), etc. - Use the fastest-growing term and ignore constants.
Example: Constant Time
This function always does the same work no matter how large the array is.
#include <iostream>
using namespace std;
int getFirst(int arr[], int n)
{
return arr[0];
}
int main()
{
int arr[] = {10, 20, 30};
cout << getFirst(arr, 3) << endl;
return 0;
}- Time complexity:
O(1) - Explanation: Single access, independent of
n.
Example: Linear Time
This function checks every element once.
#include <iostream>
using namespace std;
bool contains(int arr[], int n, int target)
{
for (int i = 0; i < n; i++)
{
if (arr[i] == target)
return true;
}
return false;
}
int main()
{
int arr[] = {4, 7, 1, 9};
cout << contains(arr, 4, 9) << endl;
return 0;
}- Time complexity:
O(n) - Explanation: Work grows in direct proportion to input size.
Example: Quadratic Time
This function compares every pair of elements.
#include <iostream>
using namespace std;
void printPairs(int arr[], int n)
{
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n; j++)
{
cout << arr[i] << "," << arr[j] << endl;
}
}
}
int main()
{
int arr[] = {1, 2, 3};
printPairs(arr, 3);
return 0;
}- Time complexity:
O(n2) - Explanation: Two nested loops cause quadratic growth.
Example: Logarithmic Time
Search by splitting the input in half each step.
#include <iostream>
using namespace std;
bool binarySearch(int arr[], int n, int target)
{
int left = 0;
int right = n - 1;
while (left <= right)
{
int mid = left + (right - left) / 2;
if (arr[mid] == target)
return true;
else if (arr[mid] < target)
left = mid + 1;
else
right = mid - 1;
}
return false;
}
int main()
{
int arr[] = {1, 3, 5, 7, 9};
cout << binarySearch(arr, 5, 7) << endl;
return 0;
}- Time complexity:
O(log n) - Explanation: Input size halves each step.
Example: n log n Time
Many efficient sorting algorithms run in O(n log n) time.
#include <algorithm>
#include <iostream>
using namespace std;
int main()
{
int arr[] = {5, 2, 8, 1, 3};
int n = sizeof(arr) / sizeof(arr[0]);
sort(arr, arr + n);
for (int i = 0; i < n; i++)
cout << arr[i] << " ";
cout << endl;
return 0;
}- Time complexity:
O(n log n) - Explanation: Sorting splits work and processes each part.
2. Best, Average, and Worst Case
- Best case: the fastest possible input for the algorithm.
- Average case: expected performance for a typical input.
- Worst case: the slowest possible input.
Example: Linear Search
- Best case:
O(1)when the target is at the first position. - Worst case:
O(n)when the target is missing or at the end.
Example: Bubble Sort
- Best case:
O(n)if the array is already sorted and the algorithm checks for no swaps. - Worst case:
O(n2)when the array is in reverse order.
Example: Binary Search
- Best case:
O(1)if the middle element is the target. - Worst case:
O(log n)if the target is not found.
Example: Quick Sort
- Average case:
O(n log n) - Worst case:
O(n2)when the pivot is always the smallest or largest element.
3. What is Space Complexity?
- Measures extra memory used by the algorithm.
- Focuses on additional data structures or recursion stack.
- Ignore the space used by the input itself.
Example: Constant Space
This function uses only a few variables.
#include <iostream>
using namespace std;
int sumFirstTwo(int arr[], int n)
{
int sum = arr[0] + arr[1];
return sum;
}
int main()
{
int arr[] = {5, 6, 7};
cout << sumFirstTwo(arr, 3) << endl;
return 0;
}- Space complexity:
O(1) - Explanation: Only a fixed number of extra variables are used.
Example: Linear Space
This function creates a new array of size n.
#include <iostream>
using namespace std;
int* copyArray(int arr[], int n)
{
int* copy = new int[n];
for (int i = 0; i < n; i++)
{
copy[i] = arr[i];
}
return copy;
}
int main()
{
int arr[] = {2, 4, 6};
int* copy = copyArray(arr, 3);
cout << copy[0] << " " << copy[1] << " " << copy[2] << endl;
delete[] copy;
return 0;
}- Space complexity:
O(n) - Explanation: Extra memory grows with input size.
Example: Recursion Space
Recursive functions use stack memory for each call.
#include <iostream>
using namespace std;
int factorial(int n)
{
if (n == 0)
return 1;
return n * factorial(n - 1);
}
int main()
{
cout << factorial(5) << endl;
return 0;
}- Space complexity:
O(n) - Explanation: Recursion adds one stack frame per call.
3. Common Complexity Patterns
O(1): single access or fixed operations.O(log n): divide-and-conquer (binary search).O(n): single loop over input.O(n log n): efficient sorting algorithms.O(n2): nested loops over input.
4. Time Complexity Priority from Best to Worst
From best to worst, use this ordering when comparing growth rates:
O(1)- constant timeO(log n)- logarithmic timeO(n)- linear timeO(n log n)- linearithmic timeO(n2)- quadratic timeO(n3)- cubic timeO(2n)- exponential timeO(n!)- factorial time
Smaller growth means better performance as input size increases.
5. How to Compare Algorithms
- Choose the algorithm with lower growth for large
n. - If two algorithms have the same Big O, compare constant factors and memory use.
- For small inputs, readability and simplicity also matter.
5. Quick Rules
- Ignore constant multipliers:
O(2n)becomesO(n). - Ignore lower-order terms:
O(n + n2)becomesO(n2). - Count loops, recursion depth, and extra data structures.
Time complexity tells how fast code runs as input grows. Space complexity tells how much extra memory it needs.