학습 목표: 루트 트리에서 재귀 또는 스택 순회로 깊이와 누적 정보를 구합니다. 모든 예시는 주어진 매개변수를 처리해 정답을 반환하는 solution 함수로 완성합니다.
문제를 푸는 기준
주어진 트리 표현을 익숙한 구조로 바꿉니다. 부모 배열이라면 자식 목록을 만들고, 무방향 간선이면 부모로 돌아가는 간선을 제외합니다.
핵심 개념
| 개념 | 코딩테스트에서의 역할 |
|---|---|
| 루트 | 부모가 없는 시작 정점입니다. |
| 전위 순회 | 현재 정점을 처리한 뒤 자식으로 내려갑니다. |
| 후위 순회 | 자식을 처리한 뒤 현재 값을 계산합니다. |
대표 문제
조직도의 최대 깊이
parent[i]가 i번 노드의 부모인 배열 parent가 주어집니다. 루트 부모는 -1이고 루트 깊이는 0일 때 최대 깊이를 반환하세요.
함수 시그니처: solution(parent)
제한사항
- 노드 수는 1 이상 200,000 이하입니다.
- 정확히 하나의 루트가 있는 올바른 트리입니다.
입출력 예
| 매개변수 | 반환값 |
|---|---|
[-1,0,0,1,1] | 2 |
[-1] | 0 |
풀이 설계
- parent를 보며 자식 목록과 루트를 찾습니다.
- 스택에 루트와 깊이를 넣습니다.
- 정점을 꺼내 최대 깊이를 갱신하고 자식을 넣습니다.
JavaScript 풀이
function solution(parent) {
const children = Array.from({ length: parent.length }, () => []);
let root = -1;
for (let node = 0; node < parent.length; node += 1) {
if (parent[node] === -1) root = node;
else children[parent[node]].push(node);
}
const stack = [[root, 0]];
let maxDepth = 0;
while (stack.length > 0) {
const [node, depth] = stack.pop();
maxDepth = Math.max(maxDepth, depth);
for (const child of children[node]) stack.push([child, depth + 1]);
}
return maxDepth;
}복잡도
- 시간복잡도:
O(n) - 공간복잡도:
O(n) - 핵심 패턴: 부모 배열을 자식 목록으로 바꾼 뒤 깊이 순회
- 경계 사례: 루트 하나뿐이면 깊이 0 반환
연습 문제
1. 리프 개수
자식이 없는 노드 개수를 반환하세요.
힌트
자식 목록 길이
2. 서브트리 크기
각 노드의 서브트리 크기를 반환하세요.
힌트
후위 순회
3. 공통 조상
두 노드의 가장 가까운 공통 조상을 반환하세요.
힌트
깊이를 맞춘 뒤 부모 이동
자주 하는 실수
- 루트 깊이 정의를 확인하지 않는 실수
- 무방향 트리에서 부모로 되돌아가는 실수
- 노드가 많아도 깊은 재귀만 사용하는 실수
- 후위 계산을 전위 시점에 하는 실수
핵심 정리
- 함수 시그니처와 반환 자료형을 먼저 확정합니다.
- 제한사항으로 가능한 시간복잡도를 판단합니다.
- 예시와 경계 사례를 손으로 추적한 뒤 구현합니다.
- 완성한
solution함수가 입력을 불필요하게 바꾸지 않는지도 확인합니다.
확인 문제
문제 14지선다
서브트리 크기를 계산하기 자연스러운 시점은?
문제 24지선다
대표 문제의 시간복잡도로 알맞은 것은?
문제 34지선다
조직도의 최대 깊이에서 사용한 핵심 패턴은?
문제 44지선다
대표 문제에서 반드시 확인할 경계 사례는?
문제 54지선다
이 과정의 정답 코드가 지켜야 할 계약은?
참고 자료
Last updated on