Big O Notation is a mathematical notation used in computer science to describe the performance or complexity of an algorithm in terms of time and space as the input size grows. It provides a high-level understanding of how an algorithm scales and is used to express the worst-case scenario of an algorithm’s growth rate.
Big O notation helps to:
Big O notation expresses the upper bound of an algorithm’s runtime or space usage in relation to the size of the input (usually denoted as n). It focuses on the dominant term, ignoring constants and lower-order terms because they become less important as the input size grows.
For example:
O(2n + 5) is simplified to O(n) because as n increases, the constant 5 and the coefficient 2 become negligible.Here are some common Big O notations, ordered from best (fastest) to worst (slowest) in terms of time complexity:
O(1) – Constant Time
int arr[5] = {10, 20, 30, 40, 50};
int element = arr[2]; // O(1)
O(log n) – Logarithmic Time
int binarySearch(int arr[], int size, int target) {
int low = 0, high = size - 1;
while (low <= high) {
int mid = (low + high) / 2;
if (arr[mid] == target) return mid;
else if (arr[mid] < target) low = mid + 1;
else high = mid - 1;
}
return -1;
}
// O(log n)
O(n) – Linear Time
void printArray(int arr[], int size) {
for (int i = 0; i < size; i++) {
cout << arr[i] << " ";
}
}
// O(n)
O(n log n) – Linearithmic Time
void merge(int arr[], int l, int m, int r) {
// Merging logic
}
void mergeSort(int arr[], int l, int r) {
if (l < r) {
int m = l + (r - l) / 2;
mergeSort(arr, l, m);
mergeSort(arr, m + 1, r);
merge(arr, l, m, r);
}
}
// O(n log n)
O(n²) – 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²)
O(2ⁿ) – Exponential Time
void towerOfHanoi(int n, char from_rod, char to_rod, char aux_rod) {
if (n == 1) {
cout << "Move disk 1 from rod " << from_rod << " to rod " << to_rod << endl;
return;
}
towerOfHanoi(n - 1, from_rod, aux_rod, to_rod);
cout << "Move disk " << n << " from rod " << from_rod << " to rod " << to_rod << endl;
towerOfHanoi(n - 1, aux_rod, to_rod, from_rod);
}
// O(2ⁿ)
O(n!) – Factorial Time
If we graph these notations with input size on the x-axis and time/operations on the y-axis, it looks like this:
n increases.When analyzing an algorithm's complexity, you usually look at three different cases:
In Big O notation, we ignore constants and smaller terms because they don’t matter much when the input size grows large. For example, an algorithm with a time complexity of O(2n + 5) is simplified to O(n), because the 2 and 5 become insignificant as n grows.
Let's say you have an unsorted array and want to find a specific element using linear search.
Big O notation helps you classify algorithms based on how they behave with increasing input size. It focuses on the worst-case scenario, simplifying the analysis and helping developers choose the most efficient algorithms for large-scale problems.
Open this section to load past papers