# How the leetcode‑master Repository Handles Edge Cases in Two‑Pointer Technique Problems

> Master edge cases in two-pointer technique problems with the leetcode-master repository. Learn sentinel initialization, pointer offset, and synchronized traversal to avoid errors.

- Repository: [程序员Carl/leetcode-master](https://github.com/youngyangyang04/leetcode-master)
- Tags: tutorial
- Published: 2026-03-05

---

**The leetcode‑master repository eliminates null‑pointer dereferences, duplicate counting, and off‑by‑one errors through a systematic workflow of sentinel initialization, pointer offset establishment, and synchronized traversal with duplicate‑skipping guards.**

The **leetcode‑master** collection by youngyangyang04 treats two‑pointer solutions as a reusable architectural pattern rather than isolated algorithms. By examining the implementations in `problems/0015.三数之和.md`, `problems/0019.删除链表的倒数第N个节点.md`, and related files, we can extract a rigorous methodology for handling edge cases in two‑pointer technique problems. This approach guarantees **O(1) extra space** while safely managing empty inputs, boundary deletions, and duplicate elements.

## The Five‑Step Edge‑Case Safety Workflow

Every two‑pointer solution in the repository follows a predictable lifecycle that hardens the algorithm against common failure modes.

### Step 1: Sentinel Node Initialization for Linked Lists

When the data structure is a linked list, the code creates a **dummy head node** (sentinel) that points to the real head. This guarantees the *slow* pointer can always reference the node **preceding** the deletion target, even when the head itself must be removed.

In `problems/0019.删除链表的倒数第N个节点.md`, the implementation constructs `ListNode dummy(0, head)` before establishing the fast and slow pointers. This pattern eliminates special‑case branching for empty lists, single‑node lists, or when `n` equals the list length.

### Step 2: Pointer Offset Establishment

The repository enforces a strict distance invariant between pointers before the main traversal begins. For the “remove nth node” pattern, the fast pointer advances `n + 1` steps ahead of the slow pointer.

```cpp
ListNode* fast = &dummy;
ListNode* slow = &dummy;
for (int i = 0; i <= n; ++i) fast = fast->next;

```

This offset ensures that when `fast` reaches `NULL`, `slow` points to the node immediately before the target, preventing off‑by‑one errors and null‑dereference crashes when deleting the final element.

### Step 3: Synchronized Traversal Until Terminus

Both pointers advance in lockstep (`while (fast) { fast = fast->next; slow = slow->next; }`) until the fast pointer exits the container. This linear‑time scan handles arrays with single elements, already‑sorted inputs, and cases where the answer resides at either end.

For array problems like **Three Sum** in `problems/0015.三数之和.md`, the repository uses **left** and **right** pointers that converge toward the center, skipping duplicate values immediately after recording a valid result.

### Step 4: Mutation or Computation

With the slow pointer (or left/right pointers) positioned safely, the algorithm performs the final update. In linked list deletion, `slow->next` is rewired; in the **Trapping Rain Water** problem (`problems/0042.接雨水.md`), the code calculates water volume using `leftMax` and `rightMax` invariants that correctly yield zero for monotonic or all‑zero height arrays.

### Step 5: Optional Cleanup

For languages without garbage collection, the repository explicitly deletes detached nodes to prevent memory leaks, satisfying strict **O(1) space** constraints required by two‑pointer problem specifications.

## Specific Edge‑Case Strategies by Problem

### Duplicate Elimination in Three Sum (0015)

The **Three Sum** implementation handles the `[0,0,0,0]` edge case by advancing pointers past duplicate values immediately after finding a valid triplet.

```cpp
while (left < right && nums[left]  == nums[left  + 1]) ++left;
while (left < right && nums[right] == nums[right - 1]) --right;
++left; --right;

```

The outer loop also contains an early termination guard—`if (nums[i] > 0) break;`—that stops processing when all remaining numbers are positive, preventing unnecessary iterations on large sorted arrays.

### Head Deletion in Remove Nth Node (0019)

When `n` equals the list length, the node to delete is the head. The dummy node ensures `slow` remains on the sentinel, allowing `slow->next = slow->next->next` to remove the real head without conditional branches. For single‑node lists where `n == 1`, the fast pointer becomes `NULL` after the initial `n+1` advances, the while‑loop is skipped, and the dummy’s next pointer is safely set to `NULL`.

### Monotonic Sequences in Trapping Rain Water (0042)

The two‑pointer variant maintains `leftMax` and `rightMax` heights. When one side is lower, its pointer moves inward and the contribution is calculated as `max(0, maxHeight - currentHeight)`. This logic correctly returns `0` for strictly increasing or decreasing sequences, as well as for arrays containing all zeros, without additional checks.

### Empty Input Guards

In `problems/0206.翻转链表.md` (Reverse Linked List), the function checks `if (!head) return nullptr;` before entering the reversal loop. Similarly, `problems/0028.实现strStr.md` returns `0` immediately for an empty `needle` and handles the `needle.size() > haystack.size()` case by limiting the loop range to `haystack.size() - needle.size() + 1`, returning `-1` when the range is negative.

## Canonical Implementation Templates

The following **C++ templates** embody the repository’s defensive programming style for array and linked list scenarios.

### Array Two‑Pointer Skeleton

```cpp
class Solution {
public:
    vector<vector<int>> twoPointerTemplate(vector<int>& nums, int target) {
        sort(nums.begin(), nums.end());
        vector<vector<int>> ans;
        int left = 0;
        int right = static_cast<int>(nums.size()) - 1;
        
        while (left < right) {
            int sum = nums[left] + nums[right];
            if (sum == target) {
                ans.push_back({nums[left], nums[right]});
                
                // Skip duplicates to avoid double counting
                while (left < right && nums[left] == nums[left + 1]) ++left;
                while (left < right && nums[right] == nums[right - 1]) --right;
                
                ++left;
                --right;
            } else if (sum < target) {
                ++left;
            } else {
                --right;
            }
        }
        return ans;
    }
};

```

### Linked List Fast‑Slow Skeleton

```cpp
class Solution {
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) {
        ListNode dummy(0, head);
        ListNode* fast = &dummy;
        ListNode* slow = &dummy;
        
        // Advance fast n+1 steps to create the offset
        for (int i = 0; i <= n; ++i) {
            fast = fast->next;
        }
        
        // Move both until fast reaches the end
        while (fast) {
            fast = fast->next;
            slow = slow->next;
        }
        
        // Safe deletion guaranteed by sentinel
        ListNode* toDelete = slow->next;
        slow->next = slow->next->next;
        delete toDelete;  // For non-GC languages
        return dummy.next;
    }
};

```

## Summary

- **Sentinel nodes** in linked list problems eliminate special‑case code for head deletion and empty inputs.
- **Pointer offset initialization** (`n + 1` steps) guarantees the slow pointer lands on the node preceding the target, preventing off‑by‑one errors.
- **Duplicate‑skipping loops** immediately after matches ensure unique results in problems like Three Sum.
- **Early termination guards** (e.g., `if (nums[i] > 0) break`) prevent unnecessary computation on sorted data.
- **Invariant maintenance** (leftMax/rightMax, pointer distance) allows the algorithm to handle monotonic sequences and all‑zero inputs without branching logic.

## Frequently Asked Questions

### Why does leetcode‑master use a dummy head node in linked list two‑pointer problems?

The dummy head node acts as a sentinel that ensures the slow pointer always points to a valid node preceding the deletion target. According to the source code in `problems/0019.删除链表的倒数第N个节点.md`, this pattern removes the need for conditional branches when deleting the first node or handling single‑node lists, preventing null‑pointer dereferences.

### How does the repository prevent duplicate triplets in the Three Sum problem?

After finding a valid triplet in `problems/0015.三数之和.md`, the algorithm advances the left pointer past all identical values using a `while (left < right && nums[left] == nums[left + 1]) ++left;` loop, and similarly for the right pointer. This guarantees that `[0,0,0,0]` produces only one unique result rather than four.

### What prevents off‑by‑one errors when removing the nth node from the end?

The repository establishes a strict invariant by advancing the fast pointer `n + 1` steps ahead of the slow pointer before the traversal loop begins. As implemented in `problems/0019.删除链表的倒数第N个节点.md`, this ensures that when the fast pointer reaches `NULL`, the slow pointer references the node immediately before the target, allowing safe removal without indexing errors.

### How are empty inputs handled in two‑pointer implementations?

The repository places explicit guards at function entry points. For example, `problems/0206.翻转链表.md` checks `if (!head) return nullptr;` before entering the reversal loop, while `problems/0028.实现strStr.md` returns `0` immediately for empty needles and calculates a non‑negative loop range to handle cases where the needle exceeds the haystack length.