📌 트리 순회 (Tree Traversal)

연결 리스트를 순회하는 방법은 선형 접근법 한 가지만 존재하지만, 트리 순회에는 다양한 접근법이 있다.
그 중 가장 널리 쓰이는 두 가지 접근법은 너비 우선 탐색깊이 우선 탐색 이다.



1. BFS(Breadth First Search) : 너비 우선 탐색

자식 노드를 탐색하기 전에 형제 노드를 먼저 탐색한다.(수평적으로 탐색)

(혹은 배열)를 만들어 수평으로 탐색한 값을 임시로 저장한다.
다른 배열을 하나 만들어 큐에 있는 값을 순서대로 전부 저장한다.

  1. 큐에 root 먼저 담기
  2. 큐에 담긴게 있을 때, 큐의 맨 앞 요소를 제거하고 다른 배열(data) 에 담기.
  3. 제거한 값의 좌/우 값이 있을 경우 큐에 담기 (2,3 과정 반복)
  4. 값이 탐색 순서대로 모두 담긴 data를 return
class Node {
    constructor(value){
        this.value = value;
        this.left = null;
        this.right = null;
    }
}

class BinarySearchTree {
    constructor(){
        this.root = null;
    }
    insert(value){
        var newNode = new Node(value);
        if(this.root === null){
            this.root = newNode;
            return this;
        }
        var current = this.root;
        while(true){
            if(value === current.value) return undefined;
            if(value < current.value){
                if(current.left === null){
                    current.left = newNode;
                    return this;
                }
                current = current.left;
            } else {
                if(current.right === null){
                    current.right = newNode;
                    return this;
                } 
                current = current.right;
            }
        }
    }
    BFS(){
        var node = this.root,
            data = [],
            queue = [];
        queue.push(node); // root 담기

        while(queue.length){
           node = queue.shift();
           data.push(node.value);
           if(node.left) queue.push(node.left);
           if(node.right) queue.push(node.right);
        }
        return data;
    }
}


var tree = new BinarySearchTree();
tree.insert(10);
tree.insert(6);
tree.insert(15);
tree.insert(3);
tree.insert(8);
tree.insert(20);

// 		 10
//	  6		15
//  3  8	 20
tree.BFS(); // [10,6,15,3,8,20]

2. DFS(Depth First Search) : 깊이 우선 탐색

재귀를 사용한다. 크게 세 종류가 있다.

1) 전위 순회(PreOrder) : root -> left -> right

2) 중위 순회(InOrder) : left -> root -> right

3) 후위 순회(PostOrder) : left -> right -> root

class Node {
    constructor(value){
        this.value = value;
        this.left = null;
        this.right = null;
    }
}

class BinarySearchTree {
    constructor(){
        this.root = null;
    }
    insert(value){
        var newNode = new Node(value);
        if(this.root === null){
            this.root = newNode;
            return this;
        }
        var current = this.root;
        while(true){
            if(value === current.value) return undefined;
            if(value < current.value){
                if(current.left === null){
                    current.left = newNode;
                    return this;
                }
                current = current.left;
            } else {
                if(current.right === null){
                    current.right = newNode;
                    return this;
                } 
                current = current.right;
            }
        }
    }
    DFSPreOrder(){
        var data = [];
        function traverse(node){
            data.push(node.value); // root 먼저 담기
            if(node.left) traverse(node.left); // 그 다음 left 재귀적으로 탐색
            if(node.right) traverse(node.right); // left가 없을 경우 right 재귀적으로 탐색
        }
        traverse(this.root);
        return data;
    }
    DFSPostOrder(){
        var data = [];
        function traverse(node){
            if(node.left) traverse(node.left); // 먼저 left 재귀적으로 탐색
            if(node.right) traverse(node.right); // 그 다음 rigth 재귀적으로 탐색
            data.push(node.value); // 마지막으로 root 담기
        }
        traverse(this.root);
        return data;
    }
    DFSInOrder(){
        var data = [];
        function traverse(node){
            if(node.left) traverse(node.left); // left 먼저 재귀적으로 탐색
            data.push(node.value); // root 담기
            if(node.right) traverse(node.right); // right 재귀적으로 탐색
        }
        traverse(this.root);
        return data;
    }
}


var tree = new BinarySearchTree();
tree.insert(10);
tree.insert(6);
tree.insert(15);
tree.insert(3);
tree.insert(8);
tree.insert(20);

// 		 10
//	  6		15
//  3  8	 20

tree.DFSPreOrder(); // [10,6,3,8,15,20]
tree.DFSPostOrder(); // [3,8,6,20,15,10]
tree.DFSInOrder(); // [3,6,8,10,15,20]



📌 BFS & DFS 비교

BFS와 DFS 중 어떤 것을 사용하는 것이 더 좋은가?

트리 모양에 따라 어떤 것을 써야 하는지가 달라진다.

ex. 깊이 보다 너비가 넓은 트리 : 깊이 우선 탐색이 공간 복잡도가 더 낫다.(시간 복잡도는 동일하다.)
너비 보다 깊이가 넓은 트리 : 너비 우선 탐색이 공간 복잡도가 더 낫다.(시간 복잡도는 동일하다.)

DFS의 세 가지 접근법 중 어떤 것을 선택해야 하는가?

  • 전위 순회 : 트리를 복사하거나 평탄화 해서 저장하는 경우 (데이터 베이스에 저장했다가 나중에 연쇄 구조로 다시 만들어 낼 때) 도움된다.
  • 중위 순회 : 이진 탐색 트리(BST)에 중위 순회를 적용하면 모든 노드가 순서대로 나오게 된다. (순서대로 작업하는 경우 유용하다.)
profile
모든게 새롭고 재밌는 프론트엔드 새싹

0개의 댓글