# Big O Notation/Time Complexity - Space Complexity

Andrés Bedoya G.

Social Engineering Specialist / Social Analyzer / Web Developer / Hardcore JavaScript Developer / Python - Nodejs enthusiastic

# Big O Notation

WARNING

This section contains some math

Don't worry, we'll survive

# Big O Notation

Imagine we have multiple implementations of the same function.

How can we determine which one is the "best"?

# Big O Notation

Suppose we want to write a function that calculates the sum of all numbers from 1 up to (and including) some number *n*.

```
function addUpTo(n) {
let total = 0;
for (let i = 1; i <= n; i++) {
total += i;
}
return total;
}
```

```
function addUpTo(n) {
return n * (n + 1) / 2;
}
```

Which one is better?

# Big O Notation

` addUpTo(n) = 1 + 2 + 3 + ... + (n - 1) + n `

`+ addUpTo(n) = n + (n - 1) + (n - 2) + ... + 2 + 1 `

```
2addUpTo(n) = (n + 1) + (n + 1) + (n + 1) + ... + (n + 1) + (n + 1)
```

*n* copies

`2addUpTo(n) = n * (n + 1)`

**addUpTo(n) = n * (n + 1) / 2**

# Big O Notation

```
function addUpTo(n) {
let total = 0;
for (let i = 1; i <= n; i++) {
total += i;
}
return total;
}
let t1 = performance.now();
addUpTo(1000000000);
let t2 = performance.now();
console.log(`Time Elapsed: ${(t2 - t1) / 1000} seconds.`)
```

Timers...

# Big O Notation

# If not time, then what?

Rather than counting *seconds*, which are so variable...

Let's count the *number* of simple operations the computer has to perform!

# Big O Notation

# Counting Operations

```
function addUpTo(n) {
return n * (n + 1) / 2;
}
```

3 simple operations, regardless of the size of *n*

1 multiplication

1 addition

1 division

# Big O Notation

# Counting Operations

```
function addUpTo(n) {
let total = 0;
for (let i = 1; i <= n; i++) {
total += i;
}
return total;
}
```

n additions

n assignments

n additions and

n assignments

1 assignment

1 assignment

n comparisions

????

# Big O Notation

Big O Notation is a way to formalize fuzzy counting

**It allows us to talk formally about how the runtime of an algorithm grows as the inputs grow**

# Big O Notation

We say that an algorithm is **O(f(n))** if the number of simple operations the computer has to do is eventually less than a constant times **f(n)**, as **n** increases

- f(n) could be linear (f(n) = n)
- f(n) could be quadratic (f(n) = n2)
- f(n) could be constant (f(n) = 1)
- f(n) could be something entirely different!

# Big O Notation

```
function addUpTo(n) {
return n * (n + 1) / 2;
}
```

Always 3 operations

**O(1)**

```
function addUpTo(n) {
let total = 0;
for (let i = 1; i <= n; i++) {
total += i;
}
return total;
}
```

Number of operations is (eventually) bounded by a multiple of *n *(say, 10*n*)

**O(n)**

# Big O Notation

O(2n)

O(500)

O(13n2)

O(n)

O(1)

O(n2)

# Big O Notation

```
function countUpAndDown(n) {
console.log("Going up!");
for (let i = 0; i < n; i++) {
console.log(i);
}
console.log("At the top!\nGoing down...");
for (let j = n - 1; j >= 0; j--) {
console.log(j);
}
console.log("Back down. Bye!");
}
```

O(n)

O(n)

Number of operations is (eventually) bounded by a multiple of *n *(say, 2*n*)

**O( n)**

# Big O Notation

```
function printAllPairs(n) {
for (var i = 0; i < n; i++) {
for (var j = 0; j < n; j++) {
console.log(i, j);
}
}
}
```

O(n)

O(n)

O(n) operation inside of an O(n) operation.

**O( n * n)**

**O( n2)**

# Big O Notation

- Arithmetic operations are constant
- Variable assignment is constant
- Accessing elements in an array (by index) or object (by key) is constant
- In a loop, the complexity is the length of the loop times the complexity of whatever happens inside of the loop

# Big O Notation

# Big O Notation

**Big O of Objects**

- Insertion - O(1)

- Removal - O(1)

- Updating O(1)

- Searching - O(N)

- Access - O(1)

**Big O of Object Methods**

- Object.keys - O(N)

- Object.values - O(N)

- Object.entries - O(N)

- hasOwnProperty - O(1)

**Big O of Arrays**

- Insertion - It depends

- Removal - It depends

- Searching - O(N)

- Access - O(N)

**Big O of Array Methods**

- push - O(1)

- pop - O(1)

- shift - O(N)

- unshift - O(N)

- concat - O(N)

- slice - O(N)

- splice - O(N)

- sort - O(N * log N)

- forEach/map/filter/reduce/etc. - O(N)

# Space Complexity

So far, we've been focusing on **time complexity**: how can we analyze the *runtime* of an algorithm as the size of the inputs increases?

We can also use big O notation to analyze **space** **complexity**: how much additional memory do we need to allocate in order to run the code in our algorithm?

# Space Complexity

- Most primitives (booleans, numbers, undefined, null) are constant space
- Strings require O(
*n*) space (where*n*is the string length) - Reference types are generally O(
*n*), where n is the length (for arrays) or the number of keys (for objects)

# Space Complexity

```
function sum(arr) {
let total = 0;
for (let i = 0; i < arr.length; i++) {
total += arr[i];
}
return total;
}
```

one number

another number

O(1) Space

# Space Complexity

```
function double(arr) {
let newArr = [];
for (let i = 0; i < arr.length; i++) {
newArr.push(2 * arr[i]);
}
return newArr;
}
```

n numbers

O(n) Space

# Recap

- To analyze the performance of an algorithm, we use Big O Notation
- Big O Notation can give us a high level understanding of the time or space complexity of an algorithm
- Big O Notation doesn't care about precision, only about general trends (linear? quadratic? constant?)
- The time or space complexity (as measured by Big O) depends only on the algorithm, not the hardware used to run the algorithm
- Big O Notation is everywhere, so get lots of practice!

# More info

#### Big O Notation

By Andrés BG

# Big O Notation

Big O Notation/Time Complexity - Space Complexity

- 610