문제 소개
다음과 같은 2차원 배열이 있다고 가정해 보겠습니다.
const arr = [
[1, 2, 3, 4],
[12,13,14,5],
[11,16,15,6],
[10,9, 8, 7]
];
입력으로 주어지는 배열은 반드시 정사각형 행렬(square matrix)이라고 가정합니다.
이 배열을 입력받아, 바깥쪽부터 시작해 나선(spiral) 모양으로 안쪽으로 감아 들어가며 요소를 차례대로 꺼내고, 그 값들로 새로운 1차원 배열을 구성하는 자바스크립트 함수를 작성해야 합니다. 마치 행렬의 외곽을 따라 이동하며 흔적을 남기는 달팽이의 경로처럼 말입니다.
따라서 위 배열에 대한 기대 출력은 다음과 같습니다.
const output = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16];
접근 방법: 재귀로 해결하기
이 문제는 재귀(recursion)를 활용하면 간결하고 우아하게 해결할 수 있습니다. 알고리즘의 핵심 흐름은 다음과 같습니다.
- 행렬의 첫 번째 행을 잘라내어 결과 배열 앞부분에 배치합니다.
- 남은 행렬을 반시계 방향으로 90도 회전합니다.
- 회전된 행렬에 대해 동일한 과정을 재귀적으로 반복합니다.
- 행이 하나만 남으면 그 행이 곧 최종 반환값이 되며 재귀가 종료됩니다.
여기서 행렬 회전은 전치(transpose) 후 뒤집기(reverse)로 구현합니다. 각 열을 추출해 새로운 행으로 만든 뒤 순서를 거꾸로 배치하면 원하는 회전이 완성됩니다.
구현 코드
const arr = [
[1, 2, 3, 4],
[12,13,14,5],
[11,16,15,6],
[10,9, 8, 7]
];
const spiralForm = arr => {
return arr.length > 1 ?
arr.splice(0, 1)[0]
.concat(spiralForm(
arr[0].map((c, i) => {
return arr.map(r => r[i]);
})
.reverse()
)) :
arr[0];
};
console.log(spiralForm(arr));
실행 결과
콘솔에는 다음과 같이 출력됩니다.
[
1, 2, 3, 4, 5, 6,
7, 8, 9, 10, 11, 12,
13, 14, 15, 16
]
코드 동작 원리 살펴보기
1단계: 첫 행 잘라내기
arr.splice(0, 1)[0]는 배열의 첫 번째 행을 제거하면서 동시에 그 값을 반환합니다. 처음 실행 시 [1, 2, 3, 4]가 결과 배열의 시작점이 됩니다.
2단계: 행렬 회전
arr[0].map((c, i) => arr.map(r => r[i]))는 남은 행렬의 각 열을 추출하여 전치 행렬을 만듭니다. 여기에 .reverse()를 적용하면 반시계 방향 90도 회전이 완성됩니다.
3단계: 재귀 반복
회전된 행렬에 대해 spiralForm이 다시 호출되면서 같은 과정이 반복되고, 결국 모든 요소가 나선 순서대로 하나의 배열에 연결됩니다.
이처럼 재귀를 활용하면 복잡한 인덱스 계산 없이도 나선형 순회 문제를 짧고 직관적인 코드로 해결할 수 있습니다. 다만 splice()는 원본 배열을 직접 변경(mutate)하므로, 원본 데이터를 보존해야 하는 상황이라면 처리 전에 배열을 먼저 복사해서 사용하는 것이 안전합니다.