0
/ \
0 0
따라서, 돔 트리 구조는 이진트리가 아니다. 이론상으로 자식의 수가 메모리가 허용하는 만큼 늘어날 수 있다.
순회, 탐색 로직은 생각보다 간단하다.
// interface로 BinaryTreeNode 정의
interface BinaryTreeNode<T> {
val: T;
left?: BinaryTreeNode<T>;
right?: BinaryTreeNode<T>;
}
// 파생 트리 BinarayTree의 뼈대가 될 추상 Tree 설계
abstract class Tree<T> {
constructor(protected root?: BinaryTreeNode<T>, arr?: T[]) {
if (root) {
this.root = root;
} else if (arr) {
this.root = this.createTree(arr, 0);
}
}
abstract createTree(arr: T[], index: number): BinaryTreeNode<T> | undefined;
abstract preOrder(): void;
abstract inOrder(): void;
abstract postOrder(): void;
abstract bfs(target: T): T | undefined;
abstract dfs(target: T): T | undefined;
}
class BinaryTree<T> extends Tree<T> {
constructor(root?: BinaryTreeNode<T>, arr?: T[]) {
super(root, arr);
}
createTree(arr: T[], index: number = 0): BinaryTreeNode<T> | undefined {
if (!arr[index]) {
return undefined;
}
const root = {
val: arr[index],
left: this.createTree(arr, index * 2 + 1),
right: this.createTree(arr, index * 2 + 2),
};
return root;
}
preOrder() {
const curNode = this.root;
const preOrder = (node?: BinaryTreeNode<T>) => {
if (!node) {
return;
}
console.log(node.val);
preOrder(node.left);
preOrder(node.right);
};
preOrder(curNode);
}
inOrder() {
const curNode = this.root;
const inOrder = (node?: BinaryTreeNode<T>) => {
if (!node) {
return;
}
inOrder(node.left);
console.log(node.val);
inOrder(node.right);
};
inOrder(curNode);
}
postOrder() {
const curNode = this.root;
const postOrder = (node?: BinaryTreeNode<T>) => {
if (!node) {
return;
}
postOrder(node.left);
postOrder(node.right);
console.log(node.val);
};
postOrder(curNode);
}
bfs(target: T) {
if (!this.root) return undefined;
const queue: BinaryTreeNode<T>[] = [this.root];
while (queue.length !== 0) {
const curNode = queue.shift();
if (curNode!.val === target) {
return curNode!.val;
}
if (curNode!.left) {
queue.push(curNode!.left);
}
if (curNode!.right) {
queue.push(curNode!.right);
}
}
return undefined;
}
dfs(target: T) {
if (!this.root) return undefined;
const stack: BinaryTreeNode<T>[] = [this.root];
while (stack.length !== 0) {
const curNode = stack.pop();
if (curNode!.val === target) {
return curNode!.val;
}
if (curNode!.right) {
stack.push(curNode!.right);
}
if (curNode!.left) {
stack.push(curNode!.left);
}
}
}
}
const binaryTree = new BinaryTree<number>(
undefined,
[1, 2, 3, 4, 5, 6, 7, 8, 9]
);
binaryTree.preOrder(); // 1, 2, 4, 8 , 5, 3, 6, 9, 7
binaryTree.inOrder(); // 8, 4, 9, 2, 5, 1, 6, 3, 7
binaryTree.postOrder(); // 8, 9, 4, 5, 2, 6, 7, 3, 1
binaryTree.bfs(8); // 8
binaryTree.bfs(10); // undefined
binaryTree.dfs(8); // 8
binaryTree.dfs(10); // undefined
돔트리, 렌더트리 등의 근원 자료구조 트리를 구현해보며 어떤 식으로 동작할지 다시 한번 짚고 넘어가는 계기가 되었다. 한가지 아쉬운 점은, Tree를 추상 클래스로 만들었는데 이 추상 클래스는 root 노드가 BinaryTreeNode로 지정되었기 때문에 반드시 BinaryTreeNode를 만들어야 한다. 추후에는 코드를 수정해서 조금 더 범용적인 추상 클래스를 만들볼 계획이다.