πŸŽ’
Technical Interview Study Guide
  • πŸ•Welcome!
  • πŸ₯Getting Started
    • Study Plan
    • Optimizing Revision
    • Summer 2024 Timeline
    • FAQs
  • πŸ₯¨Algorithms
    • Binary Search
    • Sorting
    • Recursion
    • Graph
    • Quick Select
    • Intervals
    • Binary
    • Geometry
    • Dynamic Programming
  • πŸ₯žData Structures
    • Arrays
      • Matrices
    • Strings
    • Linked Lists
      • Doubly Linked Lists
    • Hash Tables
    • Graphs
      • Trees
        • Binary Search Trees
        • Heaps
        • Tries
        • Segment Trees
    • Stacks
    • Queues
      • Double Ended Queues
    • Union-Find Disjoint Set (UFDS)
  • 🍑Problems Guide
    • Dynamic Programming Roadmap
      • Warmup
        • Climbing Stairs
        • Nth Tribonacci Number
        • Perfect Squares
      • Linear Sequence
        • Min Cost to Climb Stairs
        • Minimum Time to Make Rope Colorful
        • House Robber
        • Decode Ways
        • Minimum Cost for Tickets
        • Solving Questions with Brainpower
  • 🍣Other Technical Topics
    • General Problem Solving
    • Runtime Predictions
    • System Design
      • SQL
      • Accessing APIs
    • Operating Systems
  • 🍿Non-technical Topics
    • Behavioral Interviews
    • Resumes
Powered by GitBook
On this page
  • Runtime analysis
  • Corner cases
  • Take note…
  • Techniques
  • Sorting first
  • Two pointers
  • Sliding window
  • Reverse traversal
  • Prefix/Suffix sums
  • Using index as key/Treating indices as buckets
  • Kadane's algorithm
  • Divide and conquer
  • Iteration patterns
  1. Data Structures

Arrays

Array problems are by far the most common type of algorithm problems I have encountered. There are a few techniques that are used very commonly so it is best to familiarize yourself with all of them

PreviousDynamic ProgrammingNextMatrices

Last updated 1 year ago

Runtime analysis

  • Insert

    • Start/End: O(1)O(1)O(1)

    • Middle: O(n)O(n)O(n)

  • Search

    • Sorted (binary search): O(log⁑n)O(\log n)O(logn)

    • Unsorted (linear search): O(n)O(n)O(n)

  • Delete: similar to insert

  • Access: O(1)O(1)O(1)

Corner cases

  1. Empty array

  2. Array of size 1 or 2

  3. Monotonically increasing/decreasing

  4. Duplicate values

  5. Consecutive repeated elements

  6. Odd number of elements

Take note…

  1. Handling duplicate entries

  2. Is the array sorted?

    • If so, can you use Binary Searchinstead?

    • If not, can you sort it while preserving information?

  3. Do not over-concatenate

    • Use append and then copy the accumulation using acc[::] to reduce the amount of concatenation, refer to Recursionfor more information on this technique

  4. Relationship between elements in an array such as the information that can be gathered based on the values/indices of the elements

Techniques

Sorting first

This depends entirely on whether or not the information in the array can be preserved after sorting (take note of the effects of stable vs unstable sorting).

If the array can be sorted first, Binary Searchcould be applied afterwards

  • Useful when finding the minimum difference between all elements and finding the lowest possible running total using prefix sums (commonly found in subsequence problems)

If the array cannot be sorted without losing information, then try other solutions

Two pointers

Pointers that point to different/same points in the array and move (often) independently of one another

  • Can be used to traverse across two arrays simultaneously

  • Pointers can cross one another

  • Commonly used for problems that involve palindromes

When solving two pointer problems, focus on realizing the following conditions (similar to Binary Search):

  1. When to move the left pointer?

  2. When to move the right pointer?

  3. Where to move if neither condition is met?

Sliding window

Sliding windows are simply two pointers that never cross one another. They can be of a fixed size (useful if the window size is pre-defined) or a flexible size (resizing depending on conditions)

To understand how the window moves, first answer the following questions:

  1. Is the window fixed?

  2. When to expand right?

  3. When to retract left? (some problems don't require retracting the left at all)

  4. What does each window represent?

  5. What to do when an element enters/leaves the window?

There are two categories of problem that can be solved using sliding windows, namely:

Maximizing problems

The final answer is usually the size of the sliding window since the starting position or contents of the window does not matter

start = 0
for i in range(len(nums)):
	# Always extend
	window.append(nums[i])
	# Shift to the right
	if not condition:
		window.remove(nums[start])
		start += 1
return len(nums) - start

Minimizing problems

The size of the window should be tracked after each retraction and that represents the final answer

window, i, j = 0, 0, 0
while j < len(nums):
	# Extend first
	window += nums[j]
	# Retract till the current window is just right
	while condition:
		window -= nums[i]
		i += 1
		ans = min(ans, j - i + 1) # window size
	j += 1

Reverse traversal

Rather than traversing from left to right, try traversing from right to left and see if new information can be gathered instead

  • Try combining both normal and reverse traversal together to see if the problem can be solved that way

Prefix/Suffix sums

Often used to reduce repeated computation that requires the prefix/suffix of the array

  • Not limited to the sum, product and other properties are possible as well

  • May require both sums so try that out as well

    • If both are required, there's a chance that the one of the arrays can be converted to an accumulating variable that is computed to save both time and space

  • Pay extra attention to whether or not the prefix should include the value at i

# Prefix sums
prefix = [0] * len(arr)
prefix[0] = arr[0]
for i in range(1, len(arr)):
    prefix[i] = prefix[i - 1] + arr[i]
    
# Suffix sums
suffix = [0] * len(arr)
suffix[-1] = arr[-1]
for i in range(len(arr) - 2, -1, -1):
    suffix[i] = suffix[i + 1] + arr[i]

Using index as key/Treating indices as buckets

Sometimes, you can visualize the array as a set of buckets where the index corresponds to a key/bucket and allows you to use this information to perform computation. This pattern is quite hard to spot so I have included some questions you can try to develop the intuition for

  • Each index represents an element in the array relative to that position (like index 0 is supposed to be element 1)

  • Algorithms like cyclic sorting use this assumption

  • Common manipulations: negate the value in the cell to indicate that the value is present in some other part of the array or adding the MAX + 1 to those values

Practice questions:

  • First Missing Positive

  • Find the Duplicate Number

  • Missing Number

Kadane's algorithm

Focus on understanding how the accumulation of values can be generalized across elements

  • Often involves tracking a local value (running variable) and global value (final result)

  • Update the global value after every iteration to avoid missing the last element

Divide and conquer

Divide the existing array into two/N parts and perform simpler work on each part. This technique is the corner stone of merge sort

Iteration patterns

  • Iterate over one array while moving the other with a pointer

  • Jumping around the array given the current value

Index out of bounds, check with: 0β‰€βˆ£x∣<len(arr)0 \leq |x| < len(arr)0β‰€βˆ£x∣<len(arr)

Sliding window problems are often paired with frequency arrays like

The sliding window never shrinks, meaning that it is only extending by one or shifting by one, such as in

The sliding window retracts until the window no longer satisfies a given condition, such as

This is a relatively niche algorithm used in problems like and . However, it can still be generalized to solve other problems

πŸ₯ž
Longest Substring Without Repeating Character
Maximum Product Subarray
Minimum Window Substring
Maximum Product Subarray
Maximum Subarray