Algorithm complexity refers to the measurement of the efficiency of an algorithm in terms of time and space. It helps evaluate how well an algorithm performs as the size of the input grows. There are two major aspects of algorithm complexity:
Both are often expressed in terms of Big-O notation, which characterizes the upper bound of an algorithm's growth rate.
Time complexity describes the amount of time an algorithm takes to run, relative to the size of its input. The input size is usually represented as , and time complexity is generally expressed in Big-O notation, which gives an asymptotic upper bound on the time required to execute the algorithm.
O(1) - Constant Time:
int arr[10];
int element = arr[5]; // O(1) access
O(log n) - Logarithmic Time:
int binarySearch(int arr[], int n, int key) {
int left = 0, right = n - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (arr[mid] == key) return mid;
else if (arr[mid] < key) left = mid + 1;
else right = mid - 1;
}
return -1;
} // O(log n)
O(n) - Linear Time:
int linearSearch(int arr[], int n, int key) {
for (int i = 0; i < n; i++) {
if (arr[i] == key) return i;
}
return -1;
} // O(n)
O(n log n) - Linearithmic Time:
void mergeSort(int arr[], int left, int right) {
if (left < right) {
int mid = left + (right - left) / 2;
mergeSort(arr, left, mid);
mergeSort(arr, mid + 1, right);
merge(arr, left, mid, right); // O(n log n)
}
}
O(n^2) - Quadratic Time:
void bubbleSort(int arr[], int n) {
for (int i = 0; i < n - 1; i++) {
for (int j = 0; j < n - i - 1; j++) {
if (arr[j] > arr[j + 1]) swap(arr[j], arr[j + 1]);
}
} // O(n^2)
}
O(n^3) - Cubic Time:
O(2^n) - Exponential Time:
int fibonacci(int n) {
if (n <= 1) return n;
return fibonacci(n - 1) + fibonacci(n - 2); // O(2^n)
}
O(n!) - Factorial Time:
Space complexity describes the amount of memory an algorithm uses relative to the input size. Similar to time complexity, space complexity is usually expressed in Big-O notation.
O(1) - Constant Space:
void swap(int &a, int &b) {
int temp = a;
a = b;
b = temp; // O(1) space
}
O(n) - Linear Space:
int* arr = new int[n]; // O(n) space
O(n^2) - Quadratic Space:
int** matrix = new int*[n];
for (int i = 0; i < n; i++) {
matrix[i] = new int[n]; // O(n^2) space
}
O(log n) - Logarithmic Space:
void binarySearch(int arr[], int left, int right, int key) {
if (left <= right) {
int mid = left + (right - left) / 2;
if (arr[mid] == key) return mid;
if (arr[mid] < key) return binarySearch(arr, mid + 1, right, key); // O(log n) space
return binarySearch(arr, left, mid - 1, key);
}
return -1;
}
Big-O notation describes the upper bound or worst-case scenario of an algorithm’s performance. Here is a list of common complexities in order from fastest to slowest:
When comparing different algorithms, the choice of the algorithm should depend on:
In conclusion, understanding algorithm complexity allows developers to select the most efficient algorithms based on the nature of the data and required operations.
Open this section to load past papers