~ 재미있는 전위순회 이야기 ~
class N {
constructor(val) {
this.val = val
this.left = null
this.right = null
}
}
/**
* @param {N} root
* @returns
*
*/
function preorderTraversal(root) {
if(!root) {
return
}
console.log(`${root.val}`)
preorderTraversal(root.left)
preorderTraversal(root.right)
}
/**
* @param {N} root
*
* stack: [N{root}]
* root 노드가 왼쪽 자식 오른쪽 자식이 존재한다면
*
* 오른쪽 자식 노드를 stack에 push하고
* 왼쪽 자식 노드를 stack에 push하면
*
* 왼쪽 자식 노드가 항상 stack의 위쪽에 위치하게 된다.
* stack: [N{오른쪽 자식}, N{왼쪽 자식}]
* 위와 같은 형태로 stack은 쌓이게 된다.
*
* 이제 반복문이 실행되면서 stack을 pop하게 되면 왼쪽 자식은 pop 되고
* node = N{왼쪽 자식}이 된다. 이 상태로 다시 push를 하게 되면
*
* stack: [N{오른쪽 자식}, N{왼쪽 자식의 오른쪽 자식}, N{왼쪽 자식의 왼쪽 자식}]
* 이를 반복하게 되는데 참으로 영리한 것이 pop은 O(1)연산으로 매우 빠르고
* pop을 통해 계속 valuable한 왼쪽 자식을 DFS로 계속 찾아간다는 것이다.
*
* 또한 읽는 재미가 있다. (이해하기 위해선 잠깐 멈춰서 생각해야 하지만)
* 더군다나 한 메모리 주소를 반복적으로 쓰고 지우기만 하기 때문에 메모리 효율성과 stack이 넘쳐 망해버릴 일이 없다는 것이 좋다.
*/
function preorderTraversal2(root){
if(!root) return
const stack = [root]
while(stack.length > 0) {
const node = stack.pop()
console.log(node.val);
if(node.right) stack.push(node.right)
if(node.left) stack.push(node.left)
}
}
const root = new N(1)
root.left = new N(2)
root.right = new N(3)
root.left.left = new N(4)
root.left.right = new N(5)
/*
1
/ \
2 3
/ \
4 5
*/
preorderTraversal(root)
preorderTraversal2(root)