투포인터(Two Pointers)란 배열이나 리스트와 같은 선형 자료구조에서 두 개의 포인터를 사용해 문제를 효율적으로 푸는 알고리즘 기법이다. 주로 정렬된 배열이나 연속 구간 문제에서 사용되며 O(N^2)
의 완전 탐색을 O(N)
또는 O(N log N)
으로 최적화할 수 있다.
핵심 아이디어
- 두 개의 포인터(
left
,right
)를 사용해 탐색 범위를 좁혀가며 조건을 만족하는 구간을 찾는다.- 포인터를 움직이는 규칙을 잘 설계하면 불필요한 중복 탐색을 제거할 수 있다.
O(N)
)예시 문제
1. 두 수의 합이 target
이 되는 쌍 찾기
2. 길이가 N
인 배열에서 합이 M
이하인 최대 부분 배열 길이 찾기
3. 부분 문자열 / 연속 부분 수열의 조건 충족 여부 탐색
문제: 정렬된 배열에서 두 수의 합이 target
인 경우를 찾아라.
public class TwoPointerExample {
public static int[] twoSum(int[] nums, int target) {
int left = 0, right = nums.length - 1;
while (left < right) {
int currentSum = nums[left] + nums[right];
if (currentSum == target) {
return new int[]{nums[left], nums[right]};
} else if (currentSum < target) {
left++; // 합이 작으면 왼쪽 포인터를 오른쪽으로 이동
} else {
right--; // 합이 크면 오른쪽 포인터를 왼쪽으로 이동
}
}
return new int[]{}; // 못 찾으면 빈 배열
}
public static void main(String[] args) {
int[] nums = {1, 2, 4, 7, 11, 15};
int target = 15;
int[] result = twoSum(nums, target);
if (result.length > 0) {
System.out.println(result[0] + ", " + result[1]);
} else {
System.out.println("No pair found");
}
}
}
동작 과정
nums = [1, 2, 4, 7, 11, 15], target = 15
1. left=0, right=5 → sum=1+15=16 > 15 → right--
2. left=0, right=4 → sum=1+11=12 < 15 → left++
3. left=1, right=4 → sum=2+11=13 < 15 → left++
4. left=2, right=4 → sum=4+11=15 == target → return (4, 11)
최종 결과 : 4, 11
문제: 양의 정수 배열에서 연속 부분합이 M
이하인 최대 길이를 구하라.
public class MaxLengthSubarray {
public static int maxLengthSubarray(int[] nums, int M) {
int left = 0;
int currentSum = 0;
int maxLength = 0;
for (int right = 0; right < nums.length; right++) {
currentSum += nums[right];
// 합이 M을 초과하면 왼쪽 포인터 이동
while (currentSum > M) {
currentSum -= nums[left];
left++;
}
maxLength = Math.max(maxLength, right - left + 1);
}
return maxLength;
}
public static void main(String[] args) {
int[] nums = {2, 1, 3, 2, 4};
int M = 6;
int result = maxLengthSubarray(nums, M);
System.out.println("최대 길이: " + result); // 출력: 3
}
}
동작 과정
nums = [2, 1, 3, 2, 4], M = 6
1. right=0 → sum=2 ≤ 6 → maxLength=1
2. right=1 → sum=3 ≤ 6 → maxLength=2
3. right=2 → sum=6 ≤ 6 → maxLength=3
4. right=3 → sum=8 > 6 → left++ → sum=6 → maxLength=3
5. right=4 → sum=10 > 6 → left++ → sum=8 >6 → left++ → sum=7>6 → left++ → sum=4
최종 결과 : 3
이번에 투포인터를 정리하면서 완전 탐색으로는 O(N²)
인 문제를 O(N)
으로 최적화할 수 있다는 점이 인상 깊었다. 특히 배열의 합이나 문자열의 연속 구간 문제에서 불필요한 반복을 줄일 수 있어 효율적이다. 다만, 포인터 이동 조건을 잘못 설계하면 무한 루프에 빠질 수 있으므로 이동 규칙을 명확히 정의하는 것이 중요하다고 느꼈다. 앞으로 연속 부분합, 슬라이딩 윈도우, 문자열 처리 문제를 만날 때 우선 투포인터를 떠올리면 큰 도움이 될 것 같다.