투 포인터 알고리즘이란?
두 개의 포인터를 이용해 배열이나 문자열을 탐색하며 문제를 해결하는 알고리즘
보통 일차원 자료구조에서 특정 조건을 만족하는 연속 구간을 구할 때 O(N)의 시간 복잡도로 문제를 해결하기 위해 많이 사용되는 기법이며 코딩 테스트에서도 자주 등장하는 알고리즘이다
보통 이중루프로 풀어야 할 것 같은데 입력값이 커서 시간 초과가 날 것 같을 때 투 포인터 알고리즘을 떠올리면 대부분 풀어지더라
이와 비슷한 알고리즘 기법인 슬라이딩 윈도우도 있는 데 차이는 투 포인터는 구간이 가변적이며 슬라이딩 윈도우는 구간이 고정적이다. 자세한 건 다음 포스팅에서 다뤄보자
대부분 투 포인터 알고리즘은 인덱스를 나타내는 포인터인 left와 right를 사용하며 문제의 유형은 다음과 같다
간단하고 대표적인 투 포인터 문제이다
시간 제한이 0.5초이고 배열의 길이는 10000까지이며 구간 [i, j] 내의 값들의 합이 M이 되는 경우를 찾아야 한다
만약 투 포인터를 안 쓸 경우 이중루프를 통해 구해야 되는데 바로 시간 초과가 뜬다
#include <bits/stdc++.h>
using namespace std;
int N, M;
int arr[10004];
int answer;
void Input()
{
cin >> N >> M;
for (int i = 0; i < N; i++)
{
cin >> arr[i];
}
}
void Solve()
{
int left = 0, right = 0;
int sum = arr[0];
while (right < N)
{
if (sum == M)
{
answer++;
sum += arr[++right];
}
else if(sum > M)
{
sum -= arr[left++];
}
else if (sum < M)
{
sum += arr[++right];
}
}
cout << answer;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
Input();
Solve();
return 0;
}
이 문제의 경우 left와 right 포인터를 이용해 구간의 합을 구해가며 경우의 수를 구하는 문제이다
left와 right가 배열의 0번째 인덱스에서 같이 시작하는 것이 포인트
sum 변수는 [left, right]의 구간 합으로 생각하면 된다. 그러니까 0번째 인덱스의 변수로 초기화시켰다
루프 본문부터 살펴보면 현재 구간합이 M과 같다면 경우의 수를 증가시키고 right + 1 인덱스의 값을 sum에 더해준다. 그러니까 현재 구간을 [left, right + 1]로 바꾸는 것임
만약 M보다 크다면 현재 sum에서 left 인덱스 값을 빼준다. 구간을 [left + 1, right]로 변경하는 것
M보다 작다면 그냥 right를 한 칸 더 옮겨준다
여기서 내가 헷갈렸던 부분은 보통 left가 right보다 커지면 문제가 발생하는 것 같았는데 투 포인터의 경우 보통 이 조건이 추가되면 틀렸다
5 2
5 5 5 1 1
라는 입력이 들어온다고 생각해보자
위처럼 구현한다하면 처음부터 sum이 M을 넘겼기에 left가 right보다 커진다. 그러면 루프가 종료되어버려 답을 구할 수 없다
투 포인터에서의 핵심은 조건을 잘 정해주고 머릿속으로 시뮬레이션을 잘 해야 할 것 같다