双方向ポインタ(Two Pointers) とは、配列の異なる位置にある2つのポインタ(変数 i と j など)を使い、データを効率的に探索するアルゴリズムの手法です。
この手法は ソート済みのデータに対して特に有効 で、以下のような問題で使われます:
- 特定の条件を満たすペアを数える
- 2つの数の和が特定の値になるかを判定する
- 区間の合計や条件を満たす部分列を探索する
- ポインタ
i(左側)を固定し、ポインタj(右側)を動かす- 例えば、
A[i]を固定して、A[j]を条件を満たす最大の位置まで動かす。
- 例えば、
- 条件を満たさなくなったら
iを動かすA[j] - A[i] > Kになったらiを進める。
この問題では、A[j] - A[i] <= K を満たすペア (A[i], A[j]) の数を求めます。
iとjの2つのポインタを用意(i=0からスタート、jも0からスタート)。jを右に動かし、A[j] - A[i] <= Kを満たす最大のjを見つける。- そのとき
A[i]と組み合わせられるのはA[i+1]からA[j-1]までの(j - i - 1)個。 iを1つ進めて再計算する。iがN-1になるまで繰り返す。
function countPairsTwoPointers(N, K, A) {
let count = 0;
let j = 0; // jはiとともに進めるポインタ
for (let i = 0; i < N; i++) {
while (j < N && A[j] - A[i] <= K) {
j++; // 条件を満たす限り j を進める
}
count += j - i - 1; // (j - i - 1) 個のペアが作れる
}
console.log(count);
}
// 入力例
const [N, K, ...A] = `5 3
1 2 4 5 8`
.split(/\s+/)
.map(Number);
countPairsTwoPointers(N, K, A);N = 5, K = 3
A = [1, 2, 4, 5, 8]
i |
j の初期値 |
j の最大位置 |
(j - i - 1) |
count の累計 |
|---|---|---|---|---|
0 (A[0] = 1) |
0 | 3 (A[3] = 5) |
3 - 0 - 1 = 2 |
2 |
1 (A[1] = 2) |
3 | 3 (A[3] = 5) |
3 - 1 - 1 = 1 |
3 |
2 (A[2] = 4) |
3 | 4 (A[4] = 8) |
4 - 2 - 1 = 1 |
4 |
3 (A[3] = 5) |
4 | 4 (A[4] = 8) |
4 - 3 - 1 = 0 |
4 |
最終的に、ペア数は 4 となります。
iはN回ループする。jもN回以内に右端まで進む。- 合計
O(N)で解ける。
これは 二分探索(O(N log N))よりも高速 で、大きな N(最大 100000)でも高速に動作します。
✅ 双方向ポインタ(Two Pointers)とは?
- 2つのポインタを使って効率的に探索 するアルゴリズム。
- ソート済みデータ に適用すると O(N) で高速に解ける。
✅ この問題での適用方法
iを固定し、jを動かしてA[j] - A[i] <= Kを満たす最大のjを見つける。A[i]と組み合わせられるのは(j - i - 1)個。- O(N) の時間計算量 で解ける。
この部分は、A[i] を固定したときに、差が K 以下となる A[j] の個数を数える処理 です。
iを左ポインタとして 0 から順に動かすjを右ポインタとしてA[j] - A[i] <= Kを満たす間、右に進めるjが条件を満たさなくなったら、そのjまでに作れるペア数(j - i - 1)を加算する
j - iではなくj - i - 1になっている理由は、A[j]は条件を満たさないjの位置にあるため、カウントしないようにするためです。A[i]と組み合わせる相手はA[i+1]からA[j-1]まで なので、j-1 - iを求めるために-1している。
N = 5, K = 3
A = [1, 2, 4, 5, 8]
i (固定) |
j の最大位置 |
(j - i - 1) |
カウント加算 |
|---|---|---|---|
i = 0 (A[0] = 1) |
j = 3 (A[3] = 5) |
3 - 0 - 1 = 2 |
count += 2 |
i = 1 (A[1] = 2) |
j = 3 (A[3] = 5) |
3 - 1 - 1 = 1 |
count += 1 |
i = 2 (A[2] = 4) |
j = 4 (A[4] = 8) |
4 - 2 - 1 = 1 |
count += 1 |
i = 3 (A[3] = 5) |
j = 4 (A[4] = 8) |
4 - 3 - 1 = 0 |
count += 0 |
最終的に count = 4 となります。
jはA[j] - A[i] <= Kを満たす最大の位置まで進めるi固定時に、A[i+1]からA[j-1]までの (j - i - 1) 個 のペアが作れる- 計算量は
O(N)