Big O
Written by Josh Beto
Big O
Big O notation is a measure of how well the algorithm performs in terms of space or time. For your purposes in CS012, we’ll be focusing on time. In Big O notation, you only consider the highest-ordered term and don’t consider multiplication by constants. We’ll go over what this means with a few examples.
Examples
for (int i = 0; i < v.size(); i++) {
cout << v.at(i) << endl;
}
for (int i = 0; i < v.size(); i++) {
cout << v.at(i) << endl;
}
In this example, we are printing out a vector<int>
twice. You can think of this as O(2n)
since we are linearly scanning the vector twice. According to our second rule that we ignore constants however, our actual Big O is O(n)
.
for (int i = 0; i < v.size() / 2; i++) {
cout << v.at(i) << endl;
}
In this example, we are printing out only half of the vector<int>
. You can think of our runtime as O(n/2)
. Since constants don’t matter however, our Big O is O(n)
.
for (int i = 1; i < v.size(); i *= 2) {
cout << v.at(i) << endl;
}
Although you may be tempted to think this is O(n)
once again, the real answer is O(log(n))
. You start with i = 1 -> 2 -> 4 -> 8
. We double the i
each time, meaning we cover twice the amount of space as the last step, thus skipping a different number of elements each time.
for (int i = 0; i < v.size(); i++) { // 1
for (int i = 0; i < v.size(); i += 2) { // 2
cout << v.at(i) << endl; // 3
} // 4
} // 5
for (int i = 1; i < v.size(); i *= 3) { // 6
cout << v.at(i) << endl; // 7
}// 8
The loops from lines 1 - 5 have a Big O of O(n
2
)
. Line 1 runs O(n)
times and for each of those loop iterations, line 2 runs O(n)
times as well, thus resulting in O(n
2
)
. The loop from line 6 to line 8 runs O(log(n))
times. You can think of this code segment as having an O(n
2
+ log(n))
runtime. Our highest ordered term is n
2
since it grows much faster than log(n)
. Thus, our total runtime is O(n
2
)
.
- As a side note, one of the harder algorithms to come up with a runtime is merge sort. For merge sort, you can think of it as having a
log(n)
number of recursive levels. Each level isO(n)
because it is calling themerge()
function. Thus, our runtime isO(nlog(n))
.
Exercises
Try to come up with the runtime for these ones on your own. The answers will be at the bottom, but try it for yourself first!
Practice 1
for (int i = 0; i < 5; i++) {
for (int i = 3; i < v.size(); i += 2) {
cout << v.at(i) << endl;
}
}
Practice 2
int BinarySearch(int numbers[], int numbersSize, int key) {
int mid;
int low;
int high;
low = 0;
high = numbersSize - 1;
while (high >= low) {
// prevent integer overflow by casting to unsigned
mid = (int)((static_cast<unsigned>(high) + static_cast<unsigned>(low)) / 2);
if (numbers[mid] < key) {
low = mid + 1;
}
else if (numbers[mid] > key) {
high = mid - 1;
}
else {
return mid;
}
}
return -1; // not found
}
Practice 3
#include <algorithm>
#include <vector>
#include <iostream>
using namespace std;
void sortAndPrint(vector<int> &v) {
for (unsigned i = 0; i + 1 < v.size(); i++) {
int minLoc = i;
for (unsigned j = i + 1; j < v.size(); j++) {
if (v.at(j) < v.at(minLoc)) {
minLoc = j;
}
}
swap(v.at(minLoc), v.at(i));
}
for (int i : v) {
cout << i << " ";
}
cout << endl;
}
Answers
O(n)
O(log(n))
O(n
2
)
- Previous
- Next