마이다스 코테를 준비하면서 예전에 있던 문제들을 다시 풀어보았는데 투포인터에서 항상 막히고 못푸는 모습을 보였다.
관련 문제들을 풀면서 이번기회에 제대로 한번 개념을 잡고가보도록 하자.
우선 투포인터란
투 포인터(two pointers)는 배열에서 두 개의 인덱스를 이동시키며 조건을 만족하는 구간을 찾는 기법이다.
주로 연속된 부분 배열 문제에서 사용되며, 이중 반복문을 O(N)으로 줄이기 위해 활용한다.
핵심은 포인터가 한 방향으로만 이동한다는 점이다.
왼쪽 포인터와 오른쪽 포인터는 각각 최대 N번 이동하므로 전체 시간 복잡도는 O(N)이다.
연속 부분합 문제에서 투 포인터의 기본 구성은 다음과 같다.
- l: 구간 시작 인덱스
- r: 구간 끝 인덱스 (보통 포함하지 않음)
- sum: 현재 구간의 합
구간의 합(sum)을 유지하면서 조건에 따라 구간을 늘리거나 줄인다.
투 포인터 구현에서 가장 중요한 것은 조건 분기 순서다.
- 구간을 늘리는 동작(r 이동)은 항상 경계 체크가 필요하다
- 구간을 줄이는 동작(l 이동)은 상대적으로 안전하다
투 포인터 문제를 풀기 전 정리할 질문은 다음 정도면 충분하다.
- 연속된 구간을 다루는 문제인가?
- 구간 길이는 고정인가, 가변인가?
- 구간을 늘리는 조건과 줄이는 조건은 무엇인가?
- r이 끝에 도달했을 때 종료 조건은?
이제 문제를 풀어보도록 하겠다.
2003번: 수들의 합 2
https://www.acmicpc.net/problem/2003
문제 요약 : N개의 수로된 수열 (1 <= N <= 10,000) 에 연속된 부분수열의 합이 M이 되는 경우를 구하는 문제이다.
접근 방법 : 연속 부분 배열 문제이므로 투포인터를 사용했다. 포인터를 한 방향으로 이동시켜 O(N)에 해결한다.
풀이 전략 :
1. 처음엔 sum을 0으로 초기화하고
2. 합이 M 보다 크거나 같으면 우선 먼저 처리해준다. 같을때만 정답을 늘려주고 start를 빼준다.
3. 그 다음 end 부분이 N 까지 도달했다면
시간복잡도 : O(N)
코드 :
import java.io.*;
import java.util.*;
public class Main {
static int N, M, ans;
static int[] arr;
public static void main(String[] args) throws Exception {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());
N = Integer.parseInt(st.nextToken());
M = Integer.parseInt(st.nextToken());
arr = new int[N];
ans = 0;
st = new StringTokenizer(br.readLine());
for(int i = 0; i < N; i++){
arr[i] = Integer.parseInt(st.nextToken());
}
int start = 0, end = 0, sum = 0;
while(true){
if(sum >= M){
if(sum == M) ans++;
sum -= arr[start++];
}else if(end == N){
break;
}else{
sum += arr[end++];
}
}
System.out.println(ans);
}
}
회고
항상 이런 문제를 보면 우선 누적합을 이용하여 N^2에 문제를 풀려고 했는데 투포인터를 활용하는 방식을 머리속에 넣은것같다.
2559번: 수열
https://www.acmicpc.net/problem/2559
문제 요약 : 음수가 섞여있는 온도 N개의 수열에서 연속적인 K일의 구간합이 최대가 되는 값을 출력하는 문제이다.
접근 방법 : 이 문제는 고정 길이의 연속 구간을 다루므로 누적합으로 해결할 수 있으며 동일한 로직을 슬라이딩 윈도우 형태의 투 포인터로도 구현할 수 있다.
풀이전략 :
1. 누적합 배열을 만든다.
2. K 만큼 떨어진 곳의 차를 이용하여 구간합을 구한다.
3. 그 중 최대값을 ans 에 저장 후 출력한다.
시간복잡도 : O(N)
코드 :
import java.io.*;
import java.util.*;
public class Main {
static int N, K, ans;
static int[] arr;
public static void main(String[] args) throws Exception {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());
N = Integer.parseInt(st.nextToken());
K = Integer.parseInt(st.nextToken());
arr = new int[N + 1];
ans = Integer.MIN_VALUE;
st = new StringTokenizer(br.readLine());
for(int i = 1; i <= N; i++){
arr[i] = arr[i - 1] + Integer.parseInt(st.nextToken());
}
for(int i = K; i <= N; i++){
int sum = arr[i] - arr[i - K];
ans = Math.max(sum, ans);
}
System.out.println(ans);
}
}
회고 : 누적합을 바로 떠올리긴 했지만 구간설정에 조금 시간이 걸렸다. 배열을 0부터 안쓰고 1부터 쓰는건 편한거같다.
1644번: 소수의 연속합
https://www.acmicpc.net/problem/1644
문제 요약 : 주어지는 자연수 N을 연속된 소수의 합으로 나타낼 수 있는 경우의 수를 출력하는 문제
접근 방법 : 먼저 에라토스테네스의 채로 소수들을 구한 뒤 투포인터를 활용하여 N 이 되는 경우의 수를 구한다.
풀이전략 :
1. 에라토스테네스의 채를 사용하여 배열에 소수들을 차례대로 채워놓는다.
2. 투포인터를 활용하여 sum = 0에서 N보다 크거나 같으면 start를 줄이고 그중 N과 같다면 ans증가
3. sum이 N보다 작으면 리스트의 마지막에 end가 도달했는지 확인 후 늘리기
시간복잡도 : O(N)
코드 :
import java.io.*;
import java.util.*;
public class Main {
static int N, ans;
static int[] isPrime;
static List<Integer> list;
public static void main(String[] args) throws Exception {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());
N = Integer.parseInt(st.nextToken());
list = new ArrayList<>();
isPrime = new int[N + 1];
ans = 0;
// 1. 소수 리스트 만들기
Arrays.fill(isPrime, 1);
isPrime[0] = 0;
isPrime[1] = 0;
for(int i = 2; i * i <= N; i++){
if(isPrime[i] == 1){
for(int j = i * i; j <= N; j += i){
isPrime[j] = 0;
}
}
}
for(int i = 2; i <= N; i++){
if(isPrime[i] == 1) list.add(i);
}
// 2. 연속된 구간합 파악
int start = 0, end = 0, sum = 0;
while(true){
if(sum >= N){
if(sum == N) ans++;
sum -= list.get(start++);
}else{
if(end == list.size()) break;
sum += list.get(end++);
}
}
System.out.println(ans);
}
}
'코딩테스트 > JAVA' 카테고리의 다른 글
| [프로그래머스 JAVA] 짝지어 제거하기 (0) | 2026.02.04 |
|---|---|
| [프로그래머스 JAVA] 피보나치 수 (0) | 2026.02.03 |
| [백준 JAVA] 2138번: 전구와 스위치 (0) | 2026.01.07 |
| [백준 JAVA] 2110번: 공유기 설치 (0) | 2026.01.02 |
| [백준 JAVA] 2075번: N번째 큰 수 (1) | 2026.01.01 |