From f40a10580274bbd8ff12f1c943e6cc54f0f8cab7 Mon Sep 17 00:00:00 2001 From: Bhavesh Khandelwal Date: Sun, 14 Dec 2025 10:24:37 +0530 Subject: [PATCH 1/2] Add time/space complexity analysis and pattern deep-dive guides Features: - Add timeComplexity and spaceComplexity fields to all 175 questions - Create comprehensive pattern guides for all 22 coding patterns - Add Pattern Guides tab in UI with interactive guide viewer - Display complexity columns in question table - Make pattern badges clickable linking to guides - Add script to automatically assign complexity based on patterns - Update README with new features documentation Each pattern guide includes: - Core concept explanation - When to use the pattern - Template code (Python & JavaScript) - Related problems section - Time & Space complexity analysis - Tips and best practices --- LOCAL_SETUP.md | 59 ++ PUSH_GUIDE.md | 149 +++++ README.md | 37 ++ guides/README.md | 40 ++ guides/arrays.md | 139 +++++ guides/backtracking.md | 196 +++++++ guides/bfs.md | 176 ++++++ guides/binary-search.md | 182 ++++++ guides/bit-manipulation.md | 143 +++++ guides/bucket-sort.md | 127 ++++ guides/design.md | 161 ++++++ guides/dfs.md | 189 ++++++ guides/dynamic-programming.md | 199 +++++++ guides/fast-slow-pointers.md | 159 +++++ guides/graph.md | 208 +++++++ guides/greedy.md | 149 +++++ guides/heap.md | 214 +++++++ guides/in-place-reversal-linked-list.md | 144 +++++ guides/intervals.md | 179 ++++++ guides/quickselect.md | 120 ++++ guides/sliding-window.md | 131 +++++ guides/sorting.md | 135 +++++ guides/topological-sort.md | 164 ++++++ guides/trie.md | 189 ++++++ guides/two-pointers.md | 153 +++++ guides/union-find.md | 172 ++++++ package-lock.json | 31 + scripts/add_complexity.py | 95 +++ src/components/PatternGuides/index.js | 135 +++++ src/components/PatternGuides/styles.scss | 99 ++++ src/components/Table/index.js | 74 ++- src/components/Tabs/index.js | 15 + src/data/questions.json | 700 +++++++++++++++++------ 33 files changed, 4885 insertions(+), 178 deletions(-) create mode 100644 LOCAL_SETUP.md create mode 100644 PUSH_GUIDE.md create mode 100644 guides/README.md create mode 100644 guides/arrays.md create mode 100644 guides/backtracking.md create mode 100644 guides/bfs.md create mode 100644 guides/binary-search.md create mode 100644 guides/bit-manipulation.md create mode 100644 guides/bucket-sort.md create mode 100644 guides/design.md create mode 100644 guides/dfs.md create mode 100644 guides/dynamic-programming.md create mode 100644 guides/fast-slow-pointers.md create mode 100644 guides/graph.md create mode 100644 guides/greedy.md create mode 100644 guides/heap.md create mode 100644 guides/in-place-reversal-linked-list.md create mode 100644 guides/intervals.md create mode 100644 guides/quickselect.md create mode 100644 guides/sliding-window.md create mode 100644 guides/sorting.md create mode 100644 guides/topological-sort.md create mode 100644 guides/trie.md create mode 100644 guides/two-pointers.md create mode 100644 guides/union-find.md create mode 100644 scripts/add_complexity.py create mode 100644 src/components/PatternGuides/index.js create mode 100644 src/components/PatternGuides/styles.scss diff --git a/LOCAL_SETUP.md b/LOCAL_SETUP.md new file mode 100644 index 00000000..2386020c --- /dev/null +++ b/LOCAL_SETUP.md @@ -0,0 +1,59 @@ +# Running LeetCode Patterns Locally + +## Prerequisites + +- **Node.js**: Version >= 22.2.0 (check with `node --version`) +- **npm**: Comes with Node.js (check with `npm --version`) + +If you don't have Node.js installed: +- Download from [nodejs.org](https://nodejs.org/) +- Or use [nvm](https://github.com/nvm-sh/nvm) to manage versions: + ```bash + nvm install 22.2.0 + nvm use 22.2.0 + ``` + +## Quick Start + +1. **Install dependencies** (if not already done): + ```bash + npm install + ``` + +2. **Start the development server**: + ```bash + npm start + ``` + +3. **Open your browser**: + - The app will automatically open at `http://localhost:3000` + - If it doesn't, manually navigate to that URL + +## Available Scripts + +- `npm start` - Starts the development server (runs on port 3000) +- `npm run build` - Creates a production build in the `build/` folder +- `npm run deploy` - Deploys the build to GitHub Pages (requires gh-pages setup) + +## Troubleshooting + +### Port 3000 already in use? +The app will prompt you to use a different port. Type `Y` to accept. + +### Dependencies issues? +Try deleting `node_modules` and `package-lock.json`, then reinstall: +```bash +rm -rf node_modules package-lock.json +npm install +``` + +### Pattern Guides not loading? +The Pattern Guides component fetches markdown files from GitHub. Make sure you have an internet connection. In production, these would be served from the `public/guides` directory. + +## Development Notes + +- The app uses React and is built with Create React App +- Hot reloading is enabled - changes will automatically refresh in the browser +- Check the browser console for any errors +- The main data file is `src/data/questions.json` + diff --git a/PUSH_GUIDE.md b/PUSH_GUIDE.md new file mode 100644 index 00000000..757bf8bc --- /dev/null +++ b/PUSH_GUIDE.md @@ -0,0 +1,149 @@ +# Guide to Push Changes and Create Pull Request + +## Step 1: Fork the Repository (if not done already) + +1. Go to https://github.com/seanprashad/leetcode-patterns +2. Click the "Fork" button in the top right +3. This creates a copy under your GitHub account (e.g., `https://github.com/YOUR_USERNAME/leetcode-patterns`) + +## Step 2: Add Your Fork as a Remote + +```bash +# Add your fork as a remote (replace YOUR_USERNAME with your GitHub username) +git remote add fork https://github.com/YOUR_USERNAME/leetcode-patterns.git + +# Verify remotes +git remote -v +# You should see: +# origin https://github.com/seanprashad/leetcode-patterns.git (original) +# fork https://github.com/YOUR_USERNAME/leetcode-patterns.git (your fork) +``` + +## Step 3: Create a Feature Branch + +```bash +# Create and switch to a new branch +git checkout -b add-complexity-and-pattern-guides + +# Or use a shorter name +git checkout -b feature/complexity-guides +``` + +## Step 4: Stage Your Changes + +```bash +# Add all changes +git add . + +# Or add specific files +git add README.md +git add src/data/questions.json +git add src/components/ +git add guides/ +git add scripts/ +git add LOCAL_SETUP.md +``` + +## Step 5: Commit Your Changes + +```bash +git commit -m "Add time/space complexity analysis and pattern deep-dive guides + +- Add timeComplexity and spaceComplexity fields to all 175 questions +- Create comprehensive pattern guides for all 22 coding patterns +- Add Pattern Guides tab in UI with interactive guide viewer +- Display complexity columns in question table +- Make pattern badges clickable linking to guides +- Add script to automatically assign complexity based on patterns +- Update README with new features documentation" +``` + +## Step 6: Push to Your Fork + +```bash +# Push to your fork (replace branch-name with your branch name) +git push fork add-complexity-and-pattern-guides + +# Or if you set upstream +git push -u fork add-complexity-and-pattern-guides +``` + +## Step 7: Create Pull Request + +1. Go to your fork on GitHub: `https://github.com/YOUR_USERNAME/leetcode-patterns` +2. You'll see a banner saying "Your recently pushed branches" with a "Compare & pull request" button +3. Click "Compare & pull request" +4. Fill in the PR details: + - **Title**: "Add Time/Space Complexity Analysis and Pattern Deep-Dive Guides" + - **Description**: + ``` + ## Summary + + This PR adds two major features to enhance the educational value of the LeetCode Patterns resource: + + ### 1. Time & Space Complexity Analysis + - Added `timeComplexity` and `spaceComplexity` fields to all 175 questions + - Complexity values reflect optimal pattern-based solutions + - Displayed in new columns in the question table + - Automatically assigned based on patterns using intelligent script + + ### 2. Pattern Deep-Dive Guides + - Created comprehensive guides for all 22 coding patterns + - Each guide includes: core concept, when to use, template code, related problems, complexity analysis + - New "Pattern Guides" tab in UI with interactive markdown viewer + - Pattern badges in table are now clickable and link to guides + + ## Files Changed + - Added 22 pattern guide markdown files in `/guides` + - Added complexity fields to `src/data/questions.json` + - Updated UI components to display complexity and pattern guides + - Added script for complexity assignment + + ## Testing + - All guides render correctly in the UI + - Complexity columns display properly + - Pattern badges link to correct guides + - No breaking changes to existing functionality + ``` +5. Click "Create pull request" + +## Alternative: Quick Push Commands + +If you want to do it all at once: + +```bash +# 1. Create branch +git checkout -b feature/complexity-guides + +# 2. Add all files +git add . + +# 3. Commit +git commit -m "Add time/space complexity analysis and pattern deep-dive guides" + +# 4. Push (after adding your fork remote) +git push -u fork feature/complexity-guides +``` + +## Troubleshooting + +### If you get "remote fork already exists" +```bash +git remote remove fork +git remote add fork https://github.com/YOUR_USERNAME/leetcode-patterns.git +``` + +### If you need to update your fork +```bash +git fetch origin +git merge origin/main +git push fork +``` + +### If you want to update your branch +```bash +git fetch origin +git rebase origin/main +git push fork --force-with-lease +``` + diff --git a/README.md b/README.md index 7b801b7a..5fb2ffd6 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,9 @@ - [Fundamentals](#fundamentals) - [Notes](#notes) - [Question List](#question-list) +- [New Features](#new-features) + - [Time & Space Complexity Analysis](#time--space-complexity-analysis) + - [Pattern Deep-Dive Guides](#pattern-deep-dive-guides) - [Solutions](#solutions) - [Suggestions](#suggestions) - [Acknowledgements](#acknowledgements) @@ -54,6 +57,40 @@ Other useful methods to know include [`substring()`](https://docs.oracle.com/jav The entire question list can be found here: https://seanprashad.com/leetcode-patterns/. +## New Features + +### Time & Space Complexity Analysis + +Each problem now includes **optimal time and space complexity** information. This helps you: +- Understand the efficiency of solutions +- Prioritize problems based on complexity +- Prepare for interview discussions about optimization + +The complexity values are displayed in the main question table and reflect the optimal pattern-based solution for each problem. + +### Pattern Deep-Dive Guides + +Comprehensive guides for each coding pattern are now available in the `/guides` directory. Each guide includes: + +- **Core Concept**: Clear explanation of what the pattern is and why it exists +- **When to Use**: Key problem characteristics that signal this pattern is applicable +- **Template Code**: Generalized code templates in Python and JavaScript showing the structure +- **Related Problems**: Links back to relevant problems in the main list +- **Time & Space Complexity**: Complexity analysis for the pattern +- **Tips & Best Practices**: Practical advice for applying the pattern + +Access the guides: +- Through the **Pattern Guides** tab in the web interface +- Directly in the repository: [`/guides`](./guides/) +- Pattern badges in the question table are clickable and link to their respective guides + +Available patterns: +- Arrays, BFS, Backtracking, Binary Search, Bit Manipulation, Bucket Sort +- DFS, Design, Dynamic Programming, Fast & Slow Pointers +- Graph, Greedy, Heap, In-place reversal of a linked list +- Intervals, QuickSelect, Sliding Window, Sorting +- Topological Sort, Trie, Two Pointers, Union Find + ## Solutions Solutions written in Java can be found in the [solutions] branch. diff --git a/guides/README.md b/guides/README.md new file mode 100644 index 00000000..af9a9635 --- /dev/null +++ b/guides/README.md @@ -0,0 +1,40 @@ +# Pattern Deep-Dive Guides + +This directory contains comprehensive guides for each coding pattern used in LeetCode problems. Each guide explains the core concept, when to use the pattern, provides template code, and links to relevant problems. + +## Available Patterns + +- [Arrays](./arrays.md) +- [BFS (Breadth-First Search)](./bfs.md) +- [Backtracking](./backtracking.md) +- [Binary Search](./binary-search.md) +- [Bit Manipulation](./bit-manipulation.md) +- [Bucket Sort](./bucket-sort.md) +- [DFS (Depth-First Search)](./dfs.md) +- [Design](./design.md) +- [Dynamic Programming](./dynamic-programming.md) +- [Fast & Slow Pointers](./fast-slow-pointers.md) +- [Graph](./graph.md) +- [Greedy](./greedy.md) +- [Heap](./heap.md) +- [In-place reversal of a linked list](./in-place-reversal-linked-list.md) +- [Intervals](./intervals.md) +- [QuickSelect](./quickselect.md) +- [Sliding Window](./sliding-window.md) +- [Sorting](./sorting.md) +- [Topological Sort](./topological-sort.md) +- [Trie](./trie.md) +- [Two Pointers](./two-pointers.md) +- [Union Find](./union-find.md) + +## How to Use These Guides + +1. **Start with the Core Concept**: Understand what the pattern is and why it exists +2. **Learn When to Use It**: Identify the problem characteristics that signal this pattern +3. **Study the Template**: Review the skeleton code to understand the structure +4. **Practice with Problems**: Work through the linked problems to reinforce your understanding + +## Contributing + +If you find any errors or have suggestions for improving these guides, please open an issue or submit a pull request! + diff --git a/guides/arrays.md b/guides/arrays.md new file mode 100644 index 00000000..418a77bd --- /dev/null +++ b/guides/arrays.md @@ -0,0 +1,139 @@ +# Arrays Pattern + +## Core Concept + +Arrays are fundamental data structures that store elements in contiguous memory locations. While arrays themselves aren't a "pattern" per se, many array manipulation techniques form the foundation for other patterns. This guide covers common array techniques and problem-solving approaches. + +## When to Use + +Array techniques are used in: + +- **Index manipulation**: Accessing, updating, or rearranging elements +- **In-place operations**: Modifying arrays without extra space +- **Prefix/Suffix arrays**: Precomputing cumulative values +- **Array rotation**: Rotating or shifting elements +- **Partitioning**: Dividing arrays into segments + +### Problem Indicators + +- "Given an array..." +- "Rearrange elements..." +- "Find missing/duplicate numbers" +- "Rotate array by k positions" +- "Partition array around pivot" + +## Template Code + +### In-place Array Modification + +```python +def in_place_modification(arr): + # Two pointers for in-place operations + write_idx = 0 + + for read_idx in range(len(arr)): + if should_keep(arr[read_idx]): + arr[write_idx] = arr[read_idx] + write_idx += 1 + + return write_idx # New length +``` + +### Array Rotation + +```python +def rotate_array(arr, k): + n = len(arr) + k = k % n # Handle k > n + + # Reverse entire array + arr.reverse() + + # Reverse first k elements + arr[:k] = reversed(arr[:k]) + + # Reverse remaining elements + arr[k:] = reversed(arr[k:]) +``` + +### Prefix Sum + +```python +def prefix_sum(arr): + prefix = [0] * (len(arr) + 1) + + for i in range(len(arr)): + prefix[i + 1] = prefix[i] + arr[i] + + return prefix + +# Query range sum [i, j] +def range_sum(prefix, i, j): + return prefix[j + 1] - prefix[i] +``` + +### JavaScript Template + +```javascript +function inPlaceModification(arr) { + let writeIdx = 0; + + for (let readIdx = 0; readIdx < arr.length; readIdx++) { + if (shouldKeep(arr[readIdx])) { + arr[writeIdx] = arr[readIdx]; + writeIdx++; + } + } + + return writeIdx; +} + +function rotateArray(arr, k) { + const n = arr.length; + k = k % n; + + reverse(arr, 0, n - 1); + reverse(arr, 0, k - 1); + reverse(arr, k, n - 1); +} + +function reverse(arr, start, end) { + while (start < end) { + [arr[start], arr[end]] = [arr[end], arr[start]]; + start++; + end--; + } +} +``` + +## Common Techniques + +1. **Two Pointers**: Often combined with arrays +2. **Sliding Window**: Subarray problems +3. **Hash Map**: Tracking frequencies or indices +4. **Sorting**: Many array problems benefit from sorting first +5. **Binary Search**: On sorted arrays + +## Related Problems + +View all Arrays problems in the [main question list](https://seanprashad.com/leetcode-patterns/). + +Common problems include: +- Contains Duplicate +- Missing Number +- Product of Array Except Self +- Rotate Array +- Move Zeroes + +## Time & Space Complexity + +- **Time Complexity**: Varies by technique - O(n) for linear scans, O(n log n) if sorting needed +- **Space Complexity**: O(1) for in-place operations, O(n) if creating new arrays + +## Tips + +1. **Consider in-place**: Many problems can be solved without extra space +2. **Use indices wisely**: Array indices are powerful for tracking state +3. **Handle edge cases**: Empty arrays, single element, all same values +4. **Modulo for rotation**: Use modulo to handle k > array length + diff --git a/guides/backtracking.md b/guides/backtracking.md new file mode 100644 index 00000000..05f57ef3 --- /dev/null +++ b/guides/backtracking.md @@ -0,0 +1,196 @@ +# Backtracking Pattern + +## Core Concept + +Backtracking is a systematic method for solving problems by trying partial solutions and then abandoning them ("backtracking") if they cannot lead to a valid complete solution. It's essentially a refined brute force approach that prunes invalid paths early. + +The key idea is to: +1. Make a choice +2. Recursively solve with that choice +3. Undo the choice (backtrack) if it doesn't lead to a solution +4. Try the next choice + +## When to Use + +Use Backtracking when: + +- **Constraint satisfaction**: Problems with constraints that must be satisfied +- **Generate all solutions**: Finding all possible combinations/permutations +- **Decision tree**: Problem can be represented as a decision tree +- **Pruning possible**: Can eliminate invalid paths early +- **Exhaustive search needed**: Need to explore all possibilities + +### Problem Indicators + +- "Find all combinations/permutations" +- "Generate all possible..." +- "Find all valid..." +- "N-Queens problem" +- "Sudoku solver" +- "Word search" +- "Subset generation" + +## Template Code + +### Standard Backtracking Template + +```python +def backtrack(candidates, path, result): + # Base case: solution found + if is_solution(path): + result.append(path[:]) # Make a copy + return + + # Try all candidates + for candidate in candidates: + # Make choice + if is_valid(candidate, path): + path.append(candidate) + + # Recurse + backtrack(candidates, path, result) + + # Backtrack (undo choice) + path.pop() +``` + +### Generate All Subsets + +```python +def subsets(nums): + result = [] + + def backtrack(start, path): + result.append(path[:]) + + for i in range(start, len(nums)): + path.append(nums[i]) + backtrack(i + 1, path) + path.pop() + + backtrack(0, []) + return result +``` + +### Generate All Permutations + +```python +def permutations(nums): + result = [] + used = [False] * len(nums) + + def backtrack(path): + if len(path) == len(nums): + result.append(path[:]) + return + + for i in range(len(nums)): + if not used[i]: + used[i] = True + path.append(nums[i]) + backtrack(path) + path.pop() + used[i] = False + + backtrack([]) + return result +``` + +### N-Queens Problem + +```python +def solve_n_queens(n): + result = [] + board = [['.'] * n for _ in range(n)] + + def is_safe(row, col): + # Check column + for i in range(row): + if board[i][col] == 'Q': + return False + + # Check diagonals + for i, j in zip(range(row - 1, -1, -1), range(col - 1, -1, -1)): + if board[i][j] == 'Q': + return False + + for i, j in zip(range(row - 1, -1, -1), range(col + 1, n)): + if board[i][j] == 'Q': + return False + + return True + + def backtrack(row): + if row == n: + result.append([''.join(row) for row in board]) + return + + for col in range(n): + if is_safe(row, col): + board[row][col] = 'Q' + backtrack(row + 1) + board[row][col] = '.' + + backtrack(0) + return result +``` + +### JavaScript Template + +```javascript +function backtrack(candidates, path, result) { + if (isSolution(path)) { + result.push([...path]); + return; + } + + for (const candidate of candidates) { + if (isValid(candidate, path)) { + path.push(candidate); + backtrack(candidates, path, result); + path.pop(); // Backtrack + } + } +} +``` + +## Common Variations + +1. **Pruning**: Skip invalid candidates early +2. **Memoization**: Cache results to avoid recomputation +3. **Early termination**: Stop when first solution found +4. **Constraint propagation**: Use constraints to reduce search space + +## Related Problems + +View all Backtracking problems in the [main question list](https://seanprashad.com/leetcode-patterns/). + +Common problems include: +- Subsets +- Permutations +- Combination Sum +- N-Queens +- Sudoku Solver +- Word Search +- Generate Parentheses + +## Time & Space Complexity + +- **Time Complexity**: Often exponential O(2^n) or O(n!) depending on problem +- **Space Complexity**: O(n) for recursion stack, plus space for storing results + +## Optimization Techniques + +1. **Pruning**: Eliminate invalid paths early +2. **Memoization**: Cache computed results +3. **Constraint checking**: Check constraints before recursing +4. **Early termination**: Return immediately when solution found (if only one needed) + +## Tips + +1. **Identify constraints**: Understand what makes a solution valid +2. **Choose representation**: Pick efficient data structures for state +3. **Prune early**: Check validity before recursing +4. **Undo changes**: Always restore state after backtracking +5. **Base case**: Clearly define when to stop recursing + diff --git a/guides/bfs.md b/guides/bfs.md new file mode 100644 index 00000000..c25e2c03 --- /dev/null +++ b/guides/bfs.md @@ -0,0 +1,176 @@ +# BFS (Breadth-First Search) Pattern + +## Core Concept + +Breadth-First Search (BFS) is a graph traversal algorithm that explores all nodes at the current depth level before moving to nodes at the next depth level. BFS uses a queue data structure to maintain the order of nodes to visit. + +Key characteristics: +- Explores level by level +- Guarantees shortest path in unweighted graphs +- Uses queue (FIFO - First In First Out) +- Visits all nodes at distance k before nodes at distance k+1 + +## When to Use + +Use BFS when: + +- **Level-order traversal**: Need to process nodes level by level +- **Shortest path**: Finding shortest path in unweighted graphs +- **Level information**: Need to know which level/depth a node is at +- **Closest nodes first**: Want to visit closest nodes before distant ones +- **Tree/Graph traversal**: Traversing trees or graphs level by level + +### Problem Indicators + +- "Level order traversal" +- "Shortest path" +- "Minimum steps to reach target" +- "Find nodes at distance k" +- "Clone graph" +- "Word ladder" + +## Template Code + +### BFS on Tree + +```python +from collections import deque + +def bfs_tree(root): + if not root: + return [] + + result = [] + queue = deque([root]) + + 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 +``` + +### BFS on Graph + +```python +from collections import deque + +def bfs_graph(start, graph): + visited = set() + queue = deque([start]) + visited.add(start) + + while queue: + node = queue.popleft() + + # Process node + process(node) + + # Visit neighbors + for neighbor in graph[node]: + if neighbor not in visited: + visited.add(neighbor) + queue.append(neighbor) +``` + +### Shortest Path (Unweighted Graph) + +```python +from collections import deque + +def shortest_path(start, target, graph): + if start == target: + return 0 + + queue = deque([(start, 0)]) # (node, distance) + visited = {start} + + while queue: + node, distance = queue.popleft() + + for neighbor in graph[node]: + if neighbor == target: + return distance + 1 + + if neighbor not in visited: + visited.add(neighbor) + queue.append((neighbor, distance + 1)) + + return -1 # Not reachable +``` + +### JavaScript Template + +```javascript +function bfsTree(root) { + if (!root) return []; + + const result = []; + const queue = [root]; + + while (queue.length > 0) { + const levelSize = queue.length; + const level = []; + + for (let i = 0; i < levelSize; i++) { + const node = queue.shift(); + level.push(node.val); + + if (node.left) queue.push(node.left); + if (node.right) queue.push(node.right); + } + + result.push(level); + } + + return result; +} +``` + +## Common Variations + +1. **Level-order with levels**: Track which level each node belongs to +2. **Bidirectional BFS**: Start from both source and target +3. **Multi-source BFS**: Start from multiple nodes simultaneously +4. **BFS with state**: Track additional state (e.g., keys collected) + +## Related Problems + +View all BFS problems in the [main question list](https://seanprashad.com/leetcode-patterns/). + +Common problems include: +- Binary Tree Level Order Traversal +- Shortest Path in Binary Matrix +- Word Ladder +- Clone Graph +- Rotting Oranges +- Perfect Squares + +## Time & Space Complexity + +- **Time Complexity**: O(V + E) where V is vertices and E is edges +- **Space Complexity**: O(V) for queue and visited set + +## BFS vs DFS + +- **BFS**: Uses queue, explores level by level, finds shortest path +- **DFS**: Uses stack (recursion), explores depth first, uses less memory + +## Tips + +1. **Use deque**: Python's `collections.deque` is efficient for queue operations +2. **Track levels**: Use level size to process nodes level by level +3. **Mark visited**: Always mark nodes as visited when adding to queue +4. **Handle cycles**: Use visited set to avoid revisiting nodes in graphs + diff --git a/guides/binary-search.md b/guides/binary-search.md new file mode 100644 index 00000000..492f2340 --- /dev/null +++ b/guides/binary-search.md @@ -0,0 +1,182 @@ +# Binary Search Pattern + +## Core Concept + +Binary Search is a divide-and-conquer algorithm that efficiently searches for a target in a sorted array by repeatedly dividing the search space in half. The key requirement is that the array must be sorted (or have some ordering property). + +Binary Search can be applied beyond simple search: +- Finding boundaries (first/last occurrence) +- Search in rotated arrays +- Finding peak elements +- Search in 2D matrices + +## When to Use + +Use Binary Search when: + +- **Sorted data**: Array is sorted (or can be sorted) +- **Search optimization**: Need O(log n) instead of O(n) linear search +- **Boundary finding**: Finding first/last occurrence, insertion point +- **Monotonic property**: Problem has a monotonic property (increasing/decreasing) +- **Optimization problems**: Finding minimum/maximum value satisfying condition + +### Problem Indicators + +- "Find target in sorted array" +- "Find first/last occurrence" +- "Search in rotated sorted array" +- "Find peak element" +- "Find minimum/maximum satisfying condition" + +## Template Code + +### Standard Binary Search + +```python +def binary_search(arr, target): + left, right = 0, len(arr) - 1 + + while left <= right: + mid = left + (right - left) // 2 + + if arr[mid] == target: + return mid + elif arr[mid] < target: + left = mid + 1 + else: + right = mid - 1 + + return -1 # Not found +``` + +### Finding Left Boundary (First Occurrence) + +```python +def find_left_boundary(arr, target): + left, right = 0, len(arr) - 1 + result = -1 + + while left <= right: + mid = left + (right - left) // 2 + + if arr[mid] == target: + result = mid + right = mid - 1 # Continue searching left + elif arr[mid] < target: + left = mid + 1 + else: + right = mid - 1 + + return result +``` + +### Finding Right Boundary (Last Occurrence) + +```python +def find_right_boundary(arr, target): + left, right = 0, len(arr) - 1 + result = -1 + + while left <= right: + mid = left + (right - left) // 2 + + if arr[mid] == target: + result = mid + left = mid + 1 # Continue searching right + elif arr[mid] < target: + left = mid + 1 + else: + right = mid - 1 + + return result +``` + +### Search in Rotated Array + +```python +def search_rotated(arr, target): + left, right = 0, len(arr) - 1 + + while left <= right: + mid = left + (right - left) // 2 + + if arr[mid] == target: + return mid + + # Left half is sorted + if arr[left] <= arr[mid]: + if arr[left] <= target < arr[mid]: + right = mid - 1 + else: + left = mid + 1 + # Right half is sorted + else: + if arr[mid] < target <= arr[right]: + left = mid + 1 + else: + right = mid - 1 + + return -1 +``` + +### JavaScript Template + +```javascript +function binarySearch(arr, target) { + let left = 0; + let right = arr.length - 1; + + while (left <= right) { + const mid = Math.floor(left + (right - left) / 2); + + if (arr[mid] === target) { + return mid; + } else if (arr[mid] < target) { + left = mid + 1; + } else { + right = mid - 1; + } + } + + return -1; +} +``` + +## Common Variations + +1. **Search Space Reduction**: Binary search on answer (e.g., finding minimum capacity) +2. **2D Binary Search**: Searching in sorted 2D matrix +3. **Finding Peak**: Using binary search to find peak element +4. **Square Root**: Using binary search for numerical problems + +## Related Problems + +View all Binary Search problems in the [main question list](https://seanprashad.com/leetcode-patterns/). + +Common problems include: +- Binary Search +- Search in Rotated Sorted Array +- Find First and Last Position +- Search a 2D Matrix +- Find Peak Element +- Sqrt(x) + +## Time & Space Complexity + +- **Time Complexity**: O(log n) - halves search space each iteration +- **Space Complexity**: O(1) for iterative, O(log n) for recursive + +## Important Notes + +1. **Avoid overflow**: Use `left + (right - left) // 2` instead of `(left + right) // 2` +2. **Boundary conditions**: Carefully handle `<=` vs `<` in while condition +3. **Update bounds**: Always move `left = mid + 1` or `right = mid - 1` to avoid infinite loops +4. **Edge cases**: Empty array, single element, target not found + +## Tips + +1. **Identify sorted property**: Look for sorted or partially sorted data +2. **Monotonic function**: If problem has monotonic property, binary search may apply +3. **Template selection**: Choose appropriate template (standard, left boundary, right boundary) +4. **Test boundaries**: Always test with edge cases + diff --git a/guides/bit-manipulation.md b/guides/bit-manipulation.md new file mode 100644 index 00000000..69cabd47 --- /dev/null +++ b/guides/bit-manipulation.md @@ -0,0 +1,143 @@ +# Bit Manipulation Pattern + +## Core Concept + +Bit Manipulation involves directly manipulating bits (0s and 1s) in binary representations of numbers. This technique can lead to highly efficient solutions by leveraging bitwise operations. + +Common bitwise operations: +- **AND (&)**: Both bits must be 1 +- **OR (|)**: At least one bit must be 1 +- **XOR (^)**: Bits must be different +- **NOT (~)**: Flip all bits +- **Left Shift (<<)**: Multiply by 2 +- **Right Shift (>>)**: Divide by 2 + +## When to Use + +Use Bit Manipulation when: + +- **Efficient operations**: Need O(1) operations on bits +- **Space optimization**: Represent sets/arrays as bits +- **Power of 2**: Problems involving powers of 2 +- **Counting bits**: Problems about bit counts +- **Set operations**: Efficient set union/intersection + +### Problem Indicators + +- "Single number" (find unique element) +- "Power of two" +- "Count set bits" +- "Bitwise operations" +- "Set representation" +- "XOR problems" + +## Template Code + +### Basic Bit Operations + +```python +# Check if bit is set +def is_bit_set(num, pos): + return (num >> pos) & 1 == 1 + +# Set a bit +def set_bit(num, pos): + return num | (1 << pos) + +# Clear a bit +def clear_bit(num, pos): + return num & ~(1 << pos) + +# Toggle a bit +def toggle_bit(num, pos): + return num ^ (1 << pos) + +# Count set bits +def count_set_bits(num): + count = 0 + while num: + count += num & 1 + num >>= 1 + return count + +# Count set bits (Brian Kernighan's algorithm) +def count_set_bits_fast(num): + count = 0 + while num: + num &= num - 1 # Clear least significant set bit + count += 1 + return count +``` + +### Find Single Number (XOR) + +```python +def single_number(nums): + result = 0 + for num in nums: + result ^= num + return result +``` + +### Power of Two + +```python +def is_power_of_two(n): + return n > 0 and (n & (n - 1)) == 0 +``` + +### JavaScript Template + +```javascript +function isBitSet(num, pos) { + return ((num >> pos) & 1) === 1; +} + +function setBit(num, pos) { + return num | (1 << pos); +} + +function clearBit(num, pos) { + return num & ~(1 << pos); +} + +function countSetBits(num) { + let count = 0; + while (num) { + count += num & 1; + num >>= 1; + } + return count; +} +``` + +## Common Tricks + +1. **XOR properties**: `a ^ a = 0`, `a ^ 0 = a`, `a ^ b ^ a = b` +2. **Power of 2**: `n & (n - 1) == 0` if n is power of 2 +3. **Get rightmost set bit**: `n & -n` +4. **Clear rightmost set bit**: `n & (n - 1)` + +## Related Problems + +View all Bit Manipulation problems in the [main question list](https://seanprashad.com/leetcode-patterns/). + +Common problems include: +- Single Number +- Number of 1 Bits +- Power of Two +- Missing Number +- Reverse Bits + +## Time & Space Complexity + +- **Time Complexity**: Often O(1) for single operations, O(n) for array traversal +- **Space Complexity**: O(1) - operations are in-place + +## Tips + +1. **Know bitwise operations**: Master AND, OR, XOR, shifts +2. **XOR for duplicates**: XOR cancels duplicates +3. **Power of 2 trick**: `n & (n - 1) == 0` +4. **Bit masks**: Use masks to extract specific bits + diff --git a/guides/bucket-sort.md b/guides/bucket-sort.md new file mode 100644 index 00000000..be4670d3 --- /dev/null +++ b/guides/bucket-sort.md @@ -0,0 +1,127 @@ +# Bucket Sort Pattern + +## Core Concept + +Bucket Sort distributes elements into buckets, sorts each bucket, and concatenates the results. It's efficient when: +- Input is uniformly distributed +- Range of values is known +- Need O(n) average time complexity + +## When to Use + +Use Bucket Sort when: + +- **Uniform distribution**: Input values are uniformly distributed +- **Known range**: Range of values is known +- **Linear time needed**: Need O(n) average time +- **Floating point**: Sorting floating point numbers in range [0, 1) + +### Problem Indicators + +- "Sort with O(n) time" +- "Uniformly distributed values" +- "Floating point sorting" +- "Top K frequent" (can use bucket sort) + +## Template Code + +### Basic Bucket Sort + +```python +def bucket_sort(arr): + if not arr: + return arr + + # Find min and max + min_val = min(arr) + max_val = max(arr) + + # Create buckets + num_buckets = len(arr) + buckets = [[] for _ in range(num_buckets)] + + # Distribute elements into buckets + for num in arr: + index = int((num - min_val) / (max_val - min_val + 1) * num_buckets) + buckets[index].append(num) + + # Sort each bucket and concatenate + result = [] + for bucket in buckets: + bucket.sort() + result.extend(bucket) + + return result +``` + +### Top K Frequent (Bucket Sort Approach) + +```python +def top_k_frequent(nums, k): + from collections import Counter + + # Count frequencies + count = Counter(nums) + + # Create buckets (index = frequency) + buckets = [[] for _ in range(len(nums) + 1)] + for num, freq in count.items(): + buckets[freq].append(num) + + # Collect top k + result = [] + for i in range(len(buckets) - 1, -1, -1): + result.extend(buckets[i]) + if len(result) >= k: + break + + return result[:k] +``` + +### JavaScript Template + +```javascript +function bucketSort(arr) { + if (arr.length === 0) return arr; + + const min = Math.min(...arr); + const max = Math.max(...arr); + const numBuckets = arr.length; + const buckets = Array.from({ length: numBuckets }, () => []); + + for (const num of arr) { + const index = Math.floor((num - min) / (max - min + 1) * numBuckets); + buckets[index].push(num); + } + + const result = []; + for (const bucket of buckets) { + bucket.sort((a, b) => a - b); + result.push(...bucket); + } + + return result; +} +``` + +## Related Problems + +View all Bucket Sort problems in the [main question list](https://seanprashad.com/leetcode-patterns/). + +Common problems include: +- Top K Frequent Elements +- Sort Colors (counting sort variant) +- Maximum Gap + +## Time & Space Complexity + +- **Time Complexity**: O(n) average, O(n²) worst case +- **Space Complexity**: O(n) for buckets + +## Tips + +1. **Uniform distribution**: Works best with uniform distribution +2. **Bucket count**: Choose appropriate number of buckets +3. **Bucket sorting**: Use efficient sort for each bucket +4. **Frequency problems**: Useful for top K frequent problems + diff --git a/guides/design.md b/guides/design.md new file mode 100644 index 00000000..29d2f5c2 --- /dev/null +++ b/guides/design.md @@ -0,0 +1,161 @@ +# Design Pattern + +## Core Concept + +Design problems require implementing data structures or systems with specific operations and constraints. These problems test your ability to: +- Choose appropriate data structures +- Design efficient APIs +- Handle edge cases +- Optimize for time/space complexity + +## When to Use + +Design problems appear when: + +- **System design**: Designing systems with multiple operations +- **Data structure design**: Implementing custom data structures +- **API design**: Creating interfaces with specific operations +- **Caching**: Implementing cache systems +- **Concurrency**: Thread-safe data structures + +### Problem Indicators + +- "Design..." +- "Implement..." +- "LRU Cache" +- "Trie" +- "Time-based key-value store" +- "Design Twitter" + +## Template Code + +### LRU Cache + +```python +from collections import OrderedDict + +class LRUCache: + def __init__(self, capacity): + self.cache = OrderedDict() + self.capacity = capacity + + def get(self, key): + if key not in self.cache: + return -1 + + # Move to end (most recently used) + self.cache.move_to_end(key) + return self.cache[key] + + def put(self, key, value): + if key in self.cache: + # Update existing + self.cache.move_to_end(key) + else: + # Check capacity + if len(self.cache) >= self.capacity: + # Remove least recently used (first item) + self.cache.popitem(last=False) + + self.cache[key] = value +``` + +### Design HashMap + +```python +class MyHashMap: + def __init__(self): + self.size = 1000 + self.buckets = [[] for _ in range(self.size)] + + def _hash(self, key): + return key % self.size + + def put(self, key, value): + index = self._hash(key) + bucket = self.buckets[index] + + for i, (k, v) in enumerate(bucket): + if k == key: + bucket[i] = (key, value) + return + + bucket.append((key, value)) + + def get(self, key): + index = self._hash(key) + bucket = self.buckets[index] + + for k, v in bucket: + if k == key: + return v + + return -1 + + def remove(self, key): + index = self._hash(key) + bucket = self.buckets[index] + + for i, (k, v) in enumerate(bucket): + if k == key: + bucket.pop(i) + return +``` + +### JavaScript Template + +```javascript +class LRUCache { + constructor(capacity) { + this.cache = new Map(); + this.capacity = capacity; + } + + get(key) { + if (!this.cache.has(key)) return -1; + + const value = this.cache.get(key); + this.cache.delete(key); + this.cache.set(key, value); // Move to end + return value; + } + + put(key, value) { + if (this.cache.has(key)) { + this.cache.delete(key); + } else if (this.cache.size >= this.capacity) { + const firstKey = this.cache.keys().next().value; + this.cache.delete(firstKey); + } + + this.cache.set(key, value); + } +} +``` + +## Common Design Patterns + +1. **Hash Map/Set**: Fast lookups +2. **Linked List**: For LRU, insertion order +3. **Heap**: For priority queues +4. **Trie**: For prefix matching +5. **Two Data Structures**: Combine for efficiency + +## Related Problems + +View all Design problems in the [main question list](https://seanprashad.com/leetcode-patterns/). + +Common problems include: +- LRU Cache +- Design HashMap +- Design HashSet +- Design Twitter +- Time-based Key-Value Store + +## Tips + +1. **Choose data structures**: Pick appropriate DS for operations +2. **Trade-offs**: Consider time vs space trade-offs +3. **Edge cases**: Handle empty, single element, capacity limits +4. **API design**: Clear, intuitive method signatures + diff --git a/guides/dfs.md b/guides/dfs.md new file mode 100644 index 00000000..ba9f3f14 --- /dev/null +++ b/guides/dfs.md @@ -0,0 +1,189 @@ +# DFS (Depth-First Search) Pattern + +## Core Concept + +Depth-First Search (DFS) is a graph traversal algorithm that explores as far as possible along each branch before backtracking. DFS uses a stack data structure (or recursion, which uses the call stack) to maintain the order of nodes to visit. + +Key characteristics: +- Explores depth first +- Uses stack (LIFO - Last In First Out) or recursion +- Visits nodes along a path until dead end, then backtracks +- Memory efficient (only stores path from root to current node) + +## When to Use + +Use DFS when: + +- **Path exploration**: Need to explore all paths from a node +- **Backtracking problems**: Problems requiring backtracking +- **Tree problems**: Many tree problems naturally use DFS +- **Connected components**: Finding connected components in graphs +- **Topological sort**: Can be used for topological sorting +- **Memory constraint**: When memory is limited (DFS uses less memory than BFS) + +### Problem Indicators + +- "Find all paths" +- "Maximum depth" +- "Path sum" +- "Connected components" +- "Island problems" +- "Tree traversal" + +## Template Code + +### DFS Recursive (Tree) + +```python +def dfs_tree(node, result): + if not node: + return + + # Pre-order: process before children + result.append(node.val) + + dfs_tree(node.left, result) + dfs_tree(node.right, result) + + # Post-order: process after children + # result.append(node.val) +``` + +### DFS Iterative (Tree) + +```python +def dfs_iterative(root): + if not root: + return [] + + result = [] + stack = [root] + + while stack: + node = stack.pop() + result.append(node.val) + + # Push right first, then left (for pre-order) + if node.right: + stack.append(node.right) + if node.left: + stack.append(node.left) + + return result +``` + +### DFS on Graph (Recursive) + +```python +def dfs_graph(node, graph, visited): + visited.add(node) + + # Process node + process(node) + + # Visit neighbors + for neighbor in graph[node]: + if neighbor not in visited: + dfs_graph(neighbor, graph, visited) +``` + +### DFS on Graph (Iterative) + +```python +def dfs_graph_iterative(start, graph): + visited = set() + stack = [start] + + while stack: + node = stack.pop() + + if node not in visited: + visited.add(node) + process(node) + + for neighbor in graph[node]: + if neighbor not in visited: + stack.append(neighbor) +``` + +### JavaScript Template + +```javascript +function dfsTree(node, result) { + if (!node) return; + + result.push(node.val); // Pre-order + dfsTree(node.left, result); + dfsTree(node.right, result); +} + +function dfsGraphIterative(start, graph) { + const visited = new Set(); + const stack = [start]; + + while (stack.length > 0) { + const node = stack.pop(); + + if (!visited.has(node)) { + visited.add(node); + process(node); + + for (const neighbor of graph[node]) { + if (!visited.has(neighbor)) { + stack.push(neighbor); + } + } + } + } +} +``` + +## DFS Traversal Orders + +### Pre-order (Root → Left → Right) +Process node before children. Good for copying trees. + +### In-order (Left → Root → Right) +Process left, then node, then right. Good for BST (gives sorted order). + +### Post-order (Left → Right → Root) +Process children before node. Good for deleting trees. + +## Common Variations + +1. **Path tracking**: Maintain path from root to current node +2. **Memoization**: Cache results to avoid recomputation +3. **Early termination**: Return immediately when condition met +4. **State tracking**: Track additional state (e.g., parent, depth) + +## Related Problems + +View all DFS problems in the [main question list](https://seanprashad.com/leetcode-patterns/). + +Common problems include: +- Maximum Depth of Binary Tree +- Path Sum +- Number of Islands +- Clone Graph +- Course Schedule +- Validate Binary Search Tree + +## Time & Space Complexity + +- **Time Complexity**: O(V + E) where V is vertices and E is edges +- **Space Complexity**: + - O(h) for trees where h is height + - O(V) for graphs (visited set + recursion stack) + +## DFS vs BFS + +- **DFS**: Explores depth first, uses stack/recursion, less memory +- **BFS**: Explores level by level, uses queue, finds shortest path + +## Tips + +1. **Choose traversal order**: Pre-order, in-order, or post-order based on problem +2. **Handle cycles**: Use visited set in graphs to avoid infinite loops +3. **Backtrack properly**: Restore state when backtracking +4. **Recursive vs Iterative**: Recursive is cleaner, iterative gives more control + diff --git a/guides/dynamic-programming.md b/guides/dynamic-programming.md new file mode 100644 index 00000000..330b3e3f --- /dev/null +++ b/guides/dynamic-programming.md @@ -0,0 +1,199 @@ +# Dynamic Programming Pattern + +## Core Concept + +Dynamic Programming (DP) is an optimization technique that solves complex problems by breaking them down into simpler subproblems. It stores the results of subproblems to avoid redundant calculations. The key principle is: **optimal substructure** - the optimal solution to a problem contains optimal solutions to its subproblems. + +DP problems can be approached in two ways: +- **Top-down (Memoization)**: Recursive approach with caching +- **Bottom-up (Tabulation)**: Iterative approach building up from base cases + +## When to Use + +Use Dynamic Programming when: + +- **Overlapping subproblems**: The same subproblem is solved multiple times +- **Optimal substructure**: Optimal solution can be constructed from optimal solutions of subproblems +- **Optimization problems**: Finding maximum, minimum, or counting possibilities +- **Decision problems**: Making choices that affect future states + +### Problem Indicators + +- "Find the maximum/minimum..." +- "Count the number of ways..." +- "Find the longest/shortest..." +- "What is the optimal way to..." +- Problems asking for all possible combinations/permutations +- Problems with constraints and choices + +## Template Code + +### Top-Down (Memoization) + +```python +def dp_memoization(n, memo={}): + # Base cases + if n <= 1: + return n + + # Check if already computed + if n in memo: + return memo[n] + + # Compute and store + memo[n] = dp_memoization(n - 1, memo) + dp_memoization(n - 2, memo) + return memo[n] +``` + +### Bottom-Up (Tabulation) + +```python +def dp_tabulation(n): + # Base cases + if n <= 1: + return n + + # Initialize DP array + dp = [0] * (n + 1) + dp[0] = 0 + dp[1] = 1 + + # Fill DP array + for i in range(2, n + 1): + dp[i] = dp[i - 1] + dp[i - 2] + + return dp[n] +``` + +### Space-Optimized (1D DP) + +```python +def dp_optimized(n): + if n <= 1: + return n + + prev2 = 0 + prev1 = 1 + + for i in range(2, n + 1): + current = prev1 + prev2 + prev2 = prev1 + prev1 = current + + return prev1 +``` + +### 2D DP Template + +```python +def dp_2d(m, n): + # Initialize 2D DP array + dp = [[0] * n for _ in range(m)] + + # Base cases (first row and column) + for i in range(m): + dp[i][0] = 1 + for j in range(n): + dp[0][j] = 1 + + # Fill DP array + 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] +``` + +### JavaScript Template + +```javascript +// Memoization +function dpMemoization(n, memo = {}) { + if (n <= 1) return n; + if (n in memo) return memo[n]; + + memo[n] = dpMemoization(n - 1, memo) + dpMemoization(n - 2, memo); + return memo[n]; +} + +// Tabulation +function dpTabulation(n) { + if (n <= 1) return n; + + const dp = new Array(n + 1).fill(0); + dp[0] = 0; + dp[1] = 1; + + for (let i = 2; i <= n; i++) { + dp[i] = dp[i - 1] + dp[i - 2]; + } + + return dp[n]; +} +``` + +## Common DP Patterns + +### 1. Fibonacci Pattern +- Climbing Stairs +- House Robber +- Decode Ways + +### 2. Knapsack Pattern +- 0/1 Knapsack +- Coin Change +- Partition Equal Subset Sum + +### 3. Longest Common Subsequence (LCS) +- Longest Common Subsequence +- Edit Distance +- Longest Palindromic Subsequence + +### 4. Matrix DP +- Unique Paths +- Minimum Path Sum +- Maximal Square + +### 5. String DP +- Longest Palindromic Substring +- Word Break +- Regular Expression Matching + +## Steps to Solve DP Problems + +1. **Identify the state**: What information do we need to track? +2. **Define the recurrence relation**: How do we compute current state from previous states? +3. **Base cases**: What are the simplest subproblems? +4. **Choose approach**: Top-down (memoization) or bottom-up (tabulation) +5. **Optimize space**: Can we reduce space complexity? + +## Related Problems + +View all Dynamic Programming problems in the [main question list](https://seanprashad.com/leetcode-patterns/). + +Common problems include: +- Climbing Stairs +- House Robber +- Coin Change +- Longest Common Subsequence +- Edit Distance +- Unique Paths +- Word Break +- Longest Increasing Subsequence + +## Time & Space Complexity + +- **Time Complexity**: Typically O(n) for 1D, O(n²) for 2D, O(n³) for 3D +- **Space Complexity**: + - O(n) for 1D memoization/tabulation + - O(n²) for 2D problems + - Can often be optimized to O(1) or O(n) with space optimization + +## Tips + +1. **Start with brute force**: Understand the problem first, then optimize +2. **Draw the state space**: Visualize subproblems and dependencies +3. **Identify overlapping subproblems**: Look for repeated calculations +4. **Practice pattern recognition**: Many DP problems follow similar patterns +5. **Space optimization**: After solving, see if you can reduce space complexity + diff --git a/guides/fast-slow-pointers.md b/guides/fast-slow-pointers.md new file mode 100644 index 00000000..60af372c --- /dev/null +++ b/guides/fast-slow-pointers.md @@ -0,0 +1,159 @@ +# Fast & Slow Pointers Pattern + +## Core Concept + +The Fast & Slow Pointers pattern (also known as the "Tortoise and Hare" algorithm) uses two pointers that move through a data structure at different speeds. Typically: +- **Slow pointer**: Moves one step at a time +- **Fast pointer**: Moves two steps at a time + +This pattern is particularly useful for: +- Detecting cycles in linked lists +- Finding the middle of a linked list +- Finding the kth element from the end +- Detecting palindromes in linked lists + +## When to Use + +Use Fast & Slow Pointers when: + +- **Cycle detection**: Need to detect if a linked list has a cycle +- **Finding middle**: Need to find the middle element of a linked list +- **Linked list problems**: Many linked list problems benefit from this pattern +- **Palindrome in linked list**: Checking if a linked list is a palindrome +- **Finding kth from end**: Locating the kth element from the end + +### Problem Indicators + +- "Detect if linked list has a cycle" +- "Find the middle of a linked list" +- "Check if linked list is palindrome" +- "Find the kth node from the end" +- "Remove the nth node from end" + +## Template Code + +### Cycle Detection + +```python +def has_cycle(head): + if not head or not head.next: + return False + + slow = head + fast = head.next + + while fast and fast.next: + if slow == fast: + return True + slow = slow.next + fast = fast.next.next + + return False +``` + +### Finding Middle Node + +```python +def find_middle(head): + slow = head + fast = head + + while fast and fast.next: + slow = slow.next + fast = fast.next.next + + return slow +``` + +### Finding Cycle Start (Floyd's Algorithm) + +```python +def detect_cycle_start(head): + # Step 1: Detect if cycle exists + slow = fast = head + + while fast and fast.next: + slow = slow.next + fast = fast.next.next + if slow == fast: + break + else: + return None # No cycle + + # Step 2: Find cycle start + slow = head + while slow != fast: + slow = slow.next + fast = fast.next + + return slow +``` + +### JavaScript Template + +```javascript +function hasCycle(head) { + if (!head || !head.next) return false; + + let slow = head; + let fast = head.next; + + while (fast && fast.next) { + if (slow === fast) return true; + slow = slow.next; + fast = fast.next.next; + } + + return false; +} + +function findMiddle(head) { + let slow = head; + let fast = head; + + while (fast && fast.next) { + slow = slow.next; + fast = fast.next.next; + } + + return slow; +} +``` + +## Common Variations + +1. **Different Speeds**: Adjust speeds (e.g., 1:3 ratio) for specific problems +2. **Kth from End**: Use two pointers with k distance between them +3. **Palindrome Check**: Reverse second half and compare +4. **Cycle Length**: Find the length of the cycle + +## Related Problems + +View all Fast & Slow Pointers problems in the [main question list](https://seanprashad.com/leetcode-patterns/). + +Common problems include: +- Linked List Cycle +- Middle of the Linked List +- Palindrome Linked List +- Remove Nth Node From End of List +- Reorder List + +## Time & Space Complexity + +- **Time Complexity**: O(n) - linear traversal of the linked list +- **Space Complexity**: O(1) - only using two pointers, constant extra space + +## Mathematical Insight + +Floyd's Cycle Detection Algorithm works because: +- If there's a cycle, the fast pointer will eventually catch up to the slow pointer +- When they meet, the distance from head to cycle start equals the distance from meeting point to cycle start +- This allows us to find the cycle start with a second pass + +## Tips + +1. **Initialize carefully**: Start both pointers appropriately (same node or one step apart) +2. **Check for null**: Always check if fast and fast.next exist before accessing +3. **Edge cases**: Handle empty list, single node, no cycle scenarios +4. **Visualize**: Draw the linked list to understand pointer movement + diff --git a/guides/graph.md b/guides/graph.md new file mode 100644 index 00000000..03dfd7b2 --- /dev/null +++ b/guides/graph.md @@ -0,0 +1,208 @@ +# Graph Pattern + +## Core Concept + +Graphs are data structures consisting of nodes (vertices) connected by edges. Graph problems involve traversing, searching, or manipulating these connections. Common graph representations include: + +- **Adjacency List**: List of lists, each inner list contains neighbors +- **Adjacency Matrix**: 2D matrix where matrix[i][j] indicates edge +- **Edge List**: List of tuples representing edges + +## When to Use + +Graph techniques are used when: + +- **Relationships**: Data has relationships/connections between entities +- **Network problems**: Social networks, computer networks, dependencies +- **Path finding**: Finding paths between nodes +- **Cycle detection**: Detecting cycles in directed/undirected graphs +- **Connected components**: Finding groups of connected nodes + +### Problem Indicators + +- "Given a graph..." +- "Find path between nodes" +- "Detect cycle" +- "Find connected components" +- "Topological sort" +- "Shortest path" + +## Template Code + +### Graph Representation + +```python +# Adjacency List +graph = { + 0: [1, 2], + 1: [0, 3], + 2: [0, 3], + 3: [1, 2] +} + +# Adjacency Matrix +graph_matrix = [ + [0, 1, 1, 0], + [1, 0, 0, 1], + [1, 0, 0, 1], + [0, 1, 1, 0] +] +``` + +### Detect Cycle (Undirected Graph) + +```python +def has_cycle_undirected(graph): + visited = set() + + def dfs(node, parent): + visited.add(node) + + for neighbor in graph[node]: + if neighbor not in visited: + if dfs(neighbor, node): + return True + elif neighbor != parent: + return True # Cycle found + + return False + + for node in graph: + if node not in visited: + if dfs(node, -1): + return True + + return False +``` + +### Detect Cycle (Directed Graph) + +```python +def has_cycle_directed(graph): + WHITE, GRAY, BLACK = 0, 1, 2 + color = {node: WHITE for node in graph} + + def dfs(node): + if color[node] == GRAY: + return True # Cycle found + + if color[node] == BLACK: + return False + + color[node] = GRAY + for neighbor in graph[node]: + if dfs(neighbor): + return True + + color[node] = BLACK + return False + + for node in graph: + if color[node] == WHITE: + if dfs(node): + return True + + return False +``` + +### Find Connected Components + +```python +def connected_components(graph): + visited = set() + components = [] + + def dfs(node, component): + visited.add(node) + component.append(node) + + for neighbor in graph[node]: + if neighbor not in visited: + dfs(neighbor, component) + + for node in graph: + if node not in visited: + component = [] + dfs(node, component) + components.append(component) + + return components +``` + +### JavaScript Template + +```javascript +// Adjacency List +const graph = { + 0: [1, 2], + 1: [0, 3], + 2: [0, 3], + 3: [1, 2] +}; + +function hasCycleUndirected(graph) { + const visited = new Set(); + + function dfs(node, parent) { + visited.add(node); + + for (const neighbor of graph[node]) { + if (!visited.has(neighbor)) { + if (dfs(neighbor, node)) return true; + } else if (neighbor !== parent) { + return true; // Cycle found + } + } + + return false; + } + + for (const node in graph) { + if (!visited.has(parseInt(node))) { + if (dfs(parseInt(node), -1)) return true; + } + } + + return false; +} +``` + +## Common Graph Algorithms + +1. **BFS/DFS**: Basic traversal algorithms +2. **Topological Sort**: Ordering nodes in DAG +3. **Shortest Path**: Dijkstra, Bellman-Ford, Floyd-Warshall +4. **Minimum Spanning Tree**: Kruskal, Prim +5. **Strongly Connected Components**: Kosaraju, Tarjan + +## Related Problems + +View all Graph problems in the [main question list](https://seanprashad.com/leetcode-patterns/). + +Common problems include: +- Clone Graph +- Course Schedule +- Number of Islands +- Redundant Connection +- Network Delay Time +- Critical Connections + +## Time & Space Complexity + +- **Time Complexity**: Typically O(V + E) for traversal, varies by algorithm +- **Space Complexity**: O(V) for visited set and recursion stack + +## Graph Types + +- **Directed vs Undirected**: Edges have direction or not +- **Weighted vs Unweighted**: Edges have weights or not +- **Cyclic vs Acyclic**: Contains cycles or not +- **Connected vs Disconnected**: All nodes reachable or not + +## Tips + +1. **Choose representation**: Adjacency list for sparse graphs, matrix for dense +2. **Handle cycles**: Different logic for directed vs undirected graphs +3. **Visited tracking**: Always track visited nodes to avoid infinite loops +4. **Edge cases**: Empty graph, single node, disconnected components + diff --git a/guides/greedy.md b/guides/greedy.md new file mode 100644 index 00000000..928ec8f8 --- /dev/null +++ b/guides/greedy.md @@ -0,0 +1,149 @@ +# Greedy Pattern + +## Core Concept + +Greedy algorithms make locally optimal choices at each step with the hope that these choices will lead to a globally optimal solution. The key is identifying when a greedy approach works - not all problems can be solved greedily. + +Greedy works when: +- **Greedy choice property**: A global optimum can be reached by making locally optimal choices +- **Optimal substructure**: Optimal solution contains optimal solutions to subproblems + +## When to Use + +Use Greedy when: + +- **Optimization problems**: Finding maximum or minimum +- **Local optimality**: Local optimal choice leads to global optimum +- **Activity selection**: Scheduling, interval problems +- **Coin change**: Making change with minimum coins (specific cases) +- **MST problems**: Minimum Spanning Tree algorithms + +### Problem Indicators + +- "Maximum/Minimum..." +- "Activity selection" +- "Interval scheduling" +- "Minimum coins" +- "Optimal arrangement" +- Problems where local choice doesn't affect future choices + +## Template Code + +### Activity Selection + +```python +def activity_selection(activities): + # Sort by finish time + activities.sort(key=lambda x: x[1]) + + selected = [activities[0]] + last_finish = activities[0][1] + + for start, finish in activities[1:]: + if start >= last_finish: + selected.append((start, finish)) + last_finish = finish + + return selected +``` + +### Interval Scheduling + +```python +def erase_overlap_intervals(intervals): + if not intervals: + return 0 + + # Sort by end time + intervals.sort(key=lambda x: x[1]) + + count = 0 + end = intervals[0][1] + + for i in range(1, len(intervals)): + if intervals[i][0] < end: + count += 1 # Overlapping, remove this + else: + end = intervals[i][1] + + return count +``` + +### Coin Change (Greedy - works for certain coin systems) + +```python +def coin_change_greedy(coins, amount): + coins.sort(reverse=True) # Use largest coins first + count = 0 + + for coin in coins: + if amount >= coin: + num_coins = amount // coin + count += num_coins + amount -= num_coins * coin + + return count if amount == 0 else -1 +``` + +### JavaScript Template + +```javascript +function activitySelection(activities) { + activities.sort((a, b) => a[1] - b[1]); + + const selected = [activities[0]]; + let lastFinish = activities[0][1]; + + for (let i = 1; i < activities.length; i++) { + const [start, finish] = activities[i]; + if (start >= lastFinish) { + selected.push(activities[i]); + lastFinish = finish; + } + } + + return selected; +} +``` + +## Common Greedy Strategies + +1. **Sort first**: Many greedy problems require sorting +2. **Take earliest/latest**: Choose based on start/end times +3. **Take largest/smallest**: Choose based on value/size +4. **Local optimization**: Make best local choice + +## When Greedy Fails + +Greedy doesn't work when: +- Local optimal doesn't lead to global optimal +- Need to consider future consequences +- Problem requires exploring all possibilities + +Example: Coin change with coins [1, 3, 4] and amount 6: +- Greedy: 4 + 1 + 1 = 3 coins +- Optimal: 3 + 3 = 2 coins + +## Related Problems + +View all Greedy problems in the [main question list](https://seanprashad.com/leetcode-patterns/). + +Common problems include: +- Non-overlapping Intervals +- Jump Game +- Gas Station +- Assign Cookies +- Best Time to Buy and Sell Stock + +## Time & Space Complexity + +- **Time Complexity**: Often O(n log n) due to sorting, then O(n) for processing +- **Space Complexity**: O(1) if not storing results, O(n) if storing + +## Tips + +1. **Prove correctness**: Ensure greedy choice leads to optimal solution +2. **Sort appropriately**: Sort by the right criteria (end time, value, etc.) +3. **Start simple**: Try greedy first, fall back to DP if needed +4. **Counter-examples**: Test with edge cases to verify greedy works + diff --git a/guides/heap.md b/guides/heap.md new file mode 100644 index 00000000..9efabf46 --- /dev/null +++ b/guides/heap.md @@ -0,0 +1,214 @@ +# Heap Pattern + +## Core Concept + +A Heap is a complete binary tree that satisfies the heap property: +- **Min Heap**: Parent ≤ children (root is minimum) +- **Max Heap**: Parent ≥ children (root is maximum) + +Heaps are typically implemented as arrays where: +- Parent at index `i` has children at `2i + 1` and `2i + 2` +- Child at index `i` has parent at `(i - 1) // 2` + +## When to Use + +Use Heaps when: + +- **Priority queue**: Need to repeatedly get min/max element +- **K largest/smallest**: Find k largest or smallest elements +- **Merge k sorted**: Merging k sorted lists/arrays +- **Scheduling**: Task scheduling with priorities +- **Median finding**: Finding running median + +### Problem Indicators + +- "K largest/smallest" +- "Top K elements" +- "Merge k sorted" +- "Find median" +- "Priority queue" +- "Frequent elements" + +## Template Code + +### Python (using heapq) + +```python +import heapq + +# Min heap (default) +heap = [] +heapq.heappush(heap, 3) +heapq.heappush(heap, 1) +heapq.heappush(heap, 2) +min_val = heapq.heappop(heap) # Returns 1 + +# Max heap (negate values) +max_heap = [] +heapq.heappush(max_heap, -3) +heapq.heappush(max_heap, -1) +max_val = -heapq.heappop(max_heap) # Returns 3 + +# K largest elements +def k_largest(nums, k): + return heapq.nlargest(k, nums) + +# K smallest elements +def k_smallest(nums, k): + return heapq.nsmallest(k, nums) +``` + +### Custom Heap Implementation + +```python +class MinHeap: + def __init__(self): + self.heap = [] + + def parent(self, i): + return (i - 1) // 2 + + def left_child(self, i): + return 2 * i + 1 + + def right_child(self, i): + return 2 * i + 2 + + def insert(self, val): + self.heap.append(val) + self._heapify_up(len(self.heap) - 1) + + def extract_min(self): + if not self.heap: + return None + + if len(self.heap) == 1: + return self.heap.pop() + + min_val = self.heap[0] + self.heap[0] = self.heap.pop() + self._heapify_down(0) + return min_val + + def _heapify_up(self, i): + while i > 0 and self.heap[self.parent(i)] > self.heap[i]: + p = self.parent(i) + self.heap[i], self.heap[p] = self.heap[p], self.heap[i] + i = p + + def _heapify_down(self, i): + smallest = i + left = self.left_child(i) + right = self.right_child(i) + + if left < len(self.heap) and self.heap[left] < self.heap[smallest]: + smallest = left + + if right < len(self.heap) and self.heap[right] < self.heap[smallest]: + smallest = right + + if smallest != i: + self.heap[i], self.heap[smallest] = self.heap[smallest], self.heap[i] + self._heapify_down(smallest) +``` + +### JavaScript Template + +```javascript +// JavaScript doesn't have built-in heap, use array with manual implementation +// Or use a library like 'heap' npm package + +class MinHeap { + constructor() { + this.heap = []; + } + + parent(i) { + return Math.floor((i - 1) / 2); + } + + leftChild(i) { + return 2 * i + 1; + } + + rightChild(i) { + return 2 * i + 2; + } + + insert(val) { + this.heap.push(val); + this.heapifyUp(this.heap.length - 1); + } + + extractMin() { + if (this.heap.length === 0) return null; + if (this.heap.length === 1) return this.heap.pop(); + + const min = this.heap[0]; + this.heap[0] = this.heap.pop(); + this.heapifyDown(0); + return min; + } + + heapifyUp(i) { + while (i > 0 && this.heap[this.parent(i)] > this.heap[i]) { + const p = this.parent(i); + [this.heap[i], this.heap[p]] = [this.heap[p], this.heap[i]]; + i = p; + } + } + + heapifyDown(i) { + let smallest = i; + const left = this.leftChild(i); + const right = this.rightChild(i); + + if (left < this.heap.length && this.heap[left] < this.heap[smallest]) { + smallest = left; + } + + if (right < this.heap.length && this.heap[right] < this.heap[smallest]) { + smallest = right; + } + + if (smallest !== i) { + [this.heap[i], this.heap[smallest]] = [this.heap[smallest], this.heap[i]]; + this.heapifyDown(smallest); + } + } +} +``` + +## Common Variations + +1. **K-way merge**: Merge k sorted lists using heap +2. **Top K frequent**: Use heap with frequency counts +3. **Median**: Use two heaps (min + max) +4. **Custom comparator**: Heap with custom comparison function + +## Related Problems + +View all Heap problems in the [main question list](https://seanprashad.com/leetcode-patterns/). + +Common problems include: +- Kth Largest Element +- Merge K Sorted Lists +- Top K Frequent Elements +- Find Median from Data Stream +- Meeting Rooms II + +## Time & Space Complexity + +- **Insert**: O(log n) +- **Extract min/max**: O(log n) +- **Peek**: O(1) +- **Build heap**: O(n) +- **Space**: O(n) + +## Tips + +1. **Use library**: Python's `heapq` is efficient +2. **Max heap trick**: Negate values for max heap in Python +3. **K problems**: Maintain heap of size k +4. **Two heaps**: Use two heaps for median problems + diff --git a/guides/in-place-reversal-linked-list.md b/guides/in-place-reversal-linked-list.md new file mode 100644 index 00000000..46c9601e --- /dev/null +++ b/guides/in-place-reversal-linked-list.md @@ -0,0 +1,144 @@ +# In-place Reversal of a Linked List Pattern + +## Core Concept + +In-place reversal of a linked list involves reversing the order of nodes without using extra space for a new list. This is done by manipulating pointers to change the direction of links. + +## When to Use + +Use this pattern when: + +- **Reverse linked list**: Need to reverse entire or portion of list +- **In-place requirement**: Cannot use extra space +- **Pointer manipulation**: Comfortable with pointer operations +- **Partial reversal**: Reverse specific portion of list + +### Problem Indicators + +- "Reverse linked list" +- "Reverse nodes in k-group" +- "Reverse between positions" +- "Palindrome linked list" (reverse and compare) + +## Template Code + +### Reverse Entire List + +```python +def reverse_list(head): + prev = None + current = head + + while current: + next_node = current.next + current.next = prev + prev = current + current = next_node + + return prev +``` + +### Reverse Between Positions + +```python +def reverse_between(head, left, right): + if not head or left == right: + return head + + dummy = ListNode(0) + dummy.next = head + prev = dummy + + # Move to left position + for _ in range(left - 1): + prev = prev.next + + # Reverse from left to right + current = prev.next + for _ in range(right - left): + next_node = current.next + current.next = next_node.next + next_node.next = prev.next + prev.next = next_node + + return dummy.next +``` + +### Reverse in K-Groups + +```python +def reverse_k_group(head, k): + def reverse_linked_list(start, end): + prev = end + current = start + + while current != end: + next_node = current.next + current.next = prev + prev = current + current = next_node + + return prev + + # Count nodes + count = 0 + current = head + while current and count < k: + current = current.next + count += 1 + + if count == k: + # Reverse first k nodes + current = reverse_k_group(current, k) + head = reverse_linked_list(head, current) + + return head +``` + +### JavaScript Template + +```javascript +function reverseList(head) { + let prev = null; + let current = head; + + while (current) { + const next = current.next; + current.next = prev; + prev = current; + current = next; + } + + return prev; +} +``` + +## Common Variations + +1. **Partial reversal**: Reverse specific portion +2. **K-group reversal**: Reverse in groups of k +3. **Alternating reversal**: Reverse every other group +4. **Recursive reversal**: Recursive approach + +## Related Problems + +View all In-place reversal problems in the [main question list](https://seanprashad.com/leetcode-patterns/). + +Common problems include: +- Reverse Linked List +- Reverse Linked List II +- Reverse Nodes in k-Group +- Swap Nodes in Pairs + +## Time & Space Complexity + +- **Time Complexity**: O(n) - single pass through list +- **Space Complexity**: O(1) - only using pointers + +## Tips + +1. **Three pointers**: Use prev, current, next +2. **Dummy node**: Use dummy node for edge cases +3. **Draw it out**: Visualize pointer movements +4. **Handle edge cases**: Empty list, single node, etc. + diff --git a/guides/intervals.md b/guides/intervals.md new file mode 100644 index 00000000..8cf30e91 --- /dev/null +++ b/guides/intervals.md @@ -0,0 +1,179 @@ +# Intervals Pattern + +## Core Concept + +Interval problems involve working with ranges of values, typically represented as [start, end] pairs. Common operations include: +- Merging overlapping intervals +- Finding non-overlapping intervals +- Inserting intervals +- Finding intersections + +## When to Use + +Use Interval techniques when: + +- **Range problems**: Working with time ranges, number ranges +- **Overlap detection**: Finding or removing overlapping intervals +- **Scheduling**: Meeting rooms, event scheduling +- **Merge operations**: Combining overlapping intervals +- **Coverage problems**: Finding coverage, gaps + +### Problem Indicators + +- "Merge intervals" +- "Non-overlapping intervals" +- "Meeting rooms" +- "Insert interval" +- "Interval intersection" +- "Range problems" + +## Template Code + +### Merge Intervals + +```python +def merge_intervals(intervals): + if not intervals: + return [] + + # Sort by start time + intervals.sort(key=lambda x: x[0]) + merged = [intervals[0]] + + for current in intervals[1:]: + last = merged[-1] + + # If overlapping, merge + if current[0] <= last[1]: + last[1] = max(last[1], current[1]) + else: + merged.append(current) + + return merged +``` + +### Insert Interval + +```python +def insert_interval(intervals, new_interval): + result = [] + i = 0 + + # Add all intervals before new_interval + while i < len(intervals) and intervals[i][1] < new_interval[0]: + result.append(intervals[i]) + i += 1 + + # Merge overlapping intervals + while i < len(intervals) and intervals[i][0] <= new_interval[1]: + new_interval[0] = min(new_interval[0], intervals[i][0]) + new_interval[1] = max(new_interval[1], intervals[i][1]) + i += 1 + + result.append(new_interval) + + # Add remaining intervals + result.extend(intervals[i:]) + return result +``` + +### Find Non-Overlapping Intervals + +```python +def erase_overlap_intervals(intervals): + if not intervals: + return 0 + + # Sort by end time (greedy: keep intervals that end earliest) + intervals.sort(key=lambda x: x[1]) + + count = 0 + end = intervals[0][1] + + for i in range(1, len(intervals)): + if intervals[i][0] < end: + count += 1 # Remove overlapping interval + else: + end = intervals[i][1] + + return count +``` + +### Interval Intersection + +```python +def interval_intersection(intervals1, intervals2): + result = [] + i = j = 0 + + while i < len(intervals1) and j < len(intervals2): + # Find overlap + start = max(intervals1[i][0], intervals2[j][0]) + end = min(intervals1[i][1], intervals2[j][1]) + + if start <= end: + result.append([start, end]) + + # Move pointer of interval that ends first + if intervals1[i][1] < intervals2[j][1]: + i += 1 + else: + j += 1 + + return result +``` + +### JavaScript Template + +```javascript +function mergeIntervals(intervals) { + if (intervals.length === 0) return []; + + intervals.sort((a, b) => a[0] - b[0]); + const merged = [intervals[0]]; + + for (let i = 1; i < intervals.length; i++) { + const current = intervals[i]; + const last = merged[merged.length - 1]; + + if (current[0] <= last[1]) { + last[1] = Math.max(last[1], current[1]); + } else { + merged.push(current); + } + } + + return merged; +} +``` + +## Common Operations + +1. **Sorting**: Usually sort by start time or end time +2. **Overlap check**: `interval1[0] <= interval2[1] and interval2[0] <= interval1[1]` +3. **Merge**: `[min(start1, start2), max(end1, end2)]` +4. **Greedy selection**: Keep intervals that end earliest + +## Related Problems + +View all Intervals problems in the [main question list](https://seanprashad.com/leetcode-patterns/). + +Common problems include: +- Merge Intervals +- Non-overlapping Intervals +- Meeting Rooms +- Insert Interval +- Interval List Intersections + +## Time & Space Complexity + +- **Time Complexity**: O(n log n) for sorting, then O(n) for processing +- **Space Complexity**: O(n) for storing results + +## Tips + +1. **Sort first**: Most interval problems require sorting +2. **Choose sort key**: Sort by start time or end time based on problem +3. **Overlap condition**: Understand when intervals overlap +4. **Greedy approach**: Often use greedy (keep earliest ending intervals) + diff --git a/guides/quickselect.md b/guides/quickselect.md new file mode 100644 index 00000000..c611f5ac --- /dev/null +++ b/guides/quickselect.md @@ -0,0 +1,120 @@ +# QuickSelect Pattern + +## Core Concept + +QuickSelect is a selection algorithm to find the kth smallest (or largest) element in an unsorted array. It's based on QuickSort's partition algorithm but only recurses into the partition containing the desired element. + +## When to Use + +Use QuickSelect when: + +- **Kth element**: Finding kth smallest/largest element +- **Partial sorting**: Need k smallest/largest elements +- **O(n) average**: Need better than O(n log n) for selection +- **In-place**: Need in-place algorithm + +### Problem Indicators + +- "Find kth largest" +- "Find kth smallest" +- "Top K elements" +- "Median of array" +- "K closest points" + +## Template Code + +### Find Kth Largest + +```python +def find_kth_largest(nums, k): + def quickselect(left, right, k_smallest): + if left == right: + return nums[left] + + pivot_index = partition(left, right, left) + + if k_smallest == pivot_index: + return nums[pivot_index] + elif k_smallest < pivot_index: + return quickselect(left, pivot_index - 1, k_smallest) + else: + return quickselect(pivot_index + 1, right, k_smallest) + + def partition(left, right, pivot_index): + pivot_value = nums[pivot_index] + # Move pivot to end + nums[pivot_index], nums[right] = nums[right], nums[pivot_index] + + store_index = left + for i in range(left, right): + if nums[i] < pivot_value: + nums[store_index], nums[i] = nums[i], nums[store_index] + store_index += 1 + + # Move pivot to final position + nums[right], nums[store_index] = nums[store_index], nums[right] + return store_index + + # kth largest is (n - k)th smallest + return quickselect(0, len(nums) - 1, len(nums) - k) +``` + +### JavaScript Template + +```javascript +function findKthLargest(nums, k) { + function quickSelect(left, right, kSmallest) { + if (left === right) return nums[left]; + + const pivotIndex = partition(left, right, left); + + if (kSmallest === pivotIndex) { + return nums[pivotIndex]; + } else if (kSmallest < pivotIndex) { + return quickSelect(left, pivotIndex - 1, kSmallest); + } else { + return quickSelect(pivotIndex + 1, right, kSmallest); + } + } + + function partition(left, right, pivotIndex) { + const pivotValue = nums[pivotIndex]; + [nums[pivotIndex], nums[right]] = [nums[right], nums[pivotIndex]]; + + let storeIndex = left; + for (let i = left; i < right; i++) { + if (nums[i] < pivotValue) { + [nums[storeIndex], nums[i]] = [nums[i], nums[storeIndex]]; + storeIndex++; + } + } + + [nums[right], nums[storeIndex]] = [nums[storeIndex], nums[right]]; + return storeIndex; + } + + return quickSelect(0, nums.length - 1, nums.length - k); +} +``` + +## Related Problems + +View all QuickSelect problems in the [main question list](https://seanprashad.com/leetcode-patterns/). + +Common problems include: +- Kth Largest Element +- K Closest Points to Origin +- Top K Frequent Elements (with heap or quickselect) + +## Time & Space Complexity + +- **Time Complexity**: O(n) average, O(n²) worst case +- **Space Complexity**: O(1) for iterative, O(log n) for recursive + +## Tips + +1. **Random pivot**: Use random pivot for better average performance +2. **Kth largest**: Convert to (n-k)th smallest +3. **Partition logic**: Similar to QuickSort partition +4. **Early termination**: Stop when kth element found + diff --git a/guides/sliding-window.md b/guides/sliding-window.md new file mode 100644 index 00000000..31d3f973 --- /dev/null +++ b/guides/sliding-window.md @@ -0,0 +1,131 @@ +# Sliding Window Pattern + +## Core Concept + +The Sliding Window pattern is a technique used to solve problems involving subarrays or substrings of a fixed or variable size. Instead of recalculating values for every possible subarray, we maintain a "window" that slides through the array, efficiently updating our calculations as we move. + +The key insight is that when moving the window, we only need to: +- Remove the element leaving the window +- Add the element entering the window +- Update our calculation based on these changes + +## When to Use + +Use the Sliding Window pattern when you encounter problems with these characteristics: + +- **Subarray/Substring problems**: Finding subarrays or substrings that meet certain criteria +- **Fixed or variable window size**: Problems asking for subarrays of size k, or the longest/shortest subarray meeting a condition +- **Optimization problems**: Finding maximum, minimum, or count of subarrays +- **Common keywords**: "subarray", "substring", "contiguous", "window", "k elements" + +### Problem Indicators + +- "Find the maximum sum of k consecutive elements" +- "Find the longest substring with at most k distinct characters" +- "Find all subarrays of size k with sum less than target" +- "Minimum window substring" + +## Template Code + +### Fixed Window Size + +```python +def sliding_window_fixed(arr, k): + # Initialize window + window_sum = sum(arr[:k]) + result = window_sum + + # Slide the window + for i in range(k, len(arr)): + # Remove leftmost element, add rightmost element + window_sum = window_sum - arr[i - k] + arr[i] + result = max(result, window_sum) # or min, or other operation + + return result +``` + +### Variable Window Size (Two Pointers) + +```python +def sliding_window_variable(arr, target): + left = 0 + window_sum = 0 + result = float('inf') + + for right in range(len(arr)): + # Expand window + window_sum += arr[right] + + # Shrink window while condition is met + while window_sum >= target: + result = min(result, right - left + 1) + window_sum -= arr[left] + left += 1 + + return result if result != float('inf') else 0 +``` + +### JavaScript Template + +```javascript +function slidingWindowFixed(arr, k) { + let windowSum = arr.slice(0, k).reduce((a, b) => a + b, 0); + let result = windowSum; + + for (let i = k; i < arr.length; i++) { + windowSum = windowSum - arr[i - k] + arr[i]; + result = Math.max(result, windowSum); + } + + return result; +} + +function slidingWindowVariable(arr, target) { + let left = 0; + let windowSum = 0; + let result = Infinity; + + for (let right = 0; right < arr.length; right++) { + windowSum += arr[right]; + + while (windowSum >= target) { + result = Math.min(result, right - left + 1); + windowSum -= arr[left]; + left++; + } + } + + return result === Infinity ? 0 : result; +} +``` + +## Common Variations + +1. **Maximum/Minimum in Window**: Use a deque to track max/min efficiently +2. **Counting Subarrays**: Count subarrays meeting a condition +3. **Character Frequency**: Track character counts in substring problems +4. **Multiple Conditions**: Handle multiple constraints simultaneously + +## Related Problems + +View all Sliding Window problems in the [main question list](https://seanprashad.com/leetcode-patterns/). + +Common problems include: +- Maximum Average Subarray +- Longest Substring Without Repeating Characters +- Minimum Window Substring +- Subarray Product Less Than K +- Maximum Sum Subarray of Size K + +## Time & Space Complexity + +- **Time Complexity**: Typically O(n) - each element is visited at most twice (once by left pointer, once by right pointer) +- **Space Complexity**: O(1) for fixed window, O(k) if storing window elements, or O(min(m,n)) for character frequency maps where m is character set size + +## Tips + +1. **Identify the window type**: Fixed size vs. variable size +2. **Determine what to track**: Sum, product, character frequency, etc. +3. **Define window validity**: When is the window valid? When should we shrink it? +4. **Handle edge cases**: Empty arrays, k > array length, etc. + diff --git a/guides/sorting.md b/guides/sorting.md new file mode 100644 index 00000000..4565919d --- /dev/null +++ b/guides/sorting.md @@ -0,0 +1,135 @@ +# Sorting Pattern + +## Core Concept + +Sorting is the process of arranging elements in a particular order (ascending or descending). While sorting itself is a fundamental operation, many problems become easier after sorting the input. + +## When to Use + +Use Sorting when: + +- **Order matters**: Need elements in specific order +- **Simplify problem**: Sorting makes problem easier to solve +- **Two pointers**: Sorted array enables two-pointer technique +- **Binary search**: Requires sorted array +- **Grouping**: Group similar elements together + +### Problem Indicators + +- "Sort array" +- "Find kth largest/smallest" +- "Merge sorted arrays" +- "Intersection of arrays" +- Problems where order helps + +## Template Code + +### Built-in Sort + +```python +# Python +arr.sort() # In-place +sorted_arr = sorted(arr) # New array + +# Custom comparator +arr.sort(key=lambda x: x[1]) # Sort by second element +arr.sort(key=lambda x: (-x[0], x[1])) # Sort by first desc, second asc +``` + +### Quick Sort + +```python +def quicksort(arr, low, high): + if low < high: + pi = partition(arr, low, high) + quicksort(arr, low, pi - 1) + quicksort(arr, pi + 1, high) + +def partition(arr, low, high): + pivot = arr[high] + i = low - 1 + + for j in range(low, high): + if arr[j] < pivot: + i += 1 + arr[i], arr[j] = arr[j], arr[i] + + arr[i + 1], arr[high] = arr[high], arr[i + 1] + return i + 1 +``` + +### Merge Sort + +```python +def mergesort(arr): + if len(arr) <= 1: + return arr + + mid = len(arr) // 2 + left = mergesort(arr[:mid]) + right = mergesort(arr[mid:]) + + return merge(left, right) + +def merge(left, right): + result = [] + i = j = 0 + + while i < len(left) and j < len(right): + if left[i] <= right[j]: + result.append(left[i]) + i += 1 + else: + result.append(right[j]) + j += 1 + + result.extend(left[i:]) + result.extend(right[j:]) + return result +``` + +### JavaScript Template + +```javascript +// Built-in sort +arr.sort((a, b) => a - b); // Ascending +arr.sort((a, b) => b - a); // Descending + +// Custom comparator +arr.sort((a, b) => { + if (a[0] !== b[0]) return a[0] - b[0]; + return a[1] - b[1]; +}); +``` + +## Common Use Cases + +1. **Two Sum on sorted array**: Use two pointers +2. **K largest/smallest**: Sort and take first/last k +3. **Merge intervals**: Sort by start time +4. **Anagrams**: Sort characters to group anagrams + +## Related Problems + +View all Sorting problems in the [main question list](https://seanprashad.com/leetcode-patterns/). + +Common problems include: +- Sort Colors +- Merge Sorted Array +- Kth Largest Element +- Meeting Rooms +- Group Anagrams + +## Time & Space Complexity + +- **Comparison sorts**: O(n log n) best case +- **Counting sort**: O(n + k) for small range +- **Space**: O(1) for in-place, O(n) for merge sort + +## Tips + +1. **Use built-in**: Python's `sort()` is highly optimized +2. **Custom comparator**: Learn to write custom comparison functions +3. **Stability**: Some algorithms maintain relative order of equal elements +4. **When to sort**: Sort if it simplifies the problem + diff --git a/guides/topological-sort.md b/guides/topological-sort.md new file mode 100644 index 00000000..0ce55baf --- /dev/null +++ b/guides/topological-sort.md @@ -0,0 +1,164 @@ +# Topological Sort Pattern + +## Core Concept + +Topological Sort is an ordering of vertices in a directed acyclic graph (DAG) such that for every directed edge (u, v), vertex u comes before v in the ordering. It's used for: +- Task scheduling with dependencies +- Build systems +- Course prerequisites +- Event ordering + +## When to Use + +Use Topological Sort when: + +- **Dependencies**: Tasks/events have dependencies +- **Ordering required**: Need to find valid ordering +- **DAG problems**: Working with directed acyclic graphs +- **Prerequisites**: Course prerequisites, build dependencies +- **Scheduling**: Task scheduling with constraints + +### Problem Indicators + +- "Course schedule" +- "Task ordering" +- "Build order" +- "Dependencies" +- "Prerequisites" +- "Find ordering" + +## Template Code + +### Kahn's Algorithm (BFS-based) + +```python +from collections import deque + +def topological_sort_kahn(num_nodes, edges): + # Build graph and in-degree + graph = {i: [] for i in range(num_nodes)} + in_degree = [0] * num_nodes + + for u, v in edges: + graph[u].append(v) + in_degree[v] += 1 + + # Find all nodes with in-degree 0 + queue = deque([i for i in range(num_nodes) if in_degree[i] == 0]) + result = [] + + while queue: + node = queue.popleft() + result.append(node) + + # Remove this node and update in-degrees + for neighbor in graph[node]: + in_degree[neighbor] -= 1 + if in_degree[neighbor] == 0: + queue.append(neighbor) + + # Check if all nodes processed (no cycle) + return result if len(result) == num_nodes else [] +``` + +### DFS-based Topological Sort + +```python +def topological_sort_dfs(num_nodes, edges): + # Build graph + graph = {i: [] for i in range(num_nodes)} + for u, v in edges: + graph[u].append(v) + + WHITE, GRAY, BLACK = 0, 1, 2 + color = {i: WHITE for i in range(num_nodes)} + result = [] + + def dfs(node): + if color[node] == GRAY: + return False # Cycle detected + if color[node] == BLACK: + return True + + color[node] = GRAY + for neighbor in graph[node]: + if not dfs(neighbor): + return False + + color[node] = BLACK + result.append(node) + return True + + for node in range(num_nodes): + if color[node] == WHITE: + if not dfs(node): + return [] # Cycle exists + + return result[::-1] # Reverse for correct order +``` + +### JavaScript Template + +```javascript +function topologicalSortKahn(numNodes, edges) { + const graph = Array.from({ length: numNodes }, () => []); + const inDegree = new Array(numNodes).fill(0); + + for (const [u, v] of edges) { + graph[u].push(v); + inDegree[v]++; + } + + const queue = []; + for (let i = 0; i < numNodes; i++) { + if (inDegree[i] === 0) { + queue.push(i); + } + } + + const result = []; + while (queue.length > 0) { + const node = queue.shift(); + result.push(node); + + for (const neighbor of graph[node]) { + inDegree[neighbor]--; + if (inDegree[neighbor] === 0) { + queue.push(neighbor); + } + } + } + + return result.length === numNodes ? result : []; +} +``` + +## Cycle Detection + +Topological sort can detect cycles: +- If result length < num_nodes, cycle exists +- In DFS version, gray node indicates back edge (cycle) + +## Related Problems + +View all Topological Sort problems in the [main question list](https://seanprashad.com/leetcode-patterns/). + +Common problems include: +- Course Schedule +- Course Schedule II +- Alien Dictionary +- Sequence Reconstruction +- Find Eventual Safe States + +## Time & Space Complexity + +- **Time Complexity**: O(V + E) where V is vertices, E is edges +- **Space Complexity**: O(V) for graph, in-degree, and result + +## Tips + +1. **Choose algorithm**: Kahn's is iterative, DFS is recursive +2. **Cycle detection**: Check if all nodes processed +3. **Build graph correctly**: Understand edge direction +4. **Multiple solutions**: DAGs can have multiple valid orderings + diff --git a/guides/trie.md b/guides/trie.md new file mode 100644 index 00000000..512a5e44 --- /dev/null +++ b/guides/trie.md @@ -0,0 +1,189 @@ +# Trie Pattern + +## Core Concept + +A Trie (prefix tree) is a tree-like data structure used to store strings. Each node represents a character, and paths from root to nodes represent strings. Tries are efficient for: +- Prefix matching +- String search +- Autocomplete +- Word validation + +Key advantages: +- Fast prefix lookups O(m) where m is string length +- Space efficient for common prefixes +- Easy to implement + +## When to Use + +Use Trie when: + +- **Prefix matching**: Finding all strings with given prefix +- **String search**: Searching for words in dictionary +- **Autocomplete**: Implementing autocomplete functionality +- **Word validation**: Checking if word exists +- **Prefix problems**: Problems involving prefixes + +### Problem Indicators + +- "Prefix matching" +- "Word search" +- "Autocomplete" +- "Longest common prefix" +- "Add and search words" +- "Prefix tree" + +## Template Code + +### Basic Trie Implementation + +```python +class TrieNode: + def __init__(self): + self.children = {} + self.is_end = False + +class Trie: + def __init__(self): + self.root = TrieNode() + + def insert(self, word): + node = self.root + for char in word: + if char not in node.children: + node.children[char] = TrieNode() + node = node.children[char] + node.is_end = True + + def search(self, word): + node = self.root + for char in word: + if char not in node.children: + return False + node = node.children[char] + return node.is_end + + def starts_with(self, prefix): + node = self.root + for char in prefix: + if char not in node.children: + return False + node = node.children[char] + return True +``` + +### Search with Wildcard + +```python +class WordDictionary: + def __init__(self): + self.root = TrieNode() + + def add_word(self, word): + node = self.root + for char in word: + if char not in node.children: + node.children[char] = TrieNode() + node = node.children[char] + node.is_end = True + + def search(self, word): + def dfs(node, index): + if index == len(word): + return node.is_end + + char = word[index] + if char == '.': + # Try all children + for child in node.children.values(): + if dfs(child, index + 1): + return True + return False + else: + if char not in node.children: + return False + return dfs(node.children[char], index + 1) + + return dfs(self.root, 0) +``` + +### JavaScript Template + +```javascript +class TrieNode { + constructor() { + this.children = {}; + this.isEnd = false; + } +} + +class Trie { + constructor() { + this.root = new TrieNode(); + } + + insert(word) { + let node = this.root; + for (const char of word) { + if (!(char in node.children)) { + node.children[char] = new TrieNode(); + } + node = node.children[char]; + } + node.isEnd = true; + } + + search(word) { + let node = this.root; + for (const char of word) { + if (!(char in node.children)) { + return false; + } + node = node.children[char]; + } + return node.isEnd; + } + + startsWith(prefix) { + let node = this.root; + for (const char of prefix) { + if (!(char in node.children)) { + return false; + } + node = node.children[char]; + } + return true; + } +} +``` + +## Common Variations + +1. **Wildcard search**: Support '.' for any character +2. **Prefix counting**: Count words with given prefix +3. **Longest prefix**: Find longest common prefix +4. **Delete operation**: Remove words from trie + +## Related Problems + +View all Trie problems in the [main question list](https://seanprashad.com/leetcode-patterns/). + +Common problems include: +- Implement Trie +- Add and Search Words +- Word Search II +- Longest Word in Dictionary +- Replace Words + +## Time & Space Complexity + +- **Insert**: O(m) where m is word length +- **Search**: O(m) where m is word length +- **Space**: O(ALPHABET_SIZE * N * M) where N is number of words, M is average length + +## Tips + +1. **Node structure**: Each node has children map and is_end flag +2. **Character mapping**: Use dictionary/map for children +3. **Prefix vs word**: Distinguish between prefix and complete word +4. **DFS for wildcards**: Use DFS when searching with wildcards + diff --git a/guides/two-pointers.md b/guides/two-pointers.md new file mode 100644 index 00000000..bb48ad25 --- /dev/null +++ b/guides/two-pointers.md @@ -0,0 +1,153 @@ +# Two Pointers Pattern + +## Core Concept + +The Two Pointers pattern uses two pointers (indices) that traverse a data structure in a coordinated manner. The pointers can move: +- **Towards each other** (from both ends) +- **In the same direction** (both from start, or both from end) +- **At different speeds** (fast and slow pointers) + +This pattern eliminates the need for nested loops in many cases, reducing time complexity from O(n²) to O(n). + +## When to Use + +Use the Two Pointers pattern when: + +- **Sorted arrays**: Problems involving sorted arrays or linked lists +- **Pair searching**: Finding pairs that meet certain criteria +- **Palindrome checking**: Verifying if a sequence is a palindrome +- **Partitioning**: Dividing elements into groups based on conditions +- **Cycle detection**: Detecting cycles in linked lists (fast & slow pointers) + +### Problem Indicators + +- "Find two numbers that sum to target in sorted array" +- "Remove duplicates from sorted array" +- "Check if string is palindrome" +- "Partition array around a pivot" +- "Detect cycle in linked list" + +## Template Code + +### Opposite Ends (Converging) + +```python +def two_pointers_converging(arr, target): + left = 0 + right = len(arr) - 1 + + while left < right: + current_sum = arr[left] + arr[right] + + if current_sum == target: + return [left, right] + elif current_sum < target: + left += 1 # Need larger sum + else: + right -= 1 # Need smaller sum + + return [-1, -1] +``` + +### Same Direction (Sliding Window Variant) + +```python +def two_pointers_same_direction(arr): + slow = 0 + + for fast in range(len(arr)): + if should_keep(arr[fast]): + arr[slow] = arr[fast] + slow += 1 + + return slow # New length +``` + +### Fast & Slow Pointers (Cycle Detection) + +```python +def has_cycle(head): + if not head or not head.next: + return False + + slow = head + fast = head.next + + while fast and fast.next: + if slow == fast: + return True + slow = slow.next + fast = fast.next.next + + return False +``` + +### JavaScript Template + +```javascript +function twoPointersConverging(arr, target) { + let left = 0; + let right = arr.length - 1; + + while (left < right) { + const sum = arr[left] + arr[right]; + + if (sum === target) { + return [left, right]; + } else if (sum < target) { + left++; + } else { + right--; + } + } + + return [-1, -1]; +} + +function hasCycle(head) { + if (!head || !head.next) return false; + + let slow = head; + let fast = head.next; + + while (fast && fast.next) { + if (slow === fast) return true; + slow = slow.next; + fast = fast.next.next; + } + + return false; +} +``` + +## Common Variations + +1. **Three Pointers**: Extend to three pointers for 3Sum problems +2. **Multiple Arrays**: Use pointers on different arrays simultaneously +3. **Partitioning**: Use pointers to partition elements (e.g., Dutch National Flag) +4. **In-place Operations**: Modify arrays in-place without extra space + +## Related Problems + +View all Two Pointers problems in the [main question list](https://seanprashad.com/leetcode-patterns/). + +Common problems include: +- Two Sum (sorted array) +- Remove Duplicates from Sorted Array +- Valid Palindrome +- Container With Most Water +- 3Sum +- Trapping Rain Water + +## Time & Space Complexity + +- **Time Complexity**: O(n) - each element is visited at most once by each pointer +- **Space Complexity**: O(1) - only using a constant amount of extra space for pointers + +## Tips + +1. **Sort first**: Many two-pointer problems require sorted input +2. **Identify pointer movement**: Determine when each pointer should move +3. **Handle duplicates**: Be careful with duplicate values in sorted arrays +4. **Edge cases**: Empty arrays, single element, all elements same + diff --git a/guides/union-find.md b/guides/union-find.md new file mode 100644 index 00000000..f07fe1a7 --- /dev/null +++ b/guides/union-find.md @@ -0,0 +1,172 @@ +# Union Find Pattern + +## Core Concept + +Union Find (Disjoint Set Union - DSU) is a data structure that tracks a set of elements partitioned into disjoint subsets. It supports two main operations: +- **Find**: Determine which subset an element belongs to +- **Union**: Merge two subsets into one + +It's efficient for: +- Detecting cycles in graphs +- Finding connected components +- Network connectivity problems +- Kruskal's algorithm for MST + +## When to Use + +Use Union Find when: + +- **Connected components**: Finding connected components in graphs +- **Cycle detection**: Detecting cycles in undirected graphs +- **Dynamic connectivity**: Adding edges and checking connectivity +- **Network problems**: Social networks, computer networks +- **MST algorithms**: Kruskal's algorithm + +### Problem Indicators + +- "Connected components" +- "Detect cycle" +- "Friend circles" +- "Number of islands" +- "Redundant connection" +- "Accounts merge" + +## Template Code + +### Basic Union Find + +```python +class UnionFind: + def __init__(self, n): + self.parent = list(range(n)) + self.rank = [0] * n + + def find(self, x): + if self.parent[x] != x: + # Path compression + self.parent[x] = self.find(self.parent[x]) + return self.parent[x] + + def union(self, x, y): + root_x = self.find(x) + root_y = self.find(y) + + if root_x == root_y: + return False # Already connected + + # Union by rank + if self.rank[root_x] < self.rank[root_y]: + self.parent[root_x] = root_y + elif self.rank[root_x] > self.rank[root_y]: + self.parent[root_y] = root_x + else: + self.parent[root_y] = root_x + self.rank[root_x] += 1 + + return True + + def connected(self, x, y): + return self.find(x) == self.find(y) +``` + +### Count Components + +```python +def count_components(n, edges): + uf = UnionFind(n) + + for u, v in edges: + uf.union(u, v) + + # Count distinct roots + roots = set() + for i in range(n): + roots.add(uf.find(i)) + + return len(roots) +``` + +### Detect Cycle + +```python +def has_cycle(n, edges): + uf = UnionFind(n) + + for u, v in edges: + if not uf.union(u, v): + return True # Cycle detected + + return False +``` + +### JavaScript Template + +```javascript +class UnionFind { + constructor(n) { + this.parent = Array.from({ length: n }, (_, i) => i); + this.rank = new Array(n).fill(0); + } + + find(x) { + if (this.parent[x] !== x) { + this.parent[x] = this.find(this.parent[x]); // Path compression + } + return this.parent[x]; + } + + union(x, y) { + const rootX = this.find(x); + const rootY = this.find(y); + + if (rootX === rootY) return false; + + // Union by rank + if (this.rank[rootX] < this.rank[rootY]) { + this.parent[rootX] = rootY; + } else if (this.rank[rootX] > this.rank[rootY]) { + this.parent[rootY] = rootX; + } else { + this.parent[rootY] = rootX; + this.rank[rootX]++; + } + + return true; + } + + connected(x, y) { + return this.find(x) === this.find(y); + } +} +``` + +## Optimizations + +1. **Path Compression**: Flatten tree during find operation +2. **Union by Rank**: Always attach smaller tree to larger tree +3. **Both together**: Nearly O(1) amortized time complexity + +## Related Problems + +View all Union Find problems in the [main question list](https://seanprashad.com/leetcode-patterns/). + +Common problems include: +- Number of Connected Components +- Redundant Connection +- Accounts Merge +- Friend Circles +- Most Stones Removed + +## Time & Space Complexity + +- **Find (with optimizations)**: Nearly O(1) amortized +- **Union (with optimizations)**: Nearly O(1) amortized +- **Space**: O(n) for parent and rank arrays + +## Tips + +1. **Initialize properly**: Parent[i] = i initially +2. **Use optimizations**: Path compression and union by rank +3. **Count components**: Count distinct roots +4. **Cycle detection**: If union returns false, cycle exists + diff --git a/package-lock.json b/package-lock.json index cc5cd23e..858c2cb9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -83,6 +83,7 @@ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.5.tgz", "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==", "license": "MIT", + "peer": true, "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", @@ -687,6 +688,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.27.1.tgz", "integrity": "sha512-p9OkPbZ5G7UT1MofwYFigGebnrzGJacoBSQM0/6bi/PUMVE+qlWDD/OalvQKbwgQzU6dl0xAv6r4X7Jme0RYxA==", "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, @@ -1570,6 +1572,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.27.1.tgz", "integrity": "sha512-2KH4LWGSrJIkVf5tSiBFYuXDAoWRq2MMwgivCf+93dd0GQi8RXLjKA/0EvRnVV5G0hrHczsquXuD01L8s6dmBw==", "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.1", "@babel/helper-module-imports": "^7.27.1", @@ -3319,6 +3322,7 @@ "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", "license": "MIT", + "peer": true, "funding": { "type": "opencollective", "url": "https://opencollective.com/popperjs" @@ -4184,6 +4188,7 @@ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.62.0.tgz", "integrity": "sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==", "license": "BSD-2-Clause", + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "5.62.0", "@typescript-eslint/types": "5.62.0", @@ -4577,6 +4582,7 @@ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -4675,6 +4681,7 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -5602,6 +5609,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "baseline-browser-mapping": "^2.8.25", "caniuse-lite": "^1.0.30001754", @@ -7634,6 +7642,7 @@ "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", @@ -7836,6 +7845,7 @@ "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.32.0.tgz", "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==", "license": "MIT", + "peer": true, "dependencies": { "@rtsao/scc": "^1.1.0", "array-includes": "^3.1.9", @@ -7914,6 +7924,7 @@ "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.10.2.tgz", "integrity": "sha512-scB3nz4WmG75pV8+3eRUQOHZlNSUhFNq37xnpgRkCCELU3XMvXAxLk1eqWWyE22Ki4Q01Fnsw9BA3cJHDPgn2Q==", "license": "MIT", + "peer": true, "dependencies": { "aria-query": "^5.3.2", "array-includes": "^3.1.8", @@ -7965,6 +7976,7 @@ "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.5.tgz", "integrity": "sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==", "license": "MIT", + "peer": true, "dependencies": { "array-includes": "^3.1.8", "array.prototype.findlast": "^1.2.5", @@ -7997,6 +8009,7 @@ "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.2.tgz", "integrity": "sha512-QzliNJq4GinDBcD8gPB5v0wh6g8q3SUi6EFF0x8N/BL9PoVs0atuGc47ozMRyOWAKdwaZ5OnbOEa3WR+dSGKuQ==", "license": "MIT", + "peer": true, "engines": { "node": ">=10" }, @@ -10576,6 +10589,7 @@ "resolved": "https://registry.npmjs.org/jest/-/jest-27.5.1.tgz", "integrity": "sha512-Yn0mADZB89zTtjkPJEXwrac3LHudkQMR+Paqa8uxJHCBr9agxztUifWCyiYrjhMPBoUVBjyny0I7XH6ozDr7QQ==", "license": "MIT", + "peer": true, "dependencies": { "@jest/core": "^27.5.1", "import-local": "^3.0.2", @@ -11573,6 +11587,7 @@ "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz", "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", "license": "MIT", + "peer": true, "bin": { "jiti": "bin/jiti.js" } @@ -13617,6 +13632,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", @@ -14763,6 +14779,7 @@ "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", "license": "MIT", + "peer": true, "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -14881,6 +14898,7 @@ "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", "dev": true, "license": "MIT", + "peer": true, "bin": { "prettier": "bin-prettier.js" }, @@ -15085,6 +15103,7 @@ "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", "license": "MIT", + "peer": true, "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", @@ -15266,6 +15285,7 @@ "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", "license": "MIT", + "peer": true, "dependencies": { "loose-envify": "^1.1.0" }, @@ -15339,6 +15359,7 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", "license": "MIT", + "peer": true, "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.2" @@ -15437,6 +15458,7 @@ "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz", "integrity": "sha512-F27qZr8uUqwhWZboondsPx8tnC3Ct3SxZA3V5WyEvujRyyNv0VYPhoBg1gZ8/MV5tubQp76Trw8lTv9hzRBa+A==", "license": "MIT", + "peer": true, "engines": { "node": ">=0.10.0" } @@ -16027,6 +16049,7 @@ "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.2.tgz", "integrity": "sha512-fS6iqSPZDs3dr/y7Od6y5nha8dW1YnbgtsyotCVvoFGKbERG++CVRFv1meyGDE1SNItQA8BrnCw7ScdAhRJ3XQ==", "license": "MIT", + "peer": true, "bin": { "rollup": "dist/bin/rollup" }, @@ -16188,6 +16211,7 @@ "resolved": "https://registry.npmjs.org/sass/-/sass-1.94.2.tgz", "integrity": "sha512-N+7WK20/wOr7CzA2snJcUSSNTCzeCGUTFY3OgeQP3mZ1aj9NMQ0mSTXwlrnd89j33zzQJGqIN52GIOmYrfq46A==", "license": "MIT", + "peer": true, "dependencies": { "chokidar": "^4.0.0", "immutable": "^5.0.2", @@ -16320,6 +16344,7 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -17862,6 +17887,7 @@ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -18071,6 +18097,7 @@ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", "license": "(MIT OR CC0-1.0)", + "peer": true, "engines": { "node": ">=10" }, @@ -18618,6 +18645,7 @@ "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.103.0.tgz", "integrity": "sha512-HU1JOuV1OavsZ+mfigY0j8d1TgQgbZ6M+J75zDkpEAwYeXjWSqrGJtgnPblJjd/mAyTNQ7ygw0MiKOn6etz8yw==", "license": "MIT", + "peer": true, "dependencies": { "@types/eslint-scope": "^3.7.7", "@types/estree": "^1.0.8", @@ -18689,6 +18717,7 @@ "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.15.2.tgz", "integrity": "sha512-0XavAZbNJ5sDrCbkpWL8mia0o5WPOd2YGtxrEiZkBK9FjLppIUK2TgxK6qGD2P3hUXTJNNPVibrerKcx5WkR1g==", "license": "MIT", + "peer": true, "dependencies": { "@types/bonjour": "^3.5.9", "@types/connect-history-api-fallback": "^1.3.5", @@ -19101,6 +19130,7 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -19424,6 +19454,7 @@ "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.2.tgz", "integrity": "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==", "license": "ISC", + "peer": true, "bin": { "yaml": "bin.mjs" }, diff --git a/scripts/add_complexity.py b/scripts/add_complexity.py new file mode 100644 index 00000000..185acad2 --- /dev/null +++ b/scripts/add_complexity.py @@ -0,0 +1,95 @@ +#!/usr/bin/env python3 +""" +Script to add time and space complexity fields to questions.json +Based on patterns, assigns appropriate complexity values +""" + +import json +import sys +from pathlib import Path + +# Pattern-based complexity mapping (optimal solution for each pattern) +COMPLEXITY_MAP = { + "Arrays": {"time": "O(n)", "space": "O(1)"}, + "Sliding Window": {"time": "O(n)", "space": "O(1)"}, + "Two Pointers": {"time": "O(n)", "space": "O(1)"}, + "Fast & Slow Pointers": {"time": "O(n)", "space": "O(1)"}, + "Binary Search": {"time": "O(log n)", "space": "O(1)"}, + "BFS": {"time": "O(V + E)", "space": "O(V)"}, + "DFS": {"time": "O(V + E)", "space": "O(V)"}, + "Backtracking": {"time": "O(2^n) or O(n!)", "space": "O(n)"}, + "Dynamic Programming": {"time": "O(n) or O(n²)", "space": "O(n) or O(n²)"}, + "Greedy": {"time": "O(n log n)", "space": "O(1)"}, + "Heap": {"time": "O(n log k)", "space": "O(k)"}, + "Graph": {"time": "O(V + E)", "space": "O(V)"}, + "Intervals": {"time": "O(n log n)", "space": "O(1)"}, + "Trie": {"time": "O(m)", "space": "O(ALPHABET_SIZE * N * M)"}, + "Union Find": {"time": "O(α(n))", "space": "O(n)"}, + "Topological Sort": {"time": "O(V + E)", "space": "O(V)"}, + "Bit Manipulation": {"time": "O(n)", "space": "O(1)"}, + "Sorting": {"time": "O(n log n)", "space": "O(1)"}, + "Bucket Sort": {"time": "O(n)", "space": "O(n)"}, + "QuickSelect": {"time": "O(n)", "space": "O(1)"}, + "In-place reversal of a linked list": {"time": "O(n)", "space": "O(1)"}, + "Design": {"time": "Varies", "space": "Varies"}, +} + +def get_complexity_for_patterns(patterns): + """ + Determine complexity based on patterns. + For multiple patterns, use the most complex one or combine appropriately. + """ + if not patterns: + return {"time": "O(n)", "space": "O(1)"} + + # For single pattern, return its complexity + if len(patterns) == 1: + pattern = patterns[0] + return COMPLEXITY_MAP.get(pattern, {"time": "O(n)", "space": "O(1)"}) + + # For multiple patterns, prioritize certain patterns + # DP and Backtracking are usually the dominant complexity + if "Dynamic Programming" in patterns: + return COMPLEXITY_MAP["Dynamic Programming"] + if "Backtracking" in patterns: + return COMPLEXITY_MAP["Backtracking"] + if "BFS" in patterns or "DFS" in patterns: + return COMPLEXITY_MAP["BFS"] + + # Default to first pattern's complexity + return COMPLEXITY_MAP.get(patterns[0], {"time": "O(n)", "space": "O(1)"}) + +def add_complexity_to_questions(input_file, output_file=None): + """Add time and space complexity to all questions""" + if output_file is None: + output_file = input_file + + # Read the JSON file + with open(input_file, 'r', encoding='utf-8') as f: + data = json.load(f) + + # Add complexity to each question + for question in data['data']: + if 'timeComplexity' not in question: + complexity = get_complexity_for_patterns(question.get('pattern', [])) + question['timeComplexity'] = complexity['time'] + question['spaceComplexity'] = complexity['space'] + + # Write back to file + with open(output_file, 'w', encoding='utf-8') as f: + json.dump(data, f, indent=2, ensure_ascii=False) + + print(f"Added complexity fields to {len(data['data'])} questions") + print(f"Output written to {output_file}") + +if __name__ == "__main__": + script_dir = Path(__file__).parent + repo_root = script_dir.parent + questions_file = repo_root / "src" / "data" / "questions.json" + + if not questions_file.exists(): + print(f"Error: {questions_file} not found") + sys.exit(1) + + add_complexity_to_questions(questions_file) + diff --git a/src/components/PatternGuides/index.js b/src/components/PatternGuides/index.js new file mode 100644 index 00000000..0f98d01b --- /dev/null +++ b/src/components/PatternGuides/index.js @@ -0,0 +1,135 @@ +import React, { useState } from 'react'; +import { Container, Row, Col, Card, CardBody, CardTitle, NavLink } from 'reactstrap'; +import ReactMarkdown from 'react-markdown'; +import { FaExternalLinkAlt } from 'react-icons/fa'; +import { Event } from '../Shared/Tracking'; + +import './styles.scss'; + +const PATTERNS = [ + { name: 'Arrays', file: 'arrays.md', slug: 'arrays' }, + { name: 'BFS', file: 'bfs.md', slug: 'bfs' }, + { name: 'Backtracking', file: 'backtracking.md', slug: 'backtracking' }, + { name: 'Binary Search', file: 'binary-search.md', slug: 'binary-search' }, + { name: 'Bit Manipulation', file: 'bit-manipulation.md', slug: 'bit-manipulation' }, + { name: 'Bucket Sort', file: 'bucket-sort.md', slug: 'bucket-sort' }, + { name: 'DFS', file: 'dfs.md', slug: 'dfs' }, + { name: 'Design', file: 'design.md', slug: 'design' }, + { name: 'Dynamic Programming', file: 'dynamic-programming.md', slug: 'dynamic-programming' }, + { name: 'Fast & Slow Pointers', file: 'fast-slow-pointers.md', slug: 'fast-slow-pointers' }, + { name: 'Graph', file: 'graph.md', slug: 'graph' }, + { name: 'Greedy', file: 'greedy.md', slug: 'greedy' }, + { name: 'Heap', file: 'heap.md', slug: 'heap' }, + { name: 'In-place reversal of a linked list', file: 'in-place-reversal-linked-list.md', slug: 'in-place-reversal-linked-list' }, + { name: 'Intervals', file: 'intervals.md', slug: 'intervals' }, + { name: 'QuickSelect', file: 'quickselect.md', slug: 'quickselect' }, + { name: 'Sliding Window', file: 'sliding-window.md', slug: 'sliding-window' }, + { name: 'Sorting', file: 'sorting.md', slug: 'sorting' }, + { name: 'Topological Sort', file: 'topological-sort.md', slug: 'topological-sort' }, + { name: 'Trie', file: 'trie.md', slug: 'trie' }, + { name: 'Two Pointers', file: 'two-pointers.md', slug: 'two-pointers' }, + { name: 'Union Find', file: 'union-find.md', slug: 'union-find' }, +]; + +const PatternGuides = () => { + const [selectedPattern, setSelectedPattern] = useState(null); + const [guideContent, setGuideContent] = useState(''); + + const loadGuide = async (pattern) => { + setSelectedPattern(pattern); + try { + // In production, guides would be served from public/guides + // For now, we'll link to GitHub + const response = await fetch( + `https://raw.githubusercontent.com/SeanPrashad/leetcode-patterns/main/guides/${pattern.file}` + ); + if (response.ok) { + const text = await response.text(); + setGuideContent(text); + } else { + setGuideContent(`# ${pattern.name}\n\nGuide content will be available soon. Please check the [GitHub repository](https://github.com/SeanPrashad/leetcode-patterns/tree/main/guides) for the latest guides.`); + } + } catch (error) { + setGuideContent(`# ${pattern.name}\n\nUnable to load guide. Please check the [GitHub repository](https://github.com/SeanPrashad/leetcode-patterns/tree/main/guides/${pattern.file}) for this guide.`); + } + }; + + return ( + + + +
+

Pattern Deep-Dive Guides

+

+ Comprehensive guides for each coding pattern used in LeetCode problems. + Each guide explains the core concept, when to use the pattern, provides template code, + and links to relevant problems. +

+ Event('PatternGuides', 'Clicked link', 'GitHub guides link')} + > + View all guides on GitHub + +
+ +
+ + + + + Available Patterns +
+ {PATTERNS.map((pattern) => ( + { + e.preventDefault(); + loadGuide(pattern); + Event('PatternGuides', 'Selected pattern', pattern.name); + }} + className={selectedPattern?.slug === pattern.slug ? 'active' : ''} + > + {pattern.name} + + ))} +
+
+
+ + + {selectedPattern ? ( + + +
+ {guideContent} +
+
+
+ ) : ( + + +

+ Select a pattern from the list to view its comprehensive guide. + Each guide includes: +

+
    +
  • Core Concept: What the pattern is and why it exists
  • +
  • When to Use: Problem characteristics that signal this pattern
  • +
  • Template Code: Skeleton code showing the structure
  • +
  • Related Problems: Links to problems using this pattern
  • +
  • Time & Space Complexity: Complexity analysis
  • +
+
+
+ )} + +
+
+ ); +}; + +export default PatternGuides; + diff --git a/src/components/PatternGuides/styles.scss b/src/components/PatternGuides/styles.scss new file mode 100644 index 00000000..5a1d0600 --- /dev/null +++ b/src/components/PatternGuides/styles.scss @@ -0,0 +1,99 @@ +.pattern-guides { + padding: 20px 0; + + .guides-header { + h2 { + margin-bottom: 10px; + } + + p { + margin-bottom: 15px; + } + } + + .pattern-list { + max-height: 600px; + overflow-y: auto; + + a { + display: block; + padding: 8px 12px; + margin: 4px 0; + border-radius: 4px; + text-decoration: none; + color: var(--text-color, #333); + transition: background-color 0.2s; + + &:hover { + background-color: var(--hover-bg, #f5f5f5); + } + + &.active { + background-color: var(--active-bg, #007bff); + color: white; + font-weight: bold; + } + } + } + + .guide-content { + h1, h2, h3, h4, h5, h6 { + margin-top: 20px; + margin-bottom: 10px; + } + + code { + background-color: var(--code-bg, #f4f4f4); + padding: 2px 6px; + border-radius: 3px; + font-family: 'Courier New', monospace; + font-size: 0.9em; + } + + pre { + background-color: var(--code-bg, #f4f4f4); + padding: 15px; + border-radius: 5px; + overflow-x: auto; + + code { + background-color: transparent; + padding: 0; + } + } + + ul, ol { + margin-left: 20px; + margin-bottom: 15px; + } + + li { + margin-bottom: 5px; + } + + blockquote { + border-left: 4px solid var(--border-color, #ddd); + padding-left: 15px; + margin: 15px 0; + color: var(--text-muted, #666); + } + + table { + width: 100%; + border-collapse: collapse; + margin: 15px 0; + + th, td { + border: 1px solid var(--border-color, #ddd); + padding: 8px 12px; + text-align: left; + } + + th { + background-color: var(--table-header-bg, #f8f9fa); + font-weight: bold; + } + } + } +} + diff --git a/src/components/Table/index.js b/src/components/Table/index.js index 3ec8863b..8fae8295 100644 --- a/src/components/Table/index.js +++ b/src/components/Table/index.js @@ -398,19 +398,63 @@ const Table = () => { disableSortBy: true, id: 'pattern', Cell: (cellInfo) => { + // Map pattern names to guide slugs + const patternToSlug = { + 'Arrays': 'arrays', + 'BFS': 'bfs', + 'Backtracking': 'backtracking', + 'Binary Search': 'binary-search', + 'Bit Manipulation': 'bit-manipulation', + 'Bucket Sort': 'bucket-sort', + 'DFS': 'dfs', + 'Design': 'design', + 'Dynamic Programming': 'dynamic-programming', + 'Fast & Slow Pointers': 'fast-slow-pointers', + 'Graph': 'graph', + 'Greedy': 'greedy', + 'Heap': 'heap', + 'In-place reversal of a linked list': 'in-place-reversal-linked-list', + 'Intervals': 'intervals', + 'QuickSelect': 'quickselect', + 'Sliding Window': 'sliding-window', + 'Sorting': 'sorting', + 'Topological Sort': 'topological-sort', + 'Trie': 'trie', + 'Two Pointers': 'two-pointers', + 'Union Find': 'union-find', + }; + const patterns = `${cellInfo.row.original.pattern}` .split(',') .map((pattern) => { + const trimmedPattern = pattern.trim(); + const slug = patternToSlug[trimmedPattern]; + const guideUrl = slug + ? `https://github.com/SeanPrashad/leetcode-patterns/blob/main/guides/${slug}.md` + : null; + if (showPatterns[0] || checked[cellInfo.row.original.id]) { return ( - - {pattern} + { + if (guideUrl) { + window.open(guideUrl, '_blank'); + Event('Table', 'Clicked pattern badge', trimmedPattern); + } + }} + title={guideUrl ? `Click to view ${trimmedPattern} guide` : ''} + > + {trimmedPattern} ); } return ( - + *** ); @@ -439,6 +483,30 @@ const Table = () => { ), Filter: SelectDifficultyColumnFilter, }, + { + Header: 'Time Complexity', + accessor: 'timeComplexity', + id: 'timeComplexity', + disableSortBy: true, + Cell: (cellInfo) => ( +
+ {cellInfo.row.original.timeComplexity || 'N/A'} +
+ ), + disableFilters: true, + }, + { + Header: 'Space Complexity', + accessor: 'spaceComplexity', + id: 'spaceComplexity', + disableSortBy: true, + Cell: (cellInfo) => ( +
+ {cellInfo.row.original.spaceComplexity || 'N/A'} +
+ ), + disableFilters: true, + }, { Header: () => { const date = new Date(updated); diff --git a/src/components/Tabs/index.js b/src/components/Tabs/index.js index d676971f..3a77f6d0 100644 --- a/src/components/Tabs/index.js +++ b/src/components/Tabs/index.js @@ -13,6 +13,7 @@ import { Event } from '../Shared/Tracking'; import Table from '../Table'; import Tips from '../Tips'; import Acknowledgements from '../Acknowledgements'; +import PatternGuides from '../PatternGuides'; import './styles.scss'; @@ -53,6 +54,17 @@ const Tabs = () => { className={classnames({ active: activeTab === '3' })} onClick={() => { toggle('3'); + Event('Tabs', 'Clicked Tab', 'Pattern Guides tab'); + }} + > + Pattern Guides + + + + { + toggle('4'); Event('Tabs', 'Clicked Tab', 'Acknowledgements tab'); }} > @@ -68,6 +80,9 @@ const Tabs = () => { + + + diff --git a/src/data/questions.json b/src/data/questions.json index 289083f7..f798775d 100644 --- a/src/data/questions.json +++ b/src/data/questions.json @@ -51,7 +51,9 @@ "slug": "visa", "frequency": 2 } - ] + ], + "timeComplexity": "O(n)", + "spaceComplexity": "O(1)" }, { "id": 1, @@ -104,7 +106,9 @@ "slug": "arista-networks", "frequency": 2 } - ] + ], + "timeComplexity": "O(n)", + "spaceComplexity": "O(1)" }, { "id": 2, @@ -126,7 +130,9 @@ "slug": "amazon", "frequency": 4 } - ] + ], + "timeComplexity": "O(n)", + "spaceComplexity": "O(1)" }, { "id": 3, @@ -169,7 +175,9 @@ "slug": "bloomberg", "frequency": 3 } - ] + ], + "timeComplexity": "O(n)", + "spaceComplexity": "O(1)" }, { "id": 4, @@ -261,7 +269,9 @@ "slug": "zs-associates", "frequency": 2 } - ] + ], + "timeComplexity": "O(n)", + "spaceComplexity": "O(1)" }, { "id": 5, @@ -315,7 +325,9 @@ "slug": "goldman-sachs", "frequency": 2 } - ] + ], + "timeComplexity": "O(n)", + "spaceComplexity": "O(1)" }, { "id": 6, @@ -352,7 +364,9 @@ "slug": "facebook", "frequency": 2 } - ] + ], + "timeComplexity": "O(n)", + "spaceComplexity": "O(1)" }, { "id": 7, @@ -434,7 +448,9 @@ "slug": "jpmorgan", "frequency": 2 } - ] + ], + "timeComplexity": "O(n)", + "spaceComplexity": "O(1)" }, { "id": 8, @@ -581,7 +597,9 @@ "slug": "pornhub", "frequency": 2 } - ] + ], + "timeComplexity": "O(n)", + "spaceComplexity": "O(1)" }, { "id": 9, @@ -678,7 +696,9 @@ "slug": "rakuten", "frequency": 2 } - ] + ], + "timeComplexity": "O(n)", + "spaceComplexity": "O(1)" }, { "id": 10, @@ -795,7 +815,9 @@ "slug": "grammarly", "frequency": 2 } - ] + ], + "timeComplexity": "O(2^n) or O(n!)", + "spaceComplexity": "O(n)" }, { "id": 11, @@ -877,7 +899,9 @@ "slug": "cognizant", "frequency": 2 } - ] + ], + "timeComplexity": "O(n)", + "spaceComplexity": "O(1)" }, { "id": 12, @@ -964,7 +988,9 @@ "slug": "deloitte", "frequency": 2 } - ] + ], + "timeComplexity": "O(n)", + "spaceComplexity": "O(1)" }, { "id": 13, @@ -981,7 +1007,9 @@ "slug": "facebook", "frequency": 5 } - ] + ], + "timeComplexity": "O(2^n) or O(n!)", + "spaceComplexity": "O(n)" }, { "id": 14, @@ -1033,7 +1061,9 @@ "slug": "wix", "frequency": 2 } - ] + ], + "timeComplexity": "O(2^n) or O(n!)", + "spaceComplexity": "O(n)" }, { "id": 15, @@ -1070,7 +1100,9 @@ "slug": "bloomberg", "frequency": 3 } - ] + ], + "timeComplexity": "O(2^n) or O(n!)", + "spaceComplexity": "O(n)" }, { "id": 16, @@ -1142,7 +1174,9 @@ "slug": "american-express", "frequency": 2 } - ] + ], + "timeComplexity": "O(2^n) or O(n!)", + "spaceComplexity": "O(n)" }, { "id": 17, @@ -1189,7 +1223,9 @@ "slug": "apple", "frequency": 2 } - ] + ], + "timeComplexity": "O(2^n) or O(n!)", + "spaceComplexity": "O(n)" }, { "id": 18, @@ -1221,7 +1257,9 @@ "slug": "microsoft", "frequency": 3 } - ] + ], + "timeComplexity": "O(2^n) or O(n!)", + "spaceComplexity": "O(n)" }, { "id": 19, @@ -1308,7 +1346,9 @@ "slug": "hpe", "frequency": 2 } - ] + ], + "timeComplexity": "O(2^n) or O(n!)", + "spaceComplexity": "O(n)" }, { "id": 20, @@ -1365,7 +1405,9 @@ "slug": "tiktok", "frequency": 2 } - ] + ], + "timeComplexity": "O(2^n) or O(n!)", + "spaceComplexity": "O(n)" }, { "id": 21, @@ -1387,7 +1429,9 @@ "slug": "google", "frequency": 3 } - ] + ], + "timeComplexity": "O(2^n) or O(n!)", + "spaceComplexity": "O(n)" }, { "id": 22, @@ -1484,7 +1528,9 @@ "slug": "epam-systems", "frequency": 2 } - ] + ], + "timeComplexity": "O(2^n) or O(n!)", + "spaceComplexity": "O(n)" }, { "id": 23, @@ -1533,7 +1579,9 @@ "slug": "myntra", "frequency": 2 } - ] + ], + "timeComplexity": "O(n) or O(n²)", + "spaceComplexity": "O(n) or O(n²)" }, { "id": 24, @@ -1570,7 +1618,9 @@ "slug": "bloomberg", "frequency": 3 } - ] + ], + "timeComplexity": "O(2^n) or O(n!)", + "spaceComplexity": "O(n)" }, { "id": 25, @@ -1657,7 +1707,9 @@ "slug": "de-shaw", "frequency": 2 } - ] + ], + "timeComplexity": "O(2^n) or O(n!)", + "spaceComplexity": "O(n)" }, { "id": 26, @@ -1668,7 +1720,9 @@ ], "difficulty": "Medium", "premium": true, - "companies": [] + "companies": [], + "timeComplexity": "O(2^n) or O(n!)", + "spaceComplexity": "O(n)" }, { "id": 27, @@ -1710,7 +1764,9 @@ "slug": "microsoft", "frequency": 2 } - ] + ], + "timeComplexity": "O(2^n) or O(n!)", + "spaceComplexity": "O(n)" }, { "id": 28, @@ -1772,7 +1828,9 @@ "slug": "ibm", "frequency": 2 } - ] + ], + "timeComplexity": "O(2^n) or O(n!)", + "spaceComplexity": "O(n)" }, { "id": 29, @@ -1844,7 +1902,9 @@ "slug": "qualcomm", "frequency": 2 } - ] + ], + "timeComplexity": "O(n) or O(n²)", + "spaceComplexity": "O(n) or O(n²)" }, { "id": 30, @@ -1926,7 +1986,9 @@ "slug": "tcs", "frequency": 2 } - ] + ], + "timeComplexity": "O(n) or O(n²)", + "spaceComplexity": "O(n) or O(n²)" }, { "id": 31, @@ -2153,7 +2215,9 @@ "slug": "ozon", "frequency": 2 } - ] + ], + "timeComplexity": "O(n log n)", + "spaceComplexity": "O(1)" }, { "id": 32, @@ -2300,7 +2364,9 @@ "slug": "zomato", "frequency": 2 } - ] + ], + "timeComplexity": "O(n) or O(n²)", + "spaceComplexity": "O(n) or O(n²)" }, { "id": 33, @@ -2332,7 +2398,9 @@ "slug": "bloomberg", "frequency": 2 } - ] + ], + "timeComplexity": "O(n) or O(n²)", + "spaceComplexity": "O(n) or O(n²)" }, { "id": 34, @@ -2389,7 +2457,9 @@ "slug": "docusign", "frequency": 2 } - ] + ], + "timeComplexity": "O(n) or O(n²)", + "spaceComplexity": "O(n) or O(n²)" }, { "id": 35, @@ -2486,7 +2556,9 @@ "slug": "mastercard", "frequency": 2 } - ] + ], + "timeComplexity": "O(n) or O(n²)", + "spaceComplexity": "O(n) or O(n²)" }, { "id": 36, @@ -2553,7 +2625,9 @@ "slug": "siemens", "frequency": 2 } - ] + ], + "timeComplexity": "O(n) or O(n²)", + "spaceComplexity": "O(n) or O(n²)" }, { "id": 37, @@ -2640,7 +2714,9 @@ "slug": "nvidia", "frequency": 2 } - ] + ], + "timeComplexity": "O(n) or O(n²)", + "spaceComplexity": "O(n) or O(n²)" }, { "id": 38, @@ -2822,7 +2898,9 @@ "slug": "autodesk", "frequency": 2 } - ] + ], + "timeComplexity": "O(n) or O(n²)", + "spaceComplexity": "O(n) or O(n²)" }, { "id": 39, @@ -2899,7 +2977,9 @@ "slug": "millennium", "frequency": 2 } - ] + ], + "timeComplexity": "O(n) or O(n²)", + "spaceComplexity": "O(n) or O(n²)" }, { "id": 40, @@ -2926,7 +3006,9 @@ "slug": "facebook", "frequency": 2 } - ] + ], + "timeComplexity": "O(n) or O(n²)", + "spaceComplexity": "O(n) or O(n²)" }, { "id": 41, @@ -2998,7 +3080,9 @@ "slug": "flipkart", "frequency": 2 } - ] + ], + "timeComplexity": "O(n) or O(n²)", + "spaceComplexity": "O(n) or O(n²)" }, { "id": 42, @@ -3050,7 +3134,9 @@ "slug": "akamai", "frequency": 2 } - ] + ], + "timeComplexity": "O(n) or O(n²)", + "spaceComplexity": "O(n) or O(n²)" }, { "id": 43, @@ -3143,7 +3229,9 @@ "slug": "navi", "frequency": 2 } - ] + ], + "timeComplexity": "O(n) or O(n²)", + "spaceComplexity": "O(n) or O(n²)" }, { "id": 44, @@ -3225,7 +3313,9 @@ "slug": "netskope", "frequency": 2 } - ] + ], + "timeComplexity": "O(n) or O(n²)", + "spaceComplexity": "O(n) or O(n²)" }, { "id": 45, @@ -3242,7 +3332,9 @@ "slug": "google", "frequency": 2 } - ] + ], + "timeComplexity": "O(n) or O(n²)", + "spaceComplexity": "O(n) or O(n²)" }, { "id": 46, @@ -3289,7 +3381,9 @@ "slug": "tiktok", "frequency": 3 } - ] + ], + "timeComplexity": "O(n) or O(n²)", + "spaceComplexity": "O(n) or O(n²)" }, { "id": 47, @@ -3316,7 +3410,9 @@ "slug": "facebook", "frequency": 2 } - ] + ], + "timeComplexity": "O(n) or O(n²)", + "spaceComplexity": "O(n) or O(n²)" }, { "id": 48, @@ -3343,7 +3439,9 @@ "slug": "tiktok", "frequency": 2 } - ] + ], + "timeComplexity": "O(n) or O(n²)", + "spaceComplexity": "O(n) or O(n²)" }, { "id": 49, @@ -3381,7 +3479,9 @@ "slug": "facebook", "frequency": 2 } - ] + ], + "timeComplexity": "O(n) or O(n²)", + "spaceComplexity": "O(n) or O(n²)" }, { "id": 50, @@ -3423,7 +3523,9 @@ "slug": "facebook", "frequency": 2 } - ] + ], + "timeComplexity": "O(n)", + "spaceComplexity": "O(1)" }, { "id": 51, @@ -3470,7 +3572,9 @@ "slug": "apple", "frequency": 2 } - ] + ], + "timeComplexity": "O(n)", + "spaceComplexity": "O(1)" }, { "id": 52, @@ -3547,7 +3651,9 @@ "slug": "epam-systems", "frequency": 2 } - ] + ], + "timeComplexity": "O(n)", + "spaceComplexity": "O(1)" }, { "id": 53, @@ -3589,7 +3695,9 @@ "slug": "ebay", "frequency": 2 } - ] + ], + "timeComplexity": "O(n)", + "spaceComplexity": "O(1)" }, { "id": 54, @@ -3626,7 +3734,9 @@ "slug": "microsoft", "frequency": 2 } - ] + ], + "timeComplexity": "O(n)", + "spaceComplexity": "O(1)" }, { "id": 55, @@ -3668,7 +3778,9 @@ "slug": "facebook", "frequency": 2 } - ] + ], + "timeComplexity": "O(n)", + "spaceComplexity": "O(1)" }, { "id": 56, @@ -3705,7 +3817,9 @@ "slug": "american-express", "frequency": 2 } - ] + ], + "timeComplexity": "O(n)", + "spaceComplexity": "O(1)" }, { "id": 57, @@ -3782,7 +3896,9 @@ "slug": "zoho", "frequency": 2 } - ] + ], + "timeComplexity": "O(n)", + "spaceComplexity": "O(1)" }, { "id": 58, @@ -3859,7 +3975,9 @@ "slug": "tiktok", "frequency": 2 } - ] + ], + "timeComplexity": "O(n)", + "spaceComplexity": "O(1)" }, { "id": 59, @@ -3906,7 +4024,9 @@ "slug": "oracle", "frequency": 2 } - ] + ], + "timeComplexity": "O(n)", + "spaceComplexity": "O(1)" }, { "id": 60, @@ -3958,7 +4078,9 @@ "slug": "arista-networks", "frequency": 2 } - ] + ], + "timeComplexity": "O(n)", + "spaceComplexity": "O(1)" }, { "id": 61, @@ -3996,7 +4118,9 @@ "slug": "nutanix", "frequency": 2 } - ] + ], + "timeComplexity": "O(V + E)", + "spaceComplexity": "O(V)" }, { "id": 62, @@ -4230,7 +4354,9 @@ "slug": "barclays", "frequency": 2 } - ] + ], + "timeComplexity": "O(V + E)", + "spaceComplexity": "O(V)" }, { "id": 63, @@ -4260,7 +4386,9 @@ "slug": "facebook", "frequency": 2 } - ] + ], + "timeComplexity": "O(V + E)", + "spaceComplexity": "O(V)" }, { "id": 64, @@ -4300,7 +4428,9 @@ "slug": "facebook", "frequency": 2 } - ] + ], + "timeComplexity": "O(V + E)", + "spaceComplexity": "O(V)" }, { "id": 65, @@ -4362,7 +4492,9 @@ "slug": "nutanix", "frequency": 2 } - ] + ], + "timeComplexity": "O(n)", + "spaceComplexity": "O(1)" }, { "id": 66, @@ -4409,7 +4541,9 @@ "slug": "linkedin", "frequency": 2 } - ] + ], + "timeComplexity": "O(n)", + "spaceComplexity": "O(1)" }, { "id": 67, @@ -4456,7 +4590,9 @@ "slug": "yandex", "frequency": 2 } - ] + ], + "timeComplexity": "O(n)", + "spaceComplexity": "O(1)" }, { "id": 68, @@ -4493,7 +4629,9 @@ "slug": "bloomberg", "frequency": 3 } - ] + ], + "timeComplexity": "O(n)", + "spaceComplexity": "O(1)" }, { "id": 69, @@ -4570,7 +4708,9 @@ "slug": "cadence", "frequency": 2 } - ] + ], + "timeComplexity": "O(n)", + "spaceComplexity": "O(1)" }, { "id": 70, @@ -4667,7 +4807,9 @@ "slug": "palo-alto-networks", "frequency": 2 } - ] + ], + "timeComplexity": "O(n)", + "spaceComplexity": "O(1)" }, { "id": 71, @@ -4705,7 +4847,9 @@ "slug": "apple", "frequency": 2 } - ] + ], + "timeComplexity": "O(log n)", + "spaceComplexity": "O(1)" }, { "id": 72, @@ -4742,7 +4886,9 @@ "slug": "facebook", "frequency": 2 } - ] + ], + "timeComplexity": "O(n log k)", + "spaceComplexity": "O(k)" }, { "id": 73, @@ -4854,7 +5000,9 @@ "slug": "yandex", "frequency": 2 } - ] + ], + "timeComplexity": "O(n log k)", + "spaceComplexity": "O(k)" }, { "id": 74, @@ -4876,7 +5024,9 @@ "slug": "linkedin", "frequency": 2 } - ] + ], + "timeComplexity": "O(n log k)", + "spaceComplexity": "O(k)" }, { "id": 75, @@ -4923,7 +5073,9 @@ "slug": "apple", "frequency": 2 } - ] + ], + "timeComplexity": "O(n log n)", + "spaceComplexity": "O(1)" }, { "id": 76, @@ -5195,7 +5347,9 @@ "slug": "ripple", "frequency": 2 } - ] + ], + "timeComplexity": "O(n log n)", + "spaceComplexity": "O(1)" }, { "id": 77, @@ -5242,7 +5396,9 @@ "slug": "nuro", "frequency": 2 } - ] + ], + "timeComplexity": "O(n log n)", + "spaceComplexity": "O(1)" }, { "id": 78, @@ -5294,7 +5450,9 @@ "slug": "goldman-sachs", "frequency": 2 } - ] + ], + "timeComplexity": "O(n log n)", + "spaceComplexity": "O(1)" }, { "id": 79, @@ -5412,7 +5570,9 @@ "slug": "snowflake", "frequency": 2 } - ] + ], + "timeComplexity": "O(n log k)", + "spaceComplexity": "O(k)" }, { "id": 80, @@ -5505,7 +5665,9 @@ "slug": "goldman-sachs", "frequency": 2 } - ] + ], + "timeComplexity": "O(n log n)", + "spaceComplexity": "O(1)" }, { "id": 81, @@ -5542,7 +5704,9 @@ "slug": "goldman-sachs", "frequency": 2 } - ] + ], + "timeComplexity": "O(n log n)", + "spaceComplexity": "O(1)" }, { "id": 82, @@ -5584,7 +5748,9 @@ "slug": "phonepe", "frequency": 2 } - ] + ], + "timeComplexity": "O(n log n)", + "spaceComplexity": "O(1)" }, { "id": 83, @@ -5607,7 +5773,9 @@ "slug": "google", "frequency": 2 } - ] + ], + "timeComplexity": "O(n log k)", + "spaceComplexity": "O(k)" }, { "id": 84, @@ -5649,7 +5817,9 @@ "slug": "pure-storage", "frequency": 2 } - ] + ], + "timeComplexity": "O(log n)", + "spaceComplexity": "O(1)" }, { "id": 85, @@ -5681,7 +5851,9 @@ "slug": "facebook", "frequency": 2 } - ] + ], + "timeComplexity": "O(log n)", + "spaceComplexity": "O(1)" }, { "id": 86, @@ -5718,7 +5890,9 @@ "slug": "microsoft", "frequency": 2 } - ] + ], + "timeComplexity": "O(log n)", + "spaceComplexity": "O(1)" }, { "id": 87, @@ -5775,7 +5949,9 @@ "slug": "yandex", "frequency": 2 } - ] + ], + "timeComplexity": "O(log n)", + "spaceComplexity": "O(1)" }, { "id": 88, @@ -5867,7 +6043,9 @@ "slug": "waymo", "frequency": 2 } - ] + ], + "timeComplexity": "O(log n)", + "spaceComplexity": "O(1)" }, { "id": 89, @@ -6014,7 +6192,9 @@ "slug": "palo-alto-networks", "frequency": 2 } - ] + ], + "timeComplexity": "O(log n)", + "spaceComplexity": "O(1)" }, { "id": 90, @@ -6051,7 +6231,9 @@ "slug": "google", "frequency": 2 } - ] + ], + "timeComplexity": "O(log n)", + "spaceComplexity": "O(1)" }, { "id": 91, @@ -6118,7 +6300,9 @@ "slug": "nutanix", "frequency": 2 } - ] + ], + "timeComplexity": "O(log n)", + "spaceComplexity": "O(1)" }, { "id": 92, @@ -6165,7 +6349,9 @@ "slug": "paypal", "frequency": 2 } - ] + ], + "timeComplexity": "O(log n)", + "spaceComplexity": "O(1)" }, { "id": 93, @@ -6232,7 +6418,9 @@ "slug": "uber", "frequency": 2 } - ] + ], + "timeComplexity": "O(log n)", + "spaceComplexity": "O(1)" }, { "id": 94, @@ -6243,7 +6431,9 @@ ], "difficulty": "Hard", "premium": false, - "companies": [] + "companies": [], + "timeComplexity": "O(log n)", + "spaceComplexity": "O(1)" }, { "id": 95, @@ -6290,7 +6480,9 @@ "slug": "oracle", "frequency": 2 } - ] + ], + "timeComplexity": "O(n)", + "spaceComplexity": "O(1)" }, { "id": 96, @@ -6332,7 +6524,9 @@ "slug": "microsoft", "frequency": 2 } - ] + ], + "timeComplexity": "O(n)", + "spaceComplexity": "O(1)" }, { "id": 97, @@ -6384,7 +6578,9 @@ "slug": "cisco", "frequency": 2 } - ] + ], + "timeComplexity": "O(n)", + "spaceComplexity": "O(1)" }, { "id": 98, @@ -6451,7 +6647,9 @@ "slug": "uipath", "frequency": 2 } - ] + ], + "timeComplexity": "O(n)", + "spaceComplexity": "O(1)" }, { "id": 99, @@ -6568,7 +6766,9 @@ "slug": "zeta", "frequency": 2 } - ] + ], + "timeComplexity": "O(n)", + "spaceComplexity": "O(1)" }, { "id": 100, @@ -6820,7 +7020,9 @@ "slug": "kpmg", "frequency": 2 } - ] + ], + "timeComplexity": "O(n)", + "spaceComplexity": "O(1)" }, { "id": 101, @@ -6837,7 +7039,9 @@ "slug": "google", "frequency": 4 } - ] + ], + "timeComplexity": "O(n)", + "spaceComplexity": "O(1)" }, { "id": 102, @@ -6854,7 +7058,9 @@ "slug": "amazon", "frequency": 2 } - ] + ], + "timeComplexity": "O(n)", + "spaceComplexity": "O(1)" }, { "id": 103, @@ -6956,7 +7162,9 @@ "slug": "meesho", "frequency": 2 } - ] + ], + "timeComplexity": "O(n)", + "spaceComplexity": "O(1)" }, { "id": 104, @@ -6993,7 +7201,9 @@ "slug": "microsoft", "frequency": 2 } - ] + ], + "timeComplexity": "O(n)", + "spaceComplexity": "O(1)" }, { "id": 105, @@ -7040,7 +7250,9 @@ "slug": "bloomberg", "frequency": 2 } - ] + ], + "timeComplexity": "O(V + E)", + "spaceComplexity": "O(V)" }, { "id": 106, @@ -7087,7 +7299,9 @@ "slug": "microsoft", "frequency": 2 } - ] + ], + "timeComplexity": "O(n log k)", + "spaceComplexity": "O(k)" }, { "id": 107, @@ -7254,7 +7468,9 @@ "slug": "robinhood", "frequency": 2 } - ] + ], + "timeComplexity": "O(n log k)", + "spaceComplexity": "O(k)" }, { "id": 108, @@ -7301,7 +7517,9 @@ "slug": "epam-systems", "frequency": 2 } - ] + ], + "timeComplexity": "O(n log k)", + "spaceComplexity": "O(k)" }, { "id": 109, @@ -7394,7 +7612,9 @@ "slug": "coupang", "frequency": 2 } - ] + ], + "timeComplexity": "O(n log k)", + "spaceComplexity": "O(k)" }, { "id": 110, @@ -7452,7 +7672,9 @@ "slug": "audible", "frequency": 2 } - ] + ], + "timeComplexity": "O(n log n)", + "spaceComplexity": "O(1)" }, { "id": 111, @@ -7464,7 +7686,9 @@ ], "difficulty": "Hard", "premium": true, - "companies": [] + "companies": [], + "timeComplexity": "O(n log n)", + "spaceComplexity": "O(1)" }, { "id": 112, @@ -7482,7 +7706,9 @@ "slug": "amazon", "frequency": 3 } - ] + ], + "timeComplexity": "O(n log n)", + "spaceComplexity": "O(1)" }, { "id": 113, @@ -7520,7 +7746,9 @@ "slug": "nutanix", "frequency": 2 } - ] + ], + "timeComplexity": "O(n)", + "spaceComplexity": "O(n)" }, { "id": 114, @@ -7640,7 +7868,9 @@ "slug": "ixl", "frequency": 2 } - ] + ], + "timeComplexity": "O(V + E)", + "spaceComplexity": "O(V)" }, { "id": 115, @@ -7775,7 +8005,9 @@ "slug": "remitly", "frequency": 2 } - ] + ], + "timeComplexity": "O(V + E)", + "spaceComplexity": "O(V)" }, { "id": 116, @@ -7799,7 +8031,9 @@ "slug": "splunk", "frequency": 2 } - ] + ], + "timeComplexity": "O(V + E)", + "spaceComplexity": "O(V)" }, { "id": 117, @@ -7852,7 +8086,9 @@ "slug": "microsoft", "frequency": 2 } - ] + ], + "timeComplexity": "O(V + E)", + "spaceComplexity": "O(V)" }, { "id": 118, @@ -7870,7 +8106,9 @@ "slug": "google", "frequency": 5 } - ] + ], + "timeComplexity": "O(V + E)", + "spaceComplexity": "O(V)" }, { "id": 119, @@ -7892,7 +8130,9 @@ "slug": "facebook", "frequency": 2 } - ] + ], + "timeComplexity": "O(V + E)", + "spaceComplexity": "O(V)" }, { "id": 120, @@ -7914,7 +8154,9 @@ "slug": "google", "frequency": 2 } - ] + ], + "timeComplexity": "O(V + E)", + "spaceComplexity": "O(V)" }, { "id": 121, @@ -7942,7 +8184,9 @@ "slug": "amazon", "frequency": 2 } - ] + ], + "timeComplexity": "O(V + E)", + "spaceComplexity": "O(V)" }, { "id": 122, @@ -7994,7 +8238,9 @@ "slug": "palo-alto-networks", "frequency": 2 } - ] + ], + "timeComplexity": "O(V + E)", + "spaceComplexity": "O(V)" }, { "id": 123, @@ -8071,7 +8317,9 @@ "slug": "tiktok", "frequency": 2 } - ] + ], + "timeComplexity": "O(V + E)", + "spaceComplexity": "O(V)" }, { "id": 124, @@ -8129,7 +8377,9 @@ "slug": "servicenow", "frequency": 2 } - ] + ], + "timeComplexity": "O(V + E)", + "spaceComplexity": "O(V)" }, { "id": 125, @@ -8177,7 +8427,9 @@ "slug": "google", "frequency": 2 } - ] + ], + "timeComplexity": "O(V + E)", + "spaceComplexity": "O(V)" }, { "id": 126, @@ -8224,7 +8476,9 @@ "slug": "apple", "frequency": 2 } - ] + ], + "timeComplexity": "O(V + E)", + "spaceComplexity": "O(V)" }, { "id": 127, @@ -8266,7 +8520,9 @@ "slug": "goldman-sachs", "frequency": 2 } - ] + ], + "timeComplexity": "O(V + E)", + "spaceComplexity": "O(V)" }, { "id": 128, @@ -8323,7 +8579,9 @@ "slug": "facebook", "frequency": 2 } - ] + ], + "timeComplexity": "O(V + E)", + "spaceComplexity": "O(V)" }, { "id": 129, @@ -8370,7 +8628,9 @@ "slug": "verkada", "frequency": 2 } - ] + ], + "timeComplexity": "O(V + E)", + "spaceComplexity": "O(V)" }, { "id": 130, @@ -8407,7 +8667,9 @@ "slug": "josh-technology", "frequency": 2 } - ] + ], + "timeComplexity": "O(V + E)", + "spaceComplexity": "O(V)" }, { "id": 131, @@ -8449,7 +8711,9 @@ "slug": "microsoft", "frequency": 2 } - ] + ], + "timeComplexity": "O(V + E)", + "spaceComplexity": "O(V)" }, { "id": 132, @@ -8476,7 +8740,9 @@ "slug": "facebook", "frequency": 3 } - ] + ], + "timeComplexity": "O(V + E)", + "spaceComplexity": "O(V)" }, { "id": 133, @@ -8518,7 +8784,9 @@ "slug": "palo-alto-networks", "frequency": 2 } - ] + ], + "timeComplexity": "O(V + E)", + "spaceComplexity": "O(V)" }, { "id": 134, @@ -8560,7 +8828,9 @@ "slug": "oracle", "frequency": 2 } - ] + ], + "timeComplexity": "O(V + E)", + "spaceComplexity": "O(V)" }, { "id": 135, @@ -8602,7 +8872,9 @@ "slug": "salesforce", "frequency": 2 } - ] + ], + "timeComplexity": "O(V + E)", + "spaceComplexity": "O(V)" }, { "id": 136, @@ -8679,7 +8951,9 @@ "slug": "adobe", "frequency": 2 } - ] + ], + "timeComplexity": "O(V + E)", + "spaceComplexity": "O(V)" }, { "id": 137, @@ -8690,7 +8964,9 @@ ], "difficulty": "Medium", "premium": false, - "companies": [] + "companies": [], + "timeComplexity": "O(V + E)", + "spaceComplexity": "O(V)" }, { "id": 138, @@ -8732,7 +9008,9 @@ "slug": "bytedance", "frequency": 2 } - ] + ], + "timeComplexity": "O(V + E)", + "spaceComplexity": "O(V)" }, { "id": 139, @@ -8769,7 +9047,9 @@ "slug": "bloomberg", "frequency": 3 } - ] + ], + "timeComplexity": "O(V + E)", + "spaceComplexity": "O(V)" }, { "id": 140, @@ -8826,7 +9106,9 @@ "slug": "salesforce", "frequency": 2 } - ] + ], + "timeComplexity": "O(V + E)", + "spaceComplexity": "O(V)" }, { "id": 141, @@ -8904,7 +9186,9 @@ "slug": "grammarly", "frequency": 2 } - ] + ], + "timeComplexity": "Varies", + "spaceComplexity": "Varies" }, { "id": 142, @@ -8976,7 +9260,9 @@ "slug": "snapchat", "frequency": 2 } - ] + ], + "timeComplexity": "O(V + E)", + "spaceComplexity": "O(V)" }, { "id": 143, @@ -9058,7 +9344,9 @@ "slug": "tcs", "frequency": 2 } - ] + ], + "timeComplexity": "Varies", + "spaceComplexity": "Varies" }, { "id": 144, @@ -9136,7 +9424,9 @@ "slug": "aurora", "frequency": 2 } - ] + ], + "timeComplexity": "O(V + E)", + "spaceComplexity": "O(V)" }, { "id": 145, @@ -9273,7 +9563,9 @@ "slug": "kla", "frequency": 2 } - ] + ], + "timeComplexity": "O(n log k)", + "spaceComplexity": "O(k)" }, { "id": 146, @@ -9310,7 +9602,9 @@ "slug": "microsoft", "frequency": 2 } - ] + ], + "timeComplexity": "O(n log k)", + "spaceComplexity": "O(k)" }, { "id": 147, @@ -9587,7 +9881,9 @@ "slug": "thoughtworks", "frequency": 2 } - ] + ], + "timeComplexity": "O(n)", + "spaceComplexity": "O(1)" }, { "id": 148, @@ -9634,7 +9930,9 @@ "slug": "microsoft", "frequency": 2 } - ] + ], + "timeComplexity": "O(n)", + "spaceComplexity": "O(1)" }, { "id": 149, @@ -9676,7 +9974,9 @@ "slug": "agoda", "frequency": 2 } - ] + ], + "timeComplexity": "O(n)", + "spaceComplexity": "O(1)" }, { "id": 150, @@ -9783,7 +10083,9 @@ "slug": "meesho", "frequency": 2 } - ] + ], + "timeComplexity": "O(n)", + "spaceComplexity": "O(1)" }, { "id": 151, @@ -9825,7 +10127,9 @@ "slug": "flipkart", "frequency": 2 } - ] + ], + "timeComplexity": "O(n)", + "spaceComplexity": "O(1)" }, { "id": 152, @@ -9882,7 +10186,9 @@ "slug": "squarepoint-capital", "frequency": 2 } - ] + ], + "timeComplexity": "O(n)", + "spaceComplexity": "O(1)" }, { "id": 153, @@ -9984,7 +10290,9 @@ "slug": "capgemini", "frequency": 2 } - ] + ], + "timeComplexity": "O(n)", + "spaceComplexity": "O(1)" }, { "id": 154, @@ -10181,7 +10489,9 @@ "slug": "qualcomm", "frequency": 2 } - ] + ], + "timeComplexity": "O(n)", + "spaceComplexity": "O(1)" }, { "id": 155, @@ -10283,7 +10593,9 @@ "slug": "hashedin", "frequency": 2 } - ] + ], + "timeComplexity": "O(n)", + "spaceComplexity": "O(1)" }, { "id": 156, @@ -10294,7 +10606,9 @@ ], "difficulty": "Medium", "premium": false, - "companies": [] + "companies": [], + "timeComplexity": "O(m)", + "spaceComplexity": "O(ALPHABET_SIZE * N * M)" }, { "id": 157, @@ -10305,7 +10619,9 @@ ], "difficulty": "Easy", "premium": true, - "companies": [] + "companies": [], + "timeComplexity": "O(m)", + "spaceComplexity": "O(ALPHABET_SIZE * N * M)" }, { "id": 158, @@ -10327,7 +10643,9 @@ "slug": "amazon", "frequency": 2 } - ] + ], + "timeComplexity": "O(m)", + "spaceComplexity": "O(ALPHABET_SIZE * N * M)" }, { "id": 159, @@ -10344,7 +10662,9 @@ "slug": "amazon", "frequency": 11 } - ] + ], + "timeComplexity": "O(m)", + "spaceComplexity": "O(ALPHABET_SIZE * N * M)" }, { "id": 160, @@ -10355,7 +10675,9 @@ ], "difficulty": "Hard", "premium": false, - "companies": [] + "companies": [], + "timeComplexity": "O(m)", + "spaceComplexity": "O(ALPHABET_SIZE * N * M)" }, { "id": 161, @@ -10387,7 +10709,9 @@ "slug": "airbnb", "frequency": 2 } - ] + ], + "timeComplexity": "O(m)", + "spaceComplexity": "O(ALPHABET_SIZE * N * M)" }, { "id": 162, @@ -10439,7 +10763,9 @@ "slug": "mongodb", "frequency": 2 } - ] + ], + "timeComplexity": "O(m)", + "spaceComplexity": "O(ALPHABET_SIZE * N * M)" }, { "id": 163, @@ -10450,7 +10776,9 @@ ], "difficulty": "Hard", "premium": true, - "companies": [] + "companies": [], + "timeComplexity": "O(m)", + "spaceComplexity": "O(ALPHABET_SIZE * N * M)" }, { "id": 164, @@ -10474,7 +10802,9 @@ "slug": "bloomberg", "frequency": 2 } - ] + ], + "timeComplexity": "O(V + E)", + "spaceComplexity": "O(V)" }, { "id": 165, @@ -10561,7 +10891,9 @@ "slug": "autodesk", "frequency": 2 } - ] + ], + "timeComplexity": "O(log n)", + "spaceComplexity": "O(1)" }, { "id": 166, @@ -10623,7 +10955,9 @@ "slug": "autodesk", "frequency": 2 } - ] + ], + "timeComplexity": "O(n log n)", + "spaceComplexity": "O(1)" }, { "id": 167, @@ -10634,7 +10968,9 @@ ], "difficulty": "Easy", "premium": false, - "companies": [] + "companies": [], + "timeComplexity": "O(n)", + "spaceComplexity": "O(1)" }, { "id": 168, @@ -10732,7 +11068,9 @@ "slug": "netapp", "frequency": 2 } - ] + ], + "timeComplexity": "O(n)", + "spaceComplexity": "O(1)" }, { "id": 169, @@ -10774,7 +11112,9 @@ "slug": "tinkoff", "frequency": 2 } - ] + ], + "timeComplexity": "O(n)", + "spaceComplexity": "O(1)" }, { "id": 170, @@ -10812,7 +11152,9 @@ "slug": "bloomberg", "frequency": 2 } - ] + ], + "timeComplexity": "O(2^n) or O(n!)", + "spaceComplexity": "O(n)" }, { "id": 171, @@ -10830,7 +11172,9 @@ "slug": "linkedin", "frequency": 3 } - ] + ], + "timeComplexity": "O(2^n) or O(n!)", + "spaceComplexity": "O(n)" }, { "id": 172, @@ -10841,7 +11185,9 @@ ], "difficulty": "Medium", "premium": false, - "companies": [] + "companies": [], + "timeComplexity": "O(2^n) or O(n!)", + "spaceComplexity": "O(n)" }, { "id": 173, @@ -10878,7 +11224,9 @@ "slug": "goldman-sachs", "frequency": 2 } - ] + ], + "timeComplexity": "O(n)", + "spaceComplexity": "O(1)" }, { "id": 174, @@ -10955,7 +11303,9 @@ "slug": "visa", "frequency": 2 } - ] + ], + "timeComplexity": "O(n log n)", + "spaceComplexity": "O(1)" } ] } \ No newline at end of file From ca7506dd9959039471c07ac597d1e8c0000c7800 Mon Sep 17 00:00:00 2001 From: Bhavesh Khandelwal Date: Sun, 14 Dec 2025 10:40:24 +0530 Subject: [PATCH 2/2] Remove guide files and helper documentation - Remove pattern guide markdown files from /guides directory - Remove helper documentation files (LOCAL_SETUP.md, PUSH_GUIDE.md) - Keep complexity analysis and UI improvements --- LOCAL_SETUP.md | 59 ------- PUSH_GUIDE.md | 149 ----------------- guides/README.md | 40 ----- guides/arrays.md | 139 --------------- guides/backtracking.md | 196 ---------------------- guides/bfs.md | 176 ------------------- guides/binary-search.md | 182 -------------------- guides/bit-manipulation.md | 143 ---------------- guides/bucket-sort.md | 127 -------------- guides/design.md | 161 ------------------ guides/dfs.md | 189 --------------------- guides/dynamic-programming.md | 199 ---------------------- guides/fast-slow-pointers.md | 159 ------------------ guides/graph.md | 208 ----------------------- guides/greedy.md | 149 ----------------- guides/heap.md | 214 ------------------------ guides/in-place-reversal-linked-list.md | 144 ---------------- guides/intervals.md | 179 -------------------- guides/quickselect.md | 120 ------------- guides/sliding-window.md | 131 --------------- guides/sorting.md | 135 --------------- guides/topological-sort.md | 164 ------------------ guides/trie.md | 189 --------------------- guides/two-pointers.md | 153 ----------------- guides/union-find.md | 172 ------------------- 25 files changed, 3877 deletions(-) delete mode 100644 LOCAL_SETUP.md delete mode 100644 PUSH_GUIDE.md delete mode 100644 guides/README.md delete mode 100644 guides/arrays.md delete mode 100644 guides/backtracking.md delete mode 100644 guides/bfs.md delete mode 100644 guides/binary-search.md delete mode 100644 guides/bit-manipulation.md delete mode 100644 guides/bucket-sort.md delete mode 100644 guides/design.md delete mode 100644 guides/dfs.md delete mode 100644 guides/dynamic-programming.md delete mode 100644 guides/fast-slow-pointers.md delete mode 100644 guides/graph.md delete mode 100644 guides/greedy.md delete mode 100644 guides/heap.md delete mode 100644 guides/in-place-reversal-linked-list.md delete mode 100644 guides/intervals.md delete mode 100644 guides/quickselect.md delete mode 100644 guides/sliding-window.md delete mode 100644 guides/sorting.md delete mode 100644 guides/topological-sort.md delete mode 100644 guides/trie.md delete mode 100644 guides/two-pointers.md delete mode 100644 guides/union-find.md diff --git a/LOCAL_SETUP.md b/LOCAL_SETUP.md deleted file mode 100644 index 2386020c..00000000 --- a/LOCAL_SETUP.md +++ /dev/null @@ -1,59 +0,0 @@ -# Running LeetCode Patterns Locally - -## Prerequisites - -- **Node.js**: Version >= 22.2.0 (check with `node --version`) -- **npm**: Comes with Node.js (check with `npm --version`) - -If you don't have Node.js installed: -- Download from [nodejs.org](https://nodejs.org/) -- Or use [nvm](https://github.com/nvm-sh/nvm) to manage versions: - ```bash - nvm install 22.2.0 - nvm use 22.2.0 - ``` - -## Quick Start - -1. **Install dependencies** (if not already done): - ```bash - npm install - ``` - -2. **Start the development server**: - ```bash - npm start - ``` - -3. **Open your browser**: - - The app will automatically open at `http://localhost:3000` - - If it doesn't, manually navigate to that URL - -## Available Scripts - -- `npm start` - Starts the development server (runs on port 3000) -- `npm run build` - Creates a production build in the `build/` folder -- `npm run deploy` - Deploys the build to GitHub Pages (requires gh-pages setup) - -## Troubleshooting - -### Port 3000 already in use? -The app will prompt you to use a different port. Type `Y` to accept. - -### Dependencies issues? -Try deleting `node_modules` and `package-lock.json`, then reinstall: -```bash -rm -rf node_modules package-lock.json -npm install -``` - -### Pattern Guides not loading? -The Pattern Guides component fetches markdown files from GitHub. Make sure you have an internet connection. In production, these would be served from the `public/guides` directory. - -## Development Notes - -- The app uses React and is built with Create React App -- Hot reloading is enabled - changes will automatically refresh in the browser -- Check the browser console for any errors -- The main data file is `src/data/questions.json` - diff --git a/PUSH_GUIDE.md b/PUSH_GUIDE.md deleted file mode 100644 index 757bf8bc..00000000 --- a/PUSH_GUIDE.md +++ /dev/null @@ -1,149 +0,0 @@ -# Guide to Push Changes and Create Pull Request - -## Step 1: Fork the Repository (if not done already) - -1. Go to https://github.com/seanprashad/leetcode-patterns -2. Click the "Fork" button in the top right -3. This creates a copy under your GitHub account (e.g., `https://github.com/YOUR_USERNAME/leetcode-patterns`) - -## Step 2: Add Your Fork as a Remote - -```bash -# Add your fork as a remote (replace YOUR_USERNAME with your GitHub username) -git remote add fork https://github.com/YOUR_USERNAME/leetcode-patterns.git - -# Verify remotes -git remote -v -# You should see: -# origin https://github.com/seanprashad/leetcode-patterns.git (original) -# fork https://github.com/YOUR_USERNAME/leetcode-patterns.git (your fork) -``` - -## Step 3: Create a Feature Branch - -```bash -# Create and switch to a new branch -git checkout -b add-complexity-and-pattern-guides - -# Or use a shorter name -git checkout -b feature/complexity-guides -``` - -## Step 4: Stage Your Changes - -```bash -# Add all changes -git add . - -# Or add specific files -git add README.md -git add src/data/questions.json -git add src/components/ -git add guides/ -git add scripts/ -git add LOCAL_SETUP.md -``` - -## Step 5: Commit Your Changes - -```bash -git commit -m "Add time/space complexity analysis and pattern deep-dive guides - -- Add timeComplexity and spaceComplexity fields to all 175 questions -- Create comprehensive pattern guides for all 22 coding patterns -- Add Pattern Guides tab in UI with interactive guide viewer -- Display complexity columns in question table -- Make pattern badges clickable linking to guides -- Add script to automatically assign complexity based on patterns -- Update README with new features documentation" -``` - -## Step 6: Push to Your Fork - -```bash -# Push to your fork (replace branch-name with your branch name) -git push fork add-complexity-and-pattern-guides - -# Or if you set upstream -git push -u fork add-complexity-and-pattern-guides -``` - -## Step 7: Create Pull Request - -1. Go to your fork on GitHub: `https://github.com/YOUR_USERNAME/leetcode-patterns` -2. You'll see a banner saying "Your recently pushed branches" with a "Compare & pull request" button -3. Click "Compare & pull request" -4. Fill in the PR details: - - **Title**: "Add Time/Space Complexity Analysis and Pattern Deep-Dive Guides" - - **Description**: - ``` - ## Summary - - This PR adds two major features to enhance the educational value of the LeetCode Patterns resource: - - ### 1. Time & Space Complexity Analysis - - Added `timeComplexity` and `spaceComplexity` fields to all 175 questions - - Complexity values reflect optimal pattern-based solutions - - Displayed in new columns in the question table - - Automatically assigned based on patterns using intelligent script - - ### 2. Pattern Deep-Dive Guides - - Created comprehensive guides for all 22 coding patterns - - Each guide includes: core concept, when to use, template code, related problems, complexity analysis - - New "Pattern Guides" tab in UI with interactive markdown viewer - - Pattern badges in table are now clickable and link to guides - - ## Files Changed - - Added 22 pattern guide markdown files in `/guides` - - Added complexity fields to `src/data/questions.json` - - Updated UI components to display complexity and pattern guides - - Added script for complexity assignment - - ## Testing - - All guides render correctly in the UI - - Complexity columns display properly - - Pattern badges link to correct guides - - No breaking changes to existing functionality - ``` -5. Click "Create pull request" - -## Alternative: Quick Push Commands - -If you want to do it all at once: - -```bash -# 1. Create branch -git checkout -b feature/complexity-guides - -# 2. Add all files -git add . - -# 3. Commit -git commit -m "Add time/space complexity analysis and pattern deep-dive guides" - -# 4. Push (after adding your fork remote) -git push -u fork feature/complexity-guides -``` - -## Troubleshooting - -### If you get "remote fork already exists" -```bash -git remote remove fork -git remote add fork https://github.com/YOUR_USERNAME/leetcode-patterns.git -``` - -### If you need to update your fork -```bash -git fetch origin -git merge origin/main -git push fork -``` - -### If you want to update your branch -```bash -git fetch origin -git rebase origin/main -git push fork --force-with-lease -``` - diff --git a/guides/README.md b/guides/README.md deleted file mode 100644 index af9a9635..00000000 --- a/guides/README.md +++ /dev/null @@ -1,40 +0,0 @@ -# Pattern Deep-Dive Guides - -This directory contains comprehensive guides for each coding pattern used in LeetCode problems. Each guide explains the core concept, when to use the pattern, provides template code, and links to relevant problems. - -## Available Patterns - -- [Arrays](./arrays.md) -- [BFS (Breadth-First Search)](./bfs.md) -- [Backtracking](./backtracking.md) -- [Binary Search](./binary-search.md) -- [Bit Manipulation](./bit-manipulation.md) -- [Bucket Sort](./bucket-sort.md) -- [DFS (Depth-First Search)](./dfs.md) -- [Design](./design.md) -- [Dynamic Programming](./dynamic-programming.md) -- [Fast & Slow Pointers](./fast-slow-pointers.md) -- [Graph](./graph.md) -- [Greedy](./greedy.md) -- [Heap](./heap.md) -- [In-place reversal of a linked list](./in-place-reversal-linked-list.md) -- [Intervals](./intervals.md) -- [QuickSelect](./quickselect.md) -- [Sliding Window](./sliding-window.md) -- [Sorting](./sorting.md) -- [Topological Sort](./topological-sort.md) -- [Trie](./trie.md) -- [Two Pointers](./two-pointers.md) -- [Union Find](./union-find.md) - -## How to Use These Guides - -1. **Start with the Core Concept**: Understand what the pattern is and why it exists -2. **Learn When to Use It**: Identify the problem characteristics that signal this pattern -3. **Study the Template**: Review the skeleton code to understand the structure -4. **Practice with Problems**: Work through the linked problems to reinforce your understanding - -## Contributing - -If you find any errors or have suggestions for improving these guides, please open an issue or submit a pull request! - diff --git a/guides/arrays.md b/guides/arrays.md deleted file mode 100644 index 418a77bd..00000000 --- a/guides/arrays.md +++ /dev/null @@ -1,139 +0,0 @@ -# Arrays Pattern - -## Core Concept - -Arrays are fundamental data structures that store elements in contiguous memory locations. While arrays themselves aren't a "pattern" per se, many array manipulation techniques form the foundation for other patterns. This guide covers common array techniques and problem-solving approaches. - -## When to Use - -Array techniques are used in: - -- **Index manipulation**: Accessing, updating, or rearranging elements -- **In-place operations**: Modifying arrays without extra space -- **Prefix/Suffix arrays**: Precomputing cumulative values -- **Array rotation**: Rotating or shifting elements -- **Partitioning**: Dividing arrays into segments - -### Problem Indicators - -- "Given an array..." -- "Rearrange elements..." -- "Find missing/duplicate numbers" -- "Rotate array by k positions" -- "Partition array around pivot" - -## Template Code - -### In-place Array Modification - -```python -def in_place_modification(arr): - # Two pointers for in-place operations - write_idx = 0 - - for read_idx in range(len(arr)): - if should_keep(arr[read_idx]): - arr[write_idx] = arr[read_idx] - write_idx += 1 - - return write_idx # New length -``` - -### Array Rotation - -```python -def rotate_array(arr, k): - n = len(arr) - k = k % n # Handle k > n - - # Reverse entire array - arr.reverse() - - # Reverse first k elements - arr[:k] = reversed(arr[:k]) - - # Reverse remaining elements - arr[k:] = reversed(arr[k:]) -``` - -### Prefix Sum - -```python -def prefix_sum(arr): - prefix = [0] * (len(arr) + 1) - - for i in range(len(arr)): - prefix[i + 1] = prefix[i] + arr[i] - - return prefix - -# Query range sum [i, j] -def range_sum(prefix, i, j): - return prefix[j + 1] - prefix[i] -``` - -### JavaScript Template - -```javascript -function inPlaceModification(arr) { - let writeIdx = 0; - - for (let readIdx = 0; readIdx < arr.length; readIdx++) { - if (shouldKeep(arr[readIdx])) { - arr[writeIdx] = arr[readIdx]; - writeIdx++; - } - } - - return writeIdx; -} - -function rotateArray(arr, k) { - const n = arr.length; - k = k % n; - - reverse(arr, 0, n - 1); - reverse(arr, 0, k - 1); - reverse(arr, k, n - 1); -} - -function reverse(arr, start, end) { - while (start < end) { - [arr[start], arr[end]] = [arr[end], arr[start]]; - start++; - end--; - } -} -``` - -## Common Techniques - -1. **Two Pointers**: Often combined with arrays -2. **Sliding Window**: Subarray problems -3. **Hash Map**: Tracking frequencies or indices -4. **Sorting**: Many array problems benefit from sorting first -5. **Binary Search**: On sorted arrays - -## Related Problems - -View all Arrays problems in the [main question list](https://seanprashad.com/leetcode-patterns/). - -Common problems include: -- Contains Duplicate -- Missing Number -- Product of Array Except Self -- Rotate Array -- Move Zeroes - -## Time & Space Complexity - -- **Time Complexity**: Varies by technique - O(n) for linear scans, O(n log n) if sorting needed -- **Space Complexity**: O(1) for in-place operations, O(n) if creating new arrays - -## Tips - -1. **Consider in-place**: Many problems can be solved without extra space -2. **Use indices wisely**: Array indices are powerful for tracking state -3. **Handle edge cases**: Empty arrays, single element, all same values -4. **Modulo for rotation**: Use modulo to handle k > array length - diff --git a/guides/backtracking.md b/guides/backtracking.md deleted file mode 100644 index 05f57ef3..00000000 --- a/guides/backtracking.md +++ /dev/null @@ -1,196 +0,0 @@ -# Backtracking Pattern - -## Core Concept - -Backtracking is a systematic method for solving problems by trying partial solutions and then abandoning them ("backtracking") if they cannot lead to a valid complete solution. It's essentially a refined brute force approach that prunes invalid paths early. - -The key idea is to: -1. Make a choice -2. Recursively solve with that choice -3. Undo the choice (backtrack) if it doesn't lead to a solution -4. Try the next choice - -## When to Use - -Use Backtracking when: - -- **Constraint satisfaction**: Problems with constraints that must be satisfied -- **Generate all solutions**: Finding all possible combinations/permutations -- **Decision tree**: Problem can be represented as a decision tree -- **Pruning possible**: Can eliminate invalid paths early -- **Exhaustive search needed**: Need to explore all possibilities - -### Problem Indicators - -- "Find all combinations/permutations" -- "Generate all possible..." -- "Find all valid..." -- "N-Queens problem" -- "Sudoku solver" -- "Word search" -- "Subset generation" - -## Template Code - -### Standard Backtracking Template - -```python -def backtrack(candidates, path, result): - # Base case: solution found - if is_solution(path): - result.append(path[:]) # Make a copy - return - - # Try all candidates - for candidate in candidates: - # Make choice - if is_valid(candidate, path): - path.append(candidate) - - # Recurse - backtrack(candidates, path, result) - - # Backtrack (undo choice) - path.pop() -``` - -### Generate All Subsets - -```python -def subsets(nums): - result = [] - - def backtrack(start, path): - result.append(path[:]) - - for i in range(start, len(nums)): - path.append(nums[i]) - backtrack(i + 1, path) - path.pop() - - backtrack(0, []) - return result -``` - -### Generate All Permutations - -```python -def permutations(nums): - result = [] - used = [False] * len(nums) - - def backtrack(path): - if len(path) == len(nums): - result.append(path[:]) - return - - for i in range(len(nums)): - if not used[i]: - used[i] = True - path.append(nums[i]) - backtrack(path) - path.pop() - used[i] = False - - backtrack([]) - return result -``` - -### N-Queens Problem - -```python -def solve_n_queens(n): - result = [] - board = [['.'] * n for _ in range(n)] - - def is_safe(row, col): - # Check column - for i in range(row): - if board[i][col] == 'Q': - return False - - # Check diagonals - for i, j in zip(range(row - 1, -1, -1), range(col - 1, -1, -1)): - if board[i][j] == 'Q': - return False - - for i, j in zip(range(row - 1, -1, -1), range(col + 1, n)): - if board[i][j] == 'Q': - return False - - return True - - def backtrack(row): - if row == n: - result.append([''.join(row) for row in board]) - return - - for col in range(n): - if is_safe(row, col): - board[row][col] = 'Q' - backtrack(row + 1) - board[row][col] = '.' - - backtrack(0) - return result -``` - -### JavaScript Template - -```javascript -function backtrack(candidates, path, result) { - if (isSolution(path)) { - result.push([...path]); - return; - } - - for (const candidate of candidates) { - if (isValid(candidate, path)) { - path.push(candidate); - backtrack(candidates, path, result); - path.pop(); // Backtrack - } - } -} -``` - -## Common Variations - -1. **Pruning**: Skip invalid candidates early -2. **Memoization**: Cache results to avoid recomputation -3. **Early termination**: Stop when first solution found -4. **Constraint propagation**: Use constraints to reduce search space - -## Related Problems - -View all Backtracking problems in the [main question list](https://seanprashad.com/leetcode-patterns/). - -Common problems include: -- Subsets -- Permutations -- Combination Sum -- N-Queens -- Sudoku Solver -- Word Search -- Generate Parentheses - -## Time & Space Complexity - -- **Time Complexity**: Often exponential O(2^n) or O(n!) depending on problem -- **Space Complexity**: O(n) for recursion stack, plus space for storing results - -## Optimization Techniques - -1. **Pruning**: Eliminate invalid paths early -2. **Memoization**: Cache computed results -3. **Constraint checking**: Check constraints before recursing -4. **Early termination**: Return immediately when solution found (if only one needed) - -## Tips - -1. **Identify constraints**: Understand what makes a solution valid -2. **Choose representation**: Pick efficient data structures for state -3. **Prune early**: Check validity before recursing -4. **Undo changes**: Always restore state after backtracking -5. **Base case**: Clearly define when to stop recursing - diff --git a/guides/bfs.md b/guides/bfs.md deleted file mode 100644 index c25e2c03..00000000 --- a/guides/bfs.md +++ /dev/null @@ -1,176 +0,0 @@ -# BFS (Breadth-First Search) Pattern - -## Core Concept - -Breadth-First Search (BFS) is a graph traversal algorithm that explores all nodes at the current depth level before moving to nodes at the next depth level. BFS uses a queue data structure to maintain the order of nodes to visit. - -Key characteristics: -- Explores level by level -- Guarantees shortest path in unweighted graphs -- Uses queue (FIFO - First In First Out) -- Visits all nodes at distance k before nodes at distance k+1 - -## When to Use - -Use BFS when: - -- **Level-order traversal**: Need to process nodes level by level -- **Shortest path**: Finding shortest path in unweighted graphs -- **Level information**: Need to know which level/depth a node is at -- **Closest nodes first**: Want to visit closest nodes before distant ones -- **Tree/Graph traversal**: Traversing trees or graphs level by level - -### Problem Indicators - -- "Level order traversal" -- "Shortest path" -- "Minimum steps to reach target" -- "Find nodes at distance k" -- "Clone graph" -- "Word ladder" - -## Template Code - -### BFS on Tree - -```python -from collections import deque - -def bfs_tree(root): - if not root: - return [] - - result = [] - queue = deque([root]) - - 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 -``` - -### BFS on Graph - -```python -from collections import deque - -def bfs_graph(start, graph): - visited = set() - queue = deque([start]) - visited.add(start) - - while queue: - node = queue.popleft() - - # Process node - process(node) - - # Visit neighbors - for neighbor in graph[node]: - if neighbor not in visited: - visited.add(neighbor) - queue.append(neighbor) -``` - -### Shortest Path (Unweighted Graph) - -```python -from collections import deque - -def shortest_path(start, target, graph): - if start == target: - return 0 - - queue = deque([(start, 0)]) # (node, distance) - visited = {start} - - while queue: - node, distance = queue.popleft() - - for neighbor in graph[node]: - if neighbor == target: - return distance + 1 - - if neighbor not in visited: - visited.add(neighbor) - queue.append((neighbor, distance + 1)) - - return -1 # Not reachable -``` - -### JavaScript Template - -```javascript -function bfsTree(root) { - if (!root) return []; - - const result = []; - const queue = [root]; - - while (queue.length > 0) { - const levelSize = queue.length; - const level = []; - - for (let i = 0; i < levelSize; i++) { - const node = queue.shift(); - level.push(node.val); - - if (node.left) queue.push(node.left); - if (node.right) queue.push(node.right); - } - - result.push(level); - } - - return result; -} -``` - -## Common Variations - -1. **Level-order with levels**: Track which level each node belongs to -2. **Bidirectional BFS**: Start from both source and target -3. **Multi-source BFS**: Start from multiple nodes simultaneously -4. **BFS with state**: Track additional state (e.g., keys collected) - -## Related Problems - -View all BFS problems in the [main question list](https://seanprashad.com/leetcode-patterns/). - -Common problems include: -- Binary Tree Level Order Traversal -- Shortest Path in Binary Matrix -- Word Ladder -- Clone Graph -- Rotting Oranges -- Perfect Squares - -## Time & Space Complexity - -- **Time Complexity**: O(V + E) where V is vertices and E is edges -- **Space Complexity**: O(V) for queue and visited set - -## BFS vs DFS - -- **BFS**: Uses queue, explores level by level, finds shortest path -- **DFS**: Uses stack (recursion), explores depth first, uses less memory - -## Tips - -1. **Use deque**: Python's `collections.deque` is efficient for queue operations -2. **Track levels**: Use level size to process nodes level by level -3. **Mark visited**: Always mark nodes as visited when adding to queue -4. **Handle cycles**: Use visited set to avoid revisiting nodes in graphs - diff --git a/guides/binary-search.md b/guides/binary-search.md deleted file mode 100644 index 492f2340..00000000 --- a/guides/binary-search.md +++ /dev/null @@ -1,182 +0,0 @@ -# Binary Search Pattern - -## Core Concept - -Binary Search is a divide-and-conquer algorithm that efficiently searches for a target in a sorted array by repeatedly dividing the search space in half. The key requirement is that the array must be sorted (or have some ordering property). - -Binary Search can be applied beyond simple search: -- Finding boundaries (first/last occurrence) -- Search in rotated arrays -- Finding peak elements -- Search in 2D matrices - -## When to Use - -Use Binary Search when: - -- **Sorted data**: Array is sorted (or can be sorted) -- **Search optimization**: Need O(log n) instead of O(n) linear search -- **Boundary finding**: Finding first/last occurrence, insertion point -- **Monotonic property**: Problem has a monotonic property (increasing/decreasing) -- **Optimization problems**: Finding minimum/maximum value satisfying condition - -### Problem Indicators - -- "Find target in sorted array" -- "Find first/last occurrence" -- "Search in rotated sorted array" -- "Find peak element" -- "Find minimum/maximum satisfying condition" - -## Template Code - -### Standard Binary Search - -```python -def binary_search(arr, target): - left, right = 0, len(arr) - 1 - - while left <= right: - mid = left + (right - left) // 2 - - if arr[mid] == target: - return mid - elif arr[mid] < target: - left = mid + 1 - else: - right = mid - 1 - - return -1 # Not found -``` - -### Finding Left Boundary (First Occurrence) - -```python -def find_left_boundary(arr, target): - left, right = 0, len(arr) - 1 - result = -1 - - while left <= right: - mid = left + (right - left) // 2 - - if arr[mid] == target: - result = mid - right = mid - 1 # Continue searching left - elif arr[mid] < target: - left = mid + 1 - else: - right = mid - 1 - - return result -``` - -### Finding Right Boundary (Last Occurrence) - -```python -def find_right_boundary(arr, target): - left, right = 0, len(arr) - 1 - result = -1 - - while left <= right: - mid = left + (right - left) // 2 - - if arr[mid] == target: - result = mid - left = mid + 1 # Continue searching right - elif arr[mid] < target: - left = mid + 1 - else: - right = mid - 1 - - return result -``` - -### Search in Rotated Array - -```python -def search_rotated(arr, target): - left, right = 0, len(arr) - 1 - - while left <= right: - mid = left + (right - left) // 2 - - if arr[mid] == target: - return mid - - # Left half is sorted - if arr[left] <= arr[mid]: - if arr[left] <= target < arr[mid]: - right = mid - 1 - else: - left = mid + 1 - # Right half is sorted - else: - if arr[mid] < target <= arr[right]: - left = mid + 1 - else: - right = mid - 1 - - return -1 -``` - -### JavaScript Template - -```javascript -function binarySearch(arr, target) { - let left = 0; - let right = arr.length - 1; - - while (left <= right) { - const mid = Math.floor(left + (right - left) / 2); - - if (arr[mid] === target) { - return mid; - } else if (arr[mid] < target) { - left = mid + 1; - } else { - right = mid - 1; - } - } - - return -1; -} -``` - -## Common Variations - -1. **Search Space Reduction**: Binary search on answer (e.g., finding minimum capacity) -2. **2D Binary Search**: Searching in sorted 2D matrix -3. **Finding Peak**: Using binary search to find peak element -4. **Square Root**: Using binary search for numerical problems - -## Related Problems - -View all Binary Search problems in the [main question list](https://seanprashad.com/leetcode-patterns/). - -Common problems include: -- Binary Search -- Search in Rotated Sorted Array -- Find First and Last Position -- Search a 2D Matrix -- Find Peak Element -- Sqrt(x) - -## Time & Space Complexity - -- **Time Complexity**: O(log n) - halves search space each iteration -- **Space Complexity**: O(1) for iterative, O(log n) for recursive - -## Important Notes - -1. **Avoid overflow**: Use `left + (right - left) // 2` instead of `(left + right) // 2` -2. **Boundary conditions**: Carefully handle `<=` vs `<` in while condition -3. **Update bounds**: Always move `left = mid + 1` or `right = mid - 1` to avoid infinite loops -4. **Edge cases**: Empty array, single element, target not found - -## Tips - -1. **Identify sorted property**: Look for sorted or partially sorted data -2. **Monotonic function**: If problem has monotonic property, binary search may apply -3. **Template selection**: Choose appropriate template (standard, left boundary, right boundary) -4. **Test boundaries**: Always test with edge cases - diff --git a/guides/bit-manipulation.md b/guides/bit-manipulation.md deleted file mode 100644 index 69cabd47..00000000 --- a/guides/bit-manipulation.md +++ /dev/null @@ -1,143 +0,0 @@ -# Bit Manipulation Pattern - -## Core Concept - -Bit Manipulation involves directly manipulating bits (0s and 1s) in binary representations of numbers. This technique can lead to highly efficient solutions by leveraging bitwise operations. - -Common bitwise operations: -- **AND (&)**: Both bits must be 1 -- **OR (|)**: At least one bit must be 1 -- **XOR (^)**: Bits must be different -- **NOT (~)**: Flip all bits -- **Left Shift (<<)**: Multiply by 2 -- **Right Shift (>>)**: Divide by 2 - -## When to Use - -Use Bit Manipulation when: - -- **Efficient operations**: Need O(1) operations on bits -- **Space optimization**: Represent sets/arrays as bits -- **Power of 2**: Problems involving powers of 2 -- **Counting bits**: Problems about bit counts -- **Set operations**: Efficient set union/intersection - -### Problem Indicators - -- "Single number" (find unique element) -- "Power of two" -- "Count set bits" -- "Bitwise operations" -- "Set representation" -- "XOR problems" - -## Template Code - -### Basic Bit Operations - -```python -# Check if bit is set -def is_bit_set(num, pos): - return (num >> pos) & 1 == 1 - -# Set a bit -def set_bit(num, pos): - return num | (1 << pos) - -# Clear a bit -def clear_bit(num, pos): - return num & ~(1 << pos) - -# Toggle a bit -def toggle_bit(num, pos): - return num ^ (1 << pos) - -# Count set bits -def count_set_bits(num): - count = 0 - while num: - count += num & 1 - num >>= 1 - return count - -# Count set bits (Brian Kernighan's algorithm) -def count_set_bits_fast(num): - count = 0 - while num: - num &= num - 1 # Clear least significant set bit - count += 1 - return count -``` - -### Find Single Number (XOR) - -```python -def single_number(nums): - result = 0 - for num in nums: - result ^= num - return result -``` - -### Power of Two - -```python -def is_power_of_two(n): - return n > 0 and (n & (n - 1)) == 0 -``` - -### JavaScript Template - -```javascript -function isBitSet(num, pos) { - return ((num >> pos) & 1) === 1; -} - -function setBit(num, pos) { - return num | (1 << pos); -} - -function clearBit(num, pos) { - return num & ~(1 << pos); -} - -function countSetBits(num) { - let count = 0; - while (num) { - count += num & 1; - num >>= 1; - } - return count; -} -``` - -## Common Tricks - -1. **XOR properties**: `a ^ a = 0`, `a ^ 0 = a`, `a ^ b ^ a = b` -2. **Power of 2**: `n & (n - 1) == 0` if n is power of 2 -3. **Get rightmost set bit**: `n & -n` -4. **Clear rightmost set bit**: `n & (n - 1)` - -## Related Problems - -View all Bit Manipulation problems in the [main question list](https://seanprashad.com/leetcode-patterns/). - -Common problems include: -- Single Number -- Number of 1 Bits -- Power of Two -- Missing Number -- Reverse Bits - -## Time & Space Complexity - -- **Time Complexity**: Often O(1) for single operations, O(n) for array traversal -- **Space Complexity**: O(1) - operations are in-place - -## Tips - -1. **Know bitwise operations**: Master AND, OR, XOR, shifts -2. **XOR for duplicates**: XOR cancels duplicates -3. **Power of 2 trick**: `n & (n - 1) == 0` -4. **Bit masks**: Use masks to extract specific bits - diff --git a/guides/bucket-sort.md b/guides/bucket-sort.md deleted file mode 100644 index be4670d3..00000000 --- a/guides/bucket-sort.md +++ /dev/null @@ -1,127 +0,0 @@ -# Bucket Sort Pattern - -## Core Concept - -Bucket Sort distributes elements into buckets, sorts each bucket, and concatenates the results. It's efficient when: -- Input is uniformly distributed -- Range of values is known -- Need O(n) average time complexity - -## When to Use - -Use Bucket Sort when: - -- **Uniform distribution**: Input values are uniformly distributed -- **Known range**: Range of values is known -- **Linear time needed**: Need O(n) average time -- **Floating point**: Sorting floating point numbers in range [0, 1) - -### Problem Indicators - -- "Sort with O(n) time" -- "Uniformly distributed values" -- "Floating point sorting" -- "Top K frequent" (can use bucket sort) - -## Template Code - -### Basic Bucket Sort - -```python -def bucket_sort(arr): - if not arr: - return arr - - # Find min and max - min_val = min(arr) - max_val = max(arr) - - # Create buckets - num_buckets = len(arr) - buckets = [[] for _ in range(num_buckets)] - - # Distribute elements into buckets - for num in arr: - index = int((num - min_val) / (max_val - min_val + 1) * num_buckets) - buckets[index].append(num) - - # Sort each bucket and concatenate - result = [] - for bucket in buckets: - bucket.sort() - result.extend(bucket) - - return result -``` - -### Top K Frequent (Bucket Sort Approach) - -```python -def top_k_frequent(nums, k): - from collections import Counter - - # Count frequencies - count = Counter(nums) - - # Create buckets (index = frequency) - buckets = [[] for _ in range(len(nums) + 1)] - for num, freq in count.items(): - buckets[freq].append(num) - - # Collect top k - result = [] - for i in range(len(buckets) - 1, -1, -1): - result.extend(buckets[i]) - if len(result) >= k: - break - - return result[:k] -``` - -### JavaScript Template - -```javascript -function bucketSort(arr) { - if (arr.length === 0) return arr; - - const min = Math.min(...arr); - const max = Math.max(...arr); - const numBuckets = arr.length; - const buckets = Array.from({ length: numBuckets }, () => []); - - for (const num of arr) { - const index = Math.floor((num - min) / (max - min + 1) * numBuckets); - buckets[index].push(num); - } - - const result = []; - for (const bucket of buckets) { - bucket.sort((a, b) => a - b); - result.push(...bucket); - } - - return result; -} -``` - -## Related Problems - -View all Bucket Sort problems in the [main question list](https://seanprashad.com/leetcode-patterns/). - -Common problems include: -- Top K Frequent Elements -- Sort Colors (counting sort variant) -- Maximum Gap - -## Time & Space Complexity - -- **Time Complexity**: O(n) average, O(n²) worst case -- **Space Complexity**: O(n) for buckets - -## Tips - -1. **Uniform distribution**: Works best with uniform distribution -2. **Bucket count**: Choose appropriate number of buckets -3. **Bucket sorting**: Use efficient sort for each bucket -4. **Frequency problems**: Useful for top K frequent problems - diff --git a/guides/design.md b/guides/design.md deleted file mode 100644 index 29d2f5c2..00000000 --- a/guides/design.md +++ /dev/null @@ -1,161 +0,0 @@ -# Design Pattern - -## Core Concept - -Design problems require implementing data structures or systems with specific operations and constraints. These problems test your ability to: -- Choose appropriate data structures -- Design efficient APIs -- Handle edge cases -- Optimize for time/space complexity - -## When to Use - -Design problems appear when: - -- **System design**: Designing systems with multiple operations -- **Data structure design**: Implementing custom data structures -- **API design**: Creating interfaces with specific operations -- **Caching**: Implementing cache systems -- **Concurrency**: Thread-safe data structures - -### Problem Indicators - -- "Design..." -- "Implement..." -- "LRU Cache" -- "Trie" -- "Time-based key-value store" -- "Design Twitter" - -## Template Code - -### LRU Cache - -```python -from collections import OrderedDict - -class LRUCache: - def __init__(self, capacity): - self.cache = OrderedDict() - self.capacity = capacity - - def get(self, key): - if key not in self.cache: - return -1 - - # Move to end (most recently used) - self.cache.move_to_end(key) - return self.cache[key] - - def put(self, key, value): - if key in self.cache: - # Update existing - self.cache.move_to_end(key) - else: - # Check capacity - if len(self.cache) >= self.capacity: - # Remove least recently used (first item) - self.cache.popitem(last=False) - - self.cache[key] = value -``` - -### Design HashMap - -```python -class MyHashMap: - def __init__(self): - self.size = 1000 - self.buckets = [[] for _ in range(self.size)] - - def _hash(self, key): - return key % self.size - - def put(self, key, value): - index = self._hash(key) - bucket = self.buckets[index] - - for i, (k, v) in enumerate(bucket): - if k == key: - bucket[i] = (key, value) - return - - bucket.append((key, value)) - - def get(self, key): - index = self._hash(key) - bucket = self.buckets[index] - - for k, v in bucket: - if k == key: - return v - - return -1 - - def remove(self, key): - index = self._hash(key) - bucket = self.buckets[index] - - for i, (k, v) in enumerate(bucket): - if k == key: - bucket.pop(i) - return -``` - -### JavaScript Template - -```javascript -class LRUCache { - constructor(capacity) { - this.cache = new Map(); - this.capacity = capacity; - } - - get(key) { - if (!this.cache.has(key)) return -1; - - const value = this.cache.get(key); - this.cache.delete(key); - this.cache.set(key, value); // Move to end - return value; - } - - put(key, value) { - if (this.cache.has(key)) { - this.cache.delete(key); - } else if (this.cache.size >= this.capacity) { - const firstKey = this.cache.keys().next().value; - this.cache.delete(firstKey); - } - - this.cache.set(key, value); - } -} -``` - -## Common Design Patterns - -1. **Hash Map/Set**: Fast lookups -2. **Linked List**: For LRU, insertion order -3. **Heap**: For priority queues -4. **Trie**: For prefix matching -5. **Two Data Structures**: Combine for efficiency - -## Related Problems - -View all Design problems in the [main question list](https://seanprashad.com/leetcode-patterns/). - -Common problems include: -- LRU Cache -- Design HashMap -- Design HashSet -- Design Twitter -- Time-based Key-Value Store - -## Tips - -1. **Choose data structures**: Pick appropriate DS for operations -2. **Trade-offs**: Consider time vs space trade-offs -3. **Edge cases**: Handle empty, single element, capacity limits -4. **API design**: Clear, intuitive method signatures - diff --git a/guides/dfs.md b/guides/dfs.md deleted file mode 100644 index ba9f3f14..00000000 --- a/guides/dfs.md +++ /dev/null @@ -1,189 +0,0 @@ -# DFS (Depth-First Search) Pattern - -## Core Concept - -Depth-First Search (DFS) is a graph traversal algorithm that explores as far as possible along each branch before backtracking. DFS uses a stack data structure (or recursion, which uses the call stack) to maintain the order of nodes to visit. - -Key characteristics: -- Explores depth first -- Uses stack (LIFO - Last In First Out) or recursion -- Visits nodes along a path until dead end, then backtracks -- Memory efficient (only stores path from root to current node) - -## When to Use - -Use DFS when: - -- **Path exploration**: Need to explore all paths from a node -- **Backtracking problems**: Problems requiring backtracking -- **Tree problems**: Many tree problems naturally use DFS -- **Connected components**: Finding connected components in graphs -- **Topological sort**: Can be used for topological sorting -- **Memory constraint**: When memory is limited (DFS uses less memory than BFS) - -### Problem Indicators - -- "Find all paths" -- "Maximum depth" -- "Path sum" -- "Connected components" -- "Island problems" -- "Tree traversal" - -## Template Code - -### DFS Recursive (Tree) - -```python -def dfs_tree(node, result): - if not node: - return - - # Pre-order: process before children - result.append(node.val) - - dfs_tree(node.left, result) - dfs_tree(node.right, result) - - # Post-order: process after children - # result.append(node.val) -``` - -### DFS Iterative (Tree) - -```python -def dfs_iterative(root): - if not root: - return [] - - result = [] - stack = [root] - - while stack: - node = stack.pop() - result.append(node.val) - - # Push right first, then left (for pre-order) - if node.right: - stack.append(node.right) - if node.left: - stack.append(node.left) - - return result -``` - -### DFS on Graph (Recursive) - -```python -def dfs_graph(node, graph, visited): - visited.add(node) - - # Process node - process(node) - - # Visit neighbors - for neighbor in graph[node]: - if neighbor not in visited: - dfs_graph(neighbor, graph, visited) -``` - -### DFS on Graph (Iterative) - -```python -def dfs_graph_iterative(start, graph): - visited = set() - stack = [start] - - while stack: - node = stack.pop() - - if node not in visited: - visited.add(node) - process(node) - - for neighbor in graph[node]: - if neighbor not in visited: - stack.append(neighbor) -``` - -### JavaScript Template - -```javascript -function dfsTree(node, result) { - if (!node) return; - - result.push(node.val); // Pre-order - dfsTree(node.left, result); - dfsTree(node.right, result); -} - -function dfsGraphIterative(start, graph) { - const visited = new Set(); - const stack = [start]; - - while (stack.length > 0) { - const node = stack.pop(); - - if (!visited.has(node)) { - visited.add(node); - process(node); - - for (const neighbor of graph[node]) { - if (!visited.has(neighbor)) { - stack.push(neighbor); - } - } - } - } -} -``` - -## DFS Traversal Orders - -### Pre-order (Root → Left → Right) -Process node before children. Good for copying trees. - -### In-order (Left → Root → Right) -Process left, then node, then right. Good for BST (gives sorted order). - -### Post-order (Left → Right → Root) -Process children before node. Good for deleting trees. - -## Common Variations - -1. **Path tracking**: Maintain path from root to current node -2. **Memoization**: Cache results to avoid recomputation -3. **Early termination**: Return immediately when condition met -4. **State tracking**: Track additional state (e.g., parent, depth) - -## Related Problems - -View all DFS problems in the [main question list](https://seanprashad.com/leetcode-patterns/). - -Common problems include: -- Maximum Depth of Binary Tree -- Path Sum -- Number of Islands -- Clone Graph -- Course Schedule -- Validate Binary Search Tree - -## Time & Space Complexity - -- **Time Complexity**: O(V + E) where V is vertices and E is edges -- **Space Complexity**: - - O(h) for trees where h is height - - O(V) for graphs (visited set + recursion stack) - -## DFS vs BFS - -- **DFS**: Explores depth first, uses stack/recursion, less memory -- **BFS**: Explores level by level, uses queue, finds shortest path - -## Tips - -1. **Choose traversal order**: Pre-order, in-order, or post-order based on problem -2. **Handle cycles**: Use visited set in graphs to avoid infinite loops -3. **Backtrack properly**: Restore state when backtracking -4. **Recursive vs Iterative**: Recursive is cleaner, iterative gives more control - diff --git a/guides/dynamic-programming.md b/guides/dynamic-programming.md deleted file mode 100644 index 330b3e3f..00000000 --- a/guides/dynamic-programming.md +++ /dev/null @@ -1,199 +0,0 @@ -# Dynamic Programming Pattern - -## Core Concept - -Dynamic Programming (DP) is an optimization technique that solves complex problems by breaking them down into simpler subproblems. It stores the results of subproblems to avoid redundant calculations. The key principle is: **optimal substructure** - the optimal solution to a problem contains optimal solutions to its subproblems. - -DP problems can be approached in two ways: -- **Top-down (Memoization)**: Recursive approach with caching -- **Bottom-up (Tabulation)**: Iterative approach building up from base cases - -## When to Use - -Use Dynamic Programming when: - -- **Overlapping subproblems**: The same subproblem is solved multiple times -- **Optimal substructure**: Optimal solution can be constructed from optimal solutions of subproblems -- **Optimization problems**: Finding maximum, minimum, or counting possibilities -- **Decision problems**: Making choices that affect future states - -### Problem Indicators - -- "Find the maximum/minimum..." -- "Count the number of ways..." -- "Find the longest/shortest..." -- "What is the optimal way to..." -- Problems asking for all possible combinations/permutations -- Problems with constraints and choices - -## Template Code - -### Top-Down (Memoization) - -```python -def dp_memoization(n, memo={}): - # Base cases - if n <= 1: - return n - - # Check if already computed - if n in memo: - return memo[n] - - # Compute and store - memo[n] = dp_memoization(n - 1, memo) + dp_memoization(n - 2, memo) - return memo[n] -``` - -### Bottom-Up (Tabulation) - -```python -def dp_tabulation(n): - # Base cases - if n <= 1: - return n - - # Initialize DP array - dp = [0] * (n + 1) - dp[0] = 0 - dp[1] = 1 - - # Fill DP array - for i in range(2, n + 1): - dp[i] = dp[i - 1] + dp[i - 2] - - return dp[n] -``` - -### Space-Optimized (1D DP) - -```python -def dp_optimized(n): - if n <= 1: - return n - - prev2 = 0 - prev1 = 1 - - for i in range(2, n + 1): - current = prev1 + prev2 - prev2 = prev1 - prev1 = current - - return prev1 -``` - -### 2D DP Template - -```python -def dp_2d(m, n): - # Initialize 2D DP array - dp = [[0] * n for _ in range(m)] - - # Base cases (first row and column) - for i in range(m): - dp[i][0] = 1 - for j in range(n): - dp[0][j] = 1 - - # Fill DP array - 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] -``` - -### JavaScript Template - -```javascript -// Memoization -function dpMemoization(n, memo = {}) { - if (n <= 1) return n; - if (n in memo) return memo[n]; - - memo[n] = dpMemoization(n - 1, memo) + dpMemoization(n - 2, memo); - return memo[n]; -} - -// Tabulation -function dpTabulation(n) { - if (n <= 1) return n; - - const dp = new Array(n + 1).fill(0); - dp[0] = 0; - dp[1] = 1; - - for (let i = 2; i <= n; i++) { - dp[i] = dp[i - 1] + dp[i - 2]; - } - - return dp[n]; -} -``` - -## Common DP Patterns - -### 1. Fibonacci Pattern -- Climbing Stairs -- House Robber -- Decode Ways - -### 2. Knapsack Pattern -- 0/1 Knapsack -- Coin Change -- Partition Equal Subset Sum - -### 3. Longest Common Subsequence (LCS) -- Longest Common Subsequence -- Edit Distance -- Longest Palindromic Subsequence - -### 4. Matrix DP -- Unique Paths -- Minimum Path Sum -- Maximal Square - -### 5. String DP -- Longest Palindromic Substring -- Word Break -- Regular Expression Matching - -## Steps to Solve DP Problems - -1. **Identify the state**: What information do we need to track? -2. **Define the recurrence relation**: How do we compute current state from previous states? -3. **Base cases**: What are the simplest subproblems? -4. **Choose approach**: Top-down (memoization) or bottom-up (tabulation) -5. **Optimize space**: Can we reduce space complexity? - -## Related Problems - -View all Dynamic Programming problems in the [main question list](https://seanprashad.com/leetcode-patterns/). - -Common problems include: -- Climbing Stairs -- House Robber -- Coin Change -- Longest Common Subsequence -- Edit Distance -- Unique Paths -- Word Break -- Longest Increasing Subsequence - -## Time & Space Complexity - -- **Time Complexity**: Typically O(n) for 1D, O(n²) for 2D, O(n³) for 3D -- **Space Complexity**: - - O(n) for 1D memoization/tabulation - - O(n²) for 2D problems - - Can often be optimized to O(1) or O(n) with space optimization - -## Tips - -1. **Start with brute force**: Understand the problem first, then optimize -2. **Draw the state space**: Visualize subproblems and dependencies -3. **Identify overlapping subproblems**: Look for repeated calculations -4. **Practice pattern recognition**: Many DP problems follow similar patterns -5. **Space optimization**: After solving, see if you can reduce space complexity - diff --git a/guides/fast-slow-pointers.md b/guides/fast-slow-pointers.md deleted file mode 100644 index 60af372c..00000000 --- a/guides/fast-slow-pointers.md +++ /dev/null @@ -1,159 +0,0 @@ -# Fast & Slow Pointers Pattern - -## Core Concept - -The Fast & Slow Pointers pattern (also known as the "Tortoise and Hare" algorithm) uses two pointers that move through a data structure at different speeds. Typically: -- **Slow pointer**: Moves one step at a time -- **Fast pointer**: Moves two steps at a time - -This pattern is particularly useful for: -- Detecting cycles in linked lists -- Finding the middle of a linked list -- Finding the kth element from the end -- Detecting palindromes in linked lists - -## When to Use - -Use Fast & Slow Pointers when: - -- **Cycle detection**: Need to detect if a linked list has a cycle -- **Finding middle**: Need to find the middle element of a linked list -- **Linked list problems**: Many linked list problems benefit from this pattern -- **Palindrome in linked list**: Checking if a linked list is a palindrome -- **Finding kth from end**: Locating the kth element from the end - -### Problem Indicators - -- "Detect if linked list has a cycle" -- "Find the middle of a linked list" -- "Check if linked list is palindrome" -- "Find the kth node from the end" -- "Remove the nth node from end" - -## Template Code - -### Cycle Detection - -```python -def has_cycle(head): - if not head or not head.next: - return False - - slow = head - fast = head.next - - while fast and fast.next: - if slow == fast: - return True - slow = slow.next - fast = fast.next.next - - return False -``` - -### Finding Middle Node - -```python -def find_middle(head): - slow = head - fast = head - - while fast and fast.next: - slow = slow.next - fast = fast.next.next - - return slow -``` - -### Finding Cycle Start (Floyd's Algorithm) - -```python -def detect_cycle_start(head): - # Step 1: Detect if cycle exists - slow = fast = head - - while fast and fast.next: - slow = slow.next - fast = fast.next.next - if slow == fast: - break - else: - return None # No cycle - - # Step 2: Find cycle start - slow = head - while slow != fast: - slow = slow.next - fast = fast.next - - return slow -``` - -### JavaScript Template - -```javascript -function hasCycle(head) { - if (!head || !head.next) return false; - - let slow = head; - let fast = head.next; - - while (fast && fast.next) { - if (slow === fast) return true; - slow = slow.next; - fast = fast.next.next; - } - - return false; -} - -function findMiddle(head) { - let slow = head; - let fast = head; - - while (fast && fast.next) { - slow = slow.next; - fast = fast.next.next; - } - - return slow; -} -``` - -## Common Variations - -1. **Different Speeds**: Adjust speeds (e.g., 1:3 ratio) for specific problems -2. **Kth from End**: Use two pointers with k distance between them -3. **Palindrome Check**: Reverse second half and compare -4. **Cycle Length**: Find the length of the cycle - -## Related Problems - -View all Fast & Slow Pointers problems in the [main question list](https://seanprashad.com/leetcode-patterns/). - -Common problems include: -- Linked List Cycle -- Middle of the Linked List -- Palindrome Linked List -- Remove Nth Node From End of List -- Reorder List - -## Time & Space Complexity - -- **Time Complexity**: O(n) - linear traversal of the linked list -- **Space Complexity**: O(1) - only using two pointers, constant extra space - -## Mathematical Insight - -Floyd's Cycle Detection Algorithm works because: -- If there's a cycle, the fast pointer will eventually catch up to the slow pointer -- When they meet, the distance from head to cycle start equals the distance from meeting point to cycle start -- This allows us to find the cycle start with a second pass - -## Tips - -1. **Initialize carefully**: Start both pointers appropriately (same node or one step apart) -2. **Check for null**: Always check if fast and fast.next exist before accessing -3. **Edge cases**: Handle empty list, single node, no cycle scenarios -4. **Visualize**: Draw the linked list to understand pointer movement - diff --git a/guides/graph.md b/guides/graph.md deleted file mode 100644 index 03dfd7b2..00000000 --- a/guides/graph.md +++ /dev/null @@ -1,208 +0,0 @@ -# Graph Pattern - -## Core Concept - -Graphs are data structures consisting of nodes (vertices) connected by edges. Graph problems involve traversing, searching, or manipulating these connections. Common graph representations include: - -- **Adjacency List**: List of lists, each inner list contains neighbors -- **Adjacency Matrix**: 2D matrix where matrix[i][j] indicates edge -- **Edge List**: List of tuples representing edges - -## When to Use - -Graph techniques are used when: - -- **Relationships**: Data has relationships/connections between entities -- **Network problems**: Social networks, computer networks, dependencies -- **Path finding**: Finding paths between nodes -- **Cycle detection**: Detecting cycles in directed/undirected graphs -- **Connected components**: Finding groups of connected nodes - -### Problem Indicators - -- "Given a graph..." -- "Find path between nodes" -- "Detect cycle" -- "Find connected components" -- "Topological sort" -- "Shortest path" - -## Template Code - -### Graph Representation - -```python -# Adjacency List -graph = { - 0: [1, 2], - 1: [0, 3], - 2: [0, 3], - 3: [1, 2] -} - -# Adjacency Matrix -graph_matrix = [ - [0, 1, 1, 0], - [1, 0, 0, 1], - [1, 0, 0, 1], - [0, 1, 1, 0] -] -``` - -### Detect Cycle (Undirected Graph) - -```python -def has_cycle_undirected(graph): - visited = set() - - def dfs(node, parent): - visited.add(node) - - for neighbor in graph[node]: - if neighbor not in visited: - if dfs(neighbor, node): - return True - elif neighbor != parent: - return True # Cycle found - - return False - - for node in graph: - if node not in visited: - if dfs(node, -1): - return True - - return False -``` - -### Detect Cycle (Directed Graph) - -```python -def has_cycle_directed(graph): - WHITE, GRAY, BLACK = 0, 1, 2 - color = {node: WHITE for node in graph} - - def dfs(node): - if color[node] == GRAY: - return True # Cycle found - - if color[node] == BLACK: - return False - - color[node] = GRAY - for neighbor in graph[node]: - if dfs(neighbor): - return True - - color[node] = BLACK - return False - - for node in graph: - if color[node] == WHITE: - if dfs(node): - return True - - return False -``` - -### Find Connected Components - -```python -def connected_components(graph): - visited = set() - components = [] - - def dfs(node, component): - visited.add(node) - component.append(node) - - for neighbor in graph[node]: - if neighbor not in visited: - dfs(neighbor, component) - - for node in graph: - if node not in visited: - component = [] - dfs(node, component) - components.append(component) - - return components -``` - -### JavaScript Template - -```javascript -// Adjacency List -const graph = { - 0: [1, 2], - 1: [0, 3], - 2: [0, 3], - 3: [1, 2] -}; - -function hasCycleUndirected(graph) { - const visited = new Set(); - - function dfs(node, parent) { - visited.add(node); - - for (const neighbor of graph[node]) { - if (!visited.has(neighbor)) { - if (dfs(neighbor, node)) return true; - } else if (neighbor !== parent) { - return true; // Cycle found - } - } - - return false; - } - - for (const node in graph) { - if (!visited.has(parseInt(node))) { - if (dfs(parseInt(node), -1)) return true; - } - } - - return false; -} -``` - -## Common Graph Algorithms - -1. **BFS/DFS**: Basic traversal algorithms -2. **Topological Sort**: Ordering nodes in DAG -3. **Shortest Path**: Dijkstra, Bellman-Ford, Floyd-Warshall -4. **Minimum Spanning Tree**: Kruskal, Prim -5. **Strongly Connected Components**: Kosaraju, Tarjan - -## Related Problems - -View all Graph problems in the [main question list](https://seanprashad.com/leetcode-patterns/). - -Common problems include: -- Clone Graph -- Course Schedule -- Number of Islands -- Redundant Connection -- Network Delay Time -- Critical Connections - -## Time & Space Complexity - -- **Time Complexity**: Typically O(V + E) for traversal, varies by algorithm -- **Space Complexity**: O(V) for visited set and recursion stack - -## Graph Types - -- **Directed vs Undirected**: Edges have direction or not -- **Weighted vs Unweighted**: Edges have weights or not -- **Cyclic vs Acyclic**: Contains cycles or not -- **Connected vs Disconnected**: All nodes reachable or not - -## Tips - -1. **Choose representation**: Adjacency list for sparse graphs, matrix for dense -2. **Handle cycles**: Different logic for directed vs undirected graphs -3. **Visited tracking**: Always track visited nodes to avoid infinite loops -4. **Edge cases**: Empty graph, single node, disconnected components - diff --git a/guides/greedy.md b/guides/greedy.md deleted file mode 100644 index 928ec8f8..00000000 --- a/guides/greedy.md +++ /dev/null @@ -1,149 +0,0 @@ -# Greedy Pattern - -## Core Concept - -Greedy algorithms make locally optimal choices at each step with the hope that these choices will lead to a globally optimal solution. The key is identifying when a greedy approach works - not all problems can be solved greedily. - -Greedy works when: -- **Greedy choice property**: A global optimum can be reached by making locally optimal choices -- **Optimal substructure**: Optimal solution contains optimal solutions to subproblems - -## When to Use - -Use Greedy when: - -- **Optimization problems**: Finding maximum or minimum -- **Local optimality**: Local optimal choice leads to global optimum -- **Activity selection**: Scheduling, interval problems -- **Coin change**: Making change with minimum coins (specific cases) -- **MST problems**: Minimum Spanning Tree algorithms - -### Problem Indicators - -- "Maximum/Minimum..." -- "Activity selection" -- "Interval scheduling" -- "Minimum coins" -- "Optimal arrangement" -- Problems where local choice doesn't affect future choices - -## Template Code - -### Activity Selection - -```python -def activity_selection(activities): - # Sort by finish time - activities.sort(key=lambda x: x[1]) - - selected = [activities[0]] - last_finish = activities[0][1] - - for start, finish in activities[1:]: - if start >= last_finish: - selected.append((start, finish)) - last_finish = finish - - return selected -``` - -### Interval Scheduling - -```python -def erase_overlap_intervals(intervals): - if not intervals: - return 0 - - # Sort by end time - intervals.sort(key=lambda x: x[1]) - - count = 0 - end = intervals[0][1] - - for i in range(1, len(intervals)): - if intervals[i][0] < end: - count += 1 # Overlapping, remove this - else: - end = intervals[i][1] - - return count -``` - -### Coin Change (Greedy - works for certain coin systems) - -```python -def coin_change_greedy(coins, amount): - coins.sort(reverse=True) # Use largest coins first - count = 0 - - for coin in coins: - if amount >= coin: - num_coins = amount // coin - count += num_coins - amount -= num_coins * coin - - return count if amount == 0 else -1 -``` - -### JavaScript Template - -```javascript -function activitySelection(activities) { - activities.sort((a, b) => a[1] - b[1]); - - const selected = [activities[0]]; - let lastFinish = activities[0][1]; - - for (let i = 1; i < activities.length; i++) { - const [start, finish] = activities[i]; - if (start >= lastFinish) { - selected.push(activities[i]); - lastFinish = finish; - } - } - - return selected; -} -``` - -## Common Greedy Strategies - -1. **Sort first**: Many greedy problems require sorting -2. **Take earliest/latest**: Choose based on start/end times -3. **Take largest/smallest**: Choose based on value/size -4. **Local optimization**: Make best local choice - -## When Greedy Fails - -Greedy doesn't work when: -- Local optimal doesn't lead to global optimal -- Need to consider future consequences -- Problem requires exploring all possibilities - -Example: Coin change with coins [1, 3, 4] and amount 6: -- Greedy: 4 + 1 + 1 = 3 coins -- Optimal: 3 + 3 = 2 coins - -## Related Problems - -View all Greedy problems in the [main question list](https://seanprashad.com/leetcode-patterns/). - -Common problems include: -- Non-overlapping Intervals -- Jump Game -- Gas Station -- Assign Cookies -- Best Time to Buy and Sell Stock - -## Time & Space Complexity - -- **Time Complexity**: Often O(n log n) due to sorting, then O(n) for processing -- **Space Complexity**: O(1) if not storing results, O(n) if storing - -## Tips - -1. **Prove correctness**: Ensure greedy choice leads to optimal solution -2. **Sort appropriately**: Sort by the right criteria (end time, value, etc.) -3. **Start simple**: Try greedy first, fall back to DP if needed -4. **Counter-examples**: Test with edge cases to verify greedy works - diff --git a/guides/heap.md b/guides/heap.md deleted file mode 100644 index 9efabf46..00000000 --- a/guides/heap.md +++ /dev/null @@ -1,214 +0,0 @@ -# Heap Pattern - -## Core Concept - -A Heap is a complete binary tree that satisfies the heap property: -- **Min Heap**: Parent ≤ children (root is minimum) -- **Max Heap**: Parent ≥ children (root is maximum) - -Heaps are typically implemented as arrays where: -- Parent at index `i` has children at `2i + 1` and `2i + 2` -- Child at index `i` has parent at `(i - 1) // 2` - -## When to Use - -Use Heaps when: - -- **Priority queue**: Need to repeatedly get min/max element -- **K largest/smallest**: Find k largest or smallest elements -- **Merge k sorted**: Merging k sorted lists/arrays -- **Scheduling**: Task scheduling with priorities -- **Median finding**: Finding running median - -### Problem Indicators - -- "K largest/smallest" -- "Top K elements" -- "Merge k sorted" -- "Find median" -- "Priority queue" -- "Frequent elements" - -## Template Code - -### Python (using heapq) - -```python -import heapq - -# Min heap (default) -heap = [] -heapq.heappush(heap, 3) -heapq.heappush(heap, 1) -heapq.heappush(heap, 2) -min_val = heapq.heappop(heap) # Returns 1 - -# Max heap (negate values) -max_heap = [] -heapq.heappush(max_heap, -3) -heapq.heappush(max_heap, -1) -max_val = -heapq.heappop(max_heap) # Returns 3 - -# K largest elements -def k_largest(nums, k): - return heapq.nlargest(k, nums) - -# K smallest elements -def k_smallest(nums, k): - return heapq.nsmallest(k, nums) -``` - -### Custom Heap Implementation - -```python -class MinHeap: - def __init__(self): - self.heap = [] - - def parent(self, i): - return (i - 1) // 2 - - def left_child(self, i): - return 2 * i + 1 - - def right_child(self, i): - return 2 * i + 2 - - def insert(self, val): - self.heap.append(val) - self._heapify_up(len(self.heap) - 1) - - def extract_min(self): - if not self.heap: - return None - - if len(self.heap) == 1: - return self.heap.pop() - - min_val = self.heap[0] - self.heap[0] = self.heap.pop() - self._heapify_down(0) - return min_val - - def _heapify_up(self, i): - while i > 0 and self.heap[self.parent(i)] > self.heap[i]: - p = self.parent(i) - self.heap[i], self.heap[p] = self.heap[p], self.heap[i] - i = p - - def _heapify_down(self, i): - smallest = i - left = self.left_child(i) - right = self.right_child(i) - - if left < len(self.heap) and self.heap[left] < self.heap[smallest]: - smallest = left - - if right < len(self.heap) and self.heap[right] < self.heap[smallest]: - smallest = right - - if smallest != i: - self.heap[i], self.heap[smallest] = self.heap[smallest], self.heap[i] - self._heapify_down(smallest) -``` - -### JavaScript Template - -```javascript -// JavaScript doesn't have built-in heap, use array with manual implementation -// Or use a library like 'heap' npm package - -class MinHeap { - constructor() { - this.heap = []; - } - - parent(i) { - return Math.floor((i - 1) / 2); - } - - leftChild(i) { - return 2 * i + 1; - } - - rightChild(i) { - return 2 * i + 2; - } - - insert(val) { - this.heap.push(val); - this.heapifyUp(this.heap.length - 1); - } - - extractMin() { - if (this.heap.length === 0) return null; - if (this.heap.length === 1) return this.heap.pop(); - - const min = this.heap[0]; - this.heap[0] = this.heap.pop(); - this.heapifyDown(0); - return min; - } - - heapifyUp(i) { - while (i > 0 && this.heap[this.parent(i)] > this.heap[i]) { - const p = this.parent(i); - [this.heap[i], this.heap[p]] = [this.heap[p], this.heap[i]]; - i = p; - } - } - - heapifyDown(i) { - let smallest = i; - const left = this.leftChild(i); - const right = this.rightChild(i); - - if (left < this.heap.length && this.heap[left] < this.heap[smallest]) { - smallest = left; - } - - if (right < this.heap.length && this.heap[right] < this.heap[smallest]) { - smallest = right; - } - - if (smallest !== i) { - [this.heap[i], this.heap[smallest]] = [this.heap[smallest], this.heap[i]]; - this.heapifyDown(smallest); - } - } -} -``` - -## Common Variations - -1. **K-way merge**: Merge k sorted lists using heap -2. **Top K frequent**: Use heap with frequency counts -3. **Median**: Use two heaps (min + max) -4. **Custom comparator**: Heap with custom comparison function - -## Related Problems - -View all Heap problems in the [main question list](https://seanprashad.com/leetcode-patterns/). - -Common problems include: -- Kth Largest Element -- Merge K Sorted Lists -- Top K Frequent Elements -- Find Median from Data Stream -- Meeting Rooms II - -## Time & Space Complexity - -- **Insert**: O(log n) -- **Extract min/max**: O(log n) -- **Peek**: O(1) -- **Build heap**: O(n) -- **Space**: O(n) - -## Tips - -1. **Use library**: Python's `heapq` is efficient -2. **Max heap trick**: Negate values for max heap in Python -3. **K problems**: Maintain heap of size k -4. **Two heaps**: Use two heaps for median problems - diff --git a/guides/in-place-reversal-linked-list.md b/guides/in-place-reversal-linked-list.md deleted file mode 100644 index 46c9601e..00000000 --- a/guides/in-place-reversal-linked-list.md +++ /dev/null @@ -1,144 +0,0 @@ -# In-place Reversal of a Linked List Pattern - -## Core Concept - -In-place reversal of a linked list involves reversing the order of nodes without using extra space for a new list. This is done by manipulating pointers to change the direction of links. - -## When to Use - -Use this pattern when: - -- **Reverse linked list**: Need to reverse entire or portion of list -- **In-place requirement**: Cannot use extra space -- **Pointer manipulation**: Comfortable with pointer operations -- **Partial reversal**: Reverse specific portion of list - -### Problem Indicators - -- "Reverse linked list" -- "Reverse nodes in k-group" -- "Reverse between positions" -- "Palindrome linked list" (reverse and compare) - -## Template Code - -### Reverse Entire List - -```python -def reverse_list(head): - prev = None - current = head - - while current: - next_node = current.next - current.next = prev - prev = current - current = next_node - - return prev -``` - -### Reverse Between Positions - -```python -def reverse_between(head, left, right): - if not head or left == right: - return head - - dummy = ListNode(0) - dummy.next = head - prev = dummy - - # Move to left position - for _ in range(left - 1): - prev = prev.next - - # Reverse from left to right - current = prev.next - for _ in range(right - left): - next_node = current.next - current.next = next_node.next - next_node.next = prev.next - prev.next = next_node - - return dummy.next -``` - -### Reverse in K-Groups - -```python -def reverse_k_group(head, k): - def reverse_linked_list(start, end): - prev = end - current = start - - while current != end: - next_node = current.next - current.next = prev - prev = current - current = next_node - - return prev - - # Count nodes - count = 0 - current = head - while current and count < k: - current = current.next - count += 1 - - if count == k: - # Reverse first k nodes - current = reverse_k_group(current, k) - head = reverse_linked_list(head, current) - - return head -``` - -### JavaScript Template - -```javascript -function reverseList(head) { - let prev = null; - let current = head; - - while (current) { - const next = current.next; - current.next = prev; - prev = current; - current = next; - } - - return prev; -} -``` - -## Common Variations - -1. **Partial reversal**: Reverse specific portion -2. **K-group reversal**: Reverse in groups of k -3. **Alternating reversal**: Reverse every other group -4. **Recursive reversal**: Recursive approach - -## Related Problems - -View all In-place reversal problems in the [main question list](https://seanprashad.com/leetcode-patterns/). - -Common problems include: -- Reverse Linked List -- Reverse Linked List II -- Reverse Nodes in k-Group -- Swap Nodes in Pairs - -## Time & Space Complexity - -- **Time Complexity**: O(n) - single pass through list -- **Space Complexity**: O(1) - only using pointers - -## Tips - -1. **Three pointers**: Use prev, current, next -2. **Dummy node**: Use dummy node for edge cases -3. **Draw it out**: Visualize pointer movements -4. **Handle edge cases**: Empty list, single node, etc. - diff --git a/guides/intervals.md b/guides/intervals.md deleted file mode 100644 index 8cf30e91..00000000 --- a/guides/intervals.md +++ /dev/null @@ -1,179 +0,0 @@ -# Intervals Pattern - -## Core Concept - -Interval problems involve working with ranges of values, typically represented as [start, end] pairs. Common operations include: -- Merging overlapping intervals -- Finding non-overlapping intervals -- Inserting intervals -- Finding intersections - -## When to Use - -Use Interval techniques when: - -- **Range problems**: Working with time ranges, number ranges -- **Overlap detection**: Finding or removing overlapping intervals -- **Scheduling**: Meeting rooms, event scheduling -- **Merge operations**: Combining overlapping intervals -- **Coverage problems**: Finding coverage, gaps - -### Problem Indicators - -- "Merge intervals" -- "Non-overlapping intervals" -- "Meeting rooms" -- "Insert interval" -- "Interval intersection" -- "Range problems" - -## Template Code - -### Merge Intervals - -```python -def merge_intervals(intervals): - if not intervals: - return [] - - # Sort by start time - intervals.sort(key=lambda x: x[0]) - merged = [intervals[0]] - - for current in intervals[1:]: - last = merged[-1] - - # If overlapping, merge - if current[0] <= last[1]: - last[1] = max(last[1], current[1]) - else: - merged.append(current) - - return merged -``` - -### Insert Interval - -```python -def insert_interval(intervals, new_interval): - result = [] - i = 0 - - # Add all intervals before new_interval - while i < len(intervals) and intervals[i][1] < new_interval[0]: - result.append(intervals[i]) - i += 1 - - # Merge overlapping intervals - while i < len(intervals) and intervals[i][0] <= new_interval[1]: - new_interval[0] = min(new_interval[0], intervals[i][0]) - new_interval[1] = max(new_interval[1], intervals[i][1]) - i += 1 - - result.append(new_interval) - - # Add remaining intervals - result.extend(intervals[i:]) - return result -``` - -### Find Non-Overlapping Intervals - -```python -def erase_overlap_intervals(intervals): - if not intervals: - return 0 - - # Sort by end time (greedy: keep intervals that end earliest) - intervals.sort(key=lambda x: x[1]) - - count = 0 - end = intervals[0][1] - - for i in range(1, len(intervals)): - if intervals[i][0] < end: - count += 1 # Remove overlapping interval - else: - end = intervals[i][1] - - return count -``` - -### Interval Intersection - -```python -def interval_intersection(intervals1, intervals2): - result = [] - i = j = 0 - - while i < len(intervals1) and j < len(intervals2): - # Find overlap - start = max(intervals1[i][0], intervals2[j][0]) - end = min(intervals1[i][1], intervals2[j][1]) - - if start <= end: - result.append([start, end]) - - # Move pointer of interval that ends first - if intervals1[i][1] < intervals2[j][1]: - i += 1 - else: - j += 1 - - return result -``` - -### JavaScript Template - -```javascript -function mergeIntervals(intervals) { - if (intervals.length === 0) return []; - - intervals.sort((a, b) => a[0] - b[0]); - const merged = [intervals[0]]; - - for (let i = 1; i < intervals.length; i++) { - const current = intervals[i]; - const last = merged[merged.length - 1]; - - if (current[0] <= last[1]) { - last[1] = Math.max(last[1], current[1]); - } else { - merged.push(current); - } - } - - return merged; -} -``` - -## Common Operations - -1. **Sorting**: Usually sort by start time or end time -2. **Overlap check**: `interval1[0] <= interval2[1] and interval2[0] <= interval1[1]` -3. **Merge**: `[min(start1, start2), max(end1, end2)]` -4. **Greedy selection**: Keep intervals that end earliest - -## Related Problems - -View all Intervals problems in the [main question list](https://seanprashad.com/leetcode-patterns/). - -Common problems include: -- Merge Intervals -- Non-overlapping Intervals -- Meeting Rooms -- Insert Interval -- Interval List Intersections - -## Time & Space Complexity - -- **Time Complexity**: O(n log n) for sorting, then O(n) for processing -- **Space Complexity**: O(n) for storing results - -## Tips - -1. **Sort first**: Most interval problems require sorting -2. **Choose sort key**: Sort by start time or end time based on problem -3. **Overlap condition**: Understand when intervals overlap -4. **Greedy approach**: Often use greedy (keep earliest ending intervals) - diff --git a/guides/quickselect.md b/guides/quickselect.md deleted file mode 100644 index c611f5ac..00000000 --- a/guides/quickselect.md +++ /dev/null @@ -1,120 +0,0 @@ -# QuickSelect Pattern - -## Core Concept - -QuickSelect is a selection algorithm to find the kth smallest (or largest) element in an unsorted array. It's based on QuickSort's partition algorithm but only recurses into the partition containing the desired element. - -## When to Use - -Use QuickSelect when: - -- **Kth element**: Finding kth smallest/largest element -- **Partial sorting**: Need k smallest/largest elements -- **O(n) average**: Need better than O(n log n) for selection -- **In-place**: Need in-place algorithm - -### Problem Indicators - -- "Find kth largest" -- "Find kth smallest" -- "Top K elements" -- "Median of array" -- "K closest points" - -## Template Code - -### Find Kth Largest - -```python -def find_kth_largest(nums, k): - def quickselect(left, right, k_smallest): - if left == right: - return nums[left] - - pivot_index = partition(left, right, left) - - if k_smallest == pivot_index: - return nums[pivot_index] - elif k_smallest < pivot_index: - return quickselect(left, pivot_index - 1, k_smallest) - else: - return quickselect(pivot_index + 1, right, k_smallest) - - def partition(left, right, pivot_index): - pivot_value = nums[pivot_index] - # Move pivot to end - nums[pivot_index], nums[right] = nums[right], nums[pivot_index] - - store_index = left - for i in range(left, right): - if nums[i] < pivot_value: - nums[store_index], nums[i] = nums[i], nums[store_index] - store_index += 1 - - # Move pivot to final position - nums[right], nums[store_index] = nums[store_index], nums[right] - return store_index - - # kth largest is (n - k)th smallest - return quickselect(0, len(nums) - 1, len(nums) - k) -``` - -### JavaScript Template - -```javascript -function findKthLargest(nums, k) { - function quickSelect(left, right, kSmallest) { - if (left === right) return nums[left]; - - const pivotIndex = partition(left, right, left); - - if (kSmallest === pivotIndex) { - return nums[pivotIndex]; - } else if (kSmallest < pivotIndex) { - return quickSelect(left, pivotIndex - 1, kSmallest); - } else { - return quickSelect(pivotIndex + 1, right, kSmallest); - } - } - - function partition(left, right, pivotIndex) { - const pivotValue = nums[pivotIndex]; - [nums[pivotIndex], nums[right]] = [nums[right], nums[pivotIndex]]; - - let storeIndex = left; - for (let i = left; i < right; i++) { - if (nums[i] < pivotValue) { - [nums[storeIndex], nums[i]] = [nums[i], nums[storeIndex]]; - storeIndex++; - } - } - - [nums[right], nums[storeIndex]] = [nums[storeIndex], nums[right]]; - return storeIndex; - } - - return quickSelect(0, nums.length - 1, nums.length - k); -} -``` - -## Related Problems - -View all QuickSelect problems in the [main question list](https://seanprashad.com/leetcode-patterns/). - -Common problems include: -- Kth Largest Element -- K Closest Points to Origin -- Top K Frequent Elements (with heap or quickselect) - -## Time & Space Complexity - -- **Time Complexity**: O(n) average, O(n²) worst case -- **Space Complexity**: O(1) for iterative, O(log n) for recursive - -## Tips - -1. **Random pivot**: Use random pivot for better average performance -2. **Kth largest**: Convert to (n-k)th smallest -3. **Partition logic**: Similar to QuickSort partition -4. **Early termination**: Stop when kth element found - diff --git a/guides/sliding-window.md b/guides/sliding-window.md deleted file mode 100644 index 31d3f973..00000000 --- a/guides/sliding-window.md +++ /dev/null @@ -1,131 +0,0 @@ -# Sliding Window Pattern - -## Core Concept - -The Sliding Window pattern is a technique used to solve problems involving subarrays or substrings of a fixed or variable size. Instead of recalculating values for every possible subarray, we maintain a "window" that slides through the array, efficiently updating our calculations as we move. - -The key insight is that when moving the window, we only need to: -- Remove the element leaving the window -- Add the element entering the window -- Update our calculation based on these changes - -## When to Use - -Use the Sliding Window pattern when you encounter problems with these characteristics: - -- **Subarray/Substring problems**: Finding subarrays or substrings that meet certain criteria -- **Fixed or variable window size**: Problems asking for subarrays of size k, or the longest/shortest subarray meeting a condition -- **Optimization problems**: Finding maximum, minimum, or count of subarrays -- **Common keywords**: "subarray", "substring", "contiguous", "window", "k elements" - -### Problem Indicators - -- "Find the maximum sum of k consecutive elements" -- "Find the longest substring with at most k distinct characters" -- "Find all subarrays of size k with sum less than target" -- "Minimum window substring" - -## Template Code - -### Fixed Window Size - -```python -def sliding_window_fixed(arr, k): - # Initialize window - window_sum = sum(arr[:k]) - result = window_sum - - # Slide the window - for i in range(k, len(arr)): - # Remove leftmost element, add rightmost element - window_sum = window_sum - arr[i - k] + arr[i] - result = max(result, window_sum) # or min, or other operation - - return result -``` - -### Variable Window Size (Two Pointers) - -```python -def sliding_window_variable(arr, target): - left = 0 - window_sum = 0 - result = float('inf') - - for right in range(len(arr)): - # Expand window - window_sum += arr[right] - - # Shrink window while condition is met - while window_sum >= target: - result = min(result, right - left + 1) - window_sum -= arr[left] - left += 1 - - return result if result != float('inf') else 0 -``` - -### JavaScript Template - -```javascript -function slidingWindowFixed(arr, k) { - let windowSum = arr.slice(0, k).reduce((a, b) => a + b, 0); - let result = windowSum; - - for (let i = k; i < arr.length; i++) { - windowSum = windowSum - arr[i - k] + arr[i]; - result = Math.max(result, windowSum); - } - - return result; -} - -function slidingWindowVariable(arr, target) { - let left = 0; - let windowSum = 0; - let result = Infinity; - - for (let right = 0; right < arr.length; right++) { - windowSum += arr[right]; - - while (windowSum >= target) { - result = Math.min(result, right - left + 1); - windowSum -= arr[left]; - left++; - } - } - - return result === Infinity ? 0 : result; -} -``` - -## Common Variations - -1. **Maximum/Minimum in Window**: Use a deque to track max/min efficiently -2. **Counting Subarrays**: Count subarrays meeting a condition -3. **Character Frequency**: Track character counts in substring problems -4. **Multiple Conditions**: Handle multiple constraints simultaneously - -## Related Problems - -View all Sliding Window problems in the [main question list](https://seanprashad.com/leetcode-patterns/). - -Common problems include: -- Maximum Average Subarray -- Longest Substring Without Repeating Characters -- Minimum Window Substring -- Subarray Product Less Than K -- Maximum Sum Subarray of Size K - -## Time & Space Complexity - -- **Time Complexity**: Typically O(n) - each element is visited at most twice (once by left pointer, once by right pointer) -- **Space Complexity**: O(1) for fixed window, O(k) if storing window elements, or O(min(m,n)) for character frequency maps where m is character set size - -## Tips - -1. **Identify the window type**: Fixed size vs. variable size -2. **Determine what to track**: Sum, product, character frequency, etc. -3. **Define window validity**: When is the window valid? When should we shrink it? -4. **Handle edge cases**: Empty arrays, k > array length, etc. - diff --git a/guides/sorting.md b/guides/sorting.md deleted file mode 100644 index 4565919d..00000000 --- a/guides/sorting.md +++ /dev/null @@ -1,135 +0,0 @@ -# Sorting Pattern - -## Core Concept - -Sorting is the process of arranging elements in a particular order (ascending or descending). While sorting itself is a fundamental operation, many problems become easier after sorting the input. - -## When to Use - -Use Sorting when: - -- **Order matters**: Need elements in specific order -- **Simplify problem**: Sorting makes problem easier to solve -- **Two pointers**: Sorted array enables two-pointer technique -- **Binary search**: Requires sorted array -- **Grouping**: Group similar elements together - -### Problem Indicators - -- "Sort array" -- "Find kth largest/smallest" -- "Merge sorted arrays" -- "Intersection of arrays" -- Problems where order helps - -## Template Code - -### Built-in Sort - -```python -# Python -arr.sort() # In-place -sorted_arr = sorted(arr) # New array - -# Custom comparator -arr.sort(key=lambda x: x[1]) # Sort by second element -arr.sort(key=lambda x: (-x[0], x[1])) # Sort by first desc, second asc -``` - -### Quick Sort - -```python -def quicksort(arr, low, high): - if low < high: - pi = partition(arr, low, high) - quicksort(arr, low, pi - 1) - quicksort(arr, pi + 1, high) - -def partition(arr, low, high): - pivot = arr[high] - i = low - 1 - - for j in range(low, high): - if arr[j] < pivot: - i += 1 - arr[i], arr[j] = arr[j], arr[i] - - arr[i + 1], arr[high] = arr[high], arr[i + 1] - return i + 1 -``` - -### Merge Sort - -```python -def mergesort(arr): - if len(arr) <= 1: - return arr - - mid = len(arr) // 2 - left = mergesort(arr[:mid]) - right = mergesort(arr[mid:]) - - return merge(left, right) - -def merge(left, right): - result = [] - i = j = 0 - - while i < len(left) and j < len(right): - if left[i] <= right[j]: - result.append(left[i]) - i += 1 - else: - result.append(right[j]) - j += 1 - - result.extend(left[i:]) - result.extend(right[j:]) - return result -``` - -### JavaScript Template - -```javascript -// Built-in sort -arr.sort((a, b) => a - b); // Ascending -arr.sort((a, b) => b - a); // Descending - -// Custom comparator -arr.sort((a, b) => { - if (a[0] !== b[0]) return a[0] - b[0]; - return a[1] - b[1]; -}); -``` - -## Common Use Cases - -1. **Two Sum on sorted array**: Use two pointers -2. **K largest/smallest**: Sort and take first/last k -3. **Merge intervals**: Sort by start time -4. **Anagrams**: Sort characters to group anagrams - -## Related Problems - -View all Sorting problems in the [main question list](https://seanprashad.com/leetcode-patterns/). - -Common problems include: -- Sort Colors -- Merge Sorted Array -- Kth Largest Element -- Meeting Rooms -- Group Anagrams - -## Time & Space Complexity - -- **Comparison sorts**: O(n log n) best case -- **Counting sort**: O(n + k) for small range -- **Space**: O(1) for in-place, O(n) for merge sort - -## Tips - -1. **Use built-in**: Python's `sort()` is highly optimized -2. **Custom comparator**: Learn to write custom comparison functions -3. **Stability**: Some algorithms maintain relative order of equal elements -4. **When to sort**: Sort if it simplifies the problem - diff --git a/guides/topological-sort.md b/guides/topological-sort.md deleted file mode 100644 index 0ce55baf..00000000 --- a/guides/topological-sort.md +++ /dev/null @@ -1,164 +0,0 @@ -# Topological Sort Pattern - -## Core Concept - -Topological Sort is an ordering of vertices in a directed acyclic graph (DAG) such that for every directed edge (u, v), vertex u comes before v in the ordering. It's used for: -- Task scheduling with dependencies -- Build systems -- Course prerequisites -- Event ordering - -## When to Use - -Use Topological Sort when: - -- **Dependencies**: Tasks/events have dependencies -- **Ordering required**: Need to find valid ordering -- **DAG problems**: Working with directed acyclic graphs -- **Prerequisites**: Course prerequisites, build dependencies -- **Scheduling**: Task scheduling with constraints - -### Problem Indicators - -- "Course schedule" -- "Task ordering" -- "Build order" -- "Dependencies" -- "Prerequisites" -- "Find ordering" - -## Template Code - -### Kahn's Algorithm (BFS-based) - -```python -from collections import deque - -def topological_sort_kahn(num_nodes, edges): - # Build graph and in-degree - graph = {i: [] for i in range(num_nodes)} - in_degree = [0] * num_nodes - - for u, v in edges: - graph[u].append(v) - in_degree[v] += 1 - - # Find all nodes with in-degree 0 - queue = deque([i for i in range(num_nodes) if in_degree[i] == 0]) - result = [] - - while queue: - node = queue.popleft() - result.append(node) - - # Remove this node and update in-degrees - for neighbor in graph[node]: - in_degree[neighbor] -= 1 - if in_degree[neighbor] == 0: - queue.append(neighbor) - - # Check if all nodes processed (no cycle) - return result if len(result) == num_nodes else [] -``` - -### DFS-based Topological Sort - -```python -def topological_sort_dfs(num_nodes, edges): - # Build graph - graph = {i: [] for i in range(num_nodes)} - for u, v in edges: - graph[u].append(v) - - WHITE, GRAY, BLACK = 0, 1, 2 - color = {i: WHITE for i in range(num_nodes)} - result = [] - - def dfs(node): - if color[node] == GRAY: - return False # Cycle detected - if color[node] == BLACK: - return True - - color[node] = GRAY - for neighbor in graph[node]: - if not dfs(neighbor): - return False - - color[node] = BLACK - result.append(node) - return True - - for node in range(num_nodes): - if color[node] == WHITE: - if not dfs(node): - return [] # Cycle exists - - return result[::-1] # Reverse for correct order -``` - -### JavaScript Template - -```javascript -function topologicalSortKahn(numNodes, edges) { - const graph = Array.from({ length: numNodes }, () => []); - const inDegree = new Array(numNodes).fill(0); - - for (const [u, v] of edges) { - graph[u].push(v); - inDegree[v]++; - } - - const queue = []; - for (let i = 0; i < numNodes; i++) { - if (inDegree[i] === 0) { - queue.push(i); - } - } - - const result = []; - while (queue.length > 0) { - const node = queue.shift(); - result.push(node); - - for (const neighbor of graph[node]) { - inDegree[neighbor]--; - if (inDegree[neighbor] === 0) { - queue.push(neighbor); - } - } - } - - return result.length === numNodes ? result : []; -} -``` - -## Cycle Detection - -Topological sort can detect cycles: -- If result length < num_nodes, cycle exists -- In DFS version, gray node indicates back edge (cycle) - -## Related Problems - -View all Topological Sort problems in the [main question list](https://seanprashad.com/leetcode-patterns/). - -Common problems include: -- Course Schedule -- Course Schedule II -- Alien Dictionary -- Sequence Reconstruction -- Find Eventual Safe States - -## Time & Space Complexity - -- **Time Complexity**: O(V + E) where V is vertices, E is edges -- **Space Complexity**: O(V) for graph, in-degree, and result - -## Tips - -1. **Choose algorithm**: Kahn's is iterative, DFS is recursive -2. **Cycle detection**: Check if all nodes processed -3. **Build graph correctly**: Understand edge direction -4. **Multiple solutions**: DAGs can have multiple valid orderings - diff --git a/guides/trie.md b/guides/trie.md deleted file mode 100644 index 512a5e44..00000000 --- a/guides/trie.md +++ /dev/null @@ -1,189 +0,0 @@ -# Trie Pattern - -## Core Concept - -A Trie (prefix tree) is a tree-like data structure used to store strings. Each node represents a character, and paths from root to nodes represent strings. Tries are efficient for: -- Prefix matching -- String search -- Autocomplete -- Word validation - -Key advantages: -- Fast prefix lookups O(m) where m is string length -- Space efficient for common prefixes -- Easy to implement - -## When to Use - -Use Trie when: - -- **Prefix matching**: Finding all strings with given prefix -- **String search**: Searching for words in dictionary -- **Autocomplete**: Implementing autocomplete functionality -- **Word validation**: Checking if word exists -- **Prefix problems**: Problems involving prefixes - -### Problem Indicators - -- "Prefix matching" -- "Word search" -- "Autocomplete" -- "Longest common prefix" -- "Add and search words" -- "Prefix tree" - -## Template Code - -### Basic Trie Implementation - -```python -class TrieNode: - def __init__(self): - self.children = {} - self.is_end = False - -class Trie: - def __init__(self): - self.root = TrieNode() - - def insert(self, word): - node = self.root - for char in word: - if char not in node.children: - node.children[char] = TrieNode() - node = node.children[char] - node.is_end = True - - def search(self, word): - node = self.root - for char in word: - if char not in node.children: - return False - node = node.children[char] - return node.is_end - - def starts_with(self, prefix): - node = self.root - for char in prefix: - if char not in node.children: - return False - node = node.children[char] - return True -``` - -### Search with Wildcard - -```python -class WordDictionary: - def __init__(self): - self.root = TrieNode() - - def add_word(self, word): - node = self.root - for char in word: - if char not in node.children: - node.children[char] = TrieNode() - node = node.children[char] - node.is_end = True - - def search(self, word): - def dfs(node, index): - if index == len(word): - return node.is_end - - char = word[index] - if char == '.': - # Try all children - for child in node.children.values(): - if dfs(child, index + 1): - return True - return False - else: - if char not in node.children: - return False - return dfs(node.children[char], index + 1) - - return dfs(self.root, 0) -``` - -### JavaScript Template - -```javascript -class TrieNode { - constructor() { - this.children = {}; - this.isEnd = false; - } -} - -class Trie { - constructor() { - this.root = new TrieNode(); - } - - insert(word) { - let node = this.root; - for (const char of word) { - if (!(char in node.children)) { - node.children[char] = new TrieNode(); - } - node = node.children[char]; - } - node.isEnd = true; - } - - search(word) { - let node = this.root; - for (const char of word) { - if (!(char in node.children)) { - return false; - } - node = node.children[char]; - } - return node.isEnd; - } - - startsWith(prefix) { - let node = this.root; - for (const char of prefix) { - if (!(char in node.children)) { - return false; - } - node = node.children[char]; - } - return true; - } -} -``` - -## Common Variations - -1. **Wildcard search**: Support '.' for any character -2. **Prefix counting**: Count words with given prefix -3. **Longest prefix**: Find longest common prefix -4. **Delete operation**: Remove words from trie - -## Related Problems - -View all Trie problems in the [main question list](https://seanprashad.com/leetcode-patterns/). - -Common problems include: -- Implement Trie -- Add and Search Words -- Word Search II -- Longest Word in Dictionary -- Replace Words - -## Time & Space Complexity - -- **Insert**: O(m) where m is word length -- **Search**: O(m) where m is word length -- **Space**: O(ALPHABET_SIZE * N * M) where N is number of words, M is average length - -## Tips - -1. **Node structure**: Each node has children map and is_end flag -2. **Character mapping**: Use dictionary/map for children -3. **Prefix vs word**: Distinguish between prefix and complete word -4. **DFS for wildcards**: Use DFS when searching with wildcards - diff --git a/guides/two-pointers.md b/guides/two-pointers.md deleted file mode 100644 index bb48ad25..00000000 --- a/guides/two-pointers.md +++ /dev/null @@ -1,153 +0,0 @@ -# Two Pointers Pattern - -## Core Concept - -The Two Pointers pattern uses two pointers (indices) that traverse a data structure in a coordinated manner. The pointers can move: -- **Towards each other** (from both ends) -- **In the same direction** (both from start, or both from end) -- **At different speeds** (fast and slow pointers) - -This pattern eliminates the need for nested loops in many cases, reducing time complexity from O(n²) to O(n). - -## When to Use - -Use the Two Pointers pattern when: - -- **Sorted arrays**: Problems involving sorted arrays or linked lists -- **Pair searching**: Finding pairs that meet certain criteria -- **Palindrome checking**: Verifying if a sequence is a palindrome -- **Partitioning**: Dividing elements into groups based on conditions -- **Cycle detection**: Detecting cycles in linked lists (fast & slow pointers) - -### Problem Indicators - -- "Find two numbers that sum to target in sorted array" -- "Remove duplicates from sorted array" -- "Check if string is palindrome" -- "Partition array around a pivot" -- "Detect cycle in linked list" - -## Template Code - -### Opposite Ends (Converging) - -```python -def two_pointers_converging(arr, target): - left = 0 - right = len(arr) - 1 - - while left < right: - current_sum = arr[left] + arr[right] - - if current_sum == target: - return [left, right] - elif current_sum < target: - left += 1 # Need larger sum - else: - right -= 1 # Need smaller sum - - return [-1, -1] -``` - -### Same Direction (Sliding Window Variant) - -```python -def two_pointers_same_direction(arr): - slow = 0 - - for fast in range(len(arr)): - if should_keep(arr[fast]): - arr[slow] = arr[fast] - slow += 1 - - return slow # New length -``` - -### Fast & Slow Pointers (Cycle Detection) - -```python -def has_cycle(head): - if not head or not head.next: - return False - - slow = head - fast = head.next - - while fast and fast.next: - if slow == fast: - return True - slow = slow.next - fast = fast.next.next - - return False -``` - -### JavaScript Template - -```javascript -function twoPointersConverging(arr, target) { - let left = 0; - let right = arr.length - 1; - - while (left < right) { - const sum = arr[left] + arr[right]; - - if (sum === target) { - return [left, right]; - } else if (sum < target) { - left++; - } else { - right--; - } - } - - return [-1, -1]; -} - -function hasCycle(head) { - if (!head || !head.next) return false; - - let slow = head; - let fast = head.next; - - while (fast && fast.next) { - if (slow === fast) return true; - slow = slow.next; - fast = fast.next.next; - } - - return false; -} -``` - -## Common Variations - -1. **Three Pointers**: Extend to three pointers for 3Sum problems -2. **Multiple Arrays**: Use pointers on different arrays simultaneously -3. **Partitioning**: Use pointers to partition elements (e.g., Dutch National Flag) -4. **In-place Operations**: Modify arrays in-place without extra space - -## Related Problems - -View all Two Pointers problems in the [main question list](https://seanprashad.com/leetcode-patterns/). - -Common problems include: -- Two Sum (sorted array) -- Remove Duplicates from Sorted Array -- Valid Palindrome -- Container With Most Water -- 3Sum -- Trapping Rain Water - -## Time & Space Complexity - -- **Time Complexity**: O(n) - each element is visited at most once by each pointer -- **Space Complexity**: O(1) - only using a constant amount of extra space for pointers - -## Tips - -1. **Sort first**: Many two-pointer problems require sorted input -2. **Identify pointer movement**: Determine when each pointer should move -3. **Handle duplicates**: Be careful with duplicate values in sorted arrays -4. **Edge cases**: Empty arrays, single element, all elements same - diff --git a/guides/union-find.md b/guides/union-find.md deleted file mode 100644 index f07fe1a7..00000000 --- a/guides/union-find.md +++ /dev/null @@ -1,172 +0,0 @@ -# Union Find Pattern - -## Core Concept - -Union Find (Disjoint Set Union - DSU) is a data structure that tracks a set of elements partitioned into disjoint subsets. It supports two main operations: -- **Find**: Determine which subset an element belongs to -- **Union**: Merge two subsets into one - -It's efficient for: -- Detecting cycles in graphs -- Finding connected components -- Network connectivity problems -- Kruskal's algorithm for MST - -## When to Use - -Use Union Find when: - -- **Connected components**: Finding connected components in graphs -- **Cycle detection**: Detecting cycles in undirected graphs -- **Dynamic connectivity**: Adding edges and checking connectivity -- **Network problems**: Social networks, computer networks -- **MST algorithms**: Kruskal's algorithm - -### Problem Indicators - -- "Connected components" -- "Detect cycle" -- "Friend circles" -- "Number of islands" -- "Redundant connection" -- "Accounts merge" - -## Template Code - -### Basic Union Find - -```python -class UnionFind: - def __init__(self, n): - self.parent = list(range(n)) - self.rank = [0] * n - - def find(self, x): - if self.parent[x] != x: - # Path compression - self.parent[x] = self.find(self.parent[x]) - return self.parent[x] - - def union(self, x, y): - root_x = self.find(x) - root_y = self.find(y) - - if root_x == root_y: - return False # Already connected - - # Union by rank - if self.rank[root_x] < self.rank[root_y]: - self.parent[root_x] = root_y - elif self.rank[root_x] > self.rank[root_y]: - self.parent[root_y] = root_x - else: - self.parent[root_y] = root_x - self.rank[root_x] += 1 - - return True - - def connected(self, x, y): - return self.find(x) == self.find(y) -``` - -### Count Components - -```python -def count_components(n, edges): - uf = UnionFind(n) - - for u, v in edges: - uf.union(u, v) - - # Count distinct roots - roots = set() - for i in range(n): - roots.add(uf.find(i)) - - return len(roots) -``` - -### Detect Cycle - -```python -def has_cycle(n, edges): - uf = UnionFind(n) - - for u, v in edges: - if not uf.union(u, v): - return True # Cycle detected - - return False -``` - -### JavaScript Template - -```javascript -class UnionFind { - constructor(n) { - this.parent = Array.from({ length: n }, (_, i) => i); - this.rank = new Array(n).fill(0); - } - - find(x) { - if (this.parent[x] !== x) { - this.parent[x] = this.find(this.parent[x]); // Path compression - } - return this.parent[x]; - } - - union(x, y) { - const rootX = this.find(x); - const rootY = this.find(y); - - if (rootX === rootY) return false; - - // Union by rank - if (this.rank[rootX] < this.rank[rootY]) { - this.parent[rootX] = rootY; - } else if (this.rank[rootX] > this.rank[rootY]) { - this.parent[rootY] = rootX; - } else { - this.parent[rootY] = rootX; - this.rank[rootX]++; - } - - return true; - } - - connected(x, y) { - return this.find(x) === this.find(y); - } -} -``` - -## Optimizations - -1. **Path Compression**: Flatten tree during find operation -2. **Union by Rank**: Always attach smaller tree to larger tree -3. **Both together**: Nearly O(1) amortized time complexity - -## Related Problems - -View all Union Find problems in the [main question list](https://seanprashad.com/leetcode-patterns/). - -Common problems include: -- Number of Connected Components -- Redundant Connection -- Accounts Merge -- Friend Circles -- Most Stones Removed - -## Time & Space Complexity - -- **Find (with optimizations)**: Nearly O(1) amortized -- **Union (with optimizations)**: Nearly O(1) amortized -- **Space**: O(n) for parent and rank arrays - -## Tips - -1. **Initialize properly**: Parent[i] = i initially -2. **Use optimizations**: Path compression and union by rank -3. **Count components**: Count distinct roots -4. **Cycle detection**: If union returns false, cycle exists -