0% found this document useful (0 votes)
61 views128 pages

NeetCode_150_GPT

Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
61 views128 pages

NeetCode_150_GPT

Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 128

Array Hashing Problem Template

To create a single template for solving array and hashing problems, we can focus on the
following common steps:

1. Input Handling: Define how to read input, whether it’s from function arguments or
standard input.

2. Data Structures: Initialize data structures such as dictionaries (hashmaps) or sets to


keep track of counts, unique elements, etc.

3. Logic Implementation: Implement the specific logic needed to solve the problem, which
often involves iterating through the array and updating the data structures accordingly.

4. Output Handling: Define how to format and return the output.

Here's a general template followed by specific implementations for each problem:

Template

python

def template_problem_solver(input_data):
# Step 1: Input handling
# For example, assume input_data is a list
data = input_data

# Step 2: Initialize data structures


counts = {} # or use set() for uniqueness checks

# Step 3: Logic implementation


# For example, iterate through the array
for item in data:
# Update counts or handle logic
pass # Implement the specific logic here

# Step 4: Output handling


return # Return the result as needed

Problem Implementations
1. Contains Duplicate

Printed with ChatGPT to PDF 1/128


python

def contains_duplicate(nums):
seen = set()
for num in nums:
if num in seen:
return True
seen.add(num)
return False

2. Valid Anagram

python

def is_anagram(s, t):


if len(s) != len(t):
return False
count = {}
for char in s:
count[char] = count.get(char, 0) + 1
for char in t:
if char not in count or count[char] == 0:
return False
count[char] -= 1
return True

3. Two Sum

python

def two_sum(nums, target):


num_map = {}
for i, num in enumerate(nums):
complement = target - num
if complement in num_map:
return [num_map[complement], i]
num_map[num] = i
return []

4. Group Anagrams

Printed with ChatGPT to PDF 2/128


python

def group_anagrams(strs):
anagrams = {}
for s in strs:
key = ''.join(sorted(s))
if key in anagrams:
anagrams[key].append(s)
else:
anagrams[key] = [s]
return list(anagrams.values())

5. Top K Frequent Elements

python

def top_k_frequent(nums, k):


frequency = {}
for num in nums:
frequency[num] = frequency.get(num, 0) + 1
# Sort based on frequency
sorted_nums = sorted(frequency.items(), key=lambda x: x[1], reverse=True)
return [num for num, _ in sorted_nums[:k]]

6. Encode and Decode Strings

python

def encode(strs):
encoded = ""
for s in strs:
encoded += str(len(s)) + "#" + s
return encoded

def decode(s):
decoded = []
i = 0
while i < len(s):
j = s.find('#', i)
length = int(s[i:j])
decoded.append(s[j+1:j+1+length])

Printed with ChatGPT to PDF 3/128


i = j + 1 + length
return decoded

7. Product of Array Except Self

python

def product_except_self(nums):
n = len(nums)
output = [1] * n
left_product = 1
for i in range(n):
output[i] = left_product
left_product *= nums[i]
right_product = 1
for i in range(n-1, -1, -1):
output[i] *= right_product
right_product *= nums[i]
return output

8. Valid Sudoku

python

def is_valid_sudoku(board):
rows, cols, boxes = {}, {}, {}
for i in range(9):
for j in range(9):
num = board[i][j]
if num != '.':
if (i, num) in rows or (num, j) in cols or (i // 3, j // 3, num) in
boxes:
return False
rows[(i, num)] = True
cols[(num, j)] = True
boxes[(i // 3, j // 3, num)] = True
return True

9. Longest Consecutive Sequence

Printed with ChatGPT to PDF 4/128


python

def longest_consecutive(nums):
num_set = set(nums)
longest = 0
for num in num_set:
if num - 1 not in num_set: # Check for the start of a sequence
current_num = num
current_streak = 1
while current_num + 1 in num_set:
current_num += 1
current_streak += 1
longest = max(longest, current_streak)
return longest

Summary
This template provides a structure that can be modified for different problems, while the
individual implementations demonstrate how to apply hashing and array manipulation
techniques to solve each specific problem effectively. You can easily adapt this approach to
other similar problems by focusing on the steps outlined in the template.

Certainly! Let's create a more structured template that highlights the key concepts and
strategies for solving problems related to arrays and hashing. After defining the template,
we'll apply it to solve each of the listed problems step by step.

Template Explanation
1. Function Definition: Define a function that takes the required inputs.

2. Data Structure Initialization: Identify the data structures needed, such as hashmaps,
sets, or lists, depending on the problem.

3. Iterate Through Input: Use loops to process the input data, updating the data
structures as necessary.

4. Logic Implementation: Implement the core logic of the solution, often using conditions,
calculations, or modifications of the data structures.

5. Result Compilation: Collect the results in an appropriate format (e.g., list, dictionary) to
return.

6. Return Statement: Return the final output.

Printed with ChatGPT to PDF 5/128


Applying the Template
Now, let's apply this template to each of the problems you mentioned.

1. Contains Duplicate

python

def contains_duplicate(nums):
# Step 2: Data Structure Initialization
seen = set()

# Step 3: Iterate Through Input


for num in nums:
# Step 4: Logic Implementation
if num in seen:
return True # Duplicate found
seen.add(num) # Add to set

# Step 5: Result Compilation


return False # No duplicates found

# Example Usage
print(contains_duplicate([1, 2, 3, 4])) # Output: False
print(contains_duplicate([1, 2, 3, 1])) # Output: True

2. Valid Anagram

python

def is_anagram(s, t):


# Step 2: Data Structure Initialization
count = {}

# Step 3: Iterate Through Input (first string)


for char in s:
count[char] = count.get(char, 0) + 1

# Step 3: Iterate Through Input (second string)


for char in t:
if char not in count or count[char] == 0:
return False # Not an anagram
count[char] -= 1 # Decrease count

Printed with ChatGPT to PDF 6/128


# Step 5: Result Compilation
return True # All counts matched

# Example Usage
print(is_anagram("anagram", "nagaram")) # Output: True
print(is_anagram("rat", "car")) # Output: False

3. Two Sum

python

def two_sum(nums, target):


# Step 2: Data Structure Initialization
num_map = {}

# Step 3: Iterate Through Input


for i, num in enumerate(nums):
complement = target - num

# Step 4: Logic Implementation


if complement in num_map:
return [num_map[complement], i] # Found the indices

num_map[num] = i # Store the index of the current number

# Step 5: Result Compilation


return [] # No solution found

# Example Usage
print(two_sum([2, 7, 11, 15], 9)) # Output: [0, 1]

4. Group Anagrams

python

def group_anagrams(strs):
# Step 2: Data Structure Initialization
anagrams = {}

# Step 3: Iterate Through Input


for s in strs:
key = ''.join(sorted(s)) # Create a key by sorting the string

Printed with ChatGPT to PDF 7/128


if key in anagrams:
anagrams[key].append(s) # Add to existing key
else:
anagrams[key] = [s] # Create a new key

# Step 5: Result Compilation


return list(anagrams.values())

# Example Usage
print(group_anagrams(["eat", "tea", "tan", "ate", "nat", "bat"])) # Output:
[['eat', 'tea', 'ate'], ['tan', 'nat'], ['bat']]

5. Top K Frequent Elements

python

def top_k_frequent(nums, k):


# Step 2: Data Structure Initialization
frequency = {}

# Step 3: Iterate Through Input


for num in nums:
frequency[num] = frequency.get(num, 0) + 1 # Count frequency

# Step 4: Logic Implementation


# Sort based on frequency
sorted_nums = sorted(frequency.items(), key=lambda x: x[1], reverse=True)

# Step 5: Result Compilation


return [num for num, _ in sorted_nums[:k]] # Get top k elements

# Example Usage
print(top_k_frequent([1, 1, 1, 2, 2, 3], 2)) # Output: [1, 2]

6. Encode and Decode Strings

python

def encode(strs):
# Step 2: Data Structure Initialization
encoded = ""

# Step 3: Iterate Through Input

Printed with ChatGPT to PDF 8/128


for s in strs:
# Step 4: Logic Implementation
encoded += str(len(s)) + "#" + s # Encode length and string

# Step 5: Result Compilation


return encoded

def decode(s):
# Step 2: Data Structure Initialization
decoded = []
i = 0

# Step 3: Iterate Through Encoded String


while i < len(s):
j = s.find('#', i) # Find the delimiter
length = int(s[i:j]) # Get the length of the next string
decoded.append(s[j+1:j+1+length]) # Decode the string
i = j + 1 + length # Move to the next encoded string

# Step 5: Result Compilation


return decoded

# Example Usage
encoded = encode(["hello", "world"])
print(encoded) # Output: "5#hello5#world"
print(decode(encoded)) # Output: ["hello", "world"]

7. Product of Array Except Self

python

def product_except_self(nums):
# Step 2: Data Structure Initialization
n = len(nums)
output = [1] * n

# Step 3: Iterate for Left Products


left_product = 1
for i in range(n):
output[i] = left_product
left_product *= nums[i] # Update left product

Printed with ChatGPT to PDF 9/128


# Step 3: Iterate for Right Products
right_product = 1
for i in range(n - 1, -1, -1):
output[i] *= right_product # Update output with right product
right_product *= nums[i] # Update right product

# Step 5: Result Compilation


return output

# Example Usage
print(product_except_self([1, 2, 3, 4])) # Output: [24, 12, 8, 6]

8. Valid Sudoku

python

def is_valid_sudoku(board):
# Step 2: Data Structure Initialization
rows, cols, boxes = {}, {}, {}

# Step 3: Iterate Through the Board


for i in range(9):
for j in range(9):
num = board[i][j]
if num != '.':
# Step 4: Logic Implementation
if (i, num) in rows or (num, j) in cols or (i // 3, j // 3, num) in
boxes:
return False # Invalid Sudoku

rows[(i, num)] = True


cols[(num, j)] = True
boxes[(i // 3, j // 3, num)] = True

# Step 5: Result Compilation


return True # All checks passed

# Example Usage
board = [
["5", "3", ".", ".", "7", ".", ".", ".", "."],
["6", ".", ".", "1", "9", "5", ".", ".", "."],
[".", "9", "8", ".", ".", ".", ".", "6", "."],

Printed with ChatGPT to PDF 10/128


["8", ".", ".", ".", "6", ".", ".", ".", "3"],
["4", ".", ".", "8", ".", "3", ".", ".", "1"],
["7", ".", ".", ".", "2", ".", ".", ".", "6"],
[".", "6", ".", ".", ".", ".", "2", "8", "."],
[".", ".", ".", "4", "1", "9", ".", ".", "5"],
[".", ".", ".", ".", "8", ".", ".", "7", "9"]
]
print(is_valid_sudoku(board)) # Output: True

9. Longest Consecutive Sequence

python

def longest_consecutive(nums):
# Step 2: Data Structure Initialization
num_set = set(nums) # Use a set for O(1) lookups
longest = 0

# Step 3: Iterate Through Input


for num in num_set:
if num - 1 not in num_set: # Check if it's the start of a sequence
current_num = num
current_streak = 1

# Step 4: Logic Implementation


while current_num + 1 in num_set:
current_num += 1
current_streak += 1

# Step 5: Result Compilation


longest = max(longest, current_streak)

return longest

# Example Usage
print(longest_consecutive([100, 4, 200, 1, 3, 2])) # Output: 4 (1, 2, 3, 4)

Summary
This approach gives you a unified template for solving problems related to arrays and
hashing in Python. Each specific implementation adapts the template while applying unique

Printed with ChatGPT to PDF 11/128


logic relevant to the problem, keeping the overall structure consistent. This consistency helps
in understanding and solving similar problems effectively.

Two Pointers Template

Explanation

The Two Pointers technique is a widely used approach in algorithm design, particularly
useful for solving problems involving sorted arrays or lists. This technique involves using two
pointers to traverse the data structure from different ends, which helps in optimizing space
and time complexity.

Key Advantages:

Efficiency: Many problems that can be solved using this technique can be done in linear
time O(n) instead of quadratic time O(n2 ), which is typical with nested loops.

Simplicity: It often simplifies the logic and reduces the need for additional data
structures.

Versatility: It can be applied to a variety of problems, including searching, merging, and


comparing elements.

When to Use the Two Pointers Technique

When dealing with sorted arrays (or when you can sort them).

When the problem involves finding pairs or triplets that satisfy a specific condition (e.g.,
sums).

When you need to compare elements from both ends of an array or list.

When the problem requires finding the maximum or minimum values based on
conditions.

Template
1. Initialization: Set two pointers, usually one at the beginning ( left ) and one at the end
( right ) of the data structure.

2. Iterate with Conditions: Use a loop to traverse the array while the two pointers do not
cross each other. Implement the core logic of the problem using conditions that
compare the values at the two pointers.

3. Adjust Pointers: Depending on the logic, move either the left pointer, the right
pointer, or both.

Printed with ChatGPT to PDF 12/128


4. Result Compilation: Collect the results as required by the problem.

5. Return Statement: Return the final output.

Application of the Template


The two pointers technique can be applied to various problems, such as:

Finding pairs in an array that sum to a specific value.

Merging two sorted arrays.

Checking for palindromes.

Finding triplets that sum to zero.

Optimizing areas in problems like "Container With Most Water".

Solving Problems Using the Two Pointers Template

1. Valid Palindrome

python

def is_palindrome(s):
# Step 1: Initialization
left, right = 0, len(s) - 1

# Step 2: Iterate with Conditions


while left < right:
# Skip non-alphanumeric characters
while left < right and not s[left].isalnum():
left += 1
while left < right and not s[right].isalnum():
right -= 1

# Step 3: Logic Implementation


if s[left].lower() != s[right].lower():
return False # Not a palindrome

# Step 4: Adjust Pointers


left += 1
right -= 1

# Step 5: Result Compilation


return True # Is a palindrome

Printed with ChatGPT to PDF 13/128


# Example Usage
print(is_palindrome("A man, a plan, a canal: Panama")) # Output: True
print(is_palindrome("race a car")) # Output: False

2. Two Sum II - Input Array Is Sorted

python

def two_sum_sorted(numbers, target):


# Step 1: Initialization
left, right = 0, len(numbers) - 1

# Step 2: Iterate with Conditions


while left < right:
current_sum = numbers[left] + numbers[right]

# Step 3: Logic Implementation


if current_sum == target:
return [left + 1, right + 1] # 1-based index
elif current_sum < target:
left += 1 # Move left pointer to increase sum
else:
right -= 1 # Move right pointer to decrease sum

# Step 5: Result Compilation


return []

# Example Usage
print(two_sum_sorted([2, 7, 11, 15], 9)) # Output: [1, 2]

3. 3Sum

python

def three_sum(nums):
# Step 1: Initialization
nums.sort() # Sort the input for two-pointer technique
results = []

# Step 2: Iterate with Conditions


for i in range(len(nums) - 2):
if i > 0 and nums[i] == nums[i - 1]:

Printed with ChatGPT to PDF 14/128


continue # Skip duplicates

left, right = i + 1, len(nums) - 1

while left < right:


current_sum = nums[i] + nums[left] + nums[right]

# Step 3: Logic Implementation


if current_sum < 0:
left += 1 # Increase sum
elif current_sum > 0:
right -= 1 # Decrease sum
else:
results.append([nums[i], nums[left], nums[right]])
# Skip duplicates
while left < right and nums[left] == nums[left + 1]:
left += 1
while left < right and nums[right] == nums[right - 1]:
right -= 1
left += 1
right -= 1

# Step 5: Result Compilation


return results

# Example Usage
print(three_sum([-1, 0, 1, 2, -1, -4])) # Output: [[-1, -1, 2], [-1, 0, 1]]

4. Container With Most Water

python

def max_area(height):
# Step 1: Initialization
left, right = 0, len(height) - 1
max_area = 0

# Step 2: Iterate with Conditions


while left < right:
# Step 3: Logic Implementation
current_area = min(height[left], height[right]) * (right - left)
max_area = max(max_area, current_area)

Printed with ChatGPT to PDF 15/128


# Step 4: Adjust Pointers
if height[left] < height[right]:
left += 1
else:
right -= 1

# Step 5: Result Compilation


return max_area

# Example Usage
print(max_area([1, 8, 6, 2, 5, 4, 8, 3, 7])) # Output: 49

5. Trapping Rain Water

python

def trap(height):
# Step 1: Initialization
left, right = 0, len(height) - 1
left_max, right_max = height[left], height[right]
water_trapped = 0

# Step 2: Iterate with Conditions


while left < right:
# Step 3: Logic Implementation
if left_max < right_max:
left += 1
left_max = max(left_max, height[left])
water_trapped += left_max - height[left]
else:
right -= 1
right_max = max(right_max, height[right])
water_trapped += right_max - height[right]

# Step 5: Result Compilation


return water_trapped

# Example Usage
print(trap([0, 1, 0, 2, 1, 0, 1, 3, 2, 1, 2, 1])) # Output: 6

Summary

Printed with ChatGPT to PDF 16/128


The Two Pointers technique is a powerful and efficient method to solve a variety of problems
involving arrays, particularly when the data is sorted or can be manipulated in a two-sided
manner. This approach can significantly reduce time complexity and simplify logic, making it
a valuable tool in algorithm design. By applying this template, we can effectively tackle
problems like "Valid Palindrome," "Two Sum II," "3Sum," "Container With Most Water," and
"Trapping Rain Water."

Sliding Window Template

Explanation

The Sliding Window technique is a highly efficient approach used for solving problems
related to subarrays or substrings. It involves maintaining a window that can expand or
contract based on certain conditions to track elements within a defined range. This
technique is particularly useful for optimizing problems that would otherwise require nested
loops, reducing the time complexity from O(n2 ) to O(n).

Key Advantages:

Efficiency: By using a single pass through the data structure, it minimizes the need for
multiple iterations.

Simplicity: It provides a clean and straightforward way to manage and manipulate


subarrays or substrings.

Flexibility: It can be adapted to various problems, such as finding the maximum sum of
a subarray or determining the longest substring with certain properties.

When to Use the Sliding Window Technique

When dealing with contiguous subarrays or substrings.

When a problem requires optimizing a condition over a range of elements.

When you need to find a minimum or maximum value in a dynamic range.

When the problem can be divided into smaller, overlapping subproblems.

Template
1. Initialization: Define pointers to represent the start ( start ) and end ( end ) of the
window and any additional variables needed for calculations (e.g., count, max sum).

2. Expand the Window: Increment the end pointer to expand the window and include
more elements.

Printed with ChatGPT to PDF 17/128


3. Contract the Window: Check if the current window satisfies the problem’s condition. If it
does not, increment the start pointer to shrink the window from the left.

4. Update Results: During the expansion or contraction of the window, update any
required results (e.g., maximum length, minimum size).

5. Return Statement: After processing all elements, return the final result.

Application of the Template


The Sliding Window technique can be applied to a range of problems, including:

Finding the best time to buy and sell stocks.

Determining the longest substring without repeating characters.

Optimizing character replacements within a string.

Finding permutations within a substring.

Identifying minimum window substrings that contain all characters from another string.

Finding the maximum value within a sliding window of fixed size.

Solving Problems Using the Sliding Window Template

1. Best Time to Buy and Sell Stock

python

def max_profit(prices):
# Step 1: Initialization
min_price = float('inf')
max_profit = 0

# Step 2: Expand the Window


for price in prices:
# Update the minimum price
if price < min_price:
min_price = price
# Calculate profit
profit = price - min_price
# Update max profit
if profit > max_profit:
max_profit = profit

# Step 5: Return Statement

Printed with ChatGPT to PDF 18/128


return max_profit

# Example Usage
print(max_profit([7, 1, 5, 3, 6, 4])) # Output: 5

2. Longest Substring Without Repeating Characters

python

def length_of_longest_substring(s):
# Step 1: Initialization
char_index_map = {}
start = 0
max_length = 0

# Step 2: Expand the Window


for end in range(len(s)):
if s[end] in char_index_map:
# Step 3: Contract the Window
start = max(start, char_index_map[s[end]] + 1)

# Update the last seen index of the character


char_index_map[s[end]] = end
# Step 4: Update Results
max_length = max(max_length, end - start + 1)

# Step 5: Return Statement


return max_length

# Example Usage
print(length_of_longest_substring("abcabcbb")) # Output: 3 ("abc")

3. Longest Repeating Character Replacement

python

def character_replacement(s, k):


# Step 1: Initialization
char_count = {}
start = 0
max_length = 0
max_count = 0

Printed with ChatGPT to PDF 19/128


# Step 2: Expand the Window
for end in range(len(s)):
char_count[s[end]] = char_count.get(s[end], 0) + 1
max_count = max(max_count, char_count[s[end]])

# Step 3: Contract the Window


while end - start + 1 - max_count > k:
char_count[s[start]] -= 1
start += 1

# Step 4: Update Results


max_length = max(max_length, end - start + 1)

# Step 5: Return Statement


return max_length

# Example Usage
print(character_replacement("AABABBA", 1)) # Output: 4 ("AABA" or "ABBA")

4. Permutation In String

python

def check_inclusion(s1, s2):


from collections import Counter

# Step 1: Initialization
s1_count = Counter(s1)
s2_count = Counter()

# Step 2: Expand the Window


for end in range(len(s2)):
s2_count[s2[end]] += 1

# Step 3: Contract the Window


if end >= len(s1):
s2_count[s2[end - len(s1)]] -= 1
if s2_count[s2[end - len(s1)]] == 0:
del s2_count[s2[end - len(s1)]]

# Step 4: Update Results


if s1_count == s2_count:

Printed with ChatGPT to PDF 20/128


return True

# Step 5: Return Statement


return False

# Example Usage
print(check_inclusion("ab", "eidbaooo")) # Output: True

5. Minimum Window Substring

python

def min_window(s, t):


from collections import Counter

# Step 1: Initialization
t_count = Counter(t)
current_count = {}
start = 0
min_length = float('inf')
min_start = 0
formed = 0
required = len(t_count)

# Step 2: Expand the Window


for end in range(len(s)):
char = s[end]
current_count[char] = current_count.get(char, 0) + 1

# Step 3: Check if current character contributes to the requirement


if char in t_count and current_count[char] == t_count[char]:
formed += 1

# Step 4: Contract the Window


while formed == required:
if end - start + 1 < min_length:
min_length = end - start + 1
min_start = start

current_count[s[start]] -= 1
if s[start] in t_count and current_count[s[start]] < t_count[s[start]]:
formed -= 1

Printed with ChatGPT to PDF 21/128


start += 1

# Step 5: Return Statement


return s[min_start:min_start + min_length] if min_length != float('inf') else ""

# Example Usage
print(min_window("ADOBECODEBANC", "ABC")) # Output: "BANC"

6. Sliding Window Maximum

python

from collections import deque

def max_sliding_window(nums, k):


# Step 1: Initialization
deq = deque()
max_values = []

# Step 2: Expand the Window


for i in range(len(nums)):
# Remove indices that are out of the current window
if deq and deq[0] < i - k + 1:
deq.popleft()

# Remove indices whose values are less than the current number
while deq and nums[deq[-1]] < nums[i]:
deq.pop()

deq.append(i)

# Step 4: Collect max values after the first window is formed


if i >= k - 1:
max_values.append(nums[deq[0]])

# Step 5: Return Statement


return max_values

# Example Usage
print(max_sliding_window([1, 3, -1, -3, 5, 3, 6, 7], 3)) # Output: [3, 3, 5, 5, 6,
7]

Printed with ChatGPT to PDF 22/128


Summary
The Sliding Window technique is a powerful method for efficiently solving a variety of
problems related to substrings or subarrays. By maintaining a dynamic range that expands
and contracts based on conditions, it reduces the complexity and simplifies the
implementation. This approach can be applied effectively to problems like "Best Time to Buy
and Sell Stock," "Longest Substring Without Repeating Characters," "Longest Repeating
Character Replacement," "Permutation In String," "Minimum Window Substring," and
"Sliding Window Maximum."

Stack Template

Explanation

The Stack data structure is a linear data structure that follows the Last In First Out (LIFO)
principle. It is particularly useful for problems that require backtracking or maintaining an
order of operations. Stacks can be implemented using lists or linked lists and provide two
primary operations: push (to add an item) and pop (to remove the most recently added
item).

Key Advantages:

Backtracking: Stacks are great for solving problems where you need to revert to a
previous state, such as evaluating expressions or generating combinations.

Order Management: They help maintain the order of operations and can be used to
check for valid sequences (like parentheses).

Memory Efficiency: They can often solve problems in-place without requiring additional
space.

When to Use the Stack Technique

When dealing with nested structures (like parentheses).

When you need to reverse operations or backtrack through a process.

When evaluating expressions (like Reverse Polish Notation).

When calculating metrics based on preceding values (like daily temperatures).

Template
1. Initialization: Create an empty stack to hold elements as needed.

2. Processing: Iterate through the input (string, array, etc.):

Printed with ChatGPT to PDF 23/128


Use push to add elements to the stack when needed.

Use pop to remove elements from the stack based on specific conditions (like
matching parentheses).

Track additional information as necessary (like minimum values or counts).

3. Condition Checking: After processing each element, check conditions to determine if


further action is needed (like finalizing results or managing state).

4. Final Result: Return the desired output after processing all elements.

Application of the Template


The Stack technique can be applied to various problems, including:

Validating parentheses sequences.

Implementing a minimum stack.

Evaluating expressions in Reverse Polish Notation.

Generating combinations of parentheses.

Calculating the number of days until a warmer temperature.

Managing car fleets based on position and speed.

Finding the largest rectangle in a histogram.

Solving Problems Using the Stack Template

1. Valid Parentheses

python

def is_valid(s):
# Step 1: Initialization
stack = []
parentheses_map = {')': '(', '}': '{', ']': '['}

# Step 2: Processing
for char in s:
if char in parentheses_map.values(): # Opening parentheses
stack.append(char)
elif char in parentheses_map.keys(): # Closing parentheses
if not stack or stack.pop() != parentheses_map[char]:
return False

Printed with ChatGPT to PDF 24/128


# Step 5: Final Result
return not stack

# Example Usage
print(is_valid("()")) # Output: True
print(is_valid("()[]{}")) # Output: True
print(is_valid("(]")) # Output: False

2. Min Stack

python

class MinStack:

def __init__(self):
# Step 1: Initialization
self.stack = []
self.min_stack = []

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


# Step 2: Processing
self.stack.append(x)
if not self.min_stack or x <= self.min_stack[-1]:
self.min_stack.append(x)

def pop(self) -> None:


# Step 2: Processing
popped_value = self.stack.pop()
if popped_value == self.min_stack[-1]:
self.min_stack.pop()

def top(self) -> int:


# Step 5: Final Result
return self.stack[-1]

def get_min(self) -> int:


# Step 5: Final Result
return self.min_stack[-1]

# Example Usage
min_stack = MinStack()

Printed with ChatGPT to PDF 25/128


min_stack.push(-2)
min_stack.push(0)
min_stack.push(-3)
print(min_stack.get_min()) # Output: -3
min_stack.pop()
print(min_stack.top()) # Output: 0
print(min_stack.get_min()) # Output: -2

3. Evaluate Reverse Polish Notation

python

def eval_rpn(tokens):
# Step 1: Initialization
stack = []
operators = {"+", "-", "*", "/"}

# Step 2: Processing
for token in tokens:
if token in operators:
b = stack.pop()
a = stack.pop()
if token == '+':
stack.append(a + b)
elif token == '-':
stack.append(a - b)
elif token == '*':
stack.append(a * b)
elif token == '/':
stack.append(int(a / b)) # Use int() to mimic floor division
else:
stack.append(int(token))

# Step 5: Final Result


return stack[0]

# Example Usage
print(eval_rpn(["2", "1", "+", "3", "*"])) # Output: 9
print(eval_rpn(["4", "13", "5", "/", "+"])) # Output: 6

4. Generate Parentheses

python

Printed with ChatGPT to PDF 26/128


def generate_parentheses(n):
def backtrack(current, open_count, close_count):
# Step 2: Processing
if len(current) == 2 * n:
result.append(current)
return
if open_count < n:
backtrack(current + '(', open_count + 1, close_count)
if close_count < open_count:
backtrack(current + ')', open_count, close_count + 1)

# Step 1: Initialization
result = []
backtrack("", 0, 0)

# Step 5: Final Result


return result

# Example Usage
print(generate_parentheses(3)) # Output: ["((()))", "(()())", "(())()", "()(())", "
()()()"]

5. Daily Temperatures

python

def daily_temperatures(temperatures):
# Step 1: Initialization
stack = []
result = [0] * len(temperatures)

# Step 2: Processing
for i, temp in enumerate(temperatures):
while stack and temperatures[stack[-1]] < temp:
index = stack.pop()
result[index] = i - index
stack.append(i)

# Step 5: Final Result


return result

Printed with ChatGPT to PDF 27/128


# Example Usage
print(daily_temperatures([73, 74, 75, 71, 69, 72, 76, 73])) # Output: [1, 1, 4, 2,
1, 1, 0, 0]

6. Car Fleet

python

def car_fleet(target, position, speed):


# Step 1: Initialization
cars = sorted(zip(position, speed), reverse=True)
stack = []

# Step 2: Processing
for pos, spd in cars:
time_to_target = (target - pos) / spd
if not stack or time_to_target > stack[-1]:
stack.append(time_to_target)

# Step 5: Final Result


return len(stack)

# Example Usage
print(car_fleet(12, [10, 8, 0, 5, 3], [2, 4, 1, 1, 3])) # Output: 3

7. Largest Rectangle In Histogram

python

def largest_rectangle_area(heights):
# Step 1: Initialization
stack = []
max_area = 0
heights.append(0) # Sentinel to pop all remaining bars

# Step 2: Processing
for i, h in enumerate(heights):
while stack and heights[stack[-1]] > h:
height = heights[stack.pop()]
width = i if not stack else i - stack[-1] - 1
max_area = max(max_area, height * width)
stack.append(i)

Printed with ChatGPT to PDF 28/128


# Step 5: Final Result
return max_area

# Example Usage
print(largest_rectangle_area([2, 1, 5, 6, 2, 3])) # Output: 10

Summary
The Stack technique is essential for efficiently solving a variety of problems that require
maintaining order or backtracking through processes. By following the stack template, we
can tackle problems such as "Valid Parentheses," "Min Stack," "Evaluate Reverse Polish
Notation," "Generate Parentheses," "Daily Temperatures," "Car Fleet," and "Largest Rectangle
In Histogram" in a structured and efficient manner.

Binary Search Template

Explanation

Binary Search is a powerful technique used to find an element in a sorted array by


repeatedly dividing the search interval in half. If the value of the search key is less than the
item in the middle, the search continues in the lower half; otherwise, it continues in the
upper half. This approach reduces the time complexity from O(n) to O(log n) for finding an
element in sorted data, making it especially useful for large datasets.

Key Advantages:

Efficiency: Reduces the search space exponentially by eliminating half of the remaining
elements at each step.

Applicability: Useful for finding elements in sorted arrays, locating boundaries, and
optimizing complex conditions (like "Koko Eating Bananas").

When to Use the Binary Search Template

Classic Search: Looking for a target in a sorted array.

Boundary Problems: Finding the minimum or maximum satisfying a condition.

Rotated Arrays: Searching in rotated arrays using specific conditions.

Custom Functions: Solving optimization problems (like “Koko Eating Bananas”).

Binary Search Template

Printed with ChatGPT to PDF 29/128


1. Initialization: Define left and right boundaries of the search space. This can be an
array or range (based on the problem).

2. Condition: While left is less than or equal to right :

Calculate mid as the middle of left and right .

Check if the mid element or index meets the target condition.

3. Adjust Boundaries:

If the middle satisfies the condition but requires further optimization, adjust left
or right .

If it doesn’t satisfy, eliminate half of the search space by moving left or right
accordingly.

4. Result: Return the found index or value based on problem requirements.

Application of the Template


The Binary Search technique can be applied to the following problems:

Finding a target element (or checking its existence) in a sorted array.

Searching for values within a sorted 2D matrix.

Optimizing boundary-based conditions (like minimizing the speed at which Koko eats
bananas).

Locating minimum or maximum values in rotated arrays.

Solving Problems Using the Binary Search Template

1. Binary Search

python

def binary_search(nums, target):


left, right = 0, len(nums) - 1
while left <= right:
mid = left + (right - left) // 2
if nums[mid] == target:
return mid
elif nums[mid] < target:

Printed with ChatGPT to PDF 30/128


left = mid + 1
else:
right = mid - 1
return -1 # Target not found

# Example Usage
print(binary_search([1, 2, 3, 4, 5, 6], 4)) # Output: 3

2. Search a 2D Matrix

python

def search_matrix(matrix, target):


if not matrix or not matrix[0]:
return False
rows, cols = len(matrix), len(matrix[0])
left, right = 0, rows * cols - 1

while left <= right:


mid = left + (right - left) // 2
mid_element = matrix[mid // cols][mid % cols]
if mid_element == target:
return True
elif mid_element < target:
left = mid + 1
else:
right = mid - 1
return False

# Example Usage
print(search_matrix([[1, 3, 5], [7, 10, 12], [14, 16, 20]], 10)) # Output: True

3. Koko Eating Bananas

python

def min_eating_speed(piles, h):


left, right = 1, max(piles)

def can_finish(speed):
hours = 0
for pile in piles:
hours += (pile + speed - 1) // speed

Printed with ChatGPT to PDF 31/128


return hours <= h

while left < right:


mid = left + (right - left) // 2
if can_finish(mid):
right = mid
else:
left = mid + 1
return left

# Example Usage
print(min_eating_speed([3, 6, 7, 11], 8)) # Output: 4

4. Find Minimum in Rotated Sorted Array

python

def find_min(nums):
left, right = 0, len(nums) - 1
while left < right:
mid = left + (right - left) // 2
if nums[mid] > nums[right]: # Minimum is to the right
left = mid + 1
else:
right = mid
return nums[left]

# Example Usage
print(find_min([3, 4, 5, 1, 2])) # Output: 1

5. Search in Rotated Sorted Array

python

def search(nums, target):


left, right = 0, len(nums) - 1
while left <= right:
mid = left + (right - left) // 2
if nums[mid] == target:
return mid
if nums[left] <= nums[mid]: # Left portion is sorted
if nums[left] <= target < nums[mid]:
right = mid - 1

Printed with ChatGPT to PDF 32/128


else:
left = mid + 1
else: # Right portion is sorted
if nums[mid] < target <= nums[right]:
left = mid + 1
else:
right = mid - 1
return -1

# Example Usage
print(search([4,5,6,7,0,1,2], 0)) # Output: 4

6. Time-Based Key-Value Store (Using Binary Search)

python

from collections import defaultdict

class TimeMap:
def __init__(self):
self.store = defaultdict(list)

def set(self, key, value, timestamp):


self.store[key].append((timestamp, value))

def get(self, key, timestamp):


values = self.store[key]
left, right = 0, len(values) - 1
result = ""

while left <= right:


mid = left + (right - left) // 2
if values[mid][0] <= timestamp:
result = values[mid][1]
left = mid + 1
else:
right = mid - 1
return result

# Example Usage
time_map = TimeMap()
time_map.set("foo", "bar", 1)

Printed with ChatGPT to PDF 33/128


print(time_map.get("foo", 1)) # Output: "bar"
print(time_map.get("foo", 3)) # Output: "bar"

7. Median of Two Sorted Arrays

python

def find_median_sorted_arrays(nums1, nums2):


if len(nums1) > len(nums2):
nums1, nums2 = nums2, nums1
x, y = len(nums1), len(nums2)
left, right = 0, x

while left <= right:


partition_x = (left + right) // 2
partition_y = (x + y + 1) // 2 - partition_x

max_x = float('-inf') if partition_x == 0 else nums1[partition_x - 1]


min_x = float('inf') if partition_x == x else nums1[partition_x]

max_y = float('-inf') if partition_y == 0 else nums2[partition_y - 1]


min_y = float('inf') if partition_y == y else nums2[partition_y]

if max_x <= min_y and max_y <= min_x:


if (x + y) % 2 == 0:
return (max(max_x, max_y) + min(min_x, min_y)) / 2
else:
return max(max_x, max_y)
elif max_x > min_y:
right = partition_x - 1
else:
left = partition_x + 1

# Example Usage
print(find_median_sorted_arrays([1, 3], [2])) # Output: 2.0
print(find_median_sorted_arrays([1, 2], [3, 4])) # Output: 2.5

Summary
The Binary Search technique is essential for optimizing searches and finding target values or
boundaries efficiently in sorted data. By following the binary search template, we can
address a range of problems, such as "Binary Search," "Search a 2D Matrix," "Koko Eating

Printed with ChatGPT to PDF 34/128


Bananas," "Find Minimum in Rotated Sorted Array," "Search in Rotated Sorted Array," "Time-
Based Key-Value Store," and "Median of Two Sorted Arrays." Each of these problems
leverages binary search’s efficiency to optimize operations and make solutions more
scalable.

Linked List Template

Explanation

The Linked List is a fundamental data structure where elements (nodes) are connected in a
sequence, with each node containing a reference to the next node in the list. Linked lists are
particularly useful when the operations involve frequent insertions and deletions, especially
at the start or end of the list. In many problems, linked lists offer an efficient solution due to
their dynamic size and structure, which eliminates the need to shift elements (as in arrays).

Key Techniques in Linked List Manipulation


1. Dummy Nodes: Useful to handle edge cases, especially for operations at the head of the
list.

2. Two-Pointer Approach: Often used to find elements in the list from the end (like in
"Remove Nth Node from End").

3. Reverse Traversal: Useful for reversing the list or segments of the list.

4. Cycle Detection (Fast and Slow Pointers): Used to detect cycles in the list.

5. Recursion: Commonly used for problems involving reversing lists, merging sorted lists,
and adding numbers (similar to recursion on trees).

When to Use the Linked List Template


Reversal: Problems involving reversal of the entire list or specific parts (like "Reverse
Linked List" and "Reverse Nodes in K Group").

Merging: Combining sorted lists efficiently (as in "Merge Two Sorted Lists" and "Merge K
Sorted Lists").

Position-Based Removal/Insertion: For example, removing the nth node from the end,
which is easier with the two-pointer technique.

Cycle Detection: Detecting cycles in lists with the fast-slow pointer technique.

Pointer Manipulation: Shifting pointers to rearrange or reorder nodes.

Printed with ChatGPT to PDF 35/128


Linked List Template
1. Initialize Pointers: Commonly, prev , current , and next pointers are used. dummy
nodes help to handle head-related edge cases.

2. Iterate Over Nodes:

Adjust pointers based on the operation (e.g., reverse, reorder).

Use two pointers if locating nodes relative to the end of the list.

3. Special Operations:

Reverse pointers if required (like for reversal problems).

Use conditional checks for cycle detection or reordering.

4. Return Result: The list head or modified list segment.

Application of the Template


The Linked List template can be applied to:

Reversing linked lists.

Merging sorted lists.

Removing nodes from specific positions.

Detecting cycles.

Reordering nodes based on conditions.

Solving Problems Using the Linked List Template

1. Reverse Linked List

python

class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next

def reverse_list(head):
prev, current = None, head

Printed with ChatGPT to PDF 36/128


while current:
next_node = current.next
current.next = prev
prev = current
current = next_node
return prev

# Example Usage
head = ListNode(1, ListNode(2, ListNode(3, ListNode(4))))
reversed_head = reverse_list(head)

2. Merge Two Sorted Lists

python

def merge_two_lists(l1, l2):


dummy = ListNode()
tail = dummy

while l1 and l2:


if l1.val < l2.val:
tail.next, l1 = l1, l1.next
else:
tail.next, l2 = l2, l2.next
tail = tail.next

tail.next = l1 or l2
return dummy.next

# Example Usage
l1 = ListNode(1, ListNode(3, ListNode(5)))
l2 = ListNode(2, ListNode(4, ListNode(6)))
merged_head = merge_two_lists(l1, l2)

3. Reorder List

python

def reorder_list(head):
if not head:
return

# Step 1: Find the middle of the list

Printed with ChatGPT to PDF 37/128


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

# Step 2: Reverse the second half


prev, current = None, slow
while current:
next_node = current.next
current.next = prev
prev = current
current = next_node

# Step 3: Merge the two halves


first, second = head, prev
while second.next:
first.next, first = second, first.next
second.next, second = first, second.next

# Example Usage
head = ListNode(1, ListNode(2, ListNode(3, ListNode(4))))
reorder_list(head)

4. Remove Nth Node From End of List

python

def remove_nth_from_end(head, n):


dummy = ListNode(0, head)
slow, fast = dummy, head

for _ in range(n):
fast = fast.next

while fast:
slow, fast = slow.next, fast.next

slow.next = slow.next.next
return dummy.next

# Example Usage

Printed with ChatGPT to PDF 38/128


head = ListNode(1, ListNode(2, ListNode(3, ListNode(4, ListNode(5)))))
new_head = remove_nth_from_end(head, 2)

5. Copy List with Random Pointer

python

class Node:
def __init__(self, val=0, next=None, random=None):
self.val = val
self.next = next
self.random = random

def copy_random_list(head):
if not head:
return None

# Step 1: Insert copied nodes after each original node


current = head
while current:
next_node = current.next
copy_node = Node(current.val, next_node)
current.next = copy_node
current = next_node

# Step 2: Assign random pointers


current = head
while current:
if current.random:
current.next.random = current.random.next
current = current.next.next

# Step 3: Restore original list and extract copied list


current = head
copy_head = head.next
while current:
copy = current.next
current.next = copy.next
current = current.next
if copy.next:
copy.next = copy.next.next
return copy_head

Printed with ChatGPT to PDF 39/128


# Example Usage
# This example would need to create nodes and random pointers accordingly.

6. Linked List Cycle

python

def has_cycle(head):
slow, fast = head, head
while fast and fast.next:
slow = slow.next
fast = fast.next.next
if slow == fast:
return True
return False

# Example Usage
head = ListNode(1, ListNode(2, ListNode(3, ListNode(4))))
head.next.next.next.next = head.next # Creates a cycle
print(has_cycle(head)) # Output: True

7. LRU Cache

python

from collections import OrderedDict

class LRUCache:
def __init__(self, capacity: int):
self.cache = OrderedDict()
self.capacity = capacity

def get(self, key: int) -> int:


if key not in self.cache:
return -1
self.cache.move_to_end(key)
return self.cache[key]

def put(self, key: int, value: int) -> None:


if key in self.cache:
self.cache.move_to_end(key)
self.cache[key] = value

Printed with ChatGPT to PDF 40/128


if len(self.cache) > self.capacity:
self.cache.popitem(last=False)

# Example Usage
lru = LRUCache(2)
lru.put(1, 1)
lru.put(2, 2)
print(lru.get(1)) # Output: 1
lru.put(3, 3)
print(lru.get(2)) # Output: -1

Summary
The Linked List Template provides a structured way to handle problems that involve pointer
manipulation, traversals, and node reordering. By applying specific techniques like dummy
nodes, two-pointers, and fast-slow pointers, we can efficiently solve various linked list
problems. Problems like "Reverse Linked List," "Merge Two Sorted Lists," "Remove Nth Node
From End of List," "Linked List Cycle," and "LRU Cache" benefit greatly from this approach,
each demonstrating the template’s flexibility in handling a wide range of linked list-related
challenges.

Template for Solving Tree Problems


Tree problems generally involve recursive or iterative approaches to traverse nodes, analyze
properties, and manipulate the tree structure. Trees, specifically binary trees and binary
search trees, are widely used in algorithms due to their hierarchical structure, which allows
efficient search, insertion, and deletion operations.

Key Techniques in Tree Problems


1. Recursion: A common approach to traverse and manipulate nodes at each level or
depth, suitable for operations that need depth-first traversal.

2. Depth-First Search (DFS): DFS uses recursion or a stack to visit nodes down to the leaves
before backtracking. Ideal for calculating depth, checking tree properties, or locating
specific nodes.

Printed with ChatGPT to PDF 41/128


3. Breadth-First Search (BFS): BFS uses a queue to visit nodes level by level, suitable for
level-based traversals.

4. Backtracking: Useful in problems where we need to explore possible paths or


reconfigure parts of the tree (like finding paths with constraints).

5. Binary Search Properties: In a Binary Search Tree (BST), nodes on the left subtree are
smaller, and nodes on the right subtree are larger, enabling efficient search, min/max,
and LCA problems.

When to Use the Tree Template


Traversal Problems: Problems that need to visit all nodes in a specific order (e.g.,
Inorder, Preorder, Postorder).

Property Checking: Problems that check for a property across the tree (e.g., balanced
tree, same tree).

Path Calculations: Problems involving summing values or finding maximum depths or


paths.

BST Operations: Problems on Binary Search Trees where nodes have specific ordering
(e.g., finding kth smallest element).

Tree Template
1. Base Condition: Start with checking if the node is None .

2. Recursive Function Calls:

Implement dfs(node) for depth-first traversal, with appropriate actions.

Use bfs() for level order traversal.

3. Return Values or Aggregation:

Aggregate values, like sum or depth, based on subtree results.

Backtrack results when calculating specific paths or properties.

4. Final Output: The result of the aggregated values or the specific result derived from
traversing the tree.

Solving Problems Using the Tree Template

1. Invert Binary Tree

python

Printed with ChatGPT to PDF 42/128


class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right

def invert_tree(root):
if not root:
return None
root.left, root.right = invert_tree(root.right), invert_tree(root.left)
return root

# Example Usage
root = TreeNode(1, TreeNode(2), TreeNode(3))
inverted_root = invert_tree(root)

2. Maximum Depth of Binary Tree

python

def max_depth(root):
if not root:
return 0
left_depth = max_depth(root.left)
right_depth = max_depth(root.right)
return max(left_depth, right_depth) + 1

# Example Usage
root = TreeNode(1, TreeNode(2), TreeNode(3))
depth = max_depth(root)

3. Diameter of Binary Tree

python

def diameter_of_binary_tree(root):
diameter = 0

def dfs(node):
nonlocal diameter
if not node:
return 0

Printed with ChatGPT to PDF 43/128


left = dfs(node.left)
right = dfs(node.right)
diameter = max(diameter, left + right)
return max(left, right) + 1

dfs(root)
return diameter

# Example Usage
root = TreeNode(1, TreeNode(2, TreeNode(4)), TreeNode(3))
diameter = diameter_of_binary_tree(root)

4. Balanced Binary Tree

python

def is_balanced(root):
def check(node):
if not node:
return 0
left = check(node.left)
right = check(node.right)
if left == -1 or right == -1 or abs(left - right) > 1:
return -1
return max(left, right) + 1

return check(root) != -1

# Example Usage
root = TreeNode(1, TreeNode(2, TreeNode(3)), TreeNode(4))
balanced = is_balanced(root)

5. Lowest Common Ancestor of a Binary Search Tree

python

def lowest_common_ancestor(root, p, q):


while root:
if p.val < root.val and q.val < root.val:
root = root.left
elif p.val > root.val and q.val > root.val:
root = root.right
else:

Printed with ChatGPT to PDF 44/128


return root

# Example Usage
root = TreeNode(6, TreeNode(2, TreeNode(0), TreeNode(4)), TreeNode(8, TreeNode(7),
TreeNode(9)))
p, q = root.left, root.right # Nodes with values 2 and 8
ancestor = lowest_common_ancestor(root, p, q)

6. Binary Tree Level Order Traversal

python

from collections import deque

def level_order(root):
levels = []
if not root:
return levels

queue = deque([root])
while queue:
level = []
for _ in range(len(queue)):
node = queue.popleft()
level.append(node.val)
if node.left:
queue.append(node.left)
if node.right:
queue.append(node.right)
levels.append(level)
return levels

# Example Usage
root = TreeNode(1, TreeNode(2), TreeNode(3))
levels = level_order(root)

7. Kth Smallest Element in a BST

python

def kth_smallest(root, k):


stack = []
while True:

Printed with ChatGPT to PDF 45/128


while root:
stack.append(root)
root = root.left
root = stack.pop()
k -= 1
if k == 0:
return root.val
root = root.right

# Example Usage
root = TreeNode(3, TreeNode(1, None, TreeNode(2)), TreeNode(4))
kth_element = kth_smallest(root, 1)

8. Serialize and Deserialize Binary Tree

python

class Codec:
def serialize(self, root):
def dfs(node):
if not node:
vals.append('#')
else:
vals.append(str(node.val))
dfs(node.left)
dfs(node.right)

vals = []
dfs(root)
return ' '.join(vals)

def deserialize(self, data):


def dfs():
val = next(vals)
if val == '#':
return None
node = TreeNode(int(val))
node.left = dfs()
node.right = dfs()
return node

vals = iter(data.split())

Printed with ChatGPT to PDF 46/128


return dfs()

# Example Usage
codec = Codec()
root = TreeNode(1, TreeNode(2), TreeNode(3))
serialized = codec.serialize(root)
deserialized_root = codec.deserialize(serialized)

Summary
The Tree Template provides a systematic approach for solving problems on binary trees,
such as traversals, finding depths, checking properties, and transforming the tree structure.
By leveraging recursive traversal functions, BFS, and DFS, we can efficiently solve problems
like inverting a binary tree, finding the maximum depth, checking if a tree is balanced, and
finding the kth smallest element in a BST. This template’s versatility helps to break down
complex tree-based problems into manageable, structured steps, making it easier to reason
about and solve each problem effectively.

Template for Solving Additional Tree Problems


These problems can also be tackled using the tree traversal template by following recursive
or iterative approaches based on depth-first or breadth-first traversal. Here's a review of
additional techniques used in these problems:

1. Recursive DFS (Preorder, Inorder, Postorder):

Useful for node comparisons (like in "Same Tree"), path calculations, and
reconstructing trees.

Helps in validating tree properties by comparing nodes recursively (e.g., for


"Validate Binary Search Tree").

2. BFS (Level Order):

Especially helpful for problems needing level-by-level access (e.g., "Binary Tree Right
Side View").

A queue can be used to handle level-specific requirements like only showing the
rightmost node per level.

3. Backtracking for Path Tracking:

Printed with ChatGPT to PDF 47/128


Necessary for counting nodes that meet certain conditions across paths, as in
"Count Good Nodes in Binary Tree."

Tree Problem Template Outline


This template integrates these strategies:

1. Check Base Condition: If root is None , return immediately or set a default result.

2. Traversal (DFS or BFS):

For DFS, recursively visit left and right children.

For BFS, use a queue to handle nodes in level order.

3. Comparison or Aggregation Logic:

Use recursive or iterative structures to calculate or compare values at each node.

4. Return Results: Aggregate the results, either by summing, counting, or returning


computed values (like node counts or path sums).

Problem Applications Using the Template

1. Same Tree

python

def is_same_tree(p, q):


if not p and not q:
return True
if not p or not q or p.val != q.val:
return False
return is_same_tree(p.left, q.left) and is_same_tree(p.right, q.right)

# Example Usage
root1 = TreeNode(1, TreeNode(2), TreeNode(3))
root2 = TreeNode(1, TreeNode(2), TreeNode(3))
result = is_same_tree(root1, root2)

2. Subtree of Another Tree

python

def is_subtree(root, sub_root):


def is_same(p, q):

Printed with ChatGPT to PDF 48/128


if not p and not q:
return True
if not p or not q or p.val != q.val:
return False
return is_same(p.left, q.left) and is_same(p.right, q.right)

if not root:
return False
if is_same(root, sub_root):
return True
return is_subtree(root.left, sub_root) or is_subtree(root.right, sub_root)

# Example Usage
root = TreeNode(3, TreeNode(4, TreeNode(1), TreeNode(2)), TreeNode(5))
sub_root = TreeNode(4, TreeNode(1), TreeNode(2))
result = is_subtree(root, sub_root)

3. Binary Tree Right Side View

python

from collections import deque

def right_side_view(root):
if not root:
return []
view = []
queue = deque([root])
while queue:
level_length = len(queue)
for i in range(level_length):
node = queue.popleft()
if i == level_length - 1:
view.append(node.val)
if node.left:
queue.append(node.left)
if node.right:
queue.append(node.right)
return view

# Example Usage

Printed with ChatGPT to PDF 49/128


root = TreeNode(1, TreeNode(2, None, TreeNode(5)), TreeNode(3, None, TreeNode(4)))
result = right_side_view(root)

4. Count Good Nodes in Binary Tree

python

def good_nodes(root):
def dfs(node, max_val):
if not node:
return 0
total = 1 if node.val >= max_val else 0
max_val = max(max_val, node.val)
total += dfs(node.left, max_val)
total += dfs(node.right, max_val)
return total

return dfs(root, root.val)

# Example Usage
root = TreeNode(3, TreeNode(1, TreeNode(3)), TreeNode(4, TreeNode(1), TreeNode(5)))
result = good_nodes(root)

5. Validate Binary Search Tree

python

def is_valid_bst(root):
def dfs(node, lower=float('-inf'), upper=float('inf')):
if not node:
return True
if not (lower < node.val < upper):
return False
return dfs(node.left, lower, node.val) and dfs(node.right, node.val, upper)

return dfs(root)

# Example Usage
root = TreeNode(2, TreeNode(1), TreeNode(3))
result = is_valid_bst(root)

6. Construct Binary Tree from Preorder and Inorder Traversal

Printed with ChatGPT to PDF 50/128


python

def build_tree(preorder, inorder):


if not preorder or not inorder:
return None
root_val = preorder.pop(0)
root = TreeNode(root_val)
idx = inorder.index(root_val)
root.left = build_tree(preorder, inorder[:idx])
root.right = build_tree(preorder, inorder[idx+1:])
return root

# Example Usage
preorder = [3, 9, 20, 15, 7]
inorder = [9, 3, 15, 20, 7]
tree_root = build_tree(preorder, inorder)

7. Binary Tree Maximum Path Sum

python

def max_path_sum(root):
max_sum = float('-inf')

def dfs(node):
nonlocal max_sum
if not node:
return 0
left_gain = max(dfs(node.left), 0)
right_gain = max(dfs(node.right), 0)
max_sum = max(max_sum, node.val + left_gain + right_gain)
return node.val + max(left_gain, right_gain)

dfs(root)
return max_sum

# Example Usage
root = TreeNode(-10, TreeNode(9), TreeNode(20, TreeNode(15), TreeNode(7)))
result = max_path_sum(root)

Printed with ChatGPT to PDF 51/128


Summary
This template provides a structured approach to solve tree-based problems, utilizing DFS and
BFS traversals effectively. Recursive calls with conditions help ensure depth-first traversal for
path-specific calculations, while BFS is useful for level-based requirements like the right-side
view. By combining traversal with conditional checks, aggregation, and the appropriate
traversal technique, we can handle a variety of tree problems efficiently.

Template for Heap/Priority Queue-Based Problems


Heap (or Priority Queue) problems are characterized by needing quick access to the largest
or smallest elements in a dynamic data set. Problems that involve finding the "k-th largest"
or "k-th smallest," maintaining order in a sequence, or managing the "most frequent" tasks
are all good candidates for heap-based solutions.

Why Heaps Are Useful for These Problems:

1. Efficient Min/Max Access: Heaps allow O(log n) insertion and removal times, making
them ideal for keeping track of the largest or smallest elements as data changes.

2. Dynamic Element Ordering: For problems that continuously update and retrieve
elements based on their ordering (like tracking a median or kth largest), heaps allow
dynamic updates while maintaining the necessary order.

3. Space Optimization: When only the k largest or smallest elements are needed, we can
maintain a heap of fixed size k, saving memory compared to storing the entire dataset.

Template Outline

1. Choose the Heap Type:

Min-Heap: Good for tracking the largest k elements (use a min-heap of size k, where
the root is the smallest among the k largest).

Max-Heap: Suitable for finding the smallest k elements (can invert values to use a
max-heap as a min-heap).

2. Initialize the Heap:

For continuous input, initialize an empty heap and maintain it with the required
elements.

For a fixed set of data, initialize the heap with the first k elements.

3. Heap Operations (Push/Pop):

Printed with ChatGPT to PDF 52/128


Insert elements using heapq.heappush() (to add a new element) or
heapq.heappop() (to remove the root).

If maintaining only the top k elements, pop the root if the size exceeds k.

4. Return or Track the Result:

Access the root for the k-th largest or smallest element.

Aggregate results or transform the heap into a sorted list if needed.

Applications of the Template


Let's apply this template to specific problems:

1. Kth Largest Element in an Array

python

import heapq

def find_kth_largest(nums, k):


min_heap = nums[:k]
heapq.heapify(min_heap) # Step 2: Initialize the heap with first k elements

for num in nums[k:]: # Step 3: Maintain the min-heap with only k largest
elements
if num > min_heap[0]:
heapq.heappop(min_heap)
heapq.heappush(min_heap, num)

return min_heap[0] # Step 4: The root of the heap is the k-th largest

# Example Usage
nums = [3, 2, 1, 5, 6, 4]
k = 2
result = find_kth_largest(nums, k)

2. Last Stone Weight

python

import heapq

def last_stone_weight(stones):

Printed with ChatGPT to PDF 53/128


max_heap = [-stone for stone in stones]
heapq.heapify(max_heap) # Step 2: Max-heap by negating values

while len(max_heap) > 1: # Step 3: Keep smashing the two heaviest stones
first = -heapq.heappop(max_heap)
second = -heapq.heappop(max_heap)
if first != second:
heapq.heappush(max_heap, -(first - second))

return -max_heap[0] if max_heap else 0 # Step 4: Return the last remaining


stone

# Example Usage
stones = [2, 7, 4, 1, 8, 1]
result = last_stone_weight(stones)

3. K Closest Points to Origin

python

import heapq
import math

def k_closest(points, k):


max_heap = []

for x, y in points:
dist = -(x**2 + y**2) # Step 2: Calculate negative distance for max-heap
if len(max_heap) < k:
heapq.heappush(max_heap, (dist, (x, y)))
else:
heapq.heappushpop(max_heap, (dist, (x, y))) # Step 3: Keep top k
closest

return [point for (_, point) in max_heap] # Step 4: Return k closest points

# Example Usage
points = [[1,3],[-2,2],[5,8],[0,1]]
k = 2
result = k_closest(points, k)

4. Task Scheduler

Printed with ChatGPT to PDF 54/128


python

import heapq
from collections import Counter, deque

def least_interval(tasks, n):


task_counts = Counter(tasks)
max_heap = [-count for count in task_counts.values()]
heapq.heapify(max_heap) # Step 2: Max-heap for task frequencies
time = 0
queue = deque() # To hold tasks cooling down

while max_heap or queue:


time += 1
if max_heap:
count = 1 + heapq.heappop(max_heap) # Step 3: Process most frequent
task
if count:
queue.append((count, time + n))

if queue and queue[0][1] == time:


heapq.heappush(max_heap, queue.popleft()[0])

return time # Step 4: Total time to complete tasks with cooldowns

# Example Usage
tasks = ["A", "A", "A", "B", "B", "B"]
n = 2
result = least_interval(tasks, n)

5. Find Median from Data Stream

python

import heapq

class MedianFinder:
def __init__(self):
self.small = [] # Max-heap (inverted min-heap)
self.large = [] # Min-heap

def add_num(self, num):

Printed with ChatGPT to PDF 55/128


heapq.heappush(self.small, -num)
if self.small and self.large and (-self.small[0] > self.large[0]):
heapq.heappush(self.large, -heapq.heappop(self.small))

if len(self.small) > len(self.large) + 1:


heapq.heappush(self.large, -heapq.heappop(self.small))
if len(self.large) > len(self.small):
heapq.heappush(self.small, -heapq.heappop(self.large))

def find_median(self):
if len(self.small) > len(self.large):
return -self.small[0]
return (-self.small[0] + self.large[0]) / 2

# Example Usage
median_finder = MedianFinder()
median_finder.add_num(1)
median_finder.add_num(2)
print(median_finder.find_median()) # Output: 1.5
median_finder.add_num(3)
print(median_finder.find_median()) # Output: 2

Summary
This template demonstrates that by structuring problems around a min-heap or max-heap
and maintaining only the relevant elements in the heap, we can efficiently solve a wide
variety of problems. The heap-based approach is versatile for dynamically ordered data,
especially when quick access to maximum or minimum elements is essential.

Template for Backtracking Problems


Backtracking problems typically involve exploring all possible combinations, permutations,
or paths in a search space. They are characterized by a need to "build up" a solution
incrementally and "backtrack" when a decision does not lead to a valid solution. This
template is particularly useful in problems like subsets, permutations, and combination
sums, where all possibilities need to be explored in a structured and efficient way.

Why Backtracking Is Useful for These Problems:

Printed with ChatGPT to PDF 56/128


1. Exploring Combinatorial Search Spaces: Backtracking helps navigate through complex
search spaces without visiting all states unnecessarily.

2. Recursive Exploration with Pruning: Each recursive call explores one path, and invalid
paths are abandoned as soon as they are identified, saving computational resources.

3. Incremental Solution Building: Elements are added to the solution one by one, and if an
element doesn’t lead to a valid solution, it is removed (backtracking).

Template Outline

1. Define the Recursive Backtracking Function:

Create a helper function to handle recursion. This function should accept arguments
representing the current state of the solution and the point in the search space to
be explored.

2. Define the Base Case:

Specify the base condition to indicate when a solution is complete (e.g., a subset is
formed, all elements are used).

3. Make a Choice and Recurse:

For each recursive step, add an element to the current solution, make the recursive
call, then remove the element to backtrack.

4. Prune Invalid Paths (optional):

Use conditions to avoid exploring unnecessary or duplicate paths, especially when


elements must meet specific criteria.

5. Return or Aggregate Results:

Store the results generated by each valid path in a results list, which can be returned
or processed at the end.

Applications of the Template


Let’s apply this template to specific problems.

1. Subsets

python

def subsets(nums):
result = []

Printed with ChatGPT to PDF 57/128


def backtrack(start, path):
result.append(path[:]) # Step 2: Add the current subset to results

for i in range(start, len(nums)): # Step 3: Explore each element starting


from `start`
path.append(nums[i]) # Choose
backtrack(i + 1, path) # Recurse with the next index
path.pop() # Un-choose (backtrack)

backtrack(0, [])
return result

# Example Usage
nums = [1, 2, 3]
print(subsets(nums))

2. Combination Sum

python

def combination_sum(candidates, target):


result = []

def backtrack(start, path, remaining):


if remaining == 0: # Step 2: Found a valid combination
result.append(path[:])
return
elif remaining < 0:
return

for i in range(start, len(candidates)):


path.append(candidates[i]) # Choose
backtrack(i, path, remaining - candidates[i]) # Recurse with updated
target
path.pop() # Un-choose (backtrack)

backtrack(0, [], target)


return result

# Example Usage
candidates = [2, 3, 6, 7]

Printed with ChatGPT to PDF 58/128


target = 7
print(combination_sum(candidates, target))

3. Permutations

python

def permute(nums):
result = []

def backtrack(path, remaining):


if not remaining: # Step 2: No elements left to permute
result.append(path[:])
return

for i in range(len(remaining)):
path.append(remaining[i]) # Choose
backtrack(path, remaining[:i] + remaining[i+1:]) # Recurse with
remaining elements
path.pop() # Un-choose (backtrack)

backtrack([], nums)
return result

# Example Usage
nums = [1, 2, 3]
print(permute(nums))

4. N Queens

python

def solve_n_queens(n):
result = []

def is_valid(board, row, col):


for i in range(row):
if board[i] == col or abs(board[i] - col) == row - i:
return False
return True

def backtrack(board, row):


if row == n: # Step 2: All queens placed

Printed with ChatGPT to PDF 59/128


result.append(["." * col + "Q" + "." * (n - col - 1) for col in board])
return

for col in range(n):


if is_valid(board, row, col): # Check if the position is valid
board[row] = col # Choose
backtrack(board, row + 1) # Recurse to place the next queen
board[row] = -1 # Un-choose (backtrack)

backtrack([-1] * n, 0)
return result

# Example Usage
n = 4
print(solve_n_queens(n))

5. Palindrome Partitioning

python

def partition(s):
result = []

def is_palindrome(substring):
return substring == substring[::-1]

def backtrack(start, path):


if start == len(s): # Step 2: All characters used
result.append(path[:])
return

for end in range(start + 1, len(s) + 1):


if is_palindrome(s[start:end]):
path.append(s[start:end]) # Choose
backtrack(end, path) # Recurse with the next substring
path.pop() # Un-choose (backtrack)

backtrack(0, [])
return result

# Example Usage

Printed with ChatGPT to PDF 60/128


s = "aab"
print(partition(s))

Summary
This backtracking template allows for systematically exploring the search space of potential
solutions by incrementally building and backtracking paths based on constraints. It’s a
versatile approach that can be applied to a wide range of problems, including generating all
subsets, finding unique combinations, placing queens on a chessboard, and partitioning
palindromic substrings. The key to efficiently solving these problems lies in recursive
exploration with well-defined choices, conditions for pruning, and tracking paths that meet
the required criteria.

Alright! Let’s continue applying the backtracking template to solve additional problems in the
list. Here’s a breakdown of each problem, the approach used, and the code solution.

6. Combination Sum II
Combination Sum II is similar to Combination Sum, but each number can only be used once,
and the candidate list contains duplicates, so we need to avoid duplicate combinations.

python

def combination_sum2(candidates, target):


candidates.sort() # Sort to facilitate duplicate handling
result = []

def backtrack(start, path, remaining):


if remaining == 0: # Valid combination found
result.append(path[:])
return
elif remaining < 0:
return

for i in range(start, len(candidates)):

Printed with ChatGPT to PDF 61/128


if i > start and candidates[i] == candidates[i - 1]: # Skip duplicates
continue
path.append(candidates[i]) # Choose
backtrack(i + 1, path, remaining - candidates[i]) # Move to the next
index
path.pop() # Backtrack

backtrack(0, [], target)


return result

# Example Usage
candidates = [10, 1, 2, 7, 6, 1, 5]
target = 8
print(combination_sum2(candidates, target))

7. Word Search
In Word Search, we try to find a word in a grid by moving horizontally or vertically. This
requires a DFS search with backtracking to try each cell and track visited positions.

python

def exist(board, word):


rows, cols = len(board), len(board[0])

def backtrack(r, c, idx):


if idx == len(word): # All characters matched
return True
if r < 0 or r >= rows or c < 0 or c >= cols or board[r][c] != word[idx]:
return False

# Mark as visited
temp = board[r][c]
board[r][c] = '#'

# Explore all directions


found = (backtrack(r + 1, c, idx + 1) or
backtrack(r - 1, c, idx + 1) or
backtrack(r, c + 1, idx + 1) or

Printed with ChatGPT to PDF 62/128


backtrack(r, c - 1, idx + 1))

board[r][c] = temp # Un-mark as visited


return found

for r in range(rows):
for c in range(cols):
if board[r][c] == word[0] and backtrack(r, c, 0): # Start DFS from
matching character
return True
return False

# Example Usage
board = [["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]]
word = "ABCCED"
print(exist(board, word))

8. Letter Combinations of a Phone Number


This problem involves generating combinations based on a mapping of digits to letters
(similar to a phone keypad).

python

def letter_combinations(digits):
if not digits:
return []

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

def backtrack(index, path):


if index == len(digits): # Full combination formed
result.append("".join(path))
return

Printed with ChatGPT to PDF 63/128


for letter in phone_map[digits[index]]:
path.append(letter) # Choose a letter
backtrack(index + 1, path) # Recurse for the next digit
path.pop() # Backtrack

backtrack(0, [])
return result

# Example Usage
digits = "23"
print(letter_combinations(digits))

9. N-Queens
The classic N-Queens problem where we place N queens on an N×N chessboard so that no
two queens threaten each other. We utilize the backtracking template to place queens row by
row.

python

def solve_n_queens(n):
result = []
board = [["."] * n for _ in range(n)]

def is_valid(r, c):


# Check column and diagonals
for i in range(r):
if board[i][c] == "Q" or \
(c - (r - i) >= 0 and board[i][c - (r - i)] == "Q") or \
(c + (r - i) < n and board[i][c + (r - i)] == "Q"):
return False
return True

def backtrack(row):
if row == n: # All queens placed
solution = ["".join(row) for row in board]
result.append(solution)
return

Printed with ChatGPT to PDF 64/128


for col in range(n):
if is_valid(row, col):
board[row][col] = "Q" # Place queen
backtrack(row + 1) # Recurse to place next queen
board[row][col] = "." # Remove queen (backtrack)

backtrack(0)
return result

# Example Usage
n = 4
print(solve_n_queens(n))

Each of these examples follows a similar backtracking pattern:

A base case that checks if a solution is reached.

Recursive calls to explore possible decisions.

Backtracking steps that "undo" the last decision if it does not yield a complete solution.

Backtracking offers a powerful approach to tackle complex problems where multiple


solutions are possible, such as finding all combinations, subsets, and permutations. This
template consistently leverages these steps to solve each problem efficiently.

To tackle problems related to Tries (Prefix Trees), let's build a template that will work
effectively for each of these scenarios. Tries are particularly useful for problems involving
prefix-based search and efficient storage of words or sequences.

Trie Template Explanation

Why This Template is Useful

Tries are structured to allow quick prefix-based operations, such as checking for the
presence of words, prefixes, or even building and traversing trees in character-specific

Printed with ChatGPT to PDF 65/128


nodes. They’re very space-efficient for storing strings with shared prefixes, making them
ideal for dictionary or search applications.

Trie Template Design


The basic structure of a Trie includes:

1. Nodes with children: Each node represents a character and points to potential child
nodes (next characters in words).

2. End markers: Mark the end of a valid word so we know if a path represents a complete
word.

Core Trie Operations

1. Insert: Traverse the trie, creating nodes for each character of the word. Mark the last
node as the end of the word.

2. Search: Traverse the trie to see if a word exists by following character nodes. Ensure the
final node is marked as a word’s end.

3. Starts With: Similar to search but doesn’t require marking the end of the word, just that
all characters match in sequence.

Implementing Trie Template and Applying to Problems


Here’s the base template for a Trie and then applications to the given problems:

python

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

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

def insert(self, word):


current = self.root
for char in word:

Printed with ChatGPT to PDF 66/128


if char not in current.children:
current.children[char] = TrieNode()
current = current.children[char]
current.is_end_of_word = True

def search(self, word):


current = self.root
for char in word:
if char not in current.children:
return False
current = current.children[char]
return current.is_end_of_word

def starts_with(self, prefix):


current = self.root
for char in prefix:
if char not in current.children:
return False
current = current.children[char]
return True

Problem 1: Implement Trie (Prefix Tree)


Using the above template directly provides the solution to the Implement Trie problem:

python

# Initialize Trie
trie = Trie()
trie.insert("apple")
print(trie.search("apple")) # Returns True
print(trie.search("app")) # Returns False
print(trie.starts_with("app")) # Returns True
trie.insert("app")
print(trie.search("app")) # Returns True

Problem 2: Design Add and Search Words Data Structure

Printed with ChatGPT to PDF 67/128


This problem extends the basic Trie by adding the ability to search with . as a wildcard
character, which can match any letter. We modify search to handle wildcards by recursively
exploring all possible matches.

python

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

def add_word(self, word):


current = self.root
for char in word:
if char not in current.children:
current.children[char] = TrieNode()
current = current.children[char]
current.is_end_of_word = True

def search(self, word):


def dfs(node, i):
if i == len(word):
return node.is_end_of_word

char = word[i]
if char == '.': # Wildcard case
return any(dfs(child, i + 1) for child in node.children.values())
elif char in node.children:
return dfs(node.children[char], i + 1)
return False

return dfs(self.root, 0)

# Example usage
word_dict = WordDictionary()
word_dict.add_word("bad")
word_dict.add_word("dad")
word_dict.add_word("mad")
print(word_dict.search("pad")) # False
print(word_dict.search("bad")) # True
print(word_dict.search(".ad")) # True
print(word_dict.search("b..")) # True

Printed with ChatGPT to PDF 68/128


Problem 3: Word Search II
In Word Search II, we aim to find all words from a list in a grid. This problem combines Trie
for prefix storage with DFS for grid traversal. Here, we insert each word into a Trie and then
use DFS to find words that start with a given prefix.

python

class Solution:
def find_words(self, board, words):
# Step 1: Build the Trie with words
trie = Trie()
for word in words:
trie.insert(word)

rows, cols = len(board), len(board[0])


result = set()

def dfs(r, c, node, path):


if node.is_end_of_word:
result.add(path) # Add valid word to result

if r < 0 or r >= rows or c < 0 or c >= cols or board[r][c] == '#':


return

char = board[r][c]
if char not in node.children:
return

# Mark the board position as visited


board[r][c] = '#'
next_node = node.children[char]

# Explore all directions


for dr, dc in [(0, 1), (1, 0), (0, -1), (-1, 0)]:
dfs(r + dr, c + dc, next_node, path + char)

board[r][c] = char # Unmark as visited

# Step 3: Start DFS from each cell


for r in range(rows):

Printed with ChatGPT to PDF 69/128


for c in range(cols):
dfs(r, c, trie.root, "")

return list(result)

# Example usage
board = [
["o", "a", "a", "n"],
["e", "t", "a", "e"],
["i", "h", "k", "r"],
["i", "f", "l", "v"]
]
words = ["oath", "pea", "eat", "rain"]
solution = Solution()
print(solution.find_words(board, words)) # ["oath", "eat"]

Summary of Applications
Implement Trie: Store and check for prefixes or exact word matches.

Add and Search Words Data Structure: Trie with added complexity for handling
wildcard matching, utilizing DFS.

Word Search II: Combines DFS grid traversal with Trie search to find words in a 2D grid,
leveraging Trie to reduce search space by validating prefixes.

By following this Trie template, we manage words and prefixes efficiently, add powerful
search functionalities, and apply it across a variety of applications requiring prefix or word-
based retrieval.

Graph problems often involve understanding and traversing relationships between nodes,
whether in the form of grid-based connections (like islands or rooms) or directed/undirected
edges (like courses or paths). Problems involving graph traversal, connectivity, and
component identification can typically be solved using two key techniques: Breadth-First
Search (BFS) and Depth-First Search (DFS).

Graph Template Explanation

Why This Template is Useful

Printed with ChatGPT to PDF 70/128


For most graph problems, BFS and DFS provide methods to explore all nodes or connected
components:

DFS explores as deep as possible along a branch before backtracking. It is useful for
problems that require exploring all possible paths or marking connected components.

BFS explores all neighbors at the present depth before moving deeper. It is particularly
helpful for shortest-path or minimum-step problems.

Union-Find is another technique for detecting connected components in an undirected


graph, commonly used for problems involving cycle detection and merging of nodes in
separate sets.

Graph Template Design


The template below is designed to address common graph operations:

1. DFS/BFS Traversal for exploring nodes and connected components.

2. Visited Set/Array to avoid redundant checks and prevent infinite loops.

3. Union-Find (when applicable) for managing disjoint sets of nodes.

Implementing Graph Template and Applying to Problems


The following graph traversal functions demonstrate DFS and BFS with a standard visited set:

python

from collections import deque, defaultdict

# DFS Template
def dfs(node, visited, graph):
visited.add(node)
# Process node if needed
for neighbor in graph[node]:
if neighbor not in visited:
dfs(neighbor, visited, graph)

# BFS Template
def bfs(start, graph):
visited = set([start])

Printed with ChatGPT to PDF 71/128


queue = deque([start])
while queue:
node = queue.popleft()
# Process node if needed
for neighbor in graph[node]:
if neighbor not in visited:
visited.add(neighbor)
queue.append(neighbor)

# Union-Find Template
class UnionFind:
def __init__(self, size):
self.parent = list(range(size))
self.rank = [0] * size

def find(self, x):


if self.parent[x] != x:
self.parent[x] = self.find(self.parent[x]) # Path compression
return self.parent[x]

def union(self, x, y):


rootX = self.find(x)
rootY = self.find(y)
if rootX != rootY:
# Union by rank
if self.rank[rootX] > self.rank[rootY]:
self.parent[rootY] = rootX
elif self.rank[rootX] < self.rank[rootY]:
self.parent[rootX] = rootY
else:
self.parent[rootY] = rootX
self.rank[rootX] += 1

Applications to Specific Problems


1. Number of Islands

Problem: Count the number of islands (connected components of 1 s) in a grid.

Solution: Use DFS to mark all cells in each island.

Printed with ChatGPT to PDF 72/128


python

def numIslands(grid):
def dfs(r, c):
if r < 0 or r >= len(grid) or c < 0 or c >= len(grid[0]) or grid[r][c]
== '0':
return
grid[r][c] = '0' # Mark as visited
for dr, dc in [(0, 1), (1, 0), (0, -1), (-1, 0)]:
dfs(r + dr, c + dc)

count = 0
for r in range(len(grid)):
for c in range(len(grid[0])):
if grid[r][c] == '1':
dfs(r, c)
count += 1
return count

2. Max Area of Island

Problem: Find the largest connected area of 1 s in a grid.

Solution: Similar to Number of Islands, but we also keep track of the size of each
island.

python

def maxAreaOfIsland(grid):
def dfs(r, c):
if r < 0 or r >= len(grid) or c < 0 or c >= len(grid[0]) or grid[r][c]
== 0:
return 0
grid[r][c] = 0 # Mark as visited
return 1 + sum(dfs(r + dr, c + dc) for dr, dc in [(0, 1), (1, 0), (0,
-1), (-1, 0)])

max_area = 0
for r in range(len(grid)):
for c in range(len(grid[0])):
if grid[r][c] == 1:

Printed with ChatGPT to PDF 73/128


max_area = max(max_area, dfs(r, c))
return max_area

3. Clone Graph

Problem: Clone an undirected graph where each node points to its neighbors.

Solution: Use DFS or BFS with a mapping to clone nodes and connections.

python

def cloneGraph(node):
if not node:
return None

clones = {node: Node(node.val)}


def dfs(n):
for neighbor in n.neighbors:
if neighbor not in clones:
clones[neighbor] = Node(neighbor.val)
dfs(neighbor)
clones[n].neighbors.append(clones[neighbor])

dfs(node)
return clones[node]

4. Course Schedule (Detect Cycle in Directed Graph)

Problem: Determine if courses can be taken given prerequisites.

Solution: Use DFS to detect cycles in a directed graph.

python

def canFinish(numCourses, prerequisites):


graph = defaultdict(list)
for course, prereq in prerequisites:
graph[prereq].append(course)

visited = set()
cycle = set()

def dfs(course):
if course in cycle: # Detected cycle
return False

Printed with ChatGPT to PDF 74/128


if course in visited:
return True

cycle.add(course)
for neighbor in graph[course]:
if not dfs(neighbor):
return False
cycle.remove(course)
visited.add(course)
return True

return all(dfs(course) for course in range(numCourses))

5. Pacific Atlantic Water Flow

Problem: Find cells from which water can flow to both oceans.

Solution: Perform BFS/DFS from each ocean's boundary to find reachable cells.

python

def pacificAtlantic(matrix):
if not matrix: return []

rows, cols = len(matrix), len(matrix[0])


pacific_reachable = set()
atlantic_reachable = set()

def dfs(r, c, reachable, prev_height):


if ((r, c) in reachable or r < 0 or r >= rows or c < 0 or c >= cols or
matrix[r][c] < prev_height):
return
reachable.add((r, c))
for dr, dc in [(0, 1), (1, 0), (0, -1), (-1, 0)]:
dfs(r + dr, c + dc, reachable, matrix[r][c])

for c in range(cols):
dfs(0, c, pacific_reachable, matrix[0][c])
dfs(rows - 1, c, atlantic_reachable, matrix[rows - 1][c])
for r in range(rows):
dfs(r, 0, pacific_reachable, matrix[r][0])
dfs(r, cols - 1, atlantic_reachable, matrix[r][cols - 1])

Printed with ChatGPT to PDF 75/128


return list(pacific_reachable & atlantic_reachable)

Summary
The graph template provides a solid foundation for addressing the following types of
problems:

Grid-based problems: Use DFS or BFS to explore connected regions (e.g., islands,
rooms).

Cycle detection in directed graphs: Use DFS with cycle tracking to detect prerequisites
or dependency issues.

Cloning structures: Use DFS/BFS to replicate graph structures without duplication.

Union-Find: Efficient for connected component counting or cycle detection in undirected


graphs.

This template can be customized for each specific graph-based problem, allowing for
efficient and systematic exploration of connected components, paths, and cycles.

Let's tackle each of the given problems individually by applying the graph traversal and
manipulation techniques previously described. These include methods like DFS, BFS, and
Union-Find, as well as handling graphs in the form of matrices or adjacency lists.

1. Number of Islands
Count the number of islands in a grid.

python

def numIslands(grid):
def dfs(r, c):
if r < 0 or r >= len(grid) or c < 0 or c >= len(grid[0]) or grid[r][c] ==
'0':
return
grid[r][c] = '0' # Mark as visited
for dr, dc in [(0, 1), (1, 0), (0, -1), (-1, 0)]:
dfs(r + dr, c + dc)

Printed with ChatGPT to PDF 76/128


count = 0
for r in range(len(grid)):
for c in range(len(grid[0])):
if grid[r][c] == '1':
dfs(r, c)
count += 1
return count

2. Max Area of Island


Calculate the largest connected area of 1’s in a grid.

python

def maxAreaOfIsland(grid):
def dfs(r, c):
if r < 0 or r >= len(grid) or c < 0 or c >= len(grid[0]) or grid[r][c] == 0:
return 0
grid[r][c] = 0 # Mark as visited
return 1 + sum(dfs(r + dr, c + dc) for dr, dc in [(0, 1), (1, 0), (0, -1),
(-1, 0)])

max_area = 0
for r in range(len(grid)):
for c in range(len(grid[0])):
if grid[r][c] == 1:
max_area = max(max_area, dfs(r, c))
return max_area

3. Clone Graph
Clone an undirected graph where each node has pointers to its neighbors.

python

Printed with ChatGPT to PDF 77/128


def cloneGraph(node):
if not node:
return None

clones = {node: Node(node.val)}

def dfs(n):
for neighbor in n.neighbors:
if neighbor not in clones:
clones[neighbor] = Node(neighbor.val)
dfs(neighbor)
clones[n].neighbors.append(clones[neighbor])

dfs(node)
return clones[node]

4. Walls and Gates


Fill each empty room with the distance to its nearest gate (0) in a 2D grid.

python

from collections import deque

def wallsAndGates(rooms):
queue = deque()
for r in range(len(rooms)):
for c in range(len(rooms[0])):
if rooms[r][c] == 0: # Gate
queue.append((r, c))

while queue:
r, c = queue.popleft()
for dr, dc in [(0, 1), (1, 0), (0, -1), (-1, 0)]:
nr, nc = r + dr, c + dc
if 0 <= nr < len(rooms) and 0 <= nc < len(rooms[0]) and rooms[nr][nc] ==
2147483647:

Printed with ChatGPT to PDF 78/128


rooms[nr][nc] = rooms[r][c] + 1
queue.append((nr, nc))

5. Rotting Oranges
Determine the time taken for all fresh oranges to rot, given a grid where each cell is an
empty space, fresh orange, or rotten orange.

python

from collections import deque

def orangesRotting(grid):
queue = deque()
fresh = 0
rows, cols = len(grid), len(grid[0])

for r in range(rows):
for c in range(cols):
if grid[r][c] == 2: # Rotten orange
queue.append((r, c))
elif grid[r][c] == 1: # Fresh orange
fresh += 1

if fresh == 0:
return 0 # No fresh oranges to rot

minutes = 0
while queue and fresh > 0:
minutes += 1
for _ in range(len(queue)):
r, c = queue.popleft()
for dr, dc in [(0, 1), (1, 0), (0, -1), (-1, 0)]:
nr, nc = r + dr, c + dc
if 0 <= nr < rows and 0 <= nc < cols and grid[nr][nc] == 1:
grid[nr][nc] = 2
fresh -= 1
queue.append((nr, nc))

Printed with ChatGPT to PDF 79/128


return minutes if fresh == 0 else -1

6. Pacific Atlantic Water Flow


Determine which cells in a matrix can flow water to both the Pacific and Atlantic oceans.

python

def pacificAtlantic(matrix):
if not matrix: return []

rows, cols = len(matrix), len(matrix[0])


pacific_reachable = set()
atlantic_reachable = set()

def dfs(r, c, reachable, prev_height):


if ((r, c) in reachable or r < 0 or r >= rows or c < 0 or c >= cols or
matrix[r][c] < prev_height):
return
reachable.add((r, c))
for dr, dc in [(0, 1), (1, 0), (0, -1), (-1, 0)]:
dfs(r + dr, c + dc, reachable, matrix[r][c])

for c in range(cols):
dfs(0, c, pacific_reachable, matrix[0][c])
dfs(rows - 1, c, atlantic_reachable, matrix[rows - 1][c])
for r in range(rows):
dfs(r, 0, pacific_reachable, matrix[r][0])
dfs(r, cols - 1, atlantic_reachable, matrix[r][cols - 1])

return list(pacific_reachable & atlantic_reachable)

7. Course Schedule

Printed with ChatGPT to PDF 80/128


Determine if it's possible to finish all courses given prerequisites (detect a cycle in a directed
graph).

python

from collections import defaultdict

def canFinish(numCourses, prerequisites):


graph = defaultdict(list)
for course, prereq in prerequisites:
graph[prereq].append(course)

visited = set()
cycle = set()

def dfs(course):
if course in cycle:
return False
if course in visited:
return True

cycle.add(course)
for neighbor in graph[course]:
if not dfs(neighbor):
return False
cycle.remove(course)
visited.add(course)
return True

return all(dfs(course) for course in range(numCourses))

8. Course Schedule II
Return the order in which courses should be taken to finish all courses.

python

from collections import defaultdict, deque

Printed with ChatGPT to PDF 81/128


def findOrder(numCourses, prerequisites):
graph = defaultdict(list)
indegree = [0] * numCourses
for course, prereq in prerequisites:
graph[prereq].append(course)
indegree[course] += 1

queue = deque([i for i in range(numCourses) if indegree[i] == 0])


order = []

while queue:
course = queue.popleft()
order.append(course)
for neighbor in graph[course]:
indegree[neighbor] -= 1
if indegree[neighbor] == 0:
queue.append(neighbor)

return order if len(order) == numCourses else []

9. Word Ladder
Find the length of the shortest transformation sequence from the beginWord to endWord .

python

from collections import deque, defaultdict

def ladderLength(beginWord, endWord, wordList):


wordSet = set(wordList)
if endWord not in wordSet:
return 0

queue = deque([(beginWord, 1)])


while queue:
word, length = queue.popleft()
if word == endWord:
return length

Printed with ChatGPT to PDF 82/128


for i in range(len(word)):
for c in 'abcdefghijklmnopqrstuvwxyz':
newWord = word[:i] + c + word[i+1:]
if newWord in wordSet:
wordSet.remove(newWord)
queue.append((newWord, length + 1))
return 0

These solutions cover common graph traversal, shortest path finding, and cycle detection
problems frequently encountered in graphs. Each approach optimally fits the problem
requirements based on the graph characteristics (e.g., grid-based or adjacency list
representation).

Advanced graph problems on LeetCode require techniques beyond basic traversal, often
involving shortest-path algorithms (Dijkstra, Bellman-Ford), Minimum Spanning Tree (Prim,
Kruskal), Topological Sort, and Union-Find. These techniques help tackle problems with
constraints on path length, cost, or specific requirements around order and dependencies.

Template for Advanced Graph Problems


1. Understand the Graph Structure: Determine if the graph is directed, undirected,
weighted, or unweighted. This helps decide the appropriate traversal or pathfinding
algorithm.

2. Identify the Goal:

Shortest Path: Use Dijkstra’s or Bellman-Ford.

Minimum Spanning Tree: Use Prim’s or Kruskal’s.

Topological Sort: Use Kahn’s algorithm or DFS for order/dependency problems.

Flood Fill/Union-Find: Use for connected components or dynamic connectivity.

3. Choose the Data Structures:

Adjacency List or Matrix: Depending on edge density.

Priority Queue (Heap): For shortest-paths and MST algorithms.

Union-Find: For connectivity problems.

4. Implementation:

Initialize the graph representation.

If using a priority queue (for Dijkstra or Prim’s), initialize it with starting nodes.

Printed with ChatGPT to PDF 83/128


Traverse using the selected algorithm.

Keep track of visited nodes and conditions on path length, cost, or specific stops.

Applying the Template to Each Problem

1. Reconstruct Itinerary

Use DFS with backtracking to reconstruct the itinerary by visiting each airport
lexicographically.

python

from collections import defaultdict

def findItinerary(tickets):
graph = defaultdict(list)
for start, end in sorted(tickets):
graph[start].append(end)

itinerary = []
def dfs(airport):
while graph[airport]:
dfs(graph[airport].pop(0))
itinerary.append(airport)

dfs("JFK")
return itinerary[::-1]

2. Min Cost to Connect All Points

Use Prim's algorithm for the Minimum Spanning Tree of the points.

python

import heapq

def minCostConnectPoints(points):
n = len(points)

Printed with ChatGPT to PDF 84/128


graph = {i: [] for i in range(n)}
for i in range(n):
for j in range(i + 1, n):
cost = abs(points[i][0] - points[j][0]) + abs(points[i][1] - points[j]
[1])
graph[i].append((cost, j))
graph[j].append((cost, i))

min_heap = [(0, 0)]


visited = set()
result = 0

while len(visited) < n:


cost, u = heapq.heappop(min_heap)
if u in visited:
continue
visited.add(u)
result += cost
for edge_cost, v in graph[u]:
if v not in visited:
heapq.heappush(min_heap, (edge_cost, v))

return result

3. Network Delay Time

Use Dijkstra’s algorithm to find the shortest time to reach all nodes from a start node.

python

import heapq
from collections import defaultdict

def networkDelayTime(times, n, k):


graph = defaultdict(list)
for u, v, w in times:
graph[u].append((v, w))

min_heap = [(0, k)]


distances = {}

Printed with ChatGPT to PDF 85/128


while min_heap:
time, node = heapq.heappop(min_heap)
if node in distances:
continue
distances[node] = time
for v, w in graph[node]:
if v not in distances:
heapq.heappush(min_heap, (time + w, v))

return max(distances.values()) if len(distances) == n else -1

4. Swim In Rising Water

Use Dijkstra’s algorithm on a grid to find the minimum time required to reach the bottom-
right corner.

python

import heapq

def swimInWater(grid):
n = len(grid)
min_heap = [(grid[0][0], 0, 0)]
visited = set()
directions = [(0, 1), (1, 0), (0, -1), (-1, 0)]
answer = 0

while min_heap:
elevation, r, c = heapq.heappop(min_heap)
answer = max(answer, elevation)
if (r, c) == (n - 1, n - 1):
return answer
for dr, dc in directions:
nr, nc = r + dr, c + dc
if 0 <= nr < n and 0 <= nc < n and (nr, nc) not in visited:
visited.add((nr, nc))
heapq.heappush(min_heap, (grid[nr][nc], nr, nc))
return -1

Printed with ChatGPT to PDF 86/128


5. Alien Dictionary

Use Topological Sort to determine the correct order of characters in an alien dictionary.

python

from collections import defaultdict, deque

def alienOrder(words):
graph = defaultdict(set)
in_degree = {c: 0 for word in words for c in word}

for i in range(len(words) - 1):


w1, w2 = words[i], words[i + 1]
for c1, c2 in zip(w1, w2):
if c1 != c2:
if c2 not in graph[c1]:
graph[c1].add(c2)
in_degree[c2] += 1
break
else:
if len(w1) > len(w2):
return ""

queue = deque([c for c in in_degree if in_degree[c] == 0])


order = []

while queue:
c = queue.popleft()
order.append(c)
for neighbor in graph[c]:
in_degree[neighbor] -= 1
if in_degree[neighbor] == 0:
queue.append(neighbor)

return "".join(order) if len(order) == len(in_degree) else ""

6. Cheapest Flights Within K Stops

Printed with ChatGPT to PDF 87/128


Use a modified Bellman-Ford algorithm with constraints on the number of edges (stops).

python

import heapq
from collections import defaultdict

def findCheapestPrice(n, flights, src, dst, K):


graph = defaultdict(list)
for u, v, w in flights:
graph[u].append((v, w))

min_heap = [(0, src, K + 1)]

while min_heap:
cost, u, stops = heapq.heappop(min_heap)
if u == dst:
return cost
if stops > 0:
for v, w in graph[u]:
heapq.heappush(min_heap, (cost + w, v, stops - 1))

return -1

Explanation and Summary of Applications


Reconstruct Itinerary: DFS with lexicographic ordering helps construct a valid itinerary
for all given tickets.

Min Cost to Connect All Points: Prim’s MST to find the minimum cost to connect all
points.

Network Delay Time: Dijkstra’s for finding the shortest paths from a source node to
every other node.

Swim In Rising Water: Grid-based shortest path using Dijkstra’s for reaching the target
with a minimal maximum cost.

Alien Dictionary: Topological sorting on characters to determine the character


precedence.

Printed with ChatGPT to PDF 88/128


Cheapest Flights Within K Stops: Bellman-Ford with limited steps, focusing on least-cost
paths within a stop limit.

This template and methods are widely applicable to problems involving pathfinding,
minimum spanning trees, topological ordering, and cost-optimization within constraints,
fitting a broad range of advanced graph problems.

Dynamic programming (DP) is a powerful approach for solving problems that exhibit
overlapping subproblems and optimal substructure. In 1D DP problems, the solution often
involves breaking the problem down into a sequence of decisions or subproblems, with each
subproblem depending on the results of the previous ones.

Identifying a 1D Dynamic Programming Problem


A problem can likely be solved using DP if:

1. Optimal Substructure: The optimal solution to the problem can be constructed from
optimal solutions to its subproblems.

2. Overlapping Subproblems: The problem involves solving the same smaller problems
multiple times.

3. Sequential Decision-Making: The problem involves a sequence of decisions where each


decision depends on the previous ones (like choosing to include/exclude elements in a
subset).

Examples include finding the maximum/minimum values for subarrays, counting specific
sequences, or determining possible paths in a linear structure (like arrays or lists).

Template for 1D Dynamic Programming Problems


1. Define the State: Identify what each element in your DP array represents. For instance,
dp[i] might represent the maximum/minimum/ways at the i-th position.

2. Define the Recurrence Relation: This is the formula that determines how to transition
between states. For example, if dp[i] is based on previous states dp[i-1] , dp[i-2] ,
etc., then define the relationship accordingly.

3. Initialize the Base Cases: These are starting values of the DP array. They are essential to
kick-start the recurrence relation.

Printed with ChatGPT to PDF 89/128


4. Iterate and Populate the DP Array: Using the recurrence relation, iterate through the
problem space and fill in the DP array until reaching the desired end state.

5. Return the Final Result: Usually, the answer to the problem will be in the last position of
the DP array or derived from it.

Applying the Template to Each Problem

1. Climbing Stairs

Each step can be reached from either one step before or two steps before. Hence, dp[i] =
dp[i-1] + dp[i-2] .

python

def climbStairs(n):
if n <= 2:
return n
dp = [0] * (n + 1)
dp[1], dp[2] = 1, 2
for i in range(3, n + 1):
dp[i] = dp[i-1] + dp[i-2]
return dp[n]

2. Min Cost Climbing Stairs

You can either start from step i-1 or i-2 , so dp[i] = cost[i] + min(dp[i-1], dp[i-2]) .

python

def minCostClimbingStairs(cost):
n = len(cost)
dp = [0] * n
dp[0], dp[1] = cost[0], cost[1]
for i in range(2, n):

Printed with ChatGPT to PDF 90/128


dp[i] = cost[i] + min(dp[i-1], dp[i-2])
return min(dp[-1], dp[-2])

3. House Robber

Each house can either be robbed or skipped, resulting in dp[i] = max(dp[i-1], dp[i-2] +
nums[i]) .

python

def rob(nums):
if not nums:
return 0
n = len(nums)
if n == 1:
return nums[0]
dp = [0] * n
dp[0], dp[1] = nums[0], max(nums[0], nums[1])
for i in range(2, n):
dp[i] = max(dp[i-1], dp[i-2] + nums[i])
return dp[-1]

4. House Robber II

Since it’s a circular arrangement, split the array into two cases (excluding either the first or
the last house) and apply the House Robber logic.

python

def rob(nums):
def simple_rob(nums):
n = len(nums)
if n == 1:
return nums[0]
dp = [0] * n
dp[0], dp[1] = nums[0], max(nums[0], nums[1])

Printed with ChatGPT to PDF 91/128


for i in range(2, n):
dp[i] = max(dp[i-1], dp[i-2] + nums[i])
return dp[-1]

return max(nums[0], simple_rob(nums[1:]), simple_rob(nums[:-1]))

5. Longest Palindromic Substring

Expand around the center for each character; check if it forms a palindrome and keep track
of the longest.

python

def longestPalindrome(s):
def expandAroundCenter(s, left, right):
while left >= 0 and right < len(s) and s[left] == s[right]:
left -= 1
right += 1
return s[left + 1:right]

res = ""
for i in range(len(s)):
res = max(res, expandAroundCenter(s, i, i), expandAroundCenter(s, i, i + 1),
key=len)
return res

6. Palindromic Substrings

Use a DP approach where dp[i][j] is True if the substring s[i:j+1] is a palindrome.

python

def countSubstrings(s):
n = len(s)
dp = [[False] * n for _ in range(n)]
count = 0
for i in range(n - 1, -1, -1):

Printed with ChatGPT to PDF 92/128


for j in range(i, n):
if s[i] == s[j] and (j - i < 3 or dp[i + 1][j - 1]):
dp[i][j] = True
count += 1
return count

7. Decode Ways

dp[i] = dp[i-1] + dp[i-2] if the substring can be split into two valid numbers.

python

def numDecodings(s):
if not s or s[0] == "0":
return 0
dp = [0] * (len(s) + 1)
dp[0], dp[1] = 1, 1
for i in range(2, len(s) + 1):
if s[i-1] != "0":
dp[i] += dp[i-1]
if 10 <= int(s[i-2:i]) <= 26:
dp[i] += dp[i-2]
return dp[-1]

8. Coin Change

Use dp[i] to represent the minimum coins needed to get an amount of i .

python

def coinChange(coins, amount):


dp = [float('inf')] * (amount + 1)
dp[0] = 0
for coin in coins:
for i in range(coin, amount + 1):

Printed with ChatGPT to PDF 93/128


dp[i] = min(dp[i], dp[i - coin] + 1)
return dp[amount] if dp[amount] != float('inf') else -1

9. Longest Increasing Subsequence

Use dp[i] to represent the length of the longest increasing subsequence ending at i .

python

def lengthOfLIS(nums):
dp = [1] * len(nums)
for i in range(len(nums)):
for j in range(i):
if nums[i] > nums[j]:
dp[i] = max(dp[i], dp[j] + 1)
return max(dp)

10. Partition Equal Subset Sum

Use a boolean DP array dp[i] to determine if a subset sum i is possible.

python

def canPartition(nums):
total = sum(nums)
if total % 2 != 0:
return False
target = total // 2
dp = [False] * (target + 1)
dp[0] = True
for num in nums:
for i in range(target, num - 1, -1):
dp[i] = dp[i] or dp[i - num]
return dp[target]

Printed with ChatGPT to PDF 94/128


Each problem illustrates applying the core DP template and optimally breaking down
subproblems, calculating state values, and determining a minimum/maximum/boolean
condition for the final solution.

Let’s continue solving the remaining 1D dynamic programming problems by applying the
same template-based approach. I'll walk through each problem using the DP template:

11. Word Break


For this problem, we want to determine if a string can be segmented into words that exist in
a given dictionary.

Define the State: Let dp[i] represent whether the substring s[0:i] can be
segmented into words in the dictionary.

Recurrence Relation: For each position i , check if any substring s[j:i] (where j <
i ) exists in the dictionary and if dp[j] is True . Then, set dp[i] to True .

Base Case: dp[0] = True because an empty substring can always be segmented.

python

def wordBreak(s, wordDict):


wordSet = set(wordDict)
dp = [False] * (len(s) + 1)
dp[0] = True

for i in range(1, len(s) + 1):


for j in range(i):
if dp[j] and s[j:i] in wordSet:
dp[i] = True
break

return dp[len(s)]

12. Maximum Product Subarray


This problem requires finding the maximum product of a contiguous subarray.

Printed with ChatGPT to PDF 95/128


Define the State: Use dp_max[i] and dp_min[i] to track the maximum and minimum
products ending at i , since a negative value can turn a minimum into a maximum and
vice versa.

Recurrence Relation: dp_max[i] = max(nums[i], nums[i] * dp_max[i-1], nums[i] *


dp_min[i-1]) and similarly for dp_min .

Base Case: Initialize dp_max[0] = dp_min[0] = nums[0] .

python

def maxProduct(nums):
if not nums:
return 0

dp_max = dp_min = result = nums[0]

for i in range(1, len(nums)):


if nums[i] < 0:
dp_max, dp_min = dp_min, dp_max
dp_max = max(nums[i], dp_max * nums[i])
dp_min = min(nums[i], dp_min * nums[i])
result = max(result, dp_max)

return result

13. Longest Increasing Subsequence


This problem involves finding the length of the longest subsequence where each number is
greater than the previous one.

Define the State: dp[i] represents the length of the longest increasing subsequence
ending at i .

Recurrence Relation: For each j < i , if nums[j] < nums[i] , update dp[i] =
max(dp[i], dp[j] + 1) .

Base Case: Initialize dp[i] = 1 for each i .

python

Printed with ChatGPT to PDF 96/128


def lengthOfLIS(nums):
if not nums:
return 0

dp = [1] * len(nums)

for i in range(len(nums)):
for j in range(i):
if nums[j] < nums[i]:
dp[i] = max(dp[i], dp[j] + 1)

return max(dp)

14. Partition Equal Subset Sum


Determine if an array can be split into two subsets with equal sum.

Define the State: dp[i] is True if a subset sum of i is achievable.

Recurrence Relation: For each num , if dp[j - num] is True , set dp[j] to True .

Base Case: dp[0] = True because a subset sum of 0 is always achievable.

python

def canPartition(nums):
total = sum(nums)
if total % 2 != 0:
return False

target = total // 2
dp = [False] * (target + 1)
dp[0] = True

for num in nums:


for i in range(target, num - 1, -1):
dp[i] = dp[i] or dp[i - num]

return dp[target]

Printed with ChatGPT to PDF 97/128


Summary
With these problems solved, here’s a recap of how the DP template was applied across
various challenges:

1. Identify the State: Defined what dp[i] represents (e.g., max product, sum achievable,
subsequence length).

2. Set Recurrence Relation: Formulated the relationship between dp[i] and previous
states to build the solution.

3. Initialize Base Cases: Defined the base values to start the solution process.

4. Iterate and Solve: Populated the DP array by iterating and applying the recurrence
relation.

This template can solve a wide range of 1D DP problems, providing a structured way to
approach similar challenges.

To solve 2D dynamic programming problems, we’ll use a structured template that leverages
the multi-dimensional nature of the problem. Let’s walk through a 2D DP template and apply
it to each problem:

2D DP Template
1. Define the State:

Establish dp[i][j] as the state that represents the desired outcome at indices i
and j in the problem.

This could represent counts, minimum/maximum values, or other subproblems that


build up the final result.

2. Establish Recurrence Relation:

Determine how dp[i][j] can be constructed based on previous states in the DP


table, usually involving adjacent values in rows, columns, or diagonals.

Use the problem's constraints to define dp[i][j] in terms of dp[i-1][j] , dp[i]


[j-1] , or other previous states.

3. Initialize Base Cases:

Define base cases where dp[0][j] or dp[i][0] can be initialized directly without
recurrence, often based on problem constraints (like single-element subproblems).

Printed with ChatGPT to PDF 98/128


4. Iterate and Fill DP Table:

Loop through the entire DP table, filling each entry based on the recurrence relation
until reaching the final solution at dp[m][n] .

5. Extract the Solution:

Usually, dp[m][n] or another specific cell represents the answer. This can also
involve finding a maximum or minimum within the DP table.

Applying the Template to Each Problem

1. Unique Paths

State: dp[i][j] represents the number of unique paths to reach cell (i, j) .

Recurrence: dp[i][j] = dp[i-1][j] + dp[i][j-1] if both indices are valid.

Base Cases: dp[0][j] = 1 and dp[i][0] = 1 since there is only one way to reach any
cell in the first row or column.

python

def uniquePaths(m, n):


dp = [[1] * n for _ in range(m)]
for i in range(1, m):
for j in range(1, n):
dp[i][j] = dp[i-1][j] + dp[i][j-1]
return dp[m-1][n-1]

2. Longest Common Subsequence

State: dp[i][j] represents the length of the LCS of text1[:i] and text2[:j] .

Recurrence: If text1[i-1] == text2[j-1] , dp[i][j] = dp[i-1][j-1] + 1 , else dp[i]


[j] = max(dp[i-1][j], dp[i][j-1]) .

Base Cases: dp[0][j] = 0 and dp[i][0] = 0 as LCS with an empty string is 0.

Printed with ChatGPT to PDF 99/128


python

def longestCommonSubsequence(text1, text2):


m, n = len(text1), len(text2)
dp = [[0] * (n + 1) for _ in range(m + 1)]
for i in range(1, m + 1):
for j in range(1, n + 1):
if text1[i-1] == text2[j-1]:
dp[i][j] = dp[i-1][j-1] + 1
else:
dp[i][j] = max(dp[i-1][j], dp[i][j-1])
return dp[m][n]

3. Best Time to Buy and Sell Stock with Cooldown

State: dp[i][j] with j as 0 (holding) or 1 (not holding) represents max profit at day
i.

Recurrence: Transition from dp[i-1][j] with cooldown on dp[i-1][0] .

Base Cases: Initialize dp[0][0] as -prices[0] and dp[0][1] as 0 .

python

def maxProfit(prices):
if not prices:
return 0
n = len(prices)
dp = [[0] * 2 for _ in range(n)]
dp[0][0] = -prices[0]
dp[0][1] = 0
for i in range(1, n):
dp[i][0] = max(dp[i-1][0], dp[i-1][1] - prices[i])
dp[i][1] = max(dp[i-1][1], dp[i-2][1] + prices[i] if i > 1 else prices[i])
return dp[-1][1]

4. Coin Change II

Printed with ChatGPT to PDF 100/128


State: dp[i][j] represents the number of ways to get amount j with first i coins.

Recurrence: dp[i][j] = dp[i-1][j] + dp[i][j-coins[i-1]] if j >= coins[i-1] .

Base Cases: dp[0][j] = 0 except dp[0][0] = 1 .

python

def change(amount, coins):


dp = [[0] * (amount + 1) for _ in range(len(coins) + 1)]
dp[0][0] = 1
for i in range(1, len(coins) + 1):
dp[i][0] = 1
for j in range(1, amount + 1):
dp[i][j] = dp[i-1][j] + (dp[i][j-coins[i-1]] if j >= coins[i-1] else 0)
return dp[-1][-1]

5. Target Sum

State: dp[i][s] represents the number of ways to get sum s with first i elements.

Recurrence: dp[i][s] = dp[i-1][s-nums[i]] + dp[i-1][s+nums[i]] .

Base Cases: dp[0][target] = 1 .

python

def findTargetSumWays(nums, target):


total_sum = sum(nums)
if abs(target) > total_sum:
return 0
dp = [[0] * (2 * total_sum + 1) for _ in range(len(nums) + 1)]
dp[0][total_sum] = 1
for i in range(1, len(nums) + 1):
for j in range(2 * total_sum + 1):
if dp[i-1][j] > 0:
dp[i][j + nums[i-1]] += dp[i-1][j]
dp[i][j - nums[i-1]] += dp[i-1][j]
return dp[len(nums)][total_sum + target]

Printed with ChatGPT to PDF 101/128


The same approach can be applied to the remaining problems by defining the state, setting
up recurrence relations, initializing base cases, and filling the DP table accordingly. This
approach forms the backbone of solving multi-dimensional dynamic programming problems
in 2D space. Each problem requires understanding the transition and dependencies based
on prior cells in the DP table.

Let's go through each remaining problem from your list in the context of 2D dynamic
programming:

6. Interleaving String
Problem: Given two strings s1 and s2 , and a target string s3 , determine if s3 can be
formed by interleaving s1 and s2 .

State: dp[i][j] represents whether s3[:i+j] can be formed by interleaving s1[:i]


and s2[:j] .

Recurrence:

If dp[i-1][j] is true and s1[i-1] == s3[i+j-1] , then dp[i][j] is true.

If dp[i][j-1] is true and s2[j-1] == s3[i+j-1] , then dp[i][j] is true.

Base Cases:

dp[0][0] = True because two empty strings form an empty target.

python

def isInterleave(s1, s2, s3):


if len(s1) + len(s2) != len(s3):
return False
dp = [[False] * (len(s2) + 1) for _ in range(len(s1) + 1)]
dp[0][0] = True
for i in range(len(s1) + 1):
for j in range(len(s2) + 1):
if i > 0:
dp[i][j] |= dp[i-1][j] and s1[i-1] == s3[i+j-1]

Printed with ChatGPT to PDF 102/128


if j > 0:
dp[i][j] |= dp[i][j-1] and s2[j-1] == s3[i+j-1]
return dp[len(s1)][len(s2)]

7. Longest Increasing Path in a Matrix


Problem: Find the longest increasing path in a matrix.

State: dp[i][j] represents the length of the longest increasing path starting from cell
(i, j) .

Recurrence: For each cell (i, j) , if matrix[i][j] < matrix[x][y] for neighbors (x,
y) , update dp[i][j] = max(dp[i][j], 1 + dp[x][y]) .

Base Case: Each cell starts with length 1.

python

def longestIncreasingPath(matrix):
if not matrix or not matrix[0]:
return 0
rows, cols = len(matrix), len(matrix[0])
dp = [[-1] * cols for _ in range(rows)]

def dfs(i, j):


if dp[i][j] != -1:
return dp[i][j]
max_path = 1
for x, y in [(i-1, j), (i+1, j), (i, j-1), (i, j+1)]:
if 0 <= x < rows and 0 <= y < cols and matrix[x][y] > matrix[i][j]:
max_path = max(max_path, 1 + dfs(x, y))
dp[i][j] = max_path
return max_path

return max(dfs(i, j) for i in range(rows) for j in range(cols))

Printed with ChatGPT to PDF 103/128


8. Distinct Subsequences
Problem: Count the number of distinct subsequences of s that equal t .

State: dp[i][j] represents the number of distinct subsequences of s[:i] that equal
t[:j] .

Recurrence:

If s[i-1] == t[j-1] , then dp[i][j] = dp[i-1][j-1] + dp[i-1][j] .

Otherwise, dp[i][j] = dp[i-1][j] .

Base Cases: dp[i][0] = 1 (empty t is a subsequence of any s ).

python

def numDistinct(s, t):


m, n = len(s), len(t)
dp = [[0] * (n + 1) for _ in range(m + 1)]
for i in range(m + 1):
dp[i][0] = 1
for i in range(1, m + 1):
for j in range(1, n + 1):
if s[i - 1] == t[j - 1]:
dp[i][j] = dp[i - 1][j - 1] + dp[i - 1][j]
else:
dp[i][j] = dp[i - 1][j]
return dp[m][n]

9. Edit Distance
Problem: Find the minimum number of operations to convert word1 to word2 .

State: dp[i][j] represents the minimum operations needed to convert word1[:i] to


word2[:j] .

Recurrence:

If word1[i-1] == word2[j-1] , then dp[i][j] = dp[i-1][j-1] .

Otherwise, dp[i][j] = 1 + min(dp[i-1][j], dp[i][j-1], dp[i-1][j-1]) .

Base Cases:

Printed with ChatGPT to PDF 104/128


dp[i][0] = i (deleting all characters of word1 ).

dp[0][j] = j (inserting all characters of word2 ).

python

def minDistance(word1, word2):


m, n = len(word1), len(word2)
dp = [[0] * (n + 1) for _ in range(m + 1)]
for i in range(m + 1):
dp[i][0] = i
for j in range(n + 1):
dp[0][j] = j
for i in range(1, m + 1):
for j in range(1, n + 1):
if word1[i - 1] == word2[j - 1]:
dp[i][j] = dp[i - 1][j - 1]
else:
dp[i][j] = 1 + min(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1])
return dp[m][n]

10. Burst Balloons


Problem: Given nums , find the maximum coins obtained by bursting all balloons.

State: dp[i][j] represents the maximum coins obtained by bursting all balloons in the
subarray nums[i:j] .

Recurrence:

dp[i][j] = max(dp[i][j], dp[i][k] + dp[k][j] + nums[i-1]*nums[k]*nums[j])


for i < k < j .

Base Cases: Initialize dp with boundaries.

python

def maxCoins(nums):
nums = [1] + nums + [1]
n = len(nums)
dp = [[0] * n for _ in range(n)]

Printed with ChatGPT to PDF 105/128


for length in range(2, n):
for i in range(n - length):
j = i + length
for k in range(i + 1, j):
dp[i][j] = max(dp[i][j], dp[i][k] + dp[k][j] + nums[i] * nums[k] *
nums[j])
return dp[0][n-1]

11. Regular Expression Matching


Problem: Given strings s and p , where p contains . and * , determine if p matches
s.

State: dp[i][j] represents if s[:i] matches p[:j] .

Recurrence:

If p[j-1] == '.' or p[j-1] == s[i-1] , dp[i][j] = dp[i-1][j-1] .

If p[j-1] == '*' , dp[i][j] = dp[i][j-2] or ( dp[i-1][j] if p[j-2] == s[i-1] or


p[j-2] == '.' ).

Base Cases:

dp[0][0] = True (empty matches empty).

python

def isMatch(s, p):


m, n = len(s), len(p)
dp = [[False] * (n + 1) for _ in range(m + 1)]
dp[0][0] = True
for j in range(1, n + 1):
if p[j-1] == '*':
dp[0][j] = dp[0][j-2]
for i in range(1, m + 1):
for j in range(1, n + 1):
if p[j-1] == '.' or p[j-1] == s[i-1]:
dp[i][j] = dp[i-1][j-1]
elif p[j-1] == '*':
dp[i][j] = dp[i][j-2] or (dp[i-1][j] if p[j-2] == s[i-1] or p[j-2]

Printed with ChatGPT to PDF 106/128


== '.' else False)
return dp[m][n]

Each problem requires an understanding of how to set up and maintain the state transition,
recurrence relations, and base cases. Let me know if you want more clarification on any
specific problem!

Let's tackle these greedy problems by first creating a Greedy Template that can be applied to
most of these problems.

Greedy Problem-Solving Template

Key Steps to Approach Greedy Problems:

1. Identify the Greedy Choice:

Greedy algorithms make a sequence of choices, always choosing the most optimal
option available at each step.

To identify the greedy choice, examine the problem requirements and see if a locally
optimal choice at each step can lead to a globally optimal solution.

2. Define the Goal and Constraints:

Understand the goal of the problem (e.g., maximize, minimize, or achieve a certain
pattern).

Note any specific constraints that might influence which greedy choices are feasible.

3. Simulate the Solution Step-by-Step:

Start by solving for a small subset of the problem to validate your greedy approach.

Track how each choice impacts the solution and verify that it does not lead to
suboptimal results in future steps.

4. Optimize for Edge Cases:

Test for possible edge cases, such as empty arrays, minimum or maximum allowed
values, and identical values that might affect greedy choice effectiveness.

Printed with ChatGPT to PDF 107/128


Applying the Template to Solve Problems:

1. Maximum Subarray
Goal: Find the contiguous subarray with the maximum sum.

Greedy Choice: Start from the first element, and for each position, choose to either
extend the current subarray or start a new subarray.

Algorithm:

Track the maximum subarray sum seen so far ( max_sum ).

Keep a running sum ( current_sum ) and reset it to the current element if it becomes
negative.

python

def maxSubArray(nums):
max_sum = nums[0]
current_sum = nums[0]
for num in nums[1:]:
current_sum = max(num, current_sum + num)
max_sum = max(max_sum, current_sum)
return max_sum

2. Jump Game
Goal: Determine if you can reach the last index.

Greedy Choice: Track the farthest index you can reach as you traverse the array.

Algorithm:

Initialize the farthest index as 0 .

If the current index is within farthest , update farthest as max(farthest, i +


nums[i]) .

python

Printed with ChatGPT to PDF 108/128


def canJump(nums):
farthest = 0
for i in range(len(nums)):
if i > farthest:
return False
farthest = max(farthest, i + nums[i])
return True

3. Jump Game II
Goal: Find the minimum number of jumps to reach the last index.

Greedy Choice: Choose the farthest reach at each jump.

Algorithm:

Use jumps , end , and farthest to track the minimum jumps and update them
when reaching the current end .

python

def jump(nums):
jumps, end, farthest = 0, 0, 0
for i in range(len(nums) - 1):
farthest = max(farthest, i + nums[i])
if i == end:
jumps += 1
end = farthest
return jumps

4. Gas Station
Goal: Determine if you can complete a circuit around gas stations.

Greedy Choice: Calculate if the total gas is greater than or equal to the total cost. If not,
return -1 .

Printed with ChatGPT to PDF 109/128


Algorithm:

Track the surplus gas as you traverse the stations and reset when it goes negative.

python

def canCompleteCircuit(gas, cost):


total_tank, curr_tank, start = 0, 0, 0
for i in range(len(gas)):
total_tank += gas[i] - cost[i]
curr_tank += gas[i] - cost[i]
if curr_tank < 0:
start = i + 1
curr_tank = 0
return start if total_tank >= 0 else -1

5. Hand of Straights
Goal: Check if the cards can be grouped into hands of groupSize with consecutive
values.

Greedy Choice: Use the smallest available card as the starting point for each group.

Algorithm:

Use a min-heap or dictionary to track and arrange the cards into groups.

python

from collections import Counter


import heapq

def isNStraightHand(hand, groupSize):


if len(hand) % groupSize != 0:
return False
count = Counter(hand)
for card in sorted(count):
if count[card] > 0:
for i in range(groupSize):
if count[card + i] < count[card]:

Printed with ChatGPT to PDF 110/128


return False
count[card + i] -= count[card]
return True

6. Merge Triplets to Form Target Triplet


Goal: Determine if we can form a target triplet by merging valid triplets.

Greedy Choice: Accumulate the maximum of each component that matches the target.

Algorithm:

Only consider triplets that do not exceed the target triplet values.

python

def mergeTriplets(triplets, target):


good = set()
for triplet in triplets:
if all(triplet[i] <= target[i] for i in range(3)):
for i in range(3):
if triplet[i] == target[i]:
good.add(i)
return len(good) == 3

7. Partition Labels
Goal: Partition the string so that each character appears in only one part.

Greedy Choice: Find the farthest occurrence of each character in the string.

Algorithm:

Track the end index of the current partition, add the partition size when reaching
end , and reset.

python

Printed with ChatGPT to PDF 111/128


def partitionLabels(s):
last = {char: i for i, char in enumerate(s)}
start, end = 0, 0
result = []
for i, char in enumerate(s):
end = max(end, last[char])
if i == end:
result.append(i - start + 1)
start = i + 1
return result

8. Valid Parenthesis String


Goal: Determine if a string with ( , ) , and * is valid.

Greedy Choice: Track the minimum and maximum open parentheses as you traverse.

Algorithm:

Adjust min_open and max_open based on ( , ) , and * .

python

def checkValidString(s):
min_open, max_open = 0, 0
for char in s:
if char == '(':
min_open += 1
max_open += 1
elif char == ')':
min_open = max(min_open - 1, 0)
max_open -= 1
else:
min_open = max(min_open - 1, 0)
max_open += 1
if max_open < 0:
return False
return min_open == 0

Printed with ChatGPT to PDF 112/128


Each solution relies on making a locally optimal choice that will not compromise the overall
solution, adhering to the greedy approach. Let me know if you'd like further details on any
specific problem.

Interval problems often involve merging, partitioning, or analyzing the overlapping sections
of intervals in order to simplify, combine, or make optimal choices. A systematic approach
with sorting and merging can help handle most interval problems efficiently. Let’s create a
generalized template for interval problems, followed by applications for each problem
listed.

Interval Problem-Solving Template

Key Steps:

1. Sort Intervals:

Often, interval problems are simplified by sorting intervals by their starting points
(or sometimes by ending points).

Sorting helps in identifying overlaps and setting up consecutive intervals for


processing.

2. Iterate and Merge (or Process) Intervals:

Use a single iteration through the sorted intervals.

Depending on the problem, decide if intervals should be merged, counted, or


processed in specific ways.

3. Handle Overlapping and Non-Overlapping Logic:

Define conditions for when intervals overlap and when they don’t.

When two intervals overlap, decide whether to merge them or discard one based on
problem requirements.

4. Optimize with Data Structures if Necessary:

For problems needing real-time interval adjustments or queries, use structures like
min-heaps or segment trees.

Printed with ChatGPT to PDF 113/128


5. Edge Cases:

Handle cases with no intervals, single interval, intervals that fully overlap, or
intervals that barely overlap.

Applying the Template to Solve Problems:

1. Insert Interval
Goal: Insert a new interval into a list of sorted intervals and merge where necessary.

Approach:

Split into three cases: intervals before the new interval, overlapping intervals, and
intervals after the new interval.

python

def insert(intervals, new_interval):


result = []
for interval in intervals:
if interval[1] < new_interval[0]:
result.append(interval)
elif interval[0] > new_interval[1]:
result.append(new_interval)
new_interval = interval
else:
new_interval[0] = min(new_interval[0], interval[0])
new_interval[1] = max(new_interval[1], interval[1])
result.append(new_interval)
return result

2. Merge Intervals
Goal: Merge all overlapping intervals in a list.

Printed with ChatGPT to PDF 114/128


Approach:

Sort the intervals, then merge each interval with the previous one if they overlap.

python

def merge(intervals):
intervals.sort(key=lambda x: x[0])
merged = []
for interval in intervals:
if not merged or merged[-1][1] < interval[0]:
merged.append(interval)
else:
merged[-1][1] = max(merged[-1][1], interval[1])
return merged

3. Non-Overlapping Intervals
Goal: Find the minimum number of intervals to remove to make the rest non-
overlapping.

Approach:

Sort intervals by ending time and greedily keep track of non-overlapping intervals.

python

def eraseOverlapIntervals(intervals):
intervals.sort(key=lambda x: x[1])
count, end = 0, float('-inf')
for interval in intervals:
if interval[0] >= end:
end = interval[1]
else:
count += 1
return count

Printed with ChatGPT to PDF 115/128


4. Meeting Rooms
Goal: Determine if a person could attend all given meetings without overlapping.

Approach:

Sort by start times and check for overlaps by comparing consecutive intervals.

python

def canAttendMeetings(intervals):
intervals.sort(key=lambda x: x[0])
for i in range(1, len(intervals)):
if intervals[i][0] < intervals[i - 1][1]:
return False
return True

5. Meeting Rooms II
Goal: Find the minimum number of meeting rooms required for all meetings.

Approach:

Use a min-heap to track end times of ongoing meetings. Allocate a room when
needed and release one when a meeting ends.

python

import heapq

def minMeetingRooms(intervals):
if not intervals:
return 0
intervals.sort(key=lambda x: x[0])
heap = []
heapq.heappush(heap, intervals[0][1])
for interval in intervals[1:]:
if heap[0] <= interval[0]:
heapq.heappop(heap)

Printed with ChatGPT to PDF 116/128


heapq.heappush(heap, interval[1])
return len(heap)

6. Minimum Interval to Include Each Query


Goal: For each query, find the smallest interval that includes the query.

Approach:

Sort intervals and queries, then use a min-heap to track relevant intervals for each
query.

python

import heapq

def minInterval(intervals, queries):


intervals.sort()
queries_with_indices = sorted((q, i) for i, q in enumerate(queries))
result = [-1] * len(queries)
heap = []
i = 0

for q, qi in queries_with_indices:
while i < len(intervals) and intervals[i][0] <= q:
l, r = intervals[i]
heapq.heappush(heap, (r - l + 1, r))
i += 1
while heap and heap[0][1] < q:
heapq.heappop(heap)
if heap:
result[qi] = heap[0][0]

return result

Printed with ChatGPT to PDF 117/128


This set of solutions addresses a range of common interval-related questions, using the
template to identify and handle overlapping or sequential interval adjustments efficiently. If
you'd like a deeper explanation for any of these solutions, just let me know!

Math and geometry-related problems often rely on systematic transformations, arithmetic


manipulations, or geometric property calculations. Below is a template designed to tackle
common math and geometry problems, followed by implementations for each problem
listed.

Math & Geometry Problem-Solving Template

Key Steps:

1. Understand the Mathematical Properties or Transformations:

For problems involving matrices or geometry, understand transformations (rotation,


reflection).

For arithmetic-based problems, analyze properties (e.g., modular arithmetic, power


functions).

2. Break Down Complex Steps:

Divide large calculations into manageable parts. For instance, with matrix
manipulation, handle rows and columns independently.

For number manipulations, consider each digit separately or use appropriate data
structures.

3. Iterate and Apply Transformations or Calculations:

Use loops to apply transformations to each element (e.g., rotating a matrix cell by
cell).

For problems involving mathematical functions (e.g., exponentiation), apply efficient


methods like recursion or dynamic programming to reduce time complexity.

4. Edge Cases and Constraints:

Handle edge cases such as empty inputs, zero values, or negative numbers.

For geometry, consider matrix boundaries or overflow issues in arithmetic


operations.

Printed with ChatGPT to PDF 118/128


Applying the Template to Solve Problems:

1. Rotate Image
Goal: Rotate a square matrix by 90 degrees clockwise.

Approach:

Transpose the matrix, then reverse each row.

python

def rotate(matrix):
n = len(matrix)
for i in range(n):
for j in range(i, n):
matrix[i][j], matrix[j][i] = matrix[j][i], matrix[i][j]
for row in matrix:
row.reverse()

2. Spiral Matrix
Goal: Return all elements of an m x n matrix in spiral order.

Approach:

Use boundary pointers and adjust them after processing each row or column.

python

def spiralOrder(matrix):
result = []
left, right, top, bottom = 0, len(matrix[0]) - 1, 0, len(matrix) - 1
while left <= right and top <= bottom:
for i in range(left, right + 1):
result.append(matrix[top][i])
top += 1

Printed with ChatGPT to PDF 119/128


for i in range(top, bottom + 1):
result.append(matrix[i][right])
right -= 1
if top <= bottom:
for i in range(right, left - 1, -1):
result.append(matrix[bottom][i])
bottom -= 1
if left <= right:
for i in range(bottom, top - 1, -1):
result.append(matrix[i][left])
left += 1
return result

3. Set Matrix Zeroes


Goal: Set entire row and column to zero if an element is zero.

Approach:

Use the first row/column as markers to track zero rows and columns.

python

def setZeroes(matrix):
rows, cols = len(matrix), len(matrix[0])
row_zero = any(matrix[0][j] == 0 for j in range(cols))
col_zero = any(matrix[i][0] == 0 for i in range(rows))

for i in range(1, rows):


for j in range(1, cols):
if matrix[i][j] == 0:
matrix[i][0] = matrix[0][j] = 0

for i in range(1, rows):


for j in range(1, cols):
if matrix[i][0] == 0 or matrix[0][j] == 0:
matrix[i][j] = 0

if row_zero:
for j in range(cols):

Printed with ChatGPT to PDF 120/128


matrix[0][j] = 0
if col_zero:
for i in range(rows):
matrix[i][0] = 0

4. Happy Number
Goal: Determine if a number eventually reaches 1 when repeatedly replacing the
number with the sum of the squares of its digits.

Approach:

Use a set to detect cycles in the transformation.

python

def isHappy(n):
seen = set()
while n != 1 and n not in seen:
seen.add(n)
n = sum(int(digit)**2 for digit in str(n))
return n == 1

5. Plus One
Goal: Increment the number represented by an array of digits by one.

Approach:

Traverse digits backward, handling carry.

python

def plusOne(digits):
for i in reversed(range(len(digits))):
if digits[i] < 9:
digits[i] += 1

Printed with ChatGPT to PDF 121/128


return digits
digits[i] = 0
return [1] + digits

6. Pow(x, n)
Goal: Calculate x raised to the power n .

Approach:

Use fast exponentiation by dividing n in half recursively.

python

def myPow(x, n):


if n == 0:
return 1
if n < 0:
x, n = 1 / x, -n
half = myPow(x, n // 2)
return half * half if n % 2 == 0 else half * half * x

7. Multiply Strings
Goal: Multiply two non-negative integers represented as strings.

Approach:

Use grade-school multiplication and store intermediate results in an array.

python

def multiply(num1, num2):


result = [0] * (len(num1) + len(num2))
for i in range(len(num1) - 1, -1, -1):
for j in range(len(num2) - 1, -1, -1):
mul = int(num1[i]) * int(num2[j])

Printed with ChatGPT to PDF 122/128


sum_ = mul + result[i + j + 1]
result[i + j + 1] = sum_ % 10
result[i + j] += sum_ // 10
result = ''.join(map(str, result))
return result.lstrip('0') or '0'

8. Detect Squares
Goal: Add points and count squares that can be formed.

Approach:

Use a dictionary to store points and check valid squares based on distance.

python

from collections import defaultdict

class DetectSquares:

def __init__(self):
self.points_count = defaultdict(int)

def add(self, point):


x, y = point
self.points_count[(x, y)] += 1

def count(self, point):


x, y = point
count = 0
for (px, py), cnt in self.points_count.items():
if abs(x - px) != abs(y - py) or x == px or y == py:
continue
count += cnt * self.points_count.get((x, py), 0) *
self.points_count.get((px, y), 0)
return count

Printed with ChatGPT to PDF 123/128


This template addresses fundamental math and geometry principles to tackle each of these
problems effectively. By following the transformations, breaking down calculations, and
optimizing with appropriate structures, we can handle a wide variety of math and geometry
challenges. Let me know if you have any further questions!

Bit manipulation problems often involve using bitwise operators to achieve solutions with
optimized memory and time usage. These problems focus on leveraging properties of bits to
solve tasks that might be complex with standard arithmetic or loops. Below is a template for
approaching bit manipulation problems, followed by solutions for each of the listed
problems.

Bit Manipulation Problem-Solving Template

Key Steps:

1. Identify Bitwise Operations:

Recognize if bitwise operations like AND ( & ), OR ( | ), XOR ( ^ ), NOT ( ~ ), shift left
( << ), or shift right ( >> ) can simplify the problem.

XOR is especially useful for identifying unique elements, as a ^ a = 0 and a ^ 0 =


a.

2. Break Down the Problem:

For tasks involving counting bits or setting bits in a number, use loops to isolate
individual bits by shifting and masking.

3. Optimize Calculations with Bitwise Logic:

Use bitwise operations to avoid arithmetic operations for faster computation.

Bit shifts ( << , >> ) are useful for calculations that involve powers of two.

4. Consider Edge Cases:

Handle negative numbers carefully, as bitwise operations affect their binary


representation differently.

Check for overflow issues, especially in languages with fixed integer sizes.

Printed with ChatGPT to PDF 124/128


Applying the Template to Solve Problems:

1. Single Number
Goal: Find the single number in an array where every other number appears twice.

Approach:

Use XOR across all numbers, as duplicates will cancel out to 0.

python

def singleNumber(nums):
result = 0
for num in nums:
result ^= num
return result

2. Number of 1 Bits
Goal: Count the number of 1 s in the binary representation of an integer.

Approach:

Use bitwise AND with 1 and shift right to check each bit.

python

def hammingWeight(n):
count = 0
while n:
count += n & 1
n >>= 1
return count

Printed with ChatGPT to PDF 125/128


3. Counting Bits
Goal: Count the number of 1 s in binary representation for each number up to n .

Approach:

Use dynamic programming, where bits[i] = bits[i >> 1] + (i & 1) .

python

def countBits(n):
bits = [0] * (n + 1)
for i in range(1, n + 1):
bits[i] = bits[i >> 1] + (i & 1)
return bits

4. Reverse Bits
Goal: Reverse the bits of a 32-bit unsigned integer.

Approach:

Shift bits from the original number and add to the reversed number with left shift.

python

def reverseBits(n):
result = 0
for _ in range(32):
result = (result << 1) | (n & 1)
n >>= 1
return result

5. Missing Number
Goal: Find the missing number in a sequence from 0 to n .

Printed with ChatGPT to PDF 126/128


Approach:

XOR all indices and elements; the missing number will be left as duplicates cancel.

python

def missingNumber(nums):
result = len(nums)
for i, num in enumerate(nums):
result ^= i ^ num
return result

6. Sum of Two Integers


Goal: Find the sum of two integers without using + or - .

Approach:

Use XOR for addition without carry and AND for the carry bits. Repeat until no carry
remains.

python

def getSum(a, b):


MASK = 0xFFFFFFFF
MAX = 0x7FFFFFFF
while b != 0:
a, b = (a ^ b) & MASK, ((a & b) << 1) & MASK
return a if a <= MAX else ~(a ^ MASK)

7. Reverse Integer
Goal: Reverse the digits of an integer.

Approach:

Use modulus and division to extract and rebuild the integer.

Printed with ChatGPT to PDF 127/128


python

def reverse(x):
result, sign = 0, 1
if x < 0:
x, sign = -x, -1
while x:
result = result * 10 + x % 10
x //= 10
result *= sign
return result if -2**31 <= result <= 2**31 - 1 else 0

Explanation of Problem-Specific Solutions:


These solutions utilize the bitwise operations mentioned in the template to improve speed
and efficiency, especially in cases where bitwise operations can replace iterative or arithmetic
operations, reducing computational overhead. This template also emphasizes simplicity in
handling each bit directly, making it adaptable to similar bit manipulation problems.

If you have additional questions or need more examples, feel free to ask!

Printed with ChatGPT to PDF 128/128

You might also like