[LeetCode] #105. Construct Binary Tree from Preorder and Inorder Traversal
전위 순회, 중위 순회 값을 바탕으로 이진 트리를 구해봅니다.
문제
#105. Construct Binary Tree from Preorder and Inorder Traversal
Given preorder and inorder traversal of a tree, construct the binary tree.
Note: You may assume that duplicates do not exist in the tree.
Example 1.
Input: preorder = [3, 9, 20, 15, 7], inorder = [9, 3, 15, 20, 7]
Output: [3, 9, 20, null, null, 15, 7]
해법
주어진 전위 순회(Preorder), 중위 순회(Inorder)의 특징을 이용하여 해결합니다.
전위 순회 배열을 PRE, 중위 순회 배열을 IN이라고 하겠습니다.
전위 순회의 첫 요소 *PRE[0]*는 전위 순회 특징에 따라 루트값 입니다.
그리고 *PRE[0]*의 값이 중위 순회 배열 IN중 *IN[7]*과 일치한다고 가정하겠습니다.
이때, 다음 두가지를 알 수 있습니다.
- *PRE[0]*은 루트노드이다.
- *IN[7]*을 기준으로 좌측 요소들(IN[0] ~ IN[6])은 중위 순회 특징에 따라 왼쪽 노드에 존재하며,
우측 요소들(IN[8] ~ IN[max])은 중위 순회 특징에 따라 오른쪽 노드에 존재한다.
재귀를 통해 이를 반복하면 트리를 구할 수 있습니다.
나의 해법
성능
- 시간 복잡도 :
O(n)
- 공간 복잡도 :
O(n!)
해법
재귀 호출을 통한 해법으로, 중위 순회값 inorder
를 조정해 가며 트리를 구한다.
genTree()
함수를 통해 전위 순회 배열preorder
, 중위 순회 배열inorder
값과 루트노드를 확인 할 수 있는 전위 순회의 인덱스preorderIndex
를 받는다.preorderIndex
가 전위 순회 배열preorder
의 사이즈를 넘어가면 null을 반환한다.- 루트노드의 값이 될
val
을 구한다. - 루트노드의 값인
val
이 중위 순회 배열에서 어느 인덱스에 존재하는지 구하여inorderIndex
에 할당한다. - 루트노드의 값
val
이 현재 중위 순회 배열에 존재하지 않다면 (7) 혹은 (8)을 통한 다른쪽(왼쪽 혹은 오른쪽)에서 처리중이므로preorderIndex
값을 증가시킨 결과를 반환한다.
(이는 루트노드의 값이 중위 순회 배열에 값이 존재 할 때 까지 진행된다.) - 루트노드
node
를 할당한다. - 위에서 구한
inorderIndex
를 기준으로 중위 순회 배열의 왼쪽 배열들을 바탕으로 재귀 호출을 하고, 얻은 결과를 현재 루트노드node
의 왼쪽 자식노드로 할당한다. - 위에서 구한
inorderIndex
를 기준으로 중위 순회 배열의 오른쪽 배열들을 바탕으로 재귀 호출을 하고, 얻은 결과를 현재 루트노드node
의 오른쪽 자식노드로 할당한다. - 현재까지 구한 노드를 반환한다.
Java 코드
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
public class ConstructBinaryTreeFromPreorderAndInorderTraversal {
public TreeNode buildTree(int[] preorder, int[] inorder) {
return this.genTree(preorder, inorder, 0);
}
private TreeNode genTree(int[] preorder, int[] inorder, int preorderIndex) {
if (preorderIndex >= preorder.length) return null;
int val = preorder[preorderIndex];
Integer inorderIndex = foundIndex(inorder, val);
if (inorderIndex == null) {
return this.genTree(preorder, inorder, preorderIndex + 1);
}
TreeNode node = new TreeNode(val);
int[] leftInorder = Arrays.copyOfRange(inorder, 0, inorderIndex);
TreeNode leftNode = this.genTree(preorder, leftInorder, preorderIndex + 1);
if (leftNode != null) node.left = leftNode;
int[] rightInorder = Arrays.copyOfRange(inorder, inorderIndex + 1, inorder.length);
TreeNode rightNode = this.genTree(preorder, rightInorder, preorderIndex + 1);
if (rightNode != null) node.right = rightNode;
return node;
}
private Integer foundIndex(int[] arr, int val) {
for (int i = 0; i < arr.length; i++) {
if (arr[i] == val) {
return i;
}
}
return null;
}
}
최적 해법
성능
- 시간 복잡도 :
O(n)
- 공간 복잡도 :
O(n)
해법
위에서 알아보았던 해법과 전반적인 흐름은 동일하지만 몇가지 최적화가 이루어 졌다.