NeFut Logo NeFut
Admin Login

In-Depth Analysis of Two Pointers Technique and Sliding Window Applications

Published at: 2026-05-27 09:17 Last updated: 2026-06-06 13:04
#algorithm #C++ #Sliding Window

Core Logic and Mathematical Principles

The Two Pointers technique is not an independent algorithm but a simplified version of search. It optimizes the naive double loop $O(N^2)$ to $O(N)$ by leveraging the monotonicity of sequences.

Essentially, it operates in a two-dimensional state space $(i, j)$, utilizing the monotonic increasing or decreasing characteristics of the boundaries to prune useless state branches. If the state quantity satisfies $f(i, j) \le f(i, j+1)$ and the goal is to find a critical value, then when $i$ increases, there is no need for $j$ to backtrack. The direction of pointer movement is an irreversible topological order.


State Design and Fast/Slow/Collision Topological Evolution

1. Collision Pointers (Two-Way Convergence)

Commonly used to find a specific sum in a sorted array. Let the left and right pointers be $l, r$, initialized to $1, N$. If $A[l] + A[r] > \text{target}$, due to the monotonicity of the array, for any $k > l$, it must hold that $A[k] + A[r] > \text{target}$. Therefore, the state $(*, r)$ is no longer valuable for search, and we execute r--. Similarly, if it is less than the target value, we execute l++.

2. Fast and Slow Pointers with Sliding Window

Used to maintain the continuity property of intervals. Let the left boundary of the window be $l$ and the right boundary be $r$. State definition: $[l, r]$ is the currently maintained valid/invalid interval.

Since both $l$ and $r$ are monotonically increasing, each element can enter and exit the queue at most once. The overall time complexity is:

$$\sum_{i=1}^N (\Delta l_i + \Delta r_i) = O(N)$$


C++ Standard Source Code (Industrial-Grade Code Required)

The following is a fully compliant sliding window template in the NOIP evaluation environment (Linux, GCC, -O2). The problem to solve is: find the length of the longest contiguous subarray containing no more than $K$ distinct elements.

#include <iostream>
#include <vector>
#include <algorithm>

using std::cin;
using std::cout;
using std::vector;
using std::max;

// Constraints: N <= 100000, element value range in [0, 100000]
const int MAX_VAL = 100005;
int cnt[MAX_VAL]; // Frequency hash table, avoiding using std::map which introduces O(log N) complexity

int main() {
    // Force synchronization off to speed up I/O. Note: Do not mix with scanf/printf
    std::ios_base::sync_with_stdio(false);
    cin.tie(NULL);

    int n, k;
    if (!(cin >> n >> k)) return 0;

    vector<int> a(n);
    for (int i = 0; i < n; ++i) {
        cin >> a[i];
    }

    int l = 0;
    int distinct_types = 0;
    int max_len = 0;

    // r is the fast pointer, l is the slow pointer
    for (int r = 0; r < n; ++r) {
        // Include the right boundary element
        if (cnt[a[r]] == 0) {
            distinct_types++;
        }
        cnt[a[r]]++;

        // When the window state is invalid, continuously shrink the left boundary l
        while (distinct_types > k) {
            cnt[a[l]]--;
            if (cnt[a[l]] == 0) {
                distinct_types--; // A certain element completely exits the window
            }
            l++; // Critical pitfall: l++ must occur after state update logic, otherwise it may cause out-of-bounds or logical errors
        }

        // At this point, [l, r] must be a valid window, update the global optimal solution
        max_len = max(max_len, r - l + 1);
    }

    cout << max_len << "\n";

    return 0;
}

NOIP Practical Pitfall Guide

1. Slow Pointer Out-of-Bounds and Infinite Loop Traps

In optimized sliding windows (e.g., finding the shortest subarray satisfying the sum $\ge S$), writing while (sum >= S) { sum -= a[l]; l++; } can easily trigger a segmentation fault when $l$ exceeds $r$ or even exceeds $N$. Correction Plan: It is essential to include boundary checks l <= r inside the while. However, in the logic of "finding the longest interval with no more than $K$ distinct elements", when $k=0$, distinct_types > 0 can cause $l$ to chase and surpass $r. Therefore, whenever writingwhileto shrink, first check ifl <= r` is a logical precondition.

2. Counter Update and Pointer Increment Order

When shrinking the left boundary, novices often write cnt[a[l++]]--. If the current cnt[a[l]] is 1, this statement first retrieves cnt[a[l]] for subtraction, then increments l. It seems correct, but if subsequent logic relies on a[l], it may read from an incorrect memory location due to l already being changed. Correction Plan: It is strictly forbidden to combine ++/-- operators with complex array index accesses in the same line. Write step-by-step: first modify the count, then handle the linked state, and finally l++.


Classic NOIP/Luogu Problems

1. Luogu P1638 Visiting Art Exhibition

2. Luogu P1102 A-B Pairs

Original Source: local://1.1

[h] Back to Home