Meta Popular Questions Flashcards

1
Q

88 - Merge Sorted Array
You are given two integer arrays nums1 and nums2, sorted in non-decreasing order, and two integers m and n, representing the number of elements in nums1 and nums2 respectively.

Merge nums1 and nums2 into a single array sorted in non-decreasing order.

The final sorted array should not be returned by the function, but instead be stored inside the array nums1. To accommodate this, nums1 has a length of m + n, where the first m elements denote the elements that should be merged, and the last n elements are set to 0 and should be ignored. nums2 has a length of n

A

Have 2 pointers at the end of arrays. Have 3rd pointer pointing at the writing position. break condition is the first part of the while loop

        for write_index in range(n + m - 1, -1, -1):
            if p2 < 0:
                break
            if p1 >= 0 and nums1[p1] > nums2[p2]:
                nums1[write_index] = nums1[p1]
                p1 -= 1
            else:
                nums1[write_index] = nums2[p2]
                p2 -= 1
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
2
Q

680 - Valid Palindrome 2
Given a string s, return true if the s can be palindrome after deleting at most one character from it.

Example 1:
Input: s = “aba”
Output: true
Example 2:

Input: s = “abca”
Output: true
Explanation: You could delete the character ‘c’.
Example 3:

A

Have an inner function that checks if palindrome is valid for indexes, I and j. Then use that to check if we remove a char from the left pointer or the right pointer.
Be careful to decrement the j pointer. Beware of copy/paste errors.

class Solution:
    def validPalindrome(self, s: str) -> bool:
        def check_palindrome(s, i, j):
            while i < j:
                if s[i] != s[j]:
                    return False
                i += 1
                j -= 1
            
            return True

        i = 0
        j = len(s) - 1
        while i < j:
            # Found a mismatched pair - try both deletions
            if s[i] != s[j]:
                return check_palindrome(s, i, j - 1) or check_palindrome(s, i + 1, j)
            i += 1
            j -= 1
        
        return True
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
3
Q

125 - Valid Palindrome 1
Given a string s, return true if it is a palindrome, or false otherwise.
A palindrome is valid if, after converting all uppercase letters into lowercase letters and removing all non-alphanumeric characters, it reads the same forward and backward. Alphanumeric characters include letters and numbers.

Example 1:
Input: s = “A man, a plan, a canal: Panama”
Output: true
Explanation: “amanaplanacanalpanama” is a palindrome.

A

Remember to use isalnum( ) to check for alpha-numeric
~~~
class Solution:
def isPalindrome(self, s: str) -> bool:
i, j = 0, len(s) - 1

    while i < j:
        # ignore chars like #$%&@-=|
        while i < j and not s[i].isalnum():
            i += 1
        # ignore chars like #$%&@-=|
        while i < j and not s[j].isalnum():
            j -= 1

        # compare the lowercase values
        if s[i].lower() != s[j].lower():
            return False

        i += 1
        j -= 1

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

236 - Find Lowest Common Ancestor of 2 nodes in binary tree

A

See if 2 of 3 conditions are True

Time Complexity: O(N), where N is the number of nodes in the binary tree. In the worst case we might be visiting all the nodes of the binary tree.

Space Complexity: O(N). This is because the maximum amount of space utilized by the recursion stack would be N since the height of a skewed binary tree could be N.
class Solution:

def \_\_init\_\_(self):
    # Variable to store LCA node.
    self.ans = None

def lowestCommonAncestor(self, root: TreeNode, p: TreeNode, q: TreeNode) -> TreeNode:
    def recurse_tree(current_node: TreeNode) -> bool:

        # If reached the end of a branch, return False.
        if not current_node:
            return False

        # Left Recursion
        left = recurse_tree(current_node.left)

        # Right Recursion
        right = recurse_tree(current_node.right)

        # If the current node is one of p or q
        mid = (current_node == p or current_node == q)

        # If any two of the three flags left, right or mid become True.
        if mid + left + right >= 2:
            self.ans = current_node

        # Return True if either of the three bool values is True.
        return mid or left or right

    # Traverse the tree
    recurse_tree(root)
    return self.ans
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
5
Q

1249 - Remove min parens to make balanced parens

A

Have a Set for closing paren without opening parent,
and Stack for opening parent without closing paren. iterate through each char in string. merge stack and set . enumerate through string again, check if in newset, build char array. using ““.join
~~~
class Solution:
def minRemoveToMakeValid(self, s: str) -> str:
indexes_to_remove = set()
stack = []
for i, c in enumerate(s):
# ignore non-paren characters
if c not in “()”:
continue
# for open paren, add index to stack
if c == “(“:
stack.append(i)
elif not stack:
# we saw closing paren without seeing open paren,
# so we know that we should remove this index
indexes_to_remove.add(i)
else:
# saw closing paren, pop the open paren index from stack
stack.pop()

# make a new set that is union of removal indexes and leftover open parens
    indexes_to_remove = indexes_to_remove.union(set(stack))

# string_builder will hold the characters we want to keep
    string_builder = []
    for i, c in enumerate(s):
        if i not in indexes_to_remove:
            string_builder.append(c)
    return "".join(string_builder) ~~~
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
6
Q

TwoSum (not sorted)
Given an array of integers nums and an integer target, return indices of the two numbers such that they add up to target.

Assume that each input would have exactly one solution, and you may not use the same element twice.

Example 1:
Input: nums = [2,7,11,15], target = 9
Output: [0,1]
Explanation: Because nums[0] + nums[1] == 9, we return [0, 1].

A

Use a hash to remember the index

class Solution:
def twoSum(self, nums: List[int], target: int) -> List[int]:
h = {}
for i, n in enumerate(nums):
delta = target - n
if delta in h:
return [i, h[delta]]
h[n] = i
return []

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

Return the Kth largest element of an array

A

Call heapify on the array. Then call heappop( )
Remember to multiply by -1 and return (res * -1)

from heapq import heapify, heappop

class Solution:
def findKthLargest(self, nums, k) -> int:
nums = [-n for n in nums]
heapify(nums)
res = 0
for _ in range(k):
res = heappop(nums)
return -res

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

Three Sum
Given an integer array nums, return all the triplets [nums[i], nums[j], nums[k]] such that i != j, i != k, and j != k, and nums[i] + nums[j] + nums[k] == 0.
Notice that the solution set must not contain duplicate triplets.

Example 1:
Input: nums = [-1,0,1,2,-1,-4]
Output: [[-1,-1,2],[-1,0,1]]
Explanation:
nums[0] + nums[1] + nums[2] = (-1) + 0 + 1 = 0.
nums[1] + nums[2] + nums[4] = 0 + 1 + (-1) = 0.
nums[0] + nums[3] + nums[4] = (-1) + 2 + (-1) = 0.
The distinct triplets are [-1,0,1] and [-1,-1,2].
Notice that the order of the output and the order of the triplets does not matter.

A

You should probably provide this solution first, since it builds on TwoSum
# Time Complexity: O(n^2). TwoSumII is O(n), and we call it n times.
# Sorting the array takes O(nlogn), so overall complexity is O(nlogn+n^2).
# This is asymptotically equivalent to O(n^2).

Space Complexity: from O(logn) to O(n),
# depending on the implementation of the sorting algorithm.
class Solution:
def threeSum(self, nums: List[int]) -> List[List[int]]:
res = []
nums.sort()
for i in range(len(nums)):
if nums[i] > 0:
break
if i == 0 or nums[i - 1] != nums[i]:
self.twoSumII(nums, i, res)
return res

def twoSumII(self, nums: List[int], i: int, res: List[List[int]]):
    lo, hi = i + 1, len(nums) - 1
    while lo < hi:
        sum = nums[i] + nums[lo] + nums[hi]
        if sum < 0:
            lo += 1
        elif sum > 0:
            hi -= 1
        else:
            res.append([nums[i], nums[lo], nums[hi]])
            lo += 1
            hi -= 1
            while lo < hi and nums[lo] == nums[lo - 1]:
                lo += 1
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
9
Q

ThreeSum without sorting

A

class Solution:
def threeSum(self, nums: List[int]) -> List[List[int]]:
res, dups = set(), set()
seen = {}
for i, val1 in enumerate(nums):
if val1 not in dups:
dups.add(val1)
for j, val2 in enumerate(nums[i + 1 :]):
complement = -val1 - val2
if complement in seen and seen[complement] == i:
res.add(tuple(sorted((val1, val2, complement))))
seen[val2] = i
return res

Time Complexity: O(n^2). We have outer and inner loops, each going through n elements.

While the asymptotic complexity is the same, this algorithm is noticeably slower than the previous approach. Lookups in a hashset, though requiring a constant time, are expensive compared to the direct memory access.

Space Complexity: O(n) for the hashset/hashmap.

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

200 - Num Islands
Given an m x n 2D binary grid grid which represents a map of ‘1’s (land) and ‘0’s (water), return the number of islands.
An island is surrounded by water and is formed by connecting adjacent lands horizontally or vertically. You may assume all four edges of the grid are all surrounded by water.

Example 1:

Input: grid = [
[“1”,”1”,”1”,”1”,”0”],
[“1”,”1”,”0”,”1”,”0”],
[“1”,”1”,”0”,”0”,”0”],
[“0”,”0”,”0”,”0”,”0”]
]
Output: 1

A

Time complexity : O(M×N) where M is the number of rows and
N is the number of columns.

Space complexity : worst case O(M×N) in case that the grid map
is filled with lands where DFS goes by M×N deep.

class Solution:
def numIslands_2024_08_02(self, grid) -> int:
def adjacents(grid, row, col):
adjacents = []
last_row = len(grid) - 1
last_col = len(grid[0]) -1

        coords = [[0,1], [0,-1], [1,0], [-1,0]]
        for coord in coords:
            new_row = row + coord[0]
            new_col = col + coord[1]

            if new_row >= 0 and new_row <= last_row and new_col >= 0 and new_col <= last_col:
                adjacents.append([new_row, new_col])

        return adjacents

    def explore(grid, row, col):
        # mark as visited
        grid[row][col] = '2'

        for pair in adjacents(grid, row, col):
            r = pair[0]
            c = pair[1]
            # check if visited or ocean
            if grid[r][c] != '1':
                continue
            explore(grid, r, c)

    num_islands = 0
    num_rows = len(grid)
    num_cols = len(grid[0])
    for i in range(num_rows):
        for j in range(num_cols):
            if grid[i][j] == '1':
                explore(grid, i, j)
                num_islands += 1

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

408 - Valid Word Abbreviation
Input: word = “internationalization”, abbr = “i12iz4n”
Output: true
Input: word = “apple”, abbr = “a2e”
Output: false

A

Two pointers,
if p2 is digit do some atoi logic and move the p2 pointer. final check that the two pointers = len of original stuff

class Solution:
def validWordAbbreviation(self, word: str, abbr: str) -> bool:
p1 = p2 = 0
word_len = len(word)
abbr_len = len(abbr)

    while p1 < word_len and p2 < abbr_len:
        if abbr[p2].isdigit():
            if abbr[p2] == '0': # leading zeros are invalid
                return False
            # atoi logic
            shift = 0
            while p2 < abbr_len and abbr[p2].isdigit():
                shift = (shift*10)+int(abbr[p2])
                p2 += 1
            p1 += shift
        else:
            if word[p1] != abbr[p2]:
                return False
            p1 += 1
            p2 += 1

    return p1 == word_len and p2 == abbr_len
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
12
Q

”””
525. Contiguous Array
Given a binary array nums, return the maximum length of a contiguous subarray
with an equal number of 0 and 1.

Example 1:
Input: nums = [0,1]
Output: 2
Explanation: [0, 1] is the longest contiguous subarray with an equal number of 0 and 1.

Example 2:
Input: nums = [0,1,0]
Output: 2
Explanation: [0, 1] (or [1, 0]) is a longest contiguous subarray with equal number of 0 and 1.
“””

A

make use of a HashMap map to store the entries in the form of (index,count).
We make an entry for a count in the map whenever the count is encountered first, and later on use the corresponding index to find the length of the largest subarray with equal no. of zeros and ones when the same count is encountered again.
# Runtime: O(N)
# Space: O(N)
class Solution:
def findMaxLength(self, nums):
count_map = {0: -1}
maxlen = 0
count = 0

    for i, num in enumerate(nums):
        count += 1 if num == 1 else -1
        if count in count_map:
            maxlen = max(maxlen, i - count_map[count])
        else:
            count_map[count] = i

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

”””
Convert a Binary Search Tree to a sorted Circular Doubly-Linked List in place.
You can think of the left and right pointers as synonymous to the predecessor and successor pointers in a doubly-linked list. For a circular doubly linked list, the predecessor of the first element is the last element, and the successor of the last element is the first element.
We want to do the transformation in place. After the transformation, the left pointer of the tree node should point to its predecessor, and the right pointer should point to its successor. You should return the pointer to the smallest element of the linked list.
“””

A

class Solution:
def treeToDoublyList(self, root: ‘Node’) -> ‘Node’:
def helper(node):
“””
Performs standard inorder traversal:
left -> node -> right
and links all nodes into DLL
“””
nonlocal last, first
if node:
# left
helper(node.left)

            # node 
            if last:
                # link the previous node (last)
                # with the current one (node)
                last.right = node
                node.left = last
            else:
                # keep the smallest node
                # to close DLL later on
                first = node        
            last = node

            # right
            helper(node.right)
    
    if not root:
        return None
    
    # the smallest (first) and the largest (last) nodes
    first, last = None, None
    helper(root)

    # close DLL
    last.right = first
    first.left = last
    return first
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
14
Q

”””
71 - Given an absolute path for a Unix-style file system, which begins with a slash ‘/’, transform this path into its simplified canonical path.
In Unix-style file system context, a single period ‘.’ signifies the current directory, a double period “..” denotes moving up one directory level, and multiple slashes such as “//” are interpreted as a single slash. In this problem, treat sequences of periods not covered by the previous rules (like “…”) as valid names for files or directories.
The simplified canonical path should adhere to the following rules:

It must start with a single slash ‘/’.
Directories within the path should be separated by only one slash ‘/’.
It should not end with a slash ‘/’, unless it’s the root directory.
It should exclude any single or double periods used to denote current or parent directories.
Return the new path.

Example 1:
Input: path = “/home/”
Output: “/home”
Explanation:
The trailing slash should be removed.

Example 2:
Input: path = “/home//foo/”
Output: “/home/foo”
Explanation:
Multiple consecutive slashes are replaced by a single one.

A

Use a Stack. pop if see “..”, otherwise add to stack

class Solution:
def simplifyPath(self, path: str) -> str:

    # Initialize a stack
    stack = []

    # Split the input string on "/" as the delimiter
    # and process each portion one by one
    for portion in path.split("/"):

        # If the current component is a "..", then
        # we pop an entry from the stack if it's non-empty
        if portion == "..":
            if stack:
                stack.pop()
        elif portion == "." or not portion:
            # A no-op for a "." or an empty string
            continue
        else:
            # Finally, a legitimate directory name, so we add it
            # to our stack
            stack.append(portion)

    # Stich together all the directory names together
    final_str = "/" + "/".join(stack)
    return final_str
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
15
Q

”””
76. Minimum Window Substring
Given two strings s and t of lengths m and n respectively,
return the minimum window substring of s such that
every character in t (including duplicates) is included in the window.
If there is no such substring, return the empty string “”.

The testcases will be generated such that the answer is unique.
Example 1:
Input: s = “ADOBECODEBANC”, t = “ABC”
Output: “BANC”
Explanation: The minimum window substring “BANC” includes ‘A’, ‘B’, and ‘C’ from string t.

Example 2:
Input: s = “a”, t = “a”
Output: “a”
Explanation: The entire string s is the minimum window.

Example 3:
Input: s = “a”, t = “aa”
Output: “”
Explanation: Both ‘a’s from t must be included in the window.
Since the largest window of s only has one ‘a’, return empty string.
“””

A

Initialize a character array map of size 128 to store the frequency of characters in string t.
Initialize variables count, start, end, minLen, and startIndex.
Iterate through each character in string t and update the character frequency in the map.
Use two pointers (start and end) to slide the window and find the minimum window that contains all characters from string t.
Increment end and decrease the frequency in map for each character encountered until all characters from t are present in the window.
When all characters from t are present, update minLen and startIndex based on the current window size and starting index.
Increment start and increase the frequency in map until the window no longer contains all characters from t.
After the iteration, the minimum window is found, and the result is a substring of s starting from startIndex with length minLen.

Time complexity: O(n), where n is the length of string s.
# Space complexity: O(1), as the map array has a constant size (128).

class Solution:
def minWindow(self, s: str, t: str) -> str:
if not s or not t or len(s) < len(t):
return “”

    map = [0] * 128
    count = len(t)
    start = 0
    end = 0
    min_len = float('inf')
    start_index = 0
    # UPVOTE !
    for char in t:
        map[ord(char)] += 1

    while end < len(s):
        if map[ord(s[end])] > 0:
            count -= 1
        map[ord(s[end])] -= 1
        end += 1

        while count == 0:
            if end - start < min_len:
                start_index = start
                min_len = end - start

            if map[ord(s[start])] == 0:
                count += 1
            map[ord(s[start])] += 1
            start += 1

    return "" if min_len == float('inf') else s[start_index:start_index + min_len]
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
16
Q

146 - LRU Cache
get( ) and put( ) functions both running in O(1) time.

A

”””
Get the current node at the end of the linked list. This is the “real” tail: tail.prev. Let’s call it previousEnd.
node is being inserted after previousEnd. Therefore, set previousEnd.next = node.
Now, we set the pointers of node. First, node.prev = previousEnd.
Next, because the “real” tail is before tail, we set node.next = tail.
Finally, we update tail.prev = node.
“””
class ListNode:
def __init__(self, key, val):
self.key = key
self.val = val
self.next = None
self.prev = None

class LRUCache:
def __init__(self, capacity: int):
self.capacity = capacity
self.dic = {}
# Two sentinel values
self.head = ListNode(-1, -1)
self.tail = ListNode(-1, -1)
self.head.next = self.tail
self.tail.prev = self.head

def get(self, key: int) -> int:
    if key not in self.dic:
        return -1

    node = self.dic[key]
    # move the end of the list
    self.remove(node)
    self.add(node)
    return node.val

def put(self, key: int, value: int) -> None:
    if key in self.dic:
        old_node = self.dic[key]
        self.remove(old_node)

    node = ListNode(key, value)
    self.dic[key] = node
    self.add(node)

    if len(self.dic) > self.capacity:
        node_to_delete = self.head.next
        self.remove(node_to_delete)
        del self.dic[node_to_delete.key]

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

def remove(self, node):
    node.prev.next = node.next
    node.next.prev = node.prev
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
17
Q

314 - 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).

If two nodes are in the same row and column, the order should be from left to right.

Output:
[[9],[3,15],[20],[7]]

A

BFS, save vertical level as state,
keep a hash table of the vertical levels. keep inserting
maintain the min and max level amounts.
finally iterate from min to max vertical level and access the hash

class Solution:
def verticalOrder(self, root):
if not root:
return []

    vertTable = defaultdict(list)
    queue = deque()
    min_column = 0
    max_column = 0

    queue.append([root, 0])

    while queue:
        node, column = queue.popleft()
        vertTable[column].append(node.val)

        min_column = min(column, min_column)
        max_column = max(column, max_column)

        if node.left:
            queue.append([node.left, column - 1])
        if node.right:
            queue.append([node.right, column + 1])

    res = []
    for i in range(min_column, max_column + 1):
        res.append(vertTable[i])
    return res
18
Q

523 Continuous Subarray Sum
Given an integer array nums and an integer k, return true if nums has a good subarray or false otherwise.
A good subarray is a subarray where:
its length is at least two, and
the sum of the elements of the subarray is a multiple of k.
Note that:

A subarray is a contiguous part of the array.
An integer x is a multiple of k if there exists an integer n such that x = n * k. 0 is always a multiple of k.

Example 1:
Input: nums = [23,2,4,6,7], k = 6
Output: true
Explanation: [2, 4] is a continuous subarray of size 2 whose elements sum up to 6.

A

Initialize an integer prefixMod = 0 and a hashmap modSeen. Initialize modSeen[0] with -1 to account for the initial value of prefixMod.
Iterate over all the elements of nums:
Compute the prefixMod as prefixMod = (prefixMod + nums[i]) % k.
If prefixMod exists in the hashmap:
If the size of the longest subarray with modulo k is at least 2.
Return true.
If prefixMod doesn’t exist in the hashmap:
Set modSeen[prefixMod] = i.
Return false.

Time complexity: O(n)
We iterate through the array exactly once.
Space complexity: O(n)

class Solution:
def checkSubarraySum(self, nums, k):
prefix_mod = 0
mod_seen = {0: -1}

    for i in range(len(nums)):
        prefix_mod = (prefix_mod + nums[i]) % k

        if prefix_mod in mod_seen:
            # ensures that the size of subarray is at least 2
            if i - mod_seen[prefix_mod] > 1:
                return True
        else:
            # mark the value of prefix_mod with the current index.
            mod_seen[prefix_mod] = i

    return False
19
Q
  1. Palindromic Substrings
    Given a string s, return the number of palindromic substrings in it.
    A string is a palindrome when it reads the same backward as forward.
    A substring is a contiguous sequence of characters within the string.

Example 1:
Input: s = “abc”
Output: 3
Explanation: Three palindromic strings: “a”, “b”, “c”.

Example 2:
Input: s = “aaa”
Output: 6
Explanation: Six palindromic strings: “a”, “a”, “a”, “aa”, “aa”, “aaa”.

A

We choose all possible centers for potential palindromes:

Every single character in the string is a center for possible odd-length palindromes
Every pair of consecutive characters in the string is a center for possible
even-length palindromes.
For every center, we can expand around it as long as we get palindromes
(i.e. the first and last characters should match).
class Solution:
def countSubstrings(self, s: str) -> int:
ans = 0

    for i in range(len(s)):
        # odd-length palindromes, single character center
        ans += self.countPalindromesAroundCenter(s, i, i)

        # even-length palindromes, consecutive characters center
        ans += self.countPalindromesAroundCenter(s, i, i + 1)

    return ans

def countPalindromesAroundCenter(self, ss: str, lo: int, hi: int) -> int:
    ans = 0

    while lo >= 0 and hi < len(ss):
        if ss[lo] != ss[hi]:
            break  # the first and last characters don't match!

        # expand around the center
        lo -= 1
        hi += 1

        ans += 1

    return ans
20
Q
  1. 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.

Example 1:
Input: digits = “23”
Output: [“ad”,”ae”,”af”,”bd”,”be”,”bf”,”cd”,”ce”,”cf”]

A

Use the “backtrack” template
class Solution:
def letterCombinations(self, digits: str) -> List[str]:
# handle empty case first
if not digits:
return []

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

    def backtrack(prefix, next_digits):
        if len(next_digits) == 0:
            output.append(prefix)
        else:
            for letter in phone_map[next_digits[0]]:
                backtrack(prefix + letter, next_digits[1:])

    output = []
    backtrack("", digits)
    return output
21
Q
  1. Merge k Sorted Lists
    You are given an array of k linked-lists lists, each linked-list is sorted in ascending order.

Merge all the linked-lists into one sorted linked-list and return it.

Example 1:
Input: lists = [[1,4,5],[1,3,4],[2,6]]
Output: [1,1,2,3,4,4,5,6]
Explanation: The linked-lists are:
[
1->4->5,
1->3->4,
2->6
]
merging them into one sorted list:
1->1->2->3->4->4->5->6

A

Pair up k lists and merge each pair.

After the first pairing, k lists are merged into k/2 lists with average 2N/k length, then k/4, k/8 and so on.

Repeat this procedure until we get the final sorted linked list.

Thus, we’ll traverse almost N nodes per pairing and merging, and repeat this procedure about log
2

k times.

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]:
amount = len(lists)
interval = 1
while interval < amount:
for i in range(0, amount - interval, interval * 2):
lists[i] = self.merge2Lists(lists[i], lists[i + interval])
interval *= 2

    return lists[0] if amount > 0 else None

def merge2Lists(self, l1, l2):
    head = point = ListNode(0)
    while l1 and l2:
        if l1.val <= l2.val:
            point.next = l1
            l1 = l1.next
        else:
            point.next = l2
            l2 = l1
            l1 = point.next.next
        point = point.next

    if not l1:
        point.next = l2
    else:
        point.next = l1

    return head.next
22
Q
  1. Search in Rotated Sorted Array
    There is an integer array nums sorted in ascending order (with distinct values).
    Prior to being passed to your function, nums is possibly rotated at an unknown pivot index k (1 <= k < nums.length) such that the resulting array is [nums[k], nums[k+1], …, nums[n-1], nums[0], nums[1], …, nums[k-1]] (0-indexed). For example, [0,1,2,4,5,6,7] might be rotated at pivot index 3 and become [4,5,6,7,0,1,2].
    Given the array nums after the possible rotation and an integer target, return the index of target if it is in nums, or -1 if it is not in nums.
    You must write an algorithm with O(log n) runtime complexity.

Example 1:
Input: nums = [4,5,6,7,0,1,2], target = 0
Output: 4

Example 2:
Input: nums = [4,5,6,7,0,1,2], target = 3
Output: -1

A

33 - Normal binary search, but need to handle the 3 cases

class Solution:
def search(self, nums: List[int], target: int) -> int:
n = len(nums)
left, right = 0, n - 1
while left <= right:
mid = left + (right - left) // 2

        # Case 1: find target
        if nums[mid] == target:
            return mid

        # Case 2: subarray on mid's left is sorted
        elif nums[mid] >= nums[left]:
            if target >= nums[left] and target < nums[mid]:
                right = mid - 1
            else:
                left = mid + 1

        # Case 3: subarray on mid's right is sorted.
        else:
            if target <= nums[right] and target > nums[mid]:
                left = mid + 1
            else:
                right = mid - 1
    return -1
23
Q

28 - StrStr - find a substring within a string

A

Runtime: O(N x M)
Space: O(1)

Chris Solution
class Solution:
def strStr(self, haystack: str, needle: str) -> int:
hlen = len(haystack)
nlen = len(needle)
nlast = nlen - 1

    if nlen == 0:
        return 0

    for i in range(hlen - nlen + 1):
        for j in range(nlen):
            if haystack[i + j] != needle[j]:
                break
            if j == nlast:
                return i

    return -1
24
Q

38 - Count and Say
RLE = Run Length Encoding

Input: n = 4
Output: “1211”

Explanation:
countAndSay(1) = “1”
countAndSay(2) = RLE of “1” = “11”
countAndSay(3) = RLE of “11” = “21”
countAndSay(4) = RLE of “21” = “1211”

A

class Solution:
def countAndSay(self, n: int) -> str:
current_string = “1”
for _ in range(n - 1):
next_string = “”
j = 0
k = 0
while j < len(current_string):
while (
k < len(current_string)
and current_string[k] == current_string[j]
):
k += 1
next_string += str(k - j) + current_string[j]
j = k
current_string = next_string
return current_string

25
Q

121 Max Profit Stock Sale

A

Greedy algorithm
Keep a min_element and max_profit variable
Update them in the loop
Runtime: O(N)
Space: O(1)

def max_profit(prices):
if len(prices) == 1:
return 0

min_elem = prices[0]
max_profit = prices[1] - prices[0]

for i in range(1, len(prices)):
    elem = prices[i]
    profit = elem - min_elem
    if profit > max_profit:
        max_profit = profit
    if elem < min_elem:
        min_elem = elem

return max_profit if max_profit > 0 else 0
26
Q

162 - Find Peak Element
A peak element is an element that is strictly greater than its neighbors.

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.

You may imagine that nums[-1] = nums[n] = -∞. In other words, an element is always considered to be strictly greater than a neighbor that is outside the array.

You must write an algorithm that runs in O(log n) time.

Example 1:

Input: nums = [1,2,3,1]
Output: 2
Explanation: 3 is a peak element and your function should return the index number 2.
Example 2:

Input: nums = [1,2,1,3,5,6,4]
Output: 5
Explanation: Your function can return either index number 1 where the peak element is 2, or index number 5 where the peak element is 6.

A

WARNING:
This doesn’t follow the normal binary search template
~~~
class Solution:
def findPeakElement(self, nums: list[int]) -> int:
left = 0
right = len(nums) - 1
while left < right:
mid = (left + right) // 2
# if current elem > successor (next),
# either 1) we are on a falling slope
# 2) mid is the peak index. either way we reduce search space by 1/2
if nums[mid] > nums[mid + 1]:
right = mid
else:
left = mid + 1

    return left ~~~
27
Q
  1. Diameter of Binary Tree
    Given the root of a binary tree, return the diameter of the tree.

The diameter of a binary tree is the length of the longest path between any two nodes in a tree. This path may or may not pass through the root.

The length of a path between two nodes is represented by the number of edges between them.

 1
/ \     2   3   /\ 4   5 Input: root = [1,2,3,4,5] Output: 3 Explanation: 3 is the length of the path [4,2,1,3] or [5,2,1,3].
A

Recursion.
Base case: node is empty, return 0
Recursive case: recusive call to left and right child
Then update diameter to max(diameter, left_path + right_path)
return call
Probably need to practice this a few times

Runtime: O(N)
Space: O(N)

”””
class Solution:
def diameterOfBinaryTree(self, root: TreeNode) -> int:
diameter = 0

    def longest_path(node):
        if not node:
            return 0
        nonlocal diameter
        # recursively find the longest path in
        # both left child and right child
        left_path = longest_path(node.left)
        right_path = longest_path(node.right)

        # update the diameter if left_path plus right_path is larger
        diameter = max(diameter, left_path + right_path)

        # return the longest one between left_path and right_path;
        # remember to add 1 for the path connecting the node and its parent
        return max(left_path, right_path) + 1

    longest_path(root)
    return diameter
28
Q

670 - Max Swap
670. 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.

Example 1:
Input: num = 2736
Output: 7236
Explanation: Swap the number 2 and the number 7.

Example 2:
Input: num = 9973
Output: 9973
Explanation: No swap.

A

Approach #2: Greedy [Accepted]
Intuition

At each digit of the input number in order, if there is a larger digit that occurs later, we know that the best swap must occur with the digit we are currently considering.
Algorithm

We will compute last[d] = i, the index i of the last occurrence of digit d (if it exists).
Afterward, when scanning the number from left to right, if there is a larger digit in the future, we will swap it with the largest such digit; if there are multiple such digits, we will swap it with the one that occurs the latest.

Complexity Analysis
Let N be the total number of digits in the input number.

Time Complexity: O(N).
Every digit is considered at most once.

Space Complexity: O(N).
We keep each digit in the array of A, as in the approach #1.
The additional space used by the hashtable of last only has up to 10 values. Therefore, on this part, the space complexity is O(1).
In total, the overall space complexity is O(N).
“””
class Solution:
def maximumSwap(self, num: int) -> int:
# convert string to list of ints
nums = list(map(int, str(num)))
# create hash containing last index of a value
last = {x: i for i, x in enumerate(nums)}

    for i, x in enumerate(nums):
        for d in range(9, x, -1):
            if d in last and last[d] > i:
                # swap
                nums[i], nums[last[d]] = nums[last[d]], nums[i]
                return int("".join(map(str, nums)))
    return num
29
Q

827 - Make Largest Island
You are given an n x n binary matrix grid. You are allowed to change at most one 0 to be 1.
Return the size of the largest island in grid after applying this operation.
An island is a 4-directionally connected group of 1s.

Example 1:
Input: grid = [[1,0],[0,1]]
Output: 3
Explanation: Change one 0 to 1 and connect two 1s, then we get an island with area = 3.

A
class Solution:
    def largestIsland(self, grid):
        # returns grid value or 0 if out of bounds
        num_rows = len(grid)
        num_cols = len(grid[0])

        def get(i, j):
            if i < 0 or j < 0 or i >= num_rows or j >= num_cols:
                return 0
            return grid[i][j]

        def dfs(i, j, island_num):
            if get(i, j) != 1:
                return 0
            grid[i][j] = island_num
            return 1 + dfs(i + 1, j, island_num) + dfs(i - 1, j, island_num) + dfs(i, j + 1, island_num) + dfs(i, j - 1, island_num)

        island_sizes = [0, 0]
        island_id = len(island_sizes)
        for row in range(num_rows):
            for col in range(num_cols):
                if grid[row][col] == 1:
                    size = dfs(row, col, island_id)
                    if size == (num_rows * num_cols):
                        return size

                    sizes.append(size)
                    island_id += 1

        max_island = 0
 
        for row in range(num_rows):
            for col in range(num_cols):
                if grid[row][col] == 0:
                    island_set = { get(row+1,j), get(row-1,j), get(row,j+1), get(row,j-1) }
                    combined_island_size = sum(sizes[i] for i in island_set)
                    max_island = max(max_island, 1 + combined_island_size)

        return max_island
30
Q

1570 - Given two sparse vectors, compute their dot product.

Implement class SparseVector:

SparseVector(nums) Initializes the object with the vector nums
dotProduct(vec) Compute the dot product between the instance of SparseVector and vec
A sparse vector is a vector that has mostly zero values, you should store the sparse vector efficiently and compute the dot product between two SparseVector.

Follow up: What if only one of the vectors is sparse?

Example 1:

Input: nums1 = [1,0,0,2,3], nums2 = [0,3,0,4,0]
Output: 8
Explanation: v1 = SparseVector(nums1) , v2 = SparseVector(nums2)
v1.dotProduct(v2) = 10 + 03 + 00 + 24 + 3*0 = 8

A

index array is apparently better because hash function can take longer
class SparseVector:
def __init__(self, nums: List[int]):
self.pairs = []
for index, value in enumerate(nums):
if value != 0:
self.pairs.append([index, value])

def dotProduct(self, vec: 'SparseVector') -> int:
    result = 0
    p, q = 0, 0

    while p < len(self.pairs) and q < len(vec.pairs):
        if self.pairs[p][0] == vec.pairs[q][0]:
            result += self.pairs[p][1] * vec.pairs[q][1]
            p += 1
            q += 1
        elif self.pairs[p][0] < vec.pairs[q][0]:
            p += 1
        else:
            q += 1

    return result

If there is a follow up question, regarding lots of zeroes, then give this answer:
class SparseVector:
def __init__(self, nums):
self.pairVec = []
for i, num in enumerate(nums):
if num != 0:
self.pairVec.append((i, num))

def binarySearch(self, avec, index):
    left = 0
    right = len(avec) - 1
    while left <= right:
        mid = left + (right - left) // 2
        if avec[mid][0] > index:
            right = mid - 1
        elif avec[mid][0] < index:
            left = mid + 1
        else:
            return avec[mid][1]
    return 0

# Return the dotProduct of two sparse vectors
def dotProduct(self, vec):
    res = 0
    if len(vec.pairVec) > len(self.pairVec):
        for aPair in self.pairVec:
            res += self.binarySearch(vec.pairVec, aPair[0]) * aPair[1]
    else:
        for aPair in vec.pairVec:
            res += self.binarySearch(self.pairVec, aPair[0]) * aPair[1]
    return res
31
Q

1762 - 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.

Example 1:

Input: heights = [4,2,3,1]
Output: [0,2,3]
Explanation: Building 1 (0-indexed) does not have an ocean view because building 2 is taller.
Example 2:

Input: heights = [4,3,2,1]
Output: [0,1,2,3]
Explanation: All the buildings have an ocean view.
Example 3:

Input: heights = [1,3,2,4]
Output: [3]
Explanation: Only building 3 has an ocean view.

A


class Solution:

def findBuildings(self, heights: List[int]) -> List[int]:
    n = len(heights)
    answer = []
    max_height = -1
    
    for index in range(n-1, -1, -1):
        # If there is no building higher (or equal) than the current one to its right,
        # push it in the answer array.
        if max_height < heights[index]:
            answer.append(index)
        
            # Update max building till now.
            max_height = heights[index]
    
    answer.reverse()
    return answer
32
Q
  1. Median of Two Sorted Arrays
    Given two sorted arrays nums1 and nums2 of size m and n respectively,
    return the median of the two sorted arrays.
    The overall run time complexity should be O(log (m+n)).

Example 1:
Input: nums1 = [1,3], nums2 = [2]
Output: 2.00000
Explanation: merged array = [1,2,3] and median is 2.

Example 2:
Input: nums1 = [1,2], nums2 = [3,4]
Output: 2.50000
Explanation: merged array = [1,2,3,4] and median is (2 + 3) / 2 = 2.5.

A

class Solution:
def findMedianSortedArrays(
self, nums1: List[int], nums2: List[int]
) -> float:
if len(nums1) > len(nums2):
return self.findMedianSortedArrays(nums2, nums1)

    m, n = len(nums1), len(nums2)
    left, right = 0, m

    while left <= right:
        partitionA = (left + right) // 2
        partitionB = (m + n + 1) // 2 - partitionA

        maxLeftA = (
            float("-inf") if partitionA == 0 else nums1[partitionA - 1]
        )
        minRightA = float("inf") if partitionA == m else nums1[partitionA]
        maxLeftB = (
            float("-inf") if partitionB == 0 else nums2[partitionB - 1]
        )
        minRightB = float("inf") if partitionB == n else nums2[partitionB]

        if maxLeftA <= minRightB and maxLeftB <= minRightA:
            if (m + n) % 2 == 0:
                return (
                    max(maxLeftA, maxLeftB) + min(minRightA, minRightB)
                ) / 2
            else:
                return max(maxLeftA, maxLeftB)
        elif maxLeftA > minRightB:
            right = partitionA - 1
        else:
            left = partitionA + 1
33
Q
  1. Regular Expression Matching
    Solved
    Hard
    Topics
    Companies
    Given an input string s and a pattern p, implement regular expression matching with support for ‘.’ and ‘*’ where:

’.’ Matches any single character.​​​​
‘*’ Matches zero or more of the preceding element.
The matching should cover the entire input string (not partial).

Example 1:

Input: s = “aa”, p = “a”
Output: false
Explanation: “a” does not match the entire string “aa”.
Example 2:

Input: s = “aa”, p = “a
Output: true
Explanation: ‘
’ means zero or more of the preceding element, ‘a’. Therefore, by repeating ‘a’ once, it becomes “aa”.
Example 3:

Input: s = “ab”, p = “.
Output: true
Explanation: “.
” means “zero or more (*) of any character (.)”.

A

As the problem has an optimal substructure, it is natural to cache intermediate results.
We ask the question dp(i, j): does text[i:] and pattern[j:] match?
We can describe our answer in terms of answers to questions involving smaller strings.
Algorithm

We proceed with the same recursion as in Approach 1,
except because calls will only ever be made to match(text[i:], pattern[j:]),
we use dp(i, j) to handle those calls instead, saving us
expensive string-building operations and allowing us to cache the intermediate results.

class Solution(object):
def isMatch(self, text: str, pattern: str) -> bool:
dp = [[False] * (len(pattern) + 1) for _ in range(len(text) + 1)]

    dp[-1][-1] = True
    for i in range(len(text), -1, -1):
        for j in range(len(pattern) - 1, -1, -1):
            first_match = i < len(text) and pattern[j] in {text[i], "."}
            if j + 1 < len(pattern) and pattern[j + 1] == "*":
                dp[i][j] = dp[i][j + 2] or first_match and dp[i + 1][j]
            else:
                dp[i][j] = first_match and dp[i + 1][j + 1]

    return dp[0][0]
34
Q

”””
13. Roman to Integer

Example 1:
Input: s = “III”
Output: 3
Explanation: III = 3.

Example 2:
Input: s = “LVIII”
Output: 58
Explanation: L = 50, V= 5, III = 3.

Example 3:
Input: s = “MCMXCIV”
Output: 1994
Explanation: M = 1000, CM = 900, XC = 90 and IV = 4.

A

values = {
“I”: 1,
“V”: 5,
“X”: 10,
“L”: 50,
“C”: 100,
“D”: 500,
“M”: 1000,
}

class Solution:
def romanToInt(self, s: str) -> int:
total = values.get(s[-1])
for i in reversed(range(len(s) - 1)):
if values[s[i]] < values[s[i + 1]]:
total -= values[s[i]]
else:
total += values[s[i]]
return total

35
Q

20 - Valid Parens

Given a string s containing just the characters ‘(‘, ‘)’, ‘{‘, ‘}’, ‘[’ and ‘]’, determine if the input string is valid.

An input string is valid if:

Open brackets must be closed by the same type of brackets.
Open brackets must be closed in the correct order.
Every close bracket has a corresponding open bracket of the same type.

Example 1:
Input: s = “()”
Output: true

Example 2:
Input: s = “()[]{}”
Output: true

Example 3:
Input: s = “(]”
Output: false

A

Use a Stack.
Always. have a mapping from a paren to associated closing paren character.

Runtime: O(N)
Space: O(N)
“””
class Solution(object):
def isValid(self, s: str) -> bool:

    # The stack to keep track of opening brackets.
    stack = []

    # Hash map for keeping track of mappings. This keeps the code very clean.
    # Also makes adding more types of parenthesis easier
    mapping = {")": "(", "}": "{", "]": "["}

    # For every bracket in the expression.
    for char in s:

        # If the character is an closing bracket
        if char in mapping:

            # Pop the topmost element from the stack, if it is non empty
            # Otherwise assign a dummy value of '#' to the top_element variable
            top_element = stack.pop() if stack else "#"

            # The mapping for the opening bracket in our hash and the top
            # element of the stack don't match, return False
            if mapping[char] != top_element:
                return False
        else:
            # We have an opening bracket, simply push it onto the stack.
            stack.append(char)

    # In the end, if the stack is empty, then we have a valid expression.
    # The stack won't be empty for cases like ((()
    return not stack
36
Q
  1. Remove Duplicates from Sorted Array
    Given an integer array nums sorted in non-decreasing order,
    remove the duplicates in-place such that each unique element appears only once.
    The relative order of the elements should be kept the same. Then return the number of unique elements in nums.

Consider the number of unique elements of nums to be k, to get accepted,
you need to do the following things:

Change the array nums such that the first k elements of nums
contain the unique elements in the order they were present in nums initially.
The remaining elements of nums are not important as well as the size of nums.
Return k.

A

Have a write_index, initialized to 1. Check if values are different and start writing.

Runtime O(N), since we only have 2 pointers, and both the pointers will traverse the array at most once.
Space: O(1), since we are not using any extra space.

class Solution:
def removeDuplicates(self, nums: List[int]) -> int:
num_elems = len(nums)
write_index = 1
for i in range(1, num_elems):
# Found unique element
if nums[i - 1] != nums[i]:
# Updating write_index in our main array
nums[write_index] = nums[i]
# Incrementing write_index count by 1
write_index += 1

    return write_index
37
Q
  1. Next Permutation
    A permutation of an array of integers is an arrangement of its members into a sequence or linear order.

For example, for arr = [1,2,3], the following are all the permutations of arr: [1,2,3], [1,3,2], [2, 1, 3], [2, 3, 1], [3,1,2], [3,2,1].
The next permutation of an array of integers is the next lexicographically greater permutation of its integer. More formally, if all the permutations of the array are sorted in one container according to their lexicographical order, then the next permutation of that array is the permutation that follows it in the sorted container. If such arrangement is not possible, the array must be rearranged as the lowest possible order (i.e., sorted in ascending order).

For example, the next permutation of arr = [1,2,3] is [1,3,2].
Similarly, the next permutation of arr = [2,3,1] is [3,1,2].
While the next permutation of arr = [3,2,1] is [1,2,3] because [3,2,1] does not have a lexicographical larger rearrangement.
Given an array of integers nums, find the next permutation of nums.

The replacement must be in place and use only constant extra memory.

Example 1:

Input: nums = [1,2,3]
Output: [1,3,2]
Example 2:

Input: nums = [3,2,1]
Output: [1,2,3]
Example 3:

Input: nums = [1,1,5]
Output: [1,5,1]

A

Multi step process:
Start at right most index (last element) and iterate to the right until you find a decreasing element
Once you find such an element, swap with the closest number that is greater than it
Then reverse the remaining items to the right of the array

For example:
Start with:
|
V
1 5 8 4 7 6 5 3 1

Move pointer to the right, until you see a number which is less than previous num
In the case below, 4 is less than 7

  |
  V 1 5 8 4 7 6 5 3 1

Now find the first number that is greater than 4 (or not less than equal to 4)
Then swap the two number
| |
V V
1 5 8 4 7 6 5 3 1
becomes
1 5 8 5 7 6 4 3 1

Finally, reverse the order of numbers after the lower index
1 5 8 5 (7 6 4 3 1)
becomes
1 5 8 5 (1 3 4 6 7)

class Solution:
def nextPermutation(self, nums):
“””
:type nums: List[int]
:rtype: void Do not return anything, modify nums in-place instead.
“””
i = len(nums) - 2
while i >= 0 and nums[i + 1] <= nums[i]:
i -= 1
if i >= 0:
j = len(nums) - 1
while nums[j] <= nums[i]:
j -= 1
self.swap(nums, i, j)
self.reverse(nums, i + 1)

def reverse(self, nums, start):
    i, j = start, len(nums) - 1
    while i < j:
        self.swap(nums, i, j)
        i += 1
        j -= 1

def swap(self, nums, i, j):
    temp = nums[i]
    nums[i] = nums[j]
    nums[j] = temp
38
Q
  1. Wildcard Matching
    Given an input string (s) and a pattern (p),
    implement wildcard pattern matching with support for ‘?’ and ‘*’ where:

’?’ Matches any single character.
‘*’ Matches any sequence of characters (including the empty sequence).
The matching should cover the entire input string (not partial).

Example 1:

Input: s = “aa”, p = “a”
Output: false
Explanation: “a” does not match the entire string “aa”.
Example 2:

Input: s = “aa”, p = “
Output: true
Explanation: ‘
’ matches any sequence.
Example 3:

Input: s = “cb”, p = “?a”
Output: false
Explanation: ‘?’ matches ‘c’, but the second letter is ‘a’, which does not match ‘b’.

A

Watch one of the YouTube videos and consider the 3rd Editorial solution from LeetCode.

Runtime: O(SP) where S and P are lengths of the input string
and the pattern respectively.
Space: O(S
P) to store the matrix.

class Solution:
def isMatch(self, s: str, p: str) -> bool:
s_len = len(s)
p_len = len(p)

    # The base cases
    if p == s or set(p) == {"*"}:
        return True
    if p == "" or s == "":
        return False

    # Initialize all matrix except [0][0] element as False
    d = [[False] * (s_len + 1) for _ in range(p_len + 1)]
    d[0][0] = True

    # DP compute
    for p_idx in range(1, p_len + 1):
        # The current character in the pattern is '*'
        if p[p_idx - 1] == "*":
            s_idx = 1

            # d[p_idx - 1][s_idx - 1] is a string-pattern match
            # on the previous step, i.e. one character before.
            # Find the first idx in the string with the previous math.
            while not d[p_idx - 1][s_idx - 1] and s_idx < s_len + 1:
                s_idx += 1

            # If (string) matches (pattern),
            # when (string) matches (pattern)* as well
            d[p_idx][s_idx - 1] = d[p_idx - 1][s_idx - 1]

            # If (string) matches (pattern),
            # when (string)(whatever_characters) matches (pattern)* as well
            while s_idx < s_len + 1:
                d[p_idx][s_idx] = True
                s_idx += 1

        # The current character in the pattern is '?'
        elif p[p_idx - 1] == "?":
            for s_idx in range(1, s_len + 1):
                d[p_idx][s_idx] = d[p_idx - 1][s_idx - 1]

        # The current character in the pattern is not '*' or '?'
        else:
            for s_idx in range(1, s_len + 1):
                # Match is possible if there is a previous match
                # and current characters are the same
                d[p_idx][s_idx] = (
                    d[p_idx - 1][s_idx - 1] and p[p_idx - 1] == s[s_idx - 1]
                )

    return d[p_len][s_len]
39
Q
  1. Sliding Window Maximum
    You are given an array of integers nums, there is a sliding window of size k which is moving from the very left of the array to the very right. You can only see the k numbers in the window. Each time the sliding window moves right by one position.
    Return the max sliding window.
    Example 1:
    Input: nums = [1,3,-1,-3,5,3,6,7], k = 3
    Output: [3,3,5,5,6,7]
A
class Solution:
    def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
        q = deque() # stores *indices*
        res = []
        for i, cur in enumerate(nums):
            while q and nums[q[-1]] <= cur:
                q.pop()
            q.append(i)
            # remove first element if it's outside the window
            if q[0] == i - k:
                q.popleft()
            # if window has k elements add to results (first k-1 windows have < k elements because we start from empty window and add 1 element each iteration)
            if i >= k - 1:
                res.append(nums[q[0]])

        return res
40
Q

Write functions to serialize and deserialize a binary tree

A

Serialize function will return a comma delimited string. “null” will represent a None left/right tree.

Serialize does a DFS traversal,
it appends to nums array (appends “null” is node is None)
starting at root, then recursive traversing left side, then recursively traversing right side

Deserialize
strs variable defined before, split on the comma delimiter
build_tree func is defined
In build_tree, pop(0)
check for null, return None
create new TreeNode(int(elem))
set left, right values to recursive calls of build_tree

def serialize(self, root):
        """Encodes a tree to a single string.
        
        :type root: TreeNode
        :rtype: str
        """
        nums = []
        
        def traverse(t):
            if t is None:
              nums.append('null')
              return
            
            nums.append(t.value)
            traverse(t.left)
            traverse(t.right)
            
        traverse(root)
        res = ','.join(map(str, nums))
        #print("res is: {}".format(res))
        return res

def deserialize(self, data):
        """Decodes your encoded data to tree.
        
        :type data: str
        :rtype: TreeNode
        """
        strs = data.split(',')
        
        def build_tree():
            s = strs.pop(0)
            if s == 'null':
                return None
            
            tn = TreeNode(int(s))
            tn.left = build_tree()
            tn.right = build_tree()
            
            return tn
        
        return build_tree()
41
Q

15 - Three Sum = 0
“””
Given an integer array nums, return all the triplets [nums[i], nums[j], nums[k]] such that i != j, i != k, and j != k, and nums[i] + nums[j] + nums[k] == 0.
Notice that the solution set must not contain duplicate triplets.

Example 1:
Input: nums = [-1,0,1,2,-1,-4]
Output: [[-1,-1,2],[-1,0,1]]
Explanation:
nums[0] + nums[1] + nums[2] = (-1) + 0 + 1 = 0.
nums[1] + nums[2] + nums[4] = 0 + 1 + (-1) = 0.
nums[0] + nums[3] + nums[4] = (-1) + 2 + (-1) = 0.
The distinct triplets are [-1,0,1] and [-1,-1,2].
Notice that the order of the output and the order of the triplets does not matter.
“””

A

Sort the array.
If the first element is positive, then can’t find any triplet that can sum to zero (cuz need negative)

Iterate through array and use 2 pointers to search from (i+1) to end of array.

Review the code for this.

Runtime O(n lg n) - sort time
Space - Constant O(1)
~~~
class Solution_2024_09_13:
def threeSum(nums):
res = []
nums.sort()
for i in range(len(nums)):
if nums[i] > 0:
break
if i == 0 or nums[i] != nums[i-1]:
self.two_sum(nums, i, res)

    return res

def two_sum(nums, i, res):
    left = i + 1
    right = len(nums) - 1
    while left < right:
        sum = nums[i] + nums[left] + nums[right]
        if sum == 0:
            res.append([nums[i], nums[left], nums[right]])
            left += 1
            right -= 1
            # move left pointer if there are repeated values
            while left < right and nums[left] == nums[left -1]:
                left += 1
        elif sum > 0:
            right -= 1
        else:
            left += 1 ~~~