Maintain a window over an array, moving both ends monotonically. Each pointer advances at most times, so the whole scan is .

The sliding window template

int l = 0;
for (int r = 0; r < n; r++) {
    add(a[r]);                                 // extend the window
    while (!valid()) { remove(a[l]); l++; }    // shrink until valid
    best = max(best, r - l + 1);               // window [l, r] is valid
}

The whole design is in add, remove, and valid(). The loop shape never changes.

The three shapes

1. Longest valid window

As above: extend right, shrink left while invalid, record the length.
“Longest substring with at most distinct characters.”

2. Shortest valid window

for (int r = 0; r < n; r++) {
    add(a[r]);
    while (valid()) { best = min(best, r - l + 1); remove(a[l]); l++; }
}

Shrink while valid, recording before each shrink.
“Shortest subarray with sum .“

3. Count valid windows

for (int r = 0; r < n; r++) {
    add(a[r]);
    while (!valid()) { remove(a[l]); l++; }
    count += r - l + 1;                        // all windows ending at r
}

“Number of subarrays with at most distinct values.”

Exactly = (at most ) − (at most ). This subtraction trick converts a hard constraint into two easy ones and is worth remembering.

The requirement: monotonicity

Two pointers works only when extending the window never makes an invalid window valid, and shrinking never makes a valid window invalid. Formally, validity must be monotone in the window.

ConditionMonotone?
Sum , all values
Sum with negative values✘ — use prefix sums + a deque or a segment tree
At most distinct values
Maximum − minimum ✔ (with two monotonic deques)
Product , all values
GCD of the window ✔ (gcd only decreases as the window grows)
Contains all of a required multiset
Sum is exactly ✘ in general — use a hash map of prefix sums

Two pointers on two arrays

int i = 0, j = 0;
while (i < n && j < m) {
    if (a[i] < b[j]) i++;
    else if (a[i] > b[j]) j++;
    else { /* match */ i++; j++; }
}

Uses: merging sorted arrays, intersecting sorted sets, counting pairs with a bounded difference, and the merge step of merge sort.

Opposite-direction pointers

int l = 0, r = n - 1;
while (l < r) {
    if (a[l] + a[r] == target) { /* found */ break; }
    if (a[l] + a[r] < target) l++; else r--;
}

For a sorted array: two-sum, three-sum (fix one, two-pointer the rest), container-with-most-water, and the trapping rain water problem.

Maintaining window aggregates

AggregateStructure
Sum, counta running variable
Distinct counta frequency map plus a counter
Maximum / minimummonotonic deque, amortized
Both max and mintwo deques
GCDa sparse table, or a stack-based min-queue analogue
-th smallestPBDS or two heaps
Anything non-invertiblethe two-stack queue trick

The two-stack queue is the general answer: any aggregate a stack can maintain in can be maintained by a queue at amortized, so no “removal” operation is ever needed.

When two pointers fails

If the window’s left end must sometimes move backwards, the technique does not apply. Alternatives:

See also: Monotonic Queue · Prefix Sum · Mo’s Algorithm