이진트리 개념을 알고 있어도 빠르게 적용을 자꾸 못하는 것 같아서 이번에도 이진탐색 트리 문제를 풀어보기로 했다.
역시 부족하다는 것을 많이 느꼈다
Unique Binary Search Trees II - LeetCode
n이 주어졌을 때 1~n 까지 유니크한 값들로 이루어진 노드들로 만들 수 있는 BST의 모든 경우를 리턴하라.
( 딱 봐도 경우의 수가 많을 것 같은데 n이 1이상 8 이하로 제한되어있다. )
BST ( Binary Search Tree ) 는 어떤 규칙이 정해져 있다.
어떤 노드의 left subtree는 그 노드보다 작은 값들로 이루어져 있다.
어떤 노드의 right subtree는 그 노드보다 큰 값들로 이루어져 있다.
이 문제는 각 정답 트리들을, root인 TreeNode를 전달함으로서 완성해야한다.
- 이를 위해서는 deepcopy를 해야 하고
- delete 또한 할 수 있어야 한다.
root에 대해서는 각 경우를 미리 해 보고
for(int i=1; i <=n ;i++){
root = new TreeNode(i);
visit[i] = true;
recur(2);
visit[i] = false;
}
재귀적으로 , 하나의 숫자씩 각 위치를 찾아나가기
n개를 가진 tree가 완성되면, 이 tree를 deecopy하여, 새로운 트리를 생성하여 이를 answer로 덧붙이기
backtrack시, 기존에 붙인 노드를 null로 처리하기
public void recur(int cnt){
if( cnt > sn ) {
// deepcopy
ans.add(deepcopy(root);
return;
}
for(int i=1; i <=n ;i++ ){
if(visit[i] == true ) continue;
visit[i] = true;
TreeNode par = findLoc(i,root);
recur(cnt+1);
// delete code
if( par.val > i ) par.left = null;
else par.right = null;
visit[i] = false;
}
}
public TreeNode deepcopy(TreeNode cur){
TreeNode cop = new TreeNode(cur.val);
if(cur.left != null){
cop.left = deepcopy( cur.left );
}
if(cur.right != null){
cop.right = deepcopy(cur.right);
}
return cop;
}
// Location을 찾는 코드
// target을 child로 붙이는 parent 노드를 리턴한다.
TreeNode findLoc(int target, TreeNode par){
while(true){
if(par.val < target){
if(par.right != null){
return findLoc(target,par.right);
}else{
return par;
}
}else{
if(par.left != null){
return findLoc(target, par.left);
}else{
return par;
}
}
}
/**
* 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;
* }
* }
*/
import java.util.*;
class Solution {
public List<TreeNode> ans = new LinkedList<>();
public boolean[] visit;
public int sn;
public TreeNode root;
public List<TreeNode> generateTrees(int n) {
visit = new boolean[n+1]; // 1 ~ n integer
sn = n;
for(int i=1; i <=n ;i++){
root = new TreeNode(i);
visit[i] = true;
recur(2);
visit[i] = false;
}
return ans;
}
public void recur(int cnt){
if( cnt > sn ) {
// deepcopy
ans.add(deepcopy(root));
return;
}
for(int i=1; i <=sn ;i++ ){
if(visit[i] == true ) continue;
visit[i]=true;
TreeNode par = findLoc(i,root);
recur(cnt+1);
// delete code
if( par.val > i ) par.left = null;
else par.right = null;
visit[i] = false;
}
}
// Location을 찾는 코드
TreeNode findLoc(int target, TreeNode par){
while(true){
if(par.val < target){
if(par.right != null){
return findLoc(target,par.right);
}else{
par.right = new TreeNode(target);
return par;
}
}else{
if(par.left != null){
return findLoc(target, par.left);
}else{
par.left = new TreeNode(target);
return par;
}
}
}
}
public TreeNode deepcopy(TreeNode cur){
TreeNode cop = new TreeNode(cur.val);
if(cur.left != null){
cop.left = deepcopy( cur.left );
}
if(cur.right != null){
cop.right = deepcopy(cur.right);
}
return cop;
}
}
일종의, divide and conquer로 문제를 푸는 것 같았다.
- 재귀적으로 호출되는 함수에서는 , 정수 start ~ end 를 사용하여 생성 가능한 모든 Tree의 list를 리턴하도록 한다.
- leaf node인 경우
- start == end → 값을 contain하는 leaf node
- start < end → null인 leaf node
- 리턴되어오는 left, right 각각의 [ 생성 가능한 모든 Tree의 list ] 들로부터, root의 서브트리 조합을 생성할 수 있다.
- 매 번 root를 생성하는 것에 주의한다 . ( 그렇지 않으면, 기존의 것을 대체하게 되어버린다 )
재귀 호출하는 함수에서는, 현재 노드의 left subtree 또는 right subtree를 생성하여 리턴한다.
아래 그림은, n이 3일 때, 그리고 가장 최상위 root node가 1일 때만을 나타내 보았다.
public List<TreeNode> generateTrees(int n){
return genTrees(1,n);
}
public List<TreeNode> genTrees(int start, int end){
List<TreeNode> list = new ArrayList<TreeNode>();
if( start > end ){
list.add(null);
return list;
}
if( start == end ){
list.add( new TreeNode(start));
return list;
}
List<TreeNode> left,right;
for(int i = start ; i<= end; i++){
// left subtree로 올 수 있는 가능한 조합들이 리턴됨
left = genTrees(start, i-1);
// right subtree로 올 수 있는 가능한 조합들이 리턴됨
right = genTrees(i+1, end);
// root가 i 일 때 , 가능한 left, right 서브트리 경우들로부터
// left, right 서브트리 조합을 생성한다.
for(TreeNode lnode : left ){
for(TreeNode rnode : right ){
TreeNode root = new TreeNode(i);
root.left = lnode;
root.right = rnode;
list.add(root);
}
}
}
return list;
}