Reference patterns for search, optimization, and greedy algorithms. Covers binary search (classic and on-answer), two pointers, sliding window, greedy strategies, prefix sums, merge intervals, and monotonic stack. Use when solving problems that involve searching sorted data, optimizing over constraints, processing subarrays/substrings, or reducing range queries.
Provides reference patterns for search, optimization, and greedy algorithms when solving coding problems.
/plugin marketplace add sequenzia/agent-alchemy/plugin install agent-alchemy-cs-tools@agent-alchemyThis skill inherits all available tools. When active, it can use any tool Claude has access to.
This reference covers eight foundational search and optimization techniques that appear across competitive programming, coding interviews, and production algorithms. Each pattern includes recognition signals, a core idea summary, a Python template with type hints, key edge cases, and common mistakes.
| Trigger Signals | Technique | Typical Complexity |
|---|---|---|
| Sorted array, find target, O(log n) required | Binary Search | O(log n) |
| "Minimize the maximum", "find smallest feasible value" | Binary Search on Answer | O(n log S) where S = search space |
| Sorted array, find pair with target sum, in-place | Two Pointers | O(n) |
| Fixed/variable-length subarray, substring constraints | Sliding Window | O(n) |
| Local optimal leads to global optimal, exchange argument | Greedy | O(n log n) typical |
| Range sum queries, subarray sums, 2D region sums | Prefix Sums | O(n) build, O(1) query |
| Overlapping intervals, merge/insert, scheduling | Merge Intervals | O(n log n) |
| Next greater/smaller element, histogram areas | Monotonic Stack | O(n) |
When the problem does not immediately suggest a technique, use constraints as a guide:
Recognition Signals
Core Idea
Maintain a search interval [lo, hi] and halve it each step by comparing the midpoint against the target. The invariant is that the answer always lies within the current interval. Choose between lo = mid + 1 and hi = mid (or hi = mid - 1) depending on whether you want the leftmost or rightmost match.
Python Template
from bisect import bisect_left, bisect_right
from typing import List
def binary_search(nums: List[int], target: int) -> int:
"""Return index of target, or -1 if not found."""
lo, hi = 0, len(nums) - 1
while lo <= hi:
mid = lo + (hi - lo) // 2
if nums[mid] == target:
return mid
elif nums[mid] < target:
lo = mid + 1
else:
hi = mid - 1
return -1
def left_bound(nums: List[int], target: int) -> int:
"""Return index of first element >= target."""
return bisect_left(nums, target)
def right_bound(nums: List[int], target: int) -> int:
"""Return index of first element > target."""
return bisect_right(nums, target)
Key Edge Cases
lo + hi — use lo + (hi - lo) // 2.Common Mistakes
lo < hi when the loop should be lo <= hi (or vice versa).bisect_left returns len(nums).Recognition Signals
Core Idea
Instead of searching in the input array, binary search over the space of possible answers. Define a predicate feasible(x) -> bool that returns True if the answer x satisfies all constraints. The answer space is monotonic: once feasible becomes True, it stays True (or vice versa). Binary search finds the boundary.
Python Template
from typing import Callable, List
def binary_search_on_answer(
lo: int,
hi: int,
feasible: Callable[[int], bool],
) -> int:
"""Find the smallest value in [lo, hi] where feasible returns True."""
while lo < hi:
mid = lo + (hi - lo) // 2
if feasible(mid):
hi = mid
else:
lo = mid + 1
return lo
def can_split(nums: List[int], max_sum: int, k: int) -> bool:
"""Check if nums can be split into <= k subarrays each with sum <= max_sum."""
count, current = 1, 0
for x in nums:
if x > max_sum:
return False
if current + x > max_sum:
count += 1
current = x
else:
current += x
return count <= k
Key Edge Cases
lo < hi.lo should be the minimum possible answer, hi the maximum.Common Mistakes
Recognition Signals
Core Idea Place one pointer at each end of a sorted array (opposite direction) or both at the start (same direction). Move pointers inward based on how the current state compares to the target. Each pointer moves at most n times, giving O(n) total. The technique exploits sorted order to prune the search space without nested loops.
Python Template
from typing import List, Tuple, Optional
def two_sum_sorted(nums: List[int], target: int) -> Optional[Tuple[int, int]]:
"""Find indices of two numbers in sorted array that sum to target."""
lo, hi = 0, len(nums) - 1
while lo < hi:
s = nums[lo] + nums[hi]
if s == target:
return (lo, hi)
elif s < target:
lo += 1
else:
hi -= 1
return None
def remove_duplicates(nums: List[int]) -> int:
"""Remove duplicates in-place from sorted array. Return new length."""
if not nums:
return 0
write = 1
for read in range(1, len(nums)):
if nums[read] != nums[read - 1]:
nums[write] = nums[read]
write += 1
return write
Key Edge Cases
Common Mistakes
Recognition Signals
Core Idea
Maintain a window [left, right] over the array. Expand right to include more elements; shrink left when the window violates the constraint. Track the answer as the window slides. Fixed-size windows advance both pointers together. Variable-size windows expand until invalid, then contract until valid again.
Python Template
from collections import Counter
from typing import List
def max_sum_subarray(nums: List[int], k: int) -> int:
"""Maximum sum of any contiguous subarray of size k."""
n = len(nums)
if n < k:
return 0
window_sum = sum(nums[:k])
best = window_sum
for i in range(k, n):
window_sum += nums[i] - nums[i - k]
best = max(best, window_sum)
return best
def min_window_substring(s: str, t: str) -> str:
"""Shortest substring of s containing all characters of t."""
need = Counter(t)
missing = len(t)
left = 0
best_start, best_len = 0, len(s) + 1
for right, ch in enumerate(s):
if need[ch] > 0:
missing -= 1
need[ch] -= 1
while missing == 0:
if right - left + 1 < best_len:
best_start, best_len = left, right - left + 1
need[s[left]] += 1
if need[s[left]] > 0:
missing += 1
left += 1
return s[best_start : best_start + best_len] if best_len <= len(s) else ""
Key Edge Cases
Common Mistakes
Recognition Signals
Core Idea Make the locally optimal choice at each decision point without reconsidering previous choices. The key is proving correctness, typically via the exchange argument: assume an optimal solution differs from the greedy solution, then show you can swap one element to match the greedy choice without worsening the result. Sort the input by the criterion that enables the greedy choice.
Python Template
from typing import List, Tuple
def activity_selection(intervals: List[Tuple[int, int]]) -> List[Tuple[int, int]]:
"""Select maximum non-overlapping intervals. Each interval is (start, end)."""
intervals.sort(key=lambda x: x[1])
selected: List[Tuple[int, int]] = []
last_end = -1
for start, end in intervals:
if start >= last_end:
selected.append((start, end))
last_end = end
return selected
def min_platforms(arrivals: List[int], departures: List[int]) -> int:
"""Minimum platforms needed so no train waits (sweep line variant)."""
events: List[Tuple[int, int]] = []
for a in arrivals:
events.append((a, 1))
for d in departures:
events.append((d + 1, -1))
events.sort()
current = best = 0
for _, delta in events:
current += delta
best = max(best, current)
return best
Key Edge Cases
Common Mistakes
start == end for zero-length intervals.Recognition Signals
Core Idea
Build a prefix sum array where prefix[i] = sum of elements [0..i-1]. Any range sum [l..r] becomes prefix[r+1] - prefix[l] in O(1). For 2D, build a prefix sum matrix using inclusion-exclusion. Difference arrays are the inverse: store deltas, then reconstruct with a prefix sum after all updates.
Python Template
from typing import List
def build_prefix(nums: List[int]) -> List[int]:
"""Build 1D prefix sum array. prefix[i] = sum(nums[0:i])."""
prefix = [0] * (len(nums) + 1)
for i, x in enumerate(nums):
prefix[i + 1] = prefix[i] + x
return prefix
def range_sum(prefix: List[int], l: int, r: int) -> int:
"""Sum of nums[l..r] inclusive using prefix array."""
return prefix[r + 1] - prefix[l]
def subarray_sum_count(nums: List[int], k: int) -> int:
"""Count subarrays with sum exactly k."""
from collections import defaultdict
count = 0
current = 0
seen: dict[int, int] = defaultdict(int)
seen[0] = 1
for x in nums:
current += x
count += seen[current - k]
seen[current] += 1
return count
Key Edge Cases
[0].prefix[0] must be 0.Common Mistakes
prefix[r+1] - prefix[l], not prefix[r] - prefix[l-1] (unless 1-indexed).seen[0] = 1 in the hash map variant.Recognition Signals
Core Idea Sort intervals by start time. Iterate through and merge the current interval with the last merged interval if they overlap (current start <= last end). Otherwise, start a new group. For insertion, find the overlap region, merge, and concatenate the non-overlapping parts. The sweep-line variant converts intervals into +1/-1 events for counting concurrent overlaps.
Python Template
from typing import List, Tuple
def merge_intervals(intervals: List[Tuple[int, int]]) -> List[Tuple[int, int]]:
"""Merge all overlapping intervals. Each interval is (start, end)."""
if not intervals:
return []
intervals.sort()
merged: List[Tuple[int, int]] = [intervals[0]]
for start, end in intervals[1:]:
last_start, last_end = merged[-1]
if start <= last_end:
merged[-1] = (last_start, max(last_end, end))
else:
merged.append((start, end))
return merged
def insert_interval(
intervals: List[Tuple[int, int]],
new: Tuple[int, int],
) -> List[Tuple[int, int]]:
"""Insert new interval into sorted non-overlapping list and merge."""
result: List[Tuple[int, int]] = []
i, n = 0, len(intervals)
while i < n and intervals[i][1] < new[0]:
result.append(intervals[i])
i += 1
while i < n and intervals[i][0] <= new[1]:
new = (min(new[0], intervals[i][0]), max(new[1], intervals[i][1]))
i += 1
result.append(new)
result.extend(intervals[i:])
return result
Key Edge Cases
(1,3) and (3,5) — overlapping if using <=, not if using <.Common Mistakes
< instead of <= for overlap check (depends on whether endpoints are inclusive).max(last_end, end) when merging (the new interval might be contained).Recognition Signals
Core Idea Maintain a stack that is always monotonically increasing or decreasing. When a new element violates the monotonic property, pop elements from the stack — each popped element has found its "next greater" (or "next smaller") in the current element. Every element is pushed and popped at most once, giving O(n) amortized time. The stack stores indices rather than values to enable distance calculations.
Python Template
from typing import List
def next_greater_element(nums: List[int]) -> List[int]:
"""For each element, find the next greater element to its right. -1 if none."""
n = len(nums)
result = [-1] * n
stack: List[int] = [] # indices, monotonically decreasing values
for i in range(n):
while stack and nums[stack[-1]] < nums[i]:
result[stack.pop()] = nums[i]
stack.append(i)
return result
def largest_rectangle_histogram(heights: List[int]) -> int:
"""Largest rectangle area in a histogram."""
stack: List[int] = [] # indices, monotonically increasing heights
max_area = 0
heights_ext = heights + [0] # sentinel to flush remaining bars
for i, h in enumerate(heights_ext):
while stack and heights_ext[stack[-1]] > h:
height = heights_ext[stack.pop()]
width = i if not stack else i - stack[-1] - 1
max_area = max(max_area, height * width)
stack.append(i)
return max_area
Key Edge Cases
2 * n indices using modulo.Common Mistakes
Activates when the user asks about AI prompts, needs prompt templates, wants to search for prompts, or mentions prompts.chat. Use for discovering, retrieving, and improving prompts.
Search, retrieve, and install Agent Skills from the prompts.chat registry using MCP tools. Use when the user asks to find skills, browse skill catalogs, install a skill for Claude, or extend Claude's capabilities with reusable AI agent components.
This skill should be used when the user asks to "create an agent", "add an agent", "write a subagent", "agent frontmatter", "when to use description", "agent examples", "agent tools", "agent colors", "autonomous agent", or needs guidance on agent structure, system prompts, triggering conditions, or agent development best practices for Claude Code plugins.