FINAL INTERVIEW PREP

What to prepare for?

Important

The interview will mainly be composed on data structures, time complexity, space complexity as well as general problem solving around this domain. What makes it difficult is the fact that you will be handling questions instead of direct answers, and conclusively you will need to provide your answers through pseudocode.

So the path to study is like so:

  1. Big "O" notation - Done
  2. Data structures
    1. Arrays - Done
    2. Linked Lists - Done
    3. Stacks - Done
    4. Queues - Done
    5. Trees - Done
  3. Time complexity - Done
  4. Space Complexity - Done
  5. Pseudocode Implementations - Partially Done

Important Concepts

arr = [1,2,3] 
"""
The array size is 3 giving a space complexity of O(3)
But in the grand scheme of things this may be considered as to having
A constant space complexity of O(1)
"""

1. Constant Time: O(1)

The algorithm takes the exact same amount of time regardless of how much data you have.

def get_first_student(students):
    return students[0]  # Takes 1 step whether there are 10 or 10,000 students

2. Logarithmic Time: O(logn)

The algorithm cuts the search space in half with every single step. It is incredibly fast and scales beautifully with massive amounts of data.

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

3. Linear Time: O(n)

The time grows in a direct, straight line 1-to-1 with the size of your data. If your data doubles, the time doubles.

def find_student(students, target_name):
    for student in students:
        if student == target_name:
            return True
    return False

4. Quasilinear Time: O(nlogn)

This happens when you perform a logarithmic operation (O(logn)) a linear (n) number of times. This is the gold standard speed for efficient sorting.

def sort_students(students):
    return sorted(students)  # Built-in Timsort/Merge Sort runs in O(n log n)

5. Quadratic Time: O(n2)

The execution time is proportional to the square of the input size. If your data doubles, your execution time quadruples (22=4). If data triples, time multiplies by 9. This usually indicates nested loops.

def print_all_pairs(students):
    for i in range(len(students)):          # Runs n times
        for j in range(len(students)):      # Runs n times inside the outer loop
            print(students[i], students[j])

6. Exponential Time: O(2n)

The time doubles with every single addition to the input size. If n=10, it takes around 1,000 steps. If n=20, it takes around 1,000,000 steps. This becomes practically impossible to run very quickly.

def recursive_fibonacci(n):
    if n <= 1:
        return n
    # Spawns two new function calls for every single call!
    return recursive_fibonacci(n - 1) + recursive_fibonacci(n - 2)

7. Factorial Time: O(n!)

The absolute slowest common time complexity. It grows so blindingly fast that even an input size of n=20 could take a modern computer thousands of years to finish.

import itertools

def get_all_permutations(students):
    # Generates n! permutations. 
    # If you have just 13 students, this generates over 6 billion combinations!
    return list(itertools.permutations(students))