Skip to main content

Leetcode75

· 32 min read

Array/String

  1. #1768 Merge Strings Alternately

    Method 1 runs faster than method 2, both are implemented in the same logic.

    class Solution:
    def mergeAlternately(self, word1: str, word2: str) -> str:
    result, len1, len2 = "", len(word1), len(word2)
    for i in range(max(len1, len2)):
    if i < len1:
    result += word1[i]
    if i < len2:
    result += word2[i]
    return result
    class Solution:
    def mergeAlternately(self, word1: str, word2: str) -> str:
    return "".join(a + b for a, b in zip(word1, word2)) + word1[len(word2):] + word2[len(word1):]
  2. #1071 Greatest Common Divisor of Strings

    steps:

    1. If both strings have common gcd string, then string1 = gcdm and string2 = gcdn, thus we have string1+string2 == gcd*(m+n), so do we have string2 + string1 == gcd*(m+n).
    2. Thus we have string1 + string2 == string2 + string1, if they have common gcd.
    3. If they have common gcd string, then gcd of the both lengths is also the length of common gcd string. The first gcd length of letters in str1 or str2 is the result.
    class Solution:  
    def gcdOfStrings(self, str1: str, str2: str) -> str:
    if str1 + str2 == str2 + str1:
    return str2[:(math.gcd(len(str1), len(str2)))]
    return ""
  3. #1431 Kids With the Greatest Number of Candies

    steps:

    1. To faster the run time, we can store at least how many candies needed to make the kid having the most candies among them all

    2. Then compare each kid's candies with candies_needed too see if they can be the kid with most candies

    class Solution:
    def kidsWithCandies(self, candies: List[int], extraCandies: int) -> List[bool]:
    candies_needed = max(candies) - extraCandies
    return [c >= candies_needed for c in candies]
  4. #605 Can Place Flowers

    steps:

    1. According to the rules, if a plot and its adjacent plots are empty, then the plot can be planted
    2. For the first and last plots in the flowerbed, if its next plot or previous plot is empty, then it can be planted
    3. To make the first and last plots use the same rule as other plots, simply adding an empty plot before the first plot and after the last plot
    class Solution:
    def canPlaceFlowers(self, flowerbed: List[int], n: int) -> bool:
    if len(flowerbed) == 0:
    return n == 0

    flowerbed = [0] + flowerbed + [0]
    for i in range(1, len(flowerbed)-1):
    if flowerbed[i] == 0 and flowerbed[i-1] == 0 and flowerbed[i+1] == 0:
    flowerbed[i] = 1
    n -= 1

    return n <= 0
  5. 345 Reverse Vowels of a String

    steps:

    1. The problem can be solved by using two pointers
    2. Pointer A moves from left to right starting from index 0 and stops when it meets a vowel
    3. Pointer B moves from right to left starting from the last index and stops when it meets a vowel
    4. Exchange the vowels that pointer A and B points to
    5. Repeat steps 2-4 until the pointers meet
    class Solution:
    def reverseVowels(self, s: str) -> str:

    s = list(s)
    l, r = 0, len(s)-1
    vowels = "aeiou"

    while l < r:
    if s[l].lower() not in vowels:
    l += 1
    continue
    if s[r].lower() not in vowels:
    r -= 1
    continue
    s[l], s[r] = s[r], s[l]
    l += 1
    r -= 1
    return "".join(s)

  6. #151 Reverse Words in a String

    steps:

    1. If not using python's syntax sugar, two-pointers is a way to solve it efficiently.
    class Solution:
    def reverseWords(self, s: str) -> str:

    # s = s.split()
    # l, r = 0, len(s)-1
    # while l < r:
    # s[l], s[r] = s[r], s[l]
    # l += 1
    # r -= 1

    # return " ".join(s)

    return (" ".join(s.split()[::-1]))
  7. #238 Product of Array Except Self

    steps:

    1. There are some circumstances to consider when solving the prolem.

      1. There is no zero in the array
      2. There is only one zero in the array and the length of the array is greater than the number of zeros
      3. There is only one zero in the array and length of array is also one
      4. There are more than one zeros in the array
    2. Calculate the product of all the non-zero elements in the array

    3. Count the number of zeros in the array

    4. For every element in the array, take the corresponding action

    class Solution:
    def productExceptSelf(self, nums: List[int]) -> List[int]:

    product = 1
    zero = []
    for i, n in enumerate(nums):
    if n != 0:
    product *= n
    else:
    zero.append(i)

    if zero != []:
    if len(zero) < len(nums) and len(zero) == 1:
    return [0 if i not in zero else product for i, n in enumerate(nums)]
    return [0]*len(nums)
    else:
    return [int(product/n) for n in nums]
  8. #334 Increasing Triplet Subsequence

    steps:

    1. Use two variables to store the first two numbers of the triplet.
    2. If a third number meets the requirement, the program returns True
    from typing import List

    class Solution:
    def increasingTriplet(self, nums: List[int]) -> bool:
    if not nums or len(nums) < 3:
    return False

    first = float('inf')
    second = float('inf')

    for num in nums:
    if num <= first:
    first = num
    elif num <= second:
    second = num
    else:
    return True

    return False
  9. #443 String Compression

    steps:

    1. To solve the problem, we can use two-pointers.
    2. One pointer points to the character, the other pointer is used to count the repeating times of the character.
    class Solution:
    def compress(self, chars: List[str]) -> int:
    l, r = 0, 0
    s = len(chars)

    while l < s:
    n = 0
    r = l
    while r < s and chars[l] == chars[r]:
    if chars[l] == chars[r]:
    n += 1
    r += 1

    if n > 1:
    for k in range(l + 1, r):
    chars.pop(l + 1)
    s -= 1
    for k in range(len(str(n))):
    l = l + 1
    chars.insert(l, str(n)[k])
    s += 1

    l += 1
    r = l

    return len(chars)

Two pointers

  1. #283 Move Zeroes

    steps:

    1. One pointer A points to the number while iterating the array
    2. The other pointer B keeps track of the number of zeros that have encountered so so far
    3. When iterating the array, add one to B if A points to a new zero, and swaps numbers if A points to a non-zero element
    class Solution:
    def moveZeroes(self, nums: List[int]) -> None:
    """
    Do not return anything, modify nums in-place instead.
    """
    len_nums = len(nums)
    if len_nums == 1: return nums
    cnt_zero = 0
    for i in range(len_nums):
    if nums[i] == 0: cnt_zero += 1
    elif cnt_zero > 0:
    nums[i-cnt_zero], nums[i] = nums[i], 0
    return nums
  2. #392 Is Subsequence

    steps:

    1. If s is the subsequence of t, then for every character in s, if the character is in t, the left characters in s should also be in t.
    class Solution:
    def isSubsequence(self, s: str, t: str) -> bool:
    start = -1
    for c in s:
    start = t.find(c)

    if start == -1:
    return False

    t = t[start+1:]

    return True
  3. #11 Container With Most Water

    steps:

    1. Using two pointers left and right and they moves in the opposite diretions.
    2. Keeping track of the max_area of the pairs that the program has been checked so far.
    3. Every time we need to move a pointer, moves the pointer that has a smaller value.
    class Solution:
    def maxArea(self, height: List[int]) -> int:

    if len(height) == 1:
    return height

    l, r = 0, len(height) - 1
    max_area = 0
    while l <= r:
    area = (r - l) * min(height[l], height[r])
    max_area = max(area, max_area)
    if height[l] >= height[r]:
    r -= 1
    else:
    l += 1
    return max_area
  4. #1679 Max Number of K-Sum Pairs

    steps:

    1. sort the array from small to large
    2. use two pointers left and right that move in the opposite directions
    3. if the sum of two values equals k, add one to the variable cnt_pairs, the move each pointer one location
    4. if the sum is greater than k, we need to make the sum smaller, so the right pointer moves left one location
    5. if the sum is smaller than k, we need to make the sum bigger, so the left pointer moves right one locaiton
    class Solution:
    def maxOperations(self, nums: List[int], k: int) -> int:

    nums.sort()
    left, right = 0, len(nums) - 1
    cnt_pairs = 0

    while left < right:
    total = nums[left] + nums[right]

    if total == k:
    cnt_pairs += 1
    left += 1
    right -= 1

    elif total < k:
    left += 1

    else:
    right -= 1

    return cnt_pairs

Sliding Window

  1. #643 Maximum Average Subarray I

    steps:

    1. every time move the pointer forward, the sum will subtract the first value and add the latest value
    class Solution:
    def findMaxAverage(self, nums: List[int], k: int) -> float:
    k_sum = sum(nums[:k])
    window = k_sum
    for i in range(1, len(nums)-k+1):
    window = window - nums[i-1] + nums[i+k-1]
    k_sum = max(k_sum, window)
    return k_sum/k
  2. #1456 Maximum Number of Vowels in a Substring of Given Length

    class Solution:
    def maxVowels(self, s: str, k: int) -> int:
    vowels = "aeiou"
    vowels_sum = sum([1 for c in s[:k] if c.lower() in vowels])
    window = vowels_sum
    for i in range(1, len(s)-k+1):
    if s[i-1] in vowels:
    window -= 1
    if s[i+k-1] in vowels:
    window += 1
    vowels_sum = max(vowels_sum, window)
    return vowels_sum
  3. #1004 Max Consecutive Ones III

    steps:

    1. calculate the first consecutive ones (along with turning k zeros into k ones), then move the window forward
    2. if the new element is one, simply adds it to the window
    3. if the new element is zero, move the left pointer forward one location
    class Solution:
    def longestOnes(self, nums: List[int], k: int) -> int:
    l=r=0
    for r in range(len(nums)):
    if nums[r] == 0:
    k-=1
    if k<0:
    if nums[l] == 0:
    k+=1
    l+=1
    return r-l+1
  4. #1493 Longest Subarray of 1's After Deleting One Element

    class Solution:
    def longestSubarray(self, nums: List[int]) -> int:
    max_ones = 0
    left = 0
    zero_count = 0

    for right in range(len(nums)):
    if nums[right] == 0:
    zero_count += 1

    while zero_count > 1: # Shrink the window when more than one zero is present
    if nums[left] == 0:
    zero_count -= 1
    left += 1

    max_ones = max(max_ones, right - left)

    return max_ones

Prefix Sum

  1. #1732 Find the Highest Altitude

    class Solution:
    def largestAltitude(self, gain: List[int]) -> int:
    cur_alt = 0
    max_alt = 0
    for n in gain:
    cur_alt += n
    max_alt = max(cur_alt, max_alt)
    return max_alt
  2. #724 Find Pivot Index

    steps:

    1. store the sum of all nums first
    2. while iterate over the array nums, sum all the values that has been visited, and calculate the sum of the left part by subtracting the current element's value and the sum of the visited elements' values
    3. compare them to find out the pivot index
    class Solution:
    def pivotIndex(self, nums: List[int]) -> int:
    sum_nums = sum([n for n in nums])

    if sum_nums == nums[0]:
    return 0

    sum_first = 0
    for i in range(len(nums)-1):
    sum_first += nums[i]
    if sum_first == (sum_nums-nums[i+1])/2:
    return i+1
    return -1

Hash Map / Set

  1. #2215 Find the Difference of Two Arrays

    class Solution:
    def findDifference(self, nums1: List[int], nums2: List[int]) -> List[List[int]]:
    set_1 = set(nums1)
    set_2 = set(nums2)
    return [set_1-set_2, set_2-set_1]
  2. #1207 Unique Number of Occurrences

    class Solution:
    def uniqueOccurrences(self, arr: List[int]) -> bool:
    freq = {}
    for n in arr:
    if n in freq:
    freq[n] += 1
    else:
    freq[n] = 1

    return len(set(freq.values())) == len(freq.values())
  3. #1657 Determine if Two Strings Are Close

    class Solution:
    def closeStrings(self, word1: str, word2: str) -> bool:
    return set(word1)==set(word2) and sorted(Counter(word1).values())==sorted(Counter(word2).values())
  4. #2352 Equal Row and Column Pairs

    steps:

    1. one thing that should be noticed: if two rows and one col are the same, the return result should be two

    2. check if the sum of a row and a col are the same, then check each element, remember to check every row and every col, so that there won't be any missed

    class Solution:
    def equalPairs(self, grid: List[List[int]]) -> int:
    sumrow=[]
    sumcol=[]
    n=len(grid)
    for i in range(n):
    sumrow.append(sum(grid[i]))
    s=0
    for j in range(n):
    s+=grid[j][i]
    sumcol.append(s)

    res=0
    for i in range(n):
    for j in range(n):
    if sumrow[i]==sumcol[j]:
    test=True
    for k in range(n):
    if grid[i][k]!=grid[k][j]:
    test=False
    break
    if test:
    res+=1
    return res

Stack

  1. #2390 Removing Stars From a String

    steps:

    1. iterate over the string and use a stack to store the elements

    2. if a * is met, pop the last element out from the stack

    3. if not a *, push the element to the end of the stack

    class Solution:
    def removeStars(self, s: str) -> str:
    stack_s = []
    for c in s:
    if c != "*":
    stack_s.append(c)
    else:
    stack_s.pop()
    return "".join(stack_s)
  2. #735 Asteroid Collision

    1. make sure that if the left part moves to the left and the right part moves to the right, then they will never collide
    class Solution:
    def asteroidCollision(self, asteroids: List[int]) -> List[int]:
    stack = []
    for a in asteroids:
    while stack and stack[-1] > 0 > a:
    if stack[-1] < abs(a):
    stack.pop()
    continue
    elif stack[-1] == abs(a):
    stack.pop()
    break # this means asteroid must be destroyed (not add to stack in else statement below)
    else:
    stack.append(a)

    return stack
  3. #394 Decode String

    steps:

    1. store the (string, num) in the stack if it occurs more than once
    class Solution:
    def decodeString(self, s: str) -> str:

    stack = []
    current_str = ""
    current_num = 0

    for char in s:
    if char.isdigit():
    // in case the number is over 9
    current_num = current_num * 10 + int(char)
    elif char == '[':
    stack.append((current_str, current_num))
    current_str, current_num = "", 0
    elif char == ']':
    last_str, num = stack.pop()
    current_str = last_str + current_str * num
    else:
    current_str += char

    return current_str

Queue

  1. #933 Number of Recent Calls

    steps:

    1. use a queue to store the requests that are in range every time add a new request to the queue (pop the earliest ones that are not in range in the queue)
    from collections import deque
    class RecentCounter:

    def __init__(self):
    self.queue = deque()
    self.count = 0

    def ping(self, t: int) -> int:
    #first add the request to the queue and increment the counter
    self.queue.append(t)
    self.count += 1

    #return the number of request in the range
    while self.queue[0] < t -3000:
    self.queue.popleft()
    self.count -= 1

    return self.count
  2. #649 Dota2 Senate

    steps:

    1. store each party's indices in its own queue

    2. compare the elements from the start of the two queues

    3. if the element value is smaller, adds it to the end of the queue

    4. if the element value is larger, then it's being banned, delete the element from the queue

    from collections import deque

    class Solution:
    def predictPartyVictory(self, senate: str) -> str:
    radiant_queue, dire_queue = deque(), deque()

    for i, party in enumerate(senate):
    if party == 'R':
    radiant_queue.append(i)
    else:
    dire_queue.append(i)

    while radiant_queue and dire_queue:
    radiant_index, dire_index = radiant_queue.popleft(), dire_queue.popleft()

    # If Radiant bans Dire, add Dire to the end of the queue; vice versa
    if radiant_index < dire_index:
    radiant_queue.append(radiant_index + len(senate))
    else:
    dire_queue.append(dire_index + len(senate))

    return "Radiant" if radiant_queue else "Dire"

Linked List

  1. #2095 Delete the Middle Node of a Linked List

    steps:

    1. use two pointers to traverse the linked list

    2. one pointer moves forward one location each time while the other moves forward two locations

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

    class Solution:
    def deleteMiddle(self, head: Optional[ListNode]) -> Optional[ListNode]:
    if not head:
    # Empty list or list with one node, no middle to delete
    return head

    slow = head
    fast = head
    prev = None

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

    # At this point, 'slow' is at the middle, and 'prev' is its predecessor

    if prev:
    prev.next = slow.next
    else:
    # If there is no predecessor, it means we are deleting the head
    head = head.next

    return head
  2. #328 Odd Even Linked List

    steps:

    1. while traversing the list, use two pointers to moves forward two locations each time, and change the previous node's next pointer to point to every other node
    # Definition for singly-linked list.
    # class ListNode:
    # def __init__(self, val=0, next=None):
    # self.val = val
    # self.next = next

    class Solution:
    def oddEvenList(self, head: Optional[ListNode]) -> Optional[ListNode]:
    if not head or not head.next:
    return head

    odd, even = head, head.next
    even_head = even # Save the head of the even list

    while even and even.next:
    odd.next = even.next # Connect odd to the next odd node
    odd = odd.next

    even.next = odd.next # Connect even to the next even node
    even = even.next

    odd.next = even_head # Connect the end of the odd list to the head of the even list

    return head
  3. #206 Reverse Linked List

    steps:

    1. Traverse the linked list and use two pointers to remember the previous and current nodes
    # 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: Optional[ListNode]) -> Optional[ListNode]:
    prev, current = None, head
    while current:
    tmp = current.next
    current.next = prev
    prev, current = current, tmp
    return prev

  4. #2130 Maximum Twin Sum of a Linked List

    # Definition for singly-linked list.
    # class ListNode:
    # def __init__(self, val=0, next=None):
    # self.val = val
    # self.next = next
    class Solution:
    def pairSum(self, head: Optional[ListNode]) -> int:
    values = []
    cur = head
    while cur:
    values.append(cur.val)
    cur = cur.next

    length = len(values)
    twin_sum = values[0] + values[-1]
    for i in range(length//2):
    twin_sum = max(twin_sum, values[i]+values[length-1-i])
    return twin_sum

Binary Tree - DFS

  1. #104 Maximum Depth of Binary Tree

    steps:

    1. use dfs to do a bottom-up depth calculation for each node
    # 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 helper(self, root):
    if root == None:
    return 0

    left = self.helper(root.left)
    right = self.helper(root.right)

    return 1 + max(left, right)

    def maxDepth(self, root: Optional[TreeNode]) -> int:
    return self.helper(root)
  2. #872 Leaf-Similar Trees

    steps:

    1. if a node has no left/right node, then it is a leaf
    2. use dfs to store all the leaves (remember to traverse the two trees in the same order) and then compare them
    # 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 helper(self, root):
    leaves = []
    if not root.left and not root.right:
    leaves.append(root.val)
    if root.left:
    leaves.extend(self.helper(root.left))
    if root.right:
    leaves.extend(self.helper(root.right))
    return leaves


    def leafSimilar(self, root1: Optional[TreeNode], root2: Optional[TreeNode]) -> bool:
    if not root1 and not root2:
    return True
    if not root1 or not root2:
    return False
    return self.helper(root1) == self.helper(root2)

  3. #1448 Count Good Nodes in Binary Tree

    # 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 helper(self, root, val):
    if root:
    k = self.helper(root.left, max(val,root.val)) + self.helper(root.right, max(val,root.val))
    if root.val >= val:
    k+=1
    return k
    return 0

    def goodNodes(self, root: TreeNode) -> int:
    return self.helper(root, root.val)
  4. #437 Path Sum III

    class Solution:
    def pathSum(self, root: TreeNode, sum: int) -> int:
    def preorder(node: TreeNode, curr_sum) -> None:
    nonlocal count
    if not node:
    return

    curr_sum += node.val

    if curr_sum == k:
    count += 1

    h[curr_sum] += 1


    preorder(node.left, curr_sum)
    preorder(node.right, curr_sum)

    h[curr_sum] -= 1

    count, k = 0, sum
    h = defaultdict(int)
    preorder(root, 0)
    return count
  5. #1372 Longest ZigZag Path in a Binary Tree

    # 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 longestZigZag(self, root: Optional[TreeNode]) -> int:
    def helper(root, right, total):
    if not root:
    return total

    l = helper(root.left, False, total+1 if right else 1)
    r = helper(root.right, True, total+1 if not right else 1)

    return max(l,r)

    return helper(root, None, 0) - 1
  6. #236 Lowest Common Ancestor of a Binary Tree

    # 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 root is None: return None

    if root == p or root == q:
    return root

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

    if left is not None and right is not None: return root

    if left is not None and left == root.left: return left
    if right is not None and right == root.right : return right

    if left is not None: return left
    if right is not None: return right

Binary Tree - BFS

  1. #199 Binary Tree Right Side View

    # 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]:
    def dfs(node, depth):
    if node:
    if depth == len(ans):
    ans.append(node.val)
    dfs(node.right, depth+1)
    dfs(node.left, depth+1)
    ans = []
    dfs(root, 0)
    return ans
  2. #1161 Maximum Level Sum of a Binary Tree

    steps:

    1. a level-order traversal can help
    # 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 maxLevelSum(self, root: Optional[TreeNode]) -> int:
    if not root:
    return 0 # Return 0 or any default value when the tree is empty

    q = [root]
    level = 0
    max_sum = root.val
    max_level = 1 # Initialize max_level

    while q:
    level += 1
    level_sum = 0
    size = len(q)

    for _ in range(size):
    node = q.pop(0)
    level_sum += node.val

    if node.left:
    q.append(node.left)
    if node.right:
    q.append(node.right)

    if level_sum > max_sum:
    max_sum = level_sum
    max_level = level

    return max_level

Binary Search Tree

  1. #700 Search in a Binary Search Tree

    steps:

    1. recursively check the node in the tree

    2. if the node's val is larger than the target, then check its left child next, vice versa

    # 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 helper(self, root, target):
    if root is None: return None
    if root.val == target: return root
    if root.val < target:
    return self.helper(root.right, target)
    else:
    return self.helper(root.left, target)


    def searchBST(self, root: Optional[TreeNode], val: int) -> Optional[TreeNode]:
    return self.helper(root, val)
  2. #450 Delete Node in a BST

    steps:

    1. find the target node, switch the value with its right most child in the left side or its left most child in the right side
    # 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 deleteNode(self, root: Optional[TreeNode], key: int) -> Optional[TreeNode]:
    if root:
    if key < root.val:
    root.left = self.deleteNode(root.left, key)
    elif key > root.val:
    root.right = self.deleteNode(root.right, key)
    else:
    if (not root.left and not root.right): return None
    if (not root.left or not root.right):
    return root.left if root.left else root.right
    temp = root.left
    while temp.right:
    temp = temp.right
    root.val = temp.val
    root.left = self.deleteNode(root.left, temp.val)
    return root

Graphs - DFS

  1. #841 Keys and Rooms

    steps:

    1. store the rooms that we are going to visit, remember to check duplicates

    2. store the rooms that we are have keys, remember to check duplicates

    3. compare the length of them

    class Solution:
    def canVisitAllRooms(self, rooms: List[List[int]]) -> bool:
    room_to_visit = [0]
    keys = [0]
    while room_to_visit:
    temp = room_to_visit.pop(0)
    for k in rooms[temp]:
    if k not in keys:
    room_to_visit.append(k)
    keys.append(k)
    return len(keys) == len(rooms)
  2. #547 Number of Provinces

    steps:

    1. the problem is to find the number of connected components in an undirected graph

    2. use a list to track visited nodes

    3. for each unvisited node, it starts a new DFS traversal, and each traversal identifies a new connected component in the graph

    class Solution:
    def findCircleNum(self, g: List[List[int]]) -> int:
    v = [False] * len(g)

    def dfs(i):
    for j in range(len(g)):
    // checks if there is an edge between node i and node j
    if g[i][j] and not v[j]:
    v[j] = True
    dfs(j)

    ans = 0
    for i in range(len(g)):
    if not v[i]:
    dfs(i)
    ans += 1
    return ans
  3. #1466 Reorder Routes to Make All Paths Lead to the City Zero

    class Solution:

    def __init__(self):
    self.res = 0

    def minReorder(self, n: int, connections: List[List[int]]) -> int:

    def dfs(child,parent):
    for [c, s] in adj[child]:
    if c != parent:
    self.res += s
    dfs(c,child)

    adj=[]
    for i in range(n):
    adj.append([])
    for i in connections:
    adj[i[0]].append([i[1],1])
    adj[i[1]].append([i[0],0])

    dfs(0,-1)
    return self.res
  4. ##399 Evaluate Division

    from typing import List
    from collections import defaultdict

    class Solution:
    def calcEquation(self, equations: List[List[str]], values: List[float], queries: List[List[str]]) -> List[float]:
    graph = defaultdict(dict)

    for (u, v), val in zip(equations, values):
    graph[u][v] = val
    graph[v][u] = 1 / val
    graph[u][u] = graph[v][v] = 1.0

    for k in graph:
    for i in graph:
    for j in graph:
    if i != j and i in graph[k] and k in graph[j]:
    graph[i][j] = graph[i][k] * graph[k][j]

    result = [graph[u].get(v, -1) for u, v in queries]
    return result

Graphs - BFS

  1. #1926 Nearest Exit from Entrance in Maze

    class Solution:
    def nearestExit(self, maze: List[List[str]], entrance: List[int]) -> int:
    m = len(maze)
    n = len(maze[0])

    queue = collections.deque()
    queue.append((entrance[0], entrance[1], 0))
    visited = set()
    visited.add((entrance[0], entrance[1]))

    while queue:
    for _ in range(len(queue)):
    r, c, level = queue.popleft()

    if [r, c] != entrance:
    if r == 0 or r == m-1 or c == 0 or c == n-1:
    return level

    for nr, nc in [(r, c+1), (r, c-1), (r+1, c), (r-1, c)]:
    if 0 <= nr < m and 0 <= nc < n and (nr, nc) not in visited and maze[nr][nc] == '.':
    queue.append((nr, nc, level + 1))
    visited.add((nr, nc))

    return -1
  2. #994 Rotting Oranges

    1. remember to update the grid after finishing one round, do not update instantly while traversing each node
    from typing import List
    from collections import deque

    class Solution:
    def orangesRotting(self, grid: List[List[int]]) -> int:
    m, n = len(grid), len(grid[0])
    fresh_oranges = 0
    rotten_oranges = deque()

    # Count fresh oranges and add rotten oranges to the queue
    for i in range(m):
    for j in range(n):
    if grid[i][j] == 1:
    fresh_oranges += 1
    elif grid[i][j] == 2:
    rotten_oranges.append((i, j, 0))

    minutes = 0

    # Define 4-directional movements
    directions = [(-1, 0), (1, 0), (0, -1), (0, 1)]

    while rotten_oranges:
    i, j, minutes = rotten_oranges.popleft()

    for di, dj in directions:
    ni, nj = i + di, j + dj

    if 0 <= ni < m and 0 <= nj < n and grid[ni][nj] == 1:
    grid[ni][nj] = 2
    fresh_oranges -= 1
    rotten_oranges.append((ni, nj, minutes + 1))

    return minutes if fresh_oranges == 0 else -1

Heap / Priority Queue

  1. #215 Kth Largest Element in an Array

    from heapq import heappush, heappop
    class Solution:
    def findKthLargest(self, nums: List[int], k: int) -> int:

    def quickselect(nums, k):
    left, mid, right = [], [], []
    pivot = random.choice(nums)
    for num in nums:
    if num > pivot:
    left.append(num)
    elif num < pivot:
    right.append(num)
    else:
    mid.append(num)
    if len(left) >= k:
    return quickselect(left, k)

    if len(left) + len(mid) < k:
    return quickselect(right, k - len(left)-len(mid))
    return mid[0]

    return quickselect(nums, k)
  2. #2336 Smallest Number in Infinite Set

    class SmallestInfiniteSet:

    def __init__(self):
    self.curr = 1
    self.set = set()

    def popSmallest(self) -> int:
    if self.set:
    ans = min(self.set)
    self.set.remove(ans)
    return ans
    else:
    self.curr += 1
    return self.curr -1

    def addBack(self, num: int) -> None:
    if self.curr > num:
    self.set.add(num)
  3. #2542 Maximum Subsequence Score

    class Solution:
    def maxScore(self, nums1: List[int], nums2: List[int], k: int) -> int:
    pairs = sorted([(n2, n1) for n1, n2 in zip(nums1, nums2)], reverse=True)

    min_heap = []
    res = 0
    sum_n1 = 0

    for n2, n1 in pairs:
    sum_n1 += n1
    heapq.heappush(min_heap, n1)
    if len(min_heap) > k:
    sum_n1 -= heapq.heappop(min_heap)
    if len(min_heap) == k:
    res = max(res, sum_n1 * n2)
    return res
  4. #2462 Total Cost to Hire K Workers

    class Solution:
    def totalCost(self, costs, k, candidates):
    i = 0
    j = len(costs) - 1
    pq1 = []
    pq2 = []

    ans = 0
    while k > 0:
    while len(pq1) < candidates and i <= j:
    heapq.heappush(pq1, costs[i])
    i += 1
    while len(pq2) < candidates and i <= j:
    heapq.heappush(pq2, costs[j])
    j -= 1

    t1 = pq1[0] if pq1 else float('inf')
    t2 = pq2[0] if pq2 else float('inf')

    if t1 <= t2:
    ans += t1
    heapq.heappop(pq1)
    else:
    ans += t2
    heapq.heappop(pq2)

    k -= 1
    return ans
  1. #374 Guess Number Higher or Lower

    # The guess API is already defined for you.
    # @param num, your guess
    # @return -1 if num is higher than the picked number
    # 1 if num is lower than the picked number
    # otherwise return 0
    # def guess(num: int) -> int:

    class Solution:
    def guessNumber(self, n: int) -> int:
    l=1
    r=n
    while l <= r:
    m = (l+r)//2
    if guess(m) == 0:
    return m
    if guess(m) == -1:
    r = m-1
    if guess(m) == 1:
    l = m+1
  2. #2300 Successful Pairs of Spells and Potions

    class Solution:
    def successfulPairs(self, spells: List[int], potions: List[int], success: int) -> List[int]:
    potions.sort()
    pointer = 0
    trace = []
    hashmap ={}
    stack = []
    length = len(potions)
    lookup = []


    for idx, num in enumerate(potions):
    if num in hashmap:
    continue
    hashmap[num] = length - idx
    trace.append(num)

    for i in range(potions[-1] + 1):
    Pnum = trace[pointer]
    if i not in hashmap:

    lookup.append(hashmap[Pnum])
    continue

    lookup.append(hashmap[Pnum])
    pointer +=1

    for spell in spells:
    bar = math.ceil(success / spell)
    if bar > len(lookup) - 1:
    stack.append(0)
    elif bar < 0:
    stack.append(lookup[0])
    else:
    stack.append(lookup[bar])

    return stack
  3. #162 Find Peak Element

    class Solution:
    def findPeakElement(self, nums: List[int]) -> int:
    low=0
    high=len(nums)-1

    while low<=high:
    mid=low+(high-low)//2
    if mid>0 and nums[mid]<nums[mid-1]:
    high=mid-1
    elif mid<len(nums)-1 and nums[mid]<nums[mid+1]:
    low=mid+1
    else:
    return mid
  4. #875 Koko Eating Bananas

    class Solution:
    def minEatingSpeed(self, piles: List[int], h: int) -> int:
    def check_can_eat(k):
    actual_h = 0
    for pile in piles:
    actual_h += (pile+k-1)//k
    return actual_h <= h
    l,r = 0,max(piles)

    while r-l>1:
    m=(l+r)//2
    if check_can_eat(m):
    r = m
    else:
    l = m
    return r

Backtracking

  1. #17 Letter Combinations of a Phone Number

    from typing import List

    class Solution:
    def letterCombinations(self, digits: str) -> List[str]:

    if digits == "":
    return []

    DIGITS_TO_LETTERS = {
    "2": "abc",
    "3": "def",
    "4": "ghi",
    "5": "jkl",
    "6": "mno",
    "7": "pqrs",
    "8": "tuv",
    "9": "wxyz"
    }

    combinations = []

    for d in digits:
    temp = []
    if combinations == []:
    for c in DIGITS_TO_LETTERS[d]:
    temp.append(c)
    else:
    for c in DIGITS_TO_LETTERS[d]:
    for com in combinations:
    new_com = com + c
    temp.append(new_com)
    combinations = temp

    return combinations
  2. #216 Combination Sum III

    class Solution:
    def backtrack(self, ind, n, k, ds, res):
    if len(ds) == k and sum(ds) == n:
    res.append(ds[:])
    return None

    for i in range(ind, 10):
    ds.append(i)
    self.backtrack(i+1, n, k, ds, res)
    ds.pop()

    def combinationSum3(self, k: int, n: int) -> List[List[int]]:
    res = []
    self.backtrack(1, n, k, [], res)
    return res

DP - 1D

  1. #1137 N-th Tribonacci Number

    class Solution:

    def tribonacci(self, n: int) -> int:
    previous_three = [0, 1, 1]
    if n < 3:
    return previous_three[n]
    for i in range(3, n+1):
    a, b, c = previous_three
    a, b, c = b, c, a+b+c
    previous_three = [a, b, c]

    return previous_three[-1]
  2. #746 Min Cost Climbing Stairs

    class Solution:
    def minCostClimbingStairs(self, cost: List[int]) -> int:
    cost.append(0)
    for i in range(len(cost)-3,-1,-1):
    cost[i]+=min(cost[i+1],cost[i+2])
    return min(cost[0],cost[1])
  3. #198 House Robber

    class Solution:
    def rob(self, nums: List[int]) -> int:
    n = len(nums)
    if n >= 2:
    nums[1] = max(nums[0], nums[1])
    for i in range(2, n):
    nums[i] = max(nums[i-1], nums[i-2]+nums[i])
    return nums[n-1] if n>0 else 0
  4. #790 Domino and Tromino Tiling

    class Solution:
    def numTilings(self, n: int) -> int:
    if n <= 2: return n
    if n==3: return 5

    MD = 1000000007
    dp = [1, 2, 5] + [0]*(n-3)

    for i in range(3, n):
    dp[i] = dp[i-1] * 2 % MD + dp[i-3] % MD
    dp[i] = dp[i] % MD

    return dp[n-1]

DP - multidimensional

  1. #62 Unique Paths

    class Solution:
    def uniquePaths(self, m: int, n: int) -> int:
    row = [1]*n

    for i in range(m-1):
    new_row = [1]*n

    for j in range(n-2, -1, -1):
    new_row[j] = new_row[j+1] + row[j]

    row = new_row

    return row[0]
  2. #1143 Longest Common Subsequence

    class Solution:
    def longestCommonSubsequence(self, text1: str, text2: str) -> int:
    # dp[i][j] = length of longest common subsequence
    # between text1[:i] and text2[:j]
    dp = [
    [0 for _ in range(len(text2) + 1)] for _ in range(len(text1) + 1)
    ]
    for i in range(len(text1)):
    for j in range(len(text2)):
    dp[i + 1][j + 1] = (
    (dp[i][j] + 1)
    if text1[i] == text2[j]
    else max(dp[i + 1][j], dp[i][j + 1])
    )
    return dp[-1][-1]
  3. #714 Best Time to Buy and Sell Stock with Transaction Fee

    class Solution:
    def maxProfit(self, prices: List[int], fee: int) -> int:
    pos=-prices[0]
    profit=0

    for i in range(1,len(prices)):
    pos=max(pos,profit-prices[i])
    profit=max(profit,pos+prices[i]-fee)

    return profit
  4. #72 Edit Distance

class Solution:
def minDistance(self, word1: str, word2: str) -> int:

w1, w2 = len(word1), len(word2)

@lru_cache(None)
def dp(i, j):
# word1 used up, so all inserts
if i >= w1 : return w2-j

# word2 used up, so all deletes
if j >= w2 : return w1-i

# letters match, so no operation
if word1[i] == word2[j]: return dp(i+1, j+1)

# insert, delete, replace
return min(dp(i,j+1), dp(i+1,j), dp(i+1,j+1)) + 1

return dp(0,0)

Bit Manipulation

  1. #338 Counting Bits

    class Solution:
    def countBits(self, n: int) -> List[int]:
    ans = [0] * (n + 1)
    for i in range(1, n + 1):
    ans[i] = ans[i >> 1] + (i & 1)
    return ans
  2. #136 Single Number

    class Solution:
    def singleNumber(self, nums: List[int]) -> int:
    hashmap = {}
    for n in nums:
    if n in hashmap:
    del hashmap[n]
    else:
    hashmap[n] = 1

    return list(hashmap.keys())[0]
  3. #1318 Minimum Flips to Make a OR b Equal to c

    class Solution:
    def minFlips(self, a: int, b: int, c: int) -> int:
    a = [int(n) for n in list(str(bin(a)))[2:]]
    b = [int(n) for n in list(str(bin(b)))[2:]]
    c = [int(n) for n in list(str(bin(c)))[2:]]

    length = max(len(a), len(b), len(c))

    a = [0] * (length - len(a)) + a
    b = [0] * (length - len(b)) + b
    c = [0] * (length - len(c)) + c

    shift = 0

    for i in range(len(c)-1, -1, -1):
    if c[i] == 0:
    if a[i] != 0:
    shift += 1
    if b[i] != 0:
    shift += 1

    if c[i] == 1:
    if a[i] == 0 and b[i] == 0:
    shift += 1

    return shift

Trie

  1. #208 Implement Trie (Prefix Tree)

    class TrieNode:
    def __init__(self):
    self.children = {}
    self.end = False


    class Trie:
    def __init__(self):
    self.root = TrieNode()

    def insert(self, word: str) -> None:
    cur = self.root
    for c in word:
    if c not in cur.children:
    cur.children[c] = TrieNode()
    cur = cur.children[c]
    cur.end = True



    def search(self, word: str) -> bool:
    cur = self.root
    for c in word:
    if c not in cur.children:
    return False
    cur = cur.children[c]
    return cur.end
  2. #1268 Search Suggestions System

    from sortedcontainers import SortedList
    class Solution:
    def suggestedProducts(self, products: List[str], searchWord: str) -> List[List[str]]:
    products.sort()
    ans = []
    for i in range(len(searchWord)):
    while products and (i > len(products[0]) - 1 or searchWord[i] != products[0][i]):
    products.pop(0)
    while products and (i > len(products[-1]) - 1 or searchWord[i] != products[-1][i]):
    products.pop()
    ans.append(products[:3])

    return ans

Intervals

  1. #435 Non-overlapping Intervals

    class Solution:
    def eraseOverlapIntervals(self, points: List[List[int]]) -> int:
    ans = 0
    arrow = -math.inf

    for point in sorted(points, key = lambda x:x[1]):
    if(point[0] >= arrow):
    arrow = point[1]
    else:
    ans+=1

    return ans
  2. #452 Minimum Number of Arrows to Burst Balloons

    class Solution:

    def findMinArrowShots(self, points: List[List[int]]) -> int:
    points = sorted(points, key=lambda x: x[1])
    max_high = -float('inf')
    cnt = 0
    for p in points:
    if p[0] > max_high:
    cnt += 1
    max_high = p[1]
    return cnt

Monotonic Stack

  1. #739 Daily Temperatures

    steps:

    1. iterate over the array days

    2. create a stack, while iterating over the array, if no higher temp is met, push (temp, index) to the stack, if any day in the stack is lower than the temp, pop it out and calculate how manys days it has been waiting

    class Solution:

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

    def dailyTemperatures(self, temperatures: List[int]) -> List[int]:
    ans = [0]*len(temperatures)

    for i, n in enumerate(temperatures):
    while stack and self.stack[-1][0] < n:
    _, old_i = self.stack.pop()
    ans[old_i] = i - old_i
    self.stack.append((n, i))
    return ans
  2. #901 Online Stock Span

    class StockSpanner:

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

    def next(self, price: int) -> int:
    counter = 1
    while self.stack and self.stack[-1][0] <= price:
    counter += self.stack.pop()[1]
    self.stack.append((price, counter))
    return counter