DSA Problem Solving Patterns
DSA Problem Solving Patterns
1. Sliding Window
Concept:
Use a "window" of elements and slide it over the array to reduce repeated calculations.
When to use:
When you are asked to find something in a subarray, like max sum, longest substring,
etc.
How it works:
1. Start with a window of size k.
2. Slide it one step at a time (move left and right pointers).
3. Keep track of what you need (sum, length, etc.).
Example:
Find the maximum sum of a subarray of size 3 in the array [2, 1, 5, 1, 3, 2].
Code:
def max_sum_subarray(arr, k):
window_sum = sum(arr[:k])
max_sum = window_sum
return max_sum
return False
print(has_pair_with_sum([1, 2, 3, 4, 6], 6)) # Output: True
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
4. Merge Intervals
Concept:
Sort the intervals and merge them if they overlap.
When to use:
• Merging calendar events
• Finding total occupied time slots
How it works:
1. Sort by start time.
2. Check if the current interval overlaps with the previous.
3. If yes, merge them.
Example:
Merge [[1,3], [2,6], [8,10], [15,18]] into non-overlapping intervals.
Code:
def merge_intervals(intervals):
intervals.sort(key=lambda x: x[0])
merged = [intervals[0]]
return merged
print(merge_intervals([[1,3],[2,6],[8,10],[15,18]]))
# Output: [[1,6],[8,10],[15,18]]
5. Cyclic Sort
Concept:
Used to sort elements from 1 to n in-place in linear time.
When to use:
• Arrays with numbers from 1 to n
• Finding missing or duplicate numbers
How it works:
1. At index i, check if nums[i] == i+1.
2. If not, swap it with the correct position.
Example:
Find the missing number from [4, 3, 2, 7, 8, 2, 3, 1].
Code:
def find_missing_number(nums):
i=0
while i < len(nums):
correct = nums[i] - 1
if nums[i] > 0 and nums[i] <= len(nums) and nums[i] != nums[correct]:
nums[i], nums[correct] = nums[correct], nums[i]
else:
i += 1
# After sorting
for i in range(len(nums)):
if nums[i] != i + 1:
return i + 1
return -1
while curr:
next_node = curr.next
curr.next = prev
prev = curr
curr = next_node
queue = deque([root])
result = []
while queue:
level_size = len(queue)
level = []
for _ in range(level_size):
node = queue.popleft()
level.append(node.val)
if node.left:
queue.append(node.left)
if node.right:
queue.append(node.right)
result.append(level)
return result
9. Two Heaps
Concept:
Use a min-heap and max-heap to balance numbers for median in a stream.
When to use:
• Finding median of data stream
How it works:
1. One heap keeps smaller half (max heap)
2. Other heap keeps larger half (min heap)
3. Balance them after every insert
Example:
Add numbers and get median after each one.
Code:
import heapq
class MedianFinder:
def __init__(self):
self.small = [] # max heap (invert min heap)
self.large = [] # min heap
def find_median(self):
if len(self.small) > len(self.large):
return -self.small[0]
return (-self.small[0] + self.large[0]) / 2
10. Subsets
Concept:
Use recursion or backtracking to explore all combinations.
When to use:
• Generate all subsets
• Power set problems
How it works:
1. Include or exclude each element.
2. Use recursion to explore paths.
Example:
Find all subsets of [1, 2] → [[], [1], [2], [1,2]]
Code:
def subsets(nums):
result = []
backtrack(0, [])
return result
11. Modified Binary Search
Concept:
Binary search but adapted for rotated arrays, peaks, etc.
When to use:
• Rotated arrays
• Bitonic arrays
• Search in unknown patterns
How it works:
1. Use binary search logic
2. Check mid and decide which half to explore
Example:
Search in rotated sorted array [4,5,6,7,0,1,2] for 0
Code:
def search_rotated(arr, target):
left, right = 0, len(arr)-1
while queue:
node = queue.popleft()
result.append(node)
for neighbor in graph[node]:
in_degree[neighbor] -= 1
if in_degree[neighbor] == 0:
queue.append(neighbor)
return result