아직 내가 구현까지 하기엔 조금 어려운 레벨이긴 한 것 같지만
강의를 듣고 머리 속에서 희미해지기 전에 정리해보고자 한다..!
먼저 자료구조 중 트리, 그 중에서도 '이진 트리'에 대해 가볍게 알아보자.
🌲 트리 용어 설명
- 깊이: 루트 노드에서 해당 노드까지 도달하는데 사용하는 간선의 개수 (깊이-간선)
- 높이: 루트 노드에서 해당 노드까지 도달하는데 지나간 노드의 개수 (높이-노드)
- 내부 노드: 자식이 있는 노드
- 외부 노드(=단말 노드, leaf): 자식이 없는 노드 (트리의 맨 끝 라인)
- 노드의 차수: 노드의 자식 수
- 트리의 차수: 해당 트리에 있는 모든 노드의 차수 중 최댓값
🌲 이진 트리: 트리의 자식 수가 2 이하(0, 1, 2)인 트리
- 높이가 N인 이진 트리의 최대 노드 개수는 2^N-1개 이다.
정 이진 트리
- 모든 노드의 차수가 0 또는 2인 이진 트리
포화 이진 트리
- 정 이진 트리에서 모든 단말 노드의 깊이가 같은 이진 트리 (완벽한 삼각형 모양)
- 높이가 H인 포화 이진 트리의 노드 개수는 2^H-1개 이다.
- 깊이가 D인 포화 이진 트리의 단말 노드(leaf) 개수는 2^D개 이다.
-> 인덱스 트리(indexed tree)에서 사용완전 이진 트리
- 노드가 왼쪽에 몰려있고 마지막 레벨을 제외하면 포화 이진 트리의 구조를 가진 이진 트리
-> 힙(heap)에서 사용🌲 이진 트리의 표현
- 일차원 배열을 이용(0번 인덱스는 비워두고, N+1개)
- 배열에 트리 노드를 레벨 순, 왼쪽에서 오른쪽으로 저장한다.
이진 트리에서 부모 노드의 왼쪽 자식은 부모노드*2 (=항상 짝수),
오른쪽 자식은 부모노드*2+1 (=항상 홀수)
자식의 부모 노드는 자식/2
가볍게 라고 한것치곤 꽤나 길어졌지만 ㅎㅎㅎ..
이것까지 기억해야 인덱스 트리에 접근할 수 있기 때문에 정리해보았다!
만약, 어떤 배열의 구간 합을 구하는 문제가 주어진다면 배열의 누적합을 구해 필요한 만큼만 빼 주는 방법으로 간단하게 구할 수 있을 것이며 그 때의 시간복잡도는 O(N)일 것이다.
그런데 중간에 배열의 어떤 값이 바뀐다면, 누적합 방법을 사용할 수 없다.
이럴 때 인덱스 트리를 사용하면 아주 좋다.
시간복잡도는 아래에서 더 자세히 다루겠지만 O(MlogN)이다!
인덱스 트리 또한 배열로 표현하는데, 배열에서는 1~8이 인덱스를 뜻하고, 각 구역의 합이 데이터로 들어가서 [3, 2, 4, 5, 1, 6, 2, 7]의 형태가 된다. (형식상 설명으로 인덱스 0은 뺐다)
트리에는 적혀있지 않지만 1-2는 1구역~2구역의 합인 5, 3-4에는 9, 1-4에는 14... 와 같은 식으로 담겨있다.
(합의 값은 트리 만드는 프로그램의 특성 상 적지 못했다... 좀 기울어져있어도 넘어가도록 하자 프로그램 만지다가 홧병날뻔 ㅂㄷㅂㄷ)
leaf는 무조건 2의 거듭제곱 형태로만 가능하며, 지금은 데이터가 8개라 leaf도 8개지만 만약 9개였다면 16개의 배열을 생성해야 한다.
그렇다면 배열에는 리프 노드에 대한 정보만 담겨 있는건데, 내부 노드까지 구현하려면 어떻게 해야할까?
Top-Down 방식과 Bottom-Up 방식 두 가지가 있다.
다시 한번 말하지만 누적합 중 값이 변경되는 문제에 사용하기 좋은 방법이다! 자신 노드의 자신의 합이 포함된 부모 노드의 값만 변경해주면 되기 때문에 빠르다.
차이점은 두 가지 방식을 살펴본 후 서술하려 한다.
ex) 인덱스 3~7의 부분합을 구하고 싶은 경우
ex) leaf 노드의 3번째 값을 4->2로 변경하고 싶은 경우
S=8이므로 배열에서 A[S+3-1] = A[3] = 4 임을 알 수 있고,
diff는 변경값-원래값 = 2 - 4 = -2이다.
value가 아닌 diff를 이용해야 leaf로 내려가는 과정에서 변경하려는 노드와 관련된 노드들의 값도 변경할 수 있다!
노드가 target 미포함: 연관 없음 -> 무시
노드가 target 포함: 현재 노드에 diff 반영
- 자식이 있을 경우
ex) 인덱스 3~7의 부분합을 구하고 싶은 경우
leftNode = leftNode / 2
로 leftNode 이동leftNode = (leftNode + 1) / 2
로 leftNode 이동rightNode = (rightNode - 1) / 2
로 rightNode 이동rightNode = rightNode / 2
로 rightNode 이동ex) leaf 노드의 3번째 값을 4->2로 변경하고 싶은 경우
오늘 강의를 들으며 많이 벅차고 힘들었는데 3시간 가까이 글을 작성하며 어느 정도 정리된 것 같다. 역시 블로그 포스팅은 시간이 많이 들지만 이해에는 직빵이다... 라는 생각을 하며 간만의 포스팅을 마친다.
다음 포스팅은 시간복잡도, DFS/BFS 에 대해 다루고 싶은데 주말에 쓸 수... 있겠지...? ^^
아무튼 끝!