Problems

For loop

Simple for loop

int a = 0, b = 0;    
for (i = 0; i < N; i++) {
    a = a + 5;  
}
for (j = 0; j < M; j++) {
    b = b + 5;
}
//Time O(N+M), Space O(1)

Nested for loop

int a = 0, b = 0; 
for (i = 0; i < N; i++) { 
    for (j = 0; j < N; j++) { 
        a = a + j; 
    } 
} 
for (k = 0; k < N; k++) { 
    b = b + k; 
}
//Time O(N^2), Space O(1)
int a = 0;
for (i = 0; i < N; i++) {
    for (j = N; j > i; j--) {
        a = a + i + j;
    }
}
//Time O(N^2), Space O(1)
int count = 0;
for (int i = N; i > 0; i /= 2) {     // Outer loop
    for (int j = 0; j < i; j++) {    // Inner loop
        count += 1;
    }
}
//Time O(N), Space O(1)
int i, j, k = 0;
for (i = n / 2; i <= n; i++) {            // Outer loop
    for (j = 2; j <= n; j = j * 2) {      // Inner loop
        k = k + n / 2;                    // Body
    }
}
//Time O(Nlog(N)), Space O(1)

Outer Loop

The outer loop runs with the variable i, starting at n/2 and going up to n. Thus, the number of iterations of the outer loop is:

Number of iterations of outer loop=nโˆ’n/2+1=n/2+1

This is approximately O(n) since the dominant term is n/2, which scales linearly with n.

Inner Loop

The inner loop runs with the variable j, starting at 2 and doubling its value in each iteration (j=jโˆ—2). Thus, the values of j are: 2,4,8,16,โ€ฆ,n

The number of iterations in the inner loop is determined by how many times j can be doubled before it exceeds n. This is equivalent to the number of times n can be divided by 2, which is:

Number of iterations of inner loop=logโก(n)

Thus, the inner loop runs O(logโกn) times.

Total Iterations

To calculate the total number of iterations of the inner loop across all iterations of the outer loop:

  • The outer loop runs O(n/2) โ‰ˆ O(n) times.

  • For each iteration of the outer loop, the inner loop runs O(logn) times.

Thus, the total number of iterations is: O(n)โ‹…O(logโกn)=O(nlogโกn)

void printPairs(int[] array) {
    for (int i= 0; i < array.length; i++) {
        for (int j = 0; j < array.length; j++) {
            System.out.println(array[i] + "," + array[j]);
        }
    }
}
//Time O(N^2), Space O(1)
void printUnorderedPairs(int[] arrayA, int[] arrayB) {
    for (int i= 0; i < arrayA.length; i++) {
        for (int j = 0; j < arrayB.length; j++) {
            for (int k = 0; k < 100000; k++) {
                System.out.println(arrayA[i] + "," + arrayB[j]);
            }
        }
    }
}
// 100,000 units of work is still constant, so the runtime is 0(a b).
//Time O(ab), Space O(1)

Check if Prime number

boolean isPrime(int n) {
    for (int x = 2; x * x <= n; x++) {
        if (n % X == 0) {
            return false;
        }
    }
    return true;
}
//Time O(sqrt(n)), Space O(1)

Calculate Square root

int sqrt(int n) {
    for (int guess = 1; guess * guess <= n; guess++) {
        if (guess * guess == n) {
            return guess;
        }
    }
    return -1;
}
//Time O(sqrt(n)), Space O(1)

Array Copy

int[] copyArray(int[] array) {
    int[] copy= new int[0];
    for (int value : array) {
        copy= appendToNew(copy, value);
    }
    return copy;
}

int[] appendToNew(int[] array, int value) {
    // copy all elements over to new array
    int[] bigger= new int[array.length + 1];
    for (int i= 0; i < array.length; i++) {
        bigger[i] = array[i];
    }

    // add new element
    bigger[bigger.length - 1]
    return bigger;
}

// Time O(n^2), Space O(n^2)

While loop

Simple while loop

int a = 0, i = N;
while (i > 0) {
    a += i;
    i /= 2;
}
//Time O(log(N)), Space O(1)
int div(int a, int b) {
    int count = 0;
    int sum = bยท ,
    while (sum <= a) {
        sum += b;
        count++;
    }
    return count;
}
//Time O(a/b), Space O(1)

Sum of Digits

int sumDigits(int n) {
    int sum = 0;
    while (n > 0) {
        sum += n % 10;
        n /= 10;
    }
    return sum;
}
//Time O(logn), Space O(1)

Recursion

Divide and Conquer Method

int search(vector<int> &V, int k, int start, int end) {
    if (start > end) return 0;
    int mid = (start + end) / 2;
    if (V[mid] < k) return search(V, k, mid + 1, end);
    if (V[mid] > k) return search(V, k, start, mid - 1);
    return search(V, k, start, mid - 1) + 1 + search(V, k, mid + 1, end);
}
// Time Complexity O(N), Space Complexity O(N)

The function searches for the occurrences of the element k in a sorted vector V using a recursive approach.

  1. Base Case: If the start index exceeds the end index, it returns 0 (no match found).

  2. Divide and Conquer:

    • If V[mid]<k, search the right half of the array (mid+1 to end).

    • If V[mid]>k, search the left half of the array (start to midโˆ’1).

  3. When V[mid]==k:

    • Count this occurrence of k (+1) and recursively search both the left and right halves to count all other occurrences.

Time Complexity Analysis

Minimum Path

The function findMinPath computes the minimum path sum from the top-left corner (0,0) to the bottom-right corner (Rโˆ’1,Cโˆ’1) of a matrix V by recursively exploring the possible paths. At every cell, the function can move:

  • Down (r+1,c)

  • Right (r,c+1)

The function returns the minimum sum of the path.

int findMinimumPath(vector<vector<int> > &V, int r, int c) {
  int R = V.size();
  int C = V[0].size();
  if (r >= R || c >= C) return 100000000; // Infinity
  if (r == R - 1 && c == C - 1) return 0;
  return V[r][c] + min(findMinimumPath(V, r + 1, c), findMinimumPath(V, r, c + 1));
}
// Time Complexity O(2^(R+C)), Space Complexity O(R+C)

Time Complexity Analysis

Minimum Path with Dynamic Programming (DP)

The above implementation recomputes the results for overlapping subproblems, which makes it inefficient. To improve the time complexity, we can use Dynamic Programming (DP) with memoization or tabulation.

int findMinPath(vector<vector<int>> &V, int r, int c, vector<vector<int>> &dp) {
  int R = V.size();
  int C = V[0].size();
  if (r >= R || c >= C) return 100000000; // Infinity
  if (r == R - 1 && c == C - 1) return 0;
  if (dp[r][c] != -1) return dp[r][c]; // Use memoized result
  return dp[r][c] = V[r][c] + min(findMinPath(V, r + 1, c, dp), findMinPath(V, r, c + 1, dp));
}
// Time Complexity O(RxC), Space Complexity O(RxC+R+C)

Exponential Growth

int f(int n) {
    if (n <= 1) {
        return 1;
    }
    return f(n - 1) + f(n - 1);
}
// Time Complexity O(2^n), Space Complexity O(n)

There will be 2^0 + 2^1 + 2^2 + 2^3 + 2^4 + . . + 2^N (which is 2^(N+1) - 1) nodes.

Sum of all values of Balanced Binary Search Tree

The function performs a recursive in-order traversal of a binary tree:

  • It visits each node once (O(N) time complexity).

  • Uses recursive calls to traverse the left and right subtrees.

int sum(Node node) {
    if (node == null) {
        return 0;
    }
    return sum(node.left) + node.value + sum(node.right);
}
// Time Complexity O(N), Space Complexity = O(log N) , N is the number of nodes.

Factorial of a number

int factorial(int n) {
    if (n < 0) {
        return -1;
    } else if (n
        return 1;
    } else {
        return n * factorial(n - 1);
    }
}
//Time O(N), Space O(N)

Permutations of a string

void permutation(String str) {
    permutation(str, "");
}

void permutation(String str, String prefix) {
    if (str.length() == 0) {
        System.out.println(prefix);
    } else {
        for (int i= 0; i < str.length(); i++) {
            String rem = str.substring(0, i) + str.substring(i + 1);
            permutation(rem, prefix + str.charAt(i));
        }
    }
}
//Time O(N^2*N!), Space O(N)

Time Complexity

Fibonacci Number

int fib(int n) {
    if (n <= 0) return 0;
    else if (n == 1) return 1;
    return fib(n - 1) + fib(n - 2);
}
//Time O(2^N), Space O(N)

Time Complexity O(branches^depth)

All Fibonacci numbers from 0 to n with recursion

void allFib(int n) {
    for (int i= 0; i < n; i++) {
        System.out.println(i + ": "+ fib(i));
    }
}

int fib(int n) {
    if (n <= 0) return 0;
    else if (n == 1) return 1;
    return fib(n - 1) + fib(n - 2);
}

//Time O(2^N), Space O(N)

All Fibonacci from 0 to n numbers with memoization

void allFib(int n) {
    int[] memo = new int[n + 1];
    for (int i= 0; i < n; i++) {
        System.out.println(i + ": "+ fib(i, memo));
    }
}

int fib(int n, int[] memo) {
    if (n <= 0) return 0;
    else if (n == 1) return 1;
    else if (memo[n] > 0) return memo[n];

    memo[n] = fib(n - 1, memo)+ fib(n - 2, memo);
    return memo[n];
}
//Time O(N), Space O(N)

Time Complexity

Powers of 2 from 1

int powers0f2(int n) {
    if (n < 1) {
        return 0;
    } else if (n == 1) {
        System.out.println(l);
        return 1;
    } else {
        int prev = powers0f2(n / 2);
        int curr = prev * 2;
        System.out.println(curr);
        return curr;
    }
}
//Time O(logN), Space O(logN)

Space Complexity Analysis

Compute a^b with recursion

int power(int a, int b) {
    if (b < 0) {
        return 0;
    } else if (b == 0) {
        return 1;
    } else {
        return a * power(a, b - 1);
    }
}
//Time O(b), Space O(b)

Square root of a number by guessing

int sqrt(int n) {
    return sqrt_helper(n, 1, n);
}
int sqrt_helper(int n, int min, int max) {
    if (max < min) return -1;
    int guess = (min + max) I 2ยท,
    if (guess *guess == n) {
        return guess;
    } else if (guess * guess < n) {
        return sqrt_helper(n, guess + 1, max);
    } else { II too high
        return sqrt_helper(n, min, guess - l);
    }
}
//Time O(logn), Space O(logn)

Miscellaneous Problems

GCD with Euclidean algorithm

This is Euclidean algorithm which repeatedly replaces the pair (n, m) with (m , n mod โ€‰m) until m=0. The result is the greatest common divisor (GCD) of n and m .

int gcd(int n, int m) {
    if (n % m == 0) return m;         // Base case
    if (n < m) swap(n, m);           // Ensure n >= m
    while (m > 0) {                  // Loop until m becomes 0
        n = n % m;                   // Compute remainder
        swap(n, m);                  // Swap n and m
    }
    return n;                        // Return the GCD
}
//Time O(log(N)), Space O(1)

Worst-Case Example

Different For loops

Consider the following for loops:
  A) for(i = 0; i < n; i++)
  B) for(i = 0; i < n; i += 2)
  C) for(i = 1; i < n; i *= 2)
  D) for(i = n; i > -1; i /= 2)
If n is the size of input(positive), which function is the most efficient?
-> C

A) for (i = 0; i < n; i++)

  • Loop Analysis:

    • The loop starts at i=0 and increments by 1 (i++) until i<n.

    • The number of iterations is directly proportional to nn.

  • Time Complexity: O(n)

B) for (i = 0; i < n; i += 2)

  • Loop Analysis:

    • The loop starts at i=0 and increments by 2 (i+=2) until i<n.

    • Since the increment step is 2, the number of iterations will be approximately half the size of n. Number of iterations=โŒˆn/2โŒ‰

  • Time Complexity: O(n)

Although the number of iterations is halved compared to Case A, the time complexity remains O(n) because constants are ignored in Big-O notation.

C) for (i = 1; i < n; i = 2)

  • Loop Analysis:

    • The loop starts at i=1 and multiplies by 2 (iโˆ—=2) until i<n.

    • The value of i follows the sequence: 1,2,4,8,โ€ฆ The loop runs while i<n.

    • The number of iterations corresponds to the number of times ii can be doubled before it exceeds nn:Number of iterations=โŒŠlogโก2(n)โŒ‹

  • Time Complexity: O(logโกn)

This is more efficient than both A and B because the growth rate is logarithmic, much smaller than linear.

D) for (i = n; i > -1; i /= 2)

  • Loop Analysis:

    • The loop starts at i=n and divides i by 2 (i/=2) until i>โˆ’1.

    • The value of i follows the sequence: n,n/2,n/4,โ€ฆn. The loop runs while i>โˆ’1. Loop will not end since i will stuck at 0.

Check the boundness

Which is not bounded by O(n^2)?
1. (10^10) * n + 12099
2. n^1.95
3. n^3 / (sqrt(n))
4. (2^30) * n

-> 3

Check the growth rate

Order in increasing order of complexity of functions f1, f2, f3 and f4:
f1(n) = 2^n
f2(n) = n^(3/2)
f3(n) = nLogn
f4(n) = n^(Logn)
-> f3, f2, f4, f1

For and While loop

int j = 0;
for(int i = 0; i < n; ++i) {
  while(j < n && arr[i] < arr[j]) {
    j++;
  }
}
//Time O(N), Space O(1)

Outer Loop

Which of the following are equivalent to O(N)?

O(N + P), where P < X 0(2N) O(N + log N) O(N + M)

All but the last one are equivalent to O(N) because there is no established relationship between N and M, so we have to keep both variables in there.

String Array Sorting

Suppose we had an algorithm that took in an array of strings, sorted each string, and then sorted the full array. What would the runtime be?

Let s be the length of the longest string. Let a be the length of the array.

Sorting each string is 0( s log s). We have to do this for every string (and there are a strings), so that's 0( a* s log s). Now we have to sort all the strings. There are a strings, so we'll may be inclined to say that this takes O ( a log a) time. We should also take into account that we need to compare the strings. Each string comparison takes O(s) time. There are O(a log a) comparisons, therefore this will take 0( a*s log a) time.

Then runtime will be 0( a* s ( log a + log s)).

Intersection (the number of elements in common) of two arrays

int intersection(int[] a, int[] b) {
    mergesort(b);
    int intersect = 0;
    for (int x : a) {
        if (binarySearch(b, x) >= 0) {
            intersect++;
        }
    }
    return intersect;
}
//Time O(blogb + alogb), Space O(b) (as b is being sorted using mergesort)

Last updated

Was this helpful?