0
으로 채운다는 의미입니다. 채우는 건 숫자에 영향을 주지 않습니다. 00001
과 1
은 10진수로 변환했을 때 같은 값입니다.자신이 1인데 부모가 0이면 모순
입니다.문제의 대략적인 형태는 금방 파악할 수 있었지만 모호한 조건이 많았습니다. 결과적으로 부모가 0인데 1의 자식을 가지는 모순
이 발생하는 경우만 0, 그렇지 않으면 1을 반환해야 했습니다.
애매했던 부분
자식도 0
이고 부모도 0
이면 조건을 만족하는걸까? 만족합니다.
포화이진트리를 만들 때 반드시 0
을 앞으로 채워야할까? 앞으로만 채워야 합니다.
전체 흐름은 다음과 같습니다.
1. 입력값을 2진수로 만듭니다.
2. 2진수를 포화이진트리로 만듭니다. 2진트리는 반드시 ((2^n) - 1)개의 노드를 가집니다.
조금 더 정확히는 이진트리의 height를 구한 뒤 해당 height의 포화이진트리가 가져야 할 부족한 노드개수만큼을 앞에서부터 0으로 채워줍니다.
3. 포화이진트리에서 부모를 찾아가는 방법을 구합니다.
4. 부모가 0인데 자식이 1인 모순이 존재하는지 파악합니다.
3번을 구현하는 게 가장 어려웠습니다. 트리의 속성을 완벽히 이해하고 값을 구했다기 보다 특정 규칙을 갖는 수열을 구현하는 느낌의 풀이가 되어버렸습니다.
저는 vectorToGoParent[i] = i번째 노드가 부모로 가기 위해 움직여야 하는 값
으로 구했습니다. 즉, i + vectorToGoParent[i] = 부모노드 index
가 형성됐습니다. vectorToGoParent는 재귀적인 성질을 가집니다. 먼저 방향(부호)
을 고려하지 않았을 때 다음과 같은 규칙을 가집니다.
2^height
만큼의 이동이 필요합니다. 방향을 고려하면 우측노드는 부모로 가기 위해 해당 거리만큼 -값을 가져야 합니다. 즉 최종적으로 높이가 3인 포화이진트리는 [1,2,-1,4,1,-2,-1,0,1,2,-1,-4,1,-2-1]
모양의 vectorToGoParent
배열을 가져야 합니다. 저는 이를 재귀함수로 구현했습니다. String으로도 도전해보고 ArrayList로도 도전해봤는데 다 실패하고 결국 아래와 같은 코드가 나왔습니다. 대략 N번째 값 = N-1번째 값 + 부모노드 + N-1번째 값
형태의 재귀코드입니다.
저는 특히 우측노드를 -
처리하는 게 특히 어려웠습니다. 정확히는 이전 재귀에서 다른 값의 부호는 이미 처리되었기 때문에 우측노드의 root
만 -
처리해줘야 합니다. center + (int)Math.pow(2,depth-2)
는 N-1번째 재귀함수의 center값입니다. N-1번째 재귀함수의 center값의 부호만 -
로 바꿔줬습니다. 이때 if(depth > 1)
라는 조건을 걸었는데 높이가 0인 1
을 만들 때 덮어쓰기 되어버리기 때문에 모든 1
의 값의 부호가 반대로 나와버려서 이러한 조건을 붙였습니다.
특별한 코드라기보다는 제가 해당 알고리즘을 효율적으로 구현하지 못해서 생긴 문제 정도로 이해해 주시면 될거 같습니다.
import java.util.*;
public class Main {
public static void main(String[] args) throws Exception {
long[] numbers = new long[] {63,111,95};
int[] answer = new int[numbers.length];
for (int i = 0; i < numbers.length; i++) {
String s = Long.toBinaryString(numbers[i]);
String perfectBinaryTree = makePerfectBinaryTree(s)[1];
int height = Integer.parseInt(makePerfectBinaryTree(s)[0]);
int centerIdx = ((int)Math.pow(2, height)-1)/2;
int[] vectorToGoParent = new int[(int)Math.pow(2, height)-1];
calVectorFromChildToParent(height,vectorToGoParent,centerIdx);
vectorToGoParent[centerIdx] = 0;
if(isCorrect(perfectBinaryTree,vectorToGoParent)) {
answer[i] = 1;
}
}
System.out.println(Arrays.toString(answer));
}
// 포화이진트리 만들기
public static String[] makePerfectBinaryTree(String binaryNumber) {
int length = binaryNumber.length();
int height = findHegihtOfPerfectBinaryTree(length);
StringBuilder padding = new StringBuilder();
for (int i = 0; i < Math.pow(2, height)-1-length; i++) {
padding.append("0");
}
padding.append(binaryNumber);
return new String[]{Integer.toString(height),padding.toString()};
}
// 2진수의 문자열의 길이를 통해 포화이진트리의 높이 구하기.
public static int findHegihtOfPerfectBinaryTree(int length) {
for (int i = 0; i < 100; i++) {
if(Math.pow(2, i)-1 <= length && length < Math.pow(2, i+1)) {
return i+1;
}
}
return -1;
}
// 포화이진트리의 부모노드 인덱스로 가기 위해 이동해야 하는 값
public static void calVectorFromChildToParent(int depth, int[] arr, int center) {
if(depth == 0) return;
calVectorFromChildToParent(depth-1,arr,center - (int)Math.pow(2, depth-2));
arr[center] = (int)Math.pow(2, depth-1);
calVectorFromChildToParent(depth-1,arr,center + (int)Math.pow(2, depth-2));
if(depth > 1) {
arr[center + (int)Math.pow(2, depth-2)] *=-1;
}
}
// 부모가 0인데 1인 자식이 존재하는 오류가 있는지 검사
public static boolean isCorrect(String perfectBinaryTree, int[] vectorToGoParent) {
for (int i = 0; i < perfectBinaryTree.length(); i++) {
if(perfectBinaryTree.charAt(i) == '1' && perfectBinaryTree.charAt(i+vectorToGoParent[i]) == '0') return false;
}
return true;
}
}