PS/Tree

백준 22856번: 트리 순회 (JAVA)

닻과매 2022. 6. 5. 11:37

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

 

22856번: 트리 순회

노드가 $N$개인 이진 트리가 있다. 트리를 중위 순회와 유사하게 순회하려고 한다. 이를 유사 중위 순회라고 하자. 순회의 시작은 트리의 루트이고 순회의 끝은 중위 순회할 때 마지막 노드이다.

www.acmicpc.net

문제

노드가 N개인 이진 트리가 있다. 트리를 중위 순회와 유사하게 순회하려고 한다. 이를 유사 중위 순회라고 하자.

순회의 시작은 트리의 루트이고 순회의 끝은 중위 순회할 때 마지막 노드이다. 이때 루트 노드는 항상 1번 노드이다.

유사 중위 순회는 루트 노드에서 시작하며, 다음과 같이 진행된다.

  1. 현재 위치한 노드의 왼쪽 자식 노드가 존재하고 아직 방문하지 않았다면, 왼쪽 자식 노드로 이동한다.
  2. 그렇지 않고 현재 위치한 노드의 오른쪽 자식 노드가 존재하고 아직 방문하지 않았다면, 오른쪽 자식 노드로 이동한다.
  3. 그렇지 않고 현재 노드가 유사 중위 순회의 끝이라면, 유사 중위 순회를 종료한다.
  4. 그렇지 않고 부모 노드가 존재한다면, 부모 노드로 이동한다.
  5. 유사 중위 순회를 종료할 때까지 1 ~ 4를 반복한다.

위 그림에 있는 트리에서 중위 순회를 한다면 4→2→5→1→6→3→7 순으로 순회를 한다.

따라서, 유사 중위 순회의 끝은 노드 7이 된다.

유사 중위 순회는 위 그림과 같이 루트인 노드 1에서 시작하여 노드 7에서 끝나고 1→2→4→2→5→2→1→3→6→3→7 이와 같은 순서로 순회를 진행한다. 유사 중위 순회를 진행하면서 총 10번 이동하였다.

여기서 이동이라는 것은 하나의 노드에서 다른 노드로 한번 움직이는 것을 의미한다. 예를 들면, 노드 1에서 노드 2로 가는 것을 한번 이동하였다고 한다.

유사 중위 순회를 하면서 이동한 횟수를 구하려고 한다.

입력

첫 번째 줄에 트리를 구성하는 노드의 개수 N이 주어진다.

두 번째 줄부터 N+1번째 줄까지 현재 노드 a, 현재 노드의 왼쪽 자식 노드 b, 현재 노드의 오른쪽 자식 노드 c가 공백으로 구분되어 주어진다. 만약 자식 노드의 번호가 -1인 경우 자식 노드가 없다는 것을 의미한다.

출력

유사 중위 순회를 하면서 이동한 총 횟수를 출력한다.

제한

  •  1≤N≤100,000
  •  1≤a,b≤N
 

 

풀이

문제의 그림을 통해 파악해보도록 한다.

유사 중위 순회의 움직임을 보면, 중위 순회의 맨 마지막 노드를 방문하

는 과정을 제외하고 각각의 노드를 방문하고, 다시 돌아온다. 즉, 한 간선을 2번 지나간다는 것을 알 수 있다. 다만, 중위 순회의 맨 마지막 노드를 방문할 때에는 한 번 간 간선을 다시 돌아오지 않는다.

Tree의 성질 중 하나인, 'N개의 노드를 가진 tree는 N-1개의 간선을 가진다'를 이용하면, 총 간선의 개수는 2*(N-1)이며, 중위 순회의 맨 마지막 노드를 방문하는 간선은 해당 노드의 depth와 같다. 따라서, 2(N-1)-depth하면 끝!

다만, forest인 경우는 어떻게 되나 생각을 해봤는데, 생각해보면 forest에서 중위 탐색을 하진 않으니, 괜찮을 듯하다.

 

코드

import java.io.*;
import java.util.*;

public class Main {
    static boolean[] visited;
    static int[] l, r;
    static int N;

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        N = Integer.parseInt(br.readLine());
        l = new int[N+1];
        r = new int[N+1];
        visited = new boolean[N+1];
        for (int i = 0; i < N; i++) {
            StringTokenizer st = new StringTokenizer(br.readLine());
            int cur = Integer.parseInt(st.nextToken());
            int left = Integer.parseInt(st.nextToken());
            int right = Integer.parseInt(st.nextToken());
            l[cur] = left;
            r[cur] = right;
        }
        System.out.println(2*(N-1)-findEnd(1,0));
    }
	
    // 마지막 노드의 depth 구하기
    static int findEnd(int cur, int depth) {
        if (r[cur] != -1) return findEnd(r[cur], depth+1);
        return depth;
    }
}