[AI] build roadmap for learning

This commit is contained in:
2026-05-17 01:43:29 +03:00
parent 77824baa7c
commit a1bdd7b879
11 changed files with 2409 additions and 0 deletions
+140
View File
@@ -0,0 +1,140 @@
# 01: Array Basics
## Overview
Array basics are the foundation of algorithmic thinking. Understanding how to traverse, manipulate, and analyze arrays is crucial for solving more complex problems.
## Key Concepts
### 1. Array Traversal
**What it is**: Visiting each element in an array in sequence
**Example**:
```typescript
// Linear traversal
function linearSearch(arr: number[], target: number): number {
for (let i = 0; i < arr.length; i++) {
if (arr[i] === target) {
return i;
}
}
return -1; // Not found
}
```
**Important considerations**:
- Always check if array is empty first
- Use `for` loops for precise index control
- Use `forEach` or `map` when you need the value but not the index
### 2. Basic Array Operations
#### Finding Minimum and Maximum
```typescript
function findMinMax(arr: number[]): { min: number, max: number } {
if (arr.length === 0) {
throw new Error("Array is empty");
}
let min = arr[0];
let max = arr[0];
for (let i = 1; i < arr.length; i++) {
if (arr[i] < min) {
min = arr[i];
}
if (arr[i] > max) {
max = arr[i];
}
}
return { min, max };
}
```
**Time Complexity**: O(n) - We visit each element once
**Space Complexity**: O(1) - We only store a few variables
#### Frequency Counting
```typescript
function frequencyCount(arr: number[]): Map<number, number> {
const frequency = new Map<number, number>();
for (const num of arr) {
frequency.set(num, (frequency.get(num) || 0) + 1);
}
return frequency;
}
```
**Time Complexity**: O(n) - One pass through the array
**Space Complexity**: O(n) - Store frequency of each unique element
### 3. Edge Cases to Consider
1. **Empty array**: What happens when `arr.length === 0`?
2. **Single element**: Arrays with only one element
3. **All same elements**: Arrays where all values are identical
4. **Negative numbers**: How algorithms handle negative values
5. **Large arrays**: Performance considerations
## Practice Patterns
### Pattern 1: Linear Search Variations
**Problem**: Find all occurrences of a target
```typescript
function findAllOccurrences(arr: number[], target: number): number[] {
const indices: number[] = [];
for (let i = 0; i < arr.length; i++) {
if (arr[i] === target) {
indices.push(i);
}
}
return indices;
}
```
**Time Complexity**: O(n)
**Space Complexity**: O(k) where k is number of occurrences
### Pattern 2: Array Validation
**Problem**: Check if array meets certain criteria
```typescript
function isNonDecreasing(arr: number[]): boolean {
for (let i = 1; i < arr.length; i++) {
if (arr[i] < arr[i-1]) {
return false;
}
}
return true;
}
```
**Time Complexity**: O(n)
**Space Complexity**: O(1)
## Real-world Applications
1. **Search functionality**: Finding items in a list
2. **Data analysis**: Counting frequencies, finding trends
3. **Game development**: Player positions, inventory management
4. **Web development**: Form validation, data filtering
## Common Mistakes
1. **Off-by-one errors**: Using `<` vs `<=` in loops
2. **Undefined access**: Not checking array bounds
3. **Memory leaks**: Not clearing temporary arrays
4. **Inefficient algorithms**: Using nested loops when unnecessary
## Next Steps
After mastering array basics, you should be comfortable with:
- Traversing arrays in different ways
- Performing basic operations (search, count, min/max)
- Handling edge cases properly
- Understanding time complexity of basic operations
**Ready for**: Two pointers technique (next topic)
+346
View File
@@ -0,0 +1,346 @@
# Binary Search Pattern
## Overview
Binary search is a fundamental algorithm for finding elements in sorted arrays. It follows the divide-and-conquer strategy, repeatedly dividing the search space in half.
## Core Concepts
### Binary Search Principle
- **Precondition**: Array must be sorted
- **Process**: Compare target with middle element, eliminate half of search space
- **Complexity**: O(log n) time, O(1) space
### Basic Template
```typescript
function binarySearch(arr: number[], target: number): number {
let left = 0;
let right = arr.length - 1;
while (left <= right) {
const mid = Math.floor((left + right) / 2);
if (arr[mid] === target) {
return mid;
} else if (arr[mid] < target) {
left = mid + 1;
} else {
right = mid - 1;
}
}
return -1; // Target not found
}
```
## Variations
### 1. Lower Bound (First Occurrence)
```typescript
function lowerBound(arr: number[], target: number): number {
let left = 0;
let right = arr.length - 1;
let result = -1;
while (left <= right) {
const mid = Math.floor((left + right) / 2);
if (arr[mid] >= target) {
result = mid;
right = mid - 1;
} else {
left = mid + 1;
}
}
return result;
}
```
### 2. Upper Bound (Last Occurrence)
```typescript
function upperBound(arr: number[], target: number): number {
let left = 0;
let right = arr.length - 1;
let result = -1;
while (left <= right) {
const mid = Math.floor((left + right) / 2);
if (arr[mid] <= target) {
result = mid;
left = mid + 1;
} else {
right = mid - 1;
}
}
return result;
}
```
### 3. Find Closest Element
```typescript
function findClosest(arr: number[], target: number): number {
let left = 0;
let right = arr.length - 1;
while (left < right - 1) {
const mid = Math.floor((left + right) / 2);
if (arr[mid] < target) {
left = mid;
} else {
right = mid;
}
}
// Compare the two remaining elements
return Math.abs(arr[left] - target) <= Math.abs(arr[right] - target)
? arr[left] : arr[right];
}
```
## Advanced Applications
### 1. Search in Rotated Sorted Array
```typescript
function search(nums: number[], target: number): number {
let left = 0;
let right = nums.length - 1;
while (left <= right) {
const mid = Math.floor((left + right) / 2);
if (nums[mid] === target) {
return mid;
}
// Check if left half is sorted
if (nums[left] <= nums[mid]) {
if (nums[left] <= target && target < nums[mid]) {
right = mid - 1;
} else {
left = mid + 1;
}
} else {
// Right half is sorted
if (nums[mid] < target && target <= nums[right]) {
left = mid + 1;
} else {
right = mid - 1;
}
}
}
return -1;
}
```
### 2. Find Minimum in Rotated Sorted Array
```typescript
function findMin(nums: number[]): number {
let left = 0;
let right = nums.length - 1;
while (left < right) {
const mid = Math.floor((left + right) / 2);
if (nums[mid] > nums[right]) {
left = mid + 1;
} else {
right = mid;
}
}
return nums[left];
}
```
### 3. Search in 2D Matrix
```typescript
function searchMatrix(matrix: number[][], target: number): boolean {
if (matrix.length === 0 || matrix[0].length === 0) return false;
const rows = matrix.length;
const cols = matrix[0].length;
let left = 0;
let right = rows * cols - 1;
while (left <= right) {
const mid = Math.floor((left + right) / 2);
const row = Math.floor(mid / cols);
const col = mid % cols;
if (matrix[row][col] === target) {
return true;
} else if (matrix[row][col] < target) {
left = mid + 1;
} else {
right = mid - 1;
}
}
return false;
}
```
## Time Complexity Analysis
| Problem | Time Complexity | Space Complexity | Notes |
|---------|----------------|----------------|---------|
| Basic Binary Search | O(log n) | O(1) | Standard implementation |
| Lower Bound | O(log n) | O(1) | First occurrence |
| Upper Bound | O(log n) | O(1) | Last occurrence |
| Rotated Array | O(log n) | O(1) | Handle rotation |
| 2D Matrix | O(log(mn)) | O(1) | Treat as 1D |
## Best Practices
### 1. Pointer Initialization
- Use `left = 0` and `right = arr.length - 1`
- For exclusive bounds: `left = 0` and `right = arr.length`
### 2. Mid Calculation
```typescript
// Avoid overflow in some languages
const mid = left + Math.floor((right - left) / 2);
// Standard approach in TypeScript
const mid = Math.floor((left + right) / 2);
```
### 3. Loop Condition
```typescript
// Include equal case to find target
while (left <= right)
// For exclusive bounds
while (left < right)
```
### 4. Pointer Updates
```typescript
// Always move mid pointer
left = mid + 1; // Not mid
right = mid - 1; // Not mid
```
## Common Mistakes
### 1. Infinite Loop
```typescript
// Wrong: mid might not move
left = mid; // Should be mid + 1
right = mid; // Should be mid - 1
// Correct: move pointers past mid
left = mid + 1;
right = mid - 1;
```
### 2. Incorrect Mid Calculation
```typescript
// Wrong: potential overflow
const mid = (left + right) / 2;
// Correct: safer approach
const mid = left + Math.floor((right - left) / 2);
```
### 3. Wrong Loop Condition
```typescript
// Wrong: might miss element
while (left < right) {
// Target might be at right boundary
// Correct: include equal case
while (left <= right) {
// Ensures all elements are checked
}
```
### 4. Not Handling Edge Cases
```typescript
// Wrong: empty array
while (left <= right) {
// Will fail for empty array
// Correct: add check
if (arr.length === 0) return -1;
```
## Practice Problems
### Easy
- [ ] Basic binary search
- [ ] Find lower bound
- [ ] Find upper bound
- [ ] Find closest element
- [ ] Find peak in mountain array
### Medium
- [ ] Search in rotated sorted array
- [ ] Find minimum in rotated sorted array
- [ ] Search in 2D matrix
- [ ] Find first and last position of element
- [ ] Find peak element
### Hard
- [ ] Median of two sorted arrays
- [ ] Find k-th element in sorted matrix
- [ ] Find smallest letter greater than target
- [ ] Find in mountain array
- [ ] Find frequency of element in sorted array
## Real-world Applications
1. **Database Indexes**: B-tree searches
2. **Game Development**: Collision detection, spatial partitioning
3. **Machine Learning**: Decision trees, feature selection
4. **Operating Systems**: Process scheduling, resource allocation
## Binary Search vs Linear Search
| Aspect | Binary Search | Linear Search |
|--------|---------------|---------------|
| Time Complexity | O(log n) | O(n) |
| Space Complexity | O(1) | O(1) |
| Precondition | Array must be sorted | No precondition |
| Best for | Large sorted datasets | Small or unsorted datasets |
| Insertion | O(n) | O(1) |
## Tips for Mastery
### 1. Understand the Pattern
- Binary search works on any monotonic function
- It's not just for arrays, but any searchable space
- The key is reducing search space by half each time
### 2. Practice Edge Cases
- Empty array
- Single element
- All elements same
- Target not present
- Target at boundaries
### 3. Implement Variations
- Practice lower/upper bounds
- Work with rotated arrays
- Try 2D searches
- Implement on custom data structures
### 4. Combine with Other Patterns
- Binary search + two pointers
- Binary search + sliding window
- Binary search + dynamic programming
## Next Steps
1. Master basic binary search (5+ problems)
2. Practice rotated array variations
3. Learn to apply binary search to search spaces
4. Combine with other algorithmic patterns
---
**Key Takeaway**: Binary search is one of the most important algorithms. It's efficient, elegant, and applicable to many problems beyond simple array searches. Always consider binary search when dealing with sorted data.
+357
View File
@@ -0,0 +1,357 @@
# Prefix Sum Pattern
## Overview
Prefix sum is a powerful technique for efficiently answering range sum queries and solving various array problems. It involves precomputing cumulative sums to allow O(1) range queries.
## Core Concepts
### Prefix Sum Definition
The prefix sum array `prefix` is defined such that:
```
prefix[i] = arr[0] + arr[1] + ... + arr[i]
```
Given a range query from index `i` to `j`:
```
sum(i, j) = prefix[j] - prefix[i-1]
```
### Basic Implementation
```typescript
function buildPrefixSum(arr: number[]): number[] {
const prefix = new Array(arr.length);
prefix[0] = arr[0];
for (let i = 1; i < arr.length; i++) {
prefix[i] = prefix[i-1] + arr[i];
}
return prefix;
}
function rangeSum(prefix: number[], left: number, right: number): number {
if (left === 0) {
return prefix[right];
}
return prefix[right] - prefix[left-1];
}
```
## Variations
### 1. Prefix Product
```typescript
function buildPrefixProduct(arr: number[]): number[] {
const prefix = new Array(arr.length);
prefix[0] = arr[0];
for (let i = 1; i < arr.length; i++) {
prefix[i] = prefix[i-1] * arr[i];
}
return prefix;
}
function rangeProduct(prefix: number[], left: number, right: number): number {
if (left === 0) {
return prefix[right];
}
return prefix[right] / prefix[left-1];
}
```
### 2. 2D Prefix Sum
```typescript
function build2DPrefixSum(matrix: number[][]): number[][] {
const rows = matrix.length;
const cols = matrix[0].length;
const prefix = Array.from({ length: rows + 1 }, () => Array(cols + 1).fill(0));
for (let i = 1; i <= rows; i++) {
for (let j = 1; j <= cols; j++) {
prefix[i][j] = matrix[i-1][j-1] +
prefix[i-1][j] +
prefix[i][j-1] -
prefix[i-1][j-1];
}
}
return prefix;
}
function range2DSum(prefix: number[][], row1: number, col1: number, row2: number, col2: number): number {
return prefix[row2+1][col2+1] -
prefix[row1][col2+1] -
prefix[row2+1][col1] +
prefix[row1][col1];
}
```
### 3. Prefix XOR
```typescript
function buildPrefixXOR(arr: number[]): number[] {
const prefix = new Array(arr.length);
prefix[0] = arr[0];
for (let i = 1; i < arr.length; i++) {
prefix[i] = prefix[i-1] ^ arr[i];
}
return prefix;
}
function rangeXOR(prefix: number[], left: number, right: number): number {
if (left === 0) {
return prefix[right];
}
return prefix[right] ^ prefix[left-1];
}
```
## Applications
### 1. Subarray Sum Equals K
```typescript
function subarraySum(nums: number[], k: number): number {
const prefixSum = { 0: 1 };
let currentSum = 0;
let count = 0;
for (const num of nums) {
currentSum += num;
// Check if there's a prefix that sums to currentSum - k
if (prefixSum[currentSum - k]) {
count += prefixSum[currentSum - k];
}
// Store current prefix sum
prefixSum[currentSum] = (prefixSum[currentSum] || 0) + 1;
}
return count;
}
```
### 2. Find Maximum Subarray Sum
```typescript
function maxSubArray(nums: number[]): number {
let maxSum = nums[0];
let currentSum = nums[0];
for (let i = 1; i < nums.length; i++) {
currentSum = Math.max(nums[i], currentSum + nums[i]);
maxSum = Math.max(maxSum, currentSum);
}
return maxSum;
}
```
### 3. Product of Array Except Self
```typescript
function productExceptSelf(nums: number[]): number[] {
const n = nums.length;
const left = new Array(n);
const right = new Array(n);
const result = new Array(n);
// Left prefix products
left[0] = 1;
for (let i = 1; i < n; i++) {
left[i] = left[i-1] * nums[i-1];
}
// Right prefix products
right[n-1] = 1;
for (let i = n-2; i >= 0; i--) {
right[i] = right[i+1] * nums[i+1];
}
// Result is product of left and right
for (let i = 0; i < n; i++) {
result[i] = left[i] * right[i];
}
return result;
}
```
## Time Complexity Analysis
| Operation | Time Complexity | Space Complexity | Notes |
|-----------|----------------|----------------|---------|
| Build Prefix Sum | O(n) | O(n) | One pass through array |
| Range Query | O(1) | O(1) | Constant time lookup |
| 2D Prefix Sum | O(mn) | O(mn) | For m×n matrix |
| 2D Range Query | O(1) | O(1) | Rectangle sum query |
## Best Practices
### 1. Handling Edge Cases
```typescript
// Handle empty array
if (arr.length === 0) return [];
// Handle single element
if (arr.length === 1) return [arr[0]];
// Handle negative numbers
// Prefix sum works with negatives too
```
### 2. Space Optimization
```typescript
// In-place prefix sum (if input can be modified)
for (let i = 1; i < arr.length; i++) {
arr[i] += arr[i-1];
}
```
### 3. Multiple Prefix Arrays
```typescript
// For different operations, build separate prefix arrays
const sumPrefix = buildPrefixSum(arr);
const xorPrefix = buildPrefixXOR(arr);
```
## Common Mistakes
### 1. Index Errors
```typescript
// Wrong: off-by-one error
prefix[i] = prefix[i] + arr[i]; // Should use i-1
// Correct: proper indexing
prefix[i] = prefix[i-1] + arr[i];
```
### 2. Range Query Errors
```typescript
// Wrong: incorrect range calculation
sum = prefix[right] - prefix[left]; // Misses left element
// Correct: proper range calculation
sum = prefix[right] - prefix[left-1];
```
### 3. Integer Overflow
```typescript
// Problem: Large numbers can cause overflow
// Solution: Use BigInt or modulo operation
const prefix = new Array(n).fill(BigInt(0));
```
### 4. Not Handling Negative Numbers
```typescript
// Prefix sums work with negatives automatically
// No special handling needed
```
## Practice Problems
### Easy
- [ ] Range sum query
- [ ] Prefix sum array construction
- [ ] Sum of subarray
- [ ] Product of array except self
- [ ] Find pivot index
### Medium
- [ ] Subarray sum equals K
- [ Maximum subarray (Kadane's algorithm)
- [ ] Contiguous array
- [ ] Find the shortest subarray
- [ ] Minimum size subarray sum
### Hard
- [ ] Maximum sum circular subarray
- [ ] Subarray product less than K
- [ ] Find longest subarray with equal 0s and 1s
- [ ] 2D range sum queries
- [ ] Range sum query 2D (immutable)
## Real-world Applications
1. **Data Analysis**: Time series analysis, moving averages
2. **Image Processing**: Integral images for fast feature computation
3. **Database Queries**: Range queries on indexed data
4. **Game Development**: Collision detection, spatial queries
## Advanced Techniques
### 1. Dynamic Programming with Prefix Sums
```typescript
function countSubarrays(arr: number[], target: number): number {
const prefixMap = new Map<number, number>();
prefixMap.set(0, 1);
let currentSum = 0;
let count = 0;
for (let i = 0; i < arr.length; i++) {
currentSum += arr[i];
// Find how many times currentSum - target has occurred
if (prefixMap.has(currentSum - target)) {
count += prefixMap.get(currentSum - target);
}
// Update prefix map
prefixMap.set(currentSum, (prefixMap.get(currentSum) || 0) + 1);
}
return count;
}
```
### 2. Sliding Window with Prefix Sums
```typescript
function findMaxAverage(nums: number[], k: number): number {
// Build prefix sum
const prefix = new Array(nums.length);
prefix[0] = nums[0];
for (let i = 1; i < nums.length; i++) {
prefix[i] = prefix[i-1] + nums[i];
}
let maxSum = prefix[k-1];
for (let i = k; i < nums.length; i++) {
const currentSum = prefix[i] - prefix[i-k];
maxSum = Math.max(maxSum, currentSum);
}
return maxSum / k;
}
```
## Tips for Mastery
### 1. Understand the Mathematics
- Prefix sum is cumulative addition
- Range queries use the inclusion-exclusion principle
- The technique works for any associative operation (sum, product, min, max)
### 2. Practice Different Operations
- Sum, product, XOR, min, max
- 1D and 2D applications
- Dynamic programming combinations
### 3. Optimize Space
- In-place modifications when possible
- Single pass algorithms
- Space-efficient data structures
### 4. Combine with Other Patterns
- Sliding window
- Binary search
- Hash tables for frequency counting
## Next Steps
1. Master basic prefix sum construction and queries
2. Practice 1D and 2D applications
3. Learn to combine with dynamic programming
4. Apply to real-world problems
---
**Key Takeaway**: Prefix sum is a fundamental technique for range queries and subarray problems. It transforms O(n) range queries into O(1) operations with O(n) preprocessing time. Always consider prefix sums when dealing with range-based array problems.
+302
View File
@@ -0,0 +1,302 @@
# Two Pointers Pattern
## Overview
Two pointers is a powerful pattern for solving array and string problems efficiently. It involves using two pointers (indices) that traverse the data structure from different directions or at different speeds.
## Core Concepts
### Types of Two Pointers
1. **Opposite Direction**: One pointer starts at beginning, other at end
2. **Same Direction**: Both pointers start at beginning, move at different speeds
3. **Fast/Slow Pointers**: Detect cycles in linked lists
### Basic Template
```typescript
function twoPointers(arr: number[]): void {
let left = 0;
let right = arr.length - 1;
while (left < right) {
// Process elements at left and right
const leftVal = arr[left];
const rightVal = arr[right];
// Move pointers based on condition
if (condition) {
left++;
} else {
right--;
}
}
}
```
## Use Cases
### 1. Sorted Arrays
Perfect for problems where the array is sorted and you need to find pairs, triplets, or perform operations.
### 2. Palindrome Checking
Check if a string or array reads the same forwards and backwards.
### 3. Container Problems
Find maximum area, minimum window, or similar container-based problems.
### 4. Subarray Problems
Find subarrays that meet certain conditions.
## Example Implementations
### Two Sum (Sorted Array)
```typescript
function twoSum(numbers: number[], target: number): number[] {
let left = 0;
let right = numbers.length - 1;
while (left <= right) {
const sum = numbers[left] + numbers[right];
if (sum === target) {
return [left + 1, right + 1]; // 1-based indexing
} else if (sum > target) {
right--;
} else {
left++;
}
}
return [-1, -1];
}
```
### Container With Most Water
```typescript
function maxArea(height: number[]): number {
let left = 0;
let right = height.length - 1;
let maxArea = 0;
while (left < right) {
const currentHeight = Math.min(height[left], height[right]);
const currentWidth = right - left;
const currentArea = currentHeight * currentWidth;
maxArea = Math.max(maxArea, currentArea);
if (height[left] < height[right]) {
left++;
} else {
right--;
}
}
return maxArea;
}
```
### Sorted Squares
```typescript
function sortedSquares(nums: number[]): number[] {
const result = new Array(nums.length);
let left = 0;
let right = nums.length - 1;
let index = nums.length - 1;
while (left <= right) {
const leftSquare = nums[left] * nums[left];
const rightSquare = nums[right] * nums[right];
if (leftSquare > rightSquare) {
result[index] = leftSquare;
left++;
} else {
result[index] = rightSquare;
right--;
}
index--;
}
return result;
}
```
### Palindrome Check
```typescript
function isPalindrome(s: string): boolean {
let left = 0;
let right = s.length - 1;
while (left < right) {
if (s[left] !== s[right]) {
return false;
}
left++;
right--;
}
return true;
}
```
## Time Complexity Analysis
| Problem | Time Complexity | Space Complexity | Pattern |
|---------|----------------|----------------|---------|
| Two Sum | O(n) | O(1) | Opposite direction |
| Max Area | O(n) | O(1) | Opposite direction |
| Sorted Squares | O(n) | O(n) | Opposite direction |
| Palindrome | O(n) | O(1) | Opposite direction |
## Variations
### Fast/Slow Pointers (Cycle Detection)
```typescript
function hasCycle(head: ListNode): boolean {
let slow = head;
let fast = head;
while (fast && fast.next) {
slow = slow.next;
fast = fast.next.next;
if (slow === fast) {
return true;
}
}
return false;
}
```
### Sliding Window (Two Pointers Same Direction)
```typescript
function maxSlidingWindow(nums: number[], k: number): number[] {
const result = [];
const deque = [];
for (let i = 0; i < nums.length; i++) {
// Remove elements outside window
while (deque.length > 0 && deque[0] <= i - k) {
deque.shift();
}
// Remove elements smaller than current
while (deque.length > 0 && nums[deque[deque.length - 1]] < nums[i]) {
deque.pop();
}
deque.push(i);
// Add maximum to result
if (i >= k - 1) {
result.push(nums[deque[0]]);
}
}
return result;
}
```
## Best Practices
### 1. Pointer Initialization
- **Opposite Direction**: `left = 0, right = arr.length - 1`
- **Same Direction**: `left = 0, right = 0` or `left = 0, right = 1`
- **Fast/Slow**: `slow = head, fast = head`
### 2. Loop Conditions
- **Opposite Direction**: `while (left < right)`
- **Same Direction**: `while (right < arr.length)`
- **Fast/Slow**: `while (fast && fast.next)`
### 3. Movement Logic
- **For sorted arrays**: Move pointer based on comparison with target
- **For containers**: Move pointer pointing to smaller element
- **For palindromes**: Move both pointers towards center
### 4. Edge Cases
- Empty array/single element
- All elements same
- Array with negative numbers
- Array with very large values
## Common Mistakes
### 1. Incorrect Loop Condition
```typescript
// Wrong: includes left === right case
while (left <= right) {
// This might process the same element twice
}
// Correct: for opposite direction
while (left < right) {
// Ensures elements are distinct
}
```
### 2. Wrong Pointer Movement
```typescript
// Wrong: always move left
if (sum > target) {
left++; // Should move right
} else {
left++;
}
// Correct: move appropriate pointer
if (sum > target) {
right--;
} else {
left++;
}
```
### 3. Ignoring Edge Cases
```typescript
// Wrong: no empty array check
while (left < right) {
// Will fail for empty array
// Correct: add edge case check
if (arr.length === 0) return [];
```
## Practice Problems
### Easy
- [ ] Two Sum (sorted array)
- [ ] Palindrome check
- [ ] Valid palindrome II
- [ ] Squares of sorted array
- [ ] Reverse string
### Medium
- [ ] Container with most water
- [ ] Three sum
- [ ] Remove duplicates from sorted array
- [ ] Minimum size subarray sum
- [ ] Trapping rain water
### Hard
- [ ] Longest substring without repeating characters
- [ ] Substring with concatenation of all words
- [ ] Minimum window substring
- [ ] Shortest unsorted continuous subarray
- [ ] Longest valid parentheses
## Real-world Applications
1. **Image Processing**: Edge detection, feature matching
2. **Data Analysis**: Time series analysis, moving averages
3. **Text Processing**: String matching, pattern recognition
4. **Signal Processing**: Filter design, peak detection
## Next Steps
1. Master two pointers pattern with 10+ problems
2. Practice with different array types (sorted, unsorted)
3. Learn sliding window variation
4. Combine with other patterns (hash tables, binary search)
---
**Key Takeaway**: Two pointers is often the optimal solution for array problems, especially when dealing with sorted data or pairs of elements. Always consider this pattern when you see "sorted array" or "find pairs" in problem statements.