# 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.