『Do it! 알고리즘 코딩 테스트 with JAVA 강의』를 듣고 정리한 글입니다.
구성 요소 | 설명 |
---|---|
노드 | 데이터의 index와 value를 표현하는 요소 |
에지 | 노드와 노드의 연결 관계를 나타내는 선 |
루트 노드 | 트리에서 가장 상위에 존재하는 노드 |
부모 노드 | 두 노드 사이의 관계에서 상위 노드에 해당하는 노드 |
자식 노드 | 두 노드 사이의 관계에서 하위 노드에 해당하는 노드 |
리프 노드 | 트리에서 가장 하위에 존재하는 노드 (자식 노드가 없는 노드) |
서브 트리 | 전체 트리에 속한 작은 노드 |
index/2
)을 통해 접근한다.이진 트리의 종류에는 다음 세 종류가 있다.
코딩 테스트에서 트리 문제가 나오면 그래프의 표현 방식보다 다음 방식으로 데이터를 담는 것이 일반적이다.
만약 노드가 없다면(아직 채워지지 않는다면) 해당 공간은 비워놓는다.
int[] tree = new int[N];
tree[1] = A; // 루프 노드
tree[2] = B; // 루프 노드의 자식 노드 1
tree[3] = C; // 루프 노드의 자식 노드 2
tree[4] = D; // B 노드의 자식 노드 1
tree[5] = E; // B 노드의 자식 노드 2
tree[6] = F; // C 노드의 자식 노드 1
tree[7] = G; // C 노드의 자식 노드 2
아래의 인덱스 연산 방식은 세그먼트 트리나 LCA 알고리즘에서도 기본이 되는 연산이다.
이동 목표 노드 | 인덱스 연산 | 제약 조건 (N=노드 개수) |
---|---|---|
루트 노드 | index = 1 | |
부모 노드 | index = index / 2 | 현재 노드가 루트 노드가 아님 |
왼쪽 자식 노드 | index = index * 2 | index * 2 ≤ N |
오른쪽 자식 노드 | index = index * 2 + 1 | index * 2 + 1 ≤ N |
트리 초기화하기
트리 배열의 크기는 을 만족하는 k의 최솟값을 구한 후, 를 트리 배열의 크기로 정의한다.
// 샘플 데이터 (N=8이므로, k는 3이 된다)
int[] sample = new int {5, 8, 4, 3, 7, 2, 1, 6};
// 트리 배열의 크기 size
int size = 2^3 * 2;
int[] tree = new int[N+1];
int start_index = 8;
tree[start_index++] = node;
채워야 하는 인덱스가 N이라고 가정하면 자신의 자식 노드를 이용해 해당 값을 채울 수 있다.
자식 노드의 인덱스는 2N과 2N+1이다.
// 구간 합 (자식 노드들의 합)
A[N] = A[2N] + A[2N+1];
// 최대 (자식 노드 중 큰 값)
A[N] = max(A[2N], A[2N+1]);
// 최소 (자식 노드 중 작은 값)
A[N] = min(A[2N], A[2N+1]);
💡 이렇게 세그먼트 트리를 구성해 놓으면 그 이후 질의와 관련된 결괏값이나 데이터 업데이트 요구 사항에 관해 좀 더 빠른 시간 복잡도 안에서 해결할 수 있게 된다.
질의값 구하기
기존 샘플을 기준으로 한 인덱스값과 세그먼트 트리 배열에서의 인덱스값이 다르기 때문에 인덱스를 변경해야 한다.
// 인덱스 변경 방법
(세그먼트 트리 index) = (주어진 질의 index) + 2^k - 1
// 해당 노드의 부모가 나타내는 범위가 질의 범위를 넘어가기 때문에,
// 해당 노드를 질의값에 영향을 미치는 독립 노드로 선택하고,
// 해당 노드의 부모 노드는 대상 범위에서 제외한다.
1. start_index % 2 == 1일 때 해당 노드를 선택한다. (두 자식 노드 중 오른쪽일 때)
2. end_index % 2 == 0일 때 해당 노드를 선택한다. (두 자식 노드 중 왼쪽일 때)
// 부모 노드를 대상 범위에서 제거하는 방법 (범위를 부모 노드로 이동)
3. start_index depth 변경: start_index = (start_index + 1)/2 연산을 실행한다.
4. end_index depth 변경: end_index = (end_index - 1)/2 연산을 실행한다.
5. 1~4번 과정을 반복하다가 end_index < start_index가 되면 종료한다.
데이터 업데이트하기
P[K][N] = N번 노드의 $2^k$번째 부모의 노드 번호
P[K][N] = P[K-1][P[k-1][N]]
N의 번째 부모 노드는 N의 번째 부모 노드의 번째 부모 노드라는 의미이다. 배열에서 K는 트리의 깊이 > $2^k$
를 만족하는 최댓값이다. 즉, K=4라고 가정하면 N의 16번째 부모 노드는 N의 여덟 번째 부모 노드의 여덟 번째 부모 노드라는 의미이다.