Time and Space Complexity - Big O notation

Time Complexity

Time Complexity is the amount of time taken by a computer program to run a piece of code. It is a helpful technique used by software engineers to estimate how efficient their program is.

How to find time complexity of a program?
Add up the instructions it will execute as a function of the size of input where the input is a very large value.

What does that mean?
Here is an example, below is a small function which prints numbers in increasing order starting from 0 to the number passed to the function.

/**
 * @param {n} 
 */
var printNums= function(n) {
    for(i = 0; i <= n; i++){
       console.log(i);
    }    
};

The number of instructions executed by this program is n + 1 since the for loop is executed n + 1 times. Now imagine if n is a very large number like 1 million. The value of n is a very large amount as compared to 1 in n+1 so we ignore the constant value 1 and say that our time complexity is ORDER OF n or O(n).

Lets use another example to see how you can use the concept of time complexity in making your code perform faster.

Given an array of integers, return indices of the two numbers such that they add up to a specific target.

You may assume that each input would have exactly one solution, and you may not use the same element twice.


Given nums = [2, 7, 11, 15], target = 9,

Because nums[0] + nums[1] = 2 + 7 = 9,
return [0, 1].

A simple way to solve this problem will be to add each element in the array with each other in all possible combinations to find the target value.

/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number[]}
 */
var twoSum = function(nums, target) {
    let len = nums.length;
    for(let i = 0; i < len; i++){
       for(let j=1; j<len;j++) 
       if((nums[i]+nums[j]) === target && i !== j) {
          return [i,j];  
       }      
    }
};

Now lets analyze the time complexity of the above function:
Step 1: Count number of instructions execute in the code 2 + len2
Step 2: Remove the constant value, so here we remove 2, the result is len2
Step 3: Assuming that n = len our time complexity for this algorithm is  O(n2)

How can we make this function better? 
We know that HashMap has O(1) time complexity. Can we store the array in a HashMap so that we can avoid the nested for loop and reduce our time complexity to O(n)?

Lets try again:
/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number[]}
 */
var twoSum = function(nums, target) {
    let len = nums.length;
    let numsMap = new Map();
    for(let i = 0; i < len; i++){
           numsMap.set(nums[i],i);
    }
     
    for(let i = 0 ; i < len; i++){
        let numVal = target - nums[i];
        if(numsMap.has(numVal) && numsMap.get(numsVal) !== i){
           return  [i, numsMap.get(numsVal)];          
        }
    
};

Now lets analyze the time complexity of the above function again:
Step 1: Count number of instructions execute in the code 2 + 2len
Step 2: Remove the constant values,  so the result is len
Step 3: Assuming that n = len our time complexity for this algorithm is O(n)

Useful video related to time complexity:

Space Complexity

Space Complexity of a computer program is the amount of memory used by the program relative to the input.

How to find space complexity of a program?
Find out the variables used in the program and calculate how the size of the variable changes relative to the input.

What does that mean?
Here is an example, below is a small function which takes in two numbers as parameters and returns the sum.

/**
 * @param {num1, num2} 
 */
var sumNums= function(num1, num2) {
   return num1 + num2 ;   
};

There are no variables assigned in this program so the memory usage of this program does not increase relative to the inputs num1 and num2 so the space complexity of the above function is O(1) or constant.

Lets use another example to see how we can use the concept of space complexity to figure out the memory usage of your program relative to the input to or program.

Given an array of integers, return indices of the two numbers such that they add up to a specific target.

You may assume that each input would have exactly one solution, and you may not use the same element twice.


Given nums = [2, 7, 11, 15], target = 9,

Because nums[0] + nums[1] = 2 + 7 = 9,
return [0, 1].
/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number[]}
 */
var twoSum = function(nums, target) {
    let len = nums.length;
    let numsMap = new Map();
    for(let i = 0; i < len; i++){
           numsMap.set(nums[i],i);
    }
     
    for(let i = 0 ; i < len; i++){
        let numVal = target - nums[i];
        if(numsMap.has(numVal) && numsMap.get(numsVal) !== i){
           return  [i, numsMap.get(numsVal)];          
        }
    
};

Here we are using a variable numsMap which increases in size as the size on the nums array increases so assuming that the size of the input array is 'n' the space complexity is O(n). 

Useful links: