1. 개념
배열이나 문자열에서 한 쌍의 값이나 조건을 찾을 때 사용
인덱스 또는 위치에 해당하는 포인터 또는 값을 생성하고 특정 조건에 따라 시작, 끝 또는 중간으로 이동합니다.
공간의 복잡성을 최소화하면서 문제를 해결하는 데 매우 효율적입니다.
정렬된 배열에서 사용합니다
2. 특징
시간 복잡도: 대부분 O(n)
공간 복잡도: O(1) (추가 공간이 거의 필요 없음)
주로 정렬된 배열에서 사용됨
두 개의 값을 비교하거나 특정 조건을 찾을 때 효과적
3. 사용하는 경우
중복 값 찾기
특정 합을 가진 쌍 찾기
팰린드롬 확인
배열에서 고유한 값 개수 세기
4. 예제
1) 정렬된 배열에서 합이 0인 쌍 찾기
/**
* 배열에서 합이 0이 되는 두 숫자를 찾는 함수
* @param {number[]} arr - 정렬된 숫자 배열
* @returns {number[]|undefined} - 합이 0이 되는 두 숫자의 배열 또는 undefined
*/
function findZeroSum(arr) {
// 왼쪽 포인터 초기화
let left = 0;
// 오른쪽 포인터 초기화
let right = arr.length - 1;
// 두 포인터가 교차할 때까지 반복
while(left < right) {
// 현재 왼쪽과 오른쪽 포인터가 가리키는 값의 합 계산
let sum = arr[left] + arr[right];
if(sum === 0) {
// 합이 0이면 해당 두 숫자 반환
return [arr[left], arr[right]];
} else if(sum > 0) {
// 합이 0보다 크면 오른쪽 포인터를 왼쪽으로 이동
right--;
} else {
// 합이 0보다 작으면 왼쪽 포인터를 오른쪽으로 이동
left++;
}
}
// 합이 0이 되는 쌍을 찾지 못한 경우 undefined 반환
return undefined;
}
2) 중복 값 제거하기
/**
* 정렬된 배열에서 중복 요소를 제거하고 고유한 요소의 개수를 반환하는 함수
* @param {number[]} arr - 정렬된 숫자 배열
* @returns {number} - 중복이 제거된 후의 배열 길이
*/
function removeDuplicates(arr) {
// 빈 배열인 경우 0 반환
if(arr.length === 0) return 0;
// i는 고유한 요소를 저장할 위치를 가리키는 포인터
let i = 0;
// j는 배열을 순회하면서 새로운 요소를 찾는 포인터
for(let j = 1; j < arr.length; j++) {
// 현재 요소가 이전 고유 요소와 다른 경우
if(arr[i] !== arr[j]) {
// 다음 위치로 이동
i++;
// 새로운 고유 요소를 저장
arr[i] = arr[j];
}
// 같은 요소인 경우 j만 증가하고 넘어감
}
// 고유한 요소의 개수 반환 (i는 0부터 시작했으므로 +1)
return i + 1;
}
// 사용 예시
let arr = [1, 1, 2, 2, 3, 4, 4, 5];
console.log(removeDuplicates(arr)); // 5 출력 (고유한 요소: 1, 2, 3, 4, 5)
3) 세 수의 합이 0인 경우 찾기
/**
* 배열에서 합이 0이 되는 세 숫자의 조합을 모두 찾는 함수
* @param {number[]} arr - 숫자 배열
* @returns {number[][]} - 합이 0이 되는 세 숫자의 조합들을 담은 2차원 배열
*/
function findTripleSum(arr) {
// 배열을 오름차순으로 정렬
arr.sort((a, b) => a - b);
// 결과를 저장할 배열
let result = [];
// 첫 번째 숫자를 고정하고 나머지 두 숫자를 찾기
for(let i = 0; i < arr.length - 2; i++) {
// 중복된 값은 건너뛰어 중복 조합 방지
if(i > 0 && arr[i] === arr[i-1]) continue;
// 두 번째, 세 번째 숫자를 찾기 위한 투 포인터 설정
let left = i + 1;
let right = arr.length - 1;
while(left < right) {
// 세 숫자의 합 계산
let sum = arr[i] + arr[left] + arr[right];
if(sum === 0) {
// 합이 0이면 결과 배열에 추가
result.push([arr[i], arr[left], arr[right]]);
// 중복된 값 건너뛰기 (left)
while(left < right && arr[left] === arr[left+1]) left++;
// 중복된 값 건너뛰기 (right)
while(left < right && arr[right] === arr[right-1]) right--;
// 다음 조합을 찾기 위해 포인터 이동
left++;
right--;
} else if(sum < 0) {
// 합이 0보다 작으면 left를 증가
left++;
} else {
// 합이 0보다 크면 right를 감소
right--;
}
}
}
return result;
}
// 사용 예시
console.log(findTripleSum([-1, 0, 1, 2, -1, -4]));
// [[-1, -1, 2], [-1, 0, 1]] 출력
5. 장점
메모리 사용이 효율적
선형 시간 복잡도로 문제 해결 가능
중첩 루프를 피할 수 있음
6. 주의사항
대부분의 경우 정렬된 데이터에서 더 효과적
포인터의 이동 조건을 정확히 설정해야 함
경계 조건(edge cases) 처리에 주의
기존 알아본 패턴
1. Frequency Counter (빈도수 세기) https://daunje0.tistory.com/190
다음 알아볼 패턴들
Sliding Window https://daunje0.tistory.com/262
Divide and Conquer https://daunje0.tistory.com/263
Dynamic Programming
Greedy Algorithms
Backtracking
'JavaScript > Algorithm' 카테고리의 다른 글
[Algorithm] 알고리즘 기본 패턴 3 - Sliding Window (0) | 2025.01.12 |
---|---|
[프로그래머스] Lv1) 숫자 짝꿍 (0) | 2024.12.22 |
[Algorithm] 알고리즘 기본 패턴 1 - Frequency Counter (빈도수 세기) (0) | 2024.10.31 |
알고리즘 출제빈도순 수정중입니다 (0) | 2024.10.17 |
[Algorithm] 알고리즘의 공간 복잡도(Space Complexity)란? (0) | 2024.10.11 |