Week 7 - Complexity Analysis
Week 7 - Complexity Analysis
Agenda
1. Introduction
2. How to measure the efficiency of a program?
3. Time Complexity
○ What is Time Complexity
○ How do we measure Time Complexity?
○ Understanding Big O Notation
○ How can we compare two programs?
○ How to calculate Big O Notation?
4. Space Complexity
○ What is Space Complexity
○ How do we measure Space Complexity?
For instance, a program that is not well-written might appear to work fast on a
computer with a high-performance processor, but it might perform poorly on a
slower computer. We also need to take into account other aspects such as the size
of the task (input size) and the way the program was designed (implementation).
To get a better understanding of how efficient a program is, we can use complexity
analysis. Complexity analysis helps us analyze how much time and space a
program needs to complete a task, based on the input size and how the program is
designed.
By using complexity analysis, we can compare different programs more fairly and
see how well they perform for different input sizes. This helps us create more
efficient solutions that can handle larger tasks. So, complexity analysis is a
valuable tool for evaluating the efficiency of programs and improving their
performance.
3. Time Complexity
● What is Time Complexity?
For example, let’s say we have two programs that solve the same problem
One program the red solution seems to be faster for small inputs, but
another program the green solution is faster for larger inputs.
● How can we compare two solutions?
Big O notation can help us here. We can use it to describe how the expected
runtime of each program changes as the size of the input grows. The
program with a lower Big O notation is generally more efficient, because it
takes less time to complete its task as the input gets larger. By describing the
long-term behavior of our program, we can compare different solutions and
choose the one that will be the most efficient in the long run.
● Conclusion
● Code Examples
Input: Output:
25 5 Yes
18 8 No
Solution:
#include <iostream>
using namespace std;
int main() {
int a, b;
cin >> a >> b;
cout << ((a % b == 0) ? "Yes" : "No") << endl;
}
○ The Space Complexity of this solution is O(1). This is because there are
only two variables of type int which are allocated in the memory for the
problem requirements so it’s also a constant memory.
Example: Given a positive integer n, find the lowest power of 2 that is
greater than n.
Input: Output:
14 16
20 32
Solution:
#include <iostream>
using namespace std;
int main() {
int n;
cin >> n;
int power = 1;
while (power <= n)
power *= 2;
cout << power << endl;
}
○ The Time Complexity of this solution is O(log n). This is because, in the
worst case scenario where n is a power of 2, it will take log n iterations to
go through the loop until the highest power of 2 that is less than or equal
to n is found.
○ The Space Complexity of this solution is O(1). This is because there are
only two variables of type int which are allocated in the memory for the
problem requirements so it’s also a constant memory.
Example: Given n elements, find the maximum element among them.
Input: Output:
5 50
10 20 13 50 7
Solution:
#include <iostream>
using namespace std;
int main() {
int n;
cin >> n;
int mx = INT_MIN;
for (int i = 0; i < n; i++) {
int cur;
cin >> cur;
mx = max(mx, cur);
}
cout << mx << endl;
}
○ The Time Complexity of this solution is O(n) because the loop iterates n
times, making it run in linear time.
○ The Space Complexity of this solution is O(1). This is because there are
four variables of type int which are allocated in the memory for the
problem requirements so it’s also a constant memory.
Example: Given an array of n elements and an integer k, find if there are
two elements with sum divisible by k.
Input: Output:
5 10 “YES”
2 6 12 8 4
Solution:
#include <iostream>
using namespace std;
int main() {
int n, k;
cin >> n >> k;
for (int i = 0; i < n; i++)
cin >> arr[i];
bool flag = false;
for (int i = 0; i < n; i++)
for (int j = 0; j < n; j++)
if ((arr[i] + arr[j]) % k == 0)
flag = true;
cout << ((flag) ? "YES" : "NO") << endl;
}
○ The Time Complexity of this solution is O(n2). This is because the outer
loop iterates n times, and the inner loop iterates n times on each iteration
the outer loop iterates.
Function Complexity
min(a,b) O (1)
max(a,b) O (1)
swap(a,b) O (1)
binary_search(arr, arr + n, x) O (log(n))
lower_bound(arr, arr + n, x) O (log(n))
upper_bound(arr, arr + n, x) O (log(n))
pow(a,n) O (n)
reverse(arr, arr + n) O (n)
min_element(arr, arr + n) O (n)
max_element(arr, arr + n) O (n)
fill(arr, arr + n, x) O (n)
count(arr, arr + n, x) O (n)
find(arr, arr + n, x) O (n)
sort(arr, arr + n) O (nlog(n))
next_permutation(arr, arr + n) O (n!)
6. Time/Memory Limit Exceed
There are two types of verdicts that may appear to most of us while solving a
problem, namely "Time Limit Exceeded" (TLE) and "Memory Limit Exceeded"
(MLE). These errors mean that our solution may be 100% correct, but it needs
some optimization to be accepted. In such situations, we have to think of a better
way to solve the problem or optimize the time or memory needed to solve it.
For example, if we are given n (1 <= n <= 100000) and our complexity is
O(nlog(n)), which is about 106, it's fine. But if our complexity is O(n2), which is
about 1010, we will get a Time Limit Exceeded error.