class Solution:
def findMinHeightTrees(self, n: int, edges: List[List[int]]) -> List[int]:
if n <= 1:
return [0]
#양방향 그래프 구성
graph = collections.defaultdict(list)
for i, j in edges:
graph[i].append(j)
graph[j].append(i)
#첫 번째 리프 노드 추가
leaves = []
for i in range(n + 1):
if len(graph[i]) == 1:
leaves.append(i)
#루트 노드만 남을 때까지 반복 제거
while n>2:
n -= len(leaves)
new_leaves = []
for leaf in leaves:
neighbor = graph[leaf].pop()
graph[neighbor].remove(leaf)
if len(graph[neighbor]) == 1:
new_leaves.append(neighbor)
leaves = new_leaves
return leaves
최소 높이를 구성하려면 가장 가운데에 있는 값이 루트여야 한다.
입력값이 [[1, 3], [2, 3], [3, 4], [3, 5], [4, 6], [6, 10], [5, 7], [5, 8], [8, 9]] 와 같을 때 그래프를 구성해 본다.
여기서부터 리프 노드를 제거해 본다.
가장 가운데에 있는, 가장 마지막에 남을 루트가 될 노드는 어떤 노드가 될지 상상해본다.
1, 2, 7, 9, 10인 리프 노드를 한 번 제거했다.
6, 8인 두 번째 리프 노드를 제거했다.
리프 노드를 세 번째 제거했고 결과는 하나가 남았다.
3
이고, 3
을 루트로 했을 때 최소 높이 트리를 구성할 수 있을 것이다. 이제 코드로 이를 구현해 보자.
이 문제에서 그래프는 무방향(Undirected)이므로, 트리의 부모와 자식은 양쪽 노드 모두 번갈아 가능하다.
따라서, 양쪽 모두 graph
라는 이름의 그래프 딕셔너리 변수에 양방향으로 삽입하여 구성한다.
리프 노드를 찾아서 leaves
에 추가한다.
leaves = []
for i in range(n + 1):
if len(graph[i]) == 1:
leaves.append(i)
graph
의 값을 출력해보면 다음과 같다.>>>graph
defaultdict(<class 'list'>, {
1: [3],
3: [1, 2, 4, 5],
2:[3],
4: [3, 6],
5: [3, 7, 8],
6: [4, 10],
10: [6],
7: [5],
8: [5, 9],
9: [8]
})
입력값 [[1, 3], [2, 3], [3, 4], [3, 5], [4, 6], [6, 10], [5, 7], [5, 8], [8, 9]] 를 딕셔너리로 표현한 그래프의 값은 이런 형태가 되고, 이 중에서 값이 1개뿐인 [1, 2, 10, 7, 9]가 첫 번째 리프 노드로 leaves
리스트 변수에 담기게 된다.
leaves
에 담긴 리프 노드들이 동일함을 확인할 수 있다.이제 루트가 남을 때까지 반복해서 계속 제거한다.
n
은 전체 노드의 개수이므로 여기서 leaves
, 즉 리프 노드의 개수만큼 계속 빼나가면서 최종 2개 이하가 남을때까지 반복한다.
마지막에 남은 값이 홀수 개일때는 루트가 최종 1개가 되지만, 짝수 개일때는 2개가 될 수 있다. 따라서 while 반복문은 2개까지는 계속 반복한다.
리프노드는 반복하면서 제거한다.
그래프 딕셔너리에서 pop()
으로 제거하고, 연결된 값도 찾아서 제거한다.
계속 반복하면서 leaves
에 최종적으로 2개 이하의 노드가 남게 되었을 때, 이 노드들이 루트가 되며 최종 결과로 리턴한다.