LeetCode Patterns Cheatsheet (TypeScript)
1. Two Pointers
Когда: отсортированный массив, нужно O(n), ищем пару/подмассив.
// Пример: сумма двух чисел = target
function twoSum(nums: number[], target: number): number[] {
let i = 0, j = nums.length - 1;
while (i < j) {
const sum = nums[i] + nums[j];
if (sum === target) return [i, j];
else if (sum < target) i++;
else j--;
}
return [];
}2. Sliding Window
Когда: подмассив/подстрока фиксированной или переменной длины, нужен максимум/минимум/условие.
// Пример: максимальная сумма подмассива длины k
function maxSum(nums: number[], k: number): number {
let sum = nums.slice(0, k).reduce((a, b) => a + b, 0);
let max = sum;
for (let i = k; i < nums.length; i++) {
sum += nums[i] - nums[i - k];
max = Math.max(max, sum);
}
return max;
}3. Binary Search
Когда: отсортированный массив, нужно O(log n).
function binarySearch(nums: number[], target: number): number {
let lo = 0, hi = nums.length - 1;
while (lo <= hi) {
const mid = (lo + hi) >> 1;
if (nums[mid] === target) return mid;
else if (nums[mid] < target) lo = mid + 1;
else hi = mid - 1;
}
return -1;
}4. Prefix Sum
Когда: много запросов суммы диапазона [i, j].
function buildPrefix(nums: number[]): number[] {
const prefix = [0];
for (const n of nums) prefix.push(prefix.at(-1)! + n);
return prefix;
}
// сумма [i, j] = prefix[j+1] - prefix[i]5. HashMap / HashSet
Когда: нужно считать частоту, проверять наличие, находить дубликаты за O(1).
// Пример: два числа дают target (неотсортированный массив)
function twoSumHash(nums: number[], target: number): number[] {
const map = new Map<number, number>();
for (let i = 0; i < nums.length; i++) {
const comp = target - nums[i];
if (map.has(comp)) return [map.get(comp)!, i];
map.set(nums[i], i);
}
return [];
}6. Monotonic Stack
Когда: “следующий больший/меньший элемент”, диапазоны, температуры.
// Пример: следующий больший элемент
function nextGreater(nums: number[]): number[] {
const result = new Array(nums.length).fill(-1);
const stack: number[] = []; // хранит индексы
for (let i = 0; i < nums.length; i++) {
while (stack.length && nums[i] > nums[stack.at(-1)!]) {
result[stack.pop()!] = nums[i];
}
stack.push(i);
}
return result;
}7. BFS (обход в ширину)
Когда: кратчайший путь в графе/матрице, обход по уровням дерева.
function bfs(graph: Map<number, number[]>, start: number): number[] {
const visited = new Set([start]);
const queue = [start];
const order: number[] = [];
while (queue.length) {
const node = queue.shift()!;
order.push(node);
for (const neighbor of graph.get(node) ?? []) {
if (!visited.has(neighbor)) {
visited.add(neighbor);
queue.push(neighbor);
}
}
}
return order;
}8. DFS (обход в глубину)
Когда: все пути, компоненты связности, комбинации/перестановки.
function dfs(graph: Map<number, number[]>, node: number, visited = new Set<number>()): void {
visited.add(node);
for (const neighbor of graph.get(node) ?? []) {
if (!visited.has(neighbor)) dfs(graph, neighbor, visited);
}
}9. Backtracking
Когда: все комбинации/перестановки/подмножества с ограничениями.
// Пример: все подмножества
function subsets(nums: number[]): number[][] {
const result: number[][] = [];
function bt(start: number, current: number[]) {
result.push([...current]);
for (let i = start; i < nums.length; i++) {
current.push(nums[i]);
bt(i + 1, current);
current.pop(); // откат
}
}
bt(0, []);
return result;
}10. Dynamic Programming
Когда: оптимальное решение строится из подзадач, есть перекрывающиеся состояния.
// Пример: монеты (coin change)
function coinChange(coins: number[], amount: number): number {
const dp = new Array(amount + 1).fill(Infinity);
dp[0] = 0;
for (let i = 1; i <= amount; i++) {
for (const coin of coins) {
if (coin <= i) dp[i] = Math.min(dp[i], dp[i - coin] + 1);
}
}
return dp[amount] === Infinity ? -1 : dp[amount];
}11. Heap / Priority Queue
Когда: k-й наибольший/наименьший элемент, слияние k отсортированных списков.
(В JS нет встроенного heap — используй библиотеку или реализуй минимальную.)
// Мини-heap вручную (min-heap через массив)
class MinHeap {
private h: number[] = [];
push(val: number) {
this.h.push(val);
this.h.sort((a, b) => a - b); // упрощённо; для O(log n) — sift up
}
pop(): number { return this.h.shift()!; }
peek(): number { return this.h[0]; }
size(): number { return this.h.length; }
}12. Fast & Slow Pointers (Floyd’s)
Когда: цикл в связном списке, середина списка.
// Пример: есть ли цикл
function hasCycle(head: ListNode | null): boolean {
let slow = head, fast = head;
while (fast && fast.next) {
slow = slow!.next;
fast = fast.next.next;
if (slow === fast) return true;
}
return false;
}Когда что использовать — быстрая шпаргалка
| Сигнал в задаче | Паттерн |
|---|---|
| Отсортированный массив + O(n) | Two Pointers |
| Подмассив/подстрока + max/min | Sliding Window |
| Отсортированный + O(log n) | Binary Search |
| Сумма диапазонов | Prefix Sum |
| Дубликаты, частота, O(1) поиск | HashMap/HashSet |
| Следующий больший/меньший | Monotonic Stack |
| Кратчайший путь, уровни | BFS |
| Все пути, компоненты | DFS |
| Все комбинации с откатом | Backtracking |
| Оптимум из подзадач | Dynamic Programming |
| k-й элемент, топ-k | Heap |
| Цикл в списке, середина | Fast & Slow Pointers |