Efficient Programming : Time or Space? πŸ€”πŸ’­

KumarRupeshKumarRupesh
5 min read

Hello Reader πŸ‘‹

By the time you are reading these lines, thousands of codebases will get updated or restructured and deployed globally. Have you ever thought why?

But first thing first, this blog's meme is πŸ₯πŸ₯

Well, one answer could be technology like shifting to a new faster and better tech stack like moving from plain JS to React or in terms of today moving to Next JS from React. But the main question is WHY? Is this the only reason and Is it worth it to migrate projects with millions of lines of code?

The answer lies on the shoulders of several factors, some of which are:

  1. Efficiency of new code [Obviously]

  2. Code maintenance [Very considerable factor when considering when one has to maintain a ton of code and functions even with documentation]

  3. The control over the resources or breadth of exact authority over the usage of resources for minimal requirements leading to minimal cost of running [Business saves more 🀩]

So, what are we going to discuss here? Dissing Tech Stack ain't allowed hereπŸ₯Ί [JK we will πŸ˜‚ , but not today, cuz competition is awesome and brings the best at the top. But It ain't in scope here] We will be focusing here on the root of core programming, yes you might have guessed it right, starting from the legends themselves, C++, JAVA , basically any language that requires some CPU and memory to run its code, so let's go πŸ₯³

So First let's understand what Time and Space complexity is and how exactly it impacts business!!

Time Complexity is exactly what it says, the complexity of getting some work done in the least time possible, but what's this work we are talking about? The answer is our code, the algorithm we will be using to solve the problem 🀩

Let's say we have an unsorted array of integers, and we want to find if there are any duplicate values in the array. A straightforward approach would be to use nested loops to compare each element with every other element in the array:

public boolean hasDuplicates(int[] arr) {
    for (int i = 0; i < arr.length - 1; i++) {
        for (int j = i + 1; j < arr.length; j++) {
            if (arr[i] == arr[j]) {
                return true;
            }
        }
    }
    return false;
}

In this example, the time complexity of the hasDuplicates method is O(n^2), where n is the size of the input array. This means that the time it takes to execute the method will increase quadratically with the size of the array.

To optimize this method, we can use a data structure like a Set to keep track of the unique elements encountered so far. We can iterate through the array once and check if each element is already present in the Set. If it is, then we can conclude that there are duplicates in the array.

Here's the optimized version using a Set:

public boolean hasDuplicates(int[] arr) {
    Set<Integer> set = new HashSet<>();
    for (int num : arr) {
        if (set.contains(num)) {
            return true;
        }
        set.add(num);
    }
    return false;
}

In this optimized version, the time complexity is reduced to O(n) because we only iterate through the array once. The Set data structure allows us to check for duplicates in constant time on average, resulting in a more efficient solution.

By using a Set to keep track of unique elements, we avoid the need for nested loops and significantly improve the performance, especially for larger arrays.

So this was time complexity and established some baseline 😁

Now let's see space complexity

Suppose we have a method called fibonacci that calculates the nth Fibonacci number recursively. Here's a straightforward implementation:

public int fibonacci(int n) {
    if (n <= 1) {
        return n;
    }
    return fibonacci(n - 1) + fibonacci(n - 2);
}

In this example, the space complexity of the fibonacci method is O(n), where n is the input number. This is because the recursive implementation of the Fibonacci sequence requires additional space on the call stack for each recursive call.

As the input number increases, the depth of the recursive calls also increases, leading to a linear increase in the amount of stack space required. This results in a space complexity of O(n).

To optimize the space complexity, we can use an iterative approach to calculate the Fibonacci sequence. By keeping track of only the last two Fibonacci numbers, we can avoid the need for recursive calls and reduce the space usage.

Here's the optimized iterative implementation:

public int fibonacci(int n) {
    if (n <= 1) {
        return n;
    }

    int prev1 = 0;
    int prev2 = 1;
    int fib = 0;

    for (int i = 2; i <= n; i++) {
        fib = prev1 + prev2;
        prev1 = prev2;
        prev2 = fib;
    }

    return fib;
}

In this optimized version, we use three variables to keep track of the previous two Fibonacci numbers and the current Fibonacci number. By iteratively updating these variables, we can calculate the nth Fibonacci number without the need for recursive calls.

This optimized implementation has a space complexity of O(1), as the amount of additional space required remains constant regardless of the input number.

Optimizing space complexity is crucial when dealing with large inputs or limited memory environments. It allows for more efficient memory usage, enabling algorithms to handle larger datasets and run more quickly.

AAAaannnd this is space complexity, hope this helps you reader!!!

All the best and Take Care πŸ˜πŸ§‘β€πŸ’»πŸ‘‹

0
Subscribe to my newsletter

Read articles from KumarRupesh directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

KumarRupesh
KumarRupesh