Algorithm Implementations - Python Flashcards

Core algorithm patterns and approaches for technical interviews. Language-agnostic focus on pattern recognition, pseudocode, complexity analysis, and edge cases. Use this deck to understand when and how to apply each algorithm (29 cards)

1
Q

Two Pointers - Opposite Ends (Converging)

A

Python Implementation

ef two_pointers(arr1):
    left = 0
    right = len(arr1) -1

    while left < right:
        # Do some logic depending on the problem
        # Do some logic to determine the following
        # - left += 1
        # - right -= 1
        # - both left += 1 and right -=1
        pass
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
2
Q

Two Pointers - Multiple Arrays/Strings (Parallel)

A

Python Implementation

def two_pointers(arr1, arr2):
    i = j = 0

    while i < len(arr1) and j < len(arr2):
        # Do some logic depending on the problem
        # Do some logic to decide on the following:
        # 1. i += 1
        # 2. j += 1
        # 3. i += 1 and j += 1
        pass

    # Make sure both iterables are exhausted
    # Only one of these loops will run
    while i < len(arr1):
        # Do some logic depending on the problem
        i += 1
        pass

    while j < len(arr2):
        # Do some logic depending on the problem
        j += 1
        pass
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
3
Q

Sliding Window – Dynamic Window

A

Python Implementation

def sliding_window(arr):
    left = 0

    for right in range(len(arr)):
        # Do some logic to add arr[right] to window

        while WINDOW_IS_INVALID:
            # Do some logic to remove arr[left] from the window
            left += 1
            pass

        # Do some logic to update the answer
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
4
Q

Sliding Window - Fixed Window

A

Python Implementation

def sliding_window(arr, k):
    curr = 0 # Some data to track the window

    # Build the first window
    for i in range(k):
        # Do something with curr or other variables
        pass

    ans = curr

    # Slide the window
    for i in range(k, len(arr)):
        # Add arr[i] to window
        # Remove arr[i-k] from window
        # Update ans
        pass

    return ans
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
5
Q

Prefix Sum

A

Python Implementation

```python
def prefix_sum(nums: list[int]):
prefix = [nums[0]]

for i in range(1, len(nums)):
    prefix.append(nums[i] + prefix[-1])

return prefix

Sum between i and j is:
# sum = prefix[j] - prefix[i] + nums[i]
~~~

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
6
Q

Describe an algorithm to efficiently build a string

A

arr is a list of characters

def build_string(s: str):
    arr = []

    # Convert a string into an array
    for c in s:
        arr.append(c)

    # Convert an array to a string
    return "".join(arr)
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
7
Q

Describe an algorithm to find the number of subarrays that fit an exact criteria

A
from collections import defaultdict

def fn(arr, k):
    counts = defaultdict(int)
    counts[0] = 1
    ans = curr = 0

    for num in arr:
        # do logic to change curr
        ans += counts[curr - k]
        counts[curr] += 1
    
    return ans
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
8
Q

Describe an algorithm to implement fast and slow pointers in a linked list

A
def fn(head):
    slow = head
    fast = head
    ans = 0

    while fast and fast.next:
        # do logic
        slow = slow.next
        fast = fast.next.next
    
    return ans
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
9
Q

Describe an algorithm to return the middle of a linked list

A
def fn(head):
    slow = head
    fast = head

    while slow and fast.next:
        slow = slow.next
        fast = fast.next.next

    return slow.val
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
10
Q

Describe an algorithm to find cycles in a linked list

A
def fn(head):
    slow = head
    fast = head

    while slow and fast.next:
        slow = slow.next
        fast = fast.next.next
        if slow == fast:
            return True

    return False
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
11
Q

Describe an algorithm to reverse a linked list

A
def fn(head):
    curr = head
    prev = None

    while curr:
        next_node = curr.next
        curr.next = prev
        prev = curr
        curr = next_node

    return prev
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
12
Q

Describe an algorithm to build/maintain a monotonic increasing stack

A
def fn(arr):
    stack = []
    ans = 0

    for num in arr:
        # for monotonic decreasing, just flip the > to <
        while stack and stack[-1] > num:
            # do logic
            stack.pop()
        stack.append(num)
    
    return ans
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
13
Q

Describe and algorithm to perform a DFS (recursive) on a binary tree

A
def dfs(root):
    if not root:
        return
    
    ans = 0

    # do logic
    dfs(root.left)
    dfs(root.right)
    return ans
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
14
Q

Describe an algorithm to perform a DFS (iterative) on a binary tree

A
def dfs(root):
    stack = [root]
    ans = 0

    while stack:
        node = stack.pop()
        # do logic
        if node.left:
            stack.append(node.left)
        if node.right:
            stack.append(node.right)

    return ans
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
15
Q

Describe and algorithm to perform preorder Depth First Search (DFS) on a binary tree

A
def preorder_dfs(node):
    if node == None:
        return

    print(node.value)
    preorder_dfs(node.left)
    preorder_dfs(node.right)
    return
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
16
Q

Describe and algorithm to perform inorder Depth First Search (DFS) on a binary tree

A
def inorder_dfs(node):
    if node == None:
        return

    inorder_dfs(node.left)
    print(node.value)
    inorder_dfs(node.right)
    return
17
Q

Describe and algorithm to perform postorder Depth First Search (DFS) on a binary tree

A
def postorder_dfs(node):
    if node == None:
        return

    postorder_dfs(node.left)
    postorder_dfs(node.right)
    print(node.value)
    return
18
Q

Describe an algorithm to perform Breadth First Search (BFS) on a binary tree

A
from collections import deque

def bfs(root):
    queue = deque([root])
    while queue:
        nodes_in_current_level = len(queue)
        # Do some logic on the current level

        for _ in range(nodes_in_current_level):
            node = queue.popleft()
            # Do some logic on the current node
            print(node.val)
            if node.left:
                queue.append(node.left)

            if node.right:
                queue.append(node.right)
19
Q

Describe an algorithm to insert a value into a Binary Search Tree (BST)

A
def insertIntoBST(root, val):
    if not root:
        return TreeNode(val)

    if val < root.val:
        root.left = insertInotBST(root.left, val)
    else:
        root.right = insertInotBST(root.right, val)

    return root
20
Q

Describe an algorithm to build an adjacency list given an array of directed edges

A
from collections import defaultdict

def build_graph(edges):
    graph = defaultdict(list)
    for x, y in edges:
        graph[x].append(y)
        # uncomment if undirected
        #graph[y].append(x)

    return graph
21
Q

Describe an algorithm to build a graph given an adjacency matrix

A
from collections import defaultdict

def build_graph_from_adjacency_matrix(adj_matrix):
    graph = defaultdict(list)
    n = len(adj_matrix)
    m = len(adj_matrix[0])
    for i in range(n):
        for j in range(m):
            if adj_matrix[i][j] == 1:
                graph[i].append(j)

    return graph
22
Q

Describe an algorithm to perform a Depth First Search (DFS) on a graph

A
# build graph

seen = set()

def dfs(node):
    for neighbor in graph[node]:
        if neighbor not in seen:
            print(neighbor)
            seen.add(neighbor)
            dfs(neighbor)

for i in range(len(graph)):
    if i not in seen:
        print(i)
        seen.add(i)
        dfs(i)
23
Q

Describe and algorithm to perform a Depth First Search (DFS) to find the number of islands given a a binary matrix

A
def island_count(grid):

        m = len(grid)
        n = len(grid[0])
        directions = [(1, 0), (-1, 0), (0, 1), (0, -1)]
        seen = set()
        islands = 0

        def is_valid(row: int, col: int) -> bool:
            return 0 <= row < m and 0 <= col < n and grid[row][col] == 1:

        def dfs(row, col):
            size = 1
            for dx, dy in directions:
                next_row, next_col = row + dy, col + dx
                if is_valid(next_row, next_col) and (next_row, next_col) not in seen:
                    seen.add((next_row, next_col))
                    size += dfs(next_row, next_col)

            return size

        for row in range(m):
            for col in range(n):
                if (row, col) not in seen and is_valid(row,col):
                    islands += 1
                    seen.add((row,col))
                    dfs(row,col)

        return islands
24
Q

Describe an algorithm to perform a Breadth First Search (BFS) to find the shortest path given a a binary matrix

A
from collections import deque

def shortest_path_binary_matrix(grid):
    if grid[0][0] == 1:
        return -1

    n = len(grid)
    m = len(grid[0])
    seen = set((0,0))
    queue = deque([(0,0,1)]) # right, left, steps

    directions = [
        (1,0),
        (1,1),
        (0,1),
        (-1,1),
        (-1,0),
        (-1,-1),
        (0,-1),
        (1,-1)
    ]

    def is_valid(row, col):
        return 0 <= row < m and 0 <= col < n and grid[row][col] == 0

    while queue:
        row, col, steps = queue.popleft()
        if (row, col) == (n-1, m-1):
            return steps

        for dx, dy in directions:
            next_row, next_col = row + dy, col + dx
            if is_valid(next_row, next_col) and (next_row, next_col) not in seen:
                seen.add((next_row, next_col))
                queue.append((next_row, next_col, steps + 1))

    return -1
25
Describe the **binary search** algorithm for a sorted array with no duplicates and give it's time and space complexity
Time: `O(log n)` Space: `O(1)` ``` def binary_search(arr, target): left = 0 right = len(arr) - 1 while left <= right: mid = (right + left) // 2 print(mid) if arr[mid] == target: # Found return if arr[mid] > target: right = mid -1 else: left = mid + 1 return left ```
26
Describe the **binary search** algorithm for an array that contains duplicates and returns the left-most index. Also give it's time and space complexity
Time: `O(log n)` Space: `O(1)` ``` def binary_search_first_pos(arr, target): left = 0 right = len(arr) while left < right: mid = (left + right) // 2 if arr[mid] >= target: right = mid else: left = mid + 1 return left ```
27
Describe the **binary search** algorithm for an array that contains duplicates and returns the right-most **insertion point**. Also give it's time and space complexity
Time: `O(log n)` Space: `O(1)` ``` def binary_search_last_pos(arr, target): left = 0 right = len(arr) while left < right: mid = (left + right) // 2 if arr[mid] > target: right = mid else: left = mid + 1 return left ```
28
Describe the generic **backtracking** algorithm
``` def backtrack(curr, OTHER_ARGUMENTS...): if (BASE_CASE): # modify the answer return ans = 0 for (ITERATE_OVER_INPUT): # modify the current state ans += backtrack(curr, OTHER_ARGUMENTS...) # undo the modification of the current state return ans ```
29
Describe a generic **trie** algorithm
``` class TrieNode: def __init__(self): # you can store data at nodes if you wish self.data = None self.children = {} def fn(words): root = TrieNode() for word in words: curr = root for c in word: if c not in curr.children: curr.children[c] = TrieNode() curr = curr.children[c] # at this point, you have a full word at curr # you can perform more logic here to give curr an attribute if you want return root ```