이 글에서는 최소 공통 조상(LCA: Lowest Common Ancestor) 문제의 정의와 해결 방법을 자세히 살펴보겠습니다.
문제 정의
문제: 최소 공통 조상(Lowest Common Ancestor – LCA)
이진 트리에서 두 노드의 최소 공통 조상을 구하시오. 최소 공통 조상은 두 노드의 조상이면서 두 노드의 거리에서 가장 가까운 노드입니다.
예시로, 아래와 같은 이진 트리가 주어졌을 때:
3 / \ 5 1 / \ / \ 6 2 0 8 / \ 7 4
노드 5와 노드 1의 최소 공통 조상은 3이고, 노드 5와 노드 4의 최소 공통 조상은 5입니다.
입력 형식
- 이진 트리의 루트 노드와 두 개의 노드가 주어집니다.
출력 형식
- 주어진 두 노드의 최소 공통 조상을 출력합니다.
문제 풀이
1. 문제 분석
주어진 이진 트리에서 두 노드의 최소 공통 조상을 찾기 위해서는 다음과 같은 두 가지 조건을 충족해야 합니다:
- 노드 A의 조상인 노드 B가 존재해야 합니다.
- 노드 C의 조상인 노드 B도 존재해야 합니다.
이러한 관계를 성립시키기 위해 이진 트리를 순회하며 각 노드에 대해 부모 노드를 추적할 수 있습니다.
2. 알고리즘 설계
우선, 이진 트리에서 특정 노드를 찾기 위해 DFS(Depth First Search) 또는 BFS(Breadth First Search)와 같은 탐색 기법을 사용할 수 있습니다.
최소 공통 조상을 찾기 위한 구체적인 알고리즘은 다음과 같습니다:
- 루트 노드부터 시작하여 트리를 탐색합니다.
- 양쪽 서브트리에서 주어진 두 노드를 찾습니다.
- 만약 각각의 서브트리에서 한 개의 노드를 찾으면 현재 노드가 최소 공통 조상입니다.
- 모두 찾지 못한 경우, null을 반환합니다.
3. 자바 코드 구현
public class TreeNode { int val; TreeNode left; TreeNode right; TreeNode(int x) { val = x; } } public class LCA { public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) { // 베이스 케이스 if (root == null || root == p || root == q) { return root; } // 왼쪽과 오른쪽 서브트리에서 LCA를 찾습니다. TreeNode left = lowestCommonAncestor(root.left, p, q); TreeNode right = lowestCommonAncestor(root.right, p, q); // 둘 다 찾은 경우, 현재 노드가 LCA입니다. if (left != null && right != null) { return root; } // 한쪽 서브트리에서 찾은 경우 그 결과를 반환합니다. return left != null ? left : right; } }
4. 코드 설명
위의 코드에서 핵심 포인트는 다음과 같습니다:
- 재귀적으로 호출하여 현재 노드가 null인지 또는 찾고자 하는 노드인지 확인합니다.
- 왼쪽 및 오른쪽 서브트리에서 최소 공통 조상을 찾고, 둘 다 찾았다면 현재 노드를 반환합니다.
- 끝으로 한쪽 서브트리에서만 발견한 경우 해당 노드를 반환하며, 그 외에는 null을 반환합니다.
5. 시간 복잡도
이 알고리즘의 시간 복잡도는 O(N)으로, 컬렉션의 각 노드를 한 번씩 방문하기 때문입니다. N은 트리의 노드 수입니다.
6. 공간 복잡도
공간 복잡도는 O(H)로, H는 트리의 높이입니다. 최악의 경우, 이진 트리가 한쪽으로 치우쳐 있을 경우 최대 높이는 N이 될 수 있습니다.
실전 코드 테스트
문제를 실제로 테스트하기 위해 예제 트리를 생성하고 LCA를 구하는 코드를 작성해 보겠습니다.
public class Main { public static void main(String[] args) { // 이진 트리 생성 TreeNode root = new TreeNode(3); TreeNode node5 = new TreeNode(5); TreeNode node1 = new TreeNode(1); TreeNode node6 = new TreeNode(6); TreeNode node2 = new TreeNode(2); TreeNode node0 = new TreeNode(0); TreeNode node8 = new TreeNode(8); TreeNode node7 = new TreeNode(7); TreeNode node4 = new TreeNode(4); root.left = node5; root.right = node1; node5.left = node6; node5.right = node2; node1.left = node0; node1.right = node8; node2.left = node7; node2.right = node4; // LCA를 찾기 위한 객체 생성 LCA lcaFinder = new LCA(); TreeNode lca = lcaFinder.lowestCommonAncestor(root, node5, node1); System.out.println("LCA of 5 and 1: " + lca.val); // 출력: 3 lca = lcaFinder.lowestCommonAncestor(root, node5, node4); System.out.println("LCA of 5 and 4: " + lca.val); // 출력: 5 } }
결론
이번 글에서는 이진 트리에서 최소 공통 조상을 찾는 문제를 다루며, 문제 정의, 알고리즘 설계, Java 코드 구현, 코드 테스트까지의 전 과정에 대해 설명하였습니다. 이 문제는 다양한 상황에서 응용될 수 있으며, 알고리즘 및 데이터 구조의 기초적인 개념을 이해하는 데 큰 도움이 됩니다.
이를 통해 자바로 코딩테스트를 준비하는 데 유용한 알고리즘 패턴을 익힐 수 있으며, 문제 해결 능력을 키우는 데 기여할 것입니다.