|
1 | 1 | # 투 포인터 알고리즘(Two Pointer Algorithm) |
2 | 2 |
|
| 3 | + |
3 | 4 | ### 정의 |
4 | | -배열 또는 리스트에서 두 개의 포인터를 사용하여 원하는 조건을 만족하는 구간을 찾거나 값을 계산하는 알고리즘 |
| 5 | +투 포인터 알고리즘(Two Pointer Algorithm)은 배열 또는 리스트에서 두 개의 포인터를 사용하여 원하는 조건을 만족하는 구간을 찾거나 값을 계산하는 알고리즘 |
| 6 | + |
5 | 7 | 대개 배열이나 리스트가 정렬되어 있을 때 사용되며, 배열의 시작과 끝 지점을 가리키는 두 개의 포인터를 이용하여 특정한 조건을 만족하는 부분을 찾음 |
6 | 8 |
|
7 | 9 | --- |
8 | 10 |
|
9 | 11 | ### 동작 방식 |
10 | | -1. 배열이나 리스트가 정렬되어 있을 때, 각각의 포인터를 시작과 끝 지점에 위치시킴 |
11 | | -2. 두 포인터가 가리키는 값을 비교하여 원하는 조건을 만족하는지 확인 |
12 | | -3. 조건을 만족하면 필요한 작업을 수행하고, 조건을 만족하지 않으면 포인터를 이동하여 새로운 구간을 탐색 |
13 | | -4. 모든 조건이 만족할 때까지 위의 과정을 반복 |
14 | 12 |
|
15 | | ---- |
| 13 | +N칸의 1차원 배열이 있을 때, 부분 배열 중 그 원소의 합이 M이 되는 경우의 수를 구하는 것이다. 모든 경우의 수를 다 테스트 해보면 구간 합을 구간 배열로 O(1)만에 구한다고 해도 경우의 수는 O(N^2)이 된다. 따라서 문제를 풀 수 없다. N의 최대 범위가 너무 크기 때문이다. |
| 14 | + |
| 15 | +이 문제에서 각 원소는 자연수이고 M 또한 자연수인데, 이 조건이 성립하면 사용할 수 있는 알고리즘은 다음과 같다. |
| 16 | + |
| 17 | +포인터 2개를 준비한다. 시작과 끝을 알 수 있도록 start, end 라고 한다. |
| 18 | +맨 처음에는 start = end = 0이며, 항상 start<=end을 만족해야 한다. |
| 19 | +2개의 포인터는 현재 부분 배열의 시작과 끝을 가리키는 역할을 한다. |
| 20 | +s=e일 경우 그건 크기가 0인, 아무것도 포함하지 않는 부분 배열을 뜻한다. 다음의 과정을 s < N인 동안 반복한다. |
| 21 | + |
| 22 | +만약 현재 부분합이 M 이상이거나, 이미 e = N이면 s++ |
| 23 | +그렇지 않다면 e++ |
| 24 | +현재 부분합이 M과 같으면 결과 ++ |
| 25 | +쉽게 이해하자면, start와 end 를 무조건 증가시키는 방향으로만 변화시켜가면서 도중에 부분 배열의 합이 정확히 M이 되는 경우를 세는 것이다. |
| 26 | + |
| 27 | +Ex) M = 5인 경우를 살펴보자. |
| 28 | + |
| 29 | + |
| 30 | + |
| 31 | + |
| 32 | + |
| 33 | +초기 상태이며, 빨간색 포인터 : start, 파란색 포인터 : end이다. S : 합 |
| 34 | + |
| 35 | +end가 뒤로 움직일 때는 새로 포함한 원소를 S에 더하고, start가 뒤로 움직일 때는 새로 넘긴 원소를 S에서 빼는 식으로 현재 [start, end)의 합 S를 매번 쉽게 구할 수 있음 |
| 36 | + |
| 37 | + |
| 38 | + |
| 39 | + |
| 40 | +처음에는 이렇게 end만 증가하게 된다. S가 계속 M보다 작기 때문! 마지막엔 S>=M이 되었으므로 아래와 같다. |
| 41 | + |
| 42 | + |
| 43 | + |
| 44 | + |
| 45 | +start를 한 칸 옮겼는데, 동시에 S = 5인 경우를 만났다. 이때 결과를 1 증가시켜 준다. |
| 46 | + |
| 47 | + |
| 48 | + |
| 49 | + |
| 50 | + |
| 51 | + |
| 52 | +이런 식으로 포인터들이 움직이게 된다. 여기서 2번째로 S = 5인 지점을 만났으므로 결과를 1 증가시켜 준다. |
| 53 | + |
| 54 | + |
| 55 | + |
| 56 | +그 직후, start가 1 증가하면서 start = end인 경우가 나온다. |
| 57 | + |
| 58 | + |
| 59 | + |
| 60 | +계속 가다 보면 세 번째로 S = 5인 지점을 만난다. |
| 61 | + |
| 62 | + |
| 63 | + |
| 64 | + |
| 65 | +그 이후 조건에 맞춰 포인터를 증가시키다 보면, end가 배열 끝을 가리키게 되어 더이상 증가할 수 없는 상태가 된다. |
| 66 | + |
| 67 | + |
| 68 | + |
| 69 | +그렇게 되면 그냥 start만 증가시켜 가다가 start 역시 배열 끝에 다다르면 종료해도 되고, 그냥 그 자리에서 루프를 끝내버려도 된다. 이렇게 해서 S = 5인 경우는 3개 발견되었다. |
| 70 | + |
16 | 71 |
|
17 | | -```C++ |
18 | | - |
19 | | -#include <iostream> |
20 | | -#include <vector> |
21 | | -using namespace std; |
22 | | - |
23 | | -// 투 포인터 알고리즘을 사용하여 합이 target이 되는 두 요소의 인덱스를 찾는 함수 |
24 | | -vector<int> twoSum(vector<int>& nums, int target) { |
25 | | - int left = 0; // 왼쪽 포인터 초기화 |
26 | | - int right = nums.size() - 1; // 오른쪽 포인터 초기화 |
27 | | - |
28 | | - while (left < right) { |
29 | | - int sum = nums[left] + nums[right]; // 현재 두 요소의 합 계산 |
30 | | - if (sum == target) { |
31 | | - return {left, right}; // 합이 target과 일치하는 경우 현재 인덱스 반환 |
32 | | - } else if (sum < target) { |
33 | | - left++; // 합이 target보다 작으면 왼쪽 포인터를 오른쪽으로 이동 |
34 | | - } else { |
35 | | - right--; // 합이 target보다 크면 오른쪽 포인터를 왼쪽으로 이동 |
36 | | - } |
37 | | - } |
38 | | - |
39 | | - // 합이 target과 일치하는 두 요소가 없는 경우 빈 벡터 반환 |
40 | | - return {}; |
41 | | -} |
42 | | - |
43 | | -int main() { |
44 | | - vector<int> nums = {2, 7, 11, 15}; |
45 | | - int target = 9; |
46 | | - |
47 | | - vector<int> result = twoSum(nums, target); |
48 | | - |
49 | | - if (result.size() == 2) { |
50 | | - cout << "Indices of the two elements whose sum is equal to target:" << endl; |
51 | | - cout << result[0] << " " << result[1] << endl; |
52 | | - } else { |
53 | | - cout << "No such elements found!" << endl; |
54 | | - } |
55 | | - |
56 | | - return 0; |
57 | | -} |
58 | | - |
59 | | -``` |
60 | 72 |
|
61 | 73 | --- |
62 | 74 |
|
|
0 commit comments