본문 바로가기

코딩테스트

[Python] 트리

# 9-1 트리 알아보기

트리란?

  • 노드와 에지로 연결된 그래프의 특수한 형태
  • 순환 구조(cycle)를 지니고 있지 않고, 1개의 루트 노드가 존재한다.
  • 루트 노드를 제외한 노드는 단 1개의 부모 노드를 갖는다.
  • 트리의 부분 트리(subtree) 역시 트리의 모든 특징을 따른다.

트리의 핵심 이론(구성 요소)

  • 노드 : 데이터의 index와 value를 표현하는 요소
  • 에지 : 노드와 노드의 연결 관계를 나타내는 선
  • 루트 노드 : 트리에서 가장 상위에 존재하는 노드
  • 부모 노드 : 두 노드 사이의 관계에서 상위 노드에 해당하는 노드
  • 자식 노드 : 두 노드 사이의 관계에서 하위 노드에 해당하는 노드
  • 리프 노드 : 트리에서 가장 하위에 존재하는 노드(자식 노드가 없는 노드)
  • 서브 트리 : 전체 트리에 속한 작은 트리

 

https://www.acmicpc.net/problem/11725

import sys
sys.setrecursionlimit(10**6)
input = sys.stdin.readline

N = int(input())
visited = [False]*(N+1)
tree = [[] for _ in range(N+1)]
answer= [0]*(N+1)

for _ in range(1, N):
    n1, n2 = map(int, input().split())
    tree[n1].append(n2)
    tree[n2].append(n1)

# DFS 탐색 함수
def DFS(number):
    visited[number] = True
    for i in tree[number]:
        if not visited[i]:
            answer[i]=number # DFS를 수행하면서 부모 노드를 정답 리스트에 저장
            DFS(i)

DFS(1) # 부모 노드부터 DFS 시작

for i in range(2, N+1):
    print(answer[i])
  • 인접 리스트로 트리 데이터를 구현한다.
  • DFS 탐색을 수행한다. 수행할 때 부모 노드의 값을 정답 리스트에 저장한다.
  • 정답 리스트의 2번 인덱스 부터 값을 차례대로 출력한다.(answer[1]부터)

 

https://www.acmicpc.net/problem/1068

import sys
sys.setrecursionlimit(10**6)
input = sys.stdin.readline

N = int(input())
visited = [False]*(N)
tree = [[] for _ in range(N+1)]
answer= 0
p = list(map(int, input().split()))

for i in range(N):
    if p[i]!=-1:
        tree[i].append(p[i])
        tree[p[i]].append(i)
    else:
        root = i

# DFS 탐색 함수
def DFS(number):
    global answer
    visited[number]=True
    cNode = 0
    for i in tree[number]:
        if not visited[i] and i!=deleteNode: # 삭제 노드일 때도 탐색 중지
            cNode+=1
            DFS(i)
    if cNode==0:
        answer +=1 # 자식 노드 수가 0개일 때 리프 노드로 간주하고 정답 값 증가

deleteNode = int(input())
if deleteNode==root:
    print(0)
else:
    DFS(root)
    print(answer)
  • 인접 리스트로 트리 데이터 구현하기
  • DFS 또는 BFS 탐색을 수행하면서 리프 노드의 개수를 센다. 단, 제거 대상 노드(deleteNode)를 만났을 때는 그 아래 자식 노드들과 관련된 탐색은 중지한다.(리프 노드제거)

 


# 9-2. 트리아

트라이(trie)란?

  • 문자열 검색을 빠르게 실행할 수 있도록 설계한 트리 형태의 자료구조

트라이의 핵심 이론

  • 일반적으로 단어들을 사전의 형태로 생성한 후 트리의 부모 자식 노드 관계를 이용해 검색을 수행한다.
  • N진 트리 : 문자 종류의 개수에 따라 N이 결정된다. 예를 들어 알파벳은 26개의 문자로 이뤄져 있으므로 26진 트리로 결정된다.
  • 루트 노드는 항상 빈 문자열을 뜻하는 공백 상태를 유지한다.

apple, air, apply 문자열 삽입

 

https://www.acmicpc.net/problem/14425

from sys import stdin
from typing import Any
input = stdin.readline

class Node(object):
    def __init__(self, isEnd):
        self.isEnd = isEnd
        self.childNode = {}

class Trie(object):
    def __init__(self):
        self.parent = Node(None)

    # 문자 삽입
    def insert(self, string):
        nowNode = self.parent
        temp_length = 0
        for char in string:
            # 자식 Node들 미완성된 문자열이면 새로 생성
            if char not in nowNode.childNode:
                nowNode.childNode[char] = Node(char)
            # 자식 노드로 이동
            nowNode = nowNode.childNode[char]
            temp_length += 1
            if temp_length ==len(string):
                nowNode.isEnd = True

    # 문자열이 존재하는지 탐색
    def search(self, string):
        nowNode = self.parent
        temp_length = 0
        for char in string:
            if char in nowNode.childNode:
                nowNode = nowNode.childNode[char]
                temp_length += 1
                if temp_length == len(string) and nowNode.isEnd==True:
                    return True
            else:
                return False
        return False
    
N, M = map(int, input().split())
myTrie = Trie() # 트리 생성

for _ in range(N):
    word = input().strip()
    myTrie.insert(word) # 단어 삽입

result = 0

for _ in range(M):
    word = input().strip()
    if myTrie.search(word):  # 단어 찾기
        result += 1

print(result)
  • 트라이 자료구조를 생성한다. 현재 문자열을 가리키는 위치의 노드가 공백 상태라면 신규 노드를 생성하고, 아니라면 이동한다. 문자열의 마지막에 도달하면 리프 노드라고 표시한다(isEnd == True)
  • 트라이 자료구조 검색으로 집합 S에 포함된 문자열을 센다. 부모-자식 관계 구조를 이용해 대상 문자열을 검색했을 때 문자열이 끝날 때까지 공백 상태가 없고, 현재 문자의 마지막 노드가 트라이의 리프 노드라면 집합 S에 포함된 문자열로 센다.
  • 하지만 시간 초과 난다...
import sys
input = sys.stdin.readline

n, m = map(int, input().split())
textlist = set([input() for _ in range(n)]) # set 형태로 집합 S 문자열 저장
# print(textlist)
# {'codeplus\n', 'baekjoononlinejudge\n', 'sundaycoding\n', 'startlink\n', 'codingsh\n'}
count = 0

for _ in range(m): # set 형태의 집합 문자열에서 검사 문자열이 있는지 바로 확인
    subtext = input()
    if subtext in textlist:
        count += 1

print(count)
  • set 자료구조를 이용해 간단하게 풀 수 있다.

 


# 9-3. 이진 트리

이진 트리(binary tree)란?

  • 각 노드의 자식 노드(차수)의 개수가 2 이하로 구성돼 있는 트리. 가장 많이 사용되는 형태이다.

이진 트리의 핵심 이론

  • 이진 트리의 종류
    • 편향 이진 트리 : 노드들이 한쪽으로 편향돼 생성된 이진 트리
    • 포화 이진 트리 : 트리의 높이가 모두 일정하며 리프 노드가 꽉찬 이진 트리
    • 완전 이진 트리 : 마지막 레벨을 제외하고 완전하게 노드들이 채워져 있고, 마지막 레밸을 왼쪽부터 채워진 트리
    • 일반적으로 완전 이진 트리의 형태로 데이터를 담는다.
  • 이진 트리의 순차 표현
    • 이진 트리는 1차원 리스트의 형태로 표현할 수 있다. 왼쪽부터 차례대로 노드에 인덱스(1부터)를 붙이고 해당하는 노드의 value를 저장한다. 빈 노드도 리스트는 생성한다.
  • 트리의 노드와 리스트의 인덱스 사이 상관 관계(제약 조건)
    • 루트 노드 : index=1
    • 부모 노드 : index = index/2 (현재 노드가 루트 노드가 아닌경우)
    • 왼쪽 자식 노드 : index = index*2 (index*2 <=N)
    • 오른쪽 자식 노드 : index = index*2 + 1 (index*2 + 1 <=N)
    • 인덱스 연산 방식은 세그먼트 트리 혹은 LCA 알고리즘에서 기본적으로 사용된다.

https://www.acmicpc.net/problem/1991

import sys
input = sys.stdin.readline

N = int(input())
tree = {} # 딕셔너리 형태로 선언

for _ in range(N):
    root, left, right = input().split()
    tree[root] = [left, right]

def preOrder(now):
    if now == '.':
        return
    print(now, end = '') # 1. 현재 노드
    preOrder(tree[now][0]) # 2. 왼쪽 탐색
    preOrder(tree[now][1]) # 3. 오른쪽 탐색

def inOrder(now):
    if now == '.':
        return 
    inOrder(tree[now][0]) # 1. 왼쪽 탐색
    print(now, end ='') # 2. 현재 노드
    inOrder(tree[now][1]) # 3. 오른쪽 탐색

def postOrder(now):
    if now == '.':
        return
    postOrder(tree[now][0]) # 1. 왼쪽 탐색
    postOrder(tree[now][1]) # 2. 오른쪽 탐색
    print(now, end='') # 3. 현재 노드

preOrder('A')
print()
inOrder('A')
print()
postOrder('A')
  • 딕셔너리 자료형에 트리 데이터를 저장한다.
  • 전위 순회 함수(preOrder), 중위 순회 함수(inOrder), 후위 순회 함수(postOrder)를 구현해 실행한다.

 


# 9-4. 세그먼트 트리

세그먼트 트리(segement tree)란?

  • 데이터의 구간 합과 데이터 업데이트를 빠르게 수행하기 위해 고안해낸 자료구조의 형태. '인덱스 트리' 라고도 한다.

세그먼트 트리의 핵심 이론

  • 세그먼트 트리의 정류는 구간 합, 최대/최소 구하기로 나눌 수 있고, 구현 단계는 트리 초기화 하기, 질의값 구하기(구간합 or 최대/최소), 데이터 업데이트 하기로 나눌 수 있다.
  • 트리 초기화 하기
    • 리프 노드의 개수가 데이터의 개수(N) 이상이 되도록 트리 리스트를 만든다. 
    • 트리 리스트의 크리 : 2^k >= N을 만족하는 k의 최솟값을 구한 후 2^k * 2를 틀 리스트의 크기로 정의한다.
    • 리프 노드에 원본 데이터를 입력한다. 이때 리프 노드의 시작 위치를 트리 리스트의 인덱스로 구해야 하는데, 2^k를 시작 인덱스로 취한다.
    • 리프 노드를 제외한 나머지 노드의 값을 채운다.(2^k-1부터 1번 쪽으로) 자신의 자식 노드를 이용해 해당 값을 채울 수 있다. 자식 노드의 인덱스는 2N, 2N+1이 된다.
  • 질의값 구하기
    • 주어진 질의 인덱스를 세그먼트 트리의 리프 노드에 해당하는 인덱스로 변경한다. 기존 샘플을 기준으로 한 인덱스 값과 세그먼트 트리 리스트에서의 인덱스 값이 다르기 때문에 인덱스를 변경해야 한다.
    • 세그먼트 트리 index = 주어진 질의 index + 2^k - 1
    • 질의에서 시작 인덱스와 종료 인덱스에 관래 부모 노드로 이동하면서 주어진 질의에 해당하는 값을 구한다.
      1. start_index % 2 ==1일 때 해당 노드 선택
      2. end_index % 2 ==1일때 해당 노드 선택
      3. start_index depth 변경 : start_index = (start_index + 1)/2 연산 실행
      4. end_index depth 변경 : end_index  = (endx_index -1)/2 연산 실행
      5. 위 과정을 반복하다 end_index < start_index가 되면 종료한다.
    • 구간 합은 선택된 노드들을 모두 더하고, 최댓값/최솟값은 선택된 노드들 중 MAX/MIN값을 선택해 출력한다.
  • 데이터 업데이트 하기
    • 자신의 부모 노드로 이동하면서 업데이트 한다.
    • 구간 합에서는 원래 데이터와 변경 데이터의 차이만큼 부모 노드로 올라가면서 변경한다.
    • 최댓값/최솟값 찾기에서는 변경 데이터와 자신과 같은 부모를 지니고 있는 다른 자식 노드와 비교해 더 큰 값/작은 값으로 업데이트 한다. 업데이트가 일어나지 않으면 종료한다.

 

https://www.acmicpc.net/problem/2042

  • 데이터의 변경이 많이 일어나므로 합 배열 자료구조가 아닌 트리 자료구조를 이용한다.
import sys
input = sys.stdin.readline
# 수의 개수, 변경이 일어나는 횟수, 구간 합을 구하는 횟수
N, M, K = map(int, input().split())
treeHeight = 0
length = N

while length!=0: # 트리 높이 구하기 
    length//=2
    treeHeight += 1

treeSize = pow(2, treeHeight+1)
leftNodeStartIndex = treeSize // 2 -1 # 리프 노드 시작 인덱스
tree = [0]*(treeSize+1)

# 데이터를 리프 노드에 저장
for i in range(leftNodeStartIndex+1, leftNodeStartIndex+N+1):
    tree[i] = int(input())

# 인덱스 트리 생성 함수
def setTree(i):
    while i!=1:
        tree[i//2]+=tree[i]
        i-=1

setTree(treeSize-1) # 초기 트리 생성

# 값 변경 함수
def changeVal(index, value):
    diff = value - tree[index]
    while index > 0:
        tree[index] = tree[index]+diff
        index = index//2

# 구간 합 개선 함수
def getSum(s, e):
    partSum = 0
    while s<= e:
        if s%2 == 1:
            partSum += tree[s]
            s+=1
        if e%2 == 0:
            partSum += tree[e]
            e-=1
        s = s//2
        e = e//2
    return partSum

for _ in range(M+K):
    question, s, e = map(int, input().split())
    if question==1:
        changeVal(leftNodeStartIndex+s, e)
    elif question==2:
        s = s+leftNodeStartIndex
        e = e+leftNodeStartIndex
        print(getSum(s, e))
  • 1차원 리스트를 이용해 트리의 값을 초기화한다. 트리 리스트 크기 N=5 이므로 2^k >=N을 만족하는 k의 값은 3이고, 리스트의 크기는 2^3*2 = 16이 된다. 시작 인덱스는 2^3=start_index=8이다.
  • 질의값 연산 함수와 데이터 업데이트 함수를 수행하고, 질의와 관련된 결괏값을 출력한다.

 

https://www.acmicpc.net/problem/10868

import sys
input = sys.stdin.readline
# 수의 개수, 최솟값을 구하는 횟수
N, M = map(int, input().split())
treeHeight = 0
length = N

while length!=0:
    length//=2
    treeHeight+=1

treeSize = pow(2, treeHeight+1)
leftNodeStartIndex = treeSize // 2 -1 # 리프 노드 시작 인덱스
tree = [sys.maxsize]*(treeSize+1)

# 데이터를 리프 노드에 저장
for i in range(leftNodeStartIndex+1, leftNodeStartIndex+N+1):
    tree[i] = int(input())

# 인덱스 트리 생성 함수
def setTree(i):
    while i!=1:
        if tree[i//2]>tree[i]:
            tree[i//2]=tree[i]
        i-=1

setTree(treeSize-1)

#최솟값 계산 함수
def getMin(s,e):
    Min = sys.maxsize
    while s<=e:
        if s%2==1:
            Min=min(Min, tree[s])
            s+=1

        if e%2==0:
            Min = min(Min, tree[e])
            e-=1
        s=s//2
        e=e//2
    return Min

for _ in range(M):
    s, e = map(int, input().split())
    s = s+leftNodeStartIndex
    e = e+leftNodeStartIndex
    print(getMin(s, e))
  • 1차원 리스트로 트리의 값을 최솟값 기준으로 초기화한다. 트리 리스트 크기가 N=10이므로 2^k>=N을 만족하는 k=4이고, 리스트의 크기는 2^4*2=32가 된다. 시작 index=start_index=2^4=16이다.
  • 질의값 연산 함수를 수행하고, 결괏값을 출력한다.
    • 질의가 (1,10)인 경우 Tree[1]값 출력(5), 질의가 (3,5)인 경우 tree[9], tree[20] 중 최솟값 출력 등

 

https://www.acmicpc.net/problem/11505

import sys
input = sys.stdin.readline
# 수의 개수, 변경이 일어나는 횟수, 구간 곱을 구하는 횟수
N, M, K = map(int, input().split())
treeHeight = 0
length = N
MOD = 1000000007

while length!=0: # 트리 높이 구하기
    length//=2
    treeHeight+=1

treeSize = pow(2, treeHeight+1)
leftNodeStartIndex = treeSize // 2 -1 # 리프 노드 시작 인덱스
tree = [1]*(treeSize+1) # 곱하기 이므로 초기값ㅇ은 1 설정

# 데이터를 리프 노드에 저장
for i in range(leftNodeStartIndex+1, leftNodeStartIndex+N+1):
    tree[i] = int(input())

# 인덱스 트리 생성 함수
def setTree(i):
    while i!=1:
        tree[i//2]=tree[i//2]*tree[i]%MOD
        i-=1

setTree(treeSize-1)

# 값 변경 함수
def changeVal(index, value):
    tree[index] = value
    while index>1:
        index=index//2
        tree[index]=tree[index*2]%MOD*tree[index*2+1]%MOD

# 구간 곱 계산 함수
def getMul(s,e):
    partMul = 1
    while s<=e:
        if s%2==1:
            partMul = partMul*tree[s]%MOD
            s+=1
        if e%2==0:
            partMul=partMul*tree[e]%MOD
            e-=1
        s=s//2
        e=e//2
    return partMul

for _ in range(M+K):
    question, s, e = map(int, input().split())
    if question ==1:
        changeVal(leftNodeStartIndex+s, e)
    elif question==2:
        s = s+leftNodeStartIndex
        e = e+leftNodeStartIndex
        print(getMul(s, e))
  • 1차원리스트로 트리의 값을 초기화 한다. 트리 리스트 크기 N=5, 2^k>=N을 만족하는 k=3이다. 따라서 리스트의 크기는 2^3*2=16이고, 시작 인덱스는 2^3=8이 된다.
  • 곱셈 연산이므로 초깃값을 1로 저장해주고, 부모 노드를 양쪽 자식 노드의 곱으로 표현한다. MOD 연산을 수행해 주어진 범위를 넘지 않도록 한다.
  • 질의값 연산 함수와 데이터 업데이트 함수를 수행하고 결괏값을 출력한다. 이때 연산마다 모두 MOD 연산을 수행한다.
  • (A*B)%C = (A%C)*(B%C)%C 성질을 이용

 

 


# 최소 공통 조상

최소 공통 조상(LCA, Lowest Common Ancestor)이란?

  • 트리 그래프에서 임의의 두 노드를 선택했을 때 두 노드가 각각 자신을 포함해 거슬러 올라가면서 부모 노드를 탐색할 때 처음 공통으로 만나게 되는 부모 노드

최소 공통 조상 핵심 이론

  • 일반적인 최소 공통 조상 구하기
    • 트리의 높이가 크지 않을 때, 루트 노드에서 탐색(DFS/BFS)을 시작해 각 노드의 부모 노드와 깊이를 저장한다.
    • 선택된 두 노드의 깊이가 다른 경우, 더 깊은 노드의 노드를 부모 노드로 1개씩 올려주면서 같은 깊이로 맞춘다. 이때 두 노드가 같으면 해당 노드가 LCA이므로 탐색을 종료한다.
    • 깊이가 같은 상태에서는 동시에 부모 노드로 올라가면서 같은 노드가 될 때까지 반복한다.
    • 이 방식은 트리의 높이가 커질 경우 시간 제약 문제에 직면할 수 있다.
  • 최소 공통 조상 빠르게 구하기
    • 서로의 깊이를 맞춰주거나 같아지는 노드를 찾을 때 기존에 한 단계씩이 아닌 2^k씩 올라가 비교하는 방식.
    • 따라서 기존에 자신의 부모 노드만 저장해 놓던 방식에서 2^k번째 위치의 부모 노드까지 저장해야 한다.
      • 먼저 부모 노드 저장 리스트를 만든다. 
        • 정의 : P[K][N] = N번 노드의 2^K번째 부모의 노드 번호
        • 점화식 : P[K][N] = P[K-1][P[K-1][N]]
        • 리스트에서 K는 트리의 깊이 > 2^K를 만족하는 최댓값이다.
      • 리스트 값을 채운 후 선택된 두 노드의 깊이를 맞춘다.(높이 차가 0이 될 때까지)
      • 최소 공통 조상을 찾는다. 이 작업 역시 2^k 단위로 점프하며 맞춘다.

 

https://www.acmicpc.net/problem/11437

import sys
from collections import deque
input = sys.stdin.readline
print = sys.stdout.write
N = int(input())
tree = [[] for _ in range(N+1)]

for _ in range(0, N-1): # 인접 리스트에 트리 데이터 저장
    s, e = map(int, input().split())
    tree[s].append(e)
    tree[e].append(s)

depth = [0]*(N+1)
parent = [0]*(N+1)
visited = [False]*(N+1)

def BFS(node):
    queue = deque()
    queue.append(node)
    visited[node]=True
    level = 1
    now_size = 1
    count = 0
    while queue:
        now_node = queue.popleft()
        for next in tree[now_node]:
            if not visited[next]:
                visited[next] = True
                queue.append(next)
                parent[next] = now_node # 부모 노드 저장
                depth[next] = level # 노드 depth 저장
        count += 1
        if count == now_size:
            count = 0
            now_size=len(queue)
            level+=1

BFS(1)

def executeLCA(a,b):
    if depth[a]<depth[b]:
        a, b = b, a
    while depth[a]!=depth[b]: # depth 맞추기
        a = parent[a]

    while a!=b: # 공통 조상 찾기
        a = parent[a]
        b = parent[b]
    
    return a

M = int(input())

for _ in range(M):
    a,b = map(int, input().split())
    print(str(executeLCA(a,b)))
    print("\n")
  • 인접 리스트로 트리 데이터를 구현한다.
  • 탐색 알고리즘(BFS)를 이용해 각 노드의 깊이를 구한다.
  • 깊이를 맞추기 위해 더 깊은 노드를 같은 깊이가 될 때까지 부모 노드로 이동한다.
  • 부모 노드로 계속 올라가면서 LCA를 찾는다. 두 노드가 같아질 때의 노드가 LCA가 된다.
  • Python으로 제출하면 아슬아슬하게 시간 초과가...난다. PyPy3으로 제출함.

 

https://www.acmicpc.net/problem/11438

import sys
from collections import deque
input = sys.stdin.readline
print = sys.stdout.write
N = int(input())
tree = [[0] for _ in range(N+1)]

for _ in range(0, N-1): # 인접 리스트에 트리 데이터 저장
    s, e = map(int, input().split())
    tree[s].append(e)
    tree[e].append(s)

depth = [0]*(N+1)
visited = [False]*(N+1)
temp = 1
kmax = 0

while temp<=N: # 최대 가능 depth 구하기
    temp<<=1 # 비트 단위 연산자 입니다..temp=temp*2^1
    kmax+=1

parent = [[0 for j in range(N+1)] for i in range(kmax+1)]

def BFS(node):
    queue = deque()
    queue.append(node)
    visited[node]=True
    level = 1
    now_size = 1
    count = 0
    while queue:
        now_node = queue.popleft()
        for next in tree[now_node]:
            if not visited[next]:
                visited[next] = True
                queue.append(next)
                parent[0][next] = now_node # 부모 노드 저장
                depth[next] = level # 노드 depth 저장
        count += 1
        if count == now_size:
            count = 0
            now_size=len(queue)
            level+=1

BFS(1)

for k in range(1, kmax+1):
    for n in range(1, N+1):
        parent[k][n] = parent[k-1][parent[k-1][n]]

def executeLCA(a,b):
    if depth[a]>depth[b]: # 더 깊은 depth가 b가 되도록
        temp=a
        a=b
        b=temp
    
    for k in range(kmax, -1, -1): # depth 빠르게 맞추기
        if pow(2, k) <= depth[b]-depth[a]: 
            if depth[a]<=depth[parent[k][b]]:
                b = parent[k][b]

    for k in range(kmax, -1, -1): # 조상 빠르게 찾기
        if a==b: break
        if parent[k][a]!=parent[k][b]:
            a = parent[k][a]
            b = parent[k][b]

    LCA = a
    if a!=b:
        LCA = parent[0][LCA]
    return LCA

M = int(input())

for _ in range(M):
    a,b = map(int, input().split())
    print(str(executeLCA(a,b)))
    print("\n")
  • 이전 문제보다 노드의 개수와 질의가 커졌으므로 제곱수 형태를 이용해 LCA를 구한다.
  • 먼저 인접 리스트로 트리 데이터를 구한다.
  • 탐색 알고리즘(BFS)을 이용해 각 노드의 깊이를 구한다.
  • 점화식을 이용해 Parent 리스트(부모 노드 리스트)를 구한다.
    • P[K][N] = P[K-1][P[K-1][N]]
  • 부모 노드로 올라가며 최소 공통 조상을 찾는다. 이때 Parent 리스트를 이용해 2^k만큼 넘어가면서 찾으며, k는 depth의 최댓값에서 1씩 감소한다.

 

 


# 회고

 

어렵..어떻게 1차원 리스트에 저렇게 트리를 저장하고...풀이 공식을 만들 수 있는지 신기하다.

 

개인적으로 이 책 공부하기 전에 python 개념을 좀 완벽하게 하는게 좋지 않을까 생각했다.

갑작스러운 python 내장 함수의 사용이 있따. 이번에는 pow나 비트 연산자. pow도 오랜만에 봐서 뭔가 했다...제곱 함수...

그리고 이런 내장 함수는 잘 사용하다가 python의 다른 편리한 기능들은 안쓴다. 왜지?

'코딩테스트' 카테고리의 다른 글

[Python] 트리 문제 풀이 정리  (0) 2024.05.21
[Python] 그래프 문제 풀이 정리(1)  (0) 2024.05.19
[Python] 그래프(2)  (0) 2024.05.12
[Python] 그래프(1)  (1) 2024.05.10
[Python] 그리디 문제 풀이 정리(2)  (0) 2024.05.07