Big O Notation: Understanding Time and Space Complexity
Tags: bigo, timecomplexity, spacecomplexity, python
Published: 2024-02-03 13:41:13

Comprehending algorithm efficiency is essential in the fields of computer science and algorithm analysis. When describing the time and space complexity of an algorithm’s performance, Big O Notation is a useful tool. We will examine the fundamentals of Big O Notation, consider its applicability to algorithm analysis, and offer useful Python examples in this blog.

**What is Big O Notation?**
Big O Notation is a mathematical notation used to describe the upper bound on the growth rate of an algorithm’s time and space complexity. It provides a high-level overview of how an algorithm’s performance scales with input size.

**Time Complexity**
Time complexity represents the amount of time an algorithm takes to complete, as a function of the input size. It is often expressed in terms of the “Big O” notation, denoted as O(f(n)), where f(n) is a mathematical function describing the growth rate.

**Space Complexity**
Space complexity measures the amount of memory space an algorithm uses in relation to the input size. Similar to time complexity, it is expressed using the Big O notation as O(f(n)).

Analysing how the resource requirements — time and space — increase with increasing input size is necessary to determine an algorithm’s time and space complexity. The method for determining both time and spatial complexity is as follows:

**Calculating Time Complexity:**

1. Identify Basic Operations:

Identify the fundamental operations performed in the algorithm. These are typically the operations that contribute the most to the overall running time.

2. Count Operations:

Count the number of basic operations performed as a function of the input size (usually denoted as ‘n’).

3. Express as a Function:

Express the count of operations as a mathematical function of ’n’. Ignore constants and lower-order terms, focusing on the dominant term.

4. Determine Big O Notation:

Identify the highest-order term in the function, which represents the algorithm’s time complexity. Express it using Big O notation.

**Calculating Space Complexity:**

1.Identify Memory Usage:

Identify the memory usage of the algorithm. This includes variables, data structures, and any additional space requirements.

2. Count Space Usage:

Count the number of memory units used (in terms of ’n’) during the execution of the algorithm.

3. Express as a Function:

Express the count of memory units as a mathematical function of ’n’. Similar to time complexity, ignore constants and lower-order terms.

4. Determine Big O Notation:

Identify the highest-order term in the function, representing the algorithm’s space complexity. Express it using Big O notation.

Example:
Consider the following Python function:

```
def example_algorithm(arr):
    total = 0
    for i in range(len(arr)):
        total += arr[i]
    return total
```
**Time Complexity Calculation:**

Basic Operation: Addition inside the loop.

Count Operations: The loop runs ’n’ times, where ’n’ is the length of the input array.

Express as a Function: f(n)=n (linear time complexity).
Big O Notation: O(n).

**Space Complexity Calculation:**

Memory Usage: Two integer variables (‘total’ and ‘i’).

Count Space Usage: Constant space usage, independent of the input size.

Express as a Function: f(n)=1 (constant space complexity).
Big O Notation: O(1).

_Hope you will find it easy to understand ❤_

.
.
.

Let’s explore some common time and space complexities with practical examples in Python:

**Constant Time Complexity (O(1))**

```
def constant_example(lst):
    return lst[0]

# Time complexity: O(1)
# Space complexity: O(1)
```
**Linear Time Complexity (O(n))**

```
def linear_example(lst):
    for item in lst:
        print(item)def quadratic_example(lst):
    for i in lst:
        for j in lst:
            print(i, j)

# Time complexity: O(n^2)
# Space complexity: O(1)

# Time complexity: O(n)
# Space complexity: O(1)
```
**Quadratic Time Complexity (O(n²))**

```
def quadratic_example(lst):
    for i in lst:
        for j in lst:
            print(i, j)

# Time complexity: O(n^2)
# Space complexity: O(1)
```

**Logarithmic Time Complexity (O(log n))**

```
def binary_search(arr, target):
    low, high = 0, len(arr) - 1
    while low <= high:
        mid = (low + high) // 2
        if arr[mid] == target:
            return mid
        elif arr[mid] < target:
            low = mid + 1
        else:
            high = mid - 1
    return -1

# Time complexity: O(log n)
# Space complexity: O(1)
```

**Big O Cheat Sheet**

Here’s a quick reference cheat sheet for common time complexities:

![Big O cheat sheet](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lkthyt2p7oodwtdsz1yn.png)

**Tips for Analysing Complexity**

- Simplify Expressions: Focus on the dominant term and coefficients in Big O expressions.
- Worst-case Scenario: Analyse the algorithm’s performance in the worst-case scenario.
- Ignore Constants: Disregard constant factors; Big O describes the growth rate, not precise times or sizes.
- Use Built-in Functions Wisely: Leverage built-in functions and libraries with known complexities.
- To sum up, becoming proficient in Big O Notation is an important ability for any programmer. It helps you to choose and create algorithms with greater knowledge, which eventually results in software solutions that are more scalable and effective. Have fun with coding!

**Conclusion:**

To sum up, becoming proficient in Big O Notation is an important ability for any programmer. It helps you to choose and create algorithms with greater knowledge, which eventually results in software solutions that are more scalable and effective. Have fun with coding <3!