자료구조

[자료구조, JAVA] 트리 개념, 구현

덕배Dev 2024. 3. 8. 13:57

1. 트리의 구조

 

  • 노드(Node) : 트리 구조의 자료 값을 담고 있는 단위
  • 에지(Edge) : 노드 간의 연결선( = link, branch)
  • 루트 노드(Root) : 부모가 없는 노드, 가장 위의 노드
  • 잎새 노드(Leaf) : 자식이 없는 노드 (= 단말 노드)
  • 내부 노드(Internal) : 잎새 노드를 제외한 모든 노드
  • 부모 노드(Parent) : 연결된 두 노드 중 상위의 노드
  • 자식 노드(Child) : 연결된 두 노드 중 하위의 노드
  • 형제(Sibling) : 같은 부모를 가지는 노드
  • 깊이(Depth) : 루트에서 어떤 노드까지의 간선의 수  ex) H 같은 경우 A(루트) -> B -> D -> H 로 총 에지(->)가 3개 있으므로 깊이는 3이다.
  • 레벨(Level) : 트리의 특정 깊이를 가지는 노드 집합 ex) Level 2인 노드들은 {D, E, F, G)이다.
  • 높이(Height) : 트리에서 가장 큰 레벨 값
  • 크기(Size) : 자신을 포함한 자식 노드의 개수 ex) A의 크기는 (A, B, C... ,J)가 있으므 로 10이다.
  • 차수(Degree) : 각 노드가 지닌 가지의 수 ex) F의 차수는 0, A의 차수는 2이다.
  • 트리의 차수 : 트리의 최대 차수

 

2. 트리의 특징

  • 하나의 노드에서 다른 노드로 이동하는 경로는 유일하다.
  • 노드가 N개인 트리의 Edge의 수는 N-1개
  • Cycle이 존재하지 않는 Acyclic구조이다.
  • 모든 노드는 서로 연결되어 있음
  • 하나의 Edge를 끊으면 2개의 Sub-Tree로 분리된다. ex) A->B를 연결하는 Edge를 끊게 되면 {A, C, F, G} 인 트리와 {B, D, E, H, I, J}인 트리 2개로 분리된다.

 

3. 이진 트리(Binary Tree)

  • 특징
    • 각 노드는 최대 2개의 자식을 가질 수 있음
    • 자식 노드는 좌우를 구분함 -> 아래 두 트리는 자식 노드가 B, C로 같지만 순서가 달라서 서로 다른 트리임
    • 포화  이진 트리의 높이가 h일 때, 노드의 수는 2^(h+1) - 1개
    • 포화(or 완전) 이진 트리의 노드가 N개 일 때, 높이는 logN(소수점은 삭제)
    • 이진 트리의 노드가 N개 일 때, 최대 가능 높이는 N

  • 이진 트리 종류
    • 포화 이진 트리(Perfect binary Tree) : 모든 레벨에서 노드들이 전부 채워져 있는 트리
    • 완전 이진 트리(Complete binary Tree) : 마지막 레벨을 제외하고 모든 노드들이 전부 채워져 있는 트리
    • 정 이진 트리(Full binary Tree) : 모든 노드가 0개 또는 2개의 자식 노드를 갖는 트리
    • 편향 트리(Skewed Binary Tree) : 한쪽으로 기울어진 트리 (=사향 트리)
    • 균형 이진 트리(Balanced binary Tree) : 모든 노드의 좌우 서브 트리 높이가 1이상 차이 나지 않는 트리 -> 탐색 속도가 빠름
     

  • 이진 트리의 순회 (Traversal)
    • 모든 노드를 빠뜨리거나 중복하지 않고 방문하는 연산
    • 순회 종류는 4가지! 
      • 전위 순회, 중위 순회, 후위 순회 -> DFS
      • 레벨 순회 -> BFS
  • 전위 순회 (Preorder Traversal)
    • 현재 노드 -> 왼쪽 노드 -> 오른쪽 노드
    • 순회 경로 : A -> B -> D -> H -> I -> E -> J -> C -> F -> G
  • 중위 순회 (Inorder Traversal) 
    • 왼쪽 노드 -> 현재 노드 -> 오른쪽 노드
    • 순회 경로 : H -> D -> I -> B -> J -> E -> A -> F -> C -> G
  • 후위 순회 (Postorder Traversal)
    • 왼쪽 노드 -> 오른쪽 노드 -> 현재 노드
    • 순회 경로 : H -> I -> D ->  J -> E -> B -> F -> G -> C -> A

  • 레벨 순회 (Levelorder Traversal)
    • 위쪽 레벨 부터 왼쪽 노드 -> 오른쪽 노드 
    • 방문 순서 : A -> B -> C -> D -> E -> F -> G -> H -> I -> J

2. 트리 순회 구현

전위순회에 경우,  자기 노드를 출력하고 자식 노드가 있는지 확인 후 왼쪽 노드부터 이동하여 자식노드가 없을 때까지 현재노드 -> 왼쪽 노드 -> 오른쪽 노드 순으로 반복 출력한다. 출력할 자식 노드가 없는 경우에는 부모 노드로 이동하여 남은 노드가 있으면 출력한다. 

이러한 과정을 계속 반복하기에 재귀문을 이용하여 각 순회를 구현해보았다.

// 노드의 데이터 저장하는 클래스
class Node{
    char data;
    Node left;
    Node right;
}
// 노드를 이용해 트리를 만드는 클래스
class Tree {
    public Node root;
    public void setRoot(Node node) {
        this.root = node;
    }
    public Node getRoot() {
        return root;
    }

    public Node makeNode(char data, Node left, Node right) {
        Node node = new Node();
        node.data = data;
        node.left = left;
        node.right = right;
        return node;
    }
    // 트리 순회 
    public void inOrder(Node node) {
        if(node!=null){
            inOrder(node.left);
            System.out.print(node.data + " ");
            inOrder(node.right);
        }
    }
    public void preOrder(Node node) {
        if(node!=null){
            System.out.print(node.data + " ");
            preOrder(node.left);
            preOrder(node.right);
        }
    }
    public void postOrder(Node node) {
        if(node!=null){
            postOrder(node.left);
            postOrder(node.right);
            System.out.print(node.data + " ");
        }
    }
}

public class BinaryTreeTraversal {
    public static void main(String[] args) {
        Tree tree = new Tree();
        // 트리 노드 입력
        Node H = tree.makeNode('H', null, null);
        Node I = tree.makeNode('I', null, null);
        Node D = tree.makeNode('D', H, I);
        Node J = tree.makeNode('J', null, null);
        Node E = tree.makeNode('E', J, null);
        Node B = tree.makeNode('B', D, E);
        Node F = tree.makeNode('F', null, null);
        Node G = tree.makeNode('G', null, null);
        Node C = tree.makeNode('C', F, G);
        Node A = tree.makeNode('A', B, C);
        //루트 노드 설정
        tree.setRoot(A);
        System.out.println("====== preOrder ======");
        tree.preOrder(A);
        System.out.println("");
        System.out.println("====== inOrder ======");
        tree.inOrder(A);
        System.out.println("");
        System.out.println("====== postOrder ======");
        tree.postOrder(A);
        System.out.println("");
    }
}

[실행 결과]