이번 글에서 다룰 문제는 배열을 뒤집는 함수를 작성하되, 배열 안에 포함된 특수 문자의 인덱스는 그대로 유지해야 하는 상황입니다.
예를 들어 특수 문자가 '#'이라고 가정했을 때, 다음과 같은 배열이 주어지면,
[18,-4,'#',0,8,'#',5]
결과는 아래와 같아야 합니다.
[5, 8, "#", 0, -4, "#", 18]
숫자들은 뒤집히지만, 특수 문자 '#'은 자신이 있던 인덱스에 그대로 머물러 있습니다. 그럼 코드를 작성해 보겠습니다.
접근 방법: 두 포인터(Two-Pointer) 기법
이 문제는 두 포인터(two-pointer) 방식으로 해결할 수 있습니다. 하나의 포인터(start)는 배열의 가장 왼쪽 끝을, 다른 포인터(end)는 가장 오른쪽 끝을 각각 가리키도록 초기화합니다.
탐색 과정에서 어떤 인덱스에서 특수 문자를 발견하면 해당 인덱스는 건너뛰고 계속 진행합니다. 그리고 양쪽 모두 일반 요소(특수 문자가 아닌 값)인 인덱스 쌍을 찾으면 두 값을 서로 교환(swap)합니다. 이 과정을 start 포인터가 end 포인터보다 작은 동안 반복하면 됩니다.
코드 예제
const arr = [18,-4,'#',0,8,'#',5];
const reverseArray = (arr, special) => {
let start = 0, end = arr.length - 1, temp;
while(start < end){
if(arr[start] === special){
start++;
continue;
};
if(arr[end] === special){
end--;
continue;
};
temp = arr[start];
arr[start] = arr[end];
arr[end] = temp;
start++;
end--;
};
};
reverseArray(arr, '#');
console.log(arr);실행 결과
콘솔 출력 결과는 다음과 같습니다.
[
5, 8, '#', 0, -4, '#', 18
]동작 원리 정리
이 알고리즘의 흐름을 단계별로 살펴보면 다음과 같습니다.
1. start는 배열의 첫 번째 인덱스, end는 마지막 인덱스를 가리킵니다.
2. start 위치의 값이 특수 문자라면 start를 오른쪽으로 한 칸 이동하고 교환 없이 넘어갑니다.
3. end 위치의 값이 특수 문자라면 end를 왼쪽으로 한 칸 이동하고 넘어갑니다.
4. 두 위치 모두 일반 숫자라면 서로 값을 교환한 뒤, start는 증가시키고 end는 감소시킵니다.
5. start < end 조건이 더 이상 참이 아니면 반복을 종료합니다.
이 방식은 각 요소를 한 번씩만 확인하면 되므로 시간 복잡도는 O(n)이며, 추가적인 배열을 생성하지 않고 원본 배열을 제자리(in-place)에서 수정하기 때문에 공간 복잡도도 O(1)로 매우 효율적입니다. 특수 문자가 여러 개 존재하거나 배열의 크기가 클 때에도 안정적으로 동작하는 실용적인 해결책입니다.