Time Complexity and Space Complexity
Last Updated : 05 Dec, 2024
Many times there are more than one ways to solve a problem with different algorithms and we need a way to compare multiple ways. Also, there are situations where we would like to know how much time and resources an algorithm might take when implemented. To measure performance of algorithms, we typically use time and space complexity analysis. The idea is to measure order of growths in terms of input size.
- Independent of the machine and its configuration, on which the algorithm is running on.
- Shows a direct correlation with the number of inputs.
- Can distinguish two algorithms clearly without ambiguity.
Time Complexity: The time complexity of an algorithm quantifies the amount of time taken by an algorithm to run as a function of the length of the input. Note that the time to run is a function of the length of the input and not the actual execution time of the machine on which the algorithm is running on.
The valid algorithm takes a finite amount of time for execution. The time required by the algorithm to solve given problem is called time complexity of the algorithm. Time complexity is very useful measure in algorithm analysis.
It is the time needed for the completion of an algorithm. To estimate the time complexity, we need to consider the cost of each fundamental instruction and the number of times the instruction is executed.
Example 1: Addition of two scalar variables.
Algorithm ADD SCALAR(A, B)
//Description: Perform arithmetic addition of two numbers
//Input: Two scalar variables A and B
//Output: variable C, which holds the addition of A and B
C <- A + B
return C
The addition of two scalar numbers requires one addition operation. the time complexity of this algorithm is constant, so T(n) = O(1) .
In order to calculate time complexity on an algorithm, it is assumed that a constant time c is taken to execute one operation, and then the total operations for an input length on N are calculated. Consider an example to understand the process of calculation: Suppose a problem is to find whether a pair (X, Y) exists in an array, A of N elements whose sum is Z. The simplest idea is to consider every pair and check if it satisfies the given condition or not.
The pseudo-code is as follows:
int a[n];
for(int i = 0;i < n;i++)
cin >> a[i]
for(int i = 0;i < n;i++)
for(int j = 0;j < n;j++)
if(i!=j && a[i]+a[j] == z)
return true
return false
Below is the implementation of the above approach:
C++ // C++ program for the above approach #include <bits/stdc++.h> using namespace std; // Function to find a pair in the given // array whose sum is equal to z bool findPair(int a[], int n, int z) { // Iterate through all the pairs for (int i = 0; i < n; i++) for (int j = 0; j < n; j++) // Check if the sum of the pair // (a[i], a[j]) is equal to z if (i != j && a[i] + a[j] == z) return true; return false; } // Driver Code int main() { // Given Input int a[] = { 1, -2, 1, 0, 5 }; int z = 0; int n = sizeof(a) / sizeof(a[0]); // Function Call if (findPair(a, n, z)) cout << "True"; else cout << "False"; return 0; }
Java // Java program for the above approach import java.lang.*; import java.util.*; class GFG{ // Function to find a pair in the given // array whose sum is equal to z static boolean findPair(int a[], int n, int z) { // Iterate through all the pairs for(int i = 0; i < n; i++) for(int j = 0; j < n; j++) // Check if the sum of the pair // (a[i], a[j]) is equal to z if (i != j && a[i] + a[j] == z) return true; return false; } // Driver code public static void main(String[] args) { // Given Input int a[] = { 1, -2, 1, 0, 5 }; int z = 0; int n = a.length; // Function Call if (findPair(a, n, z)) System.out.println("True"); else System.out.println("False"); } } // This code is contributed by avijitmondal1998
Python # Python3 program for the above approach # Function to find a pair in the given # array whose sum is equal to z def findPair(a, n, z) : # Iterate through all the pairs for i in range(n) : for j in range(n) : # Check if the sum of the pair # (a[i], a[j]) is equal to z if (i != j and a[i] + a[j] == z) : return True return False # Driver Code # Given Input a = [ 1, -2, 1, 0, 5 ] z = 0 n = len(a) # Function Call if (findPair(a, n, z)) : print("True") else : print("False") # This code is contributed by splevel62.
C# // C# program for above approach using System; class GFG{ // Function to find a pair in the given // array whose sum is equal to z static bool findPair(int[] a, int n, int z) { // Iterate through all the pairs for(int i = 0; i < n; i++) for(int j = 0; j < n; j++) // Check if the sum of the pair // (a[i], a[j]) is equal to z if (i != j && a[i] + a[j] == z) return true; return false; } // Driver Code static void Main() { // Given Input int[] a = { 1, -2, 1, 0, 5 }; int z = 0; int n = a.Length; // Function Call if (findPair(a, n, z)) Console.WriteLine("True"); else Console.WriteLine("False"); } } // This code is contributed by sanjoy_62.
JavaScript <script> // JavaScript program for the above approach // Function to find a pair in the given // array whose sum is equal to z function findPair(a, n, z) { // Iterate through all the pairs for(let i = 0; i < n; i++) for(let j = 0; j < n; j++) // Check if the sum of the pair // (a[i], a[j]) is equal to z if (i != j && a[i] + a[j] == z) return true; return false; } // Driver Code // Given Input let a = [ 1, -2, 1, 0, 5 ]; let z = 0; let n = a.length; // Function Call if (findPair(a, n, z)) document.write("True"); else document.write("False"); // This code is contributed by code_hunt </script>
Assuming that each of the operations in the computer takes approximately constant time, let it be c. The number of lines of code executed actually depends on the value of Z. During analyses of the algorithm, mostly the worst-case scenario is considered, i.e., when there is no pair of elements with sum equals Z. In the worst case,
- N*c operations are required for input.
- The outer loop i loop runs N times.
- For each i, the inner loop j loop runs N times.
So total execution time is N*c + N*N*c + c. Now ignore the lower order terms since the lower order terms are relatively insignificant for large input, therefore only the highest order term is taken (without constant) which is N*N in this case. Different notations are used to describe the limiting behavior of a function, but since the worst case is taken so big-O notation will be used to represent the time complexity.
Hence, the time complexity is O(N2) for the above algorithm. Note that the time complexity is solely based on the number of elements in array A i.e the input length, so if the length of the array will increase the time of execution will also increase.
Order of growth is how the time of execution depends on the length of the input. In the above example, it is clearly evident that the time of execution quadratically depends on the length of the array. Order of growth will help to compute the running time with ease.
Another Example: Let's calculate the time complexity of the below algorithm:
C++ count = 0 for (int i = N; i > 0; i /= 2) for (int j = 0; j < i; j++) count++;
Java int count = 0 ; for (int i = N; i > 0; i /= 2) for (int j = 0; j < i; j++) count++; //This code is contributed by Shubham Singh
Python count = 0 i = N while(i > 0): for j in range(i): count+=1 i /= 2 # This code is contributed by subhamsingh10
C# int count = 0 ; for (int i = N; i > 0; i /= 2) for (int j = 0; j < i; j++) count++; // This code is contributed by Shubham Singh
JavaScript let count = 0 for(let i = N; i > 0; i /= 2) for(let j = 0; j < i; j++) count += 1; // This code is contributed by Shubham Singh
This is a tricky case. In the first look, it seems like the complexity is O(N * log N). N for the j′s loop and log(N) for i′s loop. But it's wrong. Let's see why.
Think about how many times count++ will run.
- When i = N, it will run N times.
- When i = N / 2, it will run N / 2 times.
- When i = N / 4, it will run N / 4 times.
- And so on.
The total number of times count++ will run is N + N/2 + N/4+...+1= 2 * N. So the time complexity will be O(N).
Some general time complexities are listed below with the input range for which they are accepted in competitive programming:
Input Length | Worst Accepted Time Complexity | Usually type of solutions |
10 -12 | O(N!) | Recursion and backtracking |
15-18 | O(2N * N) | Recursion, backtracking, and bit manipulation |
18-22 | O(2N * N) | Recursion, backtracking, and bit manipulation |
30-40 | O(2N/2 * N) | Meet in the middle, Divide and Conquer |
100 | O(N4) | Dynamic programming, Constructive |
400 | O(N3) | Dynamic programming, Constructive |
2K | O(N2* log N) | Dynamic programming, Binary Search, Sorting, Divide and Conquer |
10K | O(N2) | Dynamic programming, Graph, Trees, Constructive |
1M | O(N* log N) | Sorting, Binary Search, Divide and Conquer |
100M | O(N), O(log N), O(1) | Constructive, Mathematical, Greedy Algorithms |
Space Complexity:
Definition -
Problem-solving using computer requires memory to hold temporary data or final result while the program is in execution. The amount of memory required by the algorithm to solve given problem is called space complexity of the algorithm.
The space complexity of an algorithm quantifies the amount of space taken by an algorithm to run as a function of the length of the input. Consider an example: Suppose a problem to find the frequency of array elements.
It is the amount of memory needed for the completion of an algorithm.
To estimate the memory requirement we need to focus on two parts:
(1) A fixed part: It is independent of the input size. It includes memory for instructions (code), constants, variables, etc.
(2) A variable part: It is dependent on the input size. It includes memory for recursion stack, referenced variables, etc.
Example : Addition of two scalar variables
Algorithm ADD SCALAR(A, B)
//Description: Perform arithmetic addition of two numbers
//Input: Two scalar variables A and B
//Output: variable C, which holds the addition of A and B
C <— A+B
return C
The addition of two scalar numbers requires one extra memory location to hold the result. Thus the space complexity of this algorithm is constant, hence S(n) = O(1).
The pseudo-code is as follows:
int freq[n];
int a[n];
for(int i = 0; i<n; i++)
{
cin>>a[i];
freq[a[i]]++;
}
Below is the implementation of the above approach:
C++ // C++ program for the above approach #include <bits/stdc++.h> using namespace std; // Function to count frequencies of array items void countFreq(int arr[], int n) { unordered_map<int, int> freq; // Traverse through array elements and // count frequencies for (int i = 0; i < n; i++) freq[arr[i]]++; // Traverse through map and print frequencies for (auto x : freq) cout << x.first << " " << x.second << endl; } // Driver Code int main() { // Given array int arr[] = { 10, 20, 20, 10, 10, 20, 5, 20 }; int n = sizeof(arr) / sizeof(arr[0]); // Function Call countFreq(arr, n); return 0; }
Java // Java program for the above approach import java.util.*; class GFG{ // Function to count frequencies of array items static void countFreq(int arr[], int n) { HashMap<Integer,Integer> freq = new HashMap<>(); // Traverse through array elements and // count frequencies for (int i = 0; i < n; i++) { if(freq.containsKey(arr[i])){ freq.put(arr[i], freq.get(arr[i])+1); } else{ freq.put(arr[i], 1); } } // Traverse through map and print frequencies for (Map.Entry<Integer,Integer> x : freq.entrySet()) System.out.print(x.getKey()+ " " + x.getValue() +"\n"); } // Driver Code public static void main(String[] args) { // Given array int arr[] = { 10, 20, 20, 10, 10, 20, 5, 20 }; int n = arr.length; // Function Call countFreq(arr, n); } } // This code is contributed by gauravrajput1
Python # Python program for the above approach # Function to count frequencies of array items def countFreq(arr, n): freq = dict() # Traverse through array elements and # count frequencies for i in arr: if i not in freq: freq[i] = 0 freq[i]+=1 # Traverse through map and print frequencies for x in freq: print(x, freq[x]) # Driver Code # Given array arr = [10, 20, 20, 10, 10, 20, 5, 20 ] n = len(arr) # Function Call countFreq(arr, n) # This code is contributed by Shubham Singh
C# // C# program for the above approach using System; using System.Collections.Generic; public class GFG { // Function to count frequencies of array items static void countFreq(int[] arr, int n) { Dictionary<int, int> freq = new Dictionary<int, int>(); // Traverse through array elements and count // frequencies for (int i = 0; i < n; i++) { if (freq.ContainsKey(arr[i])) { freq[arr[i]] += 1; } else { freq.Add(arr[i], 1); } } // Traverse through dictionary and print frequencies foreach(KeyValuePair<int, int> x in freq) { Console.WriteLine(x.Key + " " + x.Value); } } static public void Main() { // Given array int[] arr = { 10, 20, 20, 10, 10, 20, 5, 20 }; int n = arr.Length; // Function Call countFreq(arr, n); } } // This code is contributed by lokeshmvs21
JavaScript <script> // Javascript program for the above approach // Function to count frequencies of array items function countFreq(arr, n) { let freq = new Map(); arr.sort((a, b) => a - b) // Traverse through array elements and // count frequencies for (let i = 0; i < n; i++) { if (freq.has(arr[i])) { freq.set(arr[i], freq.get(arr[i]) + 1) } else { freq.set(arr[i], 1) } } // Traverse through map and print frequencies for (let x of freq) document.write(x[0] + " " + x[1] + "<br>"); } // Driver Code // Given array let arr = [10, 20, 20, 10, 10, 20, 5, 20]; let n = arr.length; // Function Call countFreq(arr, n); // This code is contributed by Saurabh Jaiswal </script>
Here two arrays of length N, and variable i are used in the algorithm so, the total space used is N * c + N * c + 1 * c = 2N * c + c, where c is a unit space taken. For many inputs, constant c is insignificant, and it can be said that the space complexity is O(N).
There is also auxiliary space, which is different from space complexity. The main difference is where space complexity quantifies the total space used by the algorithm, auxiliary space quantifies the extra space that is used in the algorithm apart from the given input. In the above example, the auxiliary space is the space used by the freq[] array because that is not part of the given input. So total auxiliary space is N * c + c which is O(N) only.
Similar Reads
DSA Tutorial - Learn Data Structures and Algorithms DSA (Data Structures and Algorithms) is the study of organizing data efficiently using data structures like arrays, stacks, and trees, paired with step-by-step procedures (or algorithms) to solve problems effectively. Data structures manage how data is stored and accessed, while algorithms focus on
7 min read
Basics & Prerequisites
Data Structures
Getting Started with Array Data StructureArray is a collection of items of the same variable type that are stored at contiguous memory locations. It is one of the most popular and simple data structures used in programming. Basic terminologies of ArrayArray Index: In an array, elements are identified by their indexes. Array index starts fr
14 min read
String in Data StructureA string is a sequence of characters. The following facts make string an interesting data structure.Small set of elements. Unlike normal array, strings typically have smaller set of items. For example, lowercase English alphabet has only 26 characters. ASCII has only 256 characters.Strings are immut
3 min read
Hashing in Data StructureHashing is a technique used in data structures that efficiently stores and retrieves data in a way that allows for quick access. Hashing involves mapping data to a specific index in a hash table (an array of items) using a hash function. It enables fast retrieval of information based on its key. The
2 min read
Linked List Data StructureA linked list is a fundamental data structure in computer science. It mainly allows efficient insertion and deletion operations compared to arrays. Like arrays, it is also used to implement other data structures like stack, queue and deque. Hereâs the comparison of Linked List vs Arrays Linked List:
3 min read
Stack Data StructureA Stack is a linear data structure that follows a particular order in which the operations are performed. The order may be LIFO(Last In First Out) or FILO(First In Last Out). LIFO implies that the element that is inserted last, comes out first and FILO implies that the element that is inserted first
2 min read
Queue Data StructureA Queue Data Structure is a fundamental concept in computer science used for storing and managing data in a specific order. It follows the principle of "First in, First out" (FIFO), where the first element added to the queue is the first one to be removed. It is used as a buffer in computer systems
2 min read
Tree Data StructureTree Data Structure is a non-linear data structure in which a collection of elements known as nodes are connected to each other via edges such that there exists exactly one path between any two nodes. Types of TreeBinary Tree : Every node has at most two childrenTernary Tree : Every node has at most
4 min read
Graph Data StructureGraph Data Structure is a collection of nodes connected by edges. It's used to represent relationships between different entities. If you are looking for topic-wise list of problems on different topics like DFS, BFS, Topological Sort, Shortest Path, etc., please refer to Graph Algorithms. Basics of
3 min read
Trie Data StructureThe Trie data structure is a tree-like structure used for storing a dynamic set of strings. It allows for efficient retrieval and storage of keys, making it highly effective in handling large datasets. Trie supports operations such as insertion, search, deletion of keys, and prefix searches. In this
15+ min read
Algorithms
Searching AlgorithmsSearching algorithms are essential tools in computer science used to locate specific items within a collection of data. In this tutorial, we are mainly going to focus upon searching in an array. When we search an item in an array, there are two most common algorithms used based on the type of input
3 min read
Sorting AlgorithmsA Sorting Algorithm is used to rearrange a given array or list of elements in an order. For example, a given array [10, 20, 5, 2] becomes [2, 5, 10, 20] after sorting in increasing order and becomes [20, 10, 5, 2] after sorting in decreasing order. There exist different sorting algorithms for differ
3 min read
Introduction to RecursionThe process in which a function calls itself directly or indirectly is called recursion and the corresponding function is called a recursive function. A recursive algorithm takes one step toward solution and then recursively call itself to further move. The algorithm stops once we reach the solution
14 min read
Greedy AlgorithmsGreedy algorithms are a class of algorithms that make locally optimal choices at each step with the hope of finding a global optimum solution. At every step of the algorithm, we make a choice that looks the best at the moment. To make the choice, we sometimes sort the array so that we can always get
3 min read
Graph AlgorithmsGraph is a non-linear data structure like tree data structure. The limitation of tree is, it can only represent hierarchical data. For situations where nodes or vertices are randomly connected with each other other, we use Graph. Example situations where we use graph data structure are, a social net
3 min read
Dynamic Programming or DPDynamic Programming is an algorithmic technique with the following properties.It is mainly an optimization over plain recursion. Wherever we see a recursive solution that has repeated calls for the same inputs, we can optimize it using Dynamic Programming. The idea is to simply store the results of
3 min read
Bitwise AlgorithmsBitwise algorithms in Data Structures and Algorithms (DSA) involve manipulating individual bits of binary representations of numbers to perform operations efficiently. These algorithms utilize bitwise operators like AND, OR, XOR, NOT, Left Shift, and Right Shift.BasicsIntroduction to Bitwise Algorit
4 min read
Advanced
Segment TreeSegment Tree is a data structure that allows efficient querying and updating of intervals or segments of an array. It is particularly useful for problems involving range queries, such as finding the sum, minimum, maximum, or any other operation over a specific range of elements in an array. The tree
3 min read
Pattern SearchingPattern searching algorithms are essential tools in computer science and data processing. These algorithms are designed to efficiently find a particular pattern within a larger set of data. Patten SearchingImportant Pattern Searching Algorithms:Naive String Matching : A Simple Algorithm that works i
2 min read
GeometryGeometry is a branch of mathematics that studies the properties, measurements, and relationships of points, lines, angles, surfaces, and solids. From basic lines and angles to complex structures, it helps us understand the world around us.Geometry for Students and BeginnersThis section covers key br
2 min read
Interview Preparation
Practice Problem