Introduction

Practice Interviews!

A collection of 75 of the best leetcode questions to do.

If you have an interview soon, do the Most Important Problems.

If you want general practice, we have General Conceptual Problems you can practice!

Download these problems as an Anki text file! This allows you do to spaced repetition and find the best timing to learn and practice problems, especially those you forget.

Ever forget Python syntax, or a common data structure in Python? We have a Python Interview Cheat Sheet.

Python Data Structures Cheat Sheet

Lists

lst = []
lst = [3, 2, 7, 5]
lst = [i for i in range(10)] # comprehensions
lst = [0 for _ in range(10)]

lst = []
lst[5] # IndexError out of range

lst = [3, 2, 7, 5]
val = lst[3]
print(val) # 5

lst[3] = 2
print(lst[3]) # 2


lst = [None, True, 2, "Hello"]
lst.append(4)
lst.extend([3, True])
lst.insert(2, "yeet")

lst.remove(True) # finds first and remove
list.pop() # last elm
list.pop(2) # second elm

list.sort() # key=None, reverse=False, ALSO in place
list.reverse() # in place

# iterate list backwards
for i in range(len(list) - 1, -1, -1):
    print(i)

2D Arrays

# can be different size rows, most times same for a rectangle matrix

arr = [[0, 1, 2],
    [3, 4, 5],
    [6, 7, 8]]

arr = [[j for j in range(3 * i, 3 * i + 3)] for i in range(3)]

# first for which row, second for where in row
print(arr[0][0]) # 0
print(arr[0][2]) # 2
print(arr[2][0]) # 6
print(arr[2][2]) # 8

for y in range(len(arr)):
    for x in range(len(arr[y])):
        print(arr[y][x])

Stacks

# using deque
from collections import deque

stack = deque()
stack.append(5)
stack.pop() 
print(stack[-1]) # peek

# using list
stack = []
stack.append(5) # push
stack.pop() # pop
print(stack[-1]) # peek

# both
while len(stack) != 0:
  val = stack.pop()

# Stack is visualized as a vertical structure and hence has depth - DFS.

Queues

# using deque
from collections import deque

queue = deque()
queue.append(5) # push right
queue.popleft() # pop left
print(queue[-1]) # peek

# using list (not recommended)
queue = []
queue.append(5) # push right
queue.pop(0) # pop left
print(stack[-1]) # peek

# both
while len(queue) != 0:
  val = queue.pop()

# Queue can be generally thought as horizontal in structure i.e, breadth/width can be attributed to it - BFS.

Priority Queue

import heapq

q = []

heapq.heappush(q, (2, 'code')) # priority based on first value
heapq.heappush(q, (1, 'eat'))
heapq.heappush(q, (3, 'sleep'))

while q:
    next_item = heapq.heappop(q)
    print(next_item)

# Result:
#   (1, 'eat')
#   (2, 'code')
#   (3, 'sleep')

is_empty = len(q) == 0

# uses heaps, which are implemented as Python List

Dicts/HashMap

m = {}
# or
m = dict()

m = {
  2 : "Hello",
  "ee" : "TSM"
}

m[3] = True
m["Hello"] = 1
m[False] = None

a = m[3]
b = m[4] # KeyError

in_key_set = 3 in m # True in KeySet


len(m) # num elm

is_empty = len(m) == 0

m[9] += 1 # KeyError

if 9 in m:
  m[9] += 1
else:
  m[9] = 1


for k in m:
  v = m[k]
  print("k: %s, v: %s" % (k, v))
  
for k, v in m.items():
  print("k: %s, v: %s" % (k, v))
  
m.keys() # dict_keys([2, 'ee', ...])
m.values() # dict_values(['Hello', 'TSM', ..])

Sets

s = set()
s.add(5)

in_set = 3 in s
s.pop(5)

len(s) # num elm

is_empty = len(s) == 0

# intersect AND, union OR, ^ XOR

Tuples

t = ()
t = (1, 5)
t = (3, "eh", True)
a, b, c = (3, "eh", True)

def a():
    return (5, 2)

x, y = a()

tuple_contains = 5 in t

is_empty = len(t) == 0

Linked List

Simple

class LinkNode:
    
    def __init__(self, data):
        self.data = data
        self.next = None

a = LinkNode(5) # [5, null]
b = LinkNode(6) 
c = LinkNode(7) 
a.next = b # [5, -]-> [6, null]
b.next = c # [5, -]-> [6, -]-> [7, null]

cur = a # pointer
while cur != None:
  print(cur.data)
  cur = cur.next

# alt build backwards
a2 = LinkNode(7) # [7, null]
temp = LinkNode(6)
temp.next = a2 
a2 = temp # [6, -]-> [7, null]
temp = LinkNode(5)
temp.next = a2
a2 = temp # [5, -]-> [6, -]-> [7, null]

# alt build with pointer from front
a3 = LinkNode(5)
a3.next = LinkNode(6)
a3.next.next = LinkNode(7)

# alt build step by step
a4 = LinkNode(5)
cur = a4
cur.next = LinkNode(6)
cur = cur.next
cur.next = LinkNode(7)

Head Pointer Deque (Stack and/or Queue)

class LinkedDeque:
    def __init__(self):
        self.head = None
        
    def add_front(self, data):
        temp = LinkNode(data)
        temp.next = self.head
        self.head = temp
    
    def add_back(self, data):
        if self.head == None:
            self.head = LinkNode(data)
        else:
            ptr = self.head
            while ptr.next != None:
                ptr = ptr.next
            ptr.next = LinkNode(data)
    
    def remove_front(self):
        if self.head == None:
            raise Exception("nothing to remove")
        else:
            self.head = self.head.next
        
    
    def remove_back(self):
        if self.head == None:
            raise Exception("nothing to remove")
        else:
            if self.head.next == None:
                self.head = None
            else:
                ptr = self.head
                while ptr.next.next != None:
                    ptr = ptr.next
                ptr.next = None

Using Dummy Head

class LinkedDeque:
    def __init__(self):
        self.head = link_node(None)
        
    def add_front(self, data):
        temp = link_node(data)
        temp.next = self.head.next
        self.head.next = temp
    
    def add_back(self, data):
        ptr = self.head
        while ptr.next != None:
            ptr = ptr.next
        ptr.next = link_node(data)
    
    def remove_front(self):
        if self.head.next == None:
            raise Exception("nothing to remove")
        else:
            self.head.next = self.head.next.next
        
    
    def remove_back(self):
        if self.head.next == None:
            raise Exception("nothing to remove")
        else:
            if self.head.next.next == None:
                self.head.next = None
            else:
                ptr = self.head.next
                while ptr.next.next != None:
                    ptr = ptr.next
                ptr.next = None

Double Linked List with Head and Tail: Deque

class DLinkNode():
    def __init__(self, data):
        self.data = data
        self.next = None
        self.prev = None

class DLinkedDeque():
    def __init__(self):
        self.head = None
        self.tail = None
        
    def add_front(self, data):
        if self.head == None:
            self.head = DLinkNode(data)
            self.tail = self.head
        else:
            temp = DLinkNode(data)
            temp.next = self.head
            self.head.prev = temp
            self.head = temp
    
    def add_back(self, data):
        if self.head == None:
            self.head = DLinkNode(data)
            self.tail = self.head
        else:
            temp = DLinkNode(data)
            self.tail.next = temp
            temp.prev = self.tail
            self.tail = temp
    
    def remove_front(self):
        if self.head == None:
            raise Exception("nothing to remove")
        else:
            self.head.next = None
            self.head = self.head.next
        
    
    def remove_back(self):
        if self.head == None:
            raise Exception("nothing to remove")
        else:
            self.tail.prev.next = None
            self.tail = self.tail.prev

Linked List as a List

Double Linked generalizes

class LinkedList():
    # a = LinkedList()
    def __init__(self):
        self.head = None
        self.size = 0 # don't need can also choose to recalc
    
    # len(a)
    def __len__(self):
        return self.size
    
    # a[x] = c
    def __setitem__(self, data, index):
        if x > self.size:
            raise Exception("out of bounds")
            
        if self.head == None: # x must equal 0 as size is 0
            self.head = LinkNode(data) 
            self.size += 1
        else if x == 0: # size does not equal 0
            temp = LinkNode(data)
            temp.next = self.head.next
            self.head = temp
            self.size += 1
        else: # insert normally into list
            ptr = self.head
            for i in range(x - 1): # x >= 1
                ptr = ptr.next
            temp = LinkNode(data)
            temp.next = ptr.next
            ptr.next = temp
            self.size += 1
        
    # b = a[x]
    def __getitem__(self, index):
        if x >= self.size:
            raise Exception("out of bounds")
        
        ptr = self.head
        for i in range(self.size):
            if i == index:
                return ptr.data
            ptr = ptr.next
    
    # del a[x]
    def __delitem__(self, index)
        if x >= self.size:
            raise Exception("out of bounds")
        
        if self.size == 1: # x must be 0
            self.head = None
            self.size -= 1
        else:
            for i in range(self.size - 1):
                if i + 1 == index:
                    ptr.next = ptr.next.next
                    self.size -= 1
                    return
                ptr = ptr.next

Circular

a = LinkNode(5)
a.next = LinkNode(6)
a.next.next = LinkNode(7)
a.next.next.next = a # to front

# no None end

Dict to LinkedList (LRU Cache)

Description: want to map a -> b but store order b is inserted Create link(b) front on stack, most recently used map a -> link(b)

Trees

class Node: 
    def __init__(self, key): 
        self.left = None
        self.right = None
        self.val = key 

DFS

# A function to do inorder tree traversal 
def printInorder(node): 
    if not node: 
        return

    # First recur on left child 
    printInorder(node.left) 
    # then print the data of node 
    print(node.val)
    # or do action at this node
    # now recur on right child 
    printInorder(node.right) 

'''
preorder:
print(node.val)
printInorder(node.left)
printInorder(node.right) 

postorder
printInorder(node.left)
printInorder(node.right) 
print(node.val)
'''

BFS

def printLevelOrder(root): 
    # Base Case 
    if root is None: 
        return
      
    # Create an empty queue for level order traversal 
    queue = [] 
  
    # Enqueue Root and initialize height 
    queue.append(root) 
  
    while queue: 
        # Print front of queue and remove it from queue 
        print(queue[0].val)
        # or do action with this node
        node = queue.pop(0) 
  
        #Enqueue left child 
        if node.left is not None: 
            queue.append(node.left) 
  
        # Enqueue right child 
        if node.right is not None: 
            queue.append(node.right) 
  
#Driver Program to test above function 
root = Node(1) 
root.left = Node(2) 
root.right = Node(3) 
root.left.left = Node(4) 
root.left.right = Node(5) 
  
print("Level Order Traversal of binary tree is -")
printLevelOrder(root) 
#This code is contributed by Nikhil Kumar Singh(nickzuck_007) 

A common pattern is to use a set() visited to ignore where you have visited.

BFS guarantees finding the shortest path first while DFS might find longer paths before finding the shortest one.

Dijkstra's algorithm

import heapq

class GenericSearch:
    def __init__(self, graph):
        self.graph = graph

    def dijkstra_search(self, start, goal):
        open_set = []
        heapq.heappush(open_set, (0, start))
        came_from = {}
        cost_so_far = {start: 0}

        while open_set:
            current_cost, current = heapq.heappop(open_set)

            if current == goal:
                path = []
                while current in came_from:
                    path.append(current)
                    current = came_from[current]
                path.append(start)
                return path[::-1]

            for neighbor in self.graph[current]:
                new_cost = cost_so_far[current] + self.graph[current][neighbor]
                if neighbor not in cost_so_far or new_cost < cost_so_far[neighbor]:
                    cost_so_far[neighbor] = new_cost
                    came_from[neighbor] = current
                    heapq.heappush(open_set, (new_cost, neighbor))

        return None  # No path found

# Example graph
graph = {
    'A': {'B': 1, 'C': 3},
    'B': {'D': 5, 'E': 7},
    'C': {'F': 2},
    'D': {},
    'E': {'F': 1},
    'F': {}
}


# Example usage
search = GenericSearch(graph)
print("Dijkstra Search:", search.dijkstra_search('A', 'F'))

A* search algorithm

import heapq

class GenericSearch:
    def __init__(self, graph):
        self.graph = graph

    def heuristic(self, node, goal):
        return 0

    def a_star_search(self, start, goal):
        open_set = []
        heapq.heappush(open_set, (0, start))
        came_from = {}
        g_score = {node: float('inf') for node in self.graph}
        g_score[start] = 0
        f_score = {node: float('inf') for node in self.graph}
        f_score[start] = self.heuristic(start, goal)

        while open_set:
            current = heapq.heappop(open_set)[1]

            if current == goal:
                path = []
                while current in came_from:
                    path.append(current)
                    current = came_from[current]
                path.append(start)
                return path[::-1]

            for neighbor in self.graph[current]:
                tentative_g_score = g_score[current] + self.graph[current][neighbor]
                if tentative_g_score < g_score[neighbor]:
                    came_from[neighbor] = current
                    g_score[neighbor] = tentative_g_score
                    f_score[neighbor] = tentative_g_score + self.heuristic(neighbor, goal)
                    if neighbor not in [node[1] for node in open_set]:
                        heapq.heappush(open_set, (f_score[neighbor], neighbor))

        return None  # No path found


# Example graph
graph = {
    'A': {'B': 1, 'C': 3},
    'B': {'D': 5, 'E': 7},
    'C': {'F': 2},
    'D': {},
    'E': {'F': 1},
    'F': {}
}

# Example usage
search = GenericSearch(graph)
print("A* Search:", search.a_star_search('A', 'F'))

Tries

# make trie
trie = {}
for w in words:
    t = trie
    for c in w:
        if c not in t:
            t[c] = {}
        t = t[c]
    t['#'] = '#'

Graphs

# Definition for a Node for a Graph.
class Node:
    def __init__(self, val = 0, neighbors = None):
        self.val = val
        self.neighbors = neighbors if neighbors is not None else []

Most Important Problems!

Jeremy Aguilon's Ranking Interview Questions by Cram Score

Level 1: Interview Tomorrow 😫

Number of Islands

Given a 2-D Array, count the number of islands in it, where land is connected in the NSEW direction. Tags: Recursion, Search, Union-Find

Try It!

Discussion

Video

Solution

class Solution:
    def numIslands(self, grid: List[List[str]]) -> int:
        count = 0

        for i in range(len(grid)):
            for j in range(len(grid[0])):
                if grid[i][j] == '1':
                    self.dfs_marking(grid, i, j);
                    count += 1
                    
        return count
    
    def dfs_marking(self, grid, i, j):
        if i < 0 or i >= len(grid):
            return
        if j < 0 or j >= len(grid[0]):
            return
        if grid[i][j] != '1':
            return
        
        grid[i][j] = '0'
        self.dfs_marking(grid, i + 1, j)
        self.dfs_marking(grid, i - 1, j)
        self.dfs_marking(grid, i, j + 1)
        self.dfs_marking(grid, i, j - 1)

Coin Change

Given a target of N cents and a collection of coins, how many ways can you form N cents? Tags: Dynamic Programming, Recursion

Try It!

Discussion

Video

Solution

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        dp = [float('inf') for i in range(amount + 1)]
        dp[0] = 0
        for i in range(1, amount + 1):
            for coin in coins:
                if i - coin >= 0 and dp[i - coin] + 1 < dp[i]:
                    dp[i] = dp[i - coin] + 1


        if dp[amount] == float('inf'):
            return -1
        return dp[amount]

Minimum Window Substring

Given a string and a collection of characters, find the shortest substring that contains all the characters. Tags: Sliding Window

Try It!

Discussion

Video

Solution

class Solution:
    def minWindow(self, s: str, t: str) -> str:
        t_counter = collections.Counter(t)
        t_needed = len(t)
        left = 0
        right = 0
        min_len = float('inf')
        min_str = ''

        while right < len(s):
            c_right = s[right]
            if c_right in t_counter:
                count = t_counter[c_right]
                if count > 0:
                    t_needed -= 1
                t_counter[c_right] -= 1

            while t_needed == 0:
                c_left = s[left]
                if c_left in t_counter:
                    count = t_counter[c_left]
                    if count == 0:
                        t_needed += 1
                    t_counter[c_left] += 1

                if right - left + 1 < min_len:
                    min_len = right - left + 1
                    min_str = s[left:right + 1]
                left += 1
            right += 1

        return min_str

Buying and Selling Stock

Given a list of starting and ending days for a stock, maximize your profit Tags: Greedy

Try It!

Discussion

Video

Solution

class Solution:
    def maxProfit(self, prices: List[int]) -> int:
        lowest = prices[0]
        max_gain = 0
        for i in range(1, len(prices)):
            price = prices[i]
            if price - lowest > max_gain:
                max_gain = price - lowest
            if price < lowest:
                lowest = price
        return max_gain

Range Sum Query (Deprecated)

Efficiently compute the sum of the numbers between two indexes of an array. Tags: Dynamic Programming

Try It!

Discussion

Video

Solution

class NumArray:

    def __init__(self, nums: List[int]):
        for i in range(1, len(nums)):
            nums[i] += nums[i - 1]
        self.sum = nums

    def sumRange(self, i: int, j: int) -> int:
        if i == 0:
            return self.sum[j]
        else:
            return self.sum[j] - self.sum[i - 1]

Merge Intervals

Given a list of intervals with a start and end value, merge the intervals. Tags: Sorting

Try It!

Discussion

Video

Solution

class Solution:
    def merge(self, intervals: List[List[int]]) -> List[List[int]]:
        ans = []
        if len(intervals) < 2:
            return intervals
        intervals.sort(key=lambda x: x[0])
        
        cur = intervals[0]
        
        for i in range(len(intervals)):
            interval = intervals[i]
            if cur[1] >= interval[0]:
                cur[1] = max(cur[1], interval[1])
            else:
                ans.append(cur)
                cur = interval
        ans.append(cur)
        return ans

Maximum Subarray

Given an array, find the subarray with the maximum sum. Tags: Dynamic Programming

Try It!

Discussion

Video

Solution

class Solution:
    def maxSubArray(self, nums: List[int]) -> int:
        ans = nums[0]
        cur_total = 0

        for n in nums:
            cur_total += n
            ans = max(ans, cur_total)
            if cur_total < 0:
                cur_total = 0

        return ans

Level 2: Week to Prepare 😬

Range Sum Query Mutable (Deprecated)

Given a mutable array of integers, find the sum of elements between i and j. Tags: Recursion, Trees

Try It!

Discussion

Video

Solution

class Node:
    def __init__(self, start, end):
        self.start = start
        self.end = end
        self.total = 0
        self.left = None
        self.right = None

class NumArray:

    def __init__(self, nums: List[int]):
        def create_tree(self, start, end):
            if start > end:
                return None
            elif start == end:
                node = Node(start, end)
                node.total = nums[start]
                return node
            else:
                mid = start + (end - start) // 2
                node = Node(start, end)
                node.left = create_tree(nums, start, mid)
                node.right = create_tree(nums, mid + 1, end)
                node.total = node.left.total + node.right.total
                return node
        
        self.root = create_tree(nums, 0, len(nums) - 1)
        

    def update(self, index: int, val: int) -> None:
        def update_val(node, index, val):
            if node.start == node.end:
                node.total = val
                return

            mid = node.start + (node.end - node.start) // 2
            if index <= mid:
                update_val(node.left, index, val)
            else:
                update_val(node.right, index, val)
            node.total = node.left.total + node.right.total
            
        update_val(self.root, index, val)

    def sumRange(self, left: int, right: int) -> int:
        def sum_range(node, i, j):
            if node.start == i and node.end == j:
                return node.total
            
            mid = node.start + (node.end - node.start) // 2
            if j <= mid:
                return sum_range(node.left, i, j)
            elif i > mid:
                return sum_range(node.right, i, j)
            else:
                return sum_range(node.left, i, mid) + sum_range(node.right, mid + 1, j)
        
        return sum_range(self.root, left, right)
        
# Your NumArray object will be instantiated and called as such:
# obj = NumArray(nums)
# obj.update(index,val)
# param_2 = obj.sumRange(left,right)

Word Search II (Deprecated)

Given a 2-D array of characters and a list of valid words, find the number of words on the board. Tags: Recursion, Search, Trees

Try It!

Discussion

Video

Solution

class Solution:
    def findWords(self, board: List[List[str]], words: List[str]) -> List[str]:
        visited = [[False for c in range(len(board[0]))] for r in range(len(board))]
        ans = set()
        
        trie = {}
        for word in words:
            level = trie
            for letter in word:
                if letter not in level:
                    level[letter] = {}
                level = level[letter]
            level['#'] = '#'
            
        for r in range(len(board)):
            for c in range(len(board[0])):
                cur = ''
                self.dfs(board, visited, r, c, trie, cur, ans)
        
        return list(ans)
    
    def dfs(self, board, visited, r, c, trie, cur, ans):
        if '#' in trie:
            ans.add(cur)
        
        if r < 0 or r >= len(board):
            return
        if c < 0 or c >= len(board[0]):
            return
        if visited[r][c]:
            return
        if board[r][c] not in trie:
            return

        letter = board[r][c]
        if letter in trie:
            cur += letter
            trie = trie[letter]

            visited[r][c] = True
            self.dfs(board, visited, r + 1, c, trie, cur, ans)
            self.dfs(board, visited, r - 1, c, trie, cur, ans)
            self.dfs(board, visited, r, c + 1, trie, cur, ans)
            self.dfs(board, visited, r, c - 1, trie, cur, ans)
            visited[r][c] = False

Task Scheduler

Given an array of tasks an a cooldown period, given an efficient ordering. Tags: Greedy, Priority Queue

Try It!

Discussion

Video

Solution

class Solution:
    def leastInterval(self, tasks: List[str], n: int) -> int:
        task_counter = {}
        for task in tasks:
            if task not in task_counter:
                task_counter[task] = 0
            task_counter[task] += 1
        
        # -count is the way to create max-heap in python. The heapq default is min-heap.
        max_heap = []
        for task in task_counter:
            count = task_counter[task]
            heapq.heappush(max_heap, -count)
        
        tot_count = 0
        while max_heap:
            task_counts = []
            for i in range(n + 1):
                if max_heap:
                    count = heapq.heappop(max_heap)
                    task_counts.append(count)
            
            for task_count in task_counts:
                if task_count < -1: # if task_count is equal to -1, it is being used.
                    heapq.heappush(max_heap, task_count + 1)
            
            if max_heap:
                tot_count += n + 1 # + 1 due to difference in indexing +1 for spacing
            else:
                tot_count += len(task_counts)
        return tot_count

Non-overlapping Intervals (Deprecated)

Given a collection of intervals, find the number of intervals you need to remove to make the rest of the intervals non-overlapping. Tags: Greedy, Sorting

Try It!

Discussion

Video

Solution

class Solution:
    def eraseOverlapIntervals(self, intervals: List[List[int]]) -> int:
        intervals.sort(key=lambda x: x[0])
        
        count = 0
        prev = intervals[0]
        for i in range(1, len(intervals)):
            cur = intervals[i]
            if prev[1] > cur[0]:
                count += 1
                prev[1] = min(prev[1], cur[1])
            else:
                prev = cur
                
        return count

Preorder Serialization Verification of a Binary Tree (Deprecated)

Given a particular kind of preorder serialization of a binary tree, verify that it's valid. Tags: Recursion, Trees

Try It!

Discussion

Video

Solution

class Solution:
    def isValidSerialization(self, preorder: str) -> bool:
        # remember how many empty slots we have
        # non-null nodes occupy one slot but create two new slots
        # null nodes occupy one slot
        
        preorder = preorder.split(',')
        
        #initially we have one empty slot to put the root in it
        count = 1
        for node in preorder:
            
            # no empty slot to put the current node
            if count == 0:
                return False
                
            # a null node?
            if node == '#':
                # ocuppy slot
                count -= 1
            else:
                # use a slot, create 2 new slots
                count += 1
        
        #we don't allow empty slots at the end
        return count == 0

Level 3: Sixth Sense Problems 🧐

Find the Duplicate Number (Deprecated)

Find the duplicate number in an array AND use O(1) space. Tags: Search

Try It!

Discussion

Diagram

Video

As an anime video!

Solution

class Solution:
    def findDuplicate(self, nums: List[int]) -> int:
        hare = nums[0]
        tortoise = nums[0]
        while True:
            hare = nums[nums[hare]]
            tortoise = nums[tortoise]
            if hare == tortoise:
                break
        ptr1 = nums[0]
        ptr2 = tortoise
        while ptr1 != ptr2:
            ptr1 = nums[ptr1]
            ptr2 = nums[ptr2]
        return ptr1

Construct Binary Tree from Preorder and Inorder Traversal

Given the preorder and inorder serialization of a binary tree, construct a tree data structure. Tags: Recursion, Search, Trees

Try It!

Discussion

Video

Solution

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def buildTree(self, preorder: List[int], inorder: List[int]) -> TreeNode:
        if not inorder:
            return None
        
        root = preorder.pop(0)
        idx = inorder.index(root)
        node = TreeNode(root)
        node.left = self.buildTree(preorder, inorder[:idx])
        node.right = self.buildTree(preorder, inorder[idx + 1:])
        return node
        

Decode Ways (Deprecated)

Given an encoding from letters to numbers, count the number of ways to decode a string of numbers. Tags: Dynamic Programming, Recursion

Try It!

Discussion

Video

Solution

class Solution:
    def numDecodings(self, s: str) -> int:
        dp = [0 for i in range(len(s) + 1)]
        dp[0] = 1
        if 1 <= int(s[0]) <= 9:
            dp[1] = 1
        
        for i in range(2, len(s) + 1):
            if 1 <= int(s[i - 1]) <= 9:
                dp[i] += dp[i - 1]
            if 10 <= int(s[i - 2:i]) <= 26:
                dp[i] += dp[i - 2]
            
        return dp[-1]

Jump Game II (Deprecated)

Given a non-negative integer array where each index represents the maxmimum you can iterate forward, minimize the number of jumps to reach the end of the array. Tags: Greedy

Try It!

Discussion

Video

Solution

class Solution:
    def jump(self, nums: List[int]) -> int:
        if len(nums) == 1:
            return 0
        
        reachable = 0
        last_jump_idx = 0
        count = 0
        
        for i in range(len(nums)):
            # use greedy strategy
            reachable = max(reachable, i + nums[i])
                
            if i == last_jump_idx:
                last_jump_idx = reachable
                count += 1
                if last_jump_idx == len(nums) - 1:
                    # if we land on last index but don't include this case, we add an extra jump
                    return count
        return count

Find the Edit Distance Between Two Words (Deprecated)

Given two words, find the minimum number of edits to mutate one word into the other. Tags: Dynamic Programming, Recursion

Try It!

Discussion

Video

Solution

class Solution:
    def minDistance(self, word1: str, word2: str) -> int:
        dp = [[0 for x in range(len(word2) + 1)] for y in range(len(word1) + 1)]
        for y in range(len(word1) + 1):
            dp[y][0] = y 
        for x in range(len(word2) + 1):
            dp[0][x] = x
        
        for y in range(1, len(word1) + 1):
            for x in range(1, len(word2) + 1):
                if word1[y - 1] == word2[x - 1]:
                    dp[y][x] = dp[y - 1][x - 1]
                else:
                    dp[y][x] = 1 + min([
                        dp[y - 1][x],
                        dp[y][x - 1],
                        dp[y - 1][x - 1],
                    ])
        return dp[len(word1)][len(word2)]

More Practice Problems

Top 75 Leetcode Problems

String

Longest Substring Without Repeating Characters

Given a string, find the length of the longest substring without repeating characters. Tags: String

Try It!

Discussion

Video

Solution

class Solution:
    def lengthOfLongestSubstring(self, s: str) -> int:
        chars_used = set()

        l = 0
        r = 0

        max_len = 0

        while r < len(s):
            c_r = s[r]
            if c_r not in chars_used:
                chars_used.add(c_r)
                if r - l + 1 > max_len:
                    max_len = r - l + 1
                r += 1
            else:
                c_l = s[l]
                chars_used.remove(c_l)
                l += 1
        
        return max_len

Longest Repeating Character Replacement

Find the length of the longest substring containing all repeating letters you can get after changing at most k characters. Tags: String

Try It!

Discussion

Video

Solution

class Solution:
    def characterReplacement(self, s: str, k: int) -> int:
        left = 0
        right = 0
        max_len = 0
        max_count = 0 
        char_counter = {}
        
        while right < len(s):
            c = s[right]
            if c not in char_counter:
                char_counter[c] = 0    
            char_counter[c] += 1
            max_count = max(max_count, char_counter[c])
            
            while right - left + 1 - max_count > k:
                c = s[left]
                char_counter[c] -= 1
                left += 1
            
            max_len = max(max_len, right - left + 1)
            right += 1
        
        return max_len

Valid Anagram

Given two strings s and t, write a function to determine if t is an anagram of s. Tags: String

Try It!

Discussion

Video

Solution

class Solution:
    def isAnagram(self, s: str, t: str) -> bool:
        t_count = {}
        for c in t:
            if c not in t_count:
                t_count[c] = 0
            t_count[c] += 1
        
        for c in s:
            if c not in t_count:
                return False
            else:
                t_count[c] -= 1
        
        for c in t_count:
            if t_count[c] != 0:
                return False
        return True

Group Anagrams

Given an array of strings, group anagrams together. Tags: String

Try It!

Discussion

Video

Solution

class Solution:
    def groupAnagrams(self, strs: List[str]) -> List[List[str]]:
        groups = {}
        for word in strs:
            key = tuple(sorted(word))
            
            if key not in groups:
                groups[key] = []
            groups[key].append(word)
        
        return groups.values()

Valid Parentheses

Given a string containing just the characters '(', ')', '{', '}', '[' and ']', determine if the input string is valid. Tags: String

Try It!

Discussion

Video

Solution

class Solution:
    def isValid(self, s: str) -> bool:
        pair = {
            ')': '(',
            '}': '{',
            ']': '['
        }
        
        stack = []
        for c in s:
            if c in pair:
                if not stack:
                    return False
                top = stack.pop()
                if top != pair[c]:
                    return False
            else:
                stack.append(c)
        
        return len(stack) == 0

Valid Palindrome

Given a string, determine if it is a palindrome, considering only alphanumeric characters and ignoring cases. Tags: String

Try It!

Discussion

Video

Solution

class Solution:
    def isPalindrome(self, s: str) -> bool:
        left = 0
        right = len(s) - 1
        while left < right:
            while left < right and not s[left].isalnum():
                left += 1
            
            while left < right and not s[right].isalnum():
                right -= 1
            
            if s[left].lower() != s[right].lower():
                return False
            
            left += 1
            right -= 1
        return True

Longest Palindromic Substring

Given a string s, find the longest palindromic substring in s. Tags: String

Try It!

Discussion

Video

Solution

Dynamic Programming Solution

class Solution:
    def longestPalindrome(self, s: str) -> str:
        n = len(s)
        dp = [[False for j in range(n)] for i in range(n)]

        ans = ''
        # every string with one char is a palindrome
        for i in range(n):
            dp[i][i] = True
            ans = s[i]
        
        # upper dp
        for start in range(n - 1, -1, -1):
            for end in range(start + 1, n):
                if s[start] == s[end]:
                    if end - start == 1 or dp[start + 1][end - 1]:
                        dp[start][end] = True
                        if end - start + 1 > len(ans):
                            ans = s[start:end + 1]
        return ans

Expand from middle solution

class Solution:
    def longestPalindrome(self, s: str) -> str:
        max_len = 0
        max_str = ''
        
        for i in range(len(s)):
            # odd
            j = i
            k = i
            while j >= 0 and k < len(s) and s[j] == s[k]:
                if k - j + 1 > max_len:
                    max_len = k - j + 1
                    max_str = s[j:k + 1]
                j -= 1
                k += 1
            # even
            j = i
            k = i + 1
            while j >= 0 and k < len(s) and s[j] == s[k]:
                if k - j + 1 > max_len:
                    max_len = k - j + 1
                    max_str = s[j:k + 1]
                j -= 1
                k += 1

        return max_str

Palindromic Substrings

Given a string, your task is to count how many palindromic substrings in this string. Tags: String

Try It!

Discussion

Video

Solution

class Solution:
    def countSubstrings(self, s: str) -> int:
        count = 0
        for i in range(len(s)):
            # odd
            j = i
            k = i
            while j >= 0 and k < len(s) and s[j] == s[k]:
                count += 1
                j -= 1 
                k += 1

            # even
            j = i
            k = i + 1
            while j >= 0 and k < len(s) and s[j] == s[k]:
                count += 1
                j -= 1
                k += 1
                
        return count
        

Longest Palindrome

Given a string s which consists of lowercase or uppercase letters, return the length of the longest palindrome that can be built with those letters. Tags: String

Try It!

Discussion

Video

Solution

import collections

class Solution:
    def longestPalindrome(self, s: str) -> int:
        counter = collections.Counter(s)

        longest = 0
        has_odd = False
        for c in counter:
            count = counter[c]

            if count % 2 == 0:
                longest += count
            else:
                longest += count - 1
                has_odd = True
        
        if has_odd:
            longest += 1
        return longest

String to Integer (atoi)

Implement the myAtoi(string s) function, which converts a string to a 32-bit signed integer. Tags: String

Try It!

Discussion

Video

Solution

class Solution:
    def myAtoi(self, s: str) -> int:
        MIN = -2 ** 31
        MAX = 2 ** 31 - 1
        i = 0
        num = 0
        sign = 1

        while i < len(s) and s[i] == ' ':
            i += 1
        
        if i < len(s) and s[i] == '+':
            i += 1
        elif i < len(s) and s[i] == '-':
            sign = -1
            i += 1
        
        while i < len(s) and str.isdigit(s[i]):
            num = 10 * num + int(s[i])
            i += 1
        
        ans = sign * num
        if ans < MIN:
            return MIN
        elif ans > MAX:
            return MAX
        else:
            return ans

Find All Anagrams in a String

Given two strings s and p, return an array of all the start indices of p's anagrams in s. Tags: String

Try It!

Discussion

Video

Solution

import collections

class Solution:
    def findAnagrams(self, s: str, p: str) -> List[int]:
        p_counter = collections.Counter(p)
        window_counter = collections.Counter()

        start_idx = 0
        ans = []

        for i in range(len(s)):
            c_end = s[i]
            window_counter[c_end] += 1

            # first true case window is [0, len(p) - 1]
            if i >= len(p) - 1:
                if p_counter == window_counter:
                    ans.append(start_idx)
                c_start = s[start_idx]
                if c_start in window_counter:
                    window_counter[c_start] -= 1
                    if window_counter[c_start] == 0:
                        del window_counter[c_start]
                start_idx += 1
        return ans

Array

Two Sum

Given an array of integers, return indices of the two numbers such that they add up to a specific target. Tags: Array

Try It!

Discussion

Video

Solution

class Solution:
    def twoSum(self, nums: List[int], target: int) -> List[int]:
        target_dif = {}
        for i in range(len(nums)):
            num = nums[i]
            if num in target_dif:
                return [target_dif[num], i]
            else:
                dif = target - num
                target_dif[dif] = i

Contains Duplicate

Given an array of integers, find if the array contains any duplicates. Tags: Array

Try It!

Discussion

Video

Solution

class Solution:
    def containsDuplicate(self, nums: List[int]) -> bool:
        num_exists = set()
        for num in nums:
            if num in num_exists:
                return True
            else:
                num_exists.add(num)
        return False

Product of Array Except Self

Given an array nums of n integers where n > 1, return an array output such that output[i] is equal to the product of all the elements of nums except nums[i]. Tags: Array

Try It!

Discussion

Video

Solution

class Solution:
    def productExceptSelf(self, nums: List[int]) -> List[int]:
        ans = [1 for i in range(len(nums))]

        p = 1
        for i in range(len(nums)):
            ans[i] *= p
            p *= nums[i]
        
        p = 1
        for i in range(len(nums) - 1, -1, -1):
            ans[i] *= p
            p *= nums[i]
        
        return ans

Maximum Product Subarray

Given an integer array nums, find the contiguous subarray within an array (containing at least one number) which has the largest product. Tags: Array

Try It!

Discussion

Video

Solution

class Solution:
    def maxProduct(self, nums: List[int]) -> int:
        max_prod = nums[0]
        prev_max = nums[0]
        prev_min = nums[0]

        for num in nums[1:]:
            cur_max = max(num, num * prev_max, num * prev_min)
            cur_min = min(num, num * prev_max, num * prev_min)
            max_prod = max(max_prod, cur_max)
            prev_max = cur_max
            prev_min = cur_min
        return max_prod

Find Minimum in Rotated Sorted Array

Suppose an array sorted in ascending order is rotated at some pivot unknown to you beforehand. Find the minimum element. Tags: Array

Try It!

Discussion

Video

Solution

class Solution:
    def findMin(self, nums: List[int]) -> int:
        lo = 0
        hi = len(nums) - 1
        
        while lo < hi:
            mid = lo + (hi - lo) // 2
            # important to do mid, hi comparison so we converge down to min instead of highest lo num
            if nums[mid] < nums[hi]:
                hi = mid
            else:
                lo = mid + 1

        return nums[lo]   

Search in Rotated Sorted Array

Suppose an array sorted in ascending order is rotated at some pivot unknown to you beforehand. You are given a target value to search. If found in the array return its index, otherwise return -1. Tags: Array

Try It!

Discussion

Video

Solution

class Solution:
    def search(self, nums: List[int], target: int) -> int:
        lo = 0
        hi = len(nums) - 1
        
        while lo <= hi:
            mid = lo + (hi - lo) // 2
            if target == nums[mid]:
                return mid
            
            if nums[lo] <= nums[mid]:
                if nums[lo] <= target <= nums[mid]:
                    hi = mid - 1
                else:
                    lo = mid + 1
            else:
                if nums[mid] <= target <= nums[hi]:
                    lo = mid + 1
                else:
                    hi = mid - 1
        
        return -1

3Sum

Given an array nums of n integers, are there elements a, b, c in nums such that a + b + c = 0? Find all unique triplets in the array which gives the sum of zero. Tags: Array

Try It!

Discussion

Video

Solution

class Solution:
    def threeSum(self, nums: List[int]) -> List[List[int]]:
        nums.sort()
        ans = []

        for i in range(len(nums) - 2):
            if i == 0 or nums[i] != nums[i - 1]:
                self.two_sum(nums, i, ans)
        
        return ans

    def two_sum(self, nums, i, ans):
        target = -nums[i]
        j = i + 1
        k = len(nums) - 1
        while j < k:
            if nums[j] + nums[k] < target:
                j += 1
            elif nums[j] + nums[k] > target:
                k -= 1
            else:
                ans.append([nums[i], nums[j], nums[k]])

                while j < k and nums[j] == nums[j + 1]:
                    j += 1
                while j < k and nums[k] == nums[k - 1]:
                    k -= 1
                j += 1
                k -= 1
        

Container With Most Water

Given n non-negative integers a_1, a_2, ..., a_n , where each represents a point at coordinate (i, a_i). n vertical lines are drawn such that the two endpoints of line i is at (i, a_i) and (i, 0). Find two lines, which together with x-axis forms a container, such that the container contains the most water. Tags: Array

Try It!

Discussion

Video

Solution

class Solution:
    def maxArea(self, height: List[int]) -> int:
        lo = 0
        hi = len(height) - 1
        max_area = 0
        while lo < hi:
            if height[lo] < height[hi]:
                area = height[lo] * (hi - lo)
                if area > max_area:
                    max_area = area
                lo += 1
            else:
                area = height[hi] * (hi - lo)
                if area > max_area:
                    max_area = area
                hi -= 1
        return max_area

Majority Element

Given a string s which consists of lowercase or uppercase letters, return the length of the longest palindrome that can be built with those letters. Tags: String

Try It!

Discussion

Video

Solution

class Solution:
    def majorityElement(self, nums: List[int]) -> int:
        count = 0
        majority_elem = None
        for num in nums:
            if count == 0:
                majority_elem = num
            if majority_elem == num:
                count += 1
            else:
                count -= 1
        return majority_elem

Sort Colors

Given an array nums with n objects colored red, white, or blue, sort them in-place so that objects of the same color are adjacent, with the colors in the order red, white, and blue. Tags: Array

Try It!

Discussion

Video

Solution

class Solution:
    def sortColors(self, nums: List[int]) -> None:
        """
        Do not return anything, modify nums in-place instead.
        """
        red = 0
        white = 0
        blue = len(nums) - 1

        while white <= blue:
            if nums[white] == 0:
                tmp = nums[red]
                nums[red] = nums[white]
                nums[white] = tmp
                red += 1
                white += 1
            elif nums[white] == 1:
                white += 1
            else:
                tmp = nums[blue]
                nums[blue] = nums[white]
                nums[white] = tmp
                blue -= 1

Trapping Rain Water

Given n non-negative integers representing an elevation map where the width of each bar is 1, compute how much water it can trap after raining. Tags: Stack

Try It!

Discussion

Video

Solution

class Solution:
    def trap(self, height: List[int]) -> int:
        volume = 0
        left = 0
        right = len(height) - 1
        l_max = 0
        r_max = 0

        while left < right:
            l_max = max(height[left], l_max)
            r_max = max(height[right], r_max)
            if l_max <= r_max:
                volume += l_max - height[left]
                left += 1
            else:
                volume += r_max - height[right]
                right -= 1

        return volume

Dynamic Programming

Climbing Stairs

You are climbing a stair case. It takes n steps to reach to the top. Each time you can either climb 1 or 2 steps. In how many distinct ways can you climb to the top? Tags: Dynamic Programming

Try It!

Discussion

Video

Solution

class Solution:
    def climbStairs(self, n: int) -> int:
        dp = [0 for i in range(n + 1)]
        dp[0] = 1
        dp[1] = 1
        
        for i in range(2, n + 1):
            dp[i] += dp[i - 1] + dp[i - 2]
            
        return dp[n]

Longest Increasing Subsequence

Given an unsorted array of integers, find the length of longest increasing subsequence. Tags: Dynamic Programming

Try It!

Discussion

Video

Solution

class Solution:
    def lengthOfLIS(self, nums: List[int]) -> int:
        dp = [1 for i in range(len(nums))]
        max_val = 1
        
        for i in range(len(nums)):
            for j in range(i):
                if nums[j] < nums[i]:
                    dp[i] = max(dp[i], dp[j] + 1)
                    max_val = max(dp[i], max_val)
        
        return max_val

Longest Common Subsequence

Given two strings text1 and text2, return the length of their longest common subsequence. Tags: Dynamic Programming

Try It!

Discussion

Video

Solution

class Solution:
    def longestCommonSubsequence(self, text1: str, text2: str) -> int:
        dp = [[0 for j in range(len(text2) + 1)] for i in range(len(text1) + 1)]
        
        for i in range(1,  len(text1) + 1):
            for j in range(1, len(text2) + 1):
                if text1[i - 1] == text2[j - 1]:
                    dp[i][j] = dp[i - 1][j - 1] + 1
                else:
                    dp[i][j] = max(dp[i - 1][j], dp[i][j - 1])

        return dp[len(text1)][len(text2)]

Word Break Problem

Given a non-empty string s and a dictionary wordDict containing a list of non-empty words, determine if s can be segmented into a space-separated sequence of one or more dictionary words. Tags: Dynamic Programming

Try It!

Discussion

Video

Solution

class Solution:
    def wordBreak(self, s: str, wordDict: List[str]) -> bool:
        dp = [False for i in range(len(s) + 1)]
        dp[0] = True

        for c in range(len(s) + 1):
            for word in wordDict:
                if c >= len(word):
                    if dp[c - len(word)] and s[c - len(word):c] == word:
                        dp[c] = True      
        return dp[-1]

Combination Sum IV

Given an integer array with all positive numbers and no duplicates, find the number of possible combinations that add up to a positive integer target. Tags: Dynamic Programming

Try It!

Discussion

Video

Solution

class Solution:
    def combinationSum4(self, nums: List[int], target: int) -> int:
        dp = [0 for i in range(target + 1)]
        dp[0] = 1
        
        for i in range(1, target + 1):
            for j in range(len(nums)):
                num = nums[j]
                if num <= i:
                    dp[i] += dp[i - num]
        
        return dp[target]

House Robber

You are a professional robber planning to rob houses along a street. Each house has a certain amount of money stashed, the only constraint stopping you from robbing each of them is that adjacent houses have security system connected and it will automatically contact the police if two adjacent houses were broken into on the same night. Given a list of non-negative integers representing the amount of money of each house, determine the maximum amount of money you can rob tonight without alerting the police. Tags: Dynamic Programming

Try It!

Discussion

Video

Solution

class Solution:
    def rob(self, nums: List[int]) -> int:
        if len(nums) == 0:
            return 0
        if len(nums) == 1:
            return nums[0]
        
        dp = [0 for i in range(len(nums))]
        dp[0] = nums[0]
        dp[1] = max(nums[0], nums[1])
        for i in range(2, len(nums)):
            dp[i] = max(dp[i - 1], dp[i - 2] + nums[i])
        
        return dp[len(nums) - 1]

House Robber II

You are a professional robber planning to rob houses along a street. Each house has a certain amount of money stashed. All houses at this place are arranged in a circle. That means the first house is the neighbor of the last one. Meanwhile, adjacent houses have security system connected and it will automatically contact the police if two adjacent houses were broken into on the same night. Given a list of non-negative integers representing the amount of money of each house, determine the maximum amount of money you can rob tonight without alerting the police. Tags: Dynamic Programming

Try It!

Discussion

Video

Solution

class Solution:
    def rob(self, nums: List[int]) -> int:
        if len(nums) == 0:
            return 0
        if len(nums) == 1:
            return nums[0]
        
        dp_normal = [0 for i in range(len(nums))]
        dp_round = [0 for i in range(len(nums))]
        
        dp_normal[0] = nums[0]
        dp_normal[1] = max(nums[0], nums[1])
        
        dp_round[1] = nums[1]
        
        for i in range(2, len(nums)):
            dp_normal[i] = max(dp_normal[i - 1], dp_normal[i - 2] + nums[i])
            dp_round[i] = max(dp_round[i - 1], dp_round[i - 2]  + nums[i])
        
        return max(dp_normal[len(nums) - 2], dp_round[len(nums) - 1])
        

Unique Paths

A robot is located at the top-left corner of a m x n grid. The robot can only move either down or right at any point in time. The robot is trying to reach the bottom-right corner of the grid. How many possible unique paths are there? Tags: Dynamic Programming

Try It!

Discussion

Video

Solution

class Solution:
    def uniquePaths(self, m: int, n: int) -> int:
        dp = [[0 for c in range(n)] for r in range(m)]
        
        for r in range(m):
            dp[r][0] = 1
        for c in range(n):
            dp[0][c] = 1
        
        for r in range(1, m):
            for c in range(1, n):
                dp[r][c] += dp[r - 1][c] + dp[r][c - 1]

        return dp[m - 1][n - 1]

Jump Game

Given an array of non-negative integers, you are initially positioned at the first index of the array. Each element in the array represents your maximum jump length at that position. Determine if you are able to reach the last index. Tags: Dynamic Programming

Try It!

Discussion

Video

Solution

class Solution:
    def canJump(self, nums: List[int]) -> bool:
        reachable = 0
        
        for i in range(len(nums)):
            if i > reachable:
                return False
            reachable = max(reachable, i + nums[i])
        
        return True

01 Matrix

Given an m x n binary matrix mat, return the distance of the nearest 0 for each cell. Tags: Dynamic Programming

Try It!

Discussion

Video

Solution

class Solution:
    def updateMatrix(self, mat: List[List[int]]) -> List[List[int]]:
        dp = [[float('inf') for j in range(len(mat[0]))] for i in range(len(mat))]

        for i in range(len(mat)):
            for j in range(len(mat[0])):
                if mat[i][j] == 0:
                    dp[i][j] = 0
                if i > 0:
                    dp[i][j] = min(dp[i][j], 1 + dp[i - 1][j])
                if j > 0:
                    dp[i][j] = min(dp[i][j], 1 + dp[i][j - 1])
        
        for i in range(len(mat) - 1, -1, -1):
            for j in range(len(mat[0]) - 1, -1, -1):
                if i <= len(mat) - 2:
                    dp[i][j] = min(dp[i][j], 1 + dp[i + 1][j])
                if j <= len(mat[0]) - 2:
                    dp[i][j] = min(dp[i][j], 1 + dp[i][j + 1])
        
        return dp

Partition Equal Subset Sum

Given an integer array nums, return true if you can partition the array into two subsets such that the sum of the elements in both subsets is equal or false otherwise. Tags: Dynamic Programming

Try It!

Discussion

Video

Solution

class Solution:
    def canPartition(self, nums: List[int]) -> bool:
        tot = sum(nums)
        if tot % 2 != 0:
            return False
        half = tot // 2

        dp = [False for i in range(half + 1)]
        dp[0] = True

        for num in nums:
            for i in range(half, -1 , -1):
                if num <= i and dp[i - num]:
                    dp[i] = True
        return dp[-1]

Combination Sum

Given an array of distinct integers candidates and a target integer target, return a list of all unique combinations of candidates where the chosen numbers sum to target. You may return the combinations in any order. Tags: Dynamic Programming

Try It!

Discussion

Video

Solution

class Solution:
    def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:
        dp = [[] for i in range(target + 1)]

        for c in candidates:
            for i in range(c, target + 1):
                if c == i:
                    dp[i].append([c])
                for subcandidate in dp[i - c]:
                    dp[i].append([c] + subcandidate)
        return dp[-1]

Interval

Insert Interval

Given a set of non-overlapping intervals, insert a new interval into the intervals (merge if necessary). You may assume that the intervals were initially sorted according to their start times. Tags: Interval

Try It!

Discussion

Video

Solution

class Solution:
    def insert(self, intervals: List[List[int]], newInterval: List[int]) -> List[List[int]]:
        left = []
        right = []
        for cur in intervals:
            if cur[1] < newInterval[0]:
                left.append(cur)
            elif cur[0] > newInterval[1]:
                right.append(cur)
            else:
                newInterval[0] = min(newInterval[0], cur[0])
                newInterval[1] = max(newInterval[1], cur[1])
        return left + [newInterval] + right

Graph

Clone Graph

Given a reference of a node in a connected undirected graph. Return a deep copy (clone) of the graph. Tags: Graph

Try It!

Discussion

Video

Solution

"""
# Definition for a Node.
class Node:
    def __init__(self, val = 0, neighbors = None):
        self.val = val
        self.neighbors = neighbors if neighbors is not None else []
"""

from collections import deque

class Solution:
    def cloneGraph(self, node: 'Node') -> 'Node':
        if not node:
            return None
        
        # Dictionary to store the mapping of original nodes to cloned nodes
        clones = {}
        
        # Create a clone of the starting node
        clones[node] = Node(node.val)
        
        # Queue for BFS traversal
        queue = deque([node])
        
        while queue:
            curr = queue.popleft()
            
            # Iterate over the neighbors of the current node
            for neighbor in curr.neighbors:
                if neighbor not in clones:
                    # Create a clone of the neighbor if it doesn't exist
                    clones[neighbor] = Node(neighbor.val)
                    queue.append(neighbor)
                
                # Add the cloned neighbor to the cloned node's neighbors list
                clones[curr].neighbors.append(clones[neighbor])
        
        return clones[node]

Course Schedule

Given the total number of courses and a list of prerequisite pairs, is it possible for you to finish all courses? Tags: Graph

Try It!

Discussion

Video

Solution

import collections

class Solution:
    def canFinish(self, numCourses: int, prerequisites: List[List[int]]) -> bool:
        graph = collections.defaultdict(set)
        for x, y in prerequisites:
            graph[x].add(y)
        
        visited = [0 for i in range(numCourses)]

        for node in range(numCourses):
            if not self.dfs(node, graph, visited):
                return False
        
        return True

    def dfs(self, node, graph, visited):
        if visited[node] == -1:
            return False
        if visited[node] == 1:
            return True
        
        visited[node] = -1
        for subnode in graph[node]:
            if not self.dfs(subnode, graph, visited):
                return False
        visited[node] = 1
        return True

Pacific Atlantic Water Flow

Given an m x n matrix of non-negative integers representing the height of each unit cell in a continent, the "Pacific ocean" touches the left and top edges of the matrix and the "Atlantic ocean" touches the right and bottom edges. Water can only flow in four directions (up, down, left, or right) from a cell to another one with height equal or lower. Find the list of grid coordinates where water can flow to both the Pacific and Atlantic ocean. Tags: Graph

Try It!

Discussion

Video

Solution

class Solution:
    def pacificAtlantic(self, matrix: List[List[int]]) -> List[List[int]]:
        if not matrix:
            return []
        
        m = len(matrix)
        n = len(matrix[0])
        
        p_visited = [[False for c in range(n)] for r in range(m)]
        a_visited = [[False for c in range(n)] for r in range(m)]
        res = []
        
        for r in range(m):
            self.dfs(matrix, r, 0, p_visited, m, n)
            self.dfs(matrix, r, n - 1, a_visited, m, n)
        
        for c in range(n):
            self.dfs(matrix, 0, c, p_visited, m, n)
            self.dfs(matrix, m - 1, c, a_visited, m, n)
        
        for r in range(m):
            for c in range(n):
                if p_visited[r][c] and a_visited[r][c]:
                    res.append([r, c])
        return res
    
    def dfs(self, matrix, r, c, visited, m, n):
        directions = [(-1, 0), (1, 0), (0, -1), (0, 1)]
        visited[r][c] = True
        
        for direction in directions:
            next_r = r + direction[0]
            next_c = c + direction[1]
            
            if next_r < 0 or next_r >= m:
                continue
            if next_c < 0 or next_c >= n:
                continue
            if visited[next_r][next_c]:
                continue
            if matrix[next_r][next_c] < matrix[r][c]:
                continue
            
            self.dfs(matrix, next_r, next_c, visited, m, n) 

Flood Fill

Perform a flood fill, consider the starting pixel, plus any pixels connected 4-directionally to the starting pixel of the same color as the starting pixel, plus any pixels connected 4-directionally to those pixels (also with the same color), and so on. Replace the color of all of the aforementioned pixels with color. Return the modified image after performing the flood fill. Tags: Graph

Try It!

Discussion

Video

Solution

class Solution:
    def floodFill(self, image: List[List[int]], sr: int, sc: int, color: int) -> List[List[int]]:
        visited = [[False for j in range(len(image[0]))] for i in range(len(image))]
        prev_color = image[sr][sc]
        self.dfs(image, visited, sr, sc, prev_color, color)
        return image
    
    def dfs(self, image, visited, i, j, prev_color, color):
        if i < 0 or i >= len(image):
            return
        if j < 0 or j >= len(image[0]):
            return
        if image[i][j] != prev_color or visited[i][j]:
            return

        visited[i][j] = True
        image[i][j] = color
        self.dfs(image, visited, i + 1, j, prev_color, color)
        self.dfs(image, visited, i - 1, j, prev_color, color)
        self.dfs(image, visited, i, j + 1, prev_color, color)
        self.dfs(image, visited, i, j - 1, prev_color, color)

Rotting Oranges

Return the minimum number of minutes that must elapse until no cell has a fresh orange. If this is impossible, return -1. Tags: Graph

Try It!

Discussion

Video

Solution

import collections

class Solution:
    def orangesRotting(self, grid: List[List[int]]) -> int:
        queue = collections.deque()
        to_visit = set()

        for i in range(len(grid)):
            for j in range(len(grid[0])):
                if grid[i][j] == 2:
                    queue.appendleft((i, j))
                if grid[i][j] == 1:
                    to_visit.add((i, j))

        num_min = 0

        while queue and to_visit:
            for _ in range(len(queue)):
                i, j = queue.pop()
                for coords in [(i + 1, j), (i - 1, j), (i, j + 1), (i, j - 1)]:
                    if coords in to_visit:
                        to_visit.remove(coords)
                        queue.appendleft(coords)
            num_min += 1

        
        if to_visit:
            return -1
        return num_min

Accounts Merge

Given a list of accounts where each element accounts[i] is a list of strings, where the first element accounts[i][0] is a name, and the rest of the elements are emails representing emails of the account. Now, we would like to merge these accounts. Return the accounts in the following format: the first element of each account is the name, and the rest of the elements are emails in sorted order. Tags: Graph

Try It!

Discussion

Video

Solution

from collections import defaultdict

class Solution:
    def accountsMerge(self, accounts: List[List[str]]) -> List[List[str]]:
        graph = defaultdict(set)
        email_to_name = {}

        for acc in accounts:
            name = acc[0]
            first_email = acc[1]
            for email in acc[1:]:
                graph[email].add(first_email)
                graph[first_email].add(email)
                email_to_name[email] = name
        
        visited = set()
        ans = []
        for email in graph:
            if email not in visited:
                related_emails = []
                self.dfs(email, graph, visited, related_emails)
                name = email_to_name[email]
                ans.append([name] + sorted(related_emails))
        return ans
    
    def dfs(self, email, graph, visited, related_emails):
        visited.add(email)
        related_emails.append(email)

        for nei in graph[email]:
            if nei not in visited:
                self.dfs(nei, graph, visited, related_emails)

Minimum Height Trees

Given a tree of n nodes, return a list of all MHTs' root labels. Tags: Graph

Try It!

Discussion

Video

Solution

import collections

class Solution:
    def findMinHeightTrees(self, n: int, edges: List[List[int]]) -> List[int]:
        if n == 1:
            return [0]

        tree = collections.defaultdict(set)

        for x, y in edges:
            tree[x].add(y)
            tree[y].add(x)

        # only connected to 1 node
        leaves = [i for i in range(n) if len(tree[i]) == 1]

        while n > 2:
            n -= len(leaves)
            new_leaves = []

            for i in leaves:
                # tree[i] will only have 1 elem
                j = tree[i].pop()
                tree[j].remove(i)
                if len(tree[j]) == 1:
                    new_leaves.append(j)
            leaves = new_leaves
        return leaves

Word Ladder

Given two words, beginWord and endWord, and a dictionary wordList, return the number of words in the shortest transformation sequence from beginWord to endWord, or 0 if no such sequence exists. Tags: Graph

Try It!

Discussion

Video

Solution

import collections
import string

class Solution:
    def ladderLength(self, beginWord: str, endWord: str, wordList: List[str]) -> int:
        word_set = set(wordList)
        queue = collections.deque()
        queue.appendleft((beginWord, 1))

        while queue:
            word, length = queue.pop()
            if word == endWord:
                return length
            
            for i in range(len(word)):
                for c in string.ascii_lowercase:
                    new_word = word[:i] + c + word[i + 1:]
                    if new_word in word_set:
                        word_set.remove(new_word)
                        queue.appendleft((new_word, length + 1))
        
        return 0

Matrix

Set Matrix Zeroes

Given an m x n matrix. If an element is 0, set its entire row and column to 0. Do it in-place. Tags: Matrix

Try It!

Discussion

Video

Solution

class Solution:
    def setZeroes(self, matrix: List[List[int]]) -> None:
        """
        Do not return anything, modify matrix in-place instead.
        """
        top_has_zero = False
        for j in range(len(matrix[0])):
            if matrix[0][j] == 0:
                top_has_zero = True
                break
        
        # Create a marker using first row and column
        # 010
        # 110
        # 001
        for i in range(1, len(matrix)):
            for j in range(len(matrix[0])):
                if matrix[i][j] == 0:
                    matrix[i][0] = 0
                    matrix[0][j] = 0
                    
        # Set the zeros, backwards iterating through the row
        for i in range(1, len(matrix)):
            for j in range(len(matrix[0]) - 1, -1, -1):
                if matrix[i][0] == 0 or matrix[0][j] == 0:
                    matrix[i][j] = 0
        
        if top_has_zero:
            for j in range(len(matrix[0])):
                matrix[0][j] = 0

Spiral Matrix

Given a matrix of m x n elements (m rows, n columns), return all elements of the matrix in spiral order. Tags: Matrix

Try It!

Discussion

Video

Solution

class Solution:
    def spiralOrder(self, matrix: List[List[int]]) -> List[int]:
        res = []
        row_lo = 0
        row_hi = len(matrix) - 1
        col_lo = 0
        col_hi = len(matrix[0]) - 1

        while row_lo <= row_hi and col_lo <= col_hi:
            for c in range(col_lo, col_hi + 1):
                res.append(matrix[row_lo][c])
            row_lo += 1

            for r in range(row_lo, row_hi + 1):
                res.append(matrix[r][col_hi])
            col_hi -= 1

            if row_lo <= row_hi:
                for c in range(col_hi, col_lo - 1, -1):
                    res.append(matrix[row_hi][c])
            row_hi -= 1

            if col_lo <= col_hi:
                for r in range(row_hi, row_lo - 1, -1):
                    res.append(matrix[r][col_lo])
            col_lo += 1
        
        return res        

Rotate Image

You are given an n x n 2D matrix representing an image, rotate the image by 90 degrees (clockwise). You have to rotate the image in-place, which means you have to modify the input 2D matrix directly. DO NOT allocate another 2D matrix and do the rotation. Tags: Matrix

Try It!

Discussion

Video

Alternative Solution

class Solution:
    def rotate(self, matrix: List[List[int]]) -> None:
        """
        Do not return anything, modify matrix in-place instead.
        """
        l = 0
        r = len(matrix) - 1

        while l < r:
            for i in range(r - l):
                top = l
                bottom = r

                # save the topleft
                top_left = matrix[top][l + i]
                # move bottom left into top left
                matrix[top][l + i] = matrix[bottom - i][l]
                # move bottom right into bottom left
                matrix[bottom - i][l] = matrix[bottom][r - i]
                # move top right into bottom right
                matrix[bottom][r - i] = matrix[top + i][r]
                # move top left into top right
                matrix[top + i][r] = top_left
            
            r -= 1
            l += 1

Word Search

Given a 2D board and a word, find if the word exists in the grid. The word can be constructed from letters of sequentially adjacent cell, where "adjacent" cells are those horizontally or vertically neighboring. The same letter cell may not be used more than once. Tags: Matrix

Try It!

Discussion

Video

Solution

class Solution:
    def exist(self, board: List[List[str]], word: str) -> bool:
        if word == '':
            return False
        
        visited = [[False for j in range(len(board[0]))] for i in range(len(board))]
        
        for i in range(len(board)):
            for j in range(len(board[0])):
                if self.dfs(board, i, j, visited, word):
                    return True
        return False
    
    def dfs(self, board, i, j, visited, word):
        if word == '':
            return True
        if i < 0 or i >= len(board):
            return False
        if j < 0 or j >= len(board[0]):
            return False
        if visited[i][j]:
            return False
        if word[0] != board[i][j]:
            return False
        visited[i][j] = True
        
        hasWord = self.dfs(board, i - 1, j, visited, word[1:]) \
            or self.dfs(board, i + 1, j, visited, word[1:]) \
            or self.dfs(board, i, j - 1, visited, word[1:]) \
            or self.dfs(board, i, j + 1, visited, word[1:])
        
        visited[i][j] = False
        return hasWord

Tree

Maximum Depth of Binary Tree

Given a binary tree, find its maximum depth. The maximum depth is the number of nodes along the longest path from the root node down to the farthest leaf node. Tags: Tree

Try It!

Discussion

Video

Solution

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def maxDepth(self, root: TreeNode) -> int:
        if not root:
            return 0
        left_depth = self.maxDepth(root.left)
        right_depth = self.maxDepth(root.right)
        return 1 + max(left_depth, right_depth)

Same Tree

Given two binary trees, write a function to check if they are the same or not. Two binary trees are considered the same if they are structurally identical and the nodes have the same value. Tags: Tree

Try It!

Discussion

Video

Solution

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def isSameTree(self, p: TreeNode, q: TreeNode) -> bool:
        if p == None and q == None:
            return True
        if p == None:
            return False
        if q == None:
            return False
        return p.val == q.val and self.isSameTree(p.left, q.left) and self.isSameTree(p.right, q.right)

Invert/Flip Binary Tree

Invert a binary tree. Tags: Tree

Try It!

Discussion

Video

Solution

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def invertTree(self, root: Optional[TreeNode]) -> Optional[TreeNode]:
        if not root:
            return
        tmp_left = self.invertTree(root.right)
        tmp_right = self.invertTree(root.left)
        root.left = tmp_left
        root.right = tmp_right
        return root

Binary Tree Maximum Path Sum

Given a non-empty binary tree, find the maximum path sum. For this problem, a path is defined as any sequence of nodes from some starting node to any node in the tree along the parent-child connections. The path must contain at least one node and does not need to go through the root. Tags: Tree

Try It!

Discussion

Video

Solution

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def maxPathSum(self, root: TreeNode) -> int:
        self.res = float('-inf')
        self.helper(root)
        return self.res
    
    def helper(self, root):
        if not root:
            return 0
        left = self.helper(root.left)
        right = self.helper(root.right)
        if root.val + left + right > self.res:
            self.res = root.val + left + right

        pos_max_path = root.val + max(left, right)
        if pos_max_path > 0:
            return pos_max_path
        else:
            return 0

Binary Tree Level Order Traversal

Given a binary tree, return the level order traversal of its nodes' values. (ie, from left to right, level by level). Tags: Tree

Try It!

Discussion

Video

Solution

import collections

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right

class Solution:
    def levelOrder(self, root: TreeNode) -> List[List[int]]:
        if not root:
            return []
        
        queue = collections.deque()
        queue.appendleft((root, 0))
        res = []
        
        while queue:
            top, level = queue.pop()
            
            if len(res) == level:
                res.append([])
            res[level].append(top.val)
            
            if top.left:
                queue.appendleft((top.left, level + 1))
            if top.right:
                queue.appendleft((top.right, level + 1))
        return res

Serialize and Deserialize Binary Tree

Design an algorithm to serialize and deserialize a binary tree. There is no restriction on how your serialization/deserialization algorithm should work. You just need to ensure that a binary tree can be serialized to a string and this string can be deserialized to the original tree structure. Tags: Tree

Try It!

Discussion

Video

Solution

# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Codec:

    def serialize(self, root):
        """Encodes a tree to a single string.
        
        :type root: TreeNode
        :rtype: str
        """
        if not root:
            return 'n'
        
        val = str(root.val)
        left = self.serialize(root.left)
        right = self.serialize(root.right)
        return val + ',' + left + ',' + right

    def deserialize(self, data):
        """Decodes your encoded data to tree.
        
        :type data: str
        :rtype: TreeNode
        """
        self.data = data
        if self.data[0] == 'n':
            return None
        
        first_break = self.data.find(',')
        node = TreeNode(int(self.data[:first_break]))
        node.left = self.deserialize(self.data[first_break + 1:])
        second_break = self.data.find(',')
        node.right = self.deserialize(self.data[second_break + 1:])
        return node

# Your Codec object will be instantiated and called as such:
# ser = Codec()
# deser = Codec()
# ans = deser.deserialize(ser.serialize(root))

Subtree of Another Tree

Given two non-empty binary trees s and t, check whether tree t has exactly the same structure and node values with a subtree of s. A subtree of s is a tree consists of a node in s and all of this node's descendants. The tree s could also be considered as a subtree of itself. Tags: Tree

Try It!

Discussion

Video

Solution

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def isSubtree(self, s: TreeNode, t: TreeNode) -> bool:
        if s == None or t == None:
            return False
        elif self.compare(s, t):
            return True
        else:
            return self.isSubtree(s.left, t) or self.isSubtree(s.right, t)
    
    def compare(self, s, t):
        if s == None and t == None:
            return True
        elif s == None or t == None:
            return False
        elif s.val == t.val:
            return self.compare(s.left, t.left) and self.compare(s.right, t.right)
        else:
            return False

Validate Binary Search Tree

Given a binary tree, determine if it is a valid binary search tree (BST). Tags: Tree

Try It!

Discussion

Video

Solution

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def isValidBST(self, root: TreeNode) -> bool:
        return self.helper(root, float('-inf'), float('inf'))
    
    def helper(self, root, lower, upper):
        if root is None:
            return True
        elif root.val >= upper:
            return False
        elif root.val <= lower:
            return False
        else:
            return self.helper(root.left, lower, root.val) and self.helper(root.right, root.val, upper)

Kth Smallest Element in a BST

Given a binary search tree, write a function kth Smallest to find the kth smallest element in it. Tags: Tree

Try It!

Discussion

Video

Solution

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def kthSmallest(self, root: TreeNode, k: int) -> int:
        lst = []
        self.helper(root, lst)
        return lst[k - 1]
    
    def helper(self, root, lst):
        # perform an inorder traversal
        if not root:
            return
        
        self.helper(root.left, lst)
        lst.append(root.val)
        self.helper(root.right, lst)

Lowest Common Ancestor of a Binary Search Tree

Given a binary search tree (BST), find the lowest common ancestor (LCA) of two given nodes in the BST. Tags: Tree

Try It!

Discussion

Video

Solution

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution:
    def lowestCommonAncestor(self, root: TreeNode, p: TreeNode, q: TreeNode) -> TreeNode:
        if p.val < root.val and q.val < root.val:
            return self.lowestCommonAncestor(root.left, p, q)
        elif p.val > root.val and q.val > root.val:
            return self.lowestCommonAncestor(root.right, p, q)
        else:
            return root

Implement Trie (Prefix Tree)

Implement a trie with insert, search, and startsWith methods. Tags: Tree

Try It!

Discussion

Video

Solution

class Trie:

    def __init__(self):
        """
        Initialize your data structure here.
        """
        self.root = {}
        

    def insert(self, word: str) -> None:
        """
        Inserts a word into the trie.
        """
        trie = self.root
        for c in word:
            if c not in trie:
                trie[c] = {}
            trie = trie[c]
        trie['#'] = '#'

    def search(self, word: str) -> bool:
        """
        Returns if the word is in the trie.
        """
        trie = self.root
        for c in word:
            if c not in trie:
                return False
            trie = trie[c]
            
        return '#' in trie
        

    def startsWith(self, prefix: str) -> bool:
        """
        Returns if there is any word in the trie that starts with the given prefix.
        """
        trie = self.root
        for c in prefix:
            if c not in trie:
                return False
            trie = trie[c]
            
        return True
        


# Your Trie object will be instantiated and called as such:
# obj = Trie()
# obj.insert(word)
# param_2 = obj.search(word)
# param_3 = obj.startsWith(prefix)

Add and Search Word

Design a data structure that supports adding new words and finding if a string matches any previously added string. Tags: Tree

Try It!

Discussion

Video

Solution

class WordDictionary:

    def __init__(self):
        """
        Initialize your data structure here.
        """
        self.trie = {}
        

    def addWord(self, word: str) -> None:
        level = self.trie
        
        for c in word:
            if c not in level:
                level[c] = {}
            level = level[c]
        level['#'] = '#'
        

    def search(self, word: str) -> bool:
        return self.helper(word, self.trie)
                
    def helper(self, word, level):
        if word == '':
            return '#' in level
        
        c = word[0]
        if c == '.':
            for pos_c in level:
                if pos_c != '#' and self.helper(word[1:], level[pos_c]):
                    return True
            return False
        elif c not in level:
            return False
        else:
            return self.helper(word[1:], level[c])


# Your WordDictionary object will be instantiated and called as such:
# obj = WordDictionary()
# obj.addWord(word)
# param_2 = obj.search(word)

Binary Tree

Diameter of Binary Tree

Given the root of a binary tree, return the length of the diameter of the tree. Tags: Binary Tree

Try It!

Discussion

Video

Solution

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def diameterOfBinaryTree(self, root: Optional[TreeNode]) -> int:
        self.max_diameter = 0
        self.helper(root)
        return self.max_diameter

    def helper(self, root):
        if not root:
            return 0
        left = self.helper(root.left)
        right = self.helper(root.right)
        if left + right > self.max_diameter:
            self.max_diameter = left + right
        return 1 + max(left, right)

Lowest Common Ancestor of a Binary Tree

Given a binary tree, find the lowest common ancestor (LCA) of two given nodes in the tree. Tags: Binary Tree

Try It!

Discussion

Video

Solution

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution:
    def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
        if not root:
            return root
        
        if root == p or root == q:
            return root

        left = self.lowestCommonAncestor(root.left, p, q)
        right = self.lowestCommonAncestor(root.right, p, q)

        if left and right:
            return root
        elif left:
            return left
        elif right:
            return right
        else:
            return None

Binary Tree Right Side View

Given the root of a binary tree, return the length of the diameter of the tree. Tags: Binary Tree

Try It!

Discussion

Video

Solution

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def rightSideView(self, root: Optional[TreeNode]) -> List[int]:
        view = []
        self.helper(root, view, 0)
        return view

    def helper(self, root, view, level):
        if not root:
            return
        
        if level == len(view):
            view.append(root.val)
        self.helper(root.right, view, level + 1)
        self.helper(root.left, view, level + 1)

Binary Search

Binary Search

Given an array of integers nums which is sorted in ascending order, and an integer target, write a function to search target in nums. If target exists, then return its index. Otherwise, return -1. Tags: Binary Search

Try It!

Discussion

Video

Solution

class Solution:
    def search(self, nums: List[int], target: int) -> int:
        lo = 0
        hi = len(nums) - 1

        while lo <= hi:
            mid = (lo + hi) // 2
            if nums[mid] == target:
                return mid
            elif nums[mid] < target:
                lo = mid + 1
            else:
                hi = mid - 1
        return -1

First Bad Version

You are a product manager and currently leading a team to develop a new product. Unfortunately, the latest version of your product fails the quality check. Since each version is developed based on the previous version, all the versions after a bad version are also bad. Suppose you have n versions [1, 2, ..., n] and you want to find out the first bad one, which causes all the following ones to be bad. Tags: Binary Search

Try It!

Discussion

Video

Solution

# The isBadVersion API is already defined for you.
# def isBadVersion(version: int) -> bool:

class Solution:
    def firstBadVersion(self, n: int) -> int:
        lo = 0
        hi = n
        while lo <= hi:
            mid = (lo + hi) // 2
            if isBadVersion(mid):
                hi = mid - 1
            else:
                lo = mid + 1
        return lo

Time Based Key-Value Store

Design a time-based key-value data structure that can store multiple values for the same key at different time stamps and retrieve the key's value at a certain timestamp. Tags: Binary Search

Try It!

Discussion

Video

Solution

import collections

class TimeMap:

    def __init__(self):
        self.store = collections.defaultdict(list)

    def set(self, key: str, value: str, timestamp: int) -> None:
        self.store[key].append([value, timestamp])

    def get(self, key: str, timestamp: int) -> str:
        if key not in self.store:
            return ''
        
        ans = ''
        values = self.store[key]
        lo = 0
        hi = len(values) - 1
        
        while lo <= hi:
            mid = (lo + hi) // 2
            if values[mid][1] <= timestamp:
                ans = values[mid][0]
                lo = mid + 1
            else:
                hi = mid - 1
        return ans

# Your TimeMap object will be instantiated and called as such:
# obj = TimeMap()
# obj.set(key,value,timestamp)
# param_2 = obj.get(key,timestamp)

Maximum Profit in Job Scheduling

We have n jobs, where every job is scheduled to be done from startTime[i] to endTime[i], obtaining a profit of profit[i]. You're given the startTime, endTime and profit arrays, return the maximum profit you can take such that there are no two jobs in the subset with overlapping time range. Tags: Binary Search, Dynamic Programming

Try It!

Discussion

Video

Solution

class Solution:
    def jobScheduling(self, startTime: List[int], endTime: List[int], profit: List[int]) -> int:
        jobs = sorted(zip(startTime, endTime, profit), key=lambda x: x[1])
        # end, curr_profit
        dp = [(0, 0)]

        for start, end, profit in jobs:
            idx = self.binary_search(dp, start)
            
            last_profit = dp[-1][1]
            curr_profit = dp[idx - 1][1] + profit

            if curr_profit > last_profit:
                dp.append((end, curr_profit))
        return dp[-1][1]

    def binary_search(self, dp, target):
        lo = 0
        hi = len(dp) - 1

        while lo <= hi:
            mid = (lo + hi) // 2
            if dp[mid][0] <= target:
                lo = mid + 1
            else:
                hi = mid - 1
        return lo

Binary Search Tree

Balanced Binary Search

Given a binary tree, determine if it is height-balanced. Tags: Binary Search Tree

Try It!

Discussion

Video

Solution

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def isBalanced(self, root: Optional[TreeNode]) -> bool:
        return self.helper(root)[0]

    def helper(self, root):
        if not root:
            return [True, 0]
        
        left = self.helper(root.left)
        right = self.helper(root.right)

        balanced = left[0] and right[0] and abs(left[1] - right[1]) <= 1
        depth = 1 + max(left[1], right[1])
        return [balanced, depth]

Recursion

Permutations

Given an array nums of distinct integers, return all the possible permutations. You can return the answer in any order. Tags: Search, Backtracking

Try It!

Discussion

Video

Solution

class Solution:
    def permute(self, nums: List[int]) -> List[List[int]]:
        res = []
        path = []
        self.dfs(nums, path, res)
        return res
    
    def dfs(self, nums, path, res):
        if nums == []:
            res.append(path)
            return
        
        # perform backtracking
        for i in range(len(nums)):
            self.dfs(nums[:i] + nums[i + 1:], path + [nums[i]], res)

Subsets

Given an integer array nums of unique elements, return all possible subsets (the power set). Tags: Search, Backtracking

Try It!

Discussion

Video

Solution

class Solution:
    def subsets(self, nums: List[int]) -> List[List[int]]:
        res = []
        path = []
        self.dfs(nums, path, res)
        return res
    
    def dfs(self, nums, path, res):
        res.append(path)
        
        # perform backtracking
        for i in range(len(nums)):
            self.dfs(nums[:i], path + [nums[i]], res)

Letter Combinations of a Phone Number

Given a string containing digits from 2-9 inclusive, return all possible letter combinations that the number could represent. Return the answer in any order. Tags: Recursion

Try It!

Discussion

Video

Solution

class Solution:
    def letterCombinations(self, digits: str) -> List[str]:
        mapping = {
            '2': 'abc',
            '3': 'def',
            '4': 'ghi',
            '5': 'jkl',
            '6': 'mno',
            '7': 'pqrs',
            '8': 'tuv',
            '9': 'wxyz'
        }

        if not digits:
            return []

        ans = []
        self.dfs(digits, '', ans, mapping)
        return ans
    
    def dfs(self, digits, path, ans, mapping):
        if not digits:
            ans.append(path)
            return
        
        d = digits[0]
        characters = mapping[d]
        for c in characters:
            self.dfs(digits[1:], path + c, ans, mapping)

Linked List

Reverse Linked List

Reverse a singly linked list. Tags: Linked List

Try It!

Discussion

Video

Solution

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def reverseList(self, head: ListNode) -> ListNode:
        first = head
        second = None
        
        while first:
            temp = first.next
            first.next = second
            second = first
            first = temp
            
        return second

Detect Cycle in a Linked List

Given head, the head of a linked list, determine if the linked list has a cycle in it. Tags: Linked List

Try It!

Discussion

Video

Solution

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def hasCycle(self, head: ListNode) -> bool:
        fast = head
        slow = head
        while fast and fast.next:
            fast = fast.next.next
            slow = slow.next
            if fast == slow:
                return True
        return False

Merge Two Sorted Lists

Merge two sorted linked lists and return it as a new sorted list. The new list should be made by splicing together the nodes of the first two lists. Tags: Linked List

Try It!

Discussion

Video

Solution

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def mergeTwoLists(self, l1: ListNode, l2: ListNode) -> ListNode:
        dummy = ListNode("dummy")
        cur = dummy
        
        while l1 and l2:
            if l1.val < l2.val:
                cur.next = l1
                l1 = l1.next
            else:
                cur.next = l2
                l2 = l2.next
            cur = cur.next

        if l1:
            cur.next = l1
        if l2:
            cur.next = l2
        
        return dummy.next        

Remove Nth Node From End Of List

Given a linked list, remove the n-th node from the end of list and return its head. Tags: Linked List

Try It!

Discussion

Video

Solution

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def removeNthFromEnd(self, head: ListNode, n: int) -> ListNode:
        dummy = ListNode('dummy')
        dummy.next = head
        
        first = dummy
        for i in range(n + 1):
            first = first.next
        
        second = dummy
        while first:
            first = first.next
            second = second.next
        
        second.next = second.next.next
        
        return dummy.next

Reorder List

Given a singly linked list L: L0->L1->...->Ln-1->Ln, reorder it to: L0->Ln->L1->Ln-1->L2->Ln-2->... Tags: Linked List

Try It!

Discussion

Video

Solution

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def reorderList(self, head: ListNode) -> None:
        """
        Do not return anything, modify head in-place instead.
        """
        if not head:
            return
        
        fast = head.next
        slow = head
        # ensure the first part has the same or one more node
        while fast and fast.next:
            fast = fast.next.next
            slow = slow.next
        
        # reverse the second half
        cur = slow.next
        slow.next = None
        prev = None
        while cur:
            tmp = cur.next
            cur.next = prev
            prev = cur
            cur = tmp
        
        # combine first part and second part
        first = head
        second = prev
        while second:
            tmp = second.next
            second.next = first.next
            first.next = second
            first = first.next.next
            second = tmp  

Middle of the Linked List

Given the head of a singly linked list, return the middle node of the linked list. Tags: Linked List

Try It!

Discussion

Video

Solution

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def middleNode(self, head: Optional[ListNode]) -> Optional[ListNode]:
        fast = head
        slow = head
        while fast and fast.next:
            fast = fast.next.next
            slow = slow.next
        
        return slow

LRU Cache

Design a data structure that follows the constraints of a Least Recently Used (LRU) cache. Tags: Linked List

Try It!

Discussion

Video

Solution

class Node:
    def __init__(self, key=None, value=None):
        self.key = key
        self.value = value
        self.prev = None
        self.next = None

class LRUCache:
    def __init__(self, capacity: int):
        self.capacity = capacity
        self.cache = {}
        self.head = Node()
        self.tail = Node()
        self.head.next = self.tail
        self.tail.prev = self.head

    def get(self, key: int) -> int:
        if key in self.cache:
            node = self.cache[key]
            self._remove(node)
            self._add(node)
            return node.value
        return -1

    def put(self, key: int, value: int) -> None:
        if key in self.cache:
            self._remove(self.cache[key])
        node = Node(key, value)
        self._add(node)
        self.cache[key] = node
        if len(self.cache) > self.capacity:
            lru_node = self.head.next
            self._remove(lru_node)
            del self.cache[lru_node.key]

    def _remove(self, node):
        node.prev.next = node.next
        node.next.prev = node.prev

    def _add(self, node):
        node.prev = self.tail.prev
        node.next = self.tail
        self.tail.prev.next = node
        self.tail.prev = node

Hash Table

Ransom Note

Given two strings ransomNote and magazine, return true if ransomNote can be constructed by using the letters from magazine and false otherwise. Each letter in magazine can only be used once in ransomNote. Tags: Hash Table

Try It!

Discussion

Video

Solution

import collections

class Solution:
    def canConstruct(self, ransomNote: str, magazine: str) -> bool:
        ransom_counter = collections.Counter(ransomNote)

        for c in magazine:
            if c in ransom_counter:
                ransom_counter[c] -= 1
        
        for c in ransom_counter:
            if ransom_counter[c] > 0:
                return False
        
        return True

Stack

Implement Queue using Stacks

Implement a first in first out (FIFO) queue using only two stacks. The implemented queue should support all the functions of a normal queue (push, peek, pop, and empty). Tags: Stack

Try It!

Discussion

Video

Solution

class MyQueue:

    def __init__(self):
        self.stack = []
        self.stack_tmp = []

    def push(self, x: int) -> None:
        self.stack.append(x)
        

    def pop(self) -> int:
        first = None
        while self.stack:
            if len(self.stack) == 1:
                first = self.stack.pop()
            else:
                self.stack_tmp.append(self.stack.pop())
        while self.stack_tmp:
            self.stack.append(self.stack_tmp.pop())
        return first

    def peek(self) -> int:
        first = None
        while self.stack:
            if len(self.stack) == 1:
                first = self.stack.pop()
                self.stack_tmp.append(first)
            else:
                self.stack_tmp.append(self.stack.pop())
        while self.stack_tmp:
            self.stack.append(self.stack_tmp.pop())
        return first
        

    def empty(self) -> bool:
        return not self.stack

# Your MyQueue object will be instantiated and called as such:
# obj = MyQueue()
# obj.push(x)
# param_2 = obj.pop()
# param_3 = obj.peek()
# param_4 = obj.empty()

Evaluate Reverse Polish Notation

You are given an array of strings tokens that represents an arithmetic expression in a Reverse Polish Notation. Evaluate the expression. Return an integer that represents the value of the expression. Tags: Stack

Try It!

Discussion

Video

Solution

class Solution:
    def evalRPN(self, tokens: List[str]) -> int:
        stack = []

        for c in tokens:
            if c not in '+-/*':
                stack.append(int(c))
            else:
                b = stack.pop()
                a = stack.pop()
                if c == '+':
                    stack.append(a + b)
                elif c == '-':
                    stack.append(a - b)
                elif c == '*':
                    stack.append(a * b)
                else:
                    stack.append(int(a / b))

        return stack.pop()

Min Stack

Design a stack that supports push, pop, top, and retrieving the minimum element in constant time. Tags: Stack

Try It!

Discussion

Video

Solution

class MinStack:

    def __init__(self):
        self.stack = []
        

    def push(self, val: int) -> None:
        if not self.stack:
            self.stack.append((val, val))
        else:
            min_elem = min(val, self.stack[-1][1])
            self.stack.append((val, min_elem))
        

    def pop(self) -> None:
        if self.stack:
            self.stack.pop()

    def top(self) -> int:
        if self.stack:
            return self.stack[-1][0]

    def getMin(self) -> int:
        if self.stack:
            return self.stack[-1][1]

# Your MinStack object will be instantiated and called as such:
# obj = MinStack()
# obj.push(val)
# obj.pop()
# param_3 = obj.top()
# param_4 = obj.getMin()

Basic Calculator

Given a string s representing a valid expression, implement a basic calculator to evaluate it, and return the result of the evaluation. Tags: Stack

Try It!

Discussion

Video

Solution

class Solution:
    def calculate(self, s: str) -> int:
        stack = []
        sign = 1
        num = 0

        for c in s:
            if str.isdigit(c):
                num = 10 * num + int(c)
            elif c in '+-':
                stack.append(sign * num)
                num = 0
                if c == '+':
                    sign = 1
                elif c == '-':
                    sign = -1
            elif c == '(':
                stack.append(sign)
                stack.append(c)
                sign = 1
            elif c == ')':
                tmp = sign * num
                while stack[-1] != '(':
                    tmp += stack.pop()
                stack.pop() # '('
                prev_sign = stack.pop()
                stack.append(prev_sign * tmp)
                num = 0
                sign = 1

        return sum(stack) + sign * num

Largest Rectangle in Histogram

Given an array of integers heights representing the histogram's bar height where the width of each bar is 1, return the area of the largest rectangle in the histogram. Tags: Stack

Try It!

Discussion

Video

Solution

class Solution:
    def largestRectangleArea(self, heights: List[int]) -> int:
        stack = []
        max_area = 0

        for i in range(len(heights)):
            h = heights[i]
            start = i
            while stack and stack[-1][1] > h:
                idx, old_h = stack.pop()
                start = idx
                area = old_h * (i - start)
                max_area = max(max_area, area) 
            stack.append((start, h))

        for i, h in stack:
            area = h * (len(heights) - i)
            max_area = max(max_area, area)
        
        return max_area

Heap

Merge K Sorted Lists

You are given an array of k linked-lists lists, each linked-list is sorted in ascending order. Tags: Heap, Linked List

Try It!

Discussion

Video

Solution

Simple solution creating new nodes

import heapq

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def mergeKLists(self, lists: List[ListNode]) -> ListNode:
        min_heap = []
        
        for node in lists:
            while node != None:
                heapq.heappush(min_heap, node.val)        
                node = node.next
        
        dummy = ListNode("dummy")
        cur = dummy
        
        while min_heap:
            min_val = heapq.heappop(min_heap)
            cur.next = ListNode(min_val)
            cur = cur.next
        
        return dummy.next

Reuse nodes fix comparison with Monkey Patching and remove cycles

import heapq

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def mergeKLists(self, lists: List[Optional[ListNode]]) -> Optional[ListNode]:
        ListNode.__lt__ = lambda self, other: self.val < other.val

        max_heap = []
        for node in lists:
            while node:
                heapq.heappush(max_heap, node)
                node = node.next
        
        dummy = ListNode()
        cur = dummy
        while max_heap:
            node = heapq.heappop(max_heap)
            cur.next = node
            cur = cur.next
        cur.next = None
        return dummy.next

Top K Frequent Elements

Given a non-empty array of integers, return the k most frequent elements. Tags: Heap

Try It!

Discussion

Video

Solution

import heapq
from collections import Counter

class Solution:
    def topKFrequent(self, nums: List[int], k: int) -> List[int]:
        counter = Counter(nums)
        heap = []
        for num in counter:
            count = counter[num]
            heapq.heappush(heap, (-count, num))
        
        ans = []
        for i in range(k):
            neg_count, num = heapq.heappop(heap)
            ans.append(num)
        return ans

Find Median from Data Stream

Median is the middle value in an ordered integer list. If the size of the list is even, there is no middle value. So the median is the mean of the two middle value. Tags: Heap

Try It!

Discussion

Video

Solution

import heapq

class MedianFinder:

    def __init__(self):
        self.lower = []
        self.greater = []

    def addNum(self, num: int) -> None:
        if len(self.lower) == len(self.greater):
            heapq.heappush(self.greater, num)
            greater_min = heapq.heappop(self.greater)
            heapq.heappush(self.lower, -greater_min)
        else:
            heapq.heappush(self.lower, -num)
            lower_max = heapq.heappop(self.lower)
            heapq.heappush(self.greater, -lower_max)

    def findMedian(self) -> float:
        if len(self.lower) == len(self.greater):
            return (-self.lower[0] + self.greater[0]) / 2
        else:
            return -self.lower[0]

# Your MedianFinder object will be instantiated and called as such:
# obj = MedianFinder()
# obj.addNum(num)
# param_2 = obj.findMedian()

K Closest Points to Origin

Given an array of points where points[i] = [xi, yi] represents a point on the X-Y plane and an integer k, return the k closest points to the origin (0, 0). Tags: Heap

Try It!

Discussion

Video

Solution

import heapq
import math

class Solution:
    def kClosest(self, points: List[List[int]], k: int) -> List[List[int]]:
        min_heap = []

        for point in points:
            dist = math.sqrt(point[0] ** 2 + point[1] ** 2)
            heapq.heappush(min_heap, (dist, point))
    
        ans = []
        for i in range(k):
            dist, point = heapq.heappop(min_heap)
            ans.append(point)
        return ans

Binary

Sum of Two Integers

Calculate the sum of two integers a and b, but you are not allowed to use the operator + and -. Tags: Binary

Try It!

Discussion

Video

Solution

class Solution:
    def getSum(self, a: int, b: int) -> int:
        mask = 0xffffffff
        
        while b & mask != 0:
            carry = a & b
            a ^= b
            b = carry << 1
        
        # if our answer was negative b/carry would keep on being shifted outside of 32 bits 
        if b == 0:
            return a
        else:
            return a & mask

Number of 1 Bits

Write a function that takes an unsigned integer and returns the number of '1' bits it has (also known as the Hamming weight). Tags: Binary

Try It!

Discussion

Video

Solution

class Solution:
    def hammingWeight(self, n: int) -> int:
        count = 0
        while n:
            if n & 1:
                count += 1
            n >>= 1
        return count

Counting Bits

Given a non negative integer number num. For every numbers i in the range 0 <= i <= num calculate the number of 1's in their binary representation and return them as an array. Tags: Binary, Dynamic Programming

Try It!

Discussion

Video

Solution

class Solution:
    def countBits(self, num: int) -> List[int]:
        # numbers can be represented as 2n or 2n + 1
        # numBits(2n) == numBits(n)
        # numBits(2n + 1) == numBit(n) + 1
        
        dp = [0 for i in range(num + 1)]
        
        for i in range(1, num + 1):
            dp[i] = dp[i // 2] + (i % 2)
            # or with bit operators
            # dp[i] = dp[i >> 1] + (i & 1)
            
        return dp

Missing Number

Given an array containing n distinct numbers taken from 0, 1, 2, ..., n, find the one that is missing from the array. Tags: Binary

Try It!

Discussion

Video

Solution

class Solution:
    def missingNumber(self, nums: List[int]) -> int:
        res = len(nums)
        for i in range(len(nums)):
            res = res ^ i ^ nums[i]
        return res

Reverse Bits

Reverse bits of a given 32 bits unsigned integer. Tags: Binary

Try It!

Discussion

Video

Solution

class Solution:
    def reverseBits(self, n: int) -> int:
        res = 0
        for i in range(32):
            res <<= 1
            res |= n & 1
            n >>= 1
        return res

Add Binary

Given two binary strings a and b, return their sum as a binary string. Tags: Binary

Try It!

Discussion

Video

Solution

class Solution:
    def addBinary(self, a: str, b: str) -> str:
        carry = 0
        result = ''
        a = list(a)
        b = list(b)

        while a or b or carry:
            if a:
                carry += int(a.pop())
            if b:
                carry += int(b.pop())
            result += str(carry % 2)
            carry //= 2
        
        return result[::-1]

Meta

Valid Word Abbreviation

Given a string word and an abbreviation abbr, return whether the string matches the given abbreviation. Tags: String

Try It!

Valid Palindrome II

Given a string s, return true if the s can be palindrome after deleting at most one character from it. Tags: String

Try It!

Discussion

Video

Solution

class Solution:
    def validPalindrome(self, s: str) -> bool:
        p1 = 0
        p2 = len(s) - 1

        while p1 < p2:
            if s[p1] == s[p2]:
                p1 += 1
                p2 -= 1
            else:
                return self.is_palindrome(s, p1 + 1, p2) or self.is_palindrome(s, p1, p2 - 1)

        return True

    def is_palindrome(self, s: str, p1: int, p2: int) -> bool:
        while p1 < p2:
            if s[p1] == s[p2]:
                p1 += 1
                p2 -= 1
            else:
                return False

        return True

This is O(N) becaues the first while checks until a mismatch. Then the second while checks if it can be a palindrome without one of the characters.

Range Sum of BST

Given the root node of a binary search tree and two integers low and high, return the sum of values of all nodes with a value in the inclusive range [low, high]. Tags: Tree

Try It!

Moving Average from Data Stream

Given a stream of integers and a window size, calculate the moving average of all integers in the sliding window. Implement the MovingAverage class. Tags: Queue

Try It!

Diameter of Binary Tree

Given the root of a binary tree, return the length of the diameter of the tree. Tags: Binary Tree

Try It!

Closest Binary Search Tree Value

Given the root of a binary search tree and a target value, return the value in the BST that is closest to the target. If there are multiple answers, print the smallest. Tags: Binary Search Tree

Try It!

Binary Tree Vertical Order Traversal

Given the root of a binary tree, return the vertical order traversal of its nodes' values. (i.e., from top to bottom, column by column). Tags: Binary Tree

Try It!

Minimum Remove to Make Valid Parentheses

Given a string s of '(' , ')' and lowercase English characters.

Your task is to remove the minimum number of parentheses ( '(' or ')', in any positions ) so that the resulting parentheses string is valid and return any valid string. Tags: String, Stack

Try It!

Solution

class Solution:
    def minRemoveToMakeValid(self, s: str) -> str:
        stack = [] 
        to_remove = set()

        for i, c in enumerate(s):
            if c == '(':
                stack.append(i)
            elif c == ')':
                if not stack:
                    to_remove.add(i)
                else:
                    stack.pop()

        to_remove.update(stack) 

        ans = []
        for i, c in enumerate(s):
            if i not in to_remove:
                ans.append(c)

        return ''.join(ans)

Follow up, solve this without any additional data structures

class Solution:
    def minRemoveToMakeValid(self, s: str) -> str:
        # Convert the string to a list for efficient character manipulation
        s_list = list(s)
        
        # First pass: remove invalid closing parentheses
        open_count = 0
        for i in range(len(s_list)):
            if s_list[i] == '(':
                open_count += 1
            elif s_list[i] == ')':
                if open_count == 0:
                    s_list[i] = ''
                else:
                    open_count -= 1
        
        # Second pass: remove invalid opening parentheses
        open_count = 0
        for i in range(len(s_list) - 1, -1, -1):
            if s_list[i] == ')':
                open_count += 1
            elif s_list[i] == '(':
                if open_count == 0:
                    s_list[i] = ''
                else:
                    open_count -= 1
        
        # Convert the list back to a string and return
        return ''.join(s_list)

Lowest Common Ancestor of a Binary Tree III

Given two nodes of a binary tree p and q, return their lowest common ancestor (LCA). Each node will have a reference to its parent node. Tags: Binary Tree

Try It!

Nested List Weight Sum

You are given a nested list of integers nestedList. Each element is either an integer or a list whose elements may also be integers or other lists. The depth of an integer is the number of lists that it is inside of. For example, the nested list [1,[2,2],[[3],2],1] has each integer's value set to its depth. Return the sum of each integer in nestedList multiplied by its depth.

Try It!

Dot Product of Two Sparse Vectors

Try It!

Buildings With an Ocean View

There are n buildings in a line. You are given an integer array heights of size n that represents the heights of the buildings in the line. The ocean is to the right of the buildings. A building has an ocean view if the building can see the ocean without obstructions. Formally, a building has an ocean view if all the buildings to its right have a smaller height. Return a list of indices (0-indexed) of buildings that have an ocean view, sorted in increasing order. Tags: Array

Try It!

Basic Calculator II

Given a string s which represents an expression, evaluate this expression and return its value. Tags: String, Stack

Try It!

Discussion

Video

Solution

class Solution:
    def calculate(self, s: str) -> int:
        total = 0
        operator = '+'
        num = 0
        past_num = 0

        for i, c in enumerate(s):
            if c.isdigit():
                num = 10 * num + int(c)
            if c in '+-*/' or i == len(s) - 1:
                if operator == '+':
                    total += past_num
                    past_num = num
                elif operator == '-':
                    total += past_num
                    past_num = -num
                elif operator == '*':
                    past_num *= num
                elif operator == '/':
                    past_num = int(past_num / num)
                num = 0
                operator = c

        return total + past_num

*/ Don't have result += because of order of operations.

You have to watch out if c in '+-*/' or i == len(s) - 1: not elif because c can be a digit and the last number.

Stack solution

class Solution:
    def calculate(self, s: str) -> int:
        stack = []
        num = 0
        operator = '+'

        for i, c in enumerate(s):
            if c.isdigit():
                num = 10 * num + int(c)
            if c in '+-*/' or i == len(s) - 1:
                if operator == '+':
                    stack.append(num)
                elif operator == '-':
                    stack.append(-num)
                elif operator == '*':
                    stack[-1] *= num
                elif operator == '/':
                    stack[-1] = int(stack[-1] / num)
                num = 0
                operator = c

        return sum(stack)

Random Pick with Weight

You are given a 0-indexed array of positive integers w where w[i] describes the weight of the ith index. You need to implement the function pickIndex(), which randomly picks an index in the range [0, w.length - 1] (inclusive) and returns it. The probability of picking an index i is w[i] / sum(w). Tags: Array, Prefix Sum

Try It!

Kth Largest Element in an Array

Given an integer array nums and an integer k, return the kth largest element in the array. Tags: Sorting, Heap

Try It!

Simplify Path

Given a string path, which is an absolute path (starting with a slash '/') to a file or directory in a Unix-style file system, convert it to the simplified canonical path. Tags: String

Try It!

Convert Binary Search Tree to Sorted Doubly Linked List

Convert a Binary Search Tree to a sorted Circular Doubly-Linked List in place. Tags: Linked List, Binary Search Tree

Try It!

Lowest Common Ancestor of a Binary Tree

Given a binary tree, find the lowest common ancestor (LCA) of two given nodes in the tree. Tags: Binary Tree

Try It!

Discussion

Video

Solution

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution:
    def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
        if not root:
            return root
        
        if root == p or root == q:
            return root

        left = self.lowestCommonAncestor(root.left, p, q)
        right = self.lowestCommonAncestor(root.right, p, q)

        if left and right:
            return root
        elif left:
            return left
        elif right:
            return right
        else:
            return None

Minimum Add to Make Parentheses Valid

Return the minimum number of moves required to make s valid. Tags: Stack

Try It!

Pow(x, n)

Implement pow(x, n), which calculates x raised to the power n (i.e., xn). Tags:

Try It!

Sum Root to Leaf Numbers

Return the total sum of all root-to-leaf numbers. Test cases are generated so that the answer will fit in a 32-bit integer. Tags: Tree

Try It!

K Closest Points to Origin

Given an array of points where points[i] = [xi, yi] represents a point on the X-Y plane and an integer k, return the k closest points to the origin (0, 0). Tags: Heap

Try It!

Find Peak Element

Given a 0-indexed integer array nums, find a peak element, and return its index. If the array contains multiple peaks, return the index to any of the peaks. Tags: Binary Search

Try It!

Insert into a Sorted Circular Linked List

Try It!

Binary Tree Right Side View

Given the root of a binary tree, imagine yourself standing on the right side of it, return the values of the nodes you can see ordered from top to bottom. Tags: Binary Tree

Try It!

Interval List Intersections

You are given two lists of closed intervals, firstList and secondList, where firstList[i] = [starti, endi] and secondList[j] = [startj, endj]. Each list of intervals is pairwise disjoint and in sorted order. Return the intersection of these two interval lists. Tags: Array

Try It!

Custom Sort String

Permute the characters of s so that they match the order that order was sorted. More specifically, if a character x occurs before a character y in order, then x should occur before y in the permuted string. Tags: String

Try It!

Shortest Path in Binary Matrix

Given an n x n binary matrix grid, return the length of the shortest clear path in the matrix. If there is no clear path, return -1. Tags: BFS, A*

Try It!

Merge Intervals

Try It!

Group Shifted Strings

Given an array of strings strings, group all strings[i] that belong to the same shifting sequence. You may return the answer in any order. Tags: String

Try It!

LRU Cache

Design a data structure that follows the constraints of a Least Recently Used (LRU) cache. Tags: Linked List

Try It!

Discussion

Video

Solution

class Node:
    def __init__(self, key=None, value=None):
        self.key = key
        self.value = value
        self.prev = None
        self.next = None

class LRUCache:
    def __init__(self, capacity: int):
        self.capacity = capacity
        self.cache = {}
        self.head = Node()
        self.tail = Node()
        self.head.next = self.tail
        self.tail.prev = self.head

    def get(self, key: int) -> int:
        if key in self.cache:
            node = self.cache[key]
            self._remove(node)
            self._add(node)
            return node.value
        return -1

    def put(self, key: int, value: int) -> None:
        if key in self.cache:
            self._remove(self.cache[key])
        node = Node(key, value)
        self._add(node)
        self.cache[key] = node
        if len(self.cache) > self.capacity:
            lru_node = self.head.next
            self._remove(lru_node)
            del self.cache[lru_node.key]

    def _remove(self, node):
        node.prev.next = node.next
        node.next.prev = node.prev

    def _add(self, node):
        node.prev = self.tail.prev
        node.next = self.tail
        self.tail.prev.next = node
        self.tail.prev = node

Exclusive Time of Functions

Return the exclusive time of each function in an array, where the value at the ith index represents the exclusive time for the function with ID i. Tags: Array, Stack

Try It!

Maximum Swap

You are given an integer num. You can swap two digits at most once to get the maximum valued number. Return the maximum valued number you can get. Tags:

Try It!

Random Pick Index

Given an integer array nums with possible duplicates, randomly output the index of a given target number. You can assume that the given target number must exist in the array. Tags: Hash Table

Try It!

Copy List with Random Pointer

Try It!

Subarray Sum Equals K

Given an array of integers nums and an integer k, return the total number of subarrays whose sum equals to k. Tags: Array

Try It!

Diagonal Traverse

Given an m x n matrix mat, return an array of all the elements of the array in a diagonal order. Tags: Array

Try It!

Next Permutation

Given an array of integers nums, find the next permutation of nums. Tags:

Try It!

Top K Frequent Elements

Given a non-empty array of integers, return the k most frequent elements. Tags: Heap

Try It!

Discussion

Video

Solution

import heapq
from collections import Counter

class Solution:
    def topKFrequent(self, nums: List[int], k: int) -> List[int]:
        counter = Counter(nums)
        heap = []
        for num in counter:
            count = counter[num]
            heapq.heappush(heap, (-count, num))
        
        ans = []
        for i in range(k):
            neg_count, num = heapq.heappop(heap)
            ans.append(num)
        return ans

3Sum

Given an array nums of n integers, are there elements a, b, c in nums such that a + b + c = 0? Find all unique triplets in the array which gives the sum of zero. Tags: Array

Try It!

Discussion

Video

Solution

class Solution:
    def threeSum(self, nums: List[int]) -> List[List[int]]:
        nums.sort()
        ans = []

        for i in range(len(nums) - 2):
            if i == 0 or nums[i] != nums[i - 1]:
                self.two_sum(nums, i, ans)
        
        return ans

    def two_sum(self, nums, i, ans):
        target = -nums[i]
        j = i + 1
        k = len(nums) - 1
        while j < k:
            if nums[j] + nums[k] < target:
                j += 1
            elif nums[j] + nums[k] > target:
                k -= 1
            else:
                ans.append([nums[i], nums[j], nums[k]])

                while j < k and nums[j] == nums[j + 1]:
                    j += 1
                while j < k and nums[k] == nums[k - 1]:
                    k -= 1
                j += 1
                k -= 1

Max Consecutive Ones III

Given a binary array nums and an integer k, return the maximum number of consecutive 1's in the array if you can flip at most k 0's. Tags: Array

Try It!

All Nodes Distance K in Binary Tree

Given the root of a binary tree, the value of a target node target, and an integer k, return an array of the values of all nodes that have a distance k from the target node. Tags: Tree

Try It!

Accounts Merge

Given a list of accounts where each element accounts[i] is a list of strings, where the first element accounts[i][0] is a name, and the rest of the elements are emails representing emails of the account. Now, we would like to merge these accounts. Return the accounts in the following format: the first element of each account is the name, and the rest of the elements are emails in sorted order. Tags: Graph

Try It!

Discussion

Video

Solution

from collections import defaultdict

class Solution:
    def accountsMerge(self, accounts: List[List[str]]) -> List[List[str]]:
        graph = defaultdict(set)
        email_to_name = {}

        for acc in accounts:
            name = acc[0]
            first_email = acc[1]
            for email in acc[1:]:
                graph[email].add(first_email)
                graph[first_email].add(email)
                email_to_name[email] = name
        
        visited = set()
        ans = []
        for email in graph:
            if email not in visited:
                related_emails = []
                self.dfs(email, graph, visited, related_emails)
                name = email_to_name[email]
                ans.append([name] + sorted(related_emails))
        return ans
    
    def dfs(self, email, graph, visited, related_emails):
        visited.add(email)
        related_emails.append(email)

        for nei in graph[email]:
            if nei not in visited:
                self.dfs(nei, graph, visited, related_emails)

Design Tic-Tac-Toe

Implement the TicTacToe class for the tic-tac-toe game on an n x n board between two players. Tags: Array

Try It!

Product of Two Run-Length Encoded Arrays

Return the product of two run-length encoded arrays. Tags: Array

Try It!

Number of Islands

Given a 2-D Array, count the number of islands in it, where land is connected in the NSEW direction. Tags: Recursion, Search, Union-Find

Try It!

Discussion

Video

Solution

class Solution:
    def numIslands(self, grid: List[List[str]]) -> int:
        count = 0

        for i in range(len(grid)):
            for j in range(len(grid[0])):
                if grid[i][j] == '1':
                    self.dfs_marking(grid, i, j);
                    count += 1
                    
        return count
    
    def dfs_marking(self, grid, i, j):
        if i < 0 or i >= len(grid):
            return
        if j < 0 or j >= len(grid[0]):
            return
        if grid[i][j] != '1':
            return
        
        grid[i][j] = '0'
        self.dfs_marking(grid, i + 1, j)
        self.dfs_marking(grid, i - 1, j)
        self.dfs_marking(grid, i, j + 1)
        self.dfs_marking(grid, i, j - 1)

Clone Graph

Given a reference of a node in a connected undirected graph. Return a deep copy (clone) of the graph. Tags: Graph

Try It!

Discussion

Video

Solution

"""
# Definition for a Node.
class Node:
    def __init__(self, val = 0, neighbors = None):
        self.val = val
        self.neighbors = neighbors if neighbors is not None else []
"""

from collections import deque

class Solution:
    def cloneGraph(self, node: 'Node') -> 'Node':
        if not node:
            return None
        
        # Dictionary to store the mapping of original nodes to cloned nodes
        clones = {}
        
        # Create a clone of the starting node
        clones[node] = Node(node.val)
        
        # Queue for BFS traversal
        queue = deque([node])
        
        while queue:
            curr = queue.popleft()
            
            # Iterate over the neighbors of the current node
            for neighbor in curr.neighbors:
                if neighbor not in clones:
                    # Create a clone of the neighbor if it doesn't exist
                    clones[neighbor] = Node(neighbor.val)
                    queue.append(neighbor)
                
                # Add the cloned neighbor to the cloned node's neighbors list
                clones[curr].neighbors.append(clones[neighbor])
        
        return clones[node]

Continuous Subarray Sum

Try It!

Minimum Insertions to Balance a Parentheses String

Try It!

Binary Search Tree Iterator

Try It!

Find K Closest Elements

Try It!

Count and Say

Try It!

Course Schedule

Try It!

Letter Combinations of a Phone Number

Try It!

Friends Of Appropriate Ages

Try It!

Task Scheduler II

Try It!

Range Sum Query 2D - Immutable

Try It!

Max Area of Island

Try It!

Kth Smallest Element in a Sorted Matrix

Try It!

Minesweeper

Try It!

Stickers to Spell Word

Try It!

Valid Number

Try It!

Making A Large Island

Try It!

Vertical Order Traversal of a Binary Tree

Try It!

Merge k Sorted Lists

Try It!

Google

Odd Even Jump

Try It!

Maximal Rectangle

Try It!

Find Number of Coins to Place in Tree Nodes

Try It!

Smallest Sufficient Team

Try It!

Sum of Distances in Tree

Try It!

Maximum Strictly Increasing Cells in a Matrix

Try It!

Longest String Chain

Try It!

Arithmetic Slices

Try It!

Arithmetic Slices II - Subsequence

Try It!

Constrained Subsequence Sum

Try It!